EStatusCode PDFUsedFont::WriteState(ObjectsContext* inStateWriter,ObjectIDType inObjectID) { inStateWriter->StartNewIndirectObject(inObjectID); DictionaryContext* pdfUsedFontObject = inStateWriter->StartDictionary(); pdfUsedFontObject->WriteKey("Type"); pdfUsedFontObject->WriteNameValue("PDFUsedFont"); ObjectIDType writtenFontObject; if(mWrittenFont) { writtenFontObject = inStateWriter->GetInDirectObjectsRegistry().AllocateNewObjectID(); pdfUsedFontObject->WriteKey("mWrittenFont"); pdfUsedFontObject->WriteNewObjectReferenceValue(writtenFontObject); } inStateWriter->EndDictionary(pdfUsedFontObject); inStateWriter->EndIndirectObject(); if(mWrittenFont) mWrittenFont->WriteState(inStateWriter,writtenFontObject); return PDFHummus::eSuccess; }
PDFStream* ObjectsContext::StartUnfilteredPDFStream(DictionaryContext* inStreamDictionary) { // write stream header and allocate PDF stream. // PDF stream will take care of maintaining state for the stream till writing is finished // Write the stream header // Write Stream Dictionary (note that inStreamDictionary is optionally used) DictionaryContext* streamDictionaryContext = (NULL == inStreamDictionary ? StartDictionary() : inStreamDictionary); // Length (write as an indirect object) streamDictionaryContext->WriteKey(scLength); ObjectIDType lengthObjectID = mReferencesRegistry.AllocateNewObjectID(); streamDictionaryContext->WriteNewObjectReferenceValue(lengthObjectID); EndDictionary(streamDictionaryContext); // Write Stream Content WriteKeyword(scStream); // now begin the stream itself PDFStream* result = new PDFStream(false,mOutputStream, mEncryptionHelper,lengthObjectID,NULL); // break encryption, if any, when writing a stream, cause if encryption is desired, only top level elements should be encrypted. hence - the stream itself is, but its contents do not re-encrypt if(mEncryptionHelper) mEncryptionHelper->PauseEncryption(); return result; }
void ObjectsContext::EndPDFStream(PDFStream* inStream) { // finalize the stream write to end stream context and calculate length inStream->FinalizeStreamWrite(); if(inStream->GetExtentObjectID() == 0) { DictionaryContext* streamDictionaryContext = inStream->GetStreamDictionaryForDirectExtentStream(); // Length (write as a direct object) streamDictionaryContext->WriteKey(scLength); streamDictionaryContext->WriteIntegerValue(inStream->GetLength()); EndDictionary(streamDictionaryContext); // Write Stream Content WriteKeyword(scStream); inStream->FlushStreamContentForDirectExtentStream(); EndLine(); WriteKeyword(scEndStream); EndIndirectObject(); } else { WritePDFStreamEndWithoutExtent(); EndIndirectObject(); WritePDFStreamExtent(inStream); } }
EStatusCode UsedFontsRepository::WriteState(ObjectsContext* inStateWriter,ObjectIDType inObjectID) { EStatusCode status = PDFHummus::eSuccess; ObjectIDTypeList usedFontsObjects; inStateWriter->StartNewIndirectObject(inObjectID); DictionaryContext* usedFontsRepositoryObject = inStateWriter->StartDictionary(); usedFontsRepositoryObject->WriteKey("Type"); usedFontsRepositoryObject->WriteNameValue("UsedFontsRepository"); usedFontsRepositoryObject->WriteKey("mUsedFonts"); inStateWriter->StartArray(); StringAndLongToPDFUsedFontMap::iterator it = mUsedFonts.begin(); for(; it != mUsedFonts.end();++it) { PDFTextString aTextString(it->first.first); inStateWriter->WriteLiteralString(aTextString.ToString()); inStateWriter->WriteInteger(it->first.second); ObjectIDType usedFontID = inStateWriter->GetInDirectObjectsRegistry().AllocateNewObjectID(); inStateWriter->WriteNewIndirectObjectReference(usedFontID); usedFontsObjects.push_back(usedFontID); } inStateWriter->EndArray(eTokenSeparatorEndLine); usedFontsRepositoryObject->WriteKey("mOptionaMetricsFiles"); inStateWriter->StartArray(); StringToStringMap::iterator itOptionals = mOptionaMetricsFiles.begin(); for(; itOptionals != mOptionaMetricsFiles.end();++itOptionals) { PDFTextString aTextString(itOptionals->first); inStateWriter->WriteLiteralString(aTextString.ToString()); aTextString = itOptionals->second; inStateWriter->WriteLiteralString(aTextString.ToString()); } inStateWriter->EndArray(eTokenSeparatorEndLine); inStateWriter->EndDictionary(usedFontsRepositoryObject); inStateWriter->EndIndirectObject(); if(usedFontsObjects.size() > 0) { it = mUsedFonts.begin(); ObjectIDTypeList::iterator itIDs = usedFontsObjects.begin(); for(; it != mUsedFonts.end() && PDFHummus::eSuccess == status;++it,++itIDs) status = it->second->WriteState(inStateWriter,*itIDs); } return status; }
EStatusCode CFFEmbeddedFontWriter::WriteEmbeddedFont( FreeTypeFaceWrapper& inFontInfo, const UIntVector& inSubsetGlyphIDs, const std::string& inFontFile3SubType, const std::string& inSubsetFontName, ObjectsContext* inObjectsContext, UShortVector* inCIDMapping, ObjectIDType& outEmbeddedFontObjectID) { MyStringBuf rawFontProgram; bool notEmbedded; // as oppose to true type, the reason for using a memory stream here is mainly peformance - i don't want to start // setting file pointers and move in a file stream EStatusCode status; do { status = CreateCFFSubset(inFontInfo,inSubsetGlyphIDs,inCIDMapping,inSubsetFontName,notEmbedded,rawFontProgram); if(status != PDFHummus::eSuccess) { TRACE_LOG("CFFEmbeddedFontWriter::WriteEmbeddedFont, failed to write embedded font program"); break; } if(notEmbedded) { // can't embed. mark succesful, and go back empty outEmbeddedFontObjectID = 0; TRACE_LOG("CFFEmbeddedFontWriter::WriteEmbeddedFont, font may not be embedded. so not embedding"); return PDFHummus::eSuccess; } outEmbeddedFontObjectID = inObjectsContext->StartNewIndirectObject(); DictionaryContext* fontProgramDictionaryContext = inObjectsContext->StartDictionary(); rawFontProgram.pubseekoff(0,std::ios_base::beg); fontProgramDictionaryContext->WriteKey(scSubtype); fontProgramDictionaryContext->WriteNameValue(inFontFile3SubType); PDFStream* pdfStream = inObjectsContext->StartPDFStream(fontProgramDictionaryContext); // now copy the created font program to the output stream InputStringBufferStream fontProgramStream(&rawFontProgram); OutputStreamTraits streamCopier(pdfStream->GetWriteStream()); status = streamCopier.CopyToOutputStream(&fontProgramStream); if(status != PDFHummus::eSuccess) { TRACE_LOG("CFFEmbeddedFontWriter::WriteEmbeddedFont, failed to copy font program into pdf stream"); break; } inObjectsContext->EndPDFStream(pdfStream); delete pdfStream; }while(false); return status; }
EStatusCode TrueTypeEmbeddedFontWriter::WriteEmbeddedFont( FreeTypeFaceWrapper& inFontInfo, const UIntVector& inSubsetGlyphIDs, ObjectsContext* inObjectsContext, ObjectIDType& outEmbeddedFontObjectID) { MyStringBuf rawFontProgram; bool notEmbedded; EStatusCode status; do { status = CreateTrueTypeSubset(inFontInfo,inSubsetGlyphIDs,notEmbedded,rawFontProgram); if(status != PDFHummus::eSuccess) { TRACE_LOG("TrueTypeEmbeddedFontWriter::WriteEmbeddedFont, failed to write embedded font program"); break; } if(notEmbedded) { // can't embed. mark succesful, and go back empty outEmbeddedFontObjectID = 0; TRACE_LOG("TrueTypeEmbeddedFontWriter::WriteEmbeddedFont, font may not be embedded. so not embedding"); return PDFHummus::eSuccess; } outEmbeddedFontObjectID = inObjectsContext->StartNewIndirectObject(); DictionaryContext* fontProgramDictionaryContext = inObjectsContext->StartDictionary(); // Length1 (decompressed true type program length) fontProgramDictionaryContext->WriteKey(scLength1); fontProgramDictionaryContext->WriteIntegerValue(rawFontProgram.GetCurrentWritePosition()); rawFontProgram.pubseekoff(0,std::ios_base::beg); PDFStream* pdfStream = inObjectsContext->StartPDFStream(fontProgramDictionaryContext); // now copy the created font program to the output stream InputStringBufferStream fontProgramStream(&rawFontProgram); OutputStreamTraits streamCopier(pdfStream->GetWriteStream()); status = streamCopier.CopyToOutputStream(&fontProgramStream); if(status != PDFHummus::eSuccess) { TRACE_LOG("TrueTypeEmbeddedFontWriter::WriteEmbeddedFont, failed to copy font program into pdf stream"); break; } inObjectsContext->EndPDFStream(pdfStream); delete pdfStream; }while(false); return status; }
EStatusCode WrittenFontCFF::WriteState(ObjectsContext* inStateWriter,ObjectIDType inObjectID) { inStateWriter->StartNewIndirectObject(inObjectID); DictionaryContext* writtenFontDictionary = inStateWriter->StartDictionary(); writtenFontDictionary->WriteKey("Type"); writtenFontDictionary->WriteNameValue("WrittenFontCFF"); writtenFontDictionary->WriteKey("mAvailablePositionsCount"); writtenFontDictionary->WriteIntegerValue(mAvailablePositionsCount); writtenFontDictionary->WriteKey("mFreeList"); inStateWriter->StartArray(); UCharAndUCharList::iterator it = mFreeList.begin(); for(; it != mFreeList.end();++it) { inStateWriter->WriteInteger(it->first); inStateWriter->WriteInteger(it->second); } inStateWriter->EndArray(eTokenSeparatorEndLine); writtenFontDictionary->WriteKey("mAssignedPositions"); inStateWriter->StartArray(); for(int i=0;i<256;++i) inStateWriter->WriteInteger(mAssignedPositions[i]); inStateWriter->EndArray(eTokenSeparatorEndLine); writtenFontDictionary->WriteKey("mAssignedPositionsAvailable"); inStateWriter->StartArray(); for(int i=0;i<256;++i) inStateWriter->WriteBoolean(mAssignedPositionsAvailable[i]); inStateWriter->EndArray(eTokenSeparatorEndLine); writtenFontDictionary->WriteKey("mIsCID"); writtenFontDictionary->WriteBooleanValue(mIsCID); EStatusCode status = AbstractWrittenFont::WriteStateInDictionary(inStateWriter,writtenFontDictionary); if(PDFHummus::eSuccess == status) { inStateWriter->EndDictionary(writtenFontDictionary); inStateWriter->EndIndirectObject(); status = AbstractWrittenFont::WriteStateAfterDictionary(inStateWriter); } return status; }
vector<string> PDFModifiedPage::WriteNewResourcesDictionary(ObjectsContext& inObjectContext) { vector<string> formResourcesNames; // no existing resource dictionary, so write a new one DictionaryContext* dict = inObjectContext.StartDictionary(); dict->WriteKey("XObject"); DictionaryContext* xobjectDict = inObjectContext.StartDictionary(); for (unsigned long i = 0; i<mContenxts.size(); ++i) { string formObjectName = string("myForm_") + Int(i).ToString(); dict->WriteKey(formObjectName); dict->WriteObjectReferenceValue(mContenxts[i]->GetObjectID()); formResourcesNames.push_back(formObjectName); } inObjectContext.EndDictionary(xobjectDict); inObjectContext.EndDictionary(dict); return formResourcesNames; }
EStatusCode WrittenFontTrueType::WriteState(ObjectsContext* inStateWriter,ObjectIDType inObjectID) { inStateWriter->StartNewIndirectObject(inObjectID); DictionaryContext* writtenFontDictionary = inStateWriter->StartDictionary(); writtenFontDictionary->WriteKey("Type"); writtenFontDictionary->WriteNameValue("WrittenFontTrueType"); EStatusCode status = AbstractWrittenFont::WriteStateInDictionary(inStateWriter,writtenFontDictionary); if(PDFHummus::eSuccess == status) { inStateWriter->EndDictionary(writtenFontDictionary); inStateWriter->EndIndirectObject(); status = AbstractWrittenFont::WriteStateAfterDictionary(inStateWriter); } return status; }
vector<string> PDFModifiedPage::WriteModifiedResourcesDict(PDFParser* inParser,PDFDictionary* inResourcesDictionary,ObjectsContext& inObjectContext,PDFDocumentCopyingContext* inCopyingContext) { vector<string> formResourcesNames; MapIterator<PDFNameToPDFObjectMap> resourcesDictionaryIt = inResourcesDictionary->GetIterator(); // create modified page object DictionaryContext* dict = mWriter->GetObjectsContext().StartDictionary(); // copy all elements of the page to the new page object, but the "Contents" and "Resources" elements while(resourcesDictionaryIt.MoveNext()) { if(resourcesDictionaryIt.GetKey()->GetValue() != "XObject") { dict->WriteKey(resourcesDictionaryIt.GetKey()->GetValue()); inCopyingContext->CopyDirectObjectAsIs(resourcesDictionaryIt.GetValue()); } } // now write a new xobject entry. dict->WriteKey("XObject"); DictionaryContext* xobjectDict = inObjectContext.StartDictionary(); PDFObjectCastPtr<PDFDictionary> existingXObjectDict(inParser->QueryDictionaryObject(inResourcesDictionary,"XObject")); string imageObjectName; if(existingXObjectDict.GetPtr()) { // i'm having a very sophisticated algo here to create a new unique name. // i'm making sure it's different in one letter from any name, using a well known discrete math proof method MapIterator<PDFNameToPDFObjectMap> itExisting = existingXObjectDict->GetIterator(); unsigned long i=0; while(itExisting.MoveNext()) { string name = itExisting.GetKey()->GetValue(); xobjectDict->WriteKey(name); inCopyingContext->CopyDirectObjectAsIs(itExisting.GetValue()); imageObjectName.push_back((char)(GetDifferentChar((name.length() >= i+1) ? name[i]:0x39))); ++i; } inObjectContext.EndLine(); } PDFFormXObjectVector::iterator itForms = mContenxts.begin(); imageObjectName.push_back('_'); for(int i=0;itForms != mContenxts.end();++i,++itForms) { string formObjectName = imageObjectName + Int(i).ToString(); xobjectDict->WriteKey(formObjectName); xobjectDict->WriteObjectReferenceValue((*itForms)->GetObjectID()); formResourcesNames.push_back(formObjectName); } inObjectContext.EndDictionary(xobjectDict); inObjectContext.EndDictionary(dict); return formResourcesNames; }
PDFStream* ObjectsContext::StartPDFStream(DictionaryContext* inStreamDictionary,bool inForceDirectExtentObject) { // write stream header and allocate PDF stream. // PDF stream will take care of maintaining state for the stream till writing is finished // Write the stream header // Write Stream Dictionary (note that inStreamDictionary is optionally used) DictionaryContext* streamDictionaryContext = (NULL == inStreamDictionary ? StartDictionary() : inStreamDictionary); // Compression (if necessary) if(mCompressStreams) { streamDictionaryContext->WriteKey(scFilter); streamDictionaryContext->WriteNameValue(scFlateDecode); } PDFStream* result = NULL; if(!inForceDirectExtentObject) { // Length (write as an indirect object) streamDictionaryContext->WriteKey(scLength); ObjectIDType lengthObjectID = mReferencesRegistry.AllocateNewObjectID(); streamDictionaryContext->WriteNewObjectReferenceValue(lengthObjectID); EndDictionary(streamDictionaryContext); // Write Stream Content WriteKeyword(scStream); result = new PDFStream(mCompressStreams,mOutputStream, mEncryptionHelper,lengthObjectID,mExtender); } else result = new PDFStream(mCompressStreams,mOutputStream, mEncryptionHelper,streamDictionaryContext,mExtender); // break encryption, if any, when writing a stream, cause if encryption is desired, only top level elements should be encrypted. hence - the stream itself is, but its contents do not re-encrypt if (mEncryptionHelper) mEncryptionHelper->PauseEncryption(); return result; }
PDFStream* ObjectsContext::StartUnfilteredPDFStream(DictionaryContext* inStreamDictionary) { // write stream header and allocate PDF stream. // PDF stream will take care of maintaining state for the stream till writing is finished // Write the stream header // Write Stream Dictionary (note that inStreamDictionary is optionally used) DictionaryContext* streamDictionaryContext = (NULL == inStreamDictionary ? StartDictionary() : inStreamDictionary); // Length (write as an indirect object) streamDictionaryContext->WriteKey(scLength); ObjectIDType lengthObjectID = mReferencesRegistry.AllocateNewObjectID(); streamDictionaryContext->WriteNewObjectReferenceValue(lengthObjectID); EndDictionary(streamDictionaryContext); // Write Stream Content WriteKeyword(scStream); // now begin the stream itself return new PDFStream(false,mOutputStream,lengthObjectID,NULL); }
PDFStream* ObjectsContext::StartPDFStream(DictionaryContext* inStreamDictionary,bool inForceDirectExtentObject) { // write stream header and allocate PDF stream. // PDF stream will take care of maintaining state for the stream till writing is finished // Write the stream header // Write Stream Dictionary (note that inStreamDictionary is optionally used) DictionaryContext* streamDictionaryContext = (NULL == inStreamDictionary ? StartDictionary() : inStreamDictionary); // Compression (if necessary) if(mCompressStreams) { streamDictionaryContext->WriteKey(scFilter); streamDictionaryContext->WriteNameValue(scFlateDecode); } if(!inForceDirectExtentObject) { // Length (write as an indirect object) streamDictionaryContext->WriteKey(scLength); ObjectIDType lengthObjectID = mReferencesRegistry.AllocateNewObjectID(); streamDictionaryContext->WriteNewObjectReferenceValue(lengthObjectID); EndDictionary(streamDictionaryContext); // Write Stream Content WriteKeyword(scStream); return new PDFStream(mCompressStreams,mOutputStream,lengthObjectID,mExtender); } else return new PDFStream(mCompressStreams,mOutputStream,streamDictionaryContext,mExtender); }
void DescendentFontWriter::WriteCIDSystemInfo(ObjectIDType inCIDSystemInfoObjectID) { FT_Bool isCID; const char* registry; const char* ordering; FT_Int supplement; if(FT_Get_CID_Is_Internally_CID_Keyed(*mFontInfo,&isCID) != 0) isCID = false; if(isCID && FT_Get_CID_Registry_Ordering_Supplement(*mFontInfo,®istry,&ordering,&supplement) != 0) isCID = false; if(!isCID) { registry = scAdobe; ordering = scIdentity; supplement = 0; } mObjectsContext->StartNewIndirectObject(inCIDSystemInfoObjectID); DictionaryContext* systemInfoContext = mObjectsContext->StartDictionary(); // Registry systemInfoContext->WriteKey(scRegistry); systemInfoContext->WriteLiteralStringValue(registry); // Ordering systemInfoContext->WriteKey(scOrdering); systemInfoContext->WriteLiteralStringValue(ordering); // Supplement systemInfoContext->WriteKey(scSupplement); systemInfoContext->WriteIntegerValue(supplement); mObjectsContext->EndDictionary(systemInfoContext); mObjectsContext->EndIndirectObject(); }
EStatusCodeAndObjectIDType PDFCommentWriter::WriteCommentsTree(PDFComment* inComment) { EStatusCodeAndObjectIDType result; ObjectIDType repliedTo = 0; // if already written, return PDFCommentToObjectIDTypeMap::iterator it = mCommentsForNextPage.find(inComment); if(it != mCommentsForNextPage.end()) { result.first = eSuccess; result.second = it->second; return result; } // if has a referred comment, write it first if(inComment->ReplyTo != NULL) { EStatusCodeAndObjectIDType repliedtoResult = WriteCommentsTree(inComment->ReplyTo); if(repliedtoResult.first != eSuccess) { result.first = eFailure; result.second = 0; return result; } else repliedTo = repliedtoResult.second; } do { ObjectsContext& objectsContext = mPDFWriter->GetObjectsContext(); // Start new InDirect object for annotation dictionary result.second = objectsContext.StartNewIndirectObject(); DictionaryContext* dictionaryContext = objectsContext.StartDictionary(); // Type dictionaryContext->WriteKey("Type"); dictionaryContext->WriteNameValue("Annot"); // SubType dictionaryContext->WriteKey("Subtype"); dictionaryContext->WriteNameValue("Text"); // Rect dictionaryContext->WriteKey("Rect"); dictionaryContext->WriteRectangleValue( PDFRectangle(inComment->FrameBoundings[0], inComment->FrameBoundings[1], inComment->FrameBoundings[2], inComment->FrameBoundings[3])); // Contents dictionaryContext->WriteKey("Contents"); dictionaryContext->WriteLiteralStringValue(PDFTextString(inComment->Text).ToString()); // C (color) dictionaryContext->WriteKey("C"); objectsContext.StartArray(); if(inComment->Color.UseCMYK) { objectsContext.WriteDouble((double)inComment->Color.CMYKComponents[0]/255); objectsContext.WriteDouble((double)inComment->Color.CMYKComponents[1]/255); objectsContext.WriteDouble((double)inComment->Color.CMYKComponents[2]/255); objectsContext.WriteDouble((double)inComment->Color.CMYKComponents[3]/255); } else { objectsContext.WriteDouble((double)inComment->Color.RGBComponents[0]/255); objectsContext.WriteDouble((double)inComment->Color.RGBComponents[1]/255); objectsContext.WriteDouble((double)inComment->Color.RGBComponents[2]/255); } objectsContext.EndArray(eTokenSeparatorEndLine); // T dictionaryContext->WriteKey("T"); dictionaryContext->WriteLiteralStringValue(PDFTextString(inComment->CommentatorName).ToString()); // M dictionaryContext->WriteKey("M"); dictionaryContext->WriteLiteralStringValue(inComment->Time.ToString()); if(inComment->ReplyTo != NULL) { // IRT dictionaryContext->WriteKey("IRT"); dictionaryContext->WriteObjectReferenceValue(repliedTo); // RT (we're doing always "reply" at this point, being a reply to comment dictionaryContext->WriteKey("RT"); dictionaryContext->WriteNameValue("R"); } // Open (we'll have them all closed to begin with) dictionaryContext->WriteKey("Open"); dictionaryContext->WriteBooleanValue(false); // Name dictionaryContext->WriteKey("Name"); dictionaryContext->WriteNameValue("Comment"); if(objectsContext.EndDictionary(dictionaryContext) != eSuccess) { result.first = eFailure; TRACE_LOG("PDFCommentWriter::WriteCommentsTree, Exception in ending comment dictionary"); break; } objectsContext.EndIndirectObject(); mCommentsForNextPage.insert(PDFCommentToObjectIDTypeMap::value_type(inComment,result.second)); result.first = eSuccess; }while(false); return result; }
EStatusCode DCTDecodeFilterTest::ModifyImageObject(PDFWriter* inWriter,ObjectIDType inImageObject) { EStatusCode status = eSuccess; PDFDocumentCopyingContext* modifiedFileContext = inWriter->CreatePDFCopyingContextForModifiedFile(); do { // get image source dictionary PDFObjectCastPtr<PDFStreamInput> imageStream(inWriter->GetModifiedFileParser().ParseNewObject(inImageObject)); RefCountPtr<PDFDictionary> imageDictionary(imageStream->QueryStreamDictionary()); // strt object for modified image inWriter->GetObjectsContext().StartModifiedIndirectObject(inImageObject); DictionaryContext* newImageDictionary = inWriter->GetObjectsContext().StartDictionary(); MapIterator<PDFNameToPDFObjectMap> it = imageDictionary->GetIterator(); // copy all but "Filter" and "Length" ObjectIDTypeList indirectObjects; while (it.MoveNext()) { if(it.GetKey()->GetValue() == "Filter" || it.GetKey()->GetValue() == "Length") continue; newImageDictionary->WriteKey(it.GetKey()->GetValue()); EStatusCodeAndObjectIDTypeList result = modifiedFileContext->CopyDirectObjectWithDeepCopy(it.GetValue()); if(result.first != eSuccess) { status = result.first; break; } indirectObjects.insert(indirectObjects.end(),result.second.begin(),result.second.end()); } if(status != eSuccess) break; // start image stream for this dictionary (make sure it's unfiltered) PDFStream* newImageStream = inWriter->GetObjectsContext().StartUnfilteredPDFStream(newImageDictionary); // copy source stream through read filter IByteReader* sourceImage = modifiedFileContext->GetSourceDocumentParser()->StartReadingFromStream(imageStream.GetPtr()); if(!sourceImage) { cout<<"failed to read DCT stream\n"; status = eFailure; break; } OutputStreamTraits traits(newImageStream->GetWriteStream()); status = traits.CopyToOutputStream(sourceImage); // finalize stream inWriter->GetObjectsContext().EndPDFStream(newImageStream); delete newImageStream; // late check for status so stream is deleted if(status != eSuccess) break; // copy remaining indirect objects from image stream dictionary status = modifiedFileContext->CopyNewObjectsForDirectObject(indirectObjects); } while (false); delete modifiedFileContext; return status; }
EStatusCode ObjectsContext::WriteState(ObjectsContext* inStateWriter,ObjectIDType inObjectID) { EStatusCode status; do { inStateWriter->StartNewIndirectObject(inObjectID); ObjectIDType referencesRegistryObjectID = inStateWriter->GetInDirectObjectsRegistry().AllocateNewObjectID(); ObjectIDType subsetFontsNameSequanceID = inStateWriter->GetInDirectObjectsRegistry().AllocateNewObjectID(); DictionaryContext* objectsContextDict = inStateWriter->StartDictionary(); objectsContextDict->WriteKey("Type"); objectsContextDict->WriteNameValue("ObjectsContext"); objectsContextDict->WriteKey("mReferencesRegistry"); objectsContextDict->WriteNewObjectReferenceValue(referencesRegistryObjectID); objectsContextDict->WriteKey("mCompressStreams"); objectsContextDict->WriteBooleanValue(mCompressStreams); objectsContextDict->WriteKey("mSubsetFontsNamesSequance"); objectsContextDict->WriteNewObjectReferenceValue(subsetFontsNameSequanceID); inStateWriter->EndDictionary(objectsContextDict); inStateWriter->EndIndirectObject(); status = mReferencesRegistry.WriteState(inStateWriter,referencesRegistryObjectID); if(status != PDFHummus::eSuccess) break; // write subset fonts names sequance inStateWriter->StartNewIndirectObject(subsetFontsNameSequanceID); DictionaryContext* sequanceDict = inStateWriter->StartDictionary(); sequanceDict->WriteKey("Type"); sequanceDict->WriteNameValue("UppercaseSequance"); sequanceDict->WriteKey("mSequanceString"); sequanceDict->WriteLiteralStringValue(mSubsetFontsNamesSequance.ToString()); inStateWriter->EndDictionary(sequanceDict); inStateWriter->EndIndirectObject(); }while(false); return status; }
PDFHummus::EStatusCode PDFModifiedPage::WritePage() { EStatusCode status = EndContentContext(); // just in case someone forgot to close the latest content context do { if (status != eSuccess || !mIsDirty) { break; } // allocate an object ID for the new contents stream (for placing the form) // we first create the modified page object, so that we can define a name for the new form xobject // that is unique ObjectsContext& objectContext = mWriter->GetObjectsContext(); ObjectIDType newContentObjectID = objectContext.GetInDirectObjectsRegistry().AllocateNewObjectID(); ObjectIDType newEncapsulatingObjectID = 0; // create a copying context, so we can copy the page dictionary, and modify its contents + resources dict PDFDocumentCopyingContext* copyingContext = mWriter->CreatePDFCopyingContextForModifiedFile(); // get the page object ObjectIDType pageObjectID = copyingContext->GetSourceDocumentParser()->GetPageObjectID(mPageIndex); PDFObjectCastPtr<PDFDictionary> pageDictionaryObject = copyingContext->GetSourceDocumentParser()->ParsePage(mPageIndex); MapIterator<PDFNameToPDFObjectMap> pageDictionaryObjectIt = pageDictionaryObject->GetIterator(); // create modified page object objectContext.StartModifiedIndirectObject(pageObjectID); DictionaryContext* modifiedPageObject = mWriter->GetObjectsContext().StartDictionary(); // copy all elements of the page to the new page object, but the "Contents", "Resources" and "Annots" elements while (pageDictionaryObjectIt.MoveNext()) { if (pageDictionaryObjectIt.GetKey()->GetValue() != "Resources" && pageDictionaryObjectIt.GetKey()->GetValue() != "Contents" && pageDictionaryObjectIt.GetKey()->GetValue() != "Annots") { modifiedPageObject->WriteKey(pageDictionaryObjectIt.GetKey()->GetValue()); copyingContext->CopyDirectObjectAsIs(pageDictionaryObjectIt.GetValue()); } } // Write new annotations entry, joining existing annotations, and new ones (from links attaching or what not) if (pageDictionaryObject->Exists("Annots") || mWriter->GetDocumentContext().GetAnnotations().size() > 0) { modifiedPageObject->WriteKey("Annots"); objectContext.StartArray(); // write old annots, if any exist if(pageDictionaryObject->Exists("Annots")) { PDFObjectCastPtr<PDFArray> anArray(copyingContext->GetSourceDocumentParser()->QueryDictionaryObject(pageDictionaryObject.GetPtr(), "Annots")); SingleValueContainerIterator<PDFObjectVector> refs = anArray->GetIterator(); while (refs.MoveNext()) copyingContext->CopyDirectObjectAsIs(refs.GetItem()); } // write new annots from links ObjectIDTypeSet& annotations = mWriter->GetDocumentContext().GetAnnotations(); if (annotations.size() > 0) { ObjectIDTypeSet::iterator it = annotations.begin(); for (; it != annotations.end(); ++it) objectContext.WriteNewIndirectObjectReference(*it); } annotations.clear(); objectContext.EndArray(eTokenSeparatorEndLine); } // Write new contents entry, joining the existing contents with the new one. take care of various scenarios of the existing Contents modifiedPageObject->WriteKey("Contents"); if (!pageDictionaryObject->Exists("Contents")) { // no contents objectContext.WriteIndirectObjectReference(newContentObjectID); } else { objectContext.StartArray(); if (mEnsureContentEncapsulation) { newEncapsulatingObjectID = objectContext.GetInDirectObjectsRegistry().AllocateNewObjectID(); objectContext.WriteNewIndirectObjectReference(newEncapsulatingObjectID); } RefCountPtr<PDFObject> pageContent(copyingContext->GetSourceDocumentParser()->QueryDictionaryObject(pageDictionaryObject.GetPtr(), "Contents")); if (pageContent->GetType() == PDFObject::ePDFObjectStream) { // single content stream. must be a refrence which points to it PDFObjectCastPtr<PDFIndirectObjectReference> ref(pageDictionaryObject->QueryDirectObject("Contents")); objectContext.WriteIndirectObjectReference(ref->mObjectID, ref->mVersion); } else if (pageContent->GetType() == PDFObject::ePDFObjectArray) { PDFArray* anArray = (PDFArray*)pageContent.GetPtr(); // multiple content streams SingleValueContainerIterator<PDFObjectVector> refs = anArray->GetIterator(); PDFObjectCastPtr<PDFIndirectObjectReference> ref; while (refs.MoveNext()) { ref = refs.GetItem(); objectContext.WriteIndirectObjectReference(ref->mObjectID, ref->mVersion); } } else { // this basically means no content...or whatever. just ignore. } objectContext.WriteNewIndirectObjectReference(newContentObjectID); objectContext.EndArray(); objectContext.EndLine(); } // Write a new resource entry. copy all but the "XObject" entry, which needs to be modified. Just for kicks i'm keeping the original // form (either direct dictionary, or indirect object) ObjectIDType resourcesIndirect = 0; ObjectIDType newResourcesIndirect = 0; vector<string> formResourcesNames; modifiedPageObject->WriteKey("Resources"); if (!pageDictionaryObject->Exists("Resources")) { // check if there's inherited dict. if so - write directly as a modified version PDFObjectCastPtr<PDFDictionary> parentDict( pageDictionaryObject->Exists("Parent") ? copyingContext->GetSourceDocumentParser()->QueryDictionaryObject(pageDictionaryObject.GetPtr(), "Parent"): NULL); if(!parentDict) { formResourcesNames = WriteNewResourcesDictionary(objectContext); } else { PDFObjectCastPtr<PDFDictionary> inheritedResources = findInheritedResources(copyingContext->GetSourceDocumentParser(),parentDict.GetPtr()); if(!inheritedResources) { formResourcesNames = WriteNewResourcesDictionary(objectContext); } else { formResourcesNames = WriteModifiedResourcesDict(copyingContext->GetSourceDocumentParser(), inheritedResources.GetPtr(), objectContext, copyingContext); } } } else { // resources may be direct, or indirect. if direct, write as is, adding the new form xobject, otherwise wait till page object ends and write then PDFObjectCastPtr<PDFIndirectObjectReference> resourceDictRef(pageDictionaryObject->QueryDirectObject("Resources")); if (!resourceDictRef) { PDFObjectCastPtr<PDFDictionary> resourceDict(pageDictionaryObject->QueryDirectObject("Resources")); formResourcesNames = WriteModifiedResourcesDict(copyingContext->GetSourceDocumentParser(), resourceDict.GetPtr(), objectContext, copyingContext); } else { resourcesIndirect = resourceDictRef->mObjectID; // later will write a modified version of the resources dictionary, with the new form. // only modify the resources dict object if wasn't already modified (can happen when sharing resources dict between multiple pages). // in the case where it was alrady modified, create a new resources dictionary that's a copy, and use it instead, to avoid overwriting // the previous modification GetObjectWriteInformationResult res = objectContext.GetInDirectObjectsRegistry().GetObjectWriteInformation(resourcesIndirect); if (res.first && res.second.mIsDirty) { newResourcesIndirect = objectContext.GetInDirectObjectsRegistry().AllocateNewObjectID(); modifiedPageObject->WriteObjectReferenceValue(newResourcesIndirect); } else modifiedPageObject->WriteObjectReferenceValue(resourcesIndirect); } } objectContext.EndDictionary(modifiedPageObject); objectContext.EndIndirectObject(); if (resourcesIndirect != 0) { if (newResourcesIndirect != 0) objectContext.StartNewIndirectObject(newResourcesIndirect); else objectContext.StartModifiedIndirectObject(resourcesIndirect); PDFObjectCastPtr<PDFDictionary> resourceDict(copyingContext->GetSourceDocumentParser()->ParseNewObject(resourcesIndirect)); formResourcesNames = WriteModifiedResourcesDict(copyingContext->GetSourceDocumentParser(), resourceDict.GetPtr(), objectContext, copyingContext); objectContext.EndIndirectObject(); } // if required write encapsulation code, so that new stream is independent of graphic context of original PDFStream* newStream; PrimitiveObjectsWriter primitivesWriter; if (newEncapsulatingObjectID != 0) { objectContext.StartNewIndirectObject(newEncapsulatingObjectID); newStream = objectContext.StartPDFStream(); primitivesWriter.SetStreamForWriting(newStream->GetWriteStream()); primitivesWriter.WriteKeyword("q"); objectContext.EndPDFStream(newStream); } // last but not least, create the actual content stream object, placing the form objectContext.StartNewIndirectObject(newContentObjectID); newStream = objectContext.StartPDFStream(); primitivesWriter.SetStreamForWriting(newStream->GetWriteStream()); if (newEncapsulatingObjectID != 0) { primitivesWriter.WriteKeyword("Q"); } vector<string>::iterator it = formResourcesNames.begin(); for (; it != formResourcesNames.end(); ++it) { primitivesWriter.WriteKeyword("q"); primitivesWriter.WriteInteger(1); primitivesWriter.WriteInteger(0); primitivesWriter.WriteInteger(0); primitivesWriter.WriteInteger(1); primitivesWriter.WriteInteger(0); primitivesWriter.WriteInteger(0); primitivesWriter.WriteKeyword("cm"); primitivesWriter.WriteName(*it); primitivesWriter.WriteKeyword("Do"); primitivesWriter.WriteKeyword("Q"); } objectContext.EndPDFStream(newStream); } while (false); return status; }
EStatusCode IndirectObjectsReferenceRegistry::WriteState(ObjectsContext* inStateWriter,ObjectIDType inObjectID) { ObjectIDTypeList objects; inStateWriter->StartNewIndirectObject(inObjectID); DictionaryContext* myDictionary = inStateWriter->StartDictionary(); myDictionary->WriteKey("Type"); myDictionary->WriteNameValue("IndirectObjectsReferenceRegistry"); myDictionary->WriteKey("mObjectsWritesRegistry"); ObjectWriteInformationVector::iterator it = mObjectsWritesRegistry.begin(); inStateWriter->StartArray(); for(; it != mObjectsWritesRegistry.end(); ++it) { ObjectIDType objectWriteEntry = inStateWriter->GetInDirectObjectsRegistry().AllocateNewObjectID(); inStateWriter->WriteIndirectObjectReference(objectWriteEntry); objects.push_back(objectWriteEntry); } inStateWriter->EndArray(eTokenSeparatorEndLine); inStateWriter->EndDictionary(myDictionary); inStateWriter->EndIndirectObject(); ObjectIDTypeList::iterator itIDs = objects.begin(); it = mObjectsWritesRegistry.begin(); for(; it != mObjectsWritesRegistry.end(); ++it,++itIDs) { inStateWriter->StartNewIndirectObject(*itIDs); DictionaryContext* registryDictionary = inStateWriter->StartDictionary(); registryDictionary->WriteKey("Type"); registryDictionary->WriteNameValue("ObjectWriteInformation"); registryDictionary->WriteKey("mObjectWritten"); registryDictionary->WriteBooleanValue(it->mObjectWritten); if(it->mObjectWritten) { registryDictionary->WriteKey("mWritePosition"); registryDictionary->WriteIntegerValue(it->mWritePosition); } registryDictionary->WriteKey("mObjectReferenceType"); registryDictionary->WriteIntegerValue(it->mObjectReferenceType); inStateWriter->EndDictionary(registryDictionary); inStateWriter->EndIndirectObject(); } return PDFHummus::eSuccess; }
EStatusCode PDFWriter::Shutdown(const std::string& inStateFilePath) { EStatusCode status; do { StateWriter writer; status = writer.Start(inStateFilePath); if(status != eSuccess) { TRACE_LOG("PDFWriter::Shutdown, cant start state writing"); break; } ObjectIDType rootObjectID = writer.GetObjectsWriter()->StartNewIndirectObject(); DictionaryContext* pdfWriterDictionary = writer.GetObjectsWriter()->StartDictionary(); pdfWriterDictionary->WriteKey("Type"); pdfWriterDictionary->WriteNameValue("PDFWriter"); ObjectIDType objectsContextID = writer.GetObjectsWriter()->GetInDirectObjectsRegistry().AllocateNewObjectID(); ObjectIDType DocumentContextID = writer.GetObjectsWriter()->GetInDirectObjectsRegistry().AllocateNewObjectID(); pdfWriterDictionary->WriteKey("mObjectsContext"); pdfWriterDictionary->WriteNewObjectReferenceValue(objectsContextID); pdfWriterDictionary->WriteKey("mDocumentContext"); pdfWriterDictionary->WriteNewObjectReferenceValue(DocumentContextID); pdfWriterDictionary->WriteKey("mIsModified"); pdfWriterDictionary->WriteBooleanValue(mIsModified); if(mIsModified) { pdfWriterDictionary->WriteKey("mModifiedFileVersion"); pdfWriterDictionary->WriteIntegerValue(mModifiedFileVersion); } writer.GetObjectsWriter()->EndDictionary(pdfWriterDictionary); writer.GetObjectsWriter()->EndIndirectObject(); writer.SetRootObject(rootObjectID); status = mObjectsContext.WriteState(writer.GetObjectsWriter(),objectsContextID); if(status != eSuccess) break; status = mDocumentContext.WriteState(writer.GetObjectsWriter(),DocumentContextID); if(status != eSuccess) break; status = writer.Finish(); if(status != eSuccess) { TRACE_LOG("PDFWriter::Shutdown, cant finish state writing"); } }while(false); if(status != eSuccess) { mOutputFile.CloseFile(); TRACE_LOG("PDFWriter::Shutdown, Could not end PDF"); } else status = mOutputFile.CloseFile(); //ReleaseLog(); return status; }
EStatusCode DescendentFontWriter::WriteFont( ObjectIDType inDecendentObjectID, const string& inFontName, FreeTypeFaceWrapper& inFontInfo, const UIntAndGlyphEncodingInfoVector& inEncodedGlyphs, ObjectsContext* inObjectsContext, IDescendentFontWriter* inDescendentFontWriterHelper) { EStatusCode status = PDFHummus::eSuccess; FontDescriptorWriter fontDescriptorWriter; inObjectsContext->StartNewIndirectObject(inDecendentObjectID); mFontInfo = &inFontInfo; mObjectsContext = inObjectsContext; mCIDSetObjectID = 0; do { DictionaryContext* fontContext = inObjectsContext->StartDictionary(); // Type fontContext->WriteKey(scType); fontContext->WriteNameValue(scFont); // SubType fontContext->WriteKey(scSubtype); inDescendentFontWriterHelper->WriteSubTypeValue(fontContext); // BaseFont fontContext->WriteKey(scBaseFont); fontContext->WriteNameValue(inFontName); WriteWidths(inEncodedGlyphs,fontContext); // CIDSystemInfo fontContext->WriteKey(scCIDSystemInfo); ObjectIDType cidSystemInfoObjectID = mObjectsContext->GetInDirectObjectsRegistry().AllocateNewObjectID(); fontContext->WriteObjectReferenceValue(cidSystemInfoObjectID); // FontDescriptor fontContext->WriteKey(scFontDescriptor); ObjectIDType fontDescriptorObjectID = mObjectsContext->GetInDirectObjectsRegistry().AllocateNewObjectID(); fontContext->WriteObjectReferenceValue(fontDescriptorObjectID); // free dictionary end writing inDescendentFontWriterHelper->WriteAdditionalKeys(fontContext); status = inObjectsContext->EndDictionary(fontContext); if(status != PDFHummus::eSuccess) { TRACE_LOG("CFFANSIFontWriter::WriteFont, unexpected failure. Failed to end dictionary in font write."); break; } inObjectsContext->EndIndirectObject(); WriteCIDSystemInfo(cidSystemInfoObjectID); mWriterHelper = inDescendentFontWriterHelper; // save the helper pointer, to write the font program reference in the descriptor fontDescriptorWriter.WriteFontDescriptor(fontDescriptorObjectID,inFontName,&inFontInfo,inEncodedGlyphs,inObjectsContext,this); if(mCIDSetObjectID) // set by descriptor writer callback WriteCIDSet(inEncodedGlyphs); }while(false); return status; }
void FontDescriptorWriter::WriteFontDescriptor( ObjectIDType inFontDescriptorObjectID, const string& inFontPostscriptName, FreeTypeFaceWrapper* inFontInfo, const UIntAndGlyphEncodingInfoVector& inEncodedGlyphs, ObjectsContext* inObjectsContext, IFontDescriptorHelper* inDescriptorHelper) { DictionaryContext* fontDescriptorDictionary; inObjectsContext->StartNewIndirectObject(inFontDescriptorObjectID); fontDescriptorDictionary = inObjectsContext->StartDictionary(); // FontName fontDescriptorDictionary->WriteKey(scFontName); fontDescriptorDictionary->WriteNameValue(inFontPostscriptName); // FontFamily fontDescriptorDictionary->WriteKey(scFontFamily); fontDescriptorDictionary->WriteLiteralStringValue((*inFontInfo)->family_name); // FontStretch fontDescriptorDictionary->WriteKey(scFontStretch); fontDescriptorDictionary->WriteNameValue(scFontStretchLabels[inFontInfo->GetFontStretch()]); // FontWeight fontDescriptorDictionary->WriteKey(scFontWeight); fontDescriptorDictionary->WriteIntegerValue(inFontInfo->GetFontWeight()); // FontBBox fontDescriptorDictionary->WriteKey(scFontBBox); fontDescriptorDictionary->WriteRectangleValue( PDFRectangle( inFontInfo->GetInPDFMeasurements((*inFontInfo)->bbox.xMin), inFontInfo->GetInPDFMeasurements((*inFontInfo)->bbox.yMin), inFontInfo->GetInPDFMeasurements((*inFontInfo)->bbox.xMax), inFontInfo->GetInPDFMeasurements((*inFontInfo)->bbox.yMax))); // ItalicAngle fontDescriptorDictionary->WriteKey(scItalicAngle); fontDescriptorDictionary->WriteDoubleValue(inFontInfo->GetItalicAngle()); // Ascent fontDescriptorDictionary->WriteKey(scAscent); fontDescriptorDictionary->WriteIntegerValue(inFontInfo->GetInPDFMeasurements((*inFontInfo)->ascender)); // Descent fontDescriptorDictionary->WriteKey(scDescent); fontDescriptorDictionary->WriteIntegerValue(inFontInfo->GetInPDFMeasurements((*inFontInfo)->descender)); // CapHeight BoolAndFTShort result = inFontInfo->GetCapHeight(); if(result.first) { fontDescriptorDictionary->WriteKey(scCapHeight); fontDescriptorDictionary->WriteIntegerValue(result.second); } // XHeight result = inFontInfo->GetxHeight(); if(result.first) { fontDescriptorDictionary->WriteKey(scXHeight); fontDescriptorDictionary->WriteIntegerValue(result.second); } // StemV fontDescriptorDictionary->WriteKey(scStemV); fontDescriptorDictionary->WriteIntegerValue(inFontInfo->GetStemV()); // ChartSet writing (variants according to ANSI/CID) inDescriptorHelper->WriteCharSet(fontDescriptorDictionary,inObjectsContext,inFontInfo,inEncodedGlyphs); // Flags fontDescriptorDictionary->WriteKey(scFlags); fontDescriptorDictionary->WriteIntegerValue(CalculateFlags(inFontInfo,inEncodedGlyphs)); // font embedding [may not happen due to font embedding restrictions. helper is supposed to avoid reference as well] inDescriptorHelper->WriteFontFileReference(fontDescriptorDictionary,inObjectsContext); inObjectsContext->EndDictionary(fontDescriptorDictionary); inObjectsContext->EndIndirectObject(); }
EStatusCode CIDFontWriter::WriteFont(FreeTypeFaceWrapper& inFontInfo, WrittenFontRepresentation* inFontOccurrence, ObjectsContext* inObjectsContext, IDescendentFontWriter* inDescendentFontWriter) { EStatusCode status = PDFHummus::eSuccess; inObjectsContext->StartNewIndirectObject(inFontOccurrence->mWrittenObjectID); mFontInfo = &inFontInfo; mFontOccurrence = inFontOccurrence; mObjectsContext = inObjectsContext; do { DictionaryContext* fontContext = inObjectsContext->StartDictionary(); // Type fontContext->WriteKey(scType); fontContext->WriteNameValue(scFont); // SubType fontContext->WriteKey(scSubtype); fontContext->WriteNameValue(scType0); // BaseFont fontContext->WriteKey(scBaseFont); const char* postscriptFontName = FT_Get_Postscript_Name(inFontInfo); if(!postscriptFontName) { TRACE_LOG("CIDFontWriter::WriteFont, unexpected failure. no postscript font name for font"); status = PDFHummus::eFailure; break; } std::string subsetFontName = inObjectsContext->GenerateSubsetFontPrefix() + scPlus + postscriptFontName; fontContext->WriteNameValue(subsetFontName); WriteEncoding(fontContext); // DescendantFonts ObjectIDType descendantFontID = mObjectsContext->GetInDirectObjectsRegistry().AllocateNewObjectID(); fontContext->WriteKey(scDescendantFonts); mObjectsContext->StartArray(); mObjectsContext->WriteNewIndirectObjectReference(descendantFontID); mObjectsContext->EndArray(eTokenSeparatorEndLine); CalculateCharacterEncodingArray(); // put the charachter in the order of encoding, for the ToUnicode map // ToUnicode fontContext->WriteKey(scToUnicode); ObjectIDType toUnicodeMapObjectID = mObjectsContext->GetInDirectObjectsRegistry().AllocateNewObjectID(); fontContext->WriteNewObjectReferenceValue(toUnicodeMapObjectID); status = inObjectsContext->EndDictionary(fontContext); if(status != PDFHummus::eSuccess) { TRACE_LOG("CIDFontWriter::WriteFont, unexpected failure. Failed to end dictionary in font write."); break; } inObjectsContext->EndIndirectObject(); WriteToUnicodeMap(toUnicodeMapObjectID); // Write the descendant font status = inDescendentFontWriter->WriteFont(descendantFontID,subsetFontName,*mFontInfo,mCharactersVector,mObjectsContext); } while(false); return status; }