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 }