EStatusCode CFFEmbeddedFontWriter::AddDependentGlyphs(UIntVector& ioSubsetGlyphIDs) { EStatusCode status = PDFHummus::eSuccess; UIntSet glyphsSet; UIntVector::iterator it = ioSubsetGlyphIDs.begin(); bool hasCompositeGlyphs = false; for(;it != ioSubsetGlyphIDs.end() && PDFHummus::eSuccess == status; ++it) { bool localHasCompositeGlyphs; status = AddComponentGlyphs(*it,glyphsSet,localHasCompositeGlyphs); hasCompositeGlyphs |= localHasCompositeGlyphs; } if(hasCompositeGlyphs) { UIntSet::iterator itNewGlyphs; for(it = ioSubsetGlyphIDs.begin();it != ioSubsetGlyphIDs.end(); ++it) glyphsSet.insert(*it); ioSubsetGlyphIDs.clear(); for(itNewGlyphs = glyphsSet.begin(); itNewGlyphs != glyphsSet.end(); ++itNewGlyphs) ioSubsetGlyphIDs.push_back(*itNewGlyphs); sort(ioSubsetGlyphIDs.begin(),ioSubsetGlyphIDs.end()); } return status; }
EStatusCode CFFEmbeddedFontWriter::WriteCharsets(const UIntVector& inSubsetGlyphIDs, UShortVector* inCIDMapping) { // since this is a subset the chances that i'll get a defult charset are 0. // hence i'll always do some charset. and using format 0 !!1 UIntVector::const_iterator it = inSubsetGlyphIDs.begin(); ++it; // skip the 0 mCharsetPosition = mFontFileStream.GetCurrentPosition(); mPrimitivesWriter.WriteCard8(0); if(mIsCID && inCIDMapping) { UShortVector::const_iterator itCIDs = inCIDMapping->begin(); ++itCIDs; for(; it != inSubsetGlyphIDs.end(); ++it,++itCIDs) mPrimitivesWriter.WriteSID(*itCIDs); } else { // note that this also works for CIDs! cause in this case the SIDs are actually // CIDs for(; it != inSubsetGlyphIDs.end(); ++it) mPrimitivesWriter.WriteSID(mOpenTypeInput.mCFF.GetGlyphSID(0,*it)); } return mPrimitivesWriter.GetInternalState(); }
static UIntVector GetOrderedKeys(const UIntAndGlyphEncodingInfoVector& inMap) { UIntVector result; for(UIntAndGlyphEncodingInfoVector::const_iterator it = inMap.begin(); it != inMap.end(); ++it) result.push_back(it->first); sort(result.begin(),result.end()); return result; }
void Type1ToCFFEmbeddedFontWriter::TranslateFromFreeTypeToType1(FreeTypeFaceWrapper& inFontInfo, const UIntVector& inSubsetGlyphIDs, StringVector& outGlyphNames) { UIntVector::const_iterator it = inSubsetGlyphIDs.begin(); for(; it != inSubsetGlyphIDs.end(); ++it) outGlyphNames.push_back(inFontInfo.GetGlyphName(*it)); }
EStatusCode CFFEmbeddedFontWriter::WriteCharStrings(const UIntVector& inSubsetGlyphIDs) { /* 1. build the charstrings data, looping the glyphs charstrings and writing a flattened version of each charstring 2. write the charstring index based on offsets inside the data (size should be according to the max) 3. copy the data into the stream */ unsigned long* offsets = new unsigned long[inSubsetGlyphIDs.size() + 1]; MyStringBuf charStringsData; OutputStringBufferStream charStringsDataWriteStream(&charStringsData); CharStringType2Flattener charStringFlattener; UIntVector::const_iterator itGlyphs = inSubsetGlyphIDs.begin(); EStatusCode status = PDFHummus::eSuccess; do { unsigned short i=0; for(; itGlyphs != inSubsetGlyphIDs.end() && PDFHummus::eSuccess == status; ++itGlyphs,++i) { offsets[i] = (unsigned long)charStringsDataWriteStream.GetCurrentPosition(); status = charStringFlattener.WriteFlattenedGlyphProgram( 0, *itGlyphs, &(mOpenTypeInput.mCFF), &charStringsDataWriteStream); } if(status != PDFHummus::eSuccess) break; offsets[i] = (unsigned long)charStringsDataWriteStream.GetCurrentPosition(); charStringsData.pubseekoff(0,std::ios_base::beg); // write index section mCharStringPosition = mFontFileStream.GetCurrentPosition(); Byte sizeOfOffset = GetMostCompressedOffsetSize(offsets[i] + 1); mPrimitivesWriter.WriteCard16((unsigned short)inSubsetGlyphIDs.size()); mPrimitivesWriter.WriteOffSize(sizeOfOffset); mPrimitivesWriter.SetOffSize(sizeOfOffset); for(i=0;i<=inSubsetGlyphIDs.size();++i) mPrimitivesWriter.WriteOffset(offsets[i] + 1); // Write data InputStringBufferStream charStringsDataReadStream(&charStringsData); OutputStreamTraits streamCopier(&mFontFileStream); status = streamCopier.CopyToOutputStream(&charStringsDataReadStream); if(status != PDFHummus::eSuccess) break; }while(false); delete[] offsets; return status; }
EStatusCode TrueTypeEmbeddedFontWriter::WriteGlyf(const UIntVector& inSubsetGlyphIDs,unsigned long* inLocaTable) { // k. write the glyphs table. you only need to write the glyphs you are actually using. // while at it...update the locaTable TableEntry* tableEntry = mTrueTypeInput.GetTableEntry("glyf"); LongFilePositionType startTableOffset = mFontFileStream.GetCurrentPosition(); UIntVector::const_iterator it = inSubsetGlyphIDs.begin(); OutputStreamTraits streamCopier(&mFontFileStream); unsigned short glyphIndex,previousGlyphIndexEnd = 0; inLocaTable[0] = 0; EStatusCode status = eSuccess; for(;it != inSubsetGlyphIDs.end() && eSuccess == status; ++it) { glyphIndex = *it; if(glyphIndex >= mTrueTypeInput.mMaxp.NumGlyphs) { TRACE_LOG2("TrueTypeEmbeddedFontWriter::WriteGlyf, error, requested glyph index %ld is larger than the maximum glyph index for this font which is %ld. ",glyphIndex,mTrueTypeInput.mMaxp.NumGlyphs-1); status = eFailure; break; } for(unsigned short i= previousGlyphIndexEnd + 1; i<=glyphIndex;++i) inLocaTable[i] = inLocaTable[previousGlyphIndexEnd]; if(mTrueTypeInput.mGlyf[glyphIndex] != NULL) { mTrueTypeFile.GetInputStream()->SetPosition(tableEntry->Offset + mTrueTypeInput.mLoca[glyphIndex]); streamCopier.CopyToOutputStream(mTrueTypeFile.GetInputStream(), mTrueTypeInput.mLoca[(glyphIndex) + 1] - mTrueTypeInput.mLoca[glyphIndex]); } inLocaTable[glyphIndex + 1] = (unsigned long)(mFontFileStream.GetCurrentPosition() - startTableOffset); previousGlyphIndexEnd = glyphIndex + 1; } LongFilePositionType endOfTable = mFontFileStream.GetCurrentPosition(); mPrimitivesWriter.PadTo4(); LongFilePositionType endOfStream = mFontFileStream.GetCurrentPosition(); // write table entry data, which includes movement WriteTableEntryData(mGLYFEntryWritingOffset, startTableOffset, (unsigned long)(endOfTable - startTableOffset)); // restore position to end of stream mFontFileStream.SetPosition(endOfStream); return mPrimitivesWriter.GetInternalState(); }
void TrueTypeEmbeddedFontWriter::AddDependentGlyphs(UIntVector& ioSubsetGlyphIDs) { UIntSet glyphsSet; UIntVector::iterator it = ioSubsetGlyphIDs.begin(); bool hasCompositeGlyphs = false; for(;it != ioSubsetGlyphIDs.end(); ++it) hasCompositeGlyphs |= AddComponentGlyphs(*it,glyphsSet); if(hasCompositeGlyphs) { UIntSet::iterator itNewGlyphs; for(it = ioSubsetGlyphIDs.begin();it != ioSubsetGlyphIDs.end(); ++it) glyphsSet.insert(*it); ioSubsetGlyphIDs.clear(); for(itNewGlyphs = glyphsSet.begin(); itNewGlyphs != glyphsSet.end(); ++itNewGlyphs) ioSubsetGlyphIDs.push_back(*itNewGlyphs); sort(ioSubsetGlyphIDs.begin(),ioSubsetGlyphIDs.end()); } }
EStatusCode CFFEmbeddedFontWriter::WriteFDSelect(const UIntVector& inSubsetGlyphIDs,const FontDictInfoToByteMap& inNewFontDictsIndexes) { // always write format 3. cause at most cases the FD dicts count will be so low that it'd // take a bloody mircale for no repeats to occur. UIntVector::const_iterator itGlyphs = inSubsetGlyphIDs.begin(); mFDSelectPosition = mFontFileStream.GetCurrentPosition(); mPrimitivesWriter.WriteCard8(3); LongFilePositionType rangesCountPosition = mFontFileStream.GetCurrentPosition(); mPrimitivesWriter.WriteCard16(1); // temporary. will get back to this later unsigned short rangesCount = 1; Byte currentFD,newFD; unsigned short glyphIndex = 1; FontDictInfoToByteMap::const_iterator itNewIndex = inNewFontDictsIndexes.find(mOpenTypeInput.mCFF.mTopDictIndex[0].mFDSelect[*itGlyphs]); // k. seems like i probably just imagine exceptions here. i guess there must // be a proper FDSelect with FDs for all...so i'm defaulting to some 0 currentFD = (itNewIndex == inNewFontDictsIndexes.end() ? 0:itNewIndex->second); mPrimitivesWriter.WriteCard16(0); mPrimitivesWriter.WriteCard8(currentFD); ++itGlyphs; for(; itGlyphs != inSubsetGlyphIDs.end(); ++itGlyphs,++glyphIndex) { itNewIndex = inNewFontDictsIndexes.find(mOpenTypeInput.mCFF.mTopDictIndex[0].mFDSelect[*itGlyphs]); newFD = (itNewIndex == inNewFontDictsIndexes.end() ? 0:itNewIndex->second); if(newFD != currentFD) { currentFD = newFD; mPrimitivesWriter.WriteCard16(glyphIndex); mPrimitivesWriter.WriteCard8(currentFD); ++rangesCount; } } mPrimitivesWriter.WriteCard16((unsigned short)inSubsetGlyphIDs.size()); // go back to ranges count if not equal to what's already written if(rangesCount != 1) { LongFilePositionType currentPosition = mFontFileStream.GetCurrentPosition(); mFontFileStream.SetPosition(rangesCountPosition); mPrimitivesWriter.WriteCard16(rangesCount); mFontFileStream.SetPosition(currentPosition); } return mPrimitivesWriter.GetInternalState(); }
void CFFEmbeddedFontWriter::DetermineFDArrayIndexes(const UIntVector& inSubsetGlyphIDs,FontDictInfoToByteMap& outNewFontDictsIndexes) { UIntVector::const_iterator itGlyphs = inSubsetGlyphIDs.begin(); FontDictInfoSet fontDictInfos; for(; itGlyphs != inSubsetGlyphIDs.end(); ++itGlyphs) if(mOpenTypeInput.mCFF.mTopDictIndex[0].mFDSelect[*itGlyphs]) fontDictInfos.insert(mOpenTypeInput.mCFF.mTopDictIndex[0].mFDSelect[*itGlyphs]); FontDictInfoSet::iterator itFontInfos; Byte i=0; for(itFontInfos = fontDictInfos.begin(); itFontInfos != fontDictInfos.end(); ++itFontInfos,++i) outNewFontDictsIndexes.insert(FontDictInfoToByteMap::value_type(*itFontInfos,i)); }
EStatusCode CFFEmbeddedFontWriter::WriteEncodings(const UIntVector& inSubsetGlyphIDs) { // if it's a CID. don't bother with encodings (marks as 0) if(mIsCID) { mEncodingPosition = 0; return PDFHummus::eSuccess; } // not CID, write encoding, according to encoding values from the original font EncodingsInfo* encodingInfo = mOpenTypeInput.mCFF.mTopDictIndex[0].mEncoding; if(encodingInfo->mEncodingStart <= 1) { mEncodingPosition = encodingInfo->mEncodingStart; return PDFHummus::eSuccess; } else { // original font had custom encoding, let's subset it according to just the glyphs we // actually have. but cause i'm lazy i'll just do the first format. // figure out if we got supplements UIntVector::const_iterator it = inSubsetGlyphIDs.begin(); ByteAndUShortList supplements; for(; it != inSubsetGlyphIDs.end();++it) { // don't be confused! the supplements is by SID! not GID! unsigned short sid = mOpenTypeInput.mCFF.GetGlyphSID(0,*it); UShortToByteList::iterator itSupplements = encodingInfo->mSupplements.find(sid); if(itSupplements != encodingInfo->mSupplements.end()) { ByteList::iterator itMoreEncoding = itSupplements->second.begin(); for(; itMoreEncoding != itSupplements->second.end(); ++itMoreEncoding) supplements.push_back(ByteAndUShort(*itMoreEncoding,sid)); } } mEncodingPosition = mFontFileStream.GetCurrentPosition(); if(supplements.size() > 0) mPrimitivesWriter.WriteCard8(0x80); else mPrimitivesWriter.WriteCard8(0); // assuming that 0 is in the subset glyphs IDs, which does not require encoding // get the encodings count Byte encodingGlyphsCount = std::min((Byte)(inSubsetGlyphIDs.size()-1),encodingInfo->mEncodingsCount); mPrimitivesWriter.WriteCard8(encodingGlyphsCount); for(Byte i=0; i < encodingGlyphsCount;++i) { if(inSubsetGlyphIDs[i+1] < encodingInfo->mEncodingsCount) mPrimitivesWriter.WriteCard8(encodingInfo->mEncoding[inSubsetGlyphIDs[i+1]-1]); else mPrimitivesWriter.WriteCard8(0); } if(supplements.size() > 0) { mPrimitivesWriter.WriteCard8(Byte(supplements.size())); ByteAndUShortList::iterator itCollectedSupplements = supplements.begin(); for(; itCollectedSupplements != supplements.end(); ++itCollectedSupplements) { mPrimitivesWriter.WriteCard8(itCollectedSupplements->first); mPrimitivesWriter.WriteCard16(itCollectedSupplements->second); } } } return mPrimitivesWriter.GetInternalState(); }
EStatusCode CFFEmbeddedFontWriter::CreateCFFSubset( FreeTypeFaceWrapper& inFontInfo, const UIntVector& inSubsetGlyphIDs, UShortVector* inCIDMapping, const std::string& inSubsetFontName, bool& outNotEmbedded, MyStringBuf& outFontProgram) { EStatusCode status; do { status = mOpenTypeFile.OpenFile(inFontInfo.GetFontFilePath()); if(status != PDFHummus::eSuccess) { TRACE_LOG1("CFFEmbeddedFontWriter::CreateCFFSubset, cannot open type font file at %s",inFontInfo.GetFontFilePath().c_str()); break; } status = mOpenTypeInput.ReadOpenTypeFile(mOpenTypeFile.GetInputStream(),(unsigned short)inFontInfo.GetFontIndex()); if(status != PDFHummus::eSuccess) { TRACE_LOG("CFFEmbeddedFontWriter::CreateCFFSubset, failed to read true type file"); break; } if(mOpenTypeInput.GetOpenTypeFontType() != EOpenTypeCFF) { TRACE_LOG("CFFEmbeddedFontWriter::CreateCFFSubset, font file is not CFF, so there is an exceptions here. expecting CFFs only"); break; } // see if font may be embedded if(mOpenTypeInput.mOS2Exists && !FSType(mOpenTypeInput.mOS2.fsType).CanEmbed()) { outNotEmbedded = true; return PDFHummus::eSuccess; } else outNotEmbedded = false; UIntVector subsetGlyphIDs = inSubsetGlyphIDs; if(subsetGlyphIDs.front() != 0) // make sure 0 glyph is in subsetGlyphIDs.insert(subsetGlyphIDs.begin(),0); status = AddDependentGlyphs(subsetGlyphIDs); if(status != PDFHummus::eSuccess) { TRACE_LOG("CFFEmbeddedFontWriter::CreateCFFSubset, failed to add dependent glyphs"); break; } mIsCID = mOpenTypeInput.mCFF.mTopDictIndex[0].mTopDict.find(scROS) != mOpenTypeInput.mCFF.mTopDictIndex[0].mTopDict.end(); mFontFileStream.Assign(&outFontProgram); mPrimitivesWriter.SetStream(&mFontFileStream); status = WriteCFFHeader(); if(status != PDFHummus::eSuccess) { TRACE_LOG("CFFEmbeddedFontWriter::CreateCFFSubset, failed to write CFF header"); break; } status = WriteName(inSubsetFontName); if(status != PDFHummus::eSuccess) { TRACE_LOG("CFFEmbeddedFontWriter::CreateCFFSubset, failed to write CFF Name"); break; } status = WriteTopIndex(); if(status != PDFHummus::eSuccess) { TRACE_LOG("CFFEmbeddedFontWriter::CreateCFFSubset, failed to write Top Index"); break; } status = WriteStringIndex(); if(status != PDFHummus::eSuccess) { TRACE_LOG("CFFEmbeddedFontWriter::CreateCFFSubset, failed to write String Index"); break; } status = WriteGlobalSubrsIndex(); if(status != PDFHummus::eSuccess) { TRACE_LOG("CFFEmbeddedFontWriter::CreateCFFSubset, failed to write global subrs index"); break; } status = WriteEncodings(inSubsetGlyphIDs); if(status != PDFHummus::eSuccess) { TRACE_LOG("CFFEmbeddedFontWriter::CreateCFFSubset, failed to write encodings"); break; } status = WriteCharsets(inSubsetGlyphIDs,inCIDMapping); if(status != PDFHummus::eSuccess) { TRACE_LOG("CFFEmbeddedFontWriter::CreateCFFSubset, failed to write charstring"); break; } FontDictInfoToByteMap newFDIndexes; if(mIsCID) { DetermineFDArrayIndexes(inSubsetGlyphIDs,newFDIndexes); status = WriteFDSelect(inSubsetGlyphIDs,newFDIndexes); if(status != PDFHummus::eSuccess) break; } status = WriteCharStrings(inSubsetGlyphIDs); if(status != PDFHummus::eSuccess) { TRACE_LOG("CFFEmbeddedFontWriter::CreateCFFSubset, failed to write charstring"); break; } status = WritePrivateDictionary(); if(status != PDFHummus::eSuccess) { TRACE_LOG("CFFEmbeddedFontWriter::CreateCFFSubset, failed to write private"); break; } if(mIsCID) { status = WriteFDArray(inSubsetGlyphIDs,newFDIndexes); if(status != PDFHummus::eSuccess) break; } status = UpdateIndexesAtTopDict(); if(status != PDFHummus::eSuccess) { TRACE_LOG("CFFEmbeddedFontWriter::CreateCFFSubset, failed to update indexes"); break; } }while(false); mOpenTypeFile.CloseFile(); return status; }
EStatusCode Type1ToCFFEmbeddedFontWriter::CreateCFFSubset( FreeTypeFaceWrapper& inFontInfo, const UIntVector& inSubsetGlyphIDs, const std::string& inSubsetFontName, bool& outNotEmbedded, MyStringBuf& outFontProgram) { EStatusCode status; do { UIntVector subsetGlyphIDs = inSubsetGlyphIDs; StringVector subsetGlyphNames; if(subsetGlyphIDs.front() != 0) // make sure 0 glyph is in subsetGlyphIDs.insert(subsetGlyphIDs.begin(),0); status = mType1File.OpenFile(inFontInfo.GetFontFilePath()); if(status != PDFHummus::eSuccess) { TRACE_LOG1("Type1ToCFFEmbeddedFontWriter::CreateCFFSubset, cannot open Type 1 font file at %s",inFontInfo.GetFontFilePath().c_str()); break; } status = mType1Input.ReadType1File(mType1File.GetInputStream()); if(status != PDFHummus::eSuccess) { TRACE_LOG("Type1ToCFFEmbeddedFontWriter::CreateCFFSubset, failed to read Type 1 file"); break; } // see if font may be embedded if(mType1Input.mFontDictionary.FSTypeValid || mType1Input.mFontInfoDictionary.FSTypeValid) { if(!FSType( mType1Input.mFontInfoDictionary.FSTypeValid ? mType1Input.mFontInfoDictionary.fsType : mType1Input.mFontDictionary.fsType).CanEmbed()) { outNotEmbedded = true; return PDFHummus::eSuccess; } else outNotEmbedded = false; } else outNotEmbedded = false; // Found big gap between FreeType indexing and the way it's in the Type 1. obvioulsy due to encoding differences. // So i'm replacing the indexes of free type, with names...should be safer (also cleans up invalid glyph ids, in case // direct glyphs placement put them here) TranslateFromFreeTypeToType1(inFontInfo,subsetGlyphIDs,subsetGlyphNames); status = AddDependentGlyphs(subsetGlyphNames); if(status != PDFHummus::eSuccess) { TRACE_LOG("Type1ToCFFEmbeddedFontWriter::CreateCFFSubset, failed to add dependent glyphs"); break; } mFontFileStream.Assign(&outFontProgram); mPrimitivesWriter.SetStream(&mFontFileStream); status = WriteCFFHeader(); if(status != PDFHummus::eSuccess) { TRACE_LOG("Type1ToCFFEmbeddedFontWriter::CreateCFFSubset, failed to write CFF header"); break; } status = WriteName(inSubsetFontName); if(status != PDFHummus::eSuccess) { TRACE_LOG("Type1ToCFFEmbeddedFontWriter::CreateCFFSubset, failed to write CFF Name"); break; } status = WriteTopIndex(); if(status != PDFHummus::eSuccess) { TRACE_LOG("Type1ToCFFEmbeddedFontWriter::CreateCFFSubset, failed to write Top Index"); break; } // prepraring charset happens here, so that any added strings to the string index will happen...before // the index is written PrepareCharSetArray(subsetGlyphNames); status = WriteStringIndex(); if(status != PDFHummus::eSuccess) { TRACE_LOG("Type1ToCFFEmbeddedFontWriter::CreateCFFSubset, failed to write String Index"); break; } status = WriteGlobalSubrsIndex(); if(status != PDFHummus::eSuccess) { TRACE_LOG("Type1ToCFFEmbeddedFontWriter::CreateCFFSubset, failed to write global subrs index"); break; } status = WriteEncodings(subsetGlyphNames); if(status != PDFHummus::eSuccess) { TRACE_LOG("Type1ToCFFEmbeddedFontWriter::CreateCFFSubset, failed to write encodings"); break; } status = WriteCharsets(subsetGlyphNames); if(status != PDFHummus::eSuccess) { TRACE_LOG("Type1ToCFFEmbeddedFontWriter::CreateCFFSubset, failed to write charstring"); break; } status = WriteCharStrings(subsetGlyphNames); if(status != PDFHummus::eSuccess) { TRACE_LOG("Type1ToCFFEmbeddedFontWriter::CreateCFFSubset, failed to write charstring"); break; } status = WritePrivateDictionary(); if(status != PDFHummus::eSuccess) { TRACE_LOG("Type1ToCFFEmbeddedFontWriter::CreateCFFSubset, failed to write private"); break; } status = UpdateIndexesAtTopDict(); if(status != PDFHummus::eSuccess) { TRACE_LOG("Type1ToCFFEmbeddedFontWriter::CreateCFFSubset, failed to update indexes"); break; } }while(false); mType1File.CloseFile(); FreeTemporaryStructs(); return status; }