text: /*
     Source File : PDFUsedFont.cpp
  
  
     Copyright 2011 Gal Kahana PDFWriter
  
     Licensed under the Apache License, Version 2.0 (the "License");
     you may not use this file except in compliance with the License.
     You may obtain a copy of the License at
  
         http://www.apache.org/licenses/LICENSE-2.0
  
     Unless required by applicable law or agreed to in writing, software
     distributed under the License is distributed on an "AS IS" BASIS,
     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     See the License for the specific language governing permissions and
     limitations under the License.
  
     
  */
  #include "PDFUsedFont.h"
  #include "IWrittenFont.h"
  #include "UnicodeString.h"
  #include "ObjectsContext.h"
  #include "DictionaryContext.h"
  #include "PDFParser.h"
  #include "PDFObjectCast.h"
  #include "PDFDictionary.h"
  #include "PDFIndirectObjectReference.h"
  
  #include FT_GLYPH_H
  
  
  using namespace PDFHummus;
  
  PDFUsedFont::PDFUsedFont(FT_Face inInputFace,
  						 const std::string& inFontFilePath,
  						 const std::string& inAdditionalMetricsFontFilePath,
                           long inFontIndex,
  						 ObjectsContext* inObjectsContext):mFaceWrapper(inInputFace,inFontFilePath,inAdditionalMetricsFontFilePath,inFontIndex)
  {
  	mObjectsContext = inObjectsContext;
  	mWrittenFont = NULL;
  }
  
  PDFUsedFont::~PDFUsedFont(void)
  {
  	delete mWrittenFont;
  }
  
  bool PDFUsedFont::IsValid()
  {
  	return mFaceWrapper.IsValid();
  }
  
  EStatusCode PDFUsedFont::EncodeStringForShowing(const GlyphUnicodeMappingList& inText,
  												ObjectIDType &outFontObjectToUse,
  												UShortList& outCharactersToUse,
  												bool& outTreatCharactersAsCID)
  {
  	if (inText.empty()) {
  		outFontObjectToUse = 0;
  		outTreatCharactersAsCID = false;
  		return PDFHummus::eSuccess;
  	}
  
  	if(!mWrittenFont)
  		mWrittenFont = mFaceWrapper.CreateWrittenFontObject(mObjectsContext);
  
  	mWrittenFont->AppendGlyphs(inText,outCharactersToUse,outTreatCharactersAsCID,outFontObjectToUse);
  
  	return PDFHummus::eSuccess;
  }
  
  EStatusCode PDFUsedFont::TranslateStringToGlyphs(const std::string& inText,GlyphUnicodeMappingList& outGlyphsUnicodeMapping)
  {
  	UIntList glyphs;
  	UnicodeString unicode;
  
  	EStatusCode status = unicode.FromUTF8(inText);
  	if(status != PDFHummus::eSuccess)
  		return status;
  
  
  	status = mFaceWrapper.GetGlyphsForUnicodeText(unicode.GetUnicodeList(),glyphs);
  
  	ULongList::const_iterator itUnicode = unicode.GetUnicodeList().begin();
  	UIntList::iterator itGlyphs = glyphs.begin();
  
  	for(; itUnicode != unicode.GetUnicodeList().end(); ++itUnicode,++itGlyphs)
  		outGlyphsUnicodeMapping.push_back(GlyphUnicodeMapping(*itGlyphs,*itUnicode));
  
  	return status;
  }
  
  EStatusCode PDFUsedFont::EncodeStringsForShowing(const GlyphUnicodeMappingListList& inText,
  												ObjectIDType &outFontObjectToUse,
  												UShortListList& outCharactersToUse,
  												bool& outTreatCharactersAsCID)
  {
  	if (inText.empty()) {
  		outFontObjectToUse = 0;
  		outTreatCharactersAsCID = false;
  		return PDFHummus::eSuccess;
  	}
  
  	if(!mWrittenFont)
  		mWrittenFont = mFaceWrapper.CreateWrittenFontObject(mObjectsContext);
  
  	mWrittenFont->AppendGlyphs(inText,outCharactersToUse,outTreatCharactersAsCID,outFontObjectToUse);
  
  	return PDFHummus::eSuccess;
  }
  
  EStatusCode PDFUsedFont::WriteFontDefinition(bool inEmbedFont)
  {
      // note that written font may be empty, in case no glyphs were used for this font! in the empty case, just dont write the def
      if(!mWrittenFont)
          return eSuccess;
      else
          return mWrittenFont->WriteFontDefinition(mFaceWrapper,inEmbedFont);
  }
  
  EStatusCode PDFUsedFont::WriteState(ObjectsContext* inStateWriter,ObjectIDType inObjectID)
  {
  	inStateWriter->StartNewIndirectObject(inObjectID);
  	DictionaryContext* pdfUsedFontObject = inStateWriter->StartDictionary();
  
  	pdfUsedFontObject->WriteKey("Type");
  	pdfUsedFontObject->WriteNameValue("PDFUsedFont");
  
  	ObjectIDType writtenFontObject = 0;
  
  	if(mWrittenFont)
  	{
  		writtenFontObject = inStateWriter->GetInDirectObjectsRegistry().AllocateNewObjectID();
  
  		pdfUsedFontObject->WriteKey("mWrittenFont");
  		pdfUsedFontObject->WriteNewObjectReferenceValue(writtenFontObject);
  	}
  
  	inStateWriter->EndDictionary(pdfUsedFontObject);
  	inStateWriter->EndIndirectObject();
  
  	if(mWrittenFont)
  		mWrittenFont->WriteState(inStateWriter,writtenFontObject);
  
  	return PDFHummus::eSuccess;
  }
  
  EStatusCode PDFUsedFont::ReadState(PDFParser* inStateReader,ObjectIDType inObjectID)
  {
  	PDFObjectCastPtr<PDFDictionary> pdfUsedFontState(inStateReader->ParseNewObject(inObjectID));
  
  	PDFObjectCastPtr<PDFIndirectObjectReference> writtenFontReference(pdfUsedFontState->QueryDirectObject("mWrittenFont"));
  
  	if(!writtenFontReference)
  		return PDFHummus::eSuccess;
  
  	if(mWrittenFont)
  		delete mWrittenFont;
  
  	mWrittenFont = mFaceWrapper.CreateWrittenFontObject(mObjectsContext);
  	if(!mWrittenFont)
  		return PDFHummus::eFailure;
  
  	return mWrittenFont->ReadState(inStateReader,writtenFontReference->mObjectID);
  }
  
  FreeTypeFaceWrapper* PDFUsedFont::GetFreeTypeFont()
  {
      return &mFaceWrapper;
  }
  
  void PDFUsedFont::GetUnicodeGlyphs(const std::string& inText, UIntList& glyphs)
  {
  	UnicodeString unicode;
          
      unicode.FromUTF8(inText);
  	mFaceWrapper.GetGlyphsForUnicodeText(unicode.GetUnicodeList(),glyphs);
  }
  
  PDFUsedFont::TextMeasures PDFUsedFont::CalculateTextDimensions(const std::string& inText,long inFontSize)
  {
  	UIntList glyphs;
      UnicodeString unicode;
          
      unicode.FromUTF8(inText);
  	mFaceWrapper.GetGlyphsForUnicodeText(unicode.GetUnicodeList(),glyphs);
  	return CalculateTextDimensions(glyphs,inFontSize);
  }
  
  PDFUsedFont::TextMeasures PDFUsedFont::CalculateTextDimensions(const UIntList& inGlyphsList,long inFontSize)
  {
      // now calculate the placement bounding box. using the algorithm described in the FreeType turtorial part 2, minus the kerning part, and with no scale
  
      // first, calculate the pen advancements
      int           pen_x, pen_y;
      std::list<FT_Vector> pos;
      pen_x = 0;   /* start at (0,0) */
      pen_y = 0;
      
      UIntList::const_iterator it = inGlyphsList.begin();
      for(; it != inGlyphsList.end();++it)
      {
          pos.push_back(FT_Vector());
  
          pos.back().x = pen_x;
          pos.back().y = pen_y;
  
          pen_x += mFaceWrapper.GetGlyphWidth(*it);
      }
      
      // now let's combine with the bbox, so we get the nice bbox for the whole string
      
      FT_BBox  bbox;
      FT_BBox  glyph_bbox;
      bbox.xMin = bbox.yMin =  32000;
      bbox.xMax = bbox.yMax = -32000;
      
      it = inGlyphsList.begin();
      std::list<FT_Vector>::iterator itPos = pos.begin();
      
      for(; it != inGlyphsList.end();++it,++itPos)
      {
  		mFaceWrapper.LoadGlyph(mFaceWrapper.GetGlyphIndexInFreeTypeIndexes(*it));
          FT_Glyph aGlyph;
          FT_Get_Glyph( mFaceWrapper->glyph,&aGlyph);
          FT_Glyph_Get_CBox(aGlyph, FT_GLYPH_BBOX_UNSCALED,&glyph_bbox);
          FT_Done_Glyph(aGlyph);
          
          glyph_bbox.xMin = mFaceWrapper.GetInPDFMeasurements(glyph_bbox.xMin);
          glyph_bbox.xMax = mFaceWrapper.GetInPDFMeasurements(glyph_bbox.xMax);
          glyph_bbox.yMin = mFaceWrapper.GetInPDFMeasurements(glyph_bbox.yMin);
          glyph_bbox.yMax = mFaceWrapper.GetInPDFMeasurements(glyph_bbox.yMax);
          
          glyph_bbox.xMin += itPos->x;
          glyph_bbox.xMax += itPos->x;
          glyph_bbox.yMin += itPos->y;
          glyph_bbox.yMax += itPos->y;
      
          
          if ( glyph_bbox.xMin < bbox.xMin )
              bbox.xMin = glyph_bbox.xMin;
          
          if ( glyph_bbox.yMin < bbox.yMin )
              bbox.yMin = glyph_bbox.yMin;
          
          if ( glyph_bbox.xMax > bbox.xMax )
              bbox.xMax = glyph_bbox.xMax;
          
          if ( glyph_bbox.yMax > bbox.yMax )
              bbox.yMax = glyph_bbox.yMax;
      }
      if ( bbox.xMin > bbox.xMax )
      {
          bbox.xMin = 0;
          bbox.yMin = 0;
          bbox.xMax = 0;
          bbox.yMax = 0;
      }
  
  	PDFUsedFont::TextMeasures result;
  
  	result.xMin = bbox.xMin*inFontSize/1000;
  	result.yMin = bbox.yMin*inFontSize/1000;
  	result.xMax = bbox.xMax*inFontSize/1000;
  	result.yMax = bbox.yMax*inFontSize/1000;
  	result.width = (bbox.xMax-bbox.xMin)*inFontSize/1000;
  	result.height = (bbox.yMax-bbox.yMin)*inFontSize/1000;
  
  	return result;
  }
  
  double PDFUsedFont::CalculateTextAdvance(const std::string& inText,double inFontSize)
  {
  	UIntList glyphs;
  	GetUnicodeGlyphs(inText, glyphs);
  	return CalculateTextAdvance(glyphs,inFontSize);
  }
  
  double PDFUsedFont::CalculateTextAdvance(const UIntList& inGlyphsList,double inFontSize)
  {
      FT_Pos pen = 0;
      UIntList::const_iterator it = inGlyphsList.begin();
      for(; it != inGlyphsList.end();++it)
      {
  		FT_Pos adv;
  		if (mAdvanceCache.count(*it) > 0) 
  			adv = mAdvanceCache[*it];
  		else {
  			adv = mFaceWrapper.GetGlyphWidth(*it); //potentially very expensive!
  			if (mAdvanceCache.size() <= AdvanceCacheLimit) //dumb limit should cover typical usage, implement LRU if it's a problem
  				mAdvanceCache[*it] = adv;
  		}
  		pen += adv;
      }
  	return pen * inFontSize / 1000.0;
  }
  
  bool PDFUsedFont::EnumeratePaths(IOutlineEnumerator& target, const std::string& inText,double inFontSize)
  {
  	UIntList glyphs;
  	GetUnicodeGlyphs(inText, glyphs);
  	return EnumeratePaths(target, glyphs,inFontSize);
  }
  
  bool PDFUsedFont::EnumeratePaths(IOutlineEnumerator& target, const UIntList& inGlyphsList,double inFontSize)
  {
  	bool status = true;
      target.BeginEnum(inFontSize);
  	for( UIntList::const_iterator it = inGlyphsList.begin(); it != inGlyphsList.end(); ++it )
      {
  		status = mFaceWrapper.GetGlyphOutline(*it, target);
  		if (!status) break;
  
  		// Keep track of glyphs' advance
  		double adv = mFaceWrapper.GetGlyphWidth(*it);
  		target.MoveBasepoint(adv * inFontSize / 1000.0, 0);
      }
  	return status;
  }
  
  void PDFUsedFont::IOutlineEnumerator::BeginEnum(double scale)
  {
  	mFontScale = scale;
  }
  
  PDFUsedFont::IOutlineEnumerator::IOutlineEnumerator(double base_x, double base_y)
  {
  	mBase_x = base_x;
  	mBase_y = base_y;
  }
  
  void PDFUsedFont::IOutlineEnumerator::MoveBasepoint(double dx, double dy)
  {
  	mBase_x += dx;
  	mBase_y += dy;
  }
  
  bool PDFUsedFont::IOutlineEnumerator::Moveto(FT_Short x, FT_Short y)
  {
  	double scale = mFontScale / UPM();
  	return Moveto(mBase_x + scale * x, mBase_y + scale * y);
  }
  
  bool PDFUsedFont::IOutlineEnumerator::Lineto(FT_Short x, FT_Short y)
  {
  	double scale = mFontScale / UPM();
  	return Lineto(mBase_x + scale * x, mBase_y + scale * y);
  }
  
  bool PDFUsedFont::IOutlineEnumerator::Curveto(FT_Short x1, FT_Short y1, FT_Short x2, FT_Short y2, FT_Short x3, FT_Short y3)
  {
  	double scale = mFontScale / UPM();
  	return Curveto(mBase_x + scale * x1, mBase_y + scale * y1,
  	               mBase_x + scale * x2, mBase_y + scale * y2,
  	               mBase_x + scale * x3, mBase_y + scale * y3);
  }
  
  bool PDFUsedFont::IOutlineEnumerator::Close()
  {
  	return Closepath();
  }
  