Version 4.3.0.0.beta1, tag libreoffice-4.3.0.0.beta1
[LibreOffice.git] / vcl / generic / glyphs / gcach_layout.cxx
blobcbf3f9c159d548040ce5d2168418d8b692fceeab
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3 * This file is part of the LibreOffice project.
5 * This Source Code Form is subject to the terms of the Mozilla Public
6 * License, v. 2.0. If a copy of the MPL was not distributed with this
7 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
9 * This file incorporates work covered by the following license notice:
11 * Licensed to the Apache Software Foundation (ASF) under one or more
12 * contributor license agreements. See the NOTICE file distributed
13 * with this work for additional information regarding copyright
14 * ownership. The ASF licenses this file to you under the Apache
15 * License, Version 2.0 (the "License"); you may not use this file
16 * except in compliance with the License. You may obtain a copy of
17 * the License at http://www.apache.org/licenses/LICENSE-2.0 .
20 #include <gcach_ftyp.hxx>
21 #include <sallayout.hxx>
22 #include <salgdi.hxx>
23 #include <scrptrun.h>
25 #include <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>
35 #include <hb-icu.h>
36 #include <hb-ot.h>
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,
80 bool bRightToLeft)
82 if (nCharPos < 0)
83 return;
85 using namespace ::com::sun::star;
87 if (!mxBreak.is())
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
94 //font
95 OUString aRun(rArgs.mpStr);
96 sal_Int32 nDone;
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)
109 #ifndef SAL_LOG_INFO
110 (void) pFont;
111 #else
112 FT_Face aFace = pFont->GetFtFace();
113 const char* pName = FT_Get_Postscript_Name(aFace);
114 if (pName)
115 s << pName;
116 else
117 s << pFont->GetFontFileName();
118 #endif
119 return s;
122 static hb_blob_t *getFontTable(hb_face_t* /*face*/, hb_tag_t nTableTag, void* pUserData)
124 char pTagName[5];
125 pTagName[0] = (char)(nTableTag >> 24);
126 pTagName[1] = (char)(nTableTag >> 16);
127 pTagName[2] = (char)(nTableTag >> 8);
128 pTagName[3] = (char)(nTableTag);
129 pTagName[4] = 0;
131 ServerFont* pFont = (ServerFont*) pUserData;
133 SAL_INFO("vcl.harfbuzz", "getFontTable(" << pFont << ", " << pTagName << ")");
135 sal_uLong nLength;
136 const unsigned char* pBuffer = pFont->GetTable(pTagName, &nLength);
138 hb_blob_t* pBlob = NULL;
139 if (pBuffer != NULL)
140 pBlob = hb_blob_create((const char*) pBuffer, nLength, HB_MEMORY_MODE_READONLY, (void*) pBuffer, NULL);
142 return pBlob;
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,
148 void* /*pUserData*/)
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,
158 void* /*pUserData*/)
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*/,
167 void* /*pUserData*/)
169 // XXX: vertical metrics
170 return 0;
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*/,
176 void* /*pUserData*/)
178 // the horizontal origin is always (0, 0)
179 return true;
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*/,
185 void* /*pUserData*/)
187 // XXX: vertical origin
188 return true;
191 static hb_position_t getGlyphKerningH(hb_font_t* /*font*/, void* pFontData,
192 hb_codepoint_t nGlyphIndex1, hb_codepoint_t nGlyphIndex2,
193 void* /*pUserData*/)
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 << ")");
202 FT_Error error;
203 FT_Vector kerning;
204 hb_position_t ret;
206 error = FT_Get_Kerning(aFace, nGlyphIndex1, nGlyphIndex2, FT_KERNING_DEFAULT, &kerning);
207 if (error)
208 ret = 0;
209 else
210 ret = kerning.x;
212 return ret;
215 static hb_position_t getGlyphKerningV(hb_font_t* /*font*/, void* /*pFontData*/,
216 hb_codepoint_t /*nGlyphIndex1*/, hb_codepoint_t /*nGlyphIndex2*/,
217 void* /*pUserData*/)
219 // XXX vertical kerning
220 return 0;
223 static hb_bool_t getGlyphExtents(hb_font_t* /*font*/, void* pFontData,
224 hb_codepoint_t nGlyphIndex,
225 hb_glyph_extents_t* pExtents,
226 void* /*pUserData*/)
228 ServerFont* pFont = (ServerFont*) pFontData;
229 FT_Face aFace = pFont->GetFtFace();
231 SAL_INFO("vcl.harfbuzz", "getGlyphExtents(" << pFont << ", " << nGlyphIndex << ")");
233 FT_Error error;
234 error = FT_Load_Glyph(aFace, nGlyphIndex, FT_LOAD_DEFAULT);
235 if (!error)
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;
243 return !error;
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,
249 void* /*pUserData*/)
251 bool ret = false;
252 ServerFont* pFont = (ServerFont*) pFontData;
253 FT_Face aFace = pFont->GetFtFace();
255 SAL_INFO("vcl.harfbuzz", "getGlyphContourPoint(" << pFont << ", " << nGlyphIndex << ", " << nPointIndex << ")");
257 FT_Error error;
258 error = FT_Load_Glyph(aFace, nGlyphIndex, FT_LOAD_DEFAULT);
259 if (!error)
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;
267 ret = true;
272 return ret;
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);
289 return funcs;
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*/,
296 void* /*user_data*/)
298 return 0;
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);
305 return ufuncs;
308 class HbLayoutEngine : public ServerFontLayoutEngine
310 private:
311 hb_script_t maHbScript;
312 hb_face_t* mpHbFace;
313 int mnUnitsPerEM;
315 public:
316 HbLayoutEngine(ServerFont&);
317 virtual ~HbLayoutEngine();
319 virtual bool layout(ServerFontLayout&, ImplLayoutArgs&) SAL_OVERRIDE;
322 HbLayoutEngine::HbLayoutEngine(ServerFont& rServerFont)
323 : maHbScript(HB_SCRIPT_INVALID),
324 mpHbFace(NULL),
325 mnUnitsPerEM(0)
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);
340 struct HbScriptRun
342 int32_t mnMin;
343 int32_t mnEnd;
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);
378 while (true)
380 int nBidiMinRunPos, nBidiEndRunPos;
381 bool bRightToLeft;
382 if (!rArgs.GetNextRun(&nBidiMinRunPos, &nBidiEndRunPos, &bRightToLeft))
383 break;
385 // Find script subruns.
386 int nCurrentPos = nBidiMinRunPos;
387 HbScriptRuns aScriptSubRuns;
388 while (aScriptRun.next())
390 if (aScriptRun.getScriptStart() <= nCurrentPos && aScriptRun.getScriptEnd() > nCurrentPos)
391 break;
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;
402 aScriptRun.next();
405 // RTL subruns should be reversed to ensure that final glyph order is
406 // correct.
407 if (bRightToLeft)
408 std::reverse(aScriptSubRuns.begin(), aScriptSubRuns.end());
410 aScriptRun.reset();
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;
424 if (nMinRunPos == 0)
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
447 if (!nGlyphIndex)
449 rLayout.setNeedFallback(rArgs, nCharPos, bRightToLeft);
450 if (SAL_LAYOUT_FOR_FALLBACK & rArgs.mnFlags)
451 continue;
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.
457 if (nCharPos >= 0)
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)
465 bInCluster = true;
467 long nGlyphFlags = 0;
468 if (bRightToLeft)
469 nGlyphFlags |= GlyphItem::IS_RTL_GLYPH;
471 if (bInCluster)
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
479 // ugly hacks:
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
485 // width.
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)
499 bDiacritic = true;
501 else
503 // the font lacks GDEF table
504 if (pHbPositions[i].x_advance == 0)
505 bDiacritic = true;
508 if (bDiacritic)
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)
532 // XXX: why?
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;
540 return true;
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: */