1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
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>
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>
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
);
69 bool bRet
= pLE
? pLE
->layout(*this, rArgs
) : false;
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
,
103 using namespace ::com::sun::star
;
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
119 OUString
aRun(rArgs
.mpStr
);
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
)
138 FT_Face aFace
= pFont
->GetFtFace();
139 const char* pName
= FT_Get_Postscript_Name(aFace
);
143 s
<< pFont
->GetFontFileName();
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
)
154 pTagName
[0] = (char)(nTableTag
>> 24);
155 pTagName
[1] = (char)(nTableTag
>> 16);
156 pTagName
[2] = (char)(nTableTag
>> 8);
157 pTagName
[3] = (char)(nTableTag
);
160 ServerFont
* pFont
= (ServerFont
*) pUserData
;
162 SAL_INFO("vcl.harfbuzz.layout", "getFontTable(" << pFont
<< ", " << pTagName
<< ")");
165 const unsigned char* pBuffer
= pFont
->GetTable(pTagName
, &nLength
);
167 hb_blob_t
* pBlob
= NULL
;
169 pBlob
= hb_blob_create((const char*) pBuffer
, nLength
, HB_MEMORY_MODE_READONLY
, (void*) pBuffer
, NULL
);
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
,
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
,
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*/,
198 // XXX: vertical metrics
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*/,
207 // the horizontal origin is always (0, 0)
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*/,
216 // XXX: vertical origin
220 static hb_position_t
getGlyphKerningH(hb_font_t
* /*font*/, void* pFontData
,
221 hb_codepoint_t nGlyphIndex1
, hb_codepoint_t nGlyphIndex2
,
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
232 ServerFont
* pFont
= (ServerFont
*) pFontData
;
233 FT_Face aFace
= pFont
->GetFtFace();
235 SAL_INFO("vcl.harfbuzz.layout", "getGlyphKerningH(" << pFont
<< ", " << nGlyphIndex1
<< ", " << nGlyphIndex2
<< ")");
241 error
= FT_Get_Kerning(aFace
, nGlyphIndex1
, nGlyphIndex2
, FT_KERNING_DEFAULT
, &kerning
);
250 static hb_position_t
getGlyphKerningV(hb_font_t
* /*font*/, void* /*pFontData*/,
251 hb_codepoint_t
/*nGlyphIndex1*/, hb_codepoint_t
/*nGlyphIndex2*/,
254 // XXX vertical kerning
258 static hb_bool_t
getGlyphExtents(hb_font_t
* /*font*/, void* pFontData
,
259 hb_codepoint_t nGlyphIndex
,
260 hb_glyph_extents_t
* pExtents
,
263 ServerFont
* pFont
= (ServerFont
*) pFontData
;
264 FT_Face aFace
= pFont
->GetFtFace();
266 SAL_INFO("vcl.harfbuzz.layout", "getGlyphExtents(" << pFont
<< ", " << nGlyphIndex
<< ")");
269 error
= FT_Load_Glyph(aFace
, nGlyphIndex
, FT_LOAD_DEFAULT
);
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
;
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
,
287 ServerFont
* pFont
= (ServerFont
*) pFontData
;
288 FT_Face aFace
= pFont
->GetFtFace();
290 SAL_INFO("vcl.harfbuzz.layout", "getGlyphContourPoint(" << pFont
<< ", " << nGlyphIndex
<< ", " << nPointIndex
<< ")");
293 error
= FT_Load_Glyph(aFace
, nGlyphIndex
, FT_LOAD_DEFAULT
);
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
;
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
);
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*/,
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
);
343 class HbLayoutEngine
: public ServerFontLayoutEngine
346 UScriptCode meScriptCode
;
351 HbLayoutEngine(ServerFont
&);
352 virtual ~HbLayoutEngine();
354 virtual bool layout(ServerFontLayout
&, ImplLayoutArgs
&);
357 HbLayoutEngine::HbLayoutEngine(ServerFont
& rServerFont
)
358 : meScriptCode(USCRIPT_INVALID_CODE
),
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);
400 int nMinRunPos
, nEndRunPos
;
402 if (!rArgs
.GetNextRun(&nMinRunPos
, &nEndRunPos
, &bRightToLeft
))
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
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
)
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
453 rLayout
.setNeedFallback(rArgs
, nCharPos
, bRightToLeft
);
454 if (SAL_LAYOUT_FOR_FALLBACK
& rArgs
.mnFlags
)
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.
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
)
471 long nGlyphFlags
= 0;
473 nGlyphFlags
|= GlyphItem::IS_RTL_GLYPH
;
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
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
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)
507 // the font lacks GDEF table
508 if (pHbPositions
[i
].x_advance
== 0)
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)
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
;
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
561 ServerFont
& mrServerFont
;
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
593 pTagName
[0] = (char)(nICUTableTag
>> 24);
594 pTagName
[1] = (char)(nICUTableTag
>> 16);
595 pTagName
[2] = (char)(nICUTableTag
>> 8);
596 pTagName
[3] = (char)(nICUTableTag
);
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
);
605 "font( h=" << mrServerFont
.GetFontSelData().mnHeight
<< ", \""
606 << mrServerFont
.GetFontFileName()->getStr() << "\" )");
610 const void* IcuFontFromServerFont::getFontTable( LETag nICUTableTag
) const
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
;
632 // -----------------------------------------------------------------------
634 float IcuFontFromServerFont::getYPixelsPerEm() const
636 float fY
= mrServerFont
.GetFontSelData().mnHeight
;
640 // -----------------------------------------------------------------------
642 float IcuFontFromServerFont::getScaleFactorX() const
647 // -----------------------------------------------------------------------
649 float IcuFontFromServerFont::getScaleFactorY() const
654 // -----------------------------------------------------------------------
656 LEGlyphID
IcuFontFromServerFont::mapCharToGlyph( LEUnicode32 ch
) const
658 LEGlyphID nGlyphIndex
= mrServerFont
.GetRawGlyphIndex( ch
);
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; │
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;
683 // -----------------------------------------------------------------------
685 le_int32
IcuFontFromServerFont::getDescent() const
687 const FT_Size_Metrics
& rMetrics
= mrServerFont
.GetMetricsFT();
688 le_int32 nDescent
= (-rMetrics
.descender
+ 32) >> 6;
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;
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
714 const GlyphMetric
& rGM
= mrServerFont
.GetGlyphMetric( nGlyphIndex
);
715 advance
.fX
= rGM
.GetCharWidth();
721 // -----------------------------------------------------------------------
723 le_bool
IcuFontFromServerFont::getGlyphPoint( LEGlyphID
,
724 le_int32 pointNumber
, LEPoint
& ) const
726 //TODO: replace dummy implementation
727 SAL_INFO("vcl", "getGlyphPoint(" << pointNumber
<< ")");
731 // =======================================================================
733 class IcuLayoutEngine
: public ServerFontLayoutEngine
736 IcuFontFromServerFont maIcuFont
;
738 LanguageCodes meLanguageCode
;
739 le_int32 meScriptCode
;
740 le_int32 mnLayoutFlags
;
741 LayoutEngine
* mpIcuLE
;
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
),
760 // -----------------------------------------------------------------------
762 IcuLayoutEngine::~IcuLayoutEngine()
767 // -----------------------------------------------------------------------
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
))
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
;
944 if (rArgs
.mnFlags
& SAL_LAYOUT_KERNING_PAIRS
)
945 nLayoutFlags
|= 0x01;
946 if (rArgs
.mnFlags
& SAL_LAYOUT_ENABLE_LIGATURES
)
947 nLayoutFlags
|= 0x10;
950 LEUnicode
* pIcuChars
;
951 if( sizeof(LEUnicode
) == sizeof(*rArgs
.mpStr
) )
952 pIcuChars
= (LEUnicode
*)rArgs
.mpStr
;
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
;
983 if( !rArgs
.GetNextRun( &nMinRunPos
, &nEndRunPos
, &bRightToLeft
) )
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
)
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
1007 ((eScriptCode
!= meScriptCode
) && (eScriptCode
> USCRIPT_INHERITED
)) ||
1008 (mnLayoutFlags
!= nLayoutFlags
) || (meLanguageCode
!= eLanguageCode
) )
1010 // TODO: cache multiple layout engines when multiple scripts are used
1012 meLanguageCode
= eLanguageCode
;
1013 meScriptCode
= eScriptCode
;
1014 mnLayoutFlags
= nLayoutFlags
;
1015 mpIcuLE
= LayoutEngine::layoutEngineFactory( &maIcuFont
, eScriptCode
, eLanguageCode
, nLayoutFlags
, rcIcu
);
1016 if( LE_FAILURE(rcIcu
) )
1023 // fall back to default layout if needed
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
) )
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
) )
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
) )
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
)
1069 // if needed request glyph fallback by updating LayoutArgs
1072 rLayout
.setNeedFallback(rArgs
, nCharPos
, bRightToLeft
);
1073 if( SAL_LAYOUT_FOR_FALLBACK
& rArgs
.mnFlags
)
1078 // apply vertical flags, etc.
1079 bool bDiacritic
= false;
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 )
1097 // i#99367# force all diacritics to zero width
1098 // TODO: we need mnOrigWidth/mnLogicWidth/mnNewWidth
1099 else if( bDiacritic
)
1100 nGlyphWidth
= nNewWidth
= 0;
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
);
1114 LEGlyphID nNextGlyphIndex
= pIcuGlyphs
[j
];
1115 if( (nNextGlyphIndex
== ICU_MARKED_GLYPH
)
1116 || (nNextGlyphIndex
== ICU_DELETED_GLYPH
) )
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
);
1129 // heuristic to detect glyph clusters
1130 bool bInCluster
= true;
1131 if( nLastCharPos
== -1 )
1133 nClusterMinPos
= nClusterMaxPos
= nCharPos
;
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
1146 nClusterMinPos
= nClusterMaxPos
= nCharPos
; // new cluster
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
;
1167 nClusterMinPos
= nClusterMaxPos
= nCharPos
; // new cluster
1168 bInCluster
= !bClusterStart
;
1172 long nGlyphFlags
= 0;
1174 nGlyphFlags
|= GlyphItem::IS_IN_CLUSTER
;
1176 nGlyphFlags
|= GlyphItem::IS_RTL_GLYPH
;
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
;
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
;
1207 // =======================================================================
1209 ServerFontLayoutEngine
* ServerFont::GetLayoutEngine(bool bUseHarfBuzz
)
1211 // find best layout engine for font, platform, script and language
1212 if (!mpLayoutEngine
) {
1214 mpLayoutEngine
= new HbLayoutEngine(*this);
1216 mpLayoutEngine
= new IcuLayoutEngine(*this);
1218 return mpLayoutEngine
;
1221 // =======================================================================
1223 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */