fdo#74697 Add Bluez 5 support for impress remote.
[LibreOffice.git] / vcl / generic / glyphs / gcach_layout.cxx
blobc8fbda7b54ef2435a85fbc3ecf64acbe20d57861
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>
24 #include <boost/static_assert.hpp>
26 #include <i18nlangtag/mslangid.hxx>
28 #include <vcl/svapp.hxx>
30 #include <sal/alloca.h>
31 #include <rtl/instance.hxx>
33 #include <hb-icu.h>
34 #include <hb-ot.h>
36 #include <layout/LayoutEngine.h>
37 #include <layout/LEFontInstance.h>
38 #include <layout/LELanguages.h>
39 #include <layout/LEScripts.h>
41 #include <unicode/uscript.h>
42 #include <unicode/ubidi.h>
44 #include <com/sun/star/lang/XMultiServiceFactory.hpp>
45 #include <com/sun/star/i18n/CharacterIteratorMode.hpp>
46 #include <comphelper/processfactory.hxx>
48 // =======================================================================
49 // layout implementation for ServerFont
50 // =======================================================================
52 ServerFontLayout::ServerFontLayout( ServerFont& rFont )
53 : mrServerFont( rFont )
55 bUseHarfBuzz = (getenv("SAL_USE_ICULE") == NULL);
58 void ServerFontLayout::DrawText( SalGraphics& rSalGraphics ) const
60 rSalGraphics.DrawServerFontLayout( *this );
63 // -----------------------------------------------------------------------
65 bool ServerFontLayout::LayoutText( ImplLayoutArgs& rArgs )
67 ServerFontLayoutEngine* pLE = mrServerFont.GetLayoutEngine(bUseHarfBuzz);
68 assert(pLE);
69 bool bRet = pLE ? pLE->layout(*this, rArgs) : false;
70 return bRet;
73 // -----------------------------------------------------------------------
74 void ServerFontLayout::AdjustLayout( ImplLayoutArgs& rArgs )
76 GenericSalLayout::AdjustLayout( rArgs );
78 // apply asian kerning if the glyphs are not already formatted
79 if( (rArgs.mnFlags & SAL_LAYOUT_KERNING_ASIAN)
80 && !(rArgs.mnFlags & SAL_LAYOUT_VERTICAL) )
81 if( (rArgs.mpDXArray != NULL) || (rArgs.mnLayoutWidth != 0) )
82 ApplyAsianKerning( rArgs.mpStr, rArgs.mnLength );
84 // insert kashidas where requested by the formatting array
85 if( (rArgs.mnFlags & SAL_LAYOUT_KASHIDA_JUSTIFICATON) && rArgs.mpDXArray )
87 int nKashidaIndex = mrServerFont.GetGlyphIndex( 0x0640 );
88 if( nKashidaIndex != 0 )
90 const GlyphMetric& rGM = mrServerFont.GetGlyphMetric( nKashidaIndex );
91 KashidaJustify( nKashidaIndex, rGM.GetCharWidth() );
92 // TODO: kashida-GSUB/GPOS
97 void ServerFontLayout::setNeedFallback(ImplLayoutArgs& rArgs, sal_Int32 nCharPos,
98 bool bRightToLeft)
100 if (nCharPos < 0)
101 return;
103 using namespace ::com::sun::star;
105 if (!mxBreak.is())
107 uno::Reference< lang::XMultiServiceFactory > xFactory =
108 comphelper::getProcessServiceFactory();
109 mxBreak = uno::Reference< i18n::XBreakIterator >(xFactory->createInstance(
110 "com.sun.star.i18n.BreakIterator"), uno::UNO_QUERY);
113 LanguageTag aLangTag(rArgs.meLanguage);
114 lang::Locale aLocale(aLangTag.getLocale());
116 //if position nCharPos is missing in the font, grab the entire grapheme and
117 //mark all glyphs as missing so the whole thing is rendered with the same
118 //font
119 OUString aRun(rArgs.mpStr);
120 sal_Int32 nDone;
121 sal_Int32 nGraphemeStartPos =
122 mxBreak->previousCharacters(aRun, nCharPos+1, aLocale,
123 i18n::CharacterIteratorMode::SKIPCELL, 1, nDone);
124 sal_Int32 nGraphemeEndPos =
125 mxBreak->nextCharacters(aRun, nCharPos, aLocale,
126 i18n::CharacterIteratorMode::SKIPCELL, 1, nDone);
128 rArgs.NeedFallback(nGraphemeStartPos, nGraphemeEndPos, bRightToLeft);
131 // =======================================================================
133 std::ostream &operator <<(std::ostream& s, ServerFont* pFont)
135 #ifndef SAL_LOG_INFO
136 (void) pFont;
137 #else
138 FT_Face aFace = pFont->GetFtFace();
139 const char* pName = FT_Get_Postscript_Name(aFace);
140 if (pName)
141 s << pName;
142 else
143 s << pFont->GetFontFileName();
144 #endif
145 return s;
148 static hb_font_funcs_t* pHbFontFuncs = NULL;
149 static hb_unicode_funcs_t* pHbUnicodeFuncs = NULL;
151 static hb_blob_t *getFontTable(hb_face_t* /*face*/, hb_tag_t nTableTag, void* pUserData)
153 char pTagName[5];
154 pTagName[0] = (char)(nTableTag >> 24);
155 pTagName[1] = (char)(nTableTag >> 16);
156 pTagName[2] = (char)(nTableTag >> 8);
157 pTagName[3] = (char)(nTableTag);
158 pTagName[4] = 0;
160 ServerFont* pFont = (ServerFont*) pUserData;
162 SAL_INFO("vcl.harfbuzz.layout", "getFontTable(" << pFont << ", " << pTagName << ")");
164 sal_uLong nLength;
165 const unsigned char* pBuffer = pFont->GetTable(pTagName, &nLength);
167 hb_blob_t* pBlob = NULL;
168 if (pBuffer != NULL)
169 pBlob = hb_blob_create((const char*) pBuffer, nLength, HB_MEMORY_MODE_READONLY, (void*) pBuffer, NULL);
171 return pBlob;
174 static hb_bool_t getFontGlyph(hb_font_t* /*font*/, void* pFontData,
175 hb_codepoint_t ch, hb_codepoint_t vs,
176 hb_codepoint_t* nGlyphIndex,
177 void* /*pUserData*/)
179 ServerFont* pFont = (ServerFont*) pFontData;
180 *nGlyphIndex = pFont->GetRawGlyphIndex(ch, vs);
182 return *nGlyphIndex != 0;
185 static hb_position_t getGlyphAdvanceH(hb_font_t* /*font*/, void* pFontData,
186 hb_codepoint_t nGlyphIndex,
187 void* /*pUserData*/)
189 ServerFont* pFont = (ServerFont*) pFontData;
190 const GlyphMetric& rGM = pFont->GetGlyphMetric(nGlyphIndex);
191 return rGM.GetCharWidth() << 6;
194 static hb_position_t getGlyphAdvanceV(hb_font_t* /*font*/, void* /*pFontData*/,
195 hb_codepoint_t /*nGlyphIndex*/,
196 void* /*pUserData*/)
198 // XXX: vertical metrics
199 return 0;
202 static hb_bool_t getGlyphOriginH(hb_font_t* /*font*/, void* /*pFontData*/,
203 hb_codepoint_t /*nGlyphIndex*/,
204 hb_position_t* /*x*/, hb_position_t* /*y*/,
205 void* /*pUserData*/)
207 // the horizontal origin is always (0, 0)
208 return true;
211 static hb_bool_t getGlyphOriginV(hb_font_t* /*font*/, void* /*pFontData*/,
212 hb_codepoint_t /*nGlyphIndex*/,
213 hb_position_t* /*x*/, hb_position_t* /*y*/,
214 void* /*pUserData*/)
216 // XXX: vertical origin
217 return true;
220 static hb_position_t getGlyphKerningH(hb_font_t* /*font*/, void* pFontData,
221 hb_codepoint_t nGlyphIndex1, hb_codepoint_t nGlyphIndex2,
222 void* /*pUserData*/)
224 // This callback is for old style 'kern' table, GPOS kerning is handled by HarfBuzz directly
226 // XXX: there is ServerFont::GetKernPairs() but it does many "smart" things
227 // that I'm not sure about, so I'm using FreeType directly
228 // P.S. if we decided not to use ServerFont::GetKernPairs() then it and all
229 // other implementattions should be removed, don't seem to be used
230 // anywhere.
232 ServerFont* pFont = (ServerFont*) pFontData;
233 FT_Face aFace = pFont->GetFtFace();
235 SAL_INFO("vcl.harfbuzz.layout", "getGlyphKerningH(" << pFont << ", " << nGlyphIndex1 << ", " << nGlyphIndex2 << ")");
237 FT_Error error;
238 FT_Vector kerning;
239 hb_position_t ret;
241 error = FT_Get_Kerning(aFace, nGlyphIndex1, nGlyphIndex2, FT_KERNING_DEFAULT, &kerning);
242 if (error)
243 ret = 0;
244 else
245 ret = kerning.x;
247 return ret;
250 static hb_position_t getGlyphKerningV(hb_font_t* /*font*/, void* /*pFontData*/,
251 hb_codepoint_t /*nGlyphIndex1*/, hb_codepoint_t /*nGlyphIndex2*/,
252 void* /*pUserData*/)
254 // XXX vertical kerning
255 return 0;
258 static hb_bool_t getGlyphExtents(hb_font_t* /*font*/, void* pFontData,
259 hb_codepoint_t nGlyphIndex,
260 hb_glyph_extents_t* pExtents,
261 void* /*pUserData*/)
263 ServerFont* pFont = (ServerFont*) pFontData;
264 FT_Face aFace = pFont->GetFtFace();
266 SAL_INFO("vcl.harfbuzz.layout", "getGlyphExtents(" << pFont << ", " << nGlyphIndex << ")");
268 FT_Error error;
269 error = FT_Load_Glyph(aFace, nGlyphIndex, FT_LOAD_DEFAULT);
270 if (!error)
272 pExtents->x_bearing = aFace->glyph->metrics.horiBearingX;
273 pExtents->y_bearing = aFace->glyph->metrics.horiBearingY;
274 pExtents->width = aFace->glyph->metrics.width;
275 pExtents->height = -aFace->glyph->metrics.height;
278 return !error;
281 static hb_bool_t getGlyphContourPoint(hb_font_t* /*font*/, void* pFontData,
282 hb_codepoint_t nGlyphIndex, unsigned int nPointIndex,
283 hb_position_t *x, hb_position_t *y,
284 void* /*pUserData*/)
286 bool ret = false;
287 ServerFont* pFont = (ServerFont*) pFontData;
288 FT_Face aFace = pFont->GetFtFace();
290 SAL_INFO("vcl.harfbuzz.layout", "getGlyphContourPoint(" << pFont << ", " << nGlyphIndex << ", " << nPointIndex << ")");
292 FT_Error error;
293 error = FT_Load_Glyph(aFace, nGlyphIndex, FT_LOAD_DEFAULT);
294 if (!error)
296 if (aFace->glyph->format == FT_GLYPH_FORMAT_OUTLINE)
298 if (nPointIndex < (unsigned int) aFace->glyph->outline.n_points)
300 *x = aFace->glyph->outline.points[nPointIndex].x;
301 *y = aFace->glyph->outline.points[nPointIndex].y;
302 ret = true;
307 return ret;
310 static hb_font_funcs_t* getFontFuncs(void)
312 static hb_font_funcs_t* funcs = hb_font_funcs_create();
314 hb_font_funcs_set_glyph_func (funcs, getFontGlyph, NULL, NULL);
315 hb_font_funcs_set_glyph_h_advance_func (funcs, getGlyphAdvanceH, NULL, NULL);
316 hb_font_funcs_set_glyph_v_advance_func (funcs, getGlyphAdvanceV, NULL, NULL);
317 hb_font_funcs_set_glyph_h_origin_func (funcs, getGlyphOriginH, NULL, NULL);
318 hb_font_funcs_set_glyph_v_origin_func (funcs, getGlyphOriginV, NULL, NULL);
319 hb_font_funcs_set_glyph_h_kerning_func (funcs, getGlyphKerningH, NULL, NULL);
320 hb_font_funcs_set_glyph_v_kerning_func (funcs, getGlyphKerningV, NULL, NULL);
321 hb_font_funcs_set_glyph_extents_func (funcs, getGlyphExtents, NULL, NULL);
322 hb_font_funcs_set_glyph_contour_point_func (funcs, getGlyphContourPoint, NULL, NULL);
324 return funcs;
327 // Disabled Unicode compatibility decomposition, see fdo#66715
328 static unsigned int unicodeDecomposeCompatibility(hb_unicode_funcs_t* /*ufuncs*/,
329 hb_codepoint_t /*u*/,
330 hb_codepoint_t* /*decomposed*/,
331 void* /*user_data*/)
333 return 0;
336 static hb_unicode_funcs_t* getUnicodeFuncs(void)
338 static hb_unicode_funcs_t* ufuncs = hb_unicode_funcs_create(hb_icu_get_unicode_funcs());
339 hb_unicode_funcs_set_decompose_compatibility_func(ufuncs, unicodeDecomposeCompatibility, NULL, NULL);
340 return ufuncs;
343 class HbLayoutEngine : public ServerFontLayoutEngine
345 private:
346 UScriptCode meScriptCode;
347 hb_face_t* mpHbFace;
348 int fUnitsPerEM;
350 public:
351 HbLayoutEngine(ServerFont&);
352 virtual ~HbLayoutEngine();
354 virtual bool layout(ServerFontLayout&, ImplLayoutArgs&);
357 HbLayoutEngine::HbLayoutEngine(ServerFont& rServerFont)
358 : meScriptCode(USCRIPT_INVALID_CODE),
359 mpHbFace(NULL),
360 fUnitsPerEM(0)
362 FT_Face aFtFace = rServerFont.GetFtFace();
363 fUnitsPerEM = rServerFont.GetEmUnits();
365 mpHbFace = hb_face_create_for_tables(getFontTable, &rServerFont, NULL);
366 hb_face_set_index(mpHbFace, aFtFace->face_index);
367 hb_face_set_upem(mpHbFace, fUnitsPerEM);
370 HbLayoutEngine::~HbLayoutEngine()
372 hb_face_destroy(mpHbFace);
375 bool HbLayoutEngine::layout(ServerFontLayout& rLayout, ImplLayoutArgs& rArgs)
377 ServerFont& rFont = rLayout.GetServerFont();
378 FT_Face aFtFace = rFont.GetFtFace();
380 SAL_INFO("vcl.harfbuzz.layout", "layout(" << this << ",rArgs=" << rArgs << ")");
382 if (pHbFontFuncs == NULL)
383 pHbFontFuncs = getFontFuncs();
385 hb_font_t *pHbFont = hb_font_create(mpHbFace);
386 hb_font_set_funcs(pHbFont, pHbFontFuncs, &rFont, NULL);
387 hb_font_set_scale(pHbFont,
388 ((uint64_t) aFtFace->size->metrics.x_scale * (uint64_t) fUnitsPerEM) >> 16,
389 ((uint64_t) aFtFace->size->metrics.y_scale * (uint64_t) fUnitsPerEM) >> 16);
390 hb_font_set_ppem(pHbFont, aFtFace->size->metrics.x_ppem, aFtFace->size->metrics.y_ppem);
392 // allocate temporary arrays, note: round to even
393 int nGlyphCapacity = (3 * (rArgs.mnEndCharPos - rArgs.mnMinCharPos) | 15) + 1;
395 rLayout.Reserve(nGlyphCapacity);
397 Point aCurrPos(0, 0);
398 while (true)
400 int nMinRunPos, nEndRunPos;
401 bool bRightToLeft;
402 if (!rArgs.GetNextRun(&nMinRunPos, &nEndRunPos, &bRightToLeft))
403 break;
405 int nRunLen = nEndRunPos - nMinRunPos;
407 // find matching script
408 // TODO: use ICU's UScriptRun API to properly resolves "common" and
409 // "inherited" script codes, probably use it in GetNextRun() and return
410 // the script there
411 UScriptCode eScriptCode = USCRIPT_INVALID_CODE;
412 for (int i = nMinRunPos; i < nEndRunPos; ++i)
414 UErrorCode rcI18n = U_ZERO_ERROR;
415 UScriptCode eNextScriptCode = uscript_getScript(rArgs.mpStr[i], &rcI18n);
416 if ((eNextScriptCode > USCRIPT_INHERITED))
418 eScriptCode = eNextScriptCode;
419 if (eNextScriptCode != USCRIPT_LATIN)
420 break;
423 if (eScriptCode < 0) // TODO: handle errors better
424 eScriptCode = USCRIPT_LATIN;
426 meScriptCode = eScriptCode;
428 LanguageTag aLangTag(rArgs.meLanguage);
429 OString sLanguage = OUStringToOString(aLangTag.getLanguage(), RTL_TEXTENCODING_UTF8);
431 if (pHbUnicodeFuncs == NULL)
432 pHbUnicodeFuncs = getUnicodeFuncs();
434 hb_buffer_t *pHbBuffer = hb_buffer_create();
435 hb_buffer_set_unicode_funcs(pHbBuffer, pHbUnicodeFuncs);
436 hb_buffer_set_direction(pHbBuffer, bRightToLeft ? HB_DIRECTION_RTL: HB_DIRECTION_LTR);
437 hb_buffer_set_script(pHbBuffer, hb_icu_script_to_script(eScriptCode));
438 hb_buffer_set_language(pHbBuffer, hb_language_from_string(sLanguage.getStr(), -1));
439 hb_buffer_add_utf16(pHbBuffer, rArgs.mpStr, rArgs.mnLength, nMinRunPos, nRunLen);
440 hb_shape(pHbFont, pHbBuffer, NULL, 0);
442 int nRunGlyphCount = hb_buffer_get_length(pHbBuffer);
443 hb_glyph_info_t *pHbGlyphInfos = hb_buffer_get_glyph_infos(pHbBuffer, NULL);
444 hb_glyph_position_t *pHbPositions = hb_buffer_get_glyph_positions(pHbBuffer, NULL);
446 for (int i = 0; i < nRunGlyphCount; ++i) {
447 int32_t nGlyphIndex = pHbGlyphInfos[i].codepoint;
448 int32_t nCharPos = pHbGlyphInfos[i].cluster;
450 // if needed request glyph fallback by updating LayoutArgs
451 if (!nGlyphIndex)
453 rLayout.setNeedFallback(rArgs, nCharPos, bRightToLeft);
454 if (SAL_LAYOUT_FOR_FALLBACK & rArgs.mnFlags)
455 continue;
458 // apply vertical flags and glyph substitution
459 // XXX: Use HB_DIRECTION_TTB above and apply whatever flags magic
460 // FixupGlyphIndex() is doing, minus the GSUB part.
461 if (nCharPos >= 0)
463 sal_UCS4 aChar = rArgs.mpStr[nCharPos];
464 nGlyphIndex = rFont.FixupGlyphIndex(nGlyphIndex, aChar);
467 bool bInCluster = false;
468 if (i > 0 && pHbGlyphInfos[i].cluster == pHbGlyphInfos[i - 1].cluster)
469 bInCluster = true;
471 long nGlyphFlags = 0;
472 if (bRightToLeft)
473 nGlyphFlags |= GlyphItem::IS_RTL_GLYPH;
475 if (bInCluster)
476 nGlyphFlags |= GlyphItem::IS_IN_CLUSTER;
478 // The whole IS_DIACRITIC concept is a stupid hack that was
479 // introduced ages ago to work around the utter brokenness of the
480 // way justification adjustments are applied (the DXArray fiasco).
481 // Since it is such a stupid hack, there is no sane way to directly
482 // map to concepts of the "outside" world, so we do some rather
483 // ugly hacks:
484 // * If the font has a GDEF table, we check for glyphs with mark
485 // glyph class which is sensible, except that some fonts
486 // (fdo#70968) assign mark class to spacing marks (which is wrong
487 // but usually harmless), so we try to sniff what HarfBuzz thinks
488 // about this glyph by checking if it gives it a zero advance
489 // width.
490 // * If the font has no GDEF table, we just check if the glyph has
491 // zero advance width, but this is stupid and can be wrong. A
492 // better way would to check the character's Unicode combining
493 // class, but unfortunately glyph gives combining marks the
494 // cluster value of its base character, so nCharPos will be
495 // pointing to the wrong character (but HarfBuzz might change
496 // this in the future).
497 bool bDiacritic = false;
498 if (hb_ot_layout_has_glyph_classes(mpHbFace))
500 // the font has GDEF table
501 bool bMark = hb_ot_layout_get_glyph_class(mpHbFace, nGlyphIndex) == HB_OT_LAYOUT_GLYPH_CLASS_MARK;
502 if (bMark && pHbPositions[i].x_advance == 0)
503 bDiacritic = true;
505 else
507 // the font lacks GDEF table
508 if (pHbPositions[i].x_advance == 0)
509 bDiacritic = true;
512 if (bDiacritic)
513 nGlyphFlags |= GlyphItem::IS_DIACRITIC;
515 int32_t nXOffset = pHbPositions[i].x_offset >> 6;
516 int32_t nYOffset = pHbPositions[i].y_offset >> 6;
517 int32_t nXAdvance = pHbPositions[i].x_advance >> 6;
518 int32_t nYAdvance = pHbPositions[i].y_advance >> 6;
520 Point aNewPos = Point(aCurrPos.X() + nXOffset, -(aCurrPos.Y() + nYOffset));
521 const GlyphItem aGI(nCharPos, nGlyphIndex, aNewPos, nGlyphFlags, nXAdvance, nXOffset);
522 rLayout.AppendGlyph(aGI);
524 aCurrPos.X() += nXAdvance;
525 aCurrPos.Y() += nYAdvance;
528 hb_buffer_destroy(pHbBuffer);
531 hb_font_destroy(pHbFont);
533 // sort glyphs in visual order
534 // and then in logical order (e.g. diacritics after cluster start)
535 // XXX: why?
536 rLayout.SortGlyphItems();
538 // determine need for kashida justification
539 if((rArgs.mpDXArray || rArgs.mnLayoutWidth)
540 && ((meScriptCode == USCRIPT_ARABIC) || (meScriptCode == USCRIPT_SYRIAC)))
541 rArgs.mnFlags |= SAL_LAYOUT_KASHIDA_JUSTIFICATON;
543 return true;
546 // =======================================================================
547 // bridge to ICU LayoutEngine
548 // =======================================================================
550 using namespace U_ICU_NAMESPACE;
552 static const LEGlyphID ICU_DELETED_GLYPH = 0xFFFF;
553 static const LEGlyphID ICU_MARKED_GLYPH = 0xFFFE;
555 // -----------------------------------------------------------------------
557 class IcuFontFromServerFont
558 : public LEFontInstance
560 private:
561 ServerFont& mrServerFont;
563 public:
564 IcuFontFromServerFont( ServerFont& rFont )
565 : mrServerFont( rFont )
568 virtual const void* getFontTable(LETag tableTag, size_t &length) const;
569 virtual const void* getFontTable(LETag tableTag) const;
570 virtual le_int32 getUnitsPerEM() const;
571 virtual float getXPixelsPerEm() const;
572 virtual float getYPixelsPerEm() const;
573 virtual float getScaleFactorX() const;
574 virtual float getScaleFactorY() const;
576 using LEFontInstance::mapCharToGlyph;
577 virtual LEGlyphID mapCharToGlyph( LEUnicode32 ch ) const;
578 virtual LEGlyphID mapCharToGlyph( LEUnicode32 ch, const LECharMapper *mapper, le_bool filterZeroWidth ) const;
580 virtual le_int32 getAscent() const;
581 virtual le_int32 getDescent() const;
582 virtual le_int32 getLeading() const;
584 virtual void getGlyphAdvance( LEGlyphID glyph, LEPoint &advance ) const;
585 virtual le_bool getGlyphPoint( LEGlyphID glyph, le_int32 pointNumber, LEPoint& point ) const;
588 // -----------------------------------------------------------------------
590 const void* IcuFontFromServerFont::getFontTable( LETag nICUTableTag, size_t & rLength ) const
592 char pTagName[5];
593 pTagName[0] = (char)(nICUTableTag >> 24);
594 pTagName[1] = (char)(nICUTableTag >> 16);
595 pTagName[2] = (char)(nICUTableTag >> 8);
596 pTagName[3] = (char)(nICUTableTag);
597 pTagName[4] = 0;
599 sal_uLong nLength = 0;
600 const unsigned char* pBuffer = mrServerFont.GetTable( pTagName, &nLength );
601 rLength = static_cast<size_t>(nLength);
602 SAL_INFO("vcl", "IcuGetTable(\"" << pTagName << "\") => " << pBuffer << ", len=" << rLength);
603 SAL_INFO(
604 "vcl",
605 "font( h=" << mrServerFont.GetFontSelData().mnHeight << ", \""
606 << mrServerFont.GetFontFileName()->getStr() << "\" )");
607 return pBuffer;
610 const void* IcuFontFromServerFont::getFontTable( LETag nICUTableTag ) const
612 size_t nLength = 0;
613 return getFontTable( nICUTableTag, nLength);
616 // -----------------------------------------------------------------------
618 le_int32 IcuFontFromServerFont::getUnitsPerEM() const
620 return mrServerFont.GetEmUnits();
623 // -----------------------------------------------------------------------
625 float IcuFontFromServerFont::getXPixelsPerEm() const
627 const FontSelectPattern& r = mrServerFont.GetFontSelData();
628 float fX = r.mnWidth ? r.mnWidth : r.mnHeight;
629 return fX;
632 // -----------------------------------------------------------------------
634 float IcuFontFromServerFont::getYPixelsPerEm() const
636 float fY = mrServerFont.GetFontSelData().mnHeight;
637 return fY;
640 // -----------------------------------------------------------------------
642 float IcuFontFromServerFont::getScaleFactorX() const
644 return 1.0;
647 // -----------------------------------------------------------------------
649 float IcuFontFromServerFont::getScaleFactorY() const
651 return 1.0;
654 // -----------------------------------------------------------------------
656 LEGlyphID IcuFontFromServerFont::mapCharToGlyph( LEUnicode32 ch ) const
658 LEGlyphID nGlyphIndex = mrServerFont.GetRawGlyphIndex( ch );
659 return nGlyphIndex;
662 LEGlyphID IcuFontFromServerFont::mapCharToGlyph( LEUnicode32 ch, const LECharMapper *mapper, le_bool /*filterZeroWidth*/ ) const
665 fdo#31821, icu has...
666 >│93 if (filterZeroWidth && (mappedChar == 0x200C || mappedChar == 0x200D)) { │
667 │94 return canDisplay(mappedChar) ? 0x0001 : 0xFFFF; │
668 │95 }
669 so only the Indic layouts allow the joiners to get mapped to glyphs
671 return LEFontInstance::mapCharToGlyph( ch, mapper, false );
674 // -----------------------------------------------------------------------
676 le_int32 IcuFontFromServerFont::getAscent() const
678 const FT_Size_Metrics& rMetrics = mrServerFont.GetMetricsFT();
679 le_int32 nAscent = (+rMetrics.ascender + 32) >> 6;
680 return nAscent;
683 // -----------------------------------------------------------------------
685 le_int32 IcuFontFromServerFont::getDescent() const
687 const FT_Size_Metrics& rMetrics = mrServerFont.GetMetricsFT();
688 le_int32 nDescent = (-rMetrics.descender + 32) >> 6;
689 return nDescent;
692 // -----------------------------------------------------------------------
694 le_int32 IcuFontFromServerFont::getLeading() const
696 const FT_Size_Metrics& rMetrics = mrServerFont.GetMetricsFT();
697 le_int32 nLeading = ((rMetrics.height - rMetrics.ascender + rMetrics.descender) + 32) >> 6;
698 return nLeading;
701 // -----------------------------------------------------------------------
703 void IcuFontFromServerFont::getGlyphAdvance( LEGlyphID nGlyphIndex,
704 LEPoint &advance ) const
706 if( (nGlyphIndex == ICU_MARKED_GLYPH)
707 || (nGlyphIndex == ICU_DELETED_GLYPH) )
709 // deleted glyph or mark glyph has not advance
710 advance.fX = 0;
712 else
714 const GlyphMetric& rGM = mrServerFont.GetGlyphMetric( nGlyphIndex );
715 advance.fX = rGM.GetCharWidth();
718 advance.fY = 0;
721 // -----------------------------------------------------------------------
723 le_bool IcuFontFromServerFont::getGlyphPoint( LEGlyphID,
724 le_int32 pointNumber, LEPoint& ) const
726 //TODO: replace dummy implementation
727 SAL_INFO("vcl", "getGlyphPoint(" << pointNumber << ")");
728 return false;
731 // =======================================================================
733 class IcuLayoutEngine : public ServerFontLayoutEngine
735 private:
736 IcuFontFromServerFont maIcuFont;
738 LanguageCodes meLanguageCode;
739 le_int32 meScriptCode;
740 le_int32 mnLayoutFlags;
741 LayoutEngine* mpIcuLE;
743 public:
744 IcuLayoutEngine( ServerFont& );
745 virtual ~IcuLayoutEngine();
747 virtual bool layout( ServerFontLayout&, ImplLayoutArgs& );
750 // -----------------------------------------------------------------------
752 IcuLayoutEngine::IcuLayoutEngine( ServerFont& rServerFont )
753 : maIcuFont( rServerFont ),
754 meLanguageCode( nullLanguageCode ),
755 meScriptCode( USCRIPT_INVALID_CODE ),
756 mnLayoutFlags( 0 ),
757 mpIcuLE( NULL )
760 // -----------------------------------------------------------------------
762 IcuLayoutEngine::~IcuLayoutEngine()
764 delete mpIcuLE;
767 // -----------------------------------------------------------------------
769 namespace
771 LanguageCodes mapLanguageTypetoICU(LanguageType eLangCode)
773 LanguageTag aLangTag(eLangCode);
774 OUString sLanguage = aLangTag.getLanguage();
776 if (sLanguage == "af") // Afrikaans
777 return afkLanguageCode;
778 else if (sLanguage == "ar") // Arabic
779 return araLanguageCode;
780 else if (sLanguage == "as") // Assamese
781 return asmLanguageCode;
782 else if (sLanguage == "be") // Belarussian
783 return belLanguageCode;
784 else if (sLanguage == "bn") // Bengali
785 return benLanguageCode;
786 else if (sLanguage == "bo") // Tibetan
787 return tibLanguageCode;
788 else if (sLanguage == "bu") // Bulgarian
789 return bgrLanguageCode;
790 else if (sLanguage == "ca") // Catalan
791 return catLanguageCode;
792 else if (sLanguage == "cs") // Czech
793 return csyLanguageCode;
794 else if (sLanguage == "ch") // Chechen
795 return cheLanguageCode;
796 else if (sLanguage == "co") // Coptic
797 return copLanguageCode;
798 else if (sLanguage == "cy") // Welsh
799 return welLanguageCode;
800 else if (sLanguage == "da") // Danish
801 return danLanguageCode;
802 else if (sLanguage == "de") // German
803 return deuLanguageCode;
804 else if (sLanguage == "dz") // Dzongkha
805 return dznLanguageCode;
806 else if (sLanguage == "el") // Greek
807 return ellLanguageCode;
808 else if (sLanguage == "en") // English
809 return engLanguageCode;
810 else if (sLanguage == "et") // Estonian
811 return etiLanguageCode;
812 else if (sLanguage == "eu") // Basque
813 return euqLanguageCode;
814 else if (sLanguage == "fa") // Farsi
815 return farLanguageCode;
816 else if (sLanguage == "fi") // Finnish
817 return finLanguageCode;
818 else if (sLanguage == "fr") // French
819 return fraLanguageCode;
820 else if (sLanguage == "ga") // Irish Gaelic
821 return gaeLanguageCode;
822 else if (sLanguage == "gu") // Gujarati
823 return gujLanguageCode;
824 else if (sLanguage == "ha") // Hausa
825 return hauLanguageCode;
826 else if (sLanguage == "he") // Hebrew
827 return iwrLanguageCode;
828 else if (sLanguage == "hi") // Hindi
829 return hinLanguageCode;
830 else if (sLanguage == "hr") // Croatian
831 return hrvLanguageCode;
832 else if (sLanguage == "hu") // Hungarian
833 return hunLanguageCode;
834 else if (sLanguage == "hy") // Armenian
835 return hyeLanguageCode;
836 else if (sLanguage == "id") // Indonesian
837 return indLanguageCode;
838 else if (sLanguage == "it") // Italian
839 return itaLanguageCode;
840 else if (sLanguage == "ja") // Japanese
841 return janLanguageCode;
842 else if (sLanguage == "kn") // Kannada
843 return kanLanguageCode;
844 else if (sLanguage == "ks") // Kashmiri
845 return kshLanguageCode;
846 else if (sLanguage == "kh") // Khmer
847 return khmLanguageCode;
848 else if (sLanguage == "kok") // Konkani
849 return kokLanguageCode;
850 else if (sLanguage == "ko") // Korean
851 return korLanguageCode;
852 else if (sLanguage == "ml") // Malayalam - Reformed (should there be some bcp47 tag for Traditional Malayalam)
853 return mlrLanguageCode;
854 else if (sLanguage == "mr") // Marathi
855 return marLanguageCode;
856 else if (sLanguage == "mt") // Maltese
857 return mtsLanguageCode;
858 else if (sLanguage == "mni") // Manipuri
859 return mniLanguageCode;
860 else if (sLanguage == "mn") // Mongolian
861 return mngLanguageCode;
862 else if (sLanguage == "ne") // Nepali
863 return nepLanguageCode;
864 else if (sLanguage == "or") // Oriya
865 return oriLanguageCode;
866 else if (sLanguage == "pl") // Polish
867 return plkLanguageCode;
868 else if (sLanguage == "po") // Portuguese
869 return ptgLanguageCode;
870 else if (sLanguage == "ps") // Pashto
871 return pasLanguageCode;
872 else if (sLanguage == "ro") // Romanian
873 return romLanguageCode;
874 else if (sLanguage == "ru") // Russian
875 return rusLanguageCode;
876 else if (sLanguage == "sa") // Sanskrit
877 return sanLanguageCode;
878 else if (sLanguage == "si") // Sinhalese
879 return snhLanguageCode;
880 else if (sLanguage == "sk") // Slovak
881 return skyLanguageCode;
882 else if (sLanguage == "sd") // Sindhi
883 return sndLanguageCode;
884 else if (sLanguage == "sl") // Slovenian
885 return slvLanguageCode;
886 else if (sLanguage == "es") // Spanish
887 return espLanguageCode;
888 else if (sLanguage == "sq") // Albanian
889 return sqiLanguageCode;
890 else if (sLanguage == "sr") // Serbian
891 return srbLanguageCode;
892 else if (sLanguage == "sv") // Swedish
893 return sveLanguageCode;
894 else if (sLanguage == "syr") // Syriac
895 return syrLanguageCode;
896 else if (sLanguage == "ta") // Tamil
897 return tamLanguageCode;
898 else if (sLanguage == "te") // Telugu
899 return telLanguageCode;
900 else if (sLanguage == "th") // Thai
901 return thaLanguageCode;
902 else if (sLanguage == "tu") // Turkish
903 return trkLanguageCode;
904 else if (sLanguage == "ur") // Urdu
905 return urdLanguageCode;
906 else if (sLanguage == "yi") // Yiddish
907 return jiiLanguageCode;
908 else if (sLanguage == "zh") // Chinese
910 OUString sScript = aLangTag.getScript();
911 if (sScript.isEmpty())
913 if (MsLangId::isTraditionalChinese(eLangCode))
914 sScript = "Hant";
915 else
916 sScript = "Hans";
918 if (sScript == "Latn")
919 return zhpLanguageCode;
920 else if (sScript == "Hans")
921 return zhsLanguageCode;
922 else if (sScript == "Hant")
923 return zhtLanguageCode;
926 //if there are new ones, please reexamine the mapping list for the new ones
927 BOOST_STATIC_ASSERT(languageCodeCount == 72);
928 return nullLanguageCode;
932 //See https://bugs.freedesktop.org/show_bug.cgi?id=31016
933 #define ARABIC_BANDAID
935 bool IcuLayoutEngine::layout(ServerFontLayout& rLayout, ImplLayoutArgs& rArgs)
937 le_int32 nLayoutFlags = 0;
938 #if (U_ICU_VERSION_MAJOR_NUM > 4)
939 if (rArgs.mnFlags & SAL_LAYOUT_KERNING_PAIRS)
940 nLayoutFlags |= LayoutEngine::kTypoFlagKern;
941 if (rArgs.mnFlags & SAL_LAYOUT_ENABLE_LIGATURES)
942 nLayoutFlags |= LayoutEngine::kTypoFlagLiga;
943 #else
944 if (rArgs.mnFlags & SAL_LAYOUT_KERNING_PAIRS)
945 nLayoutFlags |= 0x01;
946 if (rArgs.mnFlags & SAL_LAYOUT_ENABLE_LIGATURES)
947 nLayoutFlags |= 0x10;
948 #endif
950 LEUnicode* pIcuChars;
951 if( sizeof(LEUnicode) == sizeof(*rArgs.mpStr) )
952 pIcuChars = (LEUnicode*)rArgs.mpStr;
953 else
955 // this conversion will only be needed when either
956 // ICU's or OOo's unicodes stop being unsigned shorts
957 // TODO: watch out for surrogates!
958 pIcuChars = (LEUnicode*)alloca( rArgs.mnLength * sizeof(LEUnicode) );
959 for( xub_StrLen ic = 0; ic < rArgs.mnLength; ++ic )
960 pIcuChars[ic] = static_cast<LEUnicode>( rArgs.mpStr[ic] );
963 // allocate temporary arrays, note: round to even
964 int nGlyphCapacity = (3 * (rArgs.mnEndCharPos - rArgs.mnMinCharPos ) | 15) + 1;
966 rLayout.Reserve(nGlyphCapacity);
968 struct IcuPosition{ float fX, fY; };
969 const int nAllocSize = sizeof(LEGlyphID) + sizeof(le_int32) + sizeof(IcuPosition);
970 LEGlyphID* pIcuGlyphs = (LEGlyphID*)alloca( (nGlyphCapacity * nAllocSize) + sizeof(IcuPosition) );
971 le_int32* pCharIndices = (le_int32*)((char*)pIcuGlyphs + nGlyphCapacity * sizeof(LEGlyphID) );
972 IcuPosition* pGlyphPositions = (IcuPosition*)((char*)pCharIndices + nGlyphCapacity * sizeof(le_int32) );
974 ServerFont& rFont = rLayout.GetServerFont();
976 UErrorCode rcI18n = U_ZERO_ERROR;
977 LEErrorCode rcIcu = LE_NO_ERROR;
978 Point aNewPos( 0, 0 );
979 for( int nGlyphCount = 0;; )
981 int nMinRunPos, nEndRunPos;
982 bool bRightToLeft;
983 if( !rArgs.GetNextRun( &nMinRunPos, &nEndRunPos, &bRightToLeft ) )
984 break;
986 // find matching script
987 // TODO: split up bidi run into script runs
988 le_int32 eScriptCode = -1;
989 for( int i = nMinRunPos; i < nEndRunPos; ++i )
991 le_int32 eNextScriptCode = uscript_getScript( pIcuChars[i], &rcI18n );
992 if( (eNextScriptCode > USCRIPT_INHERITED) )
994 eScriptCode = eNextScriptCode;
995 if (eNextScriptCode != latnScriptCode)
996 break;
999 if( eScriptCode < 0 ) // TODO: handle errors better
1000 eScriptCode = latnScriptCode;
1002 LanguageCodes eLanguageCode = mapLanguageTypetoICU(rArgs.meLanguage);
1004 // get layout engine matching to this script and ligature/kerning combination
1005 // no engine change necessary if script is latin
1006 if ( !mpIcuLE ||
1007 ((eScriptCode != meScriptCode) && (eScriptCode > USCRIPT_INHERITED)) ||
1008 (mnLayoutFlags != nLayoutFlags) || (meLanguageCode != eLanguageCode) )
1010 // TODO: cache multiple layout engines when multiple scripts are used
1011 delete mpIcuLE;
1012 meLanguageCode = eLanguageCode;
1013 meScriptCode = eScriptCode;
1014 mnLayoutFlags = nLayoutFlags;
1015 mpIcuLE = LayoutEngine::layoutEngineFactory( &maIcuFont, eScriptCode, eLanguageCode, nLayoutFlags, rcIcu );
1016 if( LE_FAILURE(rcIcu) )
1018 delete mpIcuLE;
1019 mpIcuLE = NULL;
1023 // fall back to default layout if needed
1024 if( !mpIcuLE )
1025 break;
1027 // run ICU layout engine
1028 // TODO: get enough context, remove extra glyps below
1029 int nRawRunGlyphCount = mpIcuLE->layoutChars( pIcuChars,
1030 nMinRunPos, nEndRunPos - nMinRunPos, rArgs.mnLength,
1031 bRightToLeft, aNewPos.X(), aNewPos.Y(), rcIcu );
1032 if( LE_FAILURE(rcIcu) )
1033 return false;
1035 // import layout info from icu
1036 mpIcuLE->getGlyphs( pIcuGlyphs, rcIcu );
1037 mpIcuLE->getCharIndices( pCharIndices, rcIcu );
1038 mpIcuLE->getGlyphPositions( &pGlyphPositions->fX, rcIcu );
1039 if( LE_FAILURE(rcIcu) )
1040 return false;
1042 // layout bidi/script runs and export them to a ServerFontLayout
1043 // convert results to GlyphItems
1044 int nLastCharPos = -1;
1045 int nClusterMinPos = -1;
1046 int nClusterMaxPos = -1;
1047 bool bClusterStart = true;
1048 int nFilteredRunGlyphCount = 0;
1049 const IcuPosition* pPos = pGlyphPositions;
1050 for( int i = 0; i < nRawRunGlyphCount; ++i, ++pPos )
1052 LEGlyphID nGlyphIndex = pIcuGlyphs[i];
1053 // ignore glyphs which were marked or deleted by ICU
1054 if( (nGlyphIndex == ICU_MARKED_GLYPH)
1055 || (nGlyphIndex == ICU_DELETED_GLYPH) )
1056 continue;
1058 // adjust the relative char pos
1059 int nCharPos = pCharIndices[i];
1060 if( nCharPos >= 0 ) {
1061 nCharPos += nMinRunPos;
1062 // ICU seems to return bad pCharIndices
1063 // for some combinations of ICU+font+text
1064 // => better give up now than crash later
1065 if( nCharPos >= nEndRunPos )
1066 continue;
1069 // if needed request glyph fallback by updating LayoutArgs
1070 if( !nGlyphIndex )
1072 rLayout.setNeedFallback(rArgs, nCharPos, bRightToLeft);
1073 if( SAL_LAYOUT_FOR_FALLBACK & rArgs.mnFlags )
1074 continue;
1078 // apply vertical flags, etc.
1079 bool bDiacritic = false;
1080 if( nCharPos >= 0 )
1082 sal_UCS4 aChar = rArgs.mpStr[ nCharPos ];
1083 nGlyphIndex = rFont.FixupGlyphIndex( nGlyphIndex, aChar );
1085 // i#99367# HACK: try to detect all diacritics
1086 if( aChar>=0x0300 && aChar<0x2100 )
1087 bDiacritic = IsDiacritic( aChar );
1090 // get glyph position and its metrics
1091 aNewPos = Point( (int)(pPos->fX+0.5), (int)(pPos->fY+0.5) );
1092 const GlyphMetric& rGM = rFont.GetGlyphMetric( nGlyphIndex );
1093 int nGlyphWidth = rGM.GetCharWidth();
1094 int nNewWidth = nGlyphWidth;
1095 if( nGlyphWidth <= 0 )
1096 bDiacritic |= true;
1097 // i#99367# force all diacritics to zero width
1098 // TODO: we need mnOrigWidth/mnLogicWidth/mnNewWidth
1099 else if( bDiacritic )
1100 nGlyphWidth = nNewWidth = 0;
1101 else
1103 // Hack, find next +ve width glyph and calculate current
1104 // glyph width by substracting the two posituons
1105 const IcuPosition* pNextPos = pPos+1;
1106 for ( int j = i + 1; j <= nRawRunGlyphCount; ++j, ++pNextPos )
1108 if ( j == nRawRunGlyphCount )
1110 nNewWidth = static_cast<int>(pNextPos->fX - pPos->fX);
1111 break;
1114 LEGlyphID nNextGlyphIndex = pIcuGlyphs[j];
1115 if( (nNextGlyphIndex == ICU_MARKED_GLYPH)
1116 || (nNextGlyphIndex == ICU_DELETED_GLYPH) )
1117 continue;
1119 const GlyphMetric& rNextGM = rFont.GetGlyphMetric( nNextGlyphIndex );
1120 int nNextGlyphWidth = rNextGM.GetCharWidth();
1121 if ( nNextGlyphWidth > 0 )
1123 nNewWidth = static_cast<int>(pNextPos->fX - pPos->fX);
1124 break;
1129 // heuristic to detect glyph clusters
1130 bool bInCluster = true;
1131 if( nLastCharPos == -1 )
1133 nClusterMinPos = nClusterMaxPos = nCharPos;
1134 bInCluster = false;
1136 else if( !bRightToLeft )
1138 // left-to-right case
1139 if( nClusterMinPos > nCharPos )
1140 nClusterMinPos = nCharPos; // extend cluster
1141 else if( nCharPos <= nClusterMaxPos )
1142 /*NOTHING*/; // inside cluster
1143 else if( bDiacritic )
1144 nClusterMaxPos = nCharPos; // add diacritic to cluster
1145 else {
1146 nClusterMinPos = nClusterMaxPos = nCharPos; // new cluster
1147 bInCluster = false;
1150 else
1152 // right-to-left case
1153 if( nClusterMaxPos < nCharPos )
1154 nClusterMaxPos = nCharPos; // extend cluster
1155 else if( nCharPos >= nClusterMinPos )
1156 /*NOTHING*/; // inside cluster
1157 else if( bDiacritic )
1159 nClusterMinPos = nCharPos; // ICU often has [diacritic* baseglyph*]
1160 if( bClusterStart ) {
1161 nClusterMaxPos = nCharPos;
1162 bInCluster = false;
1165 else
1167 nClusterMinPos = nClusterMaxPos = nCharPos; // new cluster
1168 bInCluster = !bClusterStart;
1172 long nGlyphFlags = 0;
1173 if( bInCluster )
1174 nGlyphFlags |= GlyphItem::IS_IN_CLUSTER;
1175 if( bRightToLeft )
1176 nGlyphFlags |= GlyphItem::IS_RTL_GLYPH;
1177 if( bDiacritic )
1178 nGlyphFlags |= GlyphItem::IS_DIACRITIC;
1180 // add resulting glyph item to layout
1181 GlyphItem aGI( nCharPos, nGlyphIndex, aNewPos, nGlyphFlags, nGlyphWidth );
1182 #ifdef ARABIC_BANDAID
1183 aGI.mnNewWidth = nNewWidth;
1184 #endif
1186 rLayout.AppendGlyph( aGI );
1187 ++nFilteredRunGlyphCount;
1188 nLastCharPos = nCharPos;
1189 bClusterStart = !aGI.IsDiacritic(); // TODO: only needed in RTL-codepath
1191 aNewPos = Point( (int)(pPos->fX+0.5), (int)(pPos->fY+0.5) );
1192 nGlyphCount += nFilteredRunGlyphCount;
1195 // sort glyphs in visual order
1196 // and then in logical order (e.g. diacritics after cluster start)
1197 rLayout.SortGlyphItems();
1199 // determine need for kashida justification
1200 if( (rArgs.mpDXArray || rArgs.mnLayoutWidth)
1201 && ((meScriptCode == arabScriptCode) || (meScriptCode == syrcScriptCode)) )
1202 rArgs.mnFlags |= SAL_LAYOUT_KASHIDA_JUSTIFICATON;
1204 return true;
1207 // =======================================================================
1209 ServerFontLayoutEngine* ServerFont::GetLayoutEngine(bool bUseHarfBuzz)
1211 // find best layout engine for font, platform, script and language
1212 if (!mpLayoutEngine) {
1213 if (bUseHarfBuzz)
1214 mpLayoutEngine = new HbLayoutEngine(*this);
1215 else
1216 mpLayoutEngine = new IcuLayoutEngine(*this);
1218 return mpLayoutEngine;
1221 // =======================================================================
1223 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */