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 }