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>
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>
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
,
83 using namespace ::com::sun::star
;
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
93 OUString
aRun(rArgs
.mpStr
);
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
)
110 FT_Face aFace
= pFont
->GetFtFace();
111 const char* pName
= FT_Get_Postscript_Name(aFace
);
115 s
<< pFont
->GetFontFileName();
120 static hb_blob_t
*getFontTable(hb_face_t
* /*face*/, hb_tag_t nTableTag
, void* pUserData
)
123 pTagName
[0] = (char)(nTableTag
>> 24);
124 pTagName
[1] = (char)(nTableTag
>> 16);
125 pTagName
[2] = (char)(nTableTag
>> 8);
126 pTagName
[3] = (char)(nTableTag
);
129 ServerFont
* pFont
= static_cast<ServerFont
*>(pUserData
);
131 SAL_INFO("vcl.harfbuzz", "getFontTable(" << pFont
<< ", " << pTagName
<< ")");
134 const unsigned char* pBuffer
= pFont
->GetTable(pTagName
, &nLength
);
136 hb_blob_t
* pBlob
= NULL
;
138 pBlob
= hb_blob_create(reinterpret_cast<const char*>(pBuffer
), nLength
, HB_MEMORY_MODE_READONLY
, (void*) pBuffer
, NULL
);
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
,
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
,
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*/,
167 // XXX: vertical metrics
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*/,
176 // the horizontal origin is always (0, 0)
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*/,
185 // XXX: vertical origin
189 static hb_position_t
getGlyphKerningH(hb_font_t
* /*font*/, void* pFontData
,
190 hb_codepoint_t nGlyphIndex1
, hb_codepoint_t nGlyphIndex2
,
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
<< ")");
204 error
= FT_Get_Kerning(aFace
, nGlyphIndex1
, nGlyphIndex2
, FT_KERNING_DEFAULT
, &kerning
);
213 static hb_position_t
getGlyphKerningV(hb_font_t
* /*font*/, void* /*pFontData*/,
214 hb_codepoint_t
/*nGlyphIndex1*/, hb_codepoint_t
/*nGlyphIndex2*/,
217 // XXX vertical kerning
221 static hb_bool_t
getGlyphExtents(hb_font_t
* /*font*/, void* pFontData
,
222 hb_codepoint_t nGlyphIndex
,
223 hb_glyph_extents_t
* pExtents
,
226 ServerFont
* pFont
= static_cast<ServerFont
*>(pFontData
);
227 FT_Face aFace
= pFont
->GetFtFace();
229 SAL_INFO("vcl.harfbuzz", "getGlyphExtents(" << pFont
<< ", " << nGlyphIndex
<< ")");
232 error
= FT_Load_Glyph(aFace
, nGlyphIndex
, FT_LOAD_DEFAULT
);
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
;
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
,
250 ServerFont
* pFont
= static_cast<ServerFont
*>(pFontData
);
251 FT_Face aFace
= pFont
->GetFtFace();
253 SAL_INFO("vcl.harfbuzz", "getGlyphContourPoint(" << pFont
<< ", " << nGlyphIndex
<< ", " << nPointIndex
<< ")");
256 error
= FT_Load_Glyph(aFace
, nGlyphIndex
, FT_LOAD_DEFAULT
);
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
;
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
);
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*/,
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
);
306 class HbLayoutEngine
: public ServerFontLayoutEngine
309 hb_script_t maHbScript
;
314 HbLayoutEngine(ServerFont
&);
315 virtual ~HbLayoutEngine();
317 virtual bool Layout(ServerFontLayout
&, ImplLayoutArgs
&) SAL_OVERRIDE
;
320 HbLayoutEngine::HbLayoutEngine(ServerFont
& rServerFont
)
321 : maHbScript(HB_SCRIPT_INVALID
),
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
);
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
;
358 Run(int32_t nStart_
, int32_t nEnd_
, UScriptCode nCode_
)
359 : nStart(nStart_
), nEnd(nEnd_
), nCode(nCode_
)
363 class TextLayoutCache
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
),
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!
417 pNewScriptRun
.reset(new vcl::TextLayoutCache(rArgs
.mpStr
, rArgs
.mnEndCharPos
));
418 pTextLayout
= pNewScriptRun
.get();
421 Point
aCurrPos(0, 0);
424 int nBidiMinRunPos
, nBidiEndRunPos
;
426 if (!rArgs
.GetNextRun(&nBidiMinRunPos
, &nBidiEndRunPos
, &bRightToLeft
))
429 // Find script subruns.
430 int nCurrentPos
= nBidiMinRunPos
;
431 HbScriptRuns aScriptSubRuns
;
433 for (; k
< pTextLayout
->runs
.size(); ++k
)
435 vcl::Run
const& rRun(pTextLayout
->runs
[k
]);
436 if (rRun
.nStart
<= nCurrentPos
&& nCurrentPos
< rRun
.nEnd
)
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
;
453 // RTL subruns should be reversed to ensure that final glyph order is
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
;
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
503 rLayout
.SetNeedFallback(rArgs
, nCharPos
, bRightToLeft
);
504 if (SalLayoutFlags::ForFallback
& rArgs
.mnFlags
)
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.
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
)
521 long nGlyphFlags
= 0;
523 nGlyphFlags
|= GlyphItem::IS_RTL_GLYPH
;
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
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
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)
557 // the font lacks GDEF table
558 if (pHbPositions
[i
].x_advance
== 0)
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)
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
;
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: */