Line data Source code
1 : /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 : /*
3 : * This file is part of the LibreOffice project.
4 : *
5 : * This Source Code Form is subject to the terms of the Mozilla Public
6 : * License, v. 2.0. If a copy of the MPL was not distributed with this
7 : * file, You can obtain one at http://mozilla.org/MPL/2.0/.
8 : *
9 : * This file incorporates work covered by the following license notice:
10 : *
11 : * Licensed to the Apache Software Foundation (ASF) under one or more
12 : * contributor license agreements. See the NOTICE file distributed
13 : * with this work for additional information regarding copyright
14 : * ownership. The ASF licenses this file to you under the Apache
15 : * License, Version 2.0 (the "License"); you may not use this file
16 : * except in compliance with the License. You may obtain a copy of
17 : * the License at http://www.apache.org/licenses/LICENSE-2.0 .
18 : */
19 :
20 : #include <gcach_ftyp.hxx>
21 : #include <sallayout.hxx>
22 : #include <salgdi.hxx>
23 : #include <scrptrun.h>
24 :
25 : #include <i18nlangtag/mslangid.hxx>
26 :
27 : #include <vcl/svapp.hxx>
28 : #include <vcl/unohelp.hxx>
29 :
30 : #include <sal/alloca.h>
31 : #include <rtl/instance.hxx>
32 :
33 : #include <hb-icu.h>
34 : #include <hb-ot.h>
35 :
36 : #include <com/sun/star/i18n/CharacterIteratorMode.hpp>
37 :
38 : // layout implementation for ServerFont
39 1586176 : ServerFontLayout::ServerFontLayout( ServerFont& rFont )
40 1586176 : : mrServerFont( rFont )
41 : {
42 1586176 : }
43 :
44 246022 : void ServerFontLayout::DrawText( SalGraphics& rSalGraphics ) const
45 : {
46 246022 : rSalGraphics.DrawServerFontLayout( *this );
47 246022 : }
48 :
49 1531565 : bool ServerFontLayout::LayoutText( ImplLayoutArgs& rArgs )
50 : {
51 1531565 : return mrServerFont.GetLayoutEngine()->Layout(*this, rArgs);
52 : }
53 :
54 1534125 : void ServerFontLayout::AdjustLayout( ImplLayoutArgs& rArgs )
55 : {
56 1534125 : GenericSalLayout::AdjustLayout( rArgs );
57 :
58 : // apply asian kerning if the glyphs are not already formatted
59 4602375 : if( (rArgs.mnFlags & SalLayoutFlags::KerningAsian)
60 4602375 : && !(rArgs.mnFlags & SalLayoutFlags::Vertical) )
61 522 : if( (rArgs.mpDXArray != NULL) || (rArgs.mnLayoutWidth != 0) )
62 50 : ApplyAsianKerning( rArgs.mpStr, rArgs.mnLength );
63 :
64 : // insert kashidas where requested by the formatting array
65 1534125 : if( (rArgs.mnFlags & SalLayoutFlags::KashidaJustification) && rArgs.mpDXArray )
66 : {
67 0 : int nKashidaIndex = mrServerFont.GetGlyphIndex( 0x0640 );
68 0 : if( nKashidaIndex != 0 )
69 : {
70 0 : const GlyphMetric& rGM = mrServerFont.GetGlyphMetric( nKashidaIndex );
71 0 : KashidaJustify( nKashidaIndex, rGM.GetCharWidth() );
72 : // TODO: kashida-GSUB/GPOS
73 : }
74 : }
75 1534125 : }
76 :
77 63946 : void ServerFontLayout::SetNeedFallback(ImplLayoutArgs& rArgs, sal_Int32 nCharPos,
78 : bool bRightToLeft)
79 : {
80 63946 : if (nCharPos < 0)
81 63946 : return;
82 :
83 : using namespace ::com::sun::star;
84 :
85 63946 : if (!mxBreak.is())
86 3640 : mxBreak = vcl::unohelper::CreateBreakIterator();
87 :
88 63946 : lang::Locale aLocale(rArgs.maLanguageTag.getLocale());
89 :
90 : //if position nCharPos is missing in the font, grab the entire grapheme and
91 : //mark all glyphs as missing so the whole thing is rendered with the same
92 : //font
93 127892 : OUString aRun(rArgs.mpStr);
94 : sal_Int32 nDone;
95 : sal_Int32 nGraphemeStartPos =
96 63946 : mxBreak->previousCharacters(aRun, nCharPos+1, aLocale,
97 63946 : i18n::CharacterIteratorMode::SKIPCELL, 1, nDone);
98 : sal_Int32 nGraphemeEndPos =
99 63946 : mxBreak->nextCharacters(aRun, nCharPos, aLocale,
100 63946 : i18n::CharacterIteratorMode::SKIPCELL, 1, nDone);
101 :
102 127892 : rArgs.NeedFallback(nGraphemeStartPos, nGraphemeEndPos, bRightToLeft);
103 : }
104 :
105 0 : std::ostream &operator <<(std::ostream& s, ServerFont* pFont)
106 : {
107 : #ifndef SAL_LOG_INFO
108 : (void) pFont;
109 : #else
110 : FT_Face aFace = pFont->GetFtFace();
111 : const char* pName = FT_Get_Postscript_Name(aFace);
112 : if (pName)
113 : s << pName;
114 : else
115 : s << pFont->GetFontFileName();
116 : #endif
117 0 : return s;
118 : }
119 :
120 14115 : static hb_blob_t *getFontTable(hb_face_t* /*face*/, hb_tag_t nTableTag, void* pUserData)
121 : {
122 : char pTagName[5];
123 14115 : pTagName[0] = (char)(nTableTag >> 24);
124 14115 : pTagName[1] = (char)(nTableTag >> 16);
125 14115 : pTagName[2] = (char)(nTableTag >> 8);
126 14115 : pTagName[3] = (char)(nTableTag);
127 14115 : pTagName[4] = 0;
128 :
129 14115 : ServerFont* pFont = static_cast<ServerFont*>(pUserData);
130 :
131 : SAL_INFO("vcl.harfbuzz", "getFontTable(" << pFont << ", " << pTagName << ")");
132 :
133 : sal_uLong nLength;
134 14115 : const unsigned char* pBuffer = pFont->GetTable(pTagName, &nLength);
135 :
136 14115 : hb_blob_t* pBlob = NULL;
137 14115 : if (pBuffer != NULL)
138 13497 : pBlob = hb_blob_create(reinterpret_cast<const char*>(pBuffer), nLength, HB_MEMORY_MODE_READONLY, const_cast<unsigned char *>(pBuffer), NULL);
139 :
140 14115 : return pBlob;
141 : }
142 :
143 24837657 : static hb_bool_t getFontGlyph(hb_font_t* /*font*/, void* pFontData,
144 : hb_codepoint_t ch, hb_codepoint_t vs,
145 : hb_codepoint_t* nGlyphIndex,
146 : void* /*pUserData*/)
147 : {
148 24837657 : ServerFont* pFont = static_cast<ServerFont*>(pFontData);
149 24837657 : *nGlyphIndex = pFont->GetRawGlyphIndex(ch, vs);
150 :
151 24837657 : return *nGlyphIndex != 0;
152 : }
153 :
154 24816818 : static hb_position_t getGlyphAdvanceH(hb_font_t* /*font*/, void* pFontData,
155 : hb_codepoint_t nGlyphIndex,
156 : void* /*pUserData*/)
157 : {
158 24816818 : ServerFont* pFont = static_cast<ServerFont*>(pFontData);
159 24816818 : const GlyphMetric& rGM = pFont->GetGlyphMetric(nGlyphIndex);
160 24816818 : return rGM.GetCharWidth() << 6;
161 : }
162 :
163 0 : static hb_position_t getGlyphAdvanceV(hb_font_t* /*font*/, void* /*pFontData*/,
164 : hb_codepoint_t /*nGlyphIndex*/,
165 : void* /*pUserData*/)
166 : {
167 : // XXX: vertical metrics
168 0 : return 0;
169 : }
170 :
171 74368216 : static hb_bool_t getGlyphOriginH(hb_font_t* /*font*/, void* /*pFontData*/,
172 : hb_codepoint_t /*nGlyphIndex*/,
173 : hb_position_t* /*x*/, hb_position_t* /*y*/,
174 : void* /*pUserData*/)
175 : {
176 : // the horizontal origin is always (0, 0)
177 74368216 : return true;
178 : }
179 :
180 0 : static hb_bool_t getGlyphOriginV(hb_font_t* /*font*/, void* /*pFontData*/,
181 : hb_codepoint_t /*nGlyphIndex*/,
182 : hb_position_t* /*x*/, hb_position_t* /*y*/,
183 : void* /*pUserData*/)
184 : {
185 : // XXX: vertical origin
186 0 : return true;
187 : }
188 :
189 34267 : static hb_position_t getGlyphKerningH(hb_font_t* /*font*/, void* pFontData,
190 : hb_codepoint_t nGlyphIndex1, hb_codepoint_t nGlyphIndex2,
191 : void* /*pUserData*/)
192 : {
193 : // This callback is for old style 'kern' table, GPOS kerning is handled by HarfBuzz directly
194 :
195 34267 : ServerFont* pFont = static_cast<ServerFont*>(pFontData);
196 34267 : FT_Face aFace = pFont->GetFtFace();
197 :
198 : SAL_INFO("vcl.harfbuzz", "getGlyphKerningH(" << pFont << ", " << nGlyphIndex1 << ", " << nGlyphIndex2 << ")");
199 :
200 : FT_Error error;
201 : FT_Vector kerning;
202 : hb_position_t ret;
203 :
204 34267 : error = FT_Get_Kerning(aFace, nGlyphIndex1, nGlyphIndex2, FT_KERNING_DEFAULT, &kerning);
205 34267 : if (error)
206 0 : ret = 0;
207 : else
208 34267 : ret = kerning.x;
209 :
210 34267 : return ret;
211 : }
212 :
213 0 : static hb_position_t getGlyphKerningV(hb_font_t* /*font*/, void* /*pFontData*/,
214 : hb_codepoint_t /*nGlyphIndex1*/, hb_codepoint_t /*nGlyphIndex2*/,
215 : void* /*pUserData*/)
216 : {
217 : // XXX vertical kerning
218 0 : return 0;
219 : }
220 :
221 0 : static hb_bool_t getGlyphExtents(hb_font_t* /*font*/, void* pFontData,
222 : hb_codepoint_t nGlyphIndex,
223 : hb_glyph_extents_t* pExtents,
224 : void* /*pUserData*/)
225 : {
226 0 : ServerFont* pFont = static_cast<ServerFont*>(pFontData);
227 0 : FT_Face aFace = pFont->GetFtFace();
228 :
229 : SAL_INFO("vcl.harfbuzz", "getGlyphExtents(" << pFont << ", " << nGlyphIndex << ")");
230 :
231 : FT_Error error;
232 0 : error = FT_Load_Glyph(aFace, nGlyphIndex, FT_LOAD_DEFAULT);
233 0 : if (!error)
234 : {
235 0 : pExtents->x_bearing = aFace->glyph->metrics.horiBearingX;
236 0 : pExtents->y_bearing = aFace->glyph->metrics.horiBearingY;
237 0 : pExtents->width = aFace->glyph->metrics.width;
238 0 : pExtents->height = -aFace->glyph->metrics.height;
239 : }
240 :
241 0 : return !error;
242 : }
243 :
244 0 : static hb_bool_t getGlyphContourPoint(hb_font_t* /*font*/, void* pFontData,
245 : hb_codepoint_t nGlyphIndex, unsigned int nPointIndex,
246 : hb_position_t *x, hb_position_t *y,
247 : void* /*pUserData*/)
248 : {
249 0 : bool ret = false;
250 0 : ServerFont* pFont = static_cast<ServerFont*>(pFontData);
251 0 : FT_Face aFace = pFont->GetFtFace();
252 :
253 : SAL_INFO("vcl.harfbuzz", "getGlyphContourPoint(" << pFont << ", " << nGlyphIndex << ", " << nPointIndex << ")");
254 :
255 : FT_Error error;
256 0 : error = FT_Load_Glyph(aFace, nGlyphIndex, FT_LOAD_DEFAULT);
257 0 : if (!error)
258 : {
259 0 : if (aFace->glyph->format == FT_GLYPH_FORMAT_OUTLINE)
260 : {
261 0 : if (nPointIndex < (unsigned int) aFace->glyph->outline.n_points)
262 : {
263 0 : *x = aFace->glyph->outline.points[nPointIndex].x;
264 0 : *y = aFace->glyph->outline.points[nPointIndex].y;
265 0 : ret = true;
266 : }
267 : }
268 : }
269 :
270 0 : return ret;
271 : }
272 :
273 198 : static hb_font_funcs_t* getFontFuncs()
274 : {
275 198 : static hb_font_funcs_t* funcs = hb_font_funcs_create();
276 :
277 198 : hb_font_funcs_set_glyph_func (funcs, getFontGlyph, NULL, NULL);
278 198 : hb_font_funcs_set_glyph_h_advance_func (funcs, getGlyphAdvanceH, NULL, NULL);
279 198 : hb_font_funcs_set_glyph_v_advance_func (funcs, getGlyphAdvanceV, NULL, NULL);
280 198 : hb_font_funcs_set_glyph_h_origin_func (funcs, getGlyphOriginH, NULL, NULL);
281 198 : hb_font_funcs_set_glyph_v_origin_func (funcs, getGlyphOriginV, NULL, NULL);
282 198 : hb_font_funcs_set_glyph_h_kerning_func (funcs, getGlyphKerningH, NULL, NULL);
283 198 : hb_font_funcs_set_glyph_v_kerning_func (funcs, getGlyphKerningV, NULL, NULL);
284 198 : hb_font_funcs_set_glyph_extents_func (funcs, getGlyphExtents, NULL, NULL);
285 198 : hb_font_funcs_set_glyph_contour_point_func (funcs, getGlyphContourPoint, NULL, NULL);
286 :
287 198 : return funcs;
288 : }
289 :
290 : // Disabled Unicode compatibility decomposition, see fdo#66715
291 63946 : static unsigned int unicodeDecomposeCompatibility(hb_unicode_funcs_t* /*ufuncs*/,
292 : hb_codepoint_t /*u*/,
293 : hb_codepoint_t* /*decomposed*/,
294 : void* /*user_data*/)
295 : {
296 63946 : return 0;
297 : }
298 :
299 198 : static hb_unicode_funcs_t* getUnicodeFuncs()
300 : {
301 198 : static hb_unicode_funcs_t* ufuncs = hb_unicode_funcs_create(hb_icu_get_unicode_funcs());
302 198 : hb_unicode_funcs_set_decompose_compatibility_func(ufuncs, unicodeDecomposeCompatibility, NULL, NULL);
303 198 : return ufuncs;
304 : }
305 :
306 : class HbLayoutEngine : public ServerFontLayoutEngine
307 : {
308 : private:
309 : hb_script_t maHbScript;
310 : hb_face_t* mpHbFace;
311 : int mnUnitsPerEM;
312 :
313 : public:
314 : explicit HbLayoutEngine(ServerFont&);
315 : virtual ~HbLayoutEngine();
316 :
317 : virtual bool Layout(ServerFontLayout&, ImplLayoutArgs&) SAL_OVERRIDE;
318 : };
319 :
320 4725 : HbLayoutEngine::HbLayoutEngine(ServerFont& rServerFont)
321 : : maHbScript(HB_SCRIPT_INVALID),
322 : mpHbFace(NULL),
323 4725 : mnUnitsPerEM(0)
324 : {
325 4725 : FT_Face aFtFace = rServerFont.GetFtFace();
326 4725 : mnUnitsPerEM = rServerFont.GetEmUnits();
327 :
328 4725 : mpHbFace = hb_face_create_for_tables(getFontTable, &rServerFont, NULL);
329 4725 : hb_face_set_index(mpHbFace, aFtFace->face_index);
330 4725 : hb_face_set_upem(mpHbFace, mnUnitsPerEM);
331 4725 : }
332 :
333 14175 : HbLayoutEngine::~HbLayoutEngine()
334 : {
335 4725 : hb_face_destroy(mpHbFace);
336 9450 : }
337 :
338 : struct HbScriptRun
339 : {
340 : int32_t mnMin;
341 : int32_t mnEnd;
342 : hb_script_t maScript;
343 :
344 1533807 : HbScriptRun(int32_t nMin, int32_t nEnd, UScriptCode aScript)
345 : : mnMin(nMin), mnEnd(nEnd),
346 1533807 : maScript(hb_icu_script_to_script(aScript))
347 1533807 : {}
348 : };
349 :
350 : typedef std::vector<HbScriptRun> HbScriptRuns;
351 :
352 : namespace vcl {
353 : struct Run
354 : {
355 : int32_t nStart;
356 : int32_t nEnd;
357 : UScriptCode nCode;
358 1470263 : Run(int32_t nStart_, int32_t nEnd_, UScriptCode nCode_)
359 1470263 : : nStart(nStart_), nEnd(nEnd_), nCode(nCode_)
360 1470263 : {}
361 : };
362 :
363 1513433 : class TextLayoutCache
364 : {
365 : public:
366 : std::vector<vcl::Run> runs;
367 1513433 : TextLayoutCache(sal_Unicode const* pStr, sal_Int32 const nEnd)
368 1513433 : {
369 : vcl::ScriptRun aScriptRun(
370 : reinterpret_cast<const UChar *>(pStr),
371 1513433 : nEnd);
372 4497129 : while (aScriptRun.next())
373 : {
374 : runs.push_back(Run(aScriptRun.getScriptStart(),
375 1470263 : aScriptRun.getScriptEnd(), aScriptRun.getScriptCode()));
376 1513433 : }
377 1513433 : }
378 : };
379 : }
380 :
381 54611 : std::shared_ptr<vcl::TextLayoutCache> ServerFontLayout::CreateTextLayoutCache(
382 : OUString const& rString) const
383 : {
384 54611 : return std::make_shared<vcl::TextLayoutCache>(rString.getStr(), rString.getLength());
385 : }
386 :
387 1531565 : bool HbLayoutEngine::Layout(ServerFontLayout& rLayout, ImplLayoutArgs& rArgs)
388 : {
389 1531565 : ServerFont& rFont = rLayout.GetServerFont();
390 1531565 : FT_Face aFtFace = rFont.GetFtFace();
391 :
392 : SAL_INFO("vcl.harfbuzz", "layout(" << this << ",rArgs=" << rArgs << ")");
393 :
394 1531565 : static hb_font_funcs_t* pHbFontFuncs = getFontFuncs();
395 :
396 1531565 : hb_font_t *pHbFont = hb_font_create(mpHbFace);
397 1531565 : hb_font_set_funcs(pHbFont, pHbFontFuncs, &rFont, NULL);
398 : hb_font_set_scale(pHbFont,
399 1531565 : ((uint64_t) aFtFace->size->metrics.x_scale * (uint64_t) mnUnitsPerEM) >> 16,
400 3063130 : ((uint64_t) aFtFace->size->metrics.y_scale * (uint64_t) mnUnitsPerEM) >> 16);
401 1531565 : hb_font_set_ppem(pHbFont, aFtFace->size->metrics.x_ppem, aFtFace->size->metrics.y_ppem);
402 :
403 : // allocate temporary arrays, note: round to even
404 1531565 : int nGlyphCapacity = (3 * (rArgs.mnEndCharPos - rArgs.mnMinCharPos) | 15) + 1;
405 :
406 1531565 : rLayout.Reserve(nGlyphCapacity);
407 :
408 1531565 : std::unique_ptr<vcl::TextLayoutCache> pNewScriptRun;
409 : vcl::TextLayoutCache const* pTextLayout;
410 1531565 : if (rArgs.m_pTextLayoutCache)
411 : {
412 72743 : pTextLayout = rArgs.m_pTextLayoutCache; // use cache!
413 : }
414 : else
415 : {
416 1458822 : pNewScriptRun.reset(new vcl::TextLayoutCache(rArgs.mpStr, rArgs.mnEndCharPos));
417 1458822 : pTextLayout = pNewScriptRun.get();
418 : }
419 :
420 1531565 : Point aCurrPos(0, 0);
421 : while (true)
422 : {
423 : int nBidiMinRunPos, nBidiEndRunPos;
424 : bool bRightToLeft;
425 3064802 : if (!rArgs.GetNextRun(&nBidiMinRunPos, &nBidiEndRunPos, &bRightToLeft))
426 1531565 : break;
427 :
428 : // Find script subruns.
429 1533237 : int nCurrentPos = nBidiMinRunPos;
430 1533237 : HbScriptRuns aScriptSubRuns;
431 1533237 : size_t k = 0;
432 1695534 : for (; k < pTextLayout->runs.size(); ++k)
433 : {
434 1695534 : vcl::Run const& rRun(pTextLayout->runs[k]);
435 1695534 : if (rRun.nStart <= nCurrentPos && nCurrentPos < rRun.nEnd)
436 : {
437 1533237 : break;
438 : }
439 : }
440 :
441 4600281 : while (nCurrentPos < nBidiEndRunPos && k < pTextLayout->runs.size())
442 : {
443 1533807 : int32_t nMinRunPos = nCurrentPos;
444 1533807 : int32_t nEndRunPos = std::min(pTextLayout->runs[k].nEnd, nBidiEndRunPos);
445 1533807 : HbScriptRun aRun(nMinRunPos, nEndRunPos, pTextLayout->runs[k].nCode);
446 1533807 : aScriptSubRuns.push_back(aRun);
447 :
448 1533807 : nCurrentPos = nEndRunPos;
449 1533807 : ++k;
450 : }
451 :
452 : // RTL subruns should be reversed to ensure that final glyph order is
453 : // correct.
454 1533237 : if (bRightToLeft)
455 180 : std::reverse(aScriptSubRuns.begin(), aScriptSubRuns.end());
456 :
457 3067044 : for (HbScriptRuns::iterator it = aScriptSubRuns.begin(); it != aScriptSubRuns.end(); ++it)
458 : {
459 1533807 : int nMinRunPos = it->mnMin;
460 1533807 : int nEndRunPos = it->mnEnd;
461 1533807 : int nRunLen = nEndRunPos - nMinRunPos;
462 1533807 : maHbScript = it->maScript;
463 :
464 1533807 : OString sLanguage = OUStringToOString(rArgs.maLanguageTag.getLanguage(), RTL_TEXTENCODING_UTF8);
465 :
466 1533807 : static hb_unicode_funcs_t* pHbUnicodeFuncs = getUnicodeFuncs();
467 :
468 1533807 : int nHbFlags = HB_BUFFER_FLAGS_DEFAULT;
469 1533807 : if (nMinRunPos == 0)
470 1257956 : nHbFlags |= HB_BUFFER_FLAG_BOT; /* Beginning-of-text */
471 1533807 : if (nEndRunPos == rArgs.mnLength)
472 1323269 : nHbFlags |= HB_BUFFER_FLAG_EOT; /* End-of-text */
473 :
474 1533807 : hb_buffer_t *pHbBuffer = hb_buffer_create();
475 1533807 : hb_buffer_set_unicode_funcs(pHbBuffer, pHbUnicodeFuncs);
476 1533807 : hb_buffer_set_direction(pHbBuffer, bRightToLeft ? HB_DIRECTION_RTL: HB_DIRECTION_LTR);
477 1533807 : hb_buffer_set_script(pHbBuffer, maHbScript);
478 1533807 : hb_buffer_set_language(pHbBuffer, hb_language_from_string(sLanguage.getStr(), -1));
479 1533807 : hb_buffer_set_flags(pHbBuffer, (hb_buffer_flags_t) nHbFlags);
480 1533807 : hb_buffer_add_utf16(pHbBuffer, rArgs.mpStr, rArgs.mnLength, nMinRunPos, nRunLen);
481 1533807 : hb_shape(pHbFont, pHbBuffer, NULL, 0);
482 :
483 1533807 : int nRunGlyphCount = hb_buffer_get_length(pHbBuffer);
484 1533807 : hb_glyph_info_t *pHbGlyphInfos = hb_buffer_get_glyph_infos(pHbBuffer, NULL);
485 1533807 : hb_glyph_position_t *pHbPositions = hb_buffer_get_glyph_positions(pHbBuffer, NULL);
486 :
487 26350625 : for (int i = 0; i < nRunGlyphCount; ++i) {
488 24816818 : int32_t nGlyphIndex = pHbGlyphInfos[i].codepoint;
489 24816818 : int32_t nCharPos = pHbGlyphInfos[i].cluster;
490 :
491 : // tdf#89231 if it's just a missing non-breaking space, then use a normal space
492 24816818 : if (!nGlyphIndex && (SalLayoutFlags::ForFallback & rArgs.mnFlags) && nCharPos >= 0 && rArgs.mpStr[nCharPos] == 0x202F)
493 : {
494 0 : nGlyphIndex = rFont.GetGlyphIndex(' ');
495 : }
496 :
497 : // if needed request glyph fallback by updating LayoutArgs
498 24816818 : if (!nGlyphIndex)
499 : {
500 63946 : rLayout.SetNeedFallback(rArgs, nCharPos, bRightToLeft);
501 63946 : if (SalLayoutFlags::ForFallback & rArgs.mnFlags)
502 670 : continue;
503 : }
504 :
505 : // apply vertical flags and glyph substitution
506 : // XXX: Use HB_DIRECTION_TTB above and apply whatever flags magic
507 : // FixupGlyphIndex() is doing, minus the GSUB part.
508 24816148 : if (nCharPos >= 0)
509 : {
510 24816148 : sal_UCS4 aChar = rArgs.mpStr[nCharPos];
511 24816148 : nGlyphIndex = rFont.FixupGlyphIndex(nGlyphIndex, aChar);
512 : }
513 :
514 24816148 : bool bInCluster = false;
515 24816148 : if (i > 0 && pHbGlyphInfos[i].cluster == pHbGlyphInfos[i - 1].cluster)
516 13 : bInCluster = true;
517 :
518 24816148 : long nGlyphFlags = 0;
519 24816148 : if (bRightToLeft)
520 508 : nGlyphFlags |= GlyphItem::IS_RTL_GLYPH;
521 :
522 24816148 : if (bInCluster)
523 13 : nGlyphFlags |= GlyphItem::IS_IN_CLUSTER;
524 :
525 : // The whole IS_DIACRITIC concept is a stupid hack that was
526 : // introduced ages ago to work around the utter brokenness of the
527 : // way justification adjustments are applied (the DXArray fiasco).
528 : // Since it is such a stupid hack, there is no sane way to directly
529 : // map to concepts of the "outside" world, so we do some rather
530 : // ugly hacks:
531 : // * If the font has a GDEF table, we check for glyphs with mark
532 : // glyph class which is sensible, except that some fonts
533 : // (fdo#70968) assign mark class to spacing marks (which is wrong
534 : // but usually harmless), so we try to sniff what HarfBuzz thinks
535 : // about this glyph by checking if it gives it a zero advance
536 : // width.
537 : // * If the font has no GDEF table, we just check if the glyph has
538 : // zero advance width, but this is stupid and can be wrong. A
539 : // better way would to check the character's Unicode combining
540 : // class, but unfortunately glyph gives combining marks the
541 : // cluster value of its base character, so nCharPos will be
542 : // pointing to the wrong character (but HarfBuzz might change
543 : // this in the future).
544 24816148 : bool bDiacritic = false;
545 24816148 : if (hb_ot_layout_has_glyph_classes(mpHbFace))
546 : {
547 : // the font has GDEF table
548 24775335 : bool bMark = hb_ot_layout_get_glyph_class(mpHbFace, nGlyphIndex) == HB_OT_LAYOUT_GLYPH_CLASS_MARK;
549 24775335 : if (bMark && pHbPositions[i].x_advance == 0)
550 8 : bDiacritic = true;
551 : }
552 : else
553 : {
554 : // the font lacks GDEF table
555 40813 : if (pHbPositions[i].x_advance == 0)
556 0 : bDiacritic = true;
557 : }
558 :
559 24816148 : if (bDiacritic)
560 8 : nGlyphFlags |= GlyphItem::IS_DIACRITIC;
561 :
562 24816148 : int32_t nXOffset = pHbPositions[i].x_offset >> 6;
563 24816148 : int32_t nYOffset = pHbPositions[i].y_offset >> 6;
564 24816148 : int32_t nXAdvance = pHbPositions[i].x_advance >> 6;
565 24816148 : int32_t nYAdvance = pHbPositions[i].y_advance >> 6;
566 :
567 24816148 : Point aNewPos = Point(aCurrPos.X() + nXOffset, -(aCurrPos.Y() + nYOffset));
568 24816148 : const GlyphItem aGI(nCharPos, nGlyphIndex, aNewPos, nGlyphFlags, nXAdvance, nXOffset, nYOffset);
569 24816148 : rLayout.AppendGlyph(aGI);
570 :
571 24816148 : aCurrPos.X() += nXAdvance;
572 24816148 : aCurrPos.Y() += nYAdvance;
573 : }
574 :
575 1533807 : hb_buffer_destroy(pHbBuffer);
576 1533807 : }
577 1533237 : }
578 :
579 1531565 : hb_font_destroy(pHbFont);
580 :
581 : // sort glyphs in visual order
582 : // and then in logical order (e.g. diacritics after cluster start)
583 : // XXX: why?
584 1531565 : rLayout.SortGlyphItems();
585 :
586 : // determine need for kashida justification
587 1531565 : if((rArgs.mpDXArray || rArgs.mnLayoutWidth)
588 40511 : && ((maHbScript == HB_SCRIPT_ARABIC) || (maHbScript == HB_SCRIPT_SYRIAC)))
589 0 : rArgs.mnFlags |= SalLayoutFlags::KashidaJustification;
590 :
591 1531565 : return true;
592 : }
593 :
594 1531565 : ServerFontLayoutEngine* ServerFont::GetLayoutEngine()
595 : {
596 : // find best layout engine for font, platform, script and language
597 1531565 : if (!mpLayoutEngine) {
598 4725 : mpLayoutEngine = new HbLayoutEngine(*this);
599 : }
600 1531565 : return mpLayoutEngine;
601 801 : }
602 :
603 : /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
|