Branch libreoffice-5-0-4
[LibreOffice.git] / vcl / generic / glyphs / gcach_layout.cxx
blob4ada683441747d1ba74811307bf95cec454f4a7a
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3 * This file is part of the LibreOffice project.
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/.
9 * This file incorporates work covered by the following license notice:
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 .
20 #include <gcach_ftyp.hxx>
21 #include <sallayout.hxx>
22 #include <salgdi.hxx>
23 #include <scrptrun.h>
25 #include <i18nlangtag/mslangid.hxx>
27 #include <vcl/svapp.hxx>
28 #include <vcl/unohelp.hxx>
30 #include <sal/alloca.h>
31 #include <rtl/instance.hxx>
33 #include <hb-icu.h>
34 #include <hb-ot.h>
36 #include <com/sun/star/i18n/CharacterIteratorMode.hpp>
38 // layout implementation for ServerFont
39 ServerFontLayout::ServerFontLayout( ServerFont& rFont )
40 : mrServerFont( rFont )
44 void ServerFontLayout::DrawText( SalGraphics& rSalGraphics ) const
46 rSalGraphics.DrawServerFontLayout( *this );
49 bool ServerFontLayout::LayoutText( ImplLayoutArgs& rArgs )
51 return mrServerFont.GetLayoutEngine()->Layout(*this, rArgs);
54 void ServerFontLayout::AdjustLayout( ImplLayoutArgs& rArgs )
56 GenericSalLayout::AdjustLayout( rArgs );
58 // apply asian kerning if the glyphs are not already formatted
59 if( (rArgs.mnFlags & SalLayoutFlags::KerningAsian)
60 && !(rArgs.mnFlags & SalLayoutFlags::Vertical) )
61 if( (rArgs.mpDXArray != NULL) || (rArgs.mnLayoutWidth != 0) )
62 ApplyAsianKerning( rArgs.mpStr, rArgs.mnLength );
64 // insert kashidas where requested by the formatting array
65 if( (rArgs.mnFlags & SalLayoutFlags::KashidaJustification) && rArgs.mpDXArray )
67 int nKashidaIndex = mrServerFont.GetGlyphIndex( 0x0640 );
68 if( nKashidaIndex != 0 )
70 const GlyphMetric& rGM = mrServerFont.GetGlyphMetric( nKashidaIndex );
71 KashidaJustify( nKashidaIndex, rGM.GetCharWidth() );
72 // TODO: kashida-GSUB/GPOS
77 void ServerFontLayout::SetNeedFallback(ImplLayoutArgs& rArgs, sal_Int32 nCharPos,
78 bool bRightToLeft)
80 if (nCharPos < 0)
81 return;
83 using namespace ::com::sun::star;
85 if (!mxBreak.is())
86 mxBreak = vcl::unohelper::CreateBreakIterator();
88 lang::Locale aLocale(rArgs.maLanguageTag.getLocale());
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 OUString aRun(rArgs.mpStr);
94 sal_Int32 nDone;
95 sal_Int32 nGraphemeStartPos =
96 mxBreak->previousCharacters(aRun, nCharPos+1, aLocale,
97 i18n::CharacterIteratorMode::SKIPCELL, 1, nDone);
98 sal_Int32 nGraphemeEndPos =
99 mxBreak->nextCharacters(aRun, nCharPos, aLocale,
100 i18n::CharacterIteratorMode::SKIPCELL, 1, nDone);
102 rArgs.NeedFallback(nGraphemeStartPos, nGraphemeEndPos, bRightToLeft);
105 std::ostream &operator <<(std::ostream& s, ServerFont* pFont)
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 return s;
120 static hb_blob_t *getFontTable(hb_face_t* /*face*/, hb_tag_t nTableTag, void* pUserData)
122 char pTagName[5];
123 pTagName[0] = (char)(nTableTag >> 24);
124 pTagName[1] = (char)(nTableTag >> 16);
125 pTagName[2] = (char)(nTableTag >> 8);
126 pTagName[3] = (char)(nTableTag);
127 pTagName[4] = 0;
129 ServerFont* pFont = static_cast<ServerFont*>(pUserData);
131 SAL_INFO("vcl.harfbuzz", "getFontTable(" << pFont << ", " << pTagName << ")");
133 sal_uLong nLength;
134 const unsigned char* pBuffer = pFont->GetTable(pTagName, &nLength);
136 hb_blob_t* pBlob = NULL;
137 if (pBuffer != NULL)
138 pBlob = hb_blob_create(reinterpret_cast<const char*>(pBuffer), nLength, HB_MEMORY_MODE_READONLY, (void*) pBuffer, NULL);
140 return pBlob;
143 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*/)
148 ServerFont* pFont = static_cast<ServerFont*>(pFontData);
149 *nGlyphIndex = pFont->GetRawGlyphIndex(ch, vs);
151 return *nGlyphIndex != 0;
154 static hb_position_t getGlyphAdvanceH(hb_font_t* /*font*/, void* pFontData,
155 hb_codepoint_t nGlyphIndex,
156 void* /*pUserData*/)
158 ServerFont* pFont = static_cast<ServerFont*>(pFontData);
159 const GlyphMetric& rGM = pFont->GetGlyphMetric(nGlyphIndex);
160 return rGM.GetCharWidth() << 6;
163 static hb_position_t getGlyphAdvanceV(hb_font_t* /*font*/, void* /*pFontData*/,
164 hb_codepoint_t /*nGlyphIndex*/,
165 void* /*pUserData*/)
167 // XXX: vertical metrics
168 return 0;
171 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*/)
176 // the horizontal origin is always (0, 0)
177 return true;
180 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*/)
185 // XXX: vertical origin
186 return true;
189 static hb_position_t getGlyphKerningH(hb_font_t* /*font*/, void* pFontData,
190 hb_codepoint_t nGlyphIndex1, hb_codepoint_t nGlyphIndex2,
191 void* /*pUserData*/)
193 // This callback is for old style 'kern' table, GPOS kerning is handled by HarfBuzz directly
195 ServerFont* pFont = static_cast<ServerFont*>(pFontData);
196 FT_Face aFace = pFont->GetFtFace();
198 SAL_INFO("vcl.harfbuzz", "getGlyphKerningH(" << pFont << ", " << nGlyphIndex1 << ", " << nGlyphIndex2 << ")");
200 FT_Error error;
201 FT_Vector kerning;
202 hb_position_t ret;
204 error = FT_Get_Kerning(aFace, nGlyphIndex1, nGlyphIndex2, FT_KERNING_DEFAULT, &kerning);
205 if (error)
206 ret = 0;
207 else
208 ret = kerning.x;
210 return ret;
213 static hb_position_t getGlyphKerningV(hb_font_t* /*font*/, void* /*pFontData*/,
214 hb_codepoint_t /*nGlyphIndex1*/, hb_codepoint_t /*nGlyphIndex2*/,
215 void* /*pUserData*/)
217 // XXX vertical kerning
218 return 0;
221 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*/)
226 ServerFont* pFont = static_cast<ServerFont*>(pFontData);
227 FT_Face aFace = pFont->GetFtFace();
229 SAL_INFO("vcl.harfbuzz", "getGlyphExtents(" << pFont << ", " << nGlyphIndex << ")");
231 FT_Error error;
232 error = FT_Load_Glyph(aFace, nGlyphIndex, FT_LOAD_DEFAULT);
233 if (!error)
235 pExtents->x_bearing = aFace->glyph->metrics.horiBearingX;
236 pExtents->y_bearing = aFace->glyph->metrics.horiBearingY;
237 pExtents->width = aFace->glyph->metrics.width;
238 pExtents->height = -aFace->glyph->metrics.height;
241 return !error;
244 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*/)
249 bool ret = false;
250 ServerFont* pFont = static_cast<ServerFont*>(pFontData);
251 FT_Face aFace = pFont->GetFtFace();
253 SAL_INFO("vcl.harfbuzz", "getGlyphContourPoint(" << pFont << ", " << nGlyphIndex << ", " << nPointIndex << ")");
255 FT_Error error;
256 error = FT_Load_Glyph(aFace, nGlyphIndex, FT_LOAD_DEFAULT);
257 if (!error)
259 if (aFace->glyph->format == FT_GLYPH_FORMAT_OUTLINE)
261 if (nPointIndex < (unsigned int) aFace->glyph->outline.n_points)
263 *x = aFace->glyph->outline.points[nPointIndex].x;
264 *y = aFace->glyph->outline.points[nPointIndex].y;
265 ret = true;
270 return ret;
273 static hb_font_funcs_t* getFontFuncs()
275 static hb_font_funcs_t* funcs = hb_font_funcs_create();
277 hb_font_funcs_set_glyph_func (funcs, getFontGlyph, NULL, NULL);
278 hb_font_funcs_set_glyph_h_advance_func (funcs, getGlyphAdvanceH, NULL, NULL);
279 hb_font_funcs_set_glyph_v_advance_func (funcs, getGlyphAdvanceV, NULL, NULL);
280 hb_font_funcs_set_glyph_h_origin_func (funcs, getGlyphOriginH, NULL, NULL);
281 hb_font_funcs_set_glyph_v_origin_func (funcs, getGlyphOriginV, NULL, NULL);
282 hb_font_funcs_set_glyph_h_kerning_func (funcs, getGlyphKerningH, NULL, NULL);
283 hb_font_funcs_set_glyph_v_kerning_func (funcs, getGlyphKerningV, NULL, NULL);
284 hb_font_funcs_set_glyph_extents_func (funcs, getGlyphExtents, NULL, NULL);
285 hb_font_funcs_set_glyph_contour_point_func (funcs, getGlyphContourPoint, NULL, NULL);
287 return funcs;
290 // Disabled Unicode compatibility decomposition, see fdo#66715
291 static unsigned int unicodeDecomposeCompatibility(hb_unicode_funcs_t* /*ufuncs*/,
292 hb_codepoint_t /*u*/,
293 hb_codepoint_t* /*decomposed*/,
294 void* /*user_data*/)
296 return 0;
299 static hb_unicode_funcs_t* getUnicodeFuncs()
301 static hb_unicode_funcs_t* ufuncs = hb_unicode_funcs_create(hb_icu_get_unicode_funcs());
302 hb_unicode_funcs_set_decompose_compatibility_func(ufuncs, unicodeDecomposeCompatibility, NULL, NULL);
303 return ufuncs;
306 class HbLayoutEngine : public ServerFontLayoutEngine
308 private:
309 hb_script_t maHbScript;
310 hb_face_t* mpHbFace;
311 int mnUnitsPerEM;
313 public:
314 HbLayoutEngine(ServerFont&);
315 virtual ~HbLayoutEngine();
317 virtual bool Layout(ServerFontLayout&, ImplLayoutArgs&) SAL_OVERRIDE;
320 HbLayoutEngine::HbLayoutEngine(ServerFont& rServerFont)
321 : maHbScript(HB_SCRIPT_INVALID),
322 mpHbFace(NULL),
323 mnUnitsPerEM(0)
325 FT_Face aFtFace = rServerFont.GetFtFace();
326 mnUnitsPerEM = rServerFont.GetEmUnits();
328 mpHbFace = hb_face_create_for_tables(getFontTable, &rServerFont, NULL);
329 hb_face_set_index(mpHbFace, aFtFace->face_index);
330 hb_face_set_upem(mpHbFace, mnUnitsPerEM);
333 HbLayoutEngine::~HbLayoutEngine()
335 hb_face_destroy(mpHbFace);
338 struct HbScriptRun
340 int32_t mnMin;
341 int32_t mnEnd;
342 hb_script_t maScript;
344 HbScriptRun(int32_t nMin, int32_t nEnd, UScriptCode aScript)
345 : mnMin(nMin), mnEnd(nEnd),
346 maScript(hb_icu_script_to_script(aScript))
350 typedef std::vector<HbScriptRun> HbScriptRuns;
352 namespace vcl {
353 struct Run
355 int32_t nStart;
356 int32_t nEnd;
357 UScriptCode nCode;
358 Run(int32_t nStart_, int32_t nEnd_, UScriptCode nCode_)
359 : nStart(nStart_), nEnd(nEnd_), nCode(nCode_)
363 class TextLayoutCache
365 public:
366 std::vector<vcl::Run> runs;
367 TextLayoutCache(sal_Unicode const* pStr, sal_Int32 const nEnd)
369 vcl::ScriptRun aScriptRun(
370 reinterpret_cast<const UChar *>(pStr),
371 nEnd);
372 while (aScriptRun.next())
374 runs.push_back(Run(aScriptRun.getScriptStart(),
375 aScriptRun.getScriptEnd(), aScriptRun.getScriptCode()));
381 std::shared_ptr<vcl::TextLayoutCache> ServerFontLayout::CreateTextLayoutCache(
382 OUString const& rString) const
384 return std::make_shared<vcl::TextLayoutCache>(rString.getStr(), rString.getLength());
387 bool HbLayoutEngine::Layout(ServerFontLayout& rLayout, ImplLayoutArgs& rArgs)
389 ServerFont& rFont = rLayout.GetServerFont();
390 FT_Face aFtFace = rFont.GetFtFace();
392 SAL_INFO("vcl.harfbuzz", "layout(" << this << ",rArgs=" << rArgs << ")");
394 static hb_font_funcs_t* pHbFontFuncs = getFontFuncs();
396 hb_font_t *pHbFont = hb_font_create(mpHbFace);
397 hb_font_set_funcs(pHbFont, pHbFontFuncs, &rFont, NULL);
398 hb_font_set_scale(pHbFont,
399 ((uint64_t) aFtFace->size->metrics.x_scale * (uint64_t) mnUnitsPerEM) >> 16,
400 ((uint64_t) aFtFace->size->metrics.y_scale * (uint64_t) mnUnitsPerEM) >> 16);
401 hb_font_set_ppem(pHbFont, aFtFace->size->metrics.x_ppem, aFtFace->size->metrics.y_ppem);
403 // allocate temporary arrays, note: round to even
404 int nGlyphCapacity = (3 * (rArgs.mnEndCharPos - rArgs.mnMinCharPos) | 15) + 1;
405 int32_t nVirtAdv = int32_t(aFtFace->size->metrics.height*rFont.GetStretch())>>6;
407 rLayout.Reserve(nGlyphCapacity);
409 std::unique_ptr<vcl::TextLayoutCache> pNewScriptRun;
410 vcl::TextLayoutCache const* pTextLayout;
411 if (rArgs.m_pTextLayoutCache)
413 pTextLayout = rArgs.m_pTextLayoutCache; // use cache!
415 else
417 pNewScriptRun.reset(new vcl::TextLayoutCache(rArgs.mpStr, rArgs.mnEndCharPos));
418 pTextLayout = pNewScriptRun.get();
421 Point aCurrPos(0, 0);
422 while (true)
424 int nBidiMinRunPos, nBidiEndRunPos;
425 bool bRightToLeft;
426 if (!rArgs.GetNextRun(&nBidiMinRunPos, &nBidiEndRunPos, &bRightToLeft))
427 break;
429 // Find script subruns.
430 int nCurrentPos = nBidiMinRunPos;
431 HbScriptRuns aScriptSubRuns;
432 size_t k = 0;
433 for (; k < pTextLayout->runs.size(); ++k)
435 vcl::Run const& rRun(pTextLayout->runs[k]);
436 if (rRun.nStart <= nCurrentPos && nCurrentPos < rRun.nEnd)
438 break;
442 while (nCurrentPos < nBidiEndRunPos && k < pTextLayout->runs.size())
444 int32_t nMinRunPos = nCurrentPos;
445 int32_t nEndRunPos = std::min(pTextLayout->runs[k].nEnd, nBidiEndRunPos);
446 HbScriptRun aRun(nMinRunPos, nEndRunPos, pTextLayout->runs[k].nCode);
447 aScriptSubRuns.push_back(aRun);
449 nCurrentPos = nEndRunPos;
450 ++k;
453 // RTL subruns should be reversed to ensure that final glyph order is
454 // correct.
455 if (bRightToLeft)
456 std::reverse(aScriptSubRuns.begin(), aScriptSubRuns.end());
458 for (HbScriptRuns::iterator it = aScriptSubRuns.begin(); it != aScriptSubRuns.end(); ++it)
460 int nMinRunPos = it->mnMin;
461 int nEndRunPos = it->mnEnd;
462 int nRunLen = nEndRunPos - nMinRunPos;
463 maHbScript = it->maScript;
464 // hb_language_from_string() accept ISO639-3 language tag except for Chinese.
465 LanguageTag &rTag = rArgs.maLanguageTag;
466 OString sLanguage = OUStringToOString( MsLangId::isChinese(rTag.getLanguageType()) ? rTag.getBcp47():rTag.getLanguage() , RTL_TEXTENCODING_UTF8 );
469 static hb_unicode_funcs_t* pHbUnicodeFuncs = getUnicodeFuncs();
471 int nHbFlags = HB_BUFFER_FLAGS_DEFAULT;
472 if (nMinRunPos == 0)
473 nHbFlags |= HB_BUFFER_FLAG_BOT; /* Beginning-of-text */
474 if (nEndRunPos == rArgs.mnLength)
475 nHbFlags |= HB_BUFFER_FLAG_EOT; /* End-of-text */
477 hb_buffer_t *pHbBuffer = hb_buffer_create();
478 hb_buffer_set_unicode_funcs(pHbBuffer, pHbUnicodeFuncs);
479 hb_buffer_set_direction(pHbBuffer, bRightToLeft ? HB_DIRECTION_RTL: HB_DIRECTION_LTR);
480 hb_buffer_set_script(pHbBuffer, maHbScript);
481 hb_buffer_set_language(pHbBuffer, hb_language_from_string(sLanguage.getStr(), -1));
482 hb_buffer_set_flags(pHbBuffer, (hb_buffer_flags_t) nHbFlags);
483 hb_buffer_add_utf16(pHbBuffer, rArgs.mpStr, rArgs.mnLength, nMinRunPos, nRunLen);
484 hb_shape(pHbFont, pHbBuffer, NULL, 0);
486 int nRunGlyphCount = hb_buffer_get_length(pHbBuffer);
487 hb_glyph_info_t *pHbGlyphInfos = hb_buffer_get_glyph_infos(pHbBuffer, NULL);
488 hb_glyph_position_t *pHbPositions = hb_buffer_get_glyph_positions(pHbBuffer, NULL);
490 for (int i = 0; i < nRunGlyphCount; ++i) {
491 int32_t nGlyphIndex = pHbGlyphInfos[i].codepoint;
492 int32_t nCharPos = pHbGlyphInfos[i].cluster;
494 // tdf#89231 if it's just a missing non-breaking space, then use a normal space
495 if (!nGlyphIndex && (SalLayoutFlags::ForFallback & rArgs.mnFlags) && nCharPos >= 0 && rArgs.mpStr[nCharPos] == 0x202F)
497 nGlyphIndex = rFont.GetGlyphIndex(' ');
500 // if needed request glyph fallback by updating LayoutArgs
501 if (!nGlyphIndex)
503 rLayout.SetNeedFallback(rArgs, nCharPos, bRightToLeft);
504 if (SalLayoutFlags::ForFallback & rArgs.mnFlags)
505 continue;
508 // apply vertical flags and glyph substitution
509 // XXX: Use HB_DIRECTION_TTB above and apply whatever flags magic
510 // FixupGlyphIndex() is doing, minus the GSUB part.
511 if (nCharPos >= 0)
513 sal_UCS4 aChar = rArgs.mpStr[nCharPos];
514 nGlyphIndex = rFont.FixupGlyphIndex(nGlyphIndex, aChar);
517 bool bInCluster = false;
518 if (i > 0 && pHbGlyphInfos[i].cluster == pHbGlyphInfos[i - 1].cluster)
519 bInCluster = true;
521 long nGlyphFlags = 0;
522 if (bRightToLeft)
523 nGlyphFlags |= GlyphItem::IS_RTL_GLYPH;
525 if (bInCluster)
526 nGlyphFlags |= GlyphItem::IS_IN_CLUSTER;
528 // The whole IS_DIACRITIC concept is a stupid hack that was
529 // introduced ages ago to work around the utter brokenness of the
530 // way justification adjustments are applied (the DXArray fiasco).
531 // Since it is such a stupid hack, there is no sane way to directly
532 // map to concepts of the "outside" world, so we do some rather
533 // ugly hacks:
534 // * If the font has a GDEF table, we check for glyphs with mark
535 // glyph class which is sensible, except that some fonts
536 // (fdo#70968) assign mark class to spacing marks (which is wrong
537 // but usually harmless), so we try to sniff what HarfBuzz thinks
538 // about this glyph by checking if it gives it a zero advance
539 // width.
540 // * If the font has no GDEF table, we just check if the glyph has
541 // zero advance width, but this is stupid and can be wrong. A
542 // better way would to check the character's Unicode combining
543 // class, but unfortunately glyph gives combining marks the
544 // cluster value of its base character, so nCharPos will be
545 // pointing to the wrong character (but HarfBuzz might change
546 // this in the future).
547 bool bDiacritic = false;
548 if (hb_ot_layout_has_glyph_classes(mpHbFace))
550 // the font has GDEF table
551 bool bMark = hb_ot_layout_get_glyph_class(mpHbFace, nGlyphIndex) == HB_OT_LAYOUT_GLYPH_CLASS_MARK;
552 if (bMark && pHbPositions[i].x_advance == 0)
553 bDiacritic = true;
555 else
557 // the font lacks GDEF table
558 if (pHbPositions[i].x_advance == 0)
559 bDiacritic = true;
562 if (bDiacritic)
563 nGlyphFlags |= GlyphItem::IS_DIACRITIC;
565 int32_t nXOffset = pHbPositions[i].x_offset >> 6;
566 int32_t nYOffset = pHbPositions[i].y_offset >> 6;
567 int32_t nXAdvance = pHbPositions[i].x_advance >> 6;
568 int32_t nYAdvance = pHbPositions[i].y_advance >> 6;
569 if ( nGlyphIndex & GF_ROTMASK )
570 nXAdvance = nVirtAdv;
572 Point aNewPos = Point(aCurrPos.X() + nXOffset, -(aCurrPos.Y() + nYOffset));
573 const GlyphItem aGI(nCharPos, nGlyphIndex, aNewPos, nGlyphFlags, nXAdvance, nXOffset, nYOffset);
574 rLayout.AppendGlyph(aGI);
576 aCurrPos.X() += nXAdvance;
577 aCurrPos.Y() += nYAdvance;
580 hb_buffer_destroy(pHbBuffer);
584 hb_font_destroy(pHbFont);
586 // sort glyphs in visual order
587 // and then in logical order (e.g. diacritics after cluster start)
588 // XXX: why?
589 rLayout.SortGlyphItems();
591 // determine need for kashida justification
592 if((rArgs.mpDXArray || rArgs.mnLayoutWidth)
593 && ((maHbScript == HB_SCRIPT_ARABIC) || (maHbScript == HB_SCRIPT_SYRIAC)))
594 rArgs.mnFlags |= SalLayoutFlags::KashidaJustification;
596 return true;
599 ServerFontLayoutEngine* ServerFont::GetLayoutEngine()
601 // find best layout engine for font, platform, script and language
602 if (!mpLayoutEngine) {
603 mpLayoutEngine = new HbLayoutEngine(*this);
605 return mpLayoutEngine;
608 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */