1 /*
2    Source File : CFFEmbeddedFontWriter.cpp
3 
4 
5    Copyright 2011 Gal Kahana PDFWriter
6 
7    Licensed under the Apache License, Version 2.0 (the "License");
8    you may not use this file except in compliance with the License.
9    You may obtain a copy of the License at
10 
11        http://www.apache.org/licenses/LICENSE-2.0
12 
13    Unless required by applicable law or agreed to in writing, software
14    distributed under the License is distributed on an "AS IS" BASIS,
15    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16    See the License for the specific language governing permissions and
17    limitations under the License.
18 
19 
20 */
21 #include "CFFEmbeddedFontWriter.h"
22 #include "ObjectsContext.h"
23 #include "InputStringBufferStream.h"
24 #include "OutputStreamTraits.h"
25 #include "PDFStream.h"
26 #include "Trace.h"
27 #include "FreeTypeFaceWrapper.h"
28 #include "DictionaryContext.h"
29 #include "IByteReaderWithPosition.h"
30 #include "CharStringType2Flattener.h"
31 #include "FSType.h"
32 
33 #include <algorithm>
34 #include <utility>
35 #include <list>
36 
37 
38 using namespace PDFHummus;
39 
CFFEmbeddedFontWriter(void)40 CFFEmbeddedFontWriter::CFFEmbeddedFontWriter(void)
41 {
42 }
43 
~CFFEmbeddedFontWriter(void)44 CFFEmbeddedFontWriter::~CFFEmbeddedFontWriter(void)
45 {
46 }
47 
48 static const std::string scSubtype = "Subtype";
49 
WriteEmbeddedFont(FreeTypeFaceWrapper & inFontInfo,const UIntVector & inSubsetGlyphIDs,const std::string & inFontFile3SubType,const std::string & inSubsetFontName,ObjectsContext * inObjectsContext,ObjectIDType & outEmbeddedFontObjectID)50 EStatusCode CFFEmbeddedFontWriter::WriteEmbeddedFont(
51 								FreeTypeFaceWrapper& inFontInfo,
52 								const UIntVector& inSubsetGlyphIDs,
53 								const std::string& inFontFile3SubType,
54 								const std::string& inSubsetFontName,
55 								ObjectsContext* inObjectsContext,
56 								ObjectIDType& outEmbeddedFontObjectID)
57 {
58 	return WriteEmbeddedFont(inFontInfo,inSubsetGlyphIDs,inFontFile3SubType,inSubsetFontName,inObjectsContext,NULL,outEmbeddedFontObjectID);
59 }
60 
WriteEmbeddedFont(FreeTypeFaceWrapper & inFontInfo,const UIntVector & inSubsetGlyphIDs,const std::string & inFontFile3SubType,const std::string & inSubsetFontName,ObjectsContext * inObjectsContext,UShortVector * inCIDMapping,ObjectIDType & outEmbeddedFontObjectID)61 EStatusCode CFFEmbeddedFontWriter::WriteEmbeddedFont(
62 	FreeTypeFaceWrapper& inFontInfo,
63 	const UIntVector& inSubsetGlyphIDs,
64 	const std::string& inFontFile3SubType,
65 	const std::string& inSubsetFontName,
66 	ObjectsContext* inObjectsContext,
67 	UShortVector* inCIDMapping,
68 	ObjectIDType& outEmbeddedFontObjectID)
69 {
70 	MyStringBuf rawFontProgram;
71 	bool notEmbedded;
72 		// as oppose to true type, the reason for using a memory stream here is mainly peformance - i don't want to start
73 		// setting file pointers and move in a file stream
74 	EStatusCode status;
75 
76 	do
77 	{
78 		status = CreateCFFSubset(inFontInfo,inSubsetGlyphIDs,inCIDMapping,inSubsetFontName,notEmbedded,rawFontProgram);
79 		if(status != PDFHummus::eSuccess)
80 		{
81 			TRACE_LOG("CFFEmbeddedFontWriter::WriteEmbeddedFont, failed to write embedded font program");
82 			break;
83 		}
84 
85 		if(notEmbedded)
86 		{
87 			// can't embed. mark succesful, and go back empty
88 			outEmbeddedFontObjectID = 0;
89 			TRACE_LOG("CFFEmbeddedFontWriter::WriteEmbeddedFont, font may not be embedded. so not embedding");
90 			return PDFHummus::eSuccess;
91 		}
92 
93 		outEmbeddedFontObjectID = inObjectsContext->StartNewIndirectObject();
94 
95 		DictionaryContext* fontProgramDictionaryContext = inObjectsContext->StartDictionary();
96 
97 		rawFontProgram.pubseekoff(0,std::ios_base::beg);
98 
99 		fontProgramDictionaryContext->WriteKey(scSubtype);
100 		fontProgramDictionaryContext->WriteNameValue(inFontFile3SubType);
101 		PDFStream* pdfStream = inObjectsContext->StartPDFStream(fontProgramDictionaryContext);
102 
103 
104 		// now copy the created font program to the output stream
105 		InputStringBufferStream fontProgramStream(&rawFontProgram);
106 		OutputStreamTraits streamCopier(pdfStream->GetWriteStream());
107 		status = streamCopier.CopyToOutputStream(&fontProgramStream);
108 		if(status != PDFHummus::eSuccess)
109 		{
110 			TRACE_LOG("CFFEmbeddedFontWriter::WriteEmbeddedFont, failed to copy font program into pdf stream");
111 			break;
112 		}
113 
114 
115 		inObjectsContext->EndPDFStream(pdfStream);
116 		delete pdfStream;
117 	}while(false);
118 
119 	return status;
120 }
121 
122 static const unsigned short scROS = 0xC1E;
CreateCFFSubset(FreeTypeFaceWrapper & inFontInfo,const UIntVector & inSubsetGlyphIDs,UShortVector * inCIDMapping,const std::string & inSubsetFontName,bool & outNotEmbedded,MyStringBuf & outFontProgram)123 EStatusCode CFFEmbeddedFontWriter::CreateCFFSubset(
124 									FreeTypeFaceWrapper& inFontInfo,
125 									const UIntVector& inSubsetGlyphIDs,
126 									UShortVector* inCIDMapping,
127 									const std::string& inSubsetFontName,
128 									bool& outNotEmbedded,
129 									MyStringBuf& outFontProgram)
130 {
131 	EStatusCode status;
132 
133 	do
134 	{
135 
136 		status = mOpenTypeFile.OpenFile(inFontInfo.GetFontFilePath());
137 		if(status != PDFHummus::eSuccess)
138 		{
139 			TRACE_LOG1("CFFEmbeddedFontWriter::CreateCFFSubset, cannot open type font file at %s",inFontInfo.GetFontFilePath().c_str());
140 			break;
141 		}
142 
143 		status = mOpenTypeInput.ReadOpenTypeFile(mOpenTypeFile.GetInputStream(),(unsigned short)inFontInfo.GetFontIndex());
144 		if(status != PDFHummus::eSuccess)
145 		{
146 			TRACE_LOG("CFFEmbeddedFontWriter::CreateCFFSubset, failed to read true type file");
147 			break;
148 		}
149 
150 		if(mOpenTypeInput.GetOpenTypeFontType() != EOpenTypeCFF)
151 		{
152 			TRACE_LOG("CFFEmbeddedFontWriter::CreateCFFSubset, font file is not CFF, so there is an exceptions here. expecting CFFs only");
153 			break;
154 		}
155 
156 		// see if font may be embedded
157 		if(mOpenTypeInput.mOS2Exists && !FSType(mOpenTypeInput.mOS2.fsType).CanEmbed())
158 		{
159 			outNotEmbedded = true;
160 			return PDFHummus::eSuccess;
161 		}
162 		else
163 			outNotEmbedded = false;
164 
165 		UIntVector subsetGlyphIDs = inSubsetGlyphIDs;
166 		if(subsetGlyphIDs.front() != 0) // make sure 0 glyph is in
167 			subsetGlyphIDs.insert(subsetGlyphIDs.begin(),0);
168 
169 		status = AddDependentGlyphs(subsetGlyphIDs);
170 		if(status != PDFHummus::eSuccess)
171 		{
172 			TRACE_LOG("CFFEmbeddedFontWriter::CreateCFFSubset, failed to add dependent glyphs");
173 			break;
174 		}
175 
176 		mIsCID = mOpenTypeInput.mCFF.mTopDictIndex[0].mTopDict.find(scROS) !=
177 					mOpenTypeInput.mCFF.mTopDictIndex[0].mTopDict.end();
178 
179 		mFontFileStream.Assign(&outFontProgram);
180 		mPrimitivesWriter.SetStream(&mFontFileStream);
181 
182 		status = WriteCFFHeader();
183 		if(status != PDFHummus::eSuccess)
184 		{
185 			TRACE_LOG("CFFEmbeddedFontWriter::CreateCFFSubset, failed to write CFF header");
186 			break;
187 		}
188 
189 		status = WriteName(inSubsetFontName);
190 		if(status != PDFHummus::eSuccess)
191 		{
192 			TRACE_LOG("CFFEmbeddedFontWriter::CreateCFFSubset, failed to write CFF Name");
193 			break;
194 		}
195 
196 		status = WriteTopIndex();
197 		if(status != PDFHummus::eSuccess)
198 		{
199 			TRACE_LOG("CFFEmbeddedFontWriter::CreateCFFSubset, failed to write Top Index");
200 			break;
201 		}
202 
203 		status = WriteStringIndex();
204 		if(status != PDFHummus::eSuccess)
205 		{
206 			TRACE_LOG("CFFEmbeddedFontWriter::CreateCFFSubset, failed to write String Index");
207 			break;
208 		}
209 
210 		status = WriteGlobalSubrsIndex();
211 		if(status != PDFHummus::eSuccess)
212 		{
213 			TRACE_LOG("CFFEmbeddedFontWriter::CreateCFFSubset, failed to write global subrs index");
214 			break;
215 		}
216 
217 		status = WriteEncodings(inSubsetGlyphIDs);
218 		if(status != PDFHummus::eSuccess)
219 		{
220 			TRACE_LOG("CFFEmbeddedFontWriter::CreateCFFSubset, failed to write encodings");
221 			break;
222 		}
223 
224 		status = WriteCharsets(inSubsetGlyphIDs,inCIDMapping);
225 		if(status != PDFHummus::eSuccess)
226 		{
227 			TRACE_LOG("CFFEmbeddedFontWriter::CreateCFFSubset, failed to write charstring");
228 			break;
229 		}
230 
231 		FontDictInfoToByteMap newFDIndexes;
232 
233 		if(mIsCID)
234 		{
235 			DetermineFDArrayIndexes(inSubsetGlyphIDs,newFDIndexes);
236 			status = WriteFDSelect(inSubsetGlyphIDs,newFDIndexes);
237 			if(status != PDFHummus::eSuccess)
238 				break;
239 		}
240 
241 
242 		status = WriteCharStrings(inSubsetGlyphIDs);
243 		if(status != PDFHummus::eSuccess)
244 		{
245 			TRACE_LOG("CFFEmbeddedFontWriter::CreateCFFSubset, failed to write charstring");
246 			break;
247 		}
248 
249 		status = WritePrivateDictionary();
250 		if(status != PDFHummus::eSuccess)
251 		{
252 			TRACE_LOG("CFFEmbeddedFontWriter::CreateCFFSubset, failed to write private");
253 			break;
254 		}
255 
256 		if(mIsCID)
257 		{
258 			status = WriteFDArray(inSubsetGlyphIDs,newFDIndexes);
259 			if(status != PDFHummus::eSuccess)
260 				break;
261 		}
262 
263 		status = UpdateIndexesAtTopDict();
264 		if(status != PDFHummus::eSuccess)
265 		{
266 			TRACE_LOG("CFFEmbeddedFontWriter::CreateCFFSubset, failed to update indexes");
267 			break;
268 		}
269 	}while(false);
270 
271 	mOpenTypeFile.CloseFile();
272 	return status;
273 }
274 
AddDependentGlyphs(UIntVector & ioSubsetGlyphIDs)275 EStatusCode CFFEmbeddedFontWriter::AddDependentGlyphs(UIntVector& ioSubsetGlyphIDs)
276 {
277 	EStatusCode status = PDFHummus::eSuccess;
278 	UIntSet glyphsSet;
279 	UIntVector::iterator it = ioSubsetGlyphIDs.begin();
280 	bool hasCompositeGlyphs = false;
281 
282 	for(;it != ioSubsetGlyphIDs.end() && PDFHummus::eSuccess == status; ++it)
283 	{
284 		bool localHasCompositeGlyphs;
285 		status = AddComponentGlyphs(*it,glyphsSet,localHasCompositeGlyphs);
286 		hasCompositeGlyphs |= localHasCompositeGlyphs;
287 	}
288 
289 	if(hasCompositeGlyphs)
290 	{
291 		UIntSet::iterator itNewGlyphs;
292 
293 		for(it = ioSubsetGlyphIDs.begin();it != ioSubsetGlyphIDs.end(); ++it)
294 			glyphsSet.insert(*it);
295 
296 		ioSubsetGlyphIDs.clear();
297 		for(itNewGlyphs = glyphsSet.begin(); itNewGlyphs != glyphsSet.end(); ++itNewGlyphs)
298 			ioSubsetGlyphIDs.push_back(*itNewGlyphs);
299 
300 		sort(ioSubsetGlyphIDs.begin(),ioSubsetGlyphIDs.end());
301 	}
302 	return status;
303 }
304 
AddComponentGlyphs(unsigned int inGlyphID,UIntSet & ioComponents,bool & outFoundComponents)305 EStatusCode CFFEmbeddedFontWriter::AddComponentGlyphs(unsigned int inGlyphID,UIntSet& ioComponents,bool &outFoundComponents)
306 {
307 	CharString2Dependencies dependencies;
308 	EStatusCode status = mOpenTypeInput.mCFF.CalculateDependenciesForCharIndex(0,inGlyphID,dependencies);
309 
310 	if(PDFHummus::eSuccess == status && dependencies.mCharCodes.size() !=0)
311 	{
312 		UShortSet::iterator it = dependencies.mCharCodes.begin();
313 		for(; it != dependencies.mCharCodes.end() && PDFHummus::eSuccess == status; ++it)
314 		{
315 			bool dummyFound;
316 			ioComponents.insert(*it);
317 			status = AddComponentGlyphs(*it,ioComponents,dummyFound);
318 		}
319 		outFoundComponents = true;
320 	}
321 	else
322 		outFoundComponents = false;
323 	return status;
324 }
325 
WriteCFFHeader()326 EStatusCode CFFEmbeddedFontWriter::WriteCFFHeader()
327 {
328 	 // i'm just gonna copy the header of the original CFF
329 	 // content.
330 	 // One thing i never got - OffSize does not seem to be important.
331 	 // all offeet references to (0) are dictionary items (like in Top Dict),
332 	 // and reading them follows the Integer operand rules. so why signify their size.
333 	 // it's probably even not true, cause i guess font writers write integers using the most
334 	 // compressed method, so if the number is small they'll use less bytes, and if large more.
335 	 // so i don't get it. hope it won't screw up my implementation. in any case, for the sake of a single pass.
336 	 // i'll probably just set it to something.
337 
338 	OutputStreamTraits streamCopier(&mFontFileStream);
339 	mOpenTypeFile.GetInputStream()->SetPosition(mOpenTypeInput.mCFF.mCFFOffset);
340 	return streamCopier.CopyToOutputStream(mOpenTypeFile.GetInputStream(),mOpenTypeInput.mCFF.mHeader.hdrSize);
341 }
342 
WriteName(const std::string & inSubsetFontName)343 EStatusCode CFFEmbeddedFontWriter::WriteName(const std::string& inSubsetFontName)
344 {
345 	// get the first name from the name table, and write it here
346 
347 	std::string fontName = inSubsetFontName.size() == 0 ? mOpenTypeInput.mCFF.mName.front() : inSubsetFontName;
348 
349 	Byte sizeOfOffset = GetMostCompressedOffsetSize((unsigned long)fontName.size() + 1);
350 
351 	mPrimitivesWriter.WriteCard16(1);
352 	mPrimitivesWriter.WriteOffSize(sizeOfOffset);
353 	mPrimitivesWriter.SetOffSize(sizeOfOffset);
354 	mPrimitivesWriter.WriteOffset(1);
355 	mPrimitivesWriter.WriteOffset((unsigned long)fontName.size() + 1);
356 	mPrimitivesWriter.Write((const Byte*)fontName.c_str(),fontName.size());
357 
358 	return mPrimitivesWriter.GetInternalState();
359 }
360 
GetMostCompressedOffsetSize(unsigned long inOffset)361 Byte CFFEmbeddedFontWriter::GetMostCompressedOffsetSize(unsigned long inOffset)
362 {
363 	if(inOffset < 256)
364 		return 1;
365 
366 	if(inOffset < 65536)
367 		return 2;
368 
369 	if(inOffset < 1<<24)
370 		return 3;
371 
372 	return 4;
373 }
374 
WriteTopIndex()375 EStatusCode CFFEmbeddedFontWriter::WriteTopIndex()
376 {
377 	/*
378 		what do i have to do:
379 		- write the top dictionary to a separate segment
380 			- make sure to write the ROS variable first, if one exists.
381 			- make sure to avoid writing any of the offset variables.
382 			- leave placeholders for any of the offset variables. make them maximum size. note
383 				to leave items for fdarray and fdselect only if required being a CID
384 			- be aware of the placeholders locations relative to the beginning of the segment
385 		- calculate the size of the segment
386 		- write the appropriate index header
387 		- write the segment
388 		- adjust the placeholders offset relative to the beginning of the file.
389 
390 	*/
391 	EStatusCode status;
392 	MyStringBuf topDictSegment;
393 
394 	do
395 	{
396 		status = WriteTopDictSegment(topDictSegment);
397 		if(status != PDFHummus::eSuccess)
398 			break;
399 
400 
401 		// write index section
402 		Byte sizeOfOffset = GetMostCompressedOffsetSize((unsigned long)topDictSegment.GetCurrentWritePosition() + 1);
403 
404 		mPrimitivesWriter.WriteCard16(1);
405 		mPrimitivesWriter.WriteOffSize(sizeOfOffset);
406 		mPrimitivesWriter.SetOffSize(sizeOfOffset);
407 		mPrimitivesWriter.WriteOffset(1);
408 		mPrimitivesWriter.WriteOffset((unsigned long)topDictSegment.GetCurrentWritePosition() + 1);
409 
410 		topDictSegment.pubseekoff(0,std::ios_base::beg);
411 
412 		LongFilePositionType topDictDataOffset = mFontFileStream.GetCurrentPosition();
413 
414 		// Write data
415 		InputStringBufferStream topDictStream(&topDictSegment);
416 		OutputStreamTraits streamCopier(&mFontFileStream);
417 		status = streamCopier.CopyToOutputStream(&topDictStream);
418 		if(status != PDFHummus::eSuccess)
419 			break;
420 
421 		// Adjust position locators for important placeholders
422 		mCharsetPlaceHolderPosition+=topDictDataOffset;
423 		mEncodingPlaceHolderPosition+=topDictDataOffset;
424 		mCharstringsPlaceHolderPosition+=topDictDataOffset;
425 		mPrivatePlaceHolderPosition+=topDictDataOffset;
426 		mFDArrayPlaceHolderPosition+=topDictDataOffset;
427 		mFDSelectPlaceHolderPosition+=topDictDataOffset;
428 
429 	}while(false);
430 
431 	if(status != PDFHummus::eSuccess)
432 		return status;
433 	else
434 		return mPrimitivesWriter.GetInternalState();
435 	return status;
436 }
437 
438 #define N_STD_STRINGS 391
439 
440 static const unsigned short scCharset = 15;
441 static const unsigned short scEncoding = 16;
442 static const unsigned short scCharstrings = 17;
443 static const unsigned short scPrivate = 18;
444 static const unsigned short scFDArray = 0xC24;
445 static const unsigned short scFDSelect = 0xC25;
446 static const unsigned short scEmbeddedPostscript = 0xC15;
WriteTopDictSegment(MyStringBuf & ioTopDictSegment)447 EStatusCode CFFEmbeddedFontWriter::WriteTopDictSegment(MyStringBuf& ioTopDictSegment)
448 {
449 	OutputStringBufferStream topDictStream(&ioTopDictSegment);
450 	CFFPrimitiveWriter dictPrimitiveWriter;
451 	UShortToDictOperandListMap::iterator itROS;
452 	UShortToDictOperandListMap::iterator it;
453 	dictPrimitiveWriter.SetStream(&topDictStream);
454 
455 	UShortToDictOperandListMap& originalTopDictRef = mOpenTypeInput.mCFF.mTopDictIndex[0].mTopDict;
456 
457 	itROS = originalTopDictRef.find(scROS);
458 
459 	 // make sure to write ROS first, if one exists
460 	if(mIsCID)
461 		dictPrimitiveWriter.WriteDictItems(itROS->first,itROS->second);
462 
463 	// write all keys, excluding those that we want to write on our own
464 	for(it = originalTopDictRef.begin(); it != originalTopDictRef.end();++it)
465 	{
466 		if(	it->first != scROS &&
467 			it->first != scCharset &&
468 			it->first != scEncoding &&
469 			it->first != scCharstrings &&
470 			it->first != scPrivate &&
471 			it->first != scFDArray &&
472 			it->first != scFDSelect)
473 				dictPrimitiveWriter.WriteDictItems(it->first,it->second);
474 	}
475 	// check if it had an embedded postscript (which would normally be the FSType implementation).
476 	// if not...create one to implement the FSType
477 	if(originalTopDictRef.find(scEmbeddedPostscript) == originalTopDictRef.end() && mOpenTypeInput.mOS2Exists)
478 	{
479 		// no need for sophistication here...you can consider this as the only string to be added.
480 		// so can be sure that its index would be the current count
481 		std::stringstream formatter;
482 		formatter<<"/FSType "<<mOpenTypeInput.mOS2.fsType<<" def";
483 		mOptionalEmbeddedPostscript = formatter.str();
484 		dictPrimitiveWriter.WriteIntegerOperand(mOpenTypeInput.mCFF.mStringsCount + N_STD_STRINGS);
485 		dictPrimitiveWriter.WriteDictOperator(scEmbeddedPostscript);
486 	}
487 	else
488 		mOptionalEmbeddedPostscript = "";
489 
490 	// now leave placeholders, record their positions
491 	mCharsetPlaceHolderPosition = topDictStream.GetCurrentPosition();
492 	dictPrimitiveWriter.Pad5Bytes();
493 	dictPrimitiveWriter.WriteDictOperator(scCharset);
494 	mCharstringsPlaceHolderPosition = topDictStream.GetCurrentPosition();
495 	dictPrimitiveWriter.Pad5Bytes();
496 	dictPrimitiveWriter.WriteDictOperator(scCharstrings);
497 	if(mOpenTypeInput.mCFF.mPrivateDicts[0].mPrivateDictStart != 0)
498 	{
499 		mPrivatePlaceHolderPosition = topDictStream.GetCurrentPosition();
500 		dictPrimitiveWriter.Pad5Bytes(); // for private it's two places - size and position
501 		dictPrimitiveWriter.Pad5Bytes();
502 		dictPrimitiveWriter.WriteDictOperator(scPrivate);
503 	}
504 	else
505 	{
506 		mPrivatePlaceHolderPosition = 0;
507 	}
508 	if(mIsCID)
509 	{
510 		mEncodingPlaceHolderPosition = 0;
511 		mFDArrayPlaceHolderPosition = topDictStream.GetCurrentPosition();
512 		dictPrimitiveWriter.Pad5Bytes();
513 		dictPrimitiveWriter.WriteDictOperator(scFDArray);
514 		mFDSelectPlaceHolderPosition = topDictStream.GetCurrentPosition();
515 		dictPrimitiveWriter.Pad5Bytes();
516 		dictPrimitiveWriter.WriteDictOperator(scFDSelect);
517 	}
518 	else
519 	{
520 		mEncodingPlaceHolderPosition = topDictStream.GetCurrentPosition();
521 		dictPrimitiveWriter.Pad5Bytes();
522 		dictPrimitiveWriter.WriteDictOperator(scEncoding);
523 		mFDArrayPlaceHolderPosition = 0;
524 		mFDSelectPlaceHolderPosition = 0;
525 	}
526 	return dictPrimitiveWriter.GetInternalState();
527 }
528 
WriteStringIndex()529 EStatusCode CFFEmbeddedFontWriter::WriteStringIndex()
530 {
531 	// if added a new string...needs to work hard, otherwise just copy the strings.
532 	if(mOptionalEmbeddedPostscript.size() == 0)
533 	{
534 		// copy as is from the original file. note that the global subroutines
535 		// starting position is equal to the strings end position. hence length is...
536 
537 		OutputStreamTraits streamCopier(&mFontFileStream);
538 		mOpenTypeFile.GetInputStream()->SetPosition(mOpenTypeInput.mCFF.mCFFOffset + mOpenTypeInput.mCFF.mStringIndexPosition);
539 		return streamCopier.CopyToOutputStream(mOpenTypeFile.GetInputStream(),
540 												(LongBufferSizeType)(mOpenTypeInput.mCFF.mGlobalSubrsPosition -
541 												mOpenTypeInput.mCFF.mStringIndexPosition));
542 	}
543 	else
544 	{
545 		// need to write the bloody strings...[remember that i'm adding one more string at the end]
546 		mPrimitivesWriter.WriteCard16(mOpenTypeInput.mCFF.mStringsCount + 1);
547 
548 		// calculate the total data size to determine the required offset size
549 		unsigned long totalSize=0;
550 		for(int i=0; i < mOpenTypeInput.mCFF.mStringsCount; ++i)
551 			totalSize += (unsigned long)strlen(mOpenTypeInput.mCFF.mStrings[i]);
552 		totalSize+=(unsigned long)mOptionalEmbeddedPostscript.size();
553 
554 		Byte sizeOfOffset = GetMostCompressedOffsetSize(totalSize + 1);
555 		mPrimitivesWriter.WriteOffSize(sizeOfOffset);
556 		mPrimitivesWriter.SetOffSize(sizeOfOffset);
557 
558 		unsigned long currentOffset = 1;
559 
560 		// write the offsets
561 		for(int i=0; i < mOpenTypeInput.mCFF.mStringsCount; ++i)
562 		{
563 			mPrimitivesWriter.WriteOffset(currentOffset);
564 			currentOffset += (unsigned long)strlen(mOpenTypeInput.mCFF.mStrings[i]);
565 		}
566 		mPrimitivesWriter.WriteOffset(currentOffset);
567 		currentOffset+=(unsigned long)mOptionalEmbeddedPostscript.size();
568 		mPrimitivesWriter.WriteOffset(currentOffset);
569 
570 		// write the data
571 		for(int i=0; i < mOpenTypeInput.mCFF.mStringsCount; ++i)
572 		{
573 			mFontFileStream.Write((const Byte*)(mOpenTypeInput.mCFF.mStrings[i]),strlen(mOpenTypeInput.mCFF.mStrings[i]));
574 		}
575 		mFontFileStream.Write((const Byte*)(mOptionalEmbeddedPostscript.c_str()),mOptionalEmbeddedPostscript.size());
576 		return mPrimitivesWriter.GetInternalState();
577 	}
578 }
579 
WriteGlobalSubrsIndex()580 EStatusCode CFFEmbeddedFontWriter::WriteGlobalSubrsIndex()
581 {
582 	// global subrs index is empty!. no subrs in my CFF outputs. all charstrings are flattened
583 
584 	return mPrimitivesWriter.WriteCard16(0);
585 }
586 
587 
588 typedef std::pair<Byte,unsigned short> ByteAndUShort;
589 typedef std::list<ByteAndUShort> ByteAndUShortList;
590 
WriteEncodings(const UIntVector & inSubsetGlyphIDs)591 EStatusCode CFFEmbeddedFontWriter::WriteEncodings(const UIntVector& inSubsetGlyphIDs)
592 {
593 	// if it's a CID. don't bother with encodings (marks as 0)
594 	if(mIsCID)
595 	{
596 		mEncodingPosition = 0;
597 		return PDFHummus::eSuccess;
598 	}
599 
600 	// not CID, write encoding, according to encoding values from the original font
601 	EncodingsInfo* encodingInfo = mOpenTypeInput.mCFF.mTopDictIndex[0].mEncoding;
602 	if(encodingInfo->mEncodingStart <= 1)
603 	{
604 		mEncodingPosition = encodingInfo->mEncodingStart;
605 		return PDFHummus::eSuccess;
606 	}
607 	else
608 	{
609 		// original font had custom encoding, let's subset it according to just the glyphs we
610 		// actually have. but cause i'm lazy i'll just do the first format.
611 
612 		// figure out if we got supplements
613 		UIntVector::const_iterator it = inSubsetGlyphIDs.begin();
614 		ByteAndUShortList supplements;
615 
616 		for(; it != inSubsetGlyphIDs.end();++it)
617 		{
618 			// don't be confused! the supplements is by SID! not GID!
619 			unsigned short sid = mOpenTypeInput.mCFF.GetGlyphSID(0,*it);
620 
621 			UShortToByteList::iterator itSupplements = encodingInfo->mSupplements.find(sid);
622 			if(itSupplements != encodingInfo->mSupplements.end())
623 			{
624 				ByteList::iterator itMoreEncoding = itSupplements->second.begin();
625 				for(; itMoreEncoding != itSupplements->second.end(); ++itMoreEncoding)
626 					supplements.push_back(ByteAndUShort(*itMoreEncoding,sid));
627 			}
628 		}
629 
630 		mEncodingPosition = mFontFileStream.GetCurrentPosition();
631 
632 		if(supplements.size() > 0)
633 			mPrimitivesWriter.WriteCard8(0x80);
634 		else
635 			mPrimitivesWriter.WriteCard8(0);
636 
637 		// assuming that 0 is in the subset glyphs IDs, which does not require encoding
638 		// get the encodings count
639 		Byte encodingGlyphsCount = std::min((Byte)(inSubsetGlyphIDs.size()-1),encodingInfo->mEncodingsCount);
640 
641 		mPrimitivesWriter.WriteCard8(encodingGlyphsCount);
642 		for(Byte i=0; i < encodingGlyphsCount;++i)
643 		{
644 			if(inSubsetGlyphIDs[i+1] < encodingInfo->mEncodingsCount)
645 				mPrimitivesWriter.WriteCard8(encodingInfo->mEncoding[inSubsetGlyphIDs[i+1]-1]);
646 			else
647 				mPrimitivesWriter.WriteCard8(0);
648 		}
649 
650 		if(supplements.size() > 0)
651 		{
652 			mPrimitivesWriter.WriteCard8(Byte(supplements.size()));
653 			ByteAndUShortList::iterator itCollectedSupplements = supplements.begin();
654 
655 			for(; itCollectedSupplements != supplements.end(); ++itCollectedSupplements)
656 			{
657 				mPrimitivesWriter.WriteCard8(itCollectedSupplements->first);
658 				mPrimitivesWriter.WriteCard16(itCollectedSupplements->second);
659 			}
660 		}
661 	}
662 
663 	return mPrimitivesWriter.GetInternalState();
664 }
665 
WriteCharsets(const UIntVector & inSubsetGlyphIDs,UShortVector * inCIDMapping)666 EStatusCode CFFEmbeddedFontWriter::WriteCharsets(const UIntVector& inSubsetGlyphIDs,
667 													UShortVector* inCIDMapping)
668 {
669 	// since this is a subset the chances that i'll get a defult charset are 0.
670 	// hence i'll always do some charset. and using format 0 !!1
671 	UIntVector::const_iterator it = inSubsetGlyphIDs.begin();
672 	++it; // skip the 0
673 
674 	mCharsetPosition = mFontFileStream.GetCurrentPosition();
675 
676 	mPrimitivesWriter.WriteCard8(0);
677 	if(mIsCID && inCIDMapping)
678 	{
679 		UShortVector::const_iterator itCIDs = inCIDMapping->begin();
680 		++itCIDs;
681 		for(; it != inSubsetGlyphIDs.end(); ++it,++itCIDs)
682 			mPrimitivesWriter.WriteSID(*itCIDs);
683 
684 	}
685 	else
686 	{
687 		// note that this also works for CIDs! cause in this case the SIDs are actually
688 		// CIDs
689 		for(; it != inSubsetGlyphIDs.end(); ++it)
690 			mPrimitivesWriter.WriteSID(mOpenTypeInput.mCFF.GetGlyphSID(0,*it));
691 	}
692 	return mPrimitivesWriter.GetInternalState();
693 }
694 
WriteCharStrings(const UIntVector & inSubsetGlyphIDs)695 EStatusCode CFFEmbeddedFontWriter::WriteCharStrings(const UIntVector& inSubsetGlyphIDs)
696 {
697 	/*
698 		1. build the charstrings data, looping the glyphs charstrings and writing a flattened
699 		   version of each charstring
700 		2. write the charstring index based on offsets inside the data (size should be according to the max)
701 		3. copy the data into the stream
702 	*/
703 
704 
705 	unsigned long* offsets = new unsigned long[inSubsetGlyphIDs.size() + 1];
706 	MyStringBuf charStringsData;
707 	OutputStringBufferStream charStringsDataWriteStream(&charStringsData);
708 	CharStringType2Flattener charStringFlattener;
709 	UIntVector::const_iterator itGlyphs = inSubsetGlyphIDs.begin();
710 	EStatusCode status = PDFHummus::eSuccess;
711 
712 	do
713 	{
714 		unsigned short i=0;
715 		for(; itGlyphs != inSubsetGlyphIDs.end() && PDFHummus::eSuccess == status; ++itGlyphs,++i)
716 		{
717 			offsets[i] = (unsigned long)charStringsDataWriteStream.GetCurrentPosition();
718 			status = charStringFlattener.WriteFlattenedGlyphProgram(	0,
719 																		*itGlyphs,
720 																		&(mOpenTypeInput.mCFF),
721 																		&charStringsDataWriteStream);
722 		}
723 		if(status != PDFHummus::eSuccess)
724 			break;
725 
726 		offsets[i] = (unsigned long)charStringsDataWriteStream.GetCurrentPosition();
727 
728 		charStringsData.pubseekoff(0,std::ios_base::beg);
729 
730 		// write index section
731 		mCharStringPosition = mFontFileStream.GetCurrentPosition();
732 		Byte sizeOfOffset = GetMostCompressedOffsetSize(offsets[i] + 1);
733 		mPrimitivesWriter.WriteCard16((unsigned short)inSubsetGlyphIDs.size());
734 		mPrimitivesWriter.WriteOffSize(sizeOfOffset);
735 		mPrimitivesWriter.SetOffSize(sizeOfOffset);
736 		for(i=0;i<=inSubsetGlyphIDs.size();++i)
737 			mPrimitivesWriter.WriteOffset(offsets[i] + 1);
738 
739 		// Write data
740 		InputStringBufferStream charStringsDataReadStream(&charStringsData);
741 		OutputStreamTraits streamCopier(&mFontFileStream);
742 		status = streamCopier.CopyToOutputStream(&charStringsDataReadStream);
743 		if(status != PDFHummus::eSuccess)
744 			break;
745 	}while(false);
746 
747 	delete[] offsets;
748 	return status;
749 }
750 
751 static const unsigned short scSubrs = 19;
WritePrivateDictionary()752 EStatusCode CFFEmbeddedFontWriter::WritePrivateDictionary()
753 {
754 	return WritePrivateDictionaryBody(mOpenTypeInput.mCFF.mPrivateDicts[0],mPrivateSize,mPrivatePosition);
755 }
756 
WritePrivateDictionaryBody(const PrivateDictInfo & inPrivateDictionary,LongFilePositionType & outWriteSize,LongFilePositionType & outWritePosition)757 EStatusCode CFFEmbeddedFontWriter::WritePrivateDictionaryBody(const PrivateDictInfo& inPrivateDictionary,
758 															  LongFilePositionType& outWriteSize,
759 															  LongFilePositionType& outWritePosition)
760 {
761 	// just copy the private dict, without the subrs reference
762 	if(inPrivateDictionary.mPrivateDictStart != 0)
763 	{
764 		UShortToDictOperandListMap::const_iterator it= inPrivateDictionary.mPrivateDict.begin();
765 
766 		outWritePosition = mFontFileStream.GetCurrentPosition();
767 		for(; it != inPrivateDictionary.mPrivateDict.end(); ++it)
768 			if(it->first != scSubrs) // should get me a nice little pattern for this some time..a filter thing
769 				mPrimitivesWriter.WriteDictItems(it->first,it->second);
770 
771 		outWriteSize = mFontFileStream.GetCurrentPosition() - outWritePosition;
772 		return mPrimitivesWriter.GetInternalState();
773 	}
774 	else
775 	{
776 		outWritePosition = 0;
777 		outWriteSize = 0;
778 		return PDFHummus::eSuccess;
779 	}
780 }
781 
782 typedef std::set<FontDictInfo*> FontDictInfoSet;
783 typedef std::pair<LongFilePositionType,LongFilePositionType> LongFilePositionTypePair;
784 typedef std::map<FontDictInfo*,LongFilePositionTypePair> FontDictInfoToLongFilePositionTypePairMap;
785 
DetermineFDArrayIndexes(const UIntVector & inSubsetGlyphIDs,FontDictInfoToByteMap & outNewFontDictsIndexes)786 void CFFEmbeddedFontWriter::DetermineFDArrayIndexes(const UIntVector& inSubsetGlyphIDs,FontDictInfoToByteMap& outNewFontDictsIndexes)
787 {
788 	UIntVector::const_iterator itGlyphs = inSubsetGlyphIDs.begin();
789 	FontDictInfoSet fontDictInfos;
790 
791 	for(; itGlyphs != inSubsetGlyphIDs.end(); ++itGlyphs)
792 		if(mOpenTypeInput.mCFF.mTopDictIndex[0].mFDSelect[*itGlyphs])
793 			fontDictInfos.insert(mOpenTypeInput.mCFF.mTopDictIndex[0].mFDSelect[*itGlyphs]);
794 
795 	FontDictInfoSet::iterator itFontInfos;
796 	Byte i=0;
797 
798 	for(itFontInfos = fontDictInfos.begin(); itFontInfos != fontDictInfos.end(); ++itFontInfos,++i)
799 		outNewFontDictsIndexes.insert(FontDictInfoToByteMap::value_type(*itFontInfos,i));
800 }
801 
802 
WriteFDArray(const UIntVector & inSubsetGlyphIDs,const FontDictInfoToByteMap & inNewFontDictsIndexes)803 EStatusCode CFFEmbeddedFontWriter::WriteFDArray(const UIntVector& inSubsetGlyphIDs,const FontDictInfoToByteMap& inNewFontDictsIndexes)
804 {
805 	// loop the glyphs IDs, for each get their respective dictionary. put them in a set.
806 	// now itereate them, and write each private dictionary [no need for index]. save the private dictionary position.
807 	// now write the FDArray. remember it's an index, so first write into a separate, maintain the offsets and only then write the actual buffer.
808 	// save a mapping between the original pointer and a new index.
809 
810 	FontDictInfoToLongFilePositionTypePairMap privateDictionaries;
811 	EStatusCode status = PDFHummus::eSuccess;
812 	unsigned long* offsets = NULL;
813 
814 	do
815 	{
816 		if(inNewFontDictsIndexes.size() == 0)
817 		{
818 			// if no valid font infos, write an empty index and finish
819 			mFDArrayPosition = mFontFileStream.GetCurrentPosition();
820 			status = mPrimitivesWriter.WriteCard16(0);
821 			break;
822 		}
823 
824 		// loop the font infos, and write the private dictionaries
825 		LongFilePositionType privatePosition,privateSize;
826 		FontDictInfoToByteMap::const_iterator itFontInfos = inNewFontDictsIndexes.begin();
827 		for(; itFontInfos != inNewFontDictsIndexes.end() && PDFHummus::eSuccess == status; ++itFontInfos)
828 		{
829 			status = WritePrivateDictionaryBody(itFontInfos->first->mPrivateDict,privateSize,privatePosition);
830 			privateDictionaries.insert(
831 				FontDictInfoToLongFilePositionTypePairMap::value_type(itFontInfos->first,
832 																	LongFilePositionTypePair(privateSize,privatePosition)));
833 		}
834 		if(status != PDFHummus::eSuccess)
835 			break;
836 
837 		// write FDArray segment
838 		offsets = new unsigned long[inNewFontDictsIndexes.size() + 1];
839 		MyStringBuf fontDictsInfoData;
840 		OutputStringBufferStream fontDictDataWriteStream(&fontDictsInfoData);
841 		CFFPrimitiveWriter fontDictPrimitiveWriter;
842 		Byte i=0;
843 
844 		fontDictPrimitiveWriter.SetStream(&fontDictDataWriteStream);
845 
846 		for(itFontInfos = inNewFontDictsIndexes.begin(); itFontInfos != inNewFontDictsIndexes.end() && PDFHummus::eSuccess == status; ++itFontInfos,++i)
847 		{
848 			offsets[i] = (unsigned long)fontDictDataWriteStream.GetCurrentPosition();
849 
850 			UShortToDictOperandListMap::const_iterator itDict= itFontInfos->first->mFontDict.begin();
851 
852 			for(; itDict != itFontInfos->first->mFontDict.end() && PDFHummus::eSuccess == status; ++itDict)
853 				if(itDict->first != scPrivate) // should get me a nice little pattern for this some time..a filter thing
854 					status = fontDictPrimitiveWriter.WriteDictItems(itDict->first,itDict->second);
855 
856 			// now add the private key
857 			if(PDFHummus::eSuccess == status && privateDictionaries[itFontInfos->first].first != 0)
858 			{
859 				fontDictPrimitiveWriter.WriteIntegerOperand(long(privateDictionaries[itFontInfos->first].first));
860 				fontDictPrimitiveWriter.WriteIntegerOperand(long(privateDictionaries[itFontInfos->first].second));
861 				fontDictPrimitiveWriter.WriteDictOperator(scPrivate);
862 				status = fontDictPrimitiveWriter.GetInternalState();
863 			}
864 		}
865 		if(status != PDFHummus::eSuccess)
866 			break;
867 
868 		offsets[i] = (unsigned long)fontDictDataWriteStream.GetCurrentPosition();
869 
870 		fontDictsInfoData.pubseekoff(0,std::ios_base::beg);
871 
872 		// write index section
873 		mFDArrayPosition = mFontFileStream.GetCurrentPosition();
874 		Byte sizeOfOffset = GetMostCompressedOffsetSize(offsets[i] + 1);
875 		mPrimitivesWriter.WriteCard16((unsigned short)inNewFontDictsIndexes.size());
876 		mPrimitivesWriter.WriteOffSize(sizeOfOffset);
877 		mPrimitivesWriter.SetOffSize(sizeOfOffset);
878 		for(i=0;i<=inNewFontDictsIndexes.size();++i)
879 			mPrimitivesWriter.WriteOffset(offsets[i] + 1);
880 
881 		// Write data
882 		InputStringBufferStream fontDictDataReadStream(&fontDictsInfoData);
883 		OutputStreamTraits streamCopier(&mFontFileStream);
884 		status = streamCopier.CopyToOutputStream(&fontDictDataReadStream);
885 		if(status != PDFHummus::eSuccess)
886 			break;
887 
888 	}while(false);
889 
890 	delete[] offsets;
891 	if(status != PDFHummus::eSuccess)
892 		return status;
893 	else
894 		return mPrimitivesWriter.GetInternalState();
895 }
896 
WriteFDSelect(const UIntVector & inSubsetGlyphIDs,const FontDictInfoToByteMap & inNewFontDictsIndexes)897 EStatusCode CFFEmbeddedFontWriter::WriteFDSelect(const UIntVector& inSubsetGlyphIDs,const FontDictInfoToByteMap& inNewFontDictsIndexes)
898 {
899 	// always write format 3. cause at most cases the FD dicts count will be so low that it'd
900 	// take a bloody mircale for no repeats to occur.
901 	UIntVector::const_iterator itGlyphs = inSubsetGlyphIDs.begin();
902 
903 
904 	mFDSelectPosition = mFontFileStream.GetCurrentPosition();
905 	mPrimitivesWriter.WriteCard8(3);
906 
907 	LongFilePositionType rangesCountPosition = mFontFileStream.GetCurrentPosition();
908 	mPrimitivesWriter.WriteCard16(1); // temporary. will get back to this later
909 
910 	unsigned short rangesCount = 1;
911 	Byte currentFD,newFD;
912 	unsigned short glyphIndex = 1;
913 	FontDictInfoToByteMap::const_iterator itNewIndex =
914 		inNewFontDictsIndexes.find(mOpenTypeInput.mCFF.mTopDictIndex[0].mFDSelect[*itGlyphs]);
915 
916 	// k. seems like i probably just imagine exceptions here. i guess there must
917 	// be a proper FDSelect with FDs for all...so i'm defaulting to some 0
918 	currentFD = (itNewIndex == inNewFontDictsIndexes.end() ? 0:itNewIndex->second);
919 	mPrimitivesWriter.WriteCard16(0);
920 	mPrimitivesWriter.WriteCard8(currentFD);
921 	++itGlyphs;
922 
923 	for(; itGlyphs != inSubsetGlyphIDs.end(); ++itGlyphs,++glyphIndex)
924 	{
925 		itNewIndex =
926 				inNewFontDictsIndexes.find(mOpenTypeInput.mCFF.mTopDictIndex[0].mFDSelect[*itGlyphs]);
927 		newFD = (itNewIndex == inNewFontDictsIndexes.end() ? 0:itNewIndex->second);
928 		if(newFD != currentFD)
929 		{
930 			currentFD = newFD;
931 			mPrimitivesWriter.WriteCard16(glyphIndex);
932 			mPrimitivesWriter.WriteCard8(currentFD);
933 			++rangesCount;
934 		}
935 	}
936 	mPrimitivesWriter.WriteCard16((unsigned short)inSubsetGlyphIDs.size());
937 	// go back to ranges count if not equal to what's already written
938 	if(rangesCount != 1)
939 	{
940 		LongFilePositionType currentPosition = mFontFileStream.GetCurrentPosition();
941 		mFontFileStream.SetPosition(rangesCountPosition);
942 		mPrimitivesWriter.WriteCard16(rangesCount);
943 		mFontFileStream.SetPosition(currentPosition);
944 	}
945 	return mPrimitivesWriter.GetInternalState();
946 }
947 
UpdateIndexesAtTopDict()948 EStatusCode CFFEmbeddedFontWriter::UpdateIndexesAtTopDict()
949 {
950 	mFontFileStream.SetPosition(mCharsetPlaceHolderPosition);
951 	mPrimitivesWriter.Write5ByteDictInteger((long)mCharsetPosition);
952 
953 	mFontFileStream.SetPosition(mCharstringsPlaceHolderPosition);
954 	mPrimitivesWriter.Write5ByteDictInteger((long)mCharStringPosition);
955 
956 	if(mOpenTypeInput.mCFF.mPrivateDicts[0].mPrivateDictStart != 0)
957 	{
958 		mFontFileStream.SetPosition(mPrivatePlaceHolderPosition);
959 		mPrimitivesWriter.Write5ByteDictInteger((long)mPrivateSize);
960 		mPrimitivesWriter.Write5ByteDictInteger((long)mPrivatePosition);
961 
962 	}
963 
964 	if(mIsCID)
965 	{
966 		mFontFileStream.SetPosition(mFDArrayPlaceHolderPosition);
967 		mPrimitivesWriter.Write5ByteDictInteger((long)mFDArrayPosition);
968 		mFontFileStream.SetPosition(mFDSelectPlaceHolderPosition);
969 		mPrimitivesWriter.Write5ByteDictInteger((long)mFDSelectPosition);
970 	}
971 	else
972 	{
973 		mFontFileStream.SetPosition(mEncodingPlaceHolderPosition);
974 		mPrimitivesWriter.Write5ByteDictInteger((long)mEncodingPosition);
975 	}
976 
977 	return mPrimitivesWriter.GetInternalState();
978 }