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 <boost/static_assert.hpp>
27 #include <i18nlangtag/mslangid.hxx>
29 #include <vcl/svapp.hxx>
30 #include <vcl/unohelp.hxx>
32 #include <sal/alloca.h>
33 #include <rtl/instance.hxx>
38 #include <com/sun/star/i18n/CharacterIteratorMode.hpp>
40 // layout implementation for ServerFont
41 ServerFontLayout::ServerFontLayout( ServerFont
& rFont
)
42 : mrServerFont( rFont
)
46 void ServerFontLayout::DrawText( SalGraphics
& rSalGraphics
) const
48 rSalGraphics
.DrawServerFontLayout( *this );
51 bool ServerFontLayout::LayoutText( ImplLayoutArgs
& rArgs
)
53 return mrServerFont
.GetLayoutEngine()->layout(*this, rArgs
);
56 void ServerFontLayout::AdjustLayout( ImplLayoutArgs
& rArgs
)
58 GenericSalLayout::AdjustLayout( rArgs
);
60 // apply asian kerning if the glyphs are not already formatted
61 if( (rArgs
.mnFlags
& SAL_LAYOUT_KERNING_ASIAN
)
62 && !(rArgs
.mnFlags
& SAL_LAYOUT_VERTICAL
) )
63 if( (rArgs
.mpDXArray
!= NULL
) || (rArgs
.mnLayoutWidth
!= 0) )
64 ApplyAsianKerning( rArgs
.mpStr
, rArgs
.mnLength
);
66 // insert kashidas where requested by the formatting array
67 if( (rArgs
.mnFlags
& SAL_LAYOUT_KASHIDA_JUSTIFICATON
) && rArgs
.mpDXArray
)
69 int nKashidaIndex
= mrServerFont
.GetGlyphIndex( 0x0640 );
70 if( nKashidaIndex
!= 0 )
72 const GlyphMetric
& rGM
= mrServerFont
.GetGlyphMetric( nKashidaIndex
);
73 KashidaJustify( nKashidaIndex
, rGM
.GetCharWidth() );
74 // TODO: kashida-GSUB/GPOS
79 void ServerFontLayout::setNeedFallback(ImplLayoutArgs
& rArgs
, sal_Int32 nCharPos
,
85 using namespace ::com::sun::star
;
88 mxBreak
= vcl::unohelper::CreateBreakIterator();
90 lang::Locale
aLocale(rArgs
.maLanguageTag
.getLocale());
92 //if position nCharPos is missing in the font, grab the entire grapheme and
93 //mark all glyphs as missing so the whole thing is rendered with the same
95 OUString
aRun(rArgs
.mpStr
);
97 sal_Int32 nGraphemeStartPos
=
98 mxBreak
->previousCharacters(aRun
, nCharPos
+1, aLocale
,
99 i18n::CharacterIteratorMode::SKIPCELL
, 1, nDone
);
100 sal_Int32 nGraphemeEndPos
=
101 mxBreak
->nextCharacters(aRun
, nCharPos
, aLocale
,
102 i18n::CharacterIteratorMode::SKIPCELL
, 1, nDone
);
104 rArgs
.NeedFallback(nGraphemeStartPos
, nGraphemeEndPos
, bRightToLeft
);
107 std::ostream
&operator <<(std::ostream
& s
, ServerFont
* pFont
)
112 FT_Face aFace
= pFont
->GetFtFace();
113 const char* pName
= FT_Get_Postscript_Name(aFace
);
117 s
<< pFont
->GetFontFileName();
122 static hb_blob_t
*getFontTable(hb_face_t
* /*face*/, hb_tag_t nTableTag
, void* pUserData
)
125 pTagName
[0] = (char)(nTableTag
>> 24);
126 pTagName
[1] = (char)(nTableTag
>> 16);
127 pTagName
[2] = (char)(nTableTag
>> 8);
128 pTagName
[3] = (char)(nTableTag
);
131 ServerFont
* pFont
= (ServerFont
*) pUserData
;
133 SAL_INFO("vcl.harfbuzz", "getFontTable(" << pFont
<< ", " << pTagName
<< ")");
136 const unsigned char* pBuffer
= pFont
->GetTable(pTagName
, &nLength
);
138 hb_blob_t
* pBlob
= NULL
;
140 pBlob
= hb_blob_create((const char*) pBuffer
, nLength
, HB_MEMORY_MODE_READONLY
, (void*) pBuffer
, NULL
);
145 static hb_bool_t
getFontGlyph(hb_font_t
* /*font*/, void* pFontData
,
146 hb_codepoint_t ch
, hb_codepoint_t vs
,
147 hb_codepoint_t
* nGlyphIndex
,
150 ServerFont
* pFont
= (ServerFont
*) pFontData
;
151 *nGlyphIndex
= pFont
->GetRawGlyphIndex(ch
, vs
);
153 return *nGlyphIndex
!= 0;
156 static hb_position_t
getGlyphAdvanceH(hb_font_t
* /*font*/, void* pFontData
,
157 hb_codepoint_t nGlyphIndex
,
160 ServerFont
* pFont
= (ServerFont
*) pFontData
;
161 const GlyphMetric
& rGM
= pFont
->GetGlyphMetric(nGlyphIndex
);
162 return rGM
.GetCharWidth() << 6;
165 static hb_position_t
getGlyphAdvanceV(hb_font_t
* /*font*/, void* /*pFontData*/,
166 hb_codepoint_t
/*nGlyphIndex*/,
169 // XXX: vertical metrics
173 static hb_bool_t
getGlyphOriginH(hb_font_t
* /*font*/, void* /*pFontData*/,
174 hb_codepoint_t
/*nGlyphIndex*/,
175 hb_position_t
* /*x*/, hb_position_t
* /*y*/,
178 // the horizontal origin is always (0, 0)
182 static hb_bool_t
getGlyphOriginV(hb_font_t
* /*font*/, void* /*pFontData*/,
183 hb_codepoint_t
/*nGlyphIndex*/,
184 hb_position_t
* /*x*/, hb_position_t
* /*y*/,
187 // XXX: vertical origin
191 static hb_position_t
getGlyphKerningH(hb_font_t
* /*font*/, void* pFontData
,
192 hb_codepoint_t nGlyphIndex1
, hb_codepoint_t nGlyphIndex2
,
195 // This callback is for old style 'kern' table, GPOS kerning is handled by HarfBuzz directly
197 ServerFont
* pFont
= (ServerFont
*) pFontData
;
198 FT_Face aFace
= pFont
->GetFtFace();
200 SAL_INFO("vcl.harfbuzz", "getGlyphKerningH(" << pFont
<< ", " << nGlyphIndex1
<< ", " << nGlyphIndex2
<< ")");
206 error
= FT_Get_Kerning(aFace
, nGlyphIndex1
, nGlyphIndex2
, FT_KERNING_DEFAULT
, &kerning
);
215 static hb_position_t
getGlyphKerningV(hb_font_t
* /*font*/, void* /*pFontData*/,
216 hb_codepoint_t
/*nGlyphIndex1*/, hb_codepoint_t
/*nGlyphIndex2*/,
219 // XXX vertical kerning
223 static hb_bool_t
getGlyphExtents(hb_font_t
* /*font*/, void* pFontData
,
224 hb_codepoint_t nGlyphIndex
,
225 hb_glyph_extents_t
* pExtents
,
228 ServerFont
* pFont
= (ServerFont
*) pFontData
;
229 FT_Face aFace
= pFont
->GetFtFace();
231 SAL_INFO("vcl.harfbuzz", "getGlyphExtents(" << pFont
<< ", " << nGlyphIndex
<< ")");
234 error
= FT_Load_Glyph(aFace
, nGlyphIndex
, FT_LOAD_DEFAULT
);
237 pExtents
->x_bearing
= aFace
->glyph
->metrics
.horiBearingX
;
238 pExtents
->y_bearing
= aFace
->glyph
->metrics
.horiBearingY
;
239 pExtents
->width
= aFace
->glyph
->metrics
.width
;
240 pExtents
->height
= -aFace
->glyph
->metrics
.height
;
246 static hb_bool_t
getGlyphContourPoint(hb_font_t
* /*font*/, void* pFontData
,
247 hb_codepoint_t nGlyphIndex
, unsigned int nPointIndex
,
248 hb_position_t
*x
, hb_position_t
*y
,
252 ServerFont
* pFont
= (ServerFont
*) pFontData
;
253 FT_Face aFace
= pFont
->GetFtFace();
255 SAL_INFO("vcl.harfbuzz", "getGlyphContourPoint(" << pFont
<< ", " << nGlyphIndex
<< ", " << nPointIndex
<< ")");
258 error
= FT_Load_Glyph(aFace
, nGlyphIndex
, FT_LOAD_DEFAULT
);
261 if (aFace
->glyph
->format
== FT_GLYPH_FORMAT_OUTLINE
)
263 if (nPointIndex
< (unsigned int) aFace
->glyph
->outline
.n_points
)
265 *x
= aFace
->glyph
->outline
.points
[nPointIndex
].x
;
266 *y
= aFace
->glyph
->outline
.points
[nPointIndex
].y
;
275 static hb_font_funcs_t
* getFontFuncs(void)
277 static hb_font_funcs_t
* funcs
= hb_font_funcs_create();
279 hb_font_funcs_set_glyph_func (funcs
, getFontGlyph
, NULL
, NULL
);
280 hb_font_funcs_set_glyph_h_advance_func (funcs
, getGlyphAdvanceH
, NULL
, NULL
);
281 hb_font_funcs_set_glyph_v_advance_func (funcs
, getGlyphAdvanceV
, NULL
, NULL
);
282 hb_font_funcs_set_glyph_h_origin_func (funcs
, getGlyphOriginH
, NULL
, NULL
);
283 hb_font_funcs_set_glyph_v_origin_func (funcs
, getGlyphOriginV
, NULL
, NULL
);
284 hb_font_funcs_set_glyph_h_kerning_func (funcs
, getGlyphKerningH
, NULL
, NULL
);
285 hb_font_funcs_set_glyph_v_kerning_func (funcs
, getGlyphKerningV
, NULL
, NULL
);
286 hb_font_funcs_set_glyph_extents_func (funcs
, getGlyphExtents
, NULL
, NULL
);
287 hb_font_funcs_set_glyph_contour_point_func (funcs
, getGlyphContourPoint
, NULL
, NULL
);
292 // Disabled Unicode compatibility decomposition, see fdo#66715
293 static unsigned int unicodeDecomposeCompatibility(hb_unicode_funcs_t
* /*ufuncs*/,
294 hb_codepoint_t
/*u*/,
295 hb_codepoint_t
* /*decomposed*/,
301 static hb_unicode_funcs_t
* getUnicodeFuncs(void)
303 static hb_unicode_funcs_t
* ufuncs
= hb_unicode_funcs_create(hb_icu_get_unicode_funcs());
304 hb_unicode_funcs_set_decompose_compatibility_func(ufuncs
, unicodeDecomposeCompatibility
, NULL
, NULL
);
308 class HbLayoutEngine
: public ServerFontLayoutEngine
311 hb_script_t maHbScript
;
316 HbLayoutEngine(ServerFont
&);
317 virtual ~HbLayoutEngine();
319 virtual bool layout(ServerFontLayout
&, ImplLayoutArgs
&) SAL_OVERRIDE
;
322 HbLayoutEngine::HbLayoutEngine(ServerFont
& rServerFont
)
323 : maHbScript(HB_SCRIPT_INVALID
),
327 FT_Face aFtFace
= rServerFont
.GetFtFace();
328 mnUnitsPerEM
= rServerFont
.GetEmUnits();
330 mpHbFace
= hb_face_create_for_tables(getFontTable
, &rServerFont
, NULL
);
331 hb_face_set_index(mpHbFace
, aFtFace
->face_index
);
332 hb_face_set_upem(mpHbFace
, mnUnitsPerEM
);
335 HbLayoutEngine::~HbLayoutEngine()
337 hb_face_destroy(mpHbFace
);
344 hb_script_t maScript
;
346 HbScriptRun(int32_t nMin
, int32_t nEnd
, UScriptCode aScript
)
347 : mnMin(nMin
), mnEnd(nEnd
),
348 maScript(hb_icu_script_to_script(aScript
))
352 typedef std::vector
<HbScriptRun
> HbScriptRuns
;
354 bool HbLayoutEngine::layout(ServerFontLayout
& rLayout
, ImplLayoutArgs
& rArgs
)
356 ServerFont
& rFont
= rLayout
.GetServerFont();
357 FT_Face aFtFace
= rFont
.GetFtFace();
359 SAL_INFO("vcl.harfbuzz", "layout(" << this << ",rArgs=" << rArgs
<< ")");
361 static hb_font_funcs_t
* pHbFontFuncs
= getFontFuncs();
363 hb_font_t
*pHbFont
= hb_font_create(mpHbFace
);
364 hb_font_set_funcs(pHbFont
, pHbFontFuncs
, &rFont
, NULL
);
365 hb_font_set_scale(pHbFont
,
366 ((uint64_t) aFtFace
->size
->metrics
.x_scale
* (uint64_t) mnUnitsPerEM
) >> 16,
367 ((uint64_t) aFtFace
->size
->metrics
.y_scale
* (uint64_t) mnUnitsPerEM
) >> 16);
368 hb_font_set_ppem(pHbFont
, aFtFace
->size
->metrics
.x_ppem
, aFtFace
->size
->metrics
.y_ppem
);
370 // allocate temporary arrays, note: round to even
371 int nGlyphCapacity
= (3 * (rArgs
.mnEndCharPos
- rArgs
.mnMinCharPos
) | 15) + 1;
373 rLayout
.Reserve(nGlyphCapacity
);
375 ScriptRun
aScriptRun(reinterpret_cast<const UChar
*>(rArgs
.mpStr
), rArgs
.mnLength
);
377 Point
aCurrPos(0, 0);
380 int nBidiMinRunPos
, nBidiEndRunPos
;
382 if (!rArgs
.GetNextRun(&nBidiMinRunPos
, &nBidiEndRunPos
, &bRightToLeft
))
385 // Find script subruns.
386 int nCurrentPos
= nBidiMinRunPos
;
387 HbScriptRuns aScriptSubRuns
;
388 while (aScriptRun
.next())
390 if (aScriptRun
.getScriptStart() <= nCurrentPos
&& aScriptRun
.getScriptEnd() > nCurrentPos
)
394 while (nCurrentPos
< nBidiEndRunPos
)
396 int32_t nMinRunPos
= nCurrentPos
;
397 int32_t nEndRunPos
= std::min(aScriptRun
.getScriptEnd(), nBidiEndRunPos
);
398 HbScriptRun
aRun(nMinRunPos
, nEndRunPos
, aScriptRun
.getScriptCode());
399 aScriptSubRuns
.push_back(aRun
);
401 nCurrentPos
= nEndRunPos
;
405 // RTL subruns should be reversed to ensure that final glyph order is
408 std::reverse(aScriptSubRuns
.begin(), aScriptSubRuns
.end());
412 for (HbScriptRuns::iterator it
= aScriptSubRuns
.begin(); it
!= aScriptSubRuns
.end(); ++it
)
414 int nMinRunPos
= it
->mnMin
;
415 int nEndRunPos
= it
->mnEnd
;
416 int nRunLen
= nEndRunPos
- nMinRunPos
;
417 maHbScript
= it
->maScript
;
419 OString sLanguage
= OUStringToOString(rArgs
.maLanguageTag
.getLanguage(), RTL_TEXTENCODING_UTF8
);
421 static hb_unicode_funcs_t
* pHbUnicodeFuncs
= getUnicodeFuncs();
423 int nHbFlags
= HB_BUFFER_FLAG_DEFAULT
;
425 nHbFlags
|= HB_BUFFER_FLAG_BOT
; /* Beginning-of-text */
426 if (nEndRunPos
== rArgs
.mnLength
)
427 nHbFlags
|= HB_BUFFER_FLAG_EOT
; /* End-of-text */
429 hb_buffer_t
*pHbBuffer
= hb_buffer_create();
430 hb_buffer_set_unicode_funcs(pHbBuffer
, pHbUnicodeFuncs
);
431 hb_buffer_set_direction(pHbBuffer
, bRightToLeft
? HB_DIRECTION_RTL
: HB_DIRECTION_LTR
);
432 hb_buffer_set_script(pHbBuffer
, maHbScript
);
433 hb_buffer_set_language(pHbBuffer
, hb_language_from_string(sLanguage
.getStr(), -1));
434 hb_buffer_set_flags(pHbBuffer
, (hb_buffer_flags_t
) nHbFlags
);
435 hb_buffer_add_utf16(pHbBuffer
, rArgs
.mpStr
, rArgs
.mnLength
, nMinRunPos
, nRunLen
);
436 hb_shape(pHbFont
, pHbBuffer
, NULL
, 0);
438 int nRunGlyphCount
= hb_buffer_get_length(pHbBuffer
);
439 hb_glyph_info_t
*pHbGlyphInfos
= hb_buffer_get_glyph_infos(pHbBuffer
, NULL
);
440 hb_glyph_position_t
*pHbPositions
= hb_buffer_get_glyph_positions(pHbBuffer
, NULL
);
442 for (int i
= 0; i
< nRunGlyphCount
; ++i
) {
443 int32_t nGlyphIndex
= pHbGlyphInfos
[i
].codepoint
;
444 int32_t nCharPos
= pHbGlyphInfos
[i
].cluster
;
446 // if needed request glyph fallback by updating LayoutArgs
449 rLayout
.setNeedFallback(rArgs
, nCharPos
, bRightToLeft
);
450 if (SAL_LAYOUT_FOR_FALLBACK
& rArgs
.mnFlags
)
454 // apply vertical flags and glyph substitution
455 // XXX: Use HB_DIRECTION_TTB above and apply whatever flags magic
456 // FixupGlyphIndex() is doing, minus the GSUB part.
459 sal_UCS4 aChar
= rArgs
.mpStr
[nCharPos
];
460 nGlyphIndex
= rFont
.FixupGlyphIndex(nGlyphIndex
, aChar
);
463 bool bInCluster
= false;
464 if (i
> 0 && pHbGlyphInfos
[i
].cluster
== pHbGlyphInfos
[i
- 1].cluster
)
467 long nGlyphFlags
= 0;
469 nGlyphFlags
|= GlyphItem::IS_RTL_GLYPH
;
472 nGlyphFlags
|= GlyphItem::IS_IN_CLUSTER
;
474 // The whole IS_DIACRITIC concept is a stupid hack that was
475 // introduced ages ago to work around the utter brokenness of the
476 // way justification adjustments are applied (the DXArray fiasco).
477 // Since it is such a stupid hack, there is no sane way to directly
478 // map to concepts of the "outside" world, so we do some rather
480 // * If the font has a GDEF table, we check for glyphs with mark
481 // glyph class which is sensible, except that some fonts
482 // (fdo#70968) assign mark class to spacing marks (which is wrong
483 // but usually harmless), so we try to sniff what HarfBuzz thinks
484 // about this glyph by checking if it gives it a zero advance
486 // * If the font has no GDEF table, we just check if the glyph has
487 // zero advance width, but this is stupid and can be wrong. A
488 // better way would to check the character's Unicode combining
489 // class, but unfortunately glyph gives combining marks the
490 // cluster value of its base character, so nCharPos will be
491 // pointing to the wrong character (but HarfBuzz might change
492 // this in the future).
493 bool bDiacritic
= false;
494 if (hb_ot_layout_has_glyph_classes(mpHbFace
))
496 // the font has GDEF table
497 bool bMark
= hb_ot_layout_get_glyph_class(mpHbFace
, nGlyphIndex
) == HB_OT_LAYOUT_GLYPH_CLASS_MARK
;
498 if (bMark
&& pHbPositions
[i
].x_advance
== 0)
503 // the font lacks GDEF table
504 if (pHbPositions
[i
].x_advance
== 0)
509 nGlyphFlags
|= GlyphItem::IS_DIACRITIC
;
511 int32_t nXOffset
= pHbPositions
[i
].x_offset
>> 6;
512 int32_t nYOffset
= pHbPositions
[i
].y_offset
>> 6;
513 int32_t nXAdvance
= pHbPositions
[i
].x_advance
>> 6;
514 int32_t nYAdvance
= pHbPositions
[i
].y_advance
>> 6;
516 Point aNewPos
= Point(aCurrPos
.X() + nXOffset
, -(aCurrPos
.Y() + nYOffset
));
517 const GlyphItem
aGI(nCharPos
, nGlyphIndex
, aNewPos
, nGlyphFlags
, nXAdvance
, nXOffset
);
518 rLayout
.AppendGlyph(aGI
);
520 aCurrPos
.X() += nXAdvance
;
521 aCurrPos
.Y() += nYAdvance
;
524 hb_buffer_destroy(pHbBuffer
);
528 hb_font_destroy(pHbFont
);
530 // sort glyphs in visual order
531 // and then in logical order (e.g. diacritics after cluster start)
533 rLayout
.SortGlyphItems();
535 // determine need for kashida justification
536 if((rArgs
.mpDXArray
|| rArgs
.mnLayoutWidth
)
537 && ((maHbScript
== HB_SCRIPT_ARABIC
) || (maHbScript
== HB_SCRIPT_SYRIAC
)))
538 rArgs
.mnFlags
|= SAL_LAYOUT_KASHIDA_JUSTIFICATON
;
543 ServerFontLayoutEngine
* ServerFont::GetLayoutEngine()
545 // find best layout engine for font, platform, script and language
546 if (!mpLayoutEngine
) {
547 mpLayoutEngine
= new HbLayoutEngine(*this);
549 return mpLayoutEngine
;
552 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */