1 /**************************************************************
2  *
3  * Licensed to the Apache Software Foundation (ASF) under one
4  * or more contributor license agreements.  See the NOTICE file
5  * distributed with this work for additional information
6  * regarding copyright ownership.  The ASF licenses this file
7  * to you under the Apache License, Version 2.0 (the
8  * "License"); you may not use this file except in compliance
9  * with the License.  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,
14  * software distributed under the License is distributed on an
15  * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
16  * KIND, either express or implied.  See the License for the
17  * specific language governing permissions and limitations
18  * under the License.
19  *
20  *************************************************************/
21 
22 
23 
24 // Description: Implements the Graphite interfaces with access to the
25 //              platform's font and graphics systems.
26 
27 // MARKER(update_precomp.py): autogen include statement, do not remove
28 #include "precompiled_vcl.hxx"
29 
30 // We need this to enable namespace support in libgrengine headers.
31 #define GR_NAMESPACE
32 
33 // Header files
34 //
35 // Standard Library
36 #include <string>
37 #include <cassert>
38 // Libraries
39 #include <rtl/string.hxx>
40 #include <rtl/ustring.hxx>
41 #include <i18npool/mslangid.hxx>
42 // Platform
43 #ifndef WNT
44 #include <unx/saldisp.hxx>
45 #include <salgdi.hxx>
46 
47 // Module
48 #include "gcach_ftyp.hxx"
49 #include <graphite_features.hxx>
50 #include <graphite_adaptors.hxx>
51 
52 // Module private type definitions and forward declarations.
53 //
54 using gr::GrResult;
55 namespace
56 {
from_hinted(const int x)57     inline float from_hinted(const int x) {
58         return static_cast<float>(x + 32) / 64.0;
59     }
60     typedef std::hash_map<long,bool> SilfMap;
61     SilfMap sSilfMap;
62 }
63 extern FT_Error (*pFTEmbolden)(FT_GlyphSlot);
64 extern FT_Error (*pFTOblique)(FT_GlyphSlot);
65 
66 // class CharacterRenderProperties implentation.
67 //
FontProperties(const FreetypeServerFont & font)68 FontProperties::FontProperties(const FreetypeServerFont &font) throw()
69 {
70     clrFore = gr::kclrBlack;
71     clrBack = gr::kclrTransparent;
72 
73     pixHeight = from_hinted(font.GetMetricsFT().height);
74 
75     switch (font.GetFontSelData().meWeight)
76     {
77         case WEIGHT_SEMIBOLD: case WEIGHT_BOLD:
78         case WEIGHT_ULTRABOLD: case WEIGHT_BLACK:
79             fBold = true;
80             break;
81         default :
82             fBold = false;
83     }
84 
85     switch (font.GetFontSelData().meItalic)
86     {
87         case ITALIC_NORMAL: case ITALIC_OBLIQUE:
88             fItalic = true;
89             break;
90         default :
91             fItalic = false;
92     }
93 
94     // Get the font name, but prefix with file name hash in case
95     // there are 2 fonts on the system with the same face name
96     sal_Int32 nHashCode = font.GetFontFileName()->hashCode();
97     ::rtl::OUStringBuffer nHashFaceName;
98     nHashFaceName.append(nHashCode, 16);
99     const sal_Unicode    * name = font.GetFontSelData().maName.GetBuffer();
100     nHashFaceName.append(name);
101 
102     const size_t name_sz = std::min(sizeof szFaceName/sizeof(wchar_t)-1,
103                     static_cast<size_t>(nHashFaceName.getLength()));
104 
105     std::copy(nHashFaceName.getStr(), nHashFaceName.getStr() + name_sz, szFaceName);
106     szFaceName[name_sz] = '\0';
107 }
108 
109 // class GraphiteFontAdaptor implementaion.
110 //
GraphiteFontAdaptor(ServerFont & sfont,const sal_Int32 dpiX,const sal_Int32 dpiY)111 GraphiteFontAdaptor::GraphiteFontAdaptor(ServerFont & sfont, const sal_Int32 dpiX, const sal_Int32 dpiY)
112   :    mrFont(static_cast<FreetypeServerFont &>(sfont)),
113     maFontProperties(static_cast<FreetypeServerFont &>(sfont)),
114     mnDpiX(dpiX),
115     mnDpiY(dpiY),
116     mfAscent(from_hinted(static_cast<FreetypeServerFont &>(sfont).GetMetricsFT().ascender)),
117     mfDescent(from_hinted(static_cast<FreetypeServerFont &>(sfont).GetMetricsFT().descender)),
118     mfEmUnits(static_cast<FreetypeServerFont &>(sfont).GetMetricsFT().y_ppem),
119     mpFeatures(NULL)
120 {
121     const rtl::OString aLang = MsLangId::convertLanguageToIsoByteString( sfont.GetFontSelData().meLanguage );
122     rtl::OString name = rtl::OUStringToOString(
123         sfont.GetFontSelData().maTargetName, RTL_TEXTENCODING_UTF8 );
124 #ifdef DEBUG
125     printf("GraphiteFontAdaptor %lx %s italic=%u bold=%u\n", (long)this, name.getStr(),
126            maFontProperties.fItalic, maFontProperties.fBold);
127 #endif
128     sal_Int32 nFeat = name.indexOf(grutils::GrFeatureParser::FEAT_PREFIX) + 1;
129     if (nFeat > 0)
130     {
131         rtl::OString aFeat = name.copy(nFeat, name.getLength() - nFeat);
132         mpFeatures = new grutils::GrFeatureParser(*this, aFeat.getStr(), aLang.getStr());
133 #ifdef DEBUG
134         printf("GraphiteFontAdaptor %s/%s/%s %x language %d features %d errors\n",
135             rtl::OUStringToOString( sfont.GetFontSelData().maName,
136             RTL_TEXTENCODING_UTF8 ).getStr(),
137             rtl::OUStringToOString( sfont.GetFontSelData().maTargetName,
138             RTL_TEXTENCODING_UTF8 ).getStr(),
139             rtl::OUStringToOString( sfont.GetFontSelData().maSearchName,
140             RTL_TEXTENCODING_UTF8 ).getStr(),
141             sfont.GetFontSelData().meLanguage,
142             (int)mpFeatures->getFontFeatures(NULL), mpFeatures->parseErrors());
143 #endif
144     }
145     else
146     {
147         mpFeatures = new grutils::GrFeatureParser(*this, aLang.getStr());
148     }
149 }
150 
GraphiteFontAdaptor(const GraphiteFontAdaptor & rhs)151 GraphiteFontAdaptor::GraphiteFontAdaptor(const GraphiteFontAdaptor &rhs) throw()
152  :    Font(rhs),
153      mrFont (rhs.mrFont), maFontProperties(rhs.maFontProperties),
154     mnDpiX(rhs.mnDpiX), mnDpiY(rhs.mnDpiY),
155     mfAscent(rhs.mfAscent), mfDescent(rhs.mfDescent), mfEmUnits(rhs.mfEmUnits),
156     mpFeatures(NULL)
157 {
158     if (rhs.mpFeatures) mpFeatures = new grutils::GrFeatureParser(*(rhs.mpFeatures));
159 }
160 
161 
~GraphiteFontAdaptor()162 GraphiteFontAdaptor::~GraphiteFontAdaptor() throw()
163 {
164     maGlyphMetricMap.clear();
165     if (mpFeatures) delete mpFeatures;
166     mpFeatures = NULL;
167 }
168 
UniqueCacheInfo(ext_std::wstring & face_name_out,bool & bold_out,bool & italic_out)169 void GraphiteFontAdaptor::UniqueCacheInfo(ext_std::wstring & face_name_out, bool & bold_out, bool & italic_out)
170 {
171     face_name_out = maFontProperties.szFaceName;
172     bold_out = maFontProperties.fBold;
173     italic_out = maFontProperties.fItalic;
174 }
175 
IsGraphiteEnabledFont(ServerFont & font)176 bool GraphiteFontAdaptor::IsGraphiteEnabledFont(ServerFont & font) throw()
177 {
178     // NOTE: this assumes that the same FTFace pointer won't be reused,
179     // so FtFontInfo::ReleaseFaceFT must only be called at shutdown.
180     FreetypeServerFont & aFtFont = dynamic_cast<FreetypeServerFont &>(font);
181     FT_Face aFace = reinterpret_cast<FT_FaceRec_*>(aFtFont.GetFtFace());
182     SilfMap::iterator i = sSilfMap.find(reinterpret_cast<long>(aFace));
183     if (i != sSilfMap.end())
184     {
185 #ifdef DEBUG
186         if (static_cast<bool>(aFtFont.GetTable("Silf", 0)) != (*i).second)
187             printf("Silf cache font mismatch\n");
188 #endif
189         return (*i).second;
190     }
191     bool bHasSilf = aFtFont.GetTable("Silf", 0);
192     sSilfMap[reinterpret_cast<long>(aFace)] = bHasSilf;
193     return bHasSilf;
194 }
195 
196 
copyThis()197 gr::Font * GraphiteFontAdaptor::copyThis() {
198     return new GraphiteFontAdaptor(*this);
199 }
200 
201 
getDPIx()202 unsigned int GraphiteFontAdaptor::getDPIx() {
203     return mnDpiX;
204 }
205 
206 
getDPIy()207 unsigned int GraphiteFontAdaptor::getDPIy() {
208     return mnDpiY;
209 }
210 
211 
ascent()212 float GraphiteFontAdaptor::ascent() {
213     return mfAscent;
214 }
215 
216 
descent()217 float GraphiteFontAdaptor::descent() {
218     return mfDescent;
219 }
220 
221 
bold()222 bool GraphiteFontAdaptor::bold() {
223     return maFontProperties.fBold;
224 }
225 
226 
italic()227 bool GraphiteFontAdaptor::italic() {
228     return maFontProperties.fItalic;
229 }
230 
231 
height()232 float GraphiteFontAdaptor::height() {
233     return maFontProperties.pixHeight;
234 }
235 
236 
getFontMetrics(float * ascent_out,float * descent_out,float * em_square_out)237 void GraphiteFontAdaptor::getFontMetrics(float * ascent_out, float * descent_out, float * em_square_out) {
238     if (ascent_out)        *ascent_out    = mfAscent;
239     if (descent_out)    *descent_out   = mfDescent;
240     if (em_square_out)    *em_square_out = mfEmUnits;
241 }
242 
243 
getTable(gr::fontTableId32 table_id,size_t * buffer_sz)244 const void * GraphiteFontAdaptor::getTable(gr::fontTableId32 table_id, size_t * buffer_sz)
245 {
246     char tag_name[5] = {char(table_id >> 24), char(table_id >> 16), char(table_id >> 8), char(table_id), 0};
247     sal_uLong temp = *buffer_sz;
248 
249     const void * const tbl_buf = static_cast<FreetypeServerFont &>(mrFont).GetTable(tag_name, &temp);
250     *buffer_sz = temp;
251 
252     return tbl_buf;
253 }
254 
255 #define fix26_6(x) (x >> 6) + (x & 32 ? (x > 0 ? 1 : 0) : (x < 0 ? -1 : 0))
256 
257 // Return the glyph's metrics in pixels.
getGlyphMetrics(gr::gid16 nGlyphId,gr::Rect & aBounding,gr::Point & advances)258 void GraphiteFontAdaptor::getGlyphMetrics(gr::gid16 nGlyphId, gr::Rect & aBounding, gr::Point & advances)
259 {
260     // There used to be problems when orientation was set however, this no
261     // longer seems to be the case and the Glyph Metric cache in
262     // FreetypeServerFont is more efficient since it lasts between calls to VCL
263 #if 1
264     const GlyphMetric & metric = mrFont.GetGlyphMetric(nGlyphId);
265 
266     aBounding.right  = aBounding.left = metric.GetOffset().X();
267     aBounding.bottom = aBounding.top  = -metric.GetOffset().Y();
268     aBounding.right  += metric.GetSize().Width();
269     aBounding.bottom -= metric.GetSize().Height();
270 
271     advances.x = metric.GetDelta().X();
272     advances.y = -metric.GetDelta().Y();
273 
274 #else
275     // The problem with the code below is that the cache only lasts
276     // as long as the life time of the GraphiteFontAdaptor, which
277     // is created once per call to X11SalGraphics::GetTextLayout
278     GlyphMetricMap::const_iterator gm_itr = maGlyphMetricMap.find(nGlyphId);
279     if (gm_itr != maGlyphMetricMap.end())
280     {
281         // We've cached the results from last time.
282         aBounding = gm_itr->second.first;
283         advances    = gm_itr->second.second;
284     }
285     else
286     {
287         // We need to look up the glyph.
288         FT_Int nLoadFlags = mrFont.GetLoadFlags();
289 
290         FT_Face aFace = reinterpret_cast<FT_Face>(mrFont.GetFtFace());
291         if (!aFace)
292         {
293             aBounding.top = aBounding.bottom = aBounding.left = aBounding.right = 0;
294             advances.x = advances.y = 0;
295             return;
296         }
297         FT_Error aStatus = -1;
298         aStatus = FT_Load_Glyph(aFace, nGlyphId, nLoadFlags);
299         if( aStatus != FT_Err_Ok || (!aFace->glyph))
300         {
301             aBounding.top = aBounding.bottom = aBounding.left = aBounding.right = 0;
302             advances.x = advances.y = 0;
303             return;
304         }
305         // check whether we need synthetic bold/italic otherwise metric is wrong
306         if (mrFont.NeedsArtificialBold() && pFTEmbolden)
307             (*pFTEmbolden)(aFace->glyph);
308 
309         if (mrFont.NeedsArtificialItalic() && pFTOblique)
310             (*pFTOblique)(aFace->glyph);
311 
312         const FT_Glyph_Metrics &gm = aFace->glyph->metrics;
313 
314         // Fill out the bounding box an advances.
315         aBounding.top = aBounding.bottom = fix26_6(gm.horiBearingY);
316         aBounding.bottom -= fix26_6(gm.height);
317         aBounding.left = aBounding.right = fix26_6(gm.horiBearingX);
318         aBounding.right += fix26_6(gm.width);
319         advances.x = fix26_6(gm.horiAdvance);
320         advances.y = 0;
321 
322         // Now add an entry to our metrics map.
323         maGlyphMetricMap[nGlyphId] = std::make_pair(aBounding, advances);
324     }
325 #endif
326 }
327 
328 #endif
329