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; }
void DescendentFontWriter::WriteCIDSet(const UIntAndGlyphEncodingInfoVector& inEncodedGlyphs) { mObjectsContext->StartNewIndirectObject(mCIDSetObjectID); PDFStream* pdfStream = mObjectsContext->StartPDFStream(); IByteWriter* cidSetWritingContext = pdfStream->GetWriteStream(); Byte buffer; UIntAndGlyphEncodingInfoVector::const_iterator it = inEncodedGlyphs.begin(); unsigned int upperLimit = inEncodedGlyphs.back().first; for(unsigned int i=0; i < upperLimit; i+=8) { buffer = (it->first == i) ? 1:0; if(it->first == i) ++it; for(unsigned int j=1;j<8;++j) { buffer = buffer << 1; if(it != inEncodedGlyphs.end() && (it->first == i + j)) { buffer |= 1; ++it; if(it == inEncodedGlyphs.end()) break; } } cidSetWritingContext->Write(&buffer,1); } mObjectsContext->EndPDFStream(pdfStream); delete pdfStream; }
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; }
void CIDFontWriter::WriteToUnicodeMap(ObjectIDType inToUnicodeMap) { mObjectsContext->StartNewIndirectObject(inToUnicodeMap); PDFStream* pdfStream = mObjectsContext->StartPDFStream(); IByteWriter* cmapWriteContext = pdfStream->GetWriteStream(); PrimitiveObjectsWriter primitiveWriter(cmapWriteContext); unsigned long i = 1; UIntAndGlyphEncodingInfoVector::iterator it = mCharactersVector.begin() + 1; // skip 0 glyph unsigned long vectorSize = (unsigned long)mCharactersVector.size() - 1; // cause 0 is not there cmapWriteContext->Write((const Byte*)scCmapHeader,strlen(scCmapHeader)); primitiveWriter.WriteHexString(scFourByteRangeStart); primitiveWriter.WriteHexString(scFourByteRangeEnd,eTokenSeparatorEndLine); cmapWriteContext->Write((const Byte*)scEndCodeSpaceRange,strlen(scEndCodeSpaceRange)); if(vectorSize < 100) primitiveWriter.WriteInteger(vectorSize); else primitiveWriter.WriteInteger(100); primitiveWriter.WriteKeyword(scBeginBFChar); WriteGlyphEntry(cmapWriteContext,it->second.mEncodedCharacter,it->second.mUnicodeCharacters); ++it; for(; it != mCharactersVector.end(); ++it,++i) { if(i % 100 == 0) { primitiveWriter.WriteKeyword(scEndBFChar); if(vectorSize - i < 100) primitiveWriter.WriteInteger(vectorSize - i); else primitiveWriter.WriteInteger(100); primitiveWriter.WriteKeyword(scBeginBFChar); } WriteGlyphEntry(cmapWriteContext,it->second.mEncodedCharacter,it->second.mUnicodeCharacters); } primitiveWriter.WriteKeyword(scEndBFChar); cmapWriteContext->Write((const Byte*)scCmapFooter,strlen(scCmapFooter)); mObjectsContext->EndPDFStream(pdfStream); delete pdfStream; }
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::WriteXrefStream(DictionaryContext* inDictionaryContext) { // k. complement input dictionary with the relevant entries - W and Index // then continue with a regular stream, forced to have "length" as direct object // write Index entry inDictionaryContext->WriteKey("Index"); StartArray(); ObjectIDType startID = 0; ObjectIDType firstIDNotInRange; while(startID < mReferencesRegistry.GetObjectsCount()) { firstIDNotInRange = startID; // look for first ID that does not require update [for first version of PDF...it will be the end] while(firstIDNotInRange < mReferencesRegistry.GetObjectsCount() && mReferencesRegistry.GetNthObjectReference(firstIDNotInRange).mIsDirty) ++firstIDNotInRange; // write section header mPrimitiveWriter.WriteInteger(startID); mPrimitiveWriter.WriteInteger(firstIDNotInRange - startID); startID = firstIDNotInRange; // now promote startID to the next object to update while(startID < mReferencesRegistry.GetObjectsCount() && !mReferencesRegistry.GetNthObjectReference(startID).mIsDirty) ++startID; } EndArray(); EndLine(); // write W entry, which is going to be 1 sizeof(long long) and sizeof(unsigned long), per the types i'm using size_t typeSize = 1; size_t locationSize = sizeof(LongFilePositionType); size_t generationSize = sizeof(unsigned long); inDictionaryContext->WriteKey("W"); StartArray(); WriteInteger(typeSize); WriteInteger(locationSize); WriteInteger(generationSize); EndArray(); EndLine(); // start the xref stream itself PDFStream* aStream = StartPDFStream(inDictionaryContext,true); // now write the table data itself EStatusCode status = eSuccess; ObjectIDType nextFreeObject = 0; do { for(ObjectIDType i = 0; i < mReferencesRegistry.GetObjectsCount() && eSuccess == status;++i) { if(!mReferencesRegistry.GetNthObjectReference(i).mIsDirty) continue; const ObjectWriteInformation& objectReference = mReferencesRegistry.GetNthObjectReference(i); if(objectReference.mObjectReferenceType == ObjectWriteInformation::Used) { // used object if(objectReference.mObjectWritten) { WriteXrefNumber(aStream->GetWriteStream(),1,typeSize); WriteXrefNumber(aStream->GetWriteStream(),objectReference.mWritePosition,locationSize); WriteXrefNumber(aStream->GetWriteStream(),objectReference.mGenerationNumber,generationSize); } else { // object not written. at this point this should not happen, and indicates a failure status = PDFHummus::eFailure; TRACE_LOG1("ObjectsContext::WriteXrefStream, Unexpected Failure. Object of ID = %ld was not registered as written. probably means it was not written",i); } } else { // free object ++nextFreeObject; // look for next dirty & free object, to be the next item of linked list while(nextFreeObject < mReferencesRegistry.GetObjectsCount() && (!mReferencesRegistry.GetNthObjectReference(nextFreeObject).mIsDirty || mReferencesRegistry.GetNthObjectReference(nextFreeObject).mObjectReferenceType != ObjectWriteInformation::Free)) ++nextFreeObject; // if reached end of list, then link back to head - 0 if(nextFreeObject == mReferencesRegistry.GetObjectsCount()) nextFreeObject = 0; WriteXrefNumber(aStream->GetWriteStream(),0,typeSize); WriteXrefNumber(aStream->GetWriteStream(),nextFreeObject,locationSize); WriteXrefNumber(aStream->GetWriteStream(),objectReference.mGenerationNumber,generationSize); } } if(status != eSuccess) break; // end the stream and g'bye EndPDFStream(aStream); } 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; }