| /************************************************************** |
| * |
| * Licensed to the Apache Software Foundation (ASF) under one |
| * or more contributor license agreements. See the NOTICE file |
| * distributed with this work for additional information |
| * regarding copyright ownership. The ASF licenses this file |
| * to you 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. |
| * |
| *************************************************************/ |
| |
| |
| |
| // Description: Implements the Graphite interfaces with access to the |
| // platform's font and graphics systems. |
| |
| // MARKER(update_precomp.py): autogen include statement, do not remove |
| #include "precompiled_vcl.hxx" |
| |
| // We need this to enable namespace support in libgrengine headers. |
| #define GR_NAMESPACE |
| |
| // Header files |
| // |
| // Standard Library |
| #include <string> |
| #include <cassert> |
| // Libraries |
| #include <rtl/string.hxx> |
| #include <rtl/ustring.hxx> |
| #include <i18npool/mslangid.hxx> |
| // Platform |
| #ifndef WNT |
| #include <unx/saldisp.hxx> |
| #include <salgdi.hxx> |
| |
| // Module |
| #include "gcach_ftyp.hxx" |
| #include <graphite_features.hxx> |
| #include <graphite_adaptors.hxx> |
| |
| // Module private type definitions and forward declarations. |
| // |
| using gr::GrResult; |
| namespace |
| { |
| inline float from_hinted(const int x) { |
| return static_cast<float>(x + 32) / 64.0; |
| } |
| typedef std::hash_map<long,bool> SilfMap; |
| SilfMap sSilfMap; |
| } |
| extern FT_Error (*pFTEmbolden)(FT_GlyphSlot); |
| extern FT_Error (*pFTOblique)(FT_GlyphSlot); |
| |
| // class CharacterRenderProperties implentation. |
| // |
| FontProperties::FontProperties(const FreetypeServerFont &font) throw() |
| { |
| clrFore = gr::kclrBlack; |
| clrBack = gr::kclrTransparent; |
| |
| pixHeight = from_hinted(font.GetMetricsFT().height); |
| |
| switch (font.GetFontSelData().meWeight) |
| { |
| case WEIGHT_SEMIBOLD: case WEIGHT_BOLD: |
| case WEIGHT_ULTRABOLD: case WEIGHT_BLACK: |
| fBold = true; |
| break; |
| default : |
| fBold = false; |
| } |
| |
| switch (font.GetFontSelData().meItalic) |
| { |
| case ITALIC_NORMAL: case ITALIC_OBLIQUE: |
| fItalic = true; |
| break; |
| default : |
| fItalic = false; |
| } |
| |
| // Get the font name, but prefix with file name hash in case |
| // there are 2 fonts on the system with the same face name |
| sal_Int32 nHashCode = font.GetFontFileName()->hashCode(); |
| ::rtl::OUStringBuffer nHashFaceName; |
| nHashFaceName.append(nHashCode, 16); |
| const sal_Unicode * name = font.GetFontSelData().maName.GetBuffer(); |
| nHashFaceName.append(name); |
| |
| const size_t name_sz = std::min(sizeof szFaceName/sizeof(wchar_t)-1, |
| static_cast<size_t>(nHashFaceName.getLength())); |
| |
| std::copy(nHashFaceName.getStr(), nHashFaceName.getStr() + name_sz, szFaceName); |
| szFaceName[name_sz] = '\0'; |
| } |
| |
| // class GraphiteFontAdaptor implementaion. |
| // |
| GraphiteFontAdaptor::GraphiteFontAdaptor(ServerFont & sfont, const sal_Int32 dpiX, const sal_Int32 dpiY) |
| : mrFont(static_cast<FreetypeServerFont &>(sfont)), |
| maFontProperties(static_cast<FreetypeServerFont &>(sfont)), |
| mnDpiX(dpiX), |
| mnDpiY(dpiY), |
| mfAscent(from_hinted(static_cast<FreetypeServerFont &>(sfont).GetMetricsFT().ascender)), |
| mfDescent(from_hinted(static_cast<FreetypeServerFont &>(sfont).GetMetricsFT().descender)), |
| mfEmUnits(static_cast<FreetypeServerFont &>(sfont).GetMetricsFT().y_ppem), |
| mpFeatures(NULL) |
| { |
| const rtl::OString aLang = MsLangId::convertLanguageToIsoByteString( sfont.GetFontSelData().meLanguage ); |
| rtl::OString name = rtl::OUStringToOString( |
| sfont.GetFontSelData().maTargetName, RTL_TEXTENCODING_UTF8 ); |
| #ifdef DEBUG |
| printf("GraphiteFontAdaptor %lx %s italic=%u bold=%u\n", (long)this, name.getStr(), |
| maFontProperties.fItalic, maFontProperties.fBold); |
| #endif |
| sal_Int32 nFeat = name.indexOf(grutils::GrFeatureParser::FEAT_PREFIX) + 1; |
| if (nFeat > 0) |
| { |
| rtl::OString aFeat = name.copy(nFeat, name.getLength() - nFeat); |
| mpFeatures = new grutils::GrFeatureParser(*this, aFeat.getStr(), aLang.getStr()); |
| #ifdef DEBUG |
| printf("GraphiteFontAdaptor %s/%s/%s %x language %d features %d errors\n", |
| rtl::OUStringToOString( sfont.GetFontSelData().maName, |
| RTL_TEXTENCODING_UTF8 ).getStr(), |
| rtl::OUStringToOString( sfont.GetFontSelData().maTargetName, |
| RTL_TEXTENCODING_UTF8 ).getStr(), |
| rtl::OUStringToOString( sfont.GetFontSelData().maSearchName, |
| RTL_TEXTENCODING_UTF8 ).getStr(), |
| sfont.GetFontSelData().meLanguage, |
| (int)mpFeatures->getFontFeatures(NULL), mpFeatures->parseErrors()); |
| #endif |
| } |
| else |
| { |
| mpFeatures = new grutils::GrFeatureParser(*this, aLang.getStr()); |
| } |
| } |
| |
| GraphiteFontAdaptor::GraphiteFontAdaptor(const GraphiteFontAdaptor &rhs) throw() |
| : Font(rhs), |
| mrFont (rhs.mrFont), maFontProperties(rhs.maFontProperties), |
| mnDpiX(rhs.mnDpiX), mnDpiY(rhs.mnDpiY), |
| mfAscent(rhs.mfAscent), mfDescent(rhs.mfDescent), mfEmUnits(rhs.mfEmUnits), |
| mpFeatures(NULL) |
| { |
| if (rhs.mpFeatures) mpFeatures = new grutils::GrFeatureParser(*(rhs.mpFeatures)); |
| } |
| |
| |
| GraphiteFontAdaptor::~GraphiteFontAdaptor() throw() |
| { |
| maGlyphMetricMap.clear(); |
| if (mpFeatures) delete mpFeatures; |
| mpFeatures = NULL; |
| } |
| |
| void GraphiteFontAdaptor::UniqueCacheInfo(ext_std::wstring & face_name_out, bool & bold_out, bool & italic_out) |
| { |
| face_name_out = maFontProperties.szFaceName; |
| bold_out = maFontProperties.fBold; |
| italic_out = maFontProperties.fItalic; |
| } |
| |
| bool GraphiteFontAdaptor::IsGraphiteEnabledFont(ServerFont & font) throw() |
| { |
| // NOTE: this assumes that the same FTFace pointer won't be reused, |
| // so FtFontInfo::ReleaseFaceFT must only be called at shutdown. |
| FreetypeServerFont & aFtFont = dynamic_cast<FreetypeServerFont &>(font); |
| FT_Face aFace = reinterpret_cast<FT_FaceRec_*>(aFtFont.GetFtFace()); |
| SilfMap::iterator i = sSilfMap.find(reinterpret_cast<long>(aFace)); |
| if (i != sSilfMap.end()) |
| { |
| #ifdef DEBUG |
| if (static_cast<bool>(aFtFont.GetTable("Silf", 0)) != (*i).second) |
| printf("Silf cache font mismatch\n"); |
| #endif |
| return (*i).second; |
| } |
| bool bHasSilf = aFtFont.GetTable("Silf", 0); |
| sSilfMap[reinterpret_cast<long>(aFace)] = bHasSilf; |
| return bHasSilf; |
| } |
| |
| |
| gr::Font * GraphiteFontAdaptor::copyThis() { |
| return new GraphiteFontAdaptor(*this); |
| } |
| |
| |
| unsigned int GraphiteFontAdaptor::getDPIx() { |
| return mnDpiX; |
| } |
| |
| |
| unsigned int GraphiteFontAdaptor::getDPIy() { |
| return mnDpiY; |
| } |
| |
| |
| float GraphiteFontAdaptor::ascent() { |
| return mfAscent; |
| } |
| |
| |
| float GraphiteFontAdaptor::descent() { |
| return mfDescent; |
| } |
| |
| |
| bool GraphiteFontAdaptor::bold() { |
| return maFontProperties.fBold; |
| } |
| |
| |
| bool GraphiteFontAdaptor::italic() { |
| return maFontProperties.fItalic; |
| } |
| |
| |
| float GraphiteFontAdaptor::height() { |
| return maFontProperties.pixHeight; |
| } |
| |
| |
| void GraphiteFontAdaptor::getFontMetrics(float * ascent_out, float * descent_out, float * em_square_out) { |
| if (ascent_out) *ascent_out = mfAscent; |
| if (descent_out) *descent_out = mfDescent; |
| if (em_square_out) *em_square_out = mfEmUnits; |
| } |
| |
| |
| const void * GraphiteFontAdaptor::getTable(gr::fontTableId32 table_id, size_t * buffer_sz) |
| { |
| char tag_name[5] = {char(table_id >> 24), char(table_id >> 16), char(table_id >> 8), char(table_id), 0}; |
| sal_uLong temp = *buffer_sz; |
| |
| const void * const tbl_buf = static_cast<FreetypeServerFont &>(mrFont).GetTable(tag_name, &temp); |
| *buffer_sz = temp; |
| |
| return tbl_buf; |
| } |
| |
| #define fix26_6(x) (x >> 6) + (x & 32 ? (x > 0 ? 1 : 0) : (x < 0 ? -1 : 0)) |
| |
| // Return the glyph's metrics in pixels. |
| void GraphiteFontAdaptor::getGlyphMetrics(gr::gid16 nGlyphId, gr::Rect & aBounding, gr::Point & advances) |
| { |
| // There used to be problems when orientation was set however, this no |
| // longer seems to be the case and the Glyph Metric cache in |
| // FreetypeServerFont is more efficient since it lasts between calls to VCL |
| #if 1 |
| const GlyphMetric & metric = mrFont.GetGlyphMetric(nGlyphId); |
| |
| aBounding.right = aBounding.left = metric.GetOffset().X(); |
| aBounding.bottom = aBounding.top = -metric.GetOffset().Y(); |
| aBounding.right += metric.GetSize().Width(); |
| aBounding.bottom -= metric.GetSize().Height(); |
| |
| advances.x = metric.GetDelta().X(); |
| advances.y = -metric.GetDelta().Y(); |
| |
| #else |
| // The problem with the code below is that the cache only lasts |
| // as long as the life time of the GraphiteFontAdaptor, which |
| // is created once per call to X11SalGraphics::GetTextLayout |
| GlyphMetricMap::const_iterator gm_itr = maGlyphMetricMap.find(nGlyphId); |
| if (gm_itr != maGlyphMetricMap.end()) |
| { |
| // We've cached the results from last time. |
| aBounding = gm_itr->second.first; |
| advances = gm_itr->second.second; |
| } |
| else |
| { |
| // We need to look up the glyph. |
| FT_Int nLoadFlags = mrFont.GetLoadFlags(); |
| |
| FT_Face aFace = reinterpret_cast<FT_Face>(mrFont.GetFtFace()); |
| if (!aFace) |
| { |
| aBounding.top = aBounding.bottom = aBounding.left = aBounding.right = 0; |
| advances.x = advances.y = 0; |
| return; |
| } |
| FT_Error aStatus = -1; |
| aStatus = FT_Load_Glyph(aFace, nGlyphId, nLoadFlags); |
| if( aStatus != FT_Err_Ok || (!aFace->glyph)) |
| { |
| aBounding.top = aBounding.bottom = aBounding.left = aBounding.right = 0; |
| advances.x = advances.y = 0; |
| return; |
| } |
| // check whether we need synthetic bold/italic otherwise metric is wrong |
| if (mrFont.NeedsArtificialBold() && pFTEmbolden) |
| (*pFTEmbolden)(aFace->glyph); |
| |
| if (mrFont.NeedsArtificialItalic() && pFTOblique) |
| (*pFTOblique)(aFace->glyph); |
| |
| const FT_Glyph_Metrics &gm = aFace->glyph->metrics; |
| |
| // Fill out the bounding box an advances. |
| aBounding.top = aBounding.bottom = fix26_6(gm.horiBearingY); |
| aBounding.bottom -= fix26_6(gm.height); |
| aBounding.left = aBounding.right = fix26_6(gm.horiBearingX); |
| aBounding.right += fix26_6(gm.width); |
| advances.x = fix26_6(gm.horiAdvance); |
| advances.y = 0; |
| |
| // Now add an entry to our metrics map. |
| maGlyphMetricMap[nGlyphId] = std::make_pair(aBounding, advances); |
| } |
| #endif |
| } |
| |
| #endif |