1 /*
2 Source File : Type1ToType2Converter.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 "Type1ToType2Converter.h"
22 #include "IByteWriter.h"
23 #include "Type1Input.h"
24 #include "Trace.h"
25 #include "CharStringType1Interpreter.h"
26 #include "Type2CharStringWriter.h"
27
28 #include <algorithm>
29
30 using namespace PDFHummus;
31
Type1ToType2Converter(void)32 Type1ToType2Converter::Type1ToType2Converter(void)
33 {
34 }
35
~Type1ToType2Converter(void)36 Type1ToType2Converter::~Type1ToType2Converter(void)
37 {
38 }
39
WriteConvertedFontProgram(const std::string & inGlyphName,Type1Input * inType1Input,IByteWriter * inByteWriter)40 EStatusCode Type1ToType2Converter::WriteConvertedFontProgram(const std::string& inGlyphName,
41 Type1Input* inType1Input,
42 IByteWriter* inByteWriter)
43 {
44 EStatusCode status;
45
46 do
47 {
48 CharStringType1Interpreter interpreter;
49
50 mHelper = inType1Input;
51 mHintReplacementEncountered = false;
52 mHintAdditionEncountered = false;
53 mFirstPathConstructionEncountered = false;
54 mInFlexCollectionMode = false;
55 mIsFirst2Coordinates = false;
56 mCurrentHints.clear();
57 mFlexParameters.clear();
58
59 Type1CharString* charString = inType1Input->GetGlyphCharString(inGlyphName);
60 if(!charString)
61 {
62 TRACE_LOG1("Type1ToType2Converter::WriteConvertedFontProgram, Exception, cannot find glyph name %s",inGlyphName.c_str());
63 status = PDFHummus::eFailure;
64 break;
65 }
66
67 status = interpreter.Intepret(*charString,this);
68 if(status != PDFHummus::eSuccess)
69 {
70 TRACE_LOG("Type1ToType2Converter::WriteConvertedFontProgram, Exception, failed to interpret glyph");
71 break;
72 }
73
74 // convert stem commands - put them at the beginning, and use hint masks if required
75 ConvertStems();
76
77 // convert operators to the type 2 short writing, dropping 0's, and set the first moveto
78 // to offset by sidebearing
79 ConvertPathConsturction();
80
81 // need to place width of character at the first stack clearing operator
82 AddInitialWidthParameter();
83
84 status = WriteProgramToStream(inByteWriter);
85 if(status != PDFHummus::eSuccess)
86 {
87 TRACE_LOG("Type1ToType2Converter::WriteConvertedFontProgram, Exception, can't write program to target stream");
88 break;
89 }
90 }while(false);
91
92 mVStems.clear();
93 mHStems.clear();
94 mConversionProgram.clear();
95 return status;
96 }
97
GetSubr(long inSubrIndex)98 Type1CharString* Type1ToType2Converter::GetSubr(long inSubrIndex)
99 {
100 return mHelper->GetSubr(inSubrIndex);
101 }
102
IsOtherSubrSupported(long inOtherSubrsIndex)103 bool Type1ToType2Converter::IsOtherSubrSupported(long inOtherSubrsIndex)
104 {
105 // use callothersubr op code as a marker for the start of hint replacement segment
106 if(3 == inOtherSubrsIndex)
107 {
108 mHintReplacementEncountered = true;
109 RecordOperatorMarker(0x0c10);
110 }
111 else if(1 == inOtherSubrsIndex)
112 {
113 mInFlexCollectionMode = true;
114 mIsFirst2Coordinates = true;
115 }
116 else if(0 == inOtherSubrsIndex)
117 {
118 // for 0 othersubr, i need the 3rd parameter as the FD for implementing flex in type 2. so "support" it
119 return true;
120 }
121 return false;
122 }
123
GetLenIV()124 unsigned long Type1ToType2Converter::GetLenIV()
125 {
126 return mHelper->GetLenIV();
127 }
128
CallOtherSubr(const LongList & inOperandList,LongList & outPostScriptOperandStack)129 EStatusCode Type1ToType2Converter::CallOtherSubr(const LongList& inOperandList,LongList& outPostScriptOperandStack)
130 {
131 // should get here onther for 0 othersubr, to mark flex segment end. implement it...and also insert a node
132 // for flex
133 LongList::const_reverse_iterator it = inOperandList.rbegin();
134 ++it;
135 long argumentsCount = *it;
136 ++it;
137
138 // the next argument should be the FD...place it in the parameters collection
139 long flexDepth = *it;
140
141 mFlexParameters.push_back(flexDepth);
142
143 // take care of the postscript operand stack
144 for(long i=0;i<argumentsCount;++i)
145 {
146 outPostScriptOperandStack.push_back(*it);
147 ++it;
148 }
149
150 // now finalize flex, by placing a type 2 flex command
151 EStatusCode status = RecordOperatorWithParameters(0x0c23,mFlexParameters);
152
153 // cleanup flex mode
154 mFlexParameters.clear();
155 mInFlexCollectionMode = false;
156 mIsFirst2Coordinates = false;
157 return status;
158 }
159
160
Type1Pop(const LongList & inOperandList,const LongList & inPostScriptOperandStack)161 EStatusCode Type1ToType2Converter::Type1Pop(const LongList& inOperandList,const LongList& inPostScriptOperandStack){return PDFHummus::eSuccess;}
Type1SetCurrentPoint(const LongList & inOperandList)162 EStatusCode Type1ToType2Converter::Type1SetCurrentPoint(const LongList& inOperandList){return PDFHummus::eSuccess;}
Type1InterpretNumber(long inOperand)163 EStatusCode Type1ToType2Converter::Type1InterpretNumber(long inOperand){return PDFHummus::eSuccess;}
Type1Div(const LongList & inOperandList)164 EStatusCode Type1ToType2Converter::Type1Div(const LongList& inOperandList){return PDFHummus::eSuccess;}
Type1DotSection(const LongList & inOperandList)165 EStatusCode Type1ToType2Converter::Type1DotSection(const LongList& inOperandList){return PDFHummus::eSuccess;}
Type1Return(const LongList & inOperandList)166 EStatusCode Type1ToType2Converter::Type1Return(const LongList& inOperandList){return PDFHummus::eSuccess;}
167
RecordOperatorMarker(unsigned short inMarkerType)168 void Type1ToType2Converter::RecordOperatorMarker(unsigned short inMarkerType)
169 {
170 ConversionNode node;
171 mConversionProgram.push_back(node);
172 mConversionProgram.back().mMarkerType = inMarkerType;
173 }
174
Type1Hsbw(const LongList & inOperandList)175 EStatusCode Type1ToType2Converter::Type1Hsbw(const LongList& inOperandList)
176 {
177 if(inOperandList.size() < 2)
178 return PDFHummus::eFailure;
179
180 LongList::const_reverse_iterator it = inOperandList.rbegin();
181
182 mWidth[1] = 0;
183 mWidth[0] = *it;
184 ++it;
185 mSideBearing[1] = 0;
186 mSideBearing[0] = *it;
187 return PDFHummus::eSuccess;
188 }
189
Type1Sbw(const LongList & inOperandList)190 EStatusCode Type1ToType2Converter::Type1Sbw(const LongList& inOperandList)
191 {
192 if(inOperandList.size() < 4)
193 return PDFHummus::eFailure;
194
195 LongList::const_reverse_iterator it = inOperandList.rbegin();
196
197 mWidth[1] = *it;
198 ++it;
199 mWidth[0] = *it;
200 ++it;
201 mSideBearing[1] = *it;
202 ++it;
203 mSideBearing[0] = *it;
204 return PDFHummus::eSuccess;
205 }
206
Type1Hstem(const LongList & inOperandList)207 EStatusCode Type1ToType2Converter::Type1Hstem(const LongList& inOperandList)
208 {
209 LongList::const_reverse_iterator it = inOperandList.rbegin();
210
211 long extent = *it;
212 ++it;
213 long origin = *it;
214 AddHStem(origin,extent);
215 if(mFirstPathConstructionEncountered)
216 mHintAdditionEncountered = true;
217 return RecordOperatorWithParameters(1,inOperandList);
218 }
219
AddHStem(long inOrigin,long inExtent)220 EStatusCode Type1ToType2Converter::AddHStem(long inOrigin,long inExtent)
221 {
222 Stem aStem(inOrigin,inExtent);
223 StemToSizeTMap::iterator it = mHStems.find(aStem);
224
225 if(it == mHStems.end())
226 mHStems.insert(StemToSizeTMap::value_type(aStem,0));
227 return PDFHummus::eSuccess;
228 }
229
AddVStem(long inOrigin,long inExtent)230 EStatusCode Type1ToType2Converter::AddVStem(long inOrigin,long inExtent)
231 {
232 Stem aStem(inOrigin,inExtent);
233 StemToSizeTMap::iterator it = mVStems.find(aStem);
234
235 if(it == mVStems.end())
236 mVStems.insert(StemToSizeTMap::value_type(aStem,0));
237 return PDFHummus::eSuccess;
238 }
239
RecordOperatorWithParameters(unsigned short inMarkerType,const LongList & inOperandList)240 EStatusCode Type1ToType2Converter::RecordOperatorWithParameters(unsigned short inMarkerType,const LongList& inOperandList)
241 {
242 ConversionNode node;
243 mConversionProgram.push_back(node);
244 mConversionProgram.back().mMarkerType = inMarkerType;
245 mConversionProgram.back().mOperands = inOperandList;
246 return PDFHummus::eSuccess;
247 }
248
249
Type1Vstem(const LongList & inOperandList)250 EStatusCode Type1ToType2Converter::Type1Vstem(const LongList& inOperandList)
251 {
252 LongList::const_reverse_iterator it = inOperandList.rbegin();
253
254 long extent = *it;
255 ++it;
256 long origin = *it;
257 AddVStem(origin,extent);
258 if(mFirstPathConstructionEncountered)
259 mHintAdditionEncountered = true;
260 return RecordOperatorWithParameters(3,inOperandList);
261 }
262
Type1VStem3(const LongList & inOperandList)263 EStatusCode Type1ToType2Converter::Type1VStem3(const LongList& inOperandList)
264 {
265 LongList::const_reverse_iterator it = inOperandList.rbegin();
266
267 long extent = *it;
268 ++it;
269 long origin = *it;
270 AddVStem(origin,extent);
271 ++it;
272 extent = *it;
273 ++it;
274 origin = *it;
275 AddVStem(origin,extent);
276 ++it;
277 extent = *it;
278 ++it;
279 origin = *it;
280 AddVStem(origin,extent);
281 if(mFirstPathConstructionEncountered)
282 mHintAdditionEncountered = true;
283 return RecordOperatorWithParameters(0x0c01,inOperandList);
284 }
285
Type1HStem3(const LongList & inOperandList)286 EStatusCode Type1ToType2Converter::Type1HStem3(const LongList& inOperandList)
287 {
288 LongList::const_reverse_iterator it = inOperandList.rbegin();
289
290 long extent = *it;
291 ++it;
292 long origin = *it;
293 AddHStem(origin,extent);
294 ++it;
295 extent = *it;
296 ++it;
297 origin = *it;
298 AddHStem(origin,extent);
299 ++it;
300 extent = *it;
301 ++it;
302 origin = *it;
303 AddHStem(origin,extent);
304 if(mFirstPathConstructionEncountered)
305 mHintAdditionEncountered = true;
306 return RecordOperatorWithParameters(0x0c02,inOperandList);
307 }
308
Type1VMoveto(const LongList & inOperandList)309 EStatusCode Type1ToType2Converter::Type1VMoveto(const LongList& inOperandList)
310 {
311 mFirstPathConstructionEncountered = true;
312 return RecordOperatorWithParameters(4,inOperandList);
313 }
314
Type1RMoveto(const LongList & inOperandList)315 EStatusCode Type1ToType2Converter::Type1RMoveto(const LongList& inOperandList)
316 {
317 mFirstPathConstructionEncountered = true;
318 if(mInFlexCollectionMode)
319 {
320 // while in flex, collect the parameters to the flex parameter
321 LongList::const_iterator it = inOperandList.begin();
322
323 // note a paculiarness for the 2nd pair of coordinates.
324 // in type1 flex, the first 2 coordinates summed are the first edge coordinate
325 if(mFlexParameters.size() == 2 && mIsFirst2Coordinates)
326 {
327 LongList::iterator itFlex = mFlexParameters.begin();
328 *itFlex += *it;
329 ++it;
330 ++itFlex;
331 *itFlex += *it;
332 mIsFirst2Coordinates = false;
333 }
334 else
335 {
336 mFlexParameters.push_back(*it);
337 ++it;
338 mFlexParameters.push_back(*it);
339 }
340 return PDFHummus::eSuccess;
341 }
342 else
343 {
344 return RecordOperatorWithParameters(21,inOperandList);
345 }
346 }
347
Type1HMoveto(const LongList & inOperandList)348 EStatusCode Type1ToType2Converter::Type1HMoveto(const LongList& inOperandList)
349 {
350 mFirstPathConstructionEncountered = true;
351 return RecordOperatorWithParameters(22,inOperandList);
352 }
353
Type1RLineto(const LongList & inOperandList)354 EStatusCode Type1ToType2Converter::Type1RLineto(const LongList& inOperandList)
355 {
356 mFirstPathConstructionEncountered = true;
357 return RecordOperatorWithParameters(5,inOperandList);
358 }
359
Type1HLineto(const LongList & inOperandList)360 EStatusCode Type1ToType2Converter::Type1HLineto(const LongList& inOperandList)
361 {
362 mFirstPathConstructionEncountered = true;
363 return RecordOperatorWithParameters(6,inOperandList);
364 }
365
Type1VLineto(const LongList & inOperandList)366 EStatusCode Type1ToType2Converter::Type1VLineto(const LongList& inOperandList)
367 {
368 mFirstPathConstructionEncountered = true;
369 return RecordOperatorWithParameters(7,inOperandList);
370 }
371
Type1RRCurveto(const LongList & inOperandList)372 EStatusCode Type1ToType2Converter::Type1RRCurveto(const LongList& inOperandList)
373 {
374 mFirstPathConstructionEncountered = true;
375 return RecordOperatorWithParameters(8,inOperandList);
376 }
377
Type1VHCurveto(const LongList & inOperandList)378 EStatusCode Type1ToType2Converter::Type1VHCurveto(const LongList& inOperandList)
379 {
380 mFirstPathConstructionEncountered = true;
381 return RecordOperatorWithParameters(30,inOperandList);
382 }
383
Type1HVCurveto(const LongList & inOperandList)384 EStatusCode Type1ToType2Converter::Type1HVCurveto(const LongList& inOperandList)
385 {
386 mFirstPathConstructionEncountered = true;
387 return RecordOperatorWithParameters(31,inOperandList);
388 }
389
Type1ClosePath(const LongList & inOperandList)390 EStatusCode Type1ToType2Converter::Type1ClosePath(const LongList& inOperandList)
391 {
392 // IMPORTANT - apparently closepath was removed for type 2. didn't notice it till now
393 return PDFHummus::eSuccess;
394 }
395
Type1Endchar(const LongList & inOperandList)396 EStatusCode Type1ToType2Converter::Type1Endchar(const LongList& inOperandList)
397 {
398 RecordOperatorMarker(14);
399 return PDFHummus::eSuccess;
400 }
401
Type1Seac(const LongList & inOperandList)402 EStatusCode Type1ToType2Converter::Type1Seac(const LongList& inOperandList)
403 {
404 // le'ts convert it already to the final EndChar...and stop any later recording
405
406 // note that type2 endchar implementation avoids sidebearing
407 LongList params;
408 LongList::const_iterator it = inOperandList.begin();
409 ++it;
410 for(; it != inOperandList.end();++it)
411 params.push_back(*it);
412 return RecordOperatorWithParameters(14,params);
413 }
414
sStemSort(const Stem * inLeft,const Stem * inRight)415 static bool sStemSort(const Stem* inLeft, const Stem* inRight)
416 {
417 return inLeft->mOrigin < inRight->mOrigin;
418 }
419
420
ConvertStems()421 void Type1ToType2Converter::ConvertStems()
422 {
423
424
425 if(mHStems.size() == 0 && mVStems.size() == 0)
426 return;
427
428 StemVector orderedHStems;
429 StemVector orderedVStems;
430
431 StemToSizeTMap::iterator it = mHStems.begin();
432 for(; it != mHStems.end(); ++it)
433 orderedHStems.push_back(&(it->first));
434 it = mVStems.begin();
435 for(; it != mVStems.end(); ++it)
436 orderedVStems.push_back(&(it->first));
437
438 // sort stems
439 sort(orderedHStems.begin(),orderedHStems.end(),sStemSort);
440 sort(orderedVStems.begin(),orderedVStems.end(),sStemSort);
441
442 ConversionNodeList::iterator itProgramPosition = mConversionProgram.begin();
443
444
445 // determine if hint mask is required.
446 // two conditions:
447 // 1. stem replacement encountered, then we need a new set of stems, which requires a mask
448 // 2. no stem replacement, but hints were defined after first path construction. this means
449 // that initially there's a zero hint state, and only after some drawing there'll be a hint applied
450 // i think this is theoretical...but i guess it is possible.
451 if(mHintReplacementEncountered || mHintAdditionEncountered)
452 {
453 // setup stem to index maps
454 size_t i=0;
455 for(; i < orderedHStems.size();++i)
456 mHStems[*orderedHStems[i]] = i;
457 for(i=0; i < orderedVStems.size();++i)
458 mVStems[*orderedVStems[i]] = i+orderedHStems.size();
459
460 // write initial hstemhm command
461 if(orderedHStems.size() > 0)
462 {
463 itProgramPosition = InsertOperatorMarker(18,itProgramPosition);
464 SetupStemHintsInNode(orderedHStems,mSideBearing[1],*itProgramPosition);
465 ++itProgramPosition;
466 }
467
468 // write vstemhm command
469 if(orderedVStems.size() > 0)
470 {
471 itProgramPosition = InsertOperatorMarker(23,itProgramPosition);
472 SetupStemHintsInNode(orderedVStems,mSideBearing[0],*itProgramPosition);
473 ++itProgramPosition;
474 }
475
476 // if in the target stem definiton is not the first thing, create an initial 0ing mask
477 if(!IsStemHint(itProgramPosition->mMarkerType))
478 {
479 itProgramPosition = InsertOperatorMarker(19,itProgramPosition);
480 itProgramPosition->mOperands.push_back(0);
481 ++itProgramPosition;
482 }
483
484 // now loop the nodes, converting each stem set to a call.
485 while(itProgramPosition != mConversionProgram.end())
486 {
487 if(IsStemHint(itProgramPosition->mMarkerType))
488 {
489 ConversionNodeList::iterator firstNonHint = CollectHintIndexesFromHere(itProgramPosition);
490
491 // remove stem definitions
492 itProgramPosition = mConversionProgram.erase(itProgramPosition,firstNonHint);
493
494 // place hintmask as a replacement for the hint definitions
495 itProgramPosition = InsertOperatorMarker(19,itProgramPosition);
496 itProgramPosition->mOperands.push_back(GenerateHintMaskFromCollectedHints());
497 ++itProgramPosition;
498
499 }
500 else if(0x0c10 == itProgramPosition->mMarkerType)
501 {
502 // other, hints replacement here just clears the current hint collection
503 mCurrentHints.clear();
504 itProgramPosition = mConversionProgram.erase(itProgramPosition);
505 }
506 else
507 ++itProgramPosition;
508 }
509 }
510 else
511 {
512 // if not hint mask, just write the hint definitions and go.
513 // write initial hstem command
514 if(orderedHStems.size() > 0)
515 {
516 itProgramPosition = InsertOperatorMarker(1,itProgramPosition);
517 SetupStemHintsInNode(orderedHStems,mSideBearing[1],*itProgramPosition);
518 ++itProgramPosition;
519 }
520
521 // write vstem command
522 if(orderedVStems.size() > 0)
523 {
524 itProgramPosition = InsertOperatorMarker(3,itProgramPosition);
525 SetupStemHintsInNode(orderedVStems,mSideBearing[0],*itProgramPosition);
526 ++itProgramPosition;
527 }
528
529 // now erase all other stem hints
530 while(itProgramPosition != mConversionProgram.end())
531 {
532 if(IsStemHint(itProgramPosition->mMarkerType))
533 itProgramPosition = mConversionProgram.erase(itProgramPosition);
534 else
535 ++itProgramPosition;
536 }
537 }
538 }
539
InsertOperatorMarker(unsigned short inMarkerType,ConversionNodeList::iterator inInsertPosition)540 ConversionNodeList::iterator Type1ToType2Converter::InsertOperatorMarker(unsigned short inMarkerType,ConversionNodeList::iterator inInsertPosition)
541 {
542 ConversionNode node;
543 ConversionNodeList::iterator result = mConversionProgram.insert(inInsertPosition,node);
544 result->mMarkerType = inMarkerType;
545 return result;
546 }
547
SetupStemHintsInNode(const StemVector & inStems,long inOffsetCoordinate,ConversionNode & refNode)548 void Type1ToType2Converter::SetupStemHintsInNode(const StemVector& inStems,long inOffsetCoordinate,ConversionNode& refNode)
549 {
550 StemVector::const_iterator it = inStems.begin();
551 long lastCoordinate;
552
553 refNode.mOperands.push_back((*it)->mOrigin + inOffsetCoordinate);
554 refNode.mOperands.push_back((*it)->mExtent);
555 lastCoordinate = (*it)->mOrigin + (*it)->mExtent;
556 ++it;
557
558 for(; it != inStems.end();++it)
559 {
560 refNode.mOperands.push_back((*it)->mOrigin - lastCoordinate);
561 refNode.mOperands.push_back((*it)->mExtent);
562 lastCoordinate = (*it)->mOrigin + (*it)->mExtent;
563 }
564 }
565
IsStemHint(unsigned short inMarkerType)566 bool Type1ToType2Converter::IsStemHint(unsigned short inMarkerType)
567 {
568 return 1 == inMarkerType || 3 == inMarkerType || 0x0c01 == inMarkerType || 0x0c02 == inMarkerType;
569 }
570
CollectHintIndexesFromHere(ConversionNodeList::iterator inFirstStemHint)571 ConversionNodeList::iterator Type1ToType2Converter::CollectHintIndexesFromHere(ConversionNodeList::iterator inFirstStemHint)
572 {
573 ConversionNodeList::iterator it = inFirstStemHint;
574 for(; it != mConversionProgram.end();++it)
575 {
576 // hstem
577 if(1 == it->mMarkerType)
578 {
579 LongList::const_reverse_iterator itOperands = it->mOperands.rbegin();
580
581 long extent = *itOperands;
582 ++itOperands;
583 long origin = *itOperands;
584 mCurrentHints.insert(mHStems[Stem(origin,extent)]);
585
586 } // vstem
587 else if(3 == it->mMarkerType)
588 {
589 LongList::const_reverse_iterator itOperands = it->mOperands.rbegin();
590
591 long extent = *itOperands;
592 ++itOperands;
593 long origin = *itOperands;
594 mCurrentHints.insert(mVStems[Stem(origin,extent)]);
595 }// vstem3
596 else if(0x0c01 == it->mMarkerType)
597 {
598 LongList::const_reverse_iterator itOperands = it->mOperands.rbegin();
599
600 long extent = *itOperands;
601 ++itOperands;
602 long origin = *itOperands;
603 mCurrentHints.insert(mVStems[Stem(origin,extent)]);
604 ++itOperands;
605 extent = *itOperands;
606 ++itOperands;
607 origin = *itOperands;
608 mCurrentHints.insert(mVStems[Stem(origin,extent)]);
609 ++itOperands;
610 extent = *itOperands;
611 ++itOperands;
612 origin = *itOperands;
613 mCurrentHints.insert(mVStems[Stem(origin,extent)]);
614 }// hstem3
615 else if(0x0c02 == it->mMarkerType)
616 {
617 LongList::const_reverse_iterator itOperands = it->mOperands.rbegin();
618
619 long extent = *itOperands;
620 ++itOperands;
621 long origin = *itOperands;
622 mCurrentHints.insert(mHStems[Stem(origin,extent)]);
623 ++itOperands;
624 extent = *itOperands;
625 ++itOperands;
626 origin = *itOperands;
627 mCurrentHints.insert(mHStems[Stem(origin,extent)]);
628 ++itOperands;
629 extent = *itOperands;
630 ++itOperands;
631 origin = *itOperands;
632 mCurrentHints.insert(mHStems[Stem(origin,extent)]);
633 }
634 else // stop stem set
635 {
636 break;
637 }
638 }
639 return it; // return first instruction out of this set
640 }
641
GenerateHintMaskFromCollectedHints()642 long Type1ToType2Converter::GenerateHintMaskFromCollectedHints()
643 {
644 unsigned long hintMask = 0;
645 size_t totalHints = mHStems.size() + mVStems.size();
646 SizeTSet::iterator it = mCurrentHints.begin();
647 unsigned long maskByteSize = (unsigned long)(totalHints/8 + (totalHints % 8 != 0 ? 1:0));
648
649 for(; it != mCurrentHints.end();++it)
650 hintMask = hintMask | (1 << (maskByteSize*8 - 1 - *it));
651
652 return (long)hintMask;
653 }
654
655 static const unsigned long scMergeLimit = 40; // PDF implementation details show 48. i take a safety distance. just for kicks.
656
ConvertPathConsturction()657 void Type1ToType2Converter::ConvertPathConsturction()
658 {
659 // find the first removeto, and add sidebearing offset to it
660 ConversionNodeList::iterator it = mConversionProgram.begin();
661
662 for(; it != mConversionProgram.end(); ++it)
663 {
664 // either vmoveto, rmoveto or hmoveto
665 if(21 == it->mMarkerType || 22 == it->mMarkerType || 4 == it->mMarkerType)
666 {
667 long x;
668 long y;
669 LongList::iterator itOperands = it->mOperands.begin();
670
671 if(4 == it->mMarkerType) // vmoveto
672 {
673 x = mSideBearing[0];
674 y = (*itOperands) + mSideBearing[1];
675
676 }
677 else if(22 == it->mMarkerType) // hmoveto
678 {
679 x = (*itOperands) + mSideBearing[0];
680 y = mSideBearing[1];
681 }
682 else // rmoveto
683 {
684 x = (*itOperands) + mSideBearing[0];
685 ++itOperands;
686 y = (*itOperands) + mSideBearing[1];
687 }
688
689 it->mOperands.clear();
690 if(0 == x)
691 {
692 it->mMarkerType = 4; // vmoveto
693 it->mOperands.push_back(y);
694 }
695 else if(0 == y)
696 {
697 it->mMarkerType = 22; // hmoveto
698 it->mOperands.push_back(x);
699 }
700 else
701 {
702 it->mMarkerType = 21; // rmoveto
703 it->mOperands.push_back(x);
704 it->mOperands.push_back(y);
705 }
706 break;
707 }
708 }
709
710 // now, convert some rrcurvetos into vvcurvetos and hhcurvetos, if there are any [should be at first *moveto position now]
711 ConversionNodeList::iterator itFirstMoveto = it;
712 for(;it != mConversionProgram.end();++it)
713 {
714 if(8 == it->mMarkerType)
715 {
716 // look for either horizontal curves, or vertical curves
717 LongList::iterator itOperands = it->mOperands.begin();
718
719 long operands[6];
720 for(int i=0;i<6;++i,++itOperands)
721 operands[i] = *itOperands;
722
723 if(0 == operands[1] && 0 == operands[5])
724 {
725 it->mMarkerType = 27; // hhcurveto
726 it->mOperands.clear();
727 it->mOperands.push_back(operands[0]);
728 it->mOperands.push_back(operands[2]);
729 it->mOperands.push_back(operands[3]);
730 it->mOperands.push_back(operands[4]);
731 }
732 else if(0 == operands[0] && 0 == operands[4])
733 {
734 it->mMarkerType = 26; // vvcurveto
735 it->mOperands.clear();
736 it->mOperands.push_back(operands[1]);
737 it->mOperands.push_back(operands[2]);
738 it->mOperands.push_back(operands[3]);
739 it->mOperands.push_back(operands[5]);
740 }
741 }
742 }
743
744 it = itFirstMoveto; // reset iteration
745
746 // optimize multiple calls into one. we got *linetos and *curvetos
747 // start looking from the first moveto...as no path construction
748 // can occur before it
749 while(it != mConversionProgram.end())
750 {
751 switch(it->mMarkerType)
752 {
753 case(5): // rlineto
754 case(8): // rrcurveto
755 case(26): // vvcurveto [leaving aside optimizing prior rrcurveto here]
756 case(27): // hhcurveto [leaving aside optimizing prior rrcurveto here]
757 { // see if there are more same nodes and merge them
758 it = MergeSameOperators(it);
759 break;
760 }
761 case(6): // hlineto
762 {
763 it = MergeAltenratingOperators(it,7);
764 break;
765 }
766 case(7): // vlineto
767 {
768 it = MergeAltenratingOperators(it,6);
769 break;
770 }
771 case(30): // vhcurveto
772 {
773 ConversionNodeList::iterator itStarter = it;
774
775 it = MergeAltenratingOperators(it,31);
776
777 // optionally also merge the next rrcurveto, if it's starting
778 // point conforms to the operators list
779 if(it != mConversionProgram.end() && 8 == it->mMarkerType && (itStarter->mOperands.size() + it->mOperands.size() - 1 < scMergeLimit))
780 {
781 if(itStarter->mOperands.size() % 8 == 0)
782 {
783 // this curve needs to start with vertical
784 if(0 == *(it->mOperands.begin()))
785 {
786 LongList::iterator itOperands = it->mOperands.begin();
787 ++itOperands;
788 itStarter->mOperands.insert(itStarter->mOperands.end(),itOperands,it->mOperands.end());
789 it = mConversionProgram.erase(it);
790 }
791 }
792 else
793 {
794 // this curve needs to start with horizontal
795 LongList::iterator itOperands = it->mOperands.begin();
796 ++itOperands;
797 if(0 == *itOperands)
798 {
799 ++itOperands;
800 itStarter->mOperands.insert(itStarter->mOperands.end(),*(it->mOperands.begin()));
801 itStarter->mOperands.insert(itStarter->mOperands.end(),itOperands,it->mOperands.end());
802 // need to switch the last two
803 long dyf = itStarter->mOperands.back();
804 itStarter->mOperands.pop_back();
805 long dxf = itStarter->mOperands.back();
806 itStarter->mOperands.pop_back();
807 itStarter->mOperands.push_back(dyf);
808 itStarter->mOperands.push_back(dxf);
809 it = mConversionProgram.erase(it);
810 }
811 }
812 }
813 break;
814 }
815 case(31): // hvcurveto
816 {
817 ConversionNodeList::iterator itStarter = it;
818
819 it = MergeAltenratingOperators(it,30);
820
821 // optionally also merge the next rrcurveto, if it's starting
822 // point conforms to the operators list
823 if(it != mConversionProgram.end() && 8 == it->mMarkerType && (itStarter->mOperands.size() + it->mOperands.size() - 1 < scMergeLimit))
824 {
825 if(itStarter->mOperands.size() % 8 == 0)
826 {
827 // this curve needs to start with horizontal
828 LongList::iterator itOperands = it->mOperands.begin();
829 ++itOperands;
830 if(0 == *itOperands)
831 {
832 ++itOperands;
833 itStarter->mOperands.insert(itStarter->mOperands.end(),*(it->mOperands.begin()));
834 itStarter->mOperands.insert(itStarter->mOperands.end(),itOperands,it->mOperands.end());
835 // need to switch the last two
836 long dyf = itStarter->mOperands.back();
837 itStarter->mOperands.pop_back();
838 long dxf = itStarter->mOperands.back();
839 itStarter->mOperands.pop_back();
840 itStarter->mOperands.push_back(dyf);
841 itStarter->mOperands.push_back(dxf);
842 it = mConversionProgram.erase(it);
843 }
844 }
845 else
846 {
847 // this curve needs to start with vertical
848 if(0 == *(it->mOperands.begin()))
849 {
850 LongList::iterator itOperands = it->mOperands.begin();
851 ++itOperands;
852 itStarter->mOperands.insert(itStarter->mOperands.end(),itOperands,it->mOperands.end());
853 it = mConversionProgram.erase(it);
854 }
855 }
856 }
857 break;
858 }
859 default:
860 ++it;
861 }
862 }
863 }
864
MergeSameOperators(ConversionNodeList::iterator inStartingNode)865 ConversionNodeList::iterator Type1ToType2Converter::MergeSameOperators(ConversionNodeList::iterator inStartingNode)
866 {
867 return MergeSameOperators(inStartingNode,inStartingNode->mMarkerType);
868 }
869
MergeSameOperators(ConversionNodeList::iterator inStartingNode,unsigned short inOpCode)870 ConversionNodeList::iterator Type1ToType2Converter::MergeSameOperators(ConversionNodeList::iterator inStartingNode,
871 unsigned short inOpCode)
872 {
873 ConversionNodeList::iterator itNext = inStartingNode;
874 ++itNext;
875
876 while(inOpCode == itNext->mMarkerType && (inStartingNode->mOperands.size() + itNext->mOperands.size() < scMergeLimit))
877 {
878 inStartingNode->mOperands.insert(inStartingNode->mOperands.end(),itNext->mOperands.begin(),itNext->mOperands.end());
879 itNext = mConversionProgram.erase(itNext);
880 }
881 return itNext;
882 }
883
MergeAltenratingOperators(ConversionNodeList::iterator inStartingNode,unsigned short inAlternatingOpcode)884 ConversionNodeList::iterator Type1ToType2Converter::MergeAltenratingOperators(ConversionNodeList::iterator inStartingNode,
885 unsigned short inAlternatingOpcode)
886 {
887 ConversionNodeList::iterator itNext = inStartingNode;
888 ++itNext;
889 unsigned short currentMarker = inAlternatingOpcode;
890
891 while(currentMarker == itNext->mMarkerType && (inStartingNode->mOperands.size() + itNext->mOperands.size() < scMergeLimit))
892 {
893 inStartingNode->mOperands.insert(inStartingNode->mOperands.end(),itNext->mOperands.begin(),itNext->mOperands.end());
894 itNext = mConversionProgram.erase(itNext);
895 currentMarker = inStartingNode->mMarkerType == currentMarker ? inAlternatingOpcode:inStartingNode->mMarkerType;
896 }
897 return itNext;
898 }
899
AddInitialWidthParameter()900 void Type1ToType2Converter::AddInitialWidthParameter()
901 {
902 // the first appearance of one of the following must have the width agrument added to it:
903 // hstem, hstemhm, vstem, vstemhm, [cntrmask], [hintmask], hmoveto, vmoveto,
904 // rmoveto, or endchar
905 // [note theat cntrmask i'm not using and hintmask can't appear before stem hints...so not checking fo these]
906
907 ConversionNodeList::iterator it = mConversionProgram.begin();
908
909 for(; it != mConversionProgram.end();++it)
910 {
911 if( 1 == it->mMarkerType || // hstem
912 3 == it->mMarkerType || // vstem
913 18 == it->mMarkerType || // hstemhm
914 23 == it->mMarkerType || // vstemhm
915 4 == it->mMarkerType || // vmoveto
916 21 == it->mMarkerType || // rmoveto
917 22 == it->mMarkerType || // hmoveto
918 14 == it->mMarkerType) // endchar
919 {
920 it->mOperands.push_front(mWidth[0]);
921 break;
922 }
923 }
924 }
925
WriteProgramToStream(IByteWriter * inByteWriter)926 EStatusCode Type1ToType2Converter::WriteProgramToStream(IByteWriter* inByteWriter)
927 {
928 Type2CharStringWriter commandWriter(inByteWriter);
929 EStatusCode status = PDFHummus::eSuccess;
930
931 ConversionNodeList::iterator it = mConversionProgram.begin();
932
933 for(; it != mConversionProgram.end() && PDFHummus::eSuccess == status; ++it)
934 {
935 LongList::iterator itOperands = it->mOperands.begin();
936
937 if(19 == it->mMarkerType) // hintmask
938 {
939 status = commandWriter.WriteHintMask((unsigned long)(*itOperands),(unsigned long)(mHStems.size() + mVStems.size()));
940 }
941 else
942 {
943 for(; itOperands != it->mOperands.end() && PDFHummus::eSuccess == status;++itOperands)
944 status = commandWriter.WriteIntegerOperand(*itOperands);
945
946 if(PDFHummus::eSuccess == status)
947 {
948 // if marker type is vstemhm, and next one is hintmask, no need to write vstemhm
949 if(23 == it->mMarkerType)
950 {
951 ConversionNodeList::iterator itNext = it;
952 ++itNext;
953 if(itNext->mMarkerType != 19)
954 status = commandWriter.WriteOperator(it->mMarkerType);
955 }
956 else
957 status = commandWriter.WriteOperator(it->mMarkerType);
958
959 }
960 }
961 }
962 return status;
963 }