Branch libreoffice-5-0-4
[LibreOffice.git] / vcl / win / source / gdi / winlayout.cxx
blobabbdc5a334c4bc512a819eafe62adfb8f6de218f
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 "winlayout.hxx"
22 #include "osl/module.h"
23 #include "osl/file.h"
25 #include <comphelper/windowserrorstring.hxx>
27 #include <opengl/texture.hxx>
28 #include <opengl/win/gdiimpl.hxx>
29 #include <vcl/opengl/OpenGLHelper.hxx>
30 #include <win/salgdi.h>
31 #include <win/saldata.hxx>
32 #include <outdev.h>
34 #include "sft.hxx"
35 #include "sallayout.hxx"
37 #include <cstdio>
38 #include <cstdlib>
40 #include <sal/alloca.h>
42 #include <algorithm>
44 #include <shlwapi.h>
45 #include <winver.h>
47 #include <unordered_map>
49 typedef std::unordered_map<int,int> IntMap;
51 // Graphite headers
52 #include <config_graphite.h>
53 #if ENABLE_GRAPHITE
54 #include <i18nlangtag/languagetag.hxx>
55 #include <graphite_features.hxx>
56 #endif
58 #define DROPPED_OUTGLYPH 0xFFFF
60 #include <config_mingw.h>
62 namespace
64 // Extra space at the top and bottom of the glyph in total = tmHeight / GLYPH_SPACE_RATIO;
65 const int GLYPH_SPACE_RATIO = 8;
66 // Border size at the top of the glyph = tmHeight / GLYPH_OFFSET_RATIO;
67 const int GLYPH_OFFSET_RATIO = GLYPH_SPACE_RATIO * 2;
70 struct OpenGLGlyphCacheChunk
72 WORD mnFirstGlyph;
73 int mnGlyphCount;
74 std::vector<Rectangle> maLocation;
75 std::shared_ptr<OpenGLTexture> mpTexture;
76 int mnAscent;
77 int mnHeight;
78 bool mbVertical;
80 int getExtraSpace() const
82 return std::max(mnHeight / GLYPH_SPACE_RATIO, 4);
85 int getExtraOffset() const
87 return std::max(mnHeight / GLYPH_OFFSET_RATIO, 2);
91 // win32 specific physical font instance
92 class ImplWinFontEntry : public ImplFontEntry
94 public:
95 explicit ImplWinFontEntry( FontSelectPattern& );
96 virtual ~ImplWinFontEntry();
98 private:
99 // TODO: also add HFONT??? Watch out for issues with too many active fonts...
101 public:
102 SCRIPT_CACHE& GetScriptCache() const
103 { return maScriptCache; }
104 private:
105 mutable SCRIPT_CACHE maScriptCache;
106 std::vector<OpenGLGlyphCacheChunk> maOpenGLGlyphCache;
108 public:
109 int GetCachedGlyphWidth( int nCharCode ) const;
110 void CacheGlyphWidth( int nCharCode, int nCharWidth );
112 bool InitKashidaHandling( HDC );
113 int GetMinKashidaWidth() const { return mnMinKashidaWidth; }
114 int GetMinKashidaGlyph() const { return mnMinKashidaGlyph; }
116 private:
117 IntMap maWidthMap;
118 mutable int mnMinKashidaWidth;
119 mutable int mnMinKashidaGlyph;
121 public:
122 bool GlyphIsCached(int nGlyphIndex) const;
123 bool AddChunkOfGlyphs(int nGlyphIndex, const WinLayout& rLayout, SalGraphics& rGraphics);
124 const OpenGLGlyphCacheChunk& GetCachedGlyphChunkFor(int nGlyphIndex) const;
127 #ifdef SAL_LOG_INFO
129 namespace {
131 char ColorFor(COLORREF aColor)
133 if (aColor == RGB(0xFF, 0xFF, 0xFF))
134 return ' ';
135 else if (aColor == RGB(0x00, 0x00, 0x00))
136 return 'X';
138 return '0' + (10*(GetRValue(aColor) + GetGValue(aColor) + GetBValue(aColor))) / (0xFF*3);
141 void DumpGlyphBitmap(OpenGLGlyphCacheChunk&, HDC hDC)
143 HBITMAP hBitmap = static_cast<HBITMAP>(GetCurrentObject(hDC, OBJ_BITMAP));
144 if (hBitmap == NULL)
146 SAL_WARN("vcl.gdi", "GetCurrentObject failed: " << WindowsErrorString(GetLastError()));
147 return;
150 BITMAP aBitmap;
151 if (!GetObjectW(hBitmap, sizeof(aBitmap), &aBitmap))
153 SAL_WARN("vcl.gdi", "GetObjectW failed: " << WindowsErrorString(GetLastError()));
154 return;
157 SAL_INFO("vcl.gdi.opengl", "Bitmap " << hBitmap << ": " << aBitmap.bmWidth << "x" << aBitmap.bmHeight << ":");
159 std::ostringstream sLine("\n");
160 for (long y = 0; y < aBitmap.bmHeight; y++)
162 for (long x = 0; x < std::min(75l, aBitmap.bmWidth); x++)
163 sLine << ColorFor(GetPixel(hDC, x, y));
164 if (y < aBitmap.bmHeight - 1)
165 sLine << "\n";
167 SAL_INFO("vcl.gdi.opengl", sLine.str());
170 } // anonymous namespace
172 #endif // SAL_LOG_INFO
174 template< typename charT, typename traits >
175 inline std::basic_ostream<charT, traits> & operator <<(
176 std::basic_ostream<charT, traits> & stream, const std::vector<OpenGLGlyphCacheChunk>& rCache )
178 stream << "{";
179 for (auto i = rCache.cbegin(); i != rCache.cend(); ++i)
181 stream << "[" << i->mnFirstGlyph;
182 if (i->mnGlyphCount > 1)
183 stream << ".." << (i->mnFirstGlyph + i->mnGlyphCount - 1);
184 stream << "]";
185 if (i+1 != rCache.cend())
187 stream << ",";
188 assert(i->mnFirstGlyph + i->mnGlyphCount <= (i+1)->mnFirstGlyph);
192 return stream << "}";
195 inline void ImplWinFontEntry::CacheGlyphWidth( int nCharCode, int nCharWidth )
197 maWidthMap[ nCharCode ] = nCharWidth;
200 inline int ImplWinFontEntry::GetCachedGlyphWidth( int nCharCode ) const
202 IntMap::const_iterator it = maWidthMap.find( nCharCode );
203 if( it == maWidthMap.end() )
204 return -1;
205 return it->second;
208 bool ImplWinFontEntry::GlyphIsCached(int nGlyphIndex) const
210 if (nGlyphIndex == DROPPED_OUTGLYPH)
211 return true;
213 for (size_t i = 0; i < maOpenGLGlyphCache.size(); i++)
214 if (nGlyphIndex >= maOpenGLGlyphCache[i].mnFirstGlyph &&
215 nGlyphIndex < maOpenGLGlyphCache[i].mnFirstGlyph + maOpenGLGlyphCache[i].mnGlyphCount)
216 return true;
218 return false;
221 bool ImplWinFontEntry::AddChunkOfGlyphs(int nGlyphIndex, const WinLayout& rLayout, SalGraphics& rGraphics)
223 const int DEFAULT_CHUNK_SIZE = 20;
225 if (nGlyphIndex == DROPPED_OUTGLYPH)
226 return true;
228 SAL_INFO("vcl.gdi.opengl", "this=" << this << " " << nGlyphIndex << " old: " << maOpenGLGlyphCache);
230 auto n = maOpenGLGlyphCache.begin();
231 while (n != maOpenGLGlyphCache.end() &&
232 nGlyphIndex > n->mnFirstGlyph)
233 ++n;
234 assert(n == maOpenGLGlyphCache.end() || nGlyphIndex < n->mnFirstGlyph);
236 int nCount = DEFAULT_CHUNK_SIZE;
237 if (n != maOpenGLGlyphCache.end() && nGlyphIndex + nCount >= n->mnFirstGlyph)
238 nCount = n->mnFirstGlyph - nGlyphIndex;
240 if (nCount < DEFAULT_CHUNK_SIZE)
242 if (n == maOpenGLGlyphCache.begin())
244 nGlyphIndex = std::max(0, n->mnFirstGlyph - DEFAULT_CHUNK_SIZE);
246 else
248 nGlyphIndex = std::max(n[-1].mnFirstGlyph + n[-1].mnGlyphCount,
249 n->mnFirstGlyph - DEFAULT_CHUNK_SIZE);
251 nCount = n->mnFirstGlyph - nGlyphIndex;
254 OpenGLGlyphCacheChunk aChunk;
255 aChunk.mnFirstGlyph = nGlyphIndex;
256 aChunk.mnGlyphCount = nCount;
258 std::vector<WORD> aGlyphIndices(nCount);
259 for (int i = 0; i < nCount; i++)
260 aGlyphIndices[i] = nGlyphIndex + i;
262 HDC hDC = CreateCompatibleDC(rLayout.mhDC);
263 if (hDC == NULL)
265 SAL_WARN("vcl.gdi", "CreateCompatibleDC failed: " << WindowsErrorString(GetLastError()));
266 return false;
268 HFONT hOrigFont = static_cast<HFONT>(SelectObject(hDC, rLayout.mhFont));
269 if (hOrigFont == NULL)
271 SAL_WARN("vcl.gdi", "SelectObject failed: " << WindowsErrorString(GetLastError()));
272 DeleteDC(hDC);
273 return false;
276 SIZE aSize;
278 if (!GetTextExtentExPointI(hDC, aGlyphIndices.data(), nCount, 0, NULL, NULL, &aSize))
280 SAL_WARN("vcl.gdi", "GetTextExtentExPointI failed: " << WindowsErrorString(GetLastError()));
281 SelectObject(hDC, hOrigFont);
282 DeleteDC(hDC);
283 return false;
286 std::vector<ABC> aABC(nCount);
287 if (!GetCharABCWidthsI(hDC, 0, nCount, aGlyphIndices.data(), aABC.data()))
289 SAL_WARN("vcl.gdi", "GetCharABCWidthsI failed: " << WindowsErrorString(GetLastError()));
290 SelectObject(hDC, hOrigFont);
291 DeleteDC(hDC);
292 return false;
295 std::ostringstream sLine;
296 for (int i = 0; i < nCount; i++)
297 sLine << aABC[i].abcA << ":" << aABC[i].abcB << ":" << aABC[i].abcC << " ";
298 SAL_INFO("vcl.gdi.opengl", "ABC widths: " << sLine.str());
300 TEXTMETRICW aTextMetric;
301 if (!GetTextMetricsW(hDC, &aTextMetric))
303 SAL_WARN("vcl.gdi", "GetTextMetrics failed: " << WindowsErrorString(GetLastError()));
304 SelectObject(hDC, hOrigFont);
305 DeleteDC(hDC);
306 return false;
308 aChunk.mnAscent = aTextMetric.tmAscent;
309 aChunk.mnHeight = aTextMetric.tmHeight;
311 // Try hard to avoid overlap as we want to be able to use
312 // individual rectangles for each glyph. The ABC widths don't
313 // take anti-alising into consideration. Let's hope that leaving
314 // "extra" space inbetween glyphs will help.
315 std::vector<int> aDX(nCount);
316 int totWidth = 0;
317 for (int i = 0; i < nCount; i++)
319 aDX[i] = aABC[i].abcB + std::abs(aABC[i].abcC);
320 if (i == 0)
321 aDX[0] += std::abs(aABC[0].abcA);
322 if (i < nCount-1)
323 aDX[i] += std::abs(aABC[i+1].abcA);
324 aDX[i] += aChunk.getExtraSpace();
325 totWidth += aDX[i];
328 LOGFONTW aLogfont;
329 if (!GetObjectW(rLayout.mhFont, sizeof(aLogfont), &aLogfont))
331 SAL_WARN("vcl.gdi", "GetObject failed: " << WindowsErrorString(GetLastError()));
332 SelectObject(hDC, hOrigFont);
333 DeleteDC(hDC);
334 return false;
337 wchar_t sFaceName[200];
338 int nFaceNameLen = GetTextFaceW(hDC, SAL_N_ELEMENTS(sFaceName), sFaceName);
339 if (!nFaceNameLen)
341 SAL_WARN("vcl.gdi", "GetTextFace failed: " << WindowsErrorString(GetLastError()));
342 SelectObject(hDC, hOrigFont);
343 DeleteDC(hDC);
344 return false;
347 SAL_INFO("vcl.gdi.opengl", OUString(sFaceName, nFaceNameLen) <<
348 ": Escapement=" << aLogfont.lfEscapement <<
349 " Orientation=" << aLogfont.lfOrientation <<
350 " Ascent=" << aTextMetric.tmAscent <<
351 " InternalLeading=" << aTextMetric.tmInternalLeading <<
352 " Size=(" << aSize.cx << "," << aSize.cy << ") totWidth=" << totWidth);
354 if (SelectObject(hDC, hOrigFont) == NULL)
355 SAL_WARN("vcl.gdi", "SelectObject failed: " << WindowsErrorString(GetLastError()));
356 if (!DeleteDC(hDC))
357 SAL_WARN("vcl.gdi", "DeleteDC failed: " << WindowsErrorString(GetLastError()));
359 // Leave extra space also at top and bottom
360 int nBitmapWidth, nBitmapHeight;
361 if (sFaceName[0] == '@')
363 nBitmapWidth = aSize.cy + aChunk.getExtraSpace();
364 nBitmapHeight = totWidth;
365 aChunk.mbVertical = true;
367 else
369 nBitmapWidth = totWidth;
370 nBitmapHeight = aSize.cy + aChunk.getExtraSpace();
371 aChunk.mbVertical = false;
374 if (aChunk.mbVertical && aLogfont.lfEscapement != 2700)
375 return false;
377 OpenGLCompatibleDC aDC(rGraphics, 0, 0, nBitmapWidth, nBitmapHeight);
379 HFONT hNonAntialiasedFont = NULL;
381 #ifdef DBG_UTIL
382 static bool bNoAntialias = (std::getenv("VCL_GLYPH_CACHING_HACK_NO_ANTIALIAS") != NULL);
383 if (bNoAntialias)
385 aLogfont.lfQuality = NONANTIALIASED_QUALITY;
386 hNonAntialiasedFont = CreateFontIndirectW(&aLogfont);
387 if (hNonAntialiasedFont == NULL)
389 SAL_WARN("vcl.gdi", "CreateFontIndirect failed: " << WindowsErrorString(GetLastError()));
390 return false;
393 #endif
395 hOrigFont = SelectFont(aDC.getCompatibleHDC(), hNonAntialiasedFont != NULL ? hNonAntialiasedFont : rLayout.mhFont);
396 if (hOrigFont == NULL)
398 SAL_WARN("vcl.gdi", "SelectObject failed: " << WindowsErrorString(GetLastError()));
399 return false;
402 SetTextColor(aDC.getCompatibleHDC(), RGB(0, 0, 0));
403 SetBkColor(aDC.getCompatibleHDC(), RGB(255, 255, 255));
405 aDC.fill(MAKE_SALCOLOR(0xff, 0xff, 0xff));
407 int nY = aChunk.getExtraOffset();
408 int nX = nY;
409 if (aChunk.mbVertical)
410 nX += aDX[0];
411 if (!ExtTextOutW(aDC.getCompatibleHDC(), nX, nY, ETO_GLYPH_INDEX, NULL, aGlyphIndices.data(), nCount, aDX.data()))
413 SAL_WARN("vcl.gdi", "ExtTextOutW failed: " << WindowsErrorString(GetLastError()));
414 SelectFont(aDC.getCompatibleHDC(), hOrigFont);
415 if (hNonAntialiasedFont != NULL)
416 DeleteObject(hNonAntialiasedFont);
417 return false;
420 aChunk.maLocation.resize(nCount);
421 UINT nPos = 0;
422 for (int i = 0; i < nCount; i++)
424 if (aChunk.mbVertical)
426 aChunk.maLocation[i].Left() = 0;
427 aChunk.maLocation[i].Right() = nBitmapWidth;
428 aChunk.maLocation[i].Top() = nPos;
429 aChunk.maLocation[i].Bottom() = nPos + aDX[i];
430 nPos = aChunk.maLocation[i].Bottom();
432 else
434 aChunk.maLocation[i].Left() = nPos;
435 aChunk.maLocation[i].Right() = nPos + aDX[i];
436 nPos = aChunk.maLocation[i].Right();
437 aChunk.maLocation[i].Top() = 0;
438 aChunk.maLocation[i].Bottom() = aSize.cy + aChunk.getExtraSpace();
442 aChunk.mpTexture = std::unique_ptr<OpenGLTexture>(aDC.getTexture());
444 maOpenGLGlyphCache.insert(n, aChunk);
446 SelectFont(aDC.getCompatibleHDC(), hOrigFont);
447 if (hNonAntialiasedFont != NULL)
448 DeleteObject(hNonAntialiasedFont);
450 #ifdef SAL_LOG_INFO
451 SAL_INFO("vcl.gdi.opengl", "this=" << this << " now: " << maOpenGLGlyphCache);
452 DumpGlyphBitmap(aChunk, aDC.getCompatibleHDC());
453 #endif
455 return true;
458 const OpenGLGlyphCacheChunk& ImplWinFontEntry::GetCachedGlyphChunkFor(int nGlyphIndex) const
460 auto i = maOpenGLGlyphCache.cbegin();
461 while (i != maOpenGLGlyphCache.cend() && nGlyphIndex >= i->mnFirstGlyph + i->mnGlyphCount)
462 ++i;
463 assert(i != maOpenGLGlyphCache.cend());
464 assert(nGlyphIndex >= i->mnFirstGlyph && nGlyphIndex < i->mnFirstGlyph + i->mnGlyphCount);
465 return *i;
468 WinLayout::WinLayout(HDC hDC, const ImplWinFontData& rWFD, ImplWinFontEntry& rWFE, bool bUseOpenGL)
469 : mhDC( hDC ),
470 mhFont( (HFONT)GetCurrentObject(hDC,OBJ_FONT) ),
471 mnBaseAdv( 0 ),
472 mfFontScale( 1.0 ),
473 mrWinFontData( rWFD ),
474 mrWinFontEntry(rWFE),
475 mbUseOpenGL(bUseOpenGL)
477 // keep mrWinFontEntry alive
478 mrWinFontEntry.m_pFontCache->Acquire(&mrWinFontEntry);
481 WinLayout::~WinLayout()
483 mrWinFontEntry.m_pFontCache->Release(&mrWinFontEntry);
486 void WinLayout::InitFont() const
488 SelectObject( mhDC, mhFont );
491 // Using reasonably sized fonts to emulate huge fonts works around
492 // a lot of problems in printer and display drivers. Huge fonts are
493 // mostly used by high resolution reference devices which are never
494 // painted to anyway. In the rare case that a huge font needs to be
495 // displayed somewhere then the workaround doesn't help anymore.
496 // If the drivers fail silently for huge fonts, so be it...
497 HFONT WinLayout::DisableFontScaling() const
499 if( mfFontScale == 1.0 )
500 return 0;
502 LOGFONTW aLogFont;
503 GetObjectW( mhFont, sizeof(LOGFONTW), &aLogFont);
504 aLogFont.lfHeight = (LONG)(mfFontScale * aLogFont.lfHeight);
505 aLogFont.lfWidth = (LONG)(mfFontScale * aLogFont.lfWidth);
506 HFONT hHugeFont = CreateFontIndirectW( &aLogFont);
507 if( !hHugeFont )
508 return 0;
510 return SelectFont( mhDC, hHugeFont );
513 SCRIPT_CACHE& WinLayout::GetScriptCache() const
515 return mrWinFontEntry.GetScriptCache();
518 void WinLayout::DrawText(SalGraphics& rGraphics) const
520 WinSalGraphics& rWinGraphics = static_cast<WinSalGraphics&>(rGraphics);
521 HDC hDC = rWinGraphics.getHDC();
523 if (!mbUseOpenGL)
525 // no OpenGL, just classic rendering
526 DrawTextImpl(hDC);
528 else if (CacheGlyphs(rGraphics) &&
529 DrawCachedGlyphs(rGraphics))
531 // Nothing
533 else
535 // We have to render the text to a hidden texture, and draw it.
537 // Note that Windows GDI does not really support the alpha correctly
538 // when drawing - ie. it draws nothing to the alpha channel when
539 // rendering the text, even the antialiasing is done as 'real' pixels,
540 // not alpha...
542 // Luckily, this does not really limit us:
544 // To blend properly, we draw the texture, but then use it as an alpha
545 // channel for solid color (that will define the text color). This
546 // destroys the subpixel antialiasing - turns it into 'classic'
547 // antialiasing - but that is the best we can do, because the subpixel
548 // antialiasing needs to know what is in the background: When the
549 // background is white, or white-ish, it does the subpixel, but when
550 // there is a color, it just darkens the color (and does this even
551 // when part of the character is on a colored background, and part on
552 // white). It has to work this way, the results would look strange
553 // otherwise.
555 // For the GL rendering to work even with the subpixel antialiasing,
556 // we would need to get the current texture from the screen, let GDI
557 // draw the text to it (so that it can decide well where to use the
558 // subpixel and where not), and draw the result - but in that case we
559 // don't need alpha anyway.
561 // TODO: check the performance of this 2nd approach at some stage and
562 // switch to that if it performs well.
564 Rectangle aRect;
565 GetBoundRect(rGraphics, aRect);
567 OpenGLCompatibleDC aDC(rGraphics, aRect.Left(), aRect.Top(), aRect.GetWidth(), aRect.GetHeight());
569 // we are making changes to the DC, make sure we got a new one
570 assert(aDC.getCompatibleHDC() != hDC);
572 // setup the hidden DC with black color and white background, we will
573 // use the result of the text drawing later as a mask only
574 HFONT hOrigFont = SelectFont(aDC.getCompatibleHDC(), mhFont);
576 SetTextColor(aDC.getCompatibleHDC(), RGB(0, 0, 0));
577 SetBkColor(aDC.getCompatibleHDC(), RGB(255, 255, 255));
579 UINT nTextAlign = GetTextAlign(hDC);
580 SetTextAlign(aDC.getCompatibleHDC(), nTextAlign);
582 // the actual drawing
583 DrawTextImpl(aDC.getCompatibleHDC());
585 COLORREF color = GetTextColor(hDC);
586 SalColor salColor = MAKE_SALCOLOR(GetRValue(color), GetGValue(color), GetBValue(color));
588 WinOpenGLSalGraphicsImpl *pImpl = dynamic_cast<WinOpenGLSalGraphicsImpl*>(rWinGraphics.mpImpl.get());
589 if (pImpl)
591 pImpl->PreDraw();
593 std::unique_ptr<OpenGLTexture> xTexture(aDC.getTexture());
594 if (xTexture)
595 pImpl->DrawMask(*xTexture, salColor, aDC.getTwoRect());
597 pImpl->PostDraw();
600 SelectFont(aDC.getCompatibleHDC(), hOrigFont);
604 struct VisualItem
606 public:
607 SCRIPT_ITEM* mpScriptItem;
608 int mnMinGlyphPos;
609 int mnEndGlyphPos;
610 int mnMinCharPos;
611 int mnEndCharPos;
612 //long mnPixelWidth;
613 int mnXOffset;
614 ABC maABCWidths;
615 bool mbHasKashidas;
617 public:
618 bool IsEmpty() const { return (mnEndGlyphPos <= 0); }
619 bool IsRTL() const { return mpScriptItem->a.fRTL; }
620 bool HasKashidas() const { return mbHasKashidas; }
623 static bool bUspInited = false;
625 static bool bManualCellAlign = true;
627 static void InitUSP()
629 // get the usp10.dll version info
630 HMODULE usp10 = GetModuleHandle("usp10.dll");
631 void *pScriptIsComplex = reinterpret_cast< void* >( GetProcAddress(usp10, "ScriptIsComplex"));
632 int nUspVersion = 0;
633 rtl_uString* pModuleURL = NULL;
634 osl_getModuleURLFromAddress( pScriptIsComplex, &pModuleURL );
635 rtl_uString* pModuleFileName = NULL;
636 if( pModuleURL )
637 osl_getSystemPathFromFileURL( pModuleURL, &pModuleFileName );
638 const sal_Unicode* pModuleFileCStr = NULL;
639 if( pModuleFileName )
640 pModuleFileCStr = rtl_uString_getStr( pModuleFileName );
641 if( pModuleFileCStr )
643 DWORD nHandle;
644 DWORD nBufSize = GetFileVersionInfoSizeW( const_cast<LPWSTR>(reinterpret_cast<LPCWSTR>(pModuleFileCStr)), &nHandle );
645 char* pBuffer = (char*)alloca( nBufSize );
646 BOOL bRC = GetFileVersionInfoW( const_cast<LPWSTR>(reinterpret_cast<LPCWSTR>(pModuleFileCStr)), nHandle, nBufSize, pBuffer );
647 VS_FIXEDFILEINFO* pFixedFileInfo = NULL;
648 UINT nFixedFileSize = 0;
649 if( bRC )
650 VerQueryValueW( pBuffer, const_cast<LPWSTR>(L"\\"), (void**)&pFixedFileInfo, &nFixedFileSize );
651 if( pFixedFileInfo && pFixedFileInfo->dwSignature == 0xFEEF04BD )
652 nUspVersion = HIWORD(pFixedFileInfo->dwProductVersionMS) * 10000
653 + LOWORD(pFixedFileInfo->dwProductVersionMS);
656 // #i77976# USP>=1.0600 changed the need to manually align glyphs in their cells
657 if( nUspVersion >= 10600 )
658 bManualCellAlign = false;
660 bUspInited = true;
663 UniscribeLayout::UniscribeLayout(HDC hDC, const ImplWinFontData& rWinFontData,
664 ImplWinFontEntry& rWinFontEntry, bool bUseOpenGL)
665 : WinLayout(hDC, rWinFontData, rWinFontEntry, bUseOpenGL),
666 mpScriptItems( NULL ),
667 mpVisualItems( NULL ),
668 mnItemCount( 0 ),
669 mnCharCapacity( 0 ),
670 mpLogClusters( NULL ),
671 mpCharWidths( NULL ),
672 mnSubStringMin( 0 ),
673 mnGlyphCount( 0 ),
674 mnGlyphCapacity( 0 ),
675 mpGlyphAdvances( NULL ),
676 mpJustifications( NULL ),
677 mpOutGlyphs( NULL ),
678 mpGlyphOffsets( NULL ),
679 mpVisualAttrs( NULL ),
680 mpGlyphs2Chars( NULL ),
681 mnMinKashidaWidth( 0 ),
682 mnMinKashidaGlyph( 0 ),
683 mbDisableGlyphInjection( false )
686 UniscribeLayout::~UniscribeLayout()
688 delete[] mpScriptItems;
689 delete[] mpVisualItems;
690 delete[] mpLogClusters;
691 delete[] mpCharWidths;
692 delete[] mpOutGlyphs;
693 delete[] mpGlyphAdvances;
694 delete[] mpJustifications;
695 delete[] mpGlyphOffsets;
696 delete[] mpVisualAttrs;
697 delete[] mpGlyphs2Chars;
700 bool UniscribeLayout::LayoutText( ImplLayoutArgs& rArgs )
702 // for a base layout only the context glyphs have to be dropped
703 // => when the whole string is involved there is no extra context
704 typedef std::vector<int> TIntVector;
705 TIntVector aDropChars;
706 if( rArgs.mnFlags & SalLayoutFlags::ForFallback )
708 // calculate superfluous context char positions
709 aDropChars.push_back( 0 );
710 aDropChars.push_back( rArgs.mnLength );
711 int nMin, nEnd;
712 bool bRTL;
713 for( rArgs.ResetPos(); rArgs.GetNextRun( &nMin, &nEnd, &bRTL ); )
715 aDropChars.push_back( nMin );
716 aDropChars.push_back( nEnd );
718 // prepare aDropChars for binary search which will allow to
719 // not bother with visual items that will be dropped anyway
720 std::sort( aDropChars.begin(), aDropChars.end() );
723 // prepare layout
724 // TODO: fix case when recyclying old UniscribeLayout object
725 mnMinCharPos = rArgs.mnMinCharPos;
726 mnEndCharPos = rArgs.mnEndCharPos;
728 // determine script items from string
730 // prepare itemization
731 // TODO: try to avoid itemization since it costs a lot of performance
732 SCRIPT_STATE aScriptState = {0,false,false,false,false,false,false,false,false,0,0};
733 aScriptState.uBidiLevel = bool(rArgs.mnFlags & SalLayoutFlags::BiDiRtl);
734 aScriptState.fOverrideDirection = bool(rArgs.mnFlags & SalLayoutFlags::BiDiStrong);
735 aScriptState.fDigitSubstitute = bool(rArgs.mnFlags & SalLayoutFlags::SubstituteDigits);
736 aScriptState.fArabicNumContext = aScriptState.fDigitSubstitute & aScriptState.uBidiLevel;
737 DWORD nLangId = 0; // TODO: get language from font
738 SCRIPT_CONTROL aScriptControl = {nLangId,false,false,false,false,false,false,false,false,0};
739 aScriptControl.fNeutralOverride = aScriptState.fOverrideDirection;
740 aScriptControl.fContextDigits = bool(rArgs.mnFlags & SalLayoutFlags::SubstituteDigits);
741 #if HAVE_FMERGENEUTRALITEMS
742 aScriptControl.fMergeNeutralItems = true;
743 #endif
744 // determine relevant substring and work only on it
745 // when Bidi status is unknown we need to look at the whole string though
746 mnSubStringMin = 0;
747 int nSubStringEnd = rArgs.mnLength;
748 if( aScriptState.fOverrideDirection )
750 // TODO: limit substring to portion limits
751 mnSubStringMin = rArgs.mnMinCharPos - 8;
752 if( mnSubStringMin < 0 )
753 mnSubStringMin = 0;
754 nSubStringEnd = rArgs.mnEndCharPos + 8;
755 if( nSubStringEnd > rArgs.mnLength )
756 nSubStringEnd = rArgs.mnLength;
760 // now itemize the substring with its context
761 for( int nItemCapacity = 16;; nItemCapacity *= 8 )
763 mpScriptItems = new SCRIPT_ITEM[ nItemCapacity ];
764 HRESULT nRC = ScriptItemize(
765 reinterpret_cast<LPCWSTR>(rArgs.mpStr + mnSubStringMin), nSubStringEnd - mnSubStringMin,
766 nItemCapacity - 1, &aScriptControl, &aScriptState,
767 mpScriptItems, &mnItemCount );
768 if( !nRC ) // break loop when everything is correctly itemized
769 break;
771 // prepare bigger buffers for another itemization round
772 delete[] mpScriptItems;
773 mpScriptItems = NULL;
774 if( nRC != E_OUTOFMEMORY )
775 return false;
776 if( nItemCapacity > (nSubStringEnd - mnSubStringMin) + 16 )
777 return false;
780 // calculate the order of visual items
781 int nItem, i;
783 // adjust char positions by substring offset
784 for( nItem = 0; nItem <= mnItemCount; ++nItem )
785 mpScriptItems[ nItem ].iCharPos += mnSubStringMin;
786 // default visual item ordering
787 mpVisualItems = new VisualItem[ mnItemCount ];
788 for( nItem = 0; nItem < mnItemCount; ++nItem )
790 // initialize char specific item info
791 VisualItem& rVisualItem = mpVisualItems[ nItem ];
792 SCRIPT_ITEM* pScriptItem = &mpScriptItems[ nItem ];
793 rVisualItem.mpScriptItem = pScriptItem;
794 rVisualItem.mnMinCharPos = pScriptItem[0].iCharPos;
795 rVisualItem.mnEndCharPos = pScriptItem[1].iCharPos;
798 // reorder visual item order if needed
799 if( rArgs.mnFlags & SalLayoutFlags::BiDiStrong )
801 // force RTL item ordering if requested
802 if( rArgs.mnFlags & SalLayoutFlags::BiDiRtl )
804 VisualItem* pVI0 = &mpVisualItems[ 0 ];
805 VisualItem* pVI1 = &mpVisualItems[ mnItemCount ];
806 while( pVI0 < --pVI1 )
808 VisualItem aVtmp = *pVI0;
809 *(pVI0++) = *pVI1;
810 *pVI1 = aVtmp;
814 else if( mnItemCount > 1 )
816 // apply bidi algorithm's rule L2 on item level
817 // TODO: use faster L2 algorithm
818 int nMaxBidiLevel = 0;
819 VisualItem* pVI = &mpVisualItems[0];
820 VisualItem* const pVIend = pVI + mnItemCount;
821 for(; pVI < pVIend; ++pVI )
822 if( nMaxBidiLevel < pVI->mpScriptItem->a.s.uBidiLevel )
823 nMaxBidiLevel = pVI->mpScriptItem->a.s.uBidiLevel;
825 while( --nMaxBidiLevel >= 0 )
827 for( pVI = &mpVisualItems[0]; pVI < pVIend; )
829 // find item range that needs reordering
830 for(; pVI < pVIend; ++pVI )
831 if( nMaxBidiLevel < pVI->mpScriptItem->a.s.uBidiLevel )
832 break;
833 VisualItem* pVImin = pVI++;
834 for(; pVI < pVIend; ++pVI )
835 if( nMaxBidiLevel >= pVI->mpScriptItem->a.s.uBidiLevel )
836 break;
837 VisualItem* pVImax = pVI++;
839 // reverse order of items in this range
840 while( pVImin < --pVImax )
842 VisualItem aVtmp = *pVImin;
843 *(pVImin++) = *pVImax;
844 *pVImax = aVtmp;
850 // allocate arrays
851 // TODO: when reusing object reuse old allocations or delete them
852 // TODO: use only [nSubStringMin..nSubStringEnd) instead of [0..nSubStringEnd)
853 mnCharCapacity = nSubStringEnd;
854 mpLogClusters = new WORD[ mnCharCapacity ];
855 mpCharWidths = new int[ mnCharCapacity ];
857 mnGlyphCount = 0;
858 mnGlyphCapacity = 16 + 4 * (nSubStringEnd - mnSubStringMin); // worst case assumption
859 mpGlyphAdvances = new int[ mnGlyphCapacity ];
860 mpOutGlyphs = new WORD[ mnGlyphCapacity ];
861 mpGlyphOffsets = new GOFFSET[ mnGlyphCapacity ];
862 mpVisualAttrs = new SCRIPT_VISATTR[ mnGlyphCapacity ];
864 long nXOffset = 0;
865 for( int j = mnSubStringMin; j < nSubStringEnd; ++j )
866 mpCharWidths[j] = 0;
868 // layout script items
869 SCRIPT_CACHE& rScriptCache = GetScriptCache();
870 for( nItem = 0; nItem < mnItemCount; ++nItem )
872 VisualItem& rVisualItem = mpVisualItems[ nItem ];
874 // initialize glyph specific item info
875 rVisualItem.mnMinGlyphPos = mnGlyphCount;
876 rVisualItem.mnEndGlyphPos = 0;
877 rVisualItem.mnXOffset = nXOffset;
878 //rVisualItem.mnPixelWidth = 0;
880 // shortcut ignorable items
881 if( (rArgs.mnEndCharPos <= rVisualItem.mnMinCharPos)
882 || (rArgs.mnMinCharPos >= rVisualItem.mnEndCharPos) )
884 for( int j = rVisualItem.mnMinCharPos; j < rVisualItem.mnEndCharPos; ++j )
885 mpLogClusters[j] = sal::static_int_cast<WORD>(~0U);
886 if (rArgs.mnMinCharPos >= rVisualItem.mnEndCharPos)
887 { // fdo#47553 adjust "guessed" min (maybe up to -8 off) to
888 // actual min so it can be used properly in GetNextGlyphs
889 assert(mnSubStringMin <= rVisualItem.mnEndCharPos);
890 mnSubStringMin = rVisualItem.mnEndCharPos;
892 continue;
895 // override bidi analysis if requested
896 if( rArgs.mnFlags & SalLayoutFlags::BiDiStrong )
898 // FIXME: is this intended ?
899 rVisualItem.mpScriptItem->a.fRTL = (aScriptState.uBidiLevel & 1);
900 rVisualItem.mpScriptItem->a.s.uBidiLevel = aScriptState.uBidiLevel;
901 rVisualItem.mpScriptItem->a.s.fOverrideDirection = aScriptState.fOverrideDirection;
904 // convert the unicodes to glyphs
905 int nGlyphCount = 0;
906 int nCharCount = rVisualItem.mnEndCharPos - rVisualItem.mnMinCharPos;
907 HRESULT nRC = ScriptShape( mhDC, &rScriptCache,
908 reinterpret_cast<LPCWSTR>(rArgs.mpStr + rVisualItem.mnMinCharPos),
909 nCharCount,
910 mnGlyphCapacity - rVisualItem.mnMinGlyphPos, // problem when >0xFFFF
911 &rVisualItem.mpScriptItem->a,
912 mpOutGlyphs + rVisualItem.mnMinGlyphPos,
913 mpLogClusters + rVisualItem.mnMinCharPos,
914 mpVisualAttrs + rVisualItem.mnMinGlyphPos,
915 &nGlyphCount );
917 // find and handle problems in the unicode to glyph conversion
918 if( nRC == USP_E_SCRIPT_NOT_IN_FONT )
920 // the whole visual item needs a fallback, but make sure that the next
921 // fallback request is limited to the characters in the original request
922 // => this is handled in ImplLayoutArgs::PrepareFallback()
923 rArgs.NeedFallback( rVisualItem.mnMinCharPos, rVisualItem.mnEndCharPos,
924 rVisualItem.IsRTL() );
926 // don't bother to do a default layout in a fallback level
927 if( rArgs.mnFlags & SalLayoutFlags::ForFallback )
928 continue;
930 // the primitive layout engine is good enough for the default layout
931 rVisualItem.mpScriptItem->a.eScript = SCRIPT_UNDEFINED;
932 nRC = ScriptShape( mhDC, &rScriptCache,
933 reinterpret_cast<LPCWSTR>(rArgs.mpStr + rVisualItem.mnMinCharPos),
934 nCharCount,
935 mnGlyphCapacity - rVisualItem.mnMinGlyphPos,
936 &rVisualItem.mpScriptItem->a,
937 mpOutGlyphs + rVisualItem.mnMinGlyphPos,
938 mpLogClusters + rVisualItem.mnMinCharPos,
939 mpVisualAttrs + rVisualItem.mnMinGlyphPos,
940 &nGlyphCount );
942 if( nRC != 0 )
943 continue;
946 else if( nRC != 0 )
947 // something undefined happened => give up for this visual item
948 continue;
949 else // if( nRC == 0 )
951 // check if there are any NotDef glyphs
952 for( i = 0; i < nGlyphCount; ++i )
953 if( 0 == mpOutGlyphs[ i + rVisualItem.mnMinGlyphPos ] )
954 break;
955 if( i < nGlyphCount )
957 // clip charpos limits to the layout string without context
958 int nMinCharPos = rVisualItem.mnMinCharPos;
959 if( nMinCharPos < rArgs.mnMinCharPos )
960 nMinCharPos = rArgs.mnMinCharPos;
961 int nEndCharPos = rVisualItem.mnEndCharPos;
962 if( nEndCharPos > rArgs.mnEndCharPos )
963 nEndCharPos = rArgs.mnEndCharPos;
964 // request fallback for individual NotDef glyphs
967 // ignore non-NotDef glyphs
968 if( 0 != mpOutGlyphs[ i + rVisualItem.mnMinGlyphPos ] )
969 continue;
970 mpOutGlyphs[ i + rVisualItem.mnMinGlyphPos ] = DROPPED_OUTGLYPH;
971 // request fallback for the whole cell that resulted in a NotDef glyph
972 // TODO: optimize algorithm
973 const bool bRTL = rVisualItem.IsRTL();
974 if( !bRTL )
976 // request fallback for the left-to-right cell
977 for( int c = nMinCharPos; c < nEndCharPos; ++c )
979 if( mpLogClusters[ c ] == i )
981 // #i55716# skip WORDJOINER
982 if( rArgs.mpStr[ c ] == 0x2060 )
983 mpOutGlyphs[ i + rVisualItem.mnMinGlyphPos ] = 1;
984 else
985 rArgs.NeedFallback( c, false );
989 else
991 // request fallback for the right to left cell
992 for( int c = nEndCharPos; --c >= nMinCharPos; )
994 if( mpLogClusters[ c ] == i )
996 // #i55716# skip WORDJOINER
997 if( rArgs.mpStr[ c ] == 0x2060 )
998 mpOutGlyphs[ i + rVisualItem.mnMinGlyphPos ] = 1;
999 else
1000 rArgs.NeedFallback( c, true );
1004 } while( ++i < nGlyphCount );
1008 // now place the glyphs
1009 nRC = ScriptPlace( mhDC, &rScriptCache,
1010 mpOutGlyphs + rVisualItem.mnMinGlyphPos,
1011 nGlyphCount,
1012 mpVisualAttrs + rVisualItem.mnMinGlyphPos,
1013 &rVisualItem.mpScriptItem->a,
1014 mpGlyphAdvances + rVisualItem.mnMinGlyphPos,
1015 mpGlyphOffsets + rVisualItem.mnMinGlyphPos,
1016 &rVisualItem.maABCWidths );
1018 if( nRC != 0 )
1019 continue;
1021 // calculate the logical char widths from the glyph layout
1022 nRC = ScriptGetLogicalWidths(
1023 &rVisualItem.mpScriptItem->a,
1024 nCharCount, nGlyphCount,
1025 mpGlyphAdvances + rVisualItem.mnMinGlyphPos,
1026 mpLogClusters + rVisualItem.mnMinCharPos,
1027 mpVisualAttrs + rVisualItem.mnMinGlyphPos,
1028 mpCharWidths + rVisualItem.mnMinCharPos );
1030 // update the glyph counters
1031 mnGlyphCount += nGlyphCount;
1032 rVisualItem.mnEndGlyphPos = mnGlyphCount;
1034 // update nXOffset
1035 int nEndGlyphPos;
1036 if( GetItemSubrange( rVisualItem, i, nEndGlyphPos ) )
1037 for(; i < nEndGlyphPos; ++i )
1038 nXOffset += mpGlyphAdvances[ i ];
1040 // TODO: shrink glyphpos limits to match charpos/fallback limits
1041 //pVI->mnMinGlyphPos = nMinGlyphPos;
1042 //pVI->mnEndGlyphPos = nEndGlyphPos;
1044 // drop the superfluous context glyphs
1045 TIntVector::const_iterator it = aDropChars.begin();
1046 while( it != aDropChars.end() )
1048 // find matching "drop range"
1049 int nMinDropPos = *(it++); // begin of drop range
1050 if( nMinDropPos >= rVisualItem.mnEndCharPos )
1051 break;
1052 int nEndDropPos = *(it++); // end of drop range
1053 if( nEndDropPos <= rVisualItem.mnMinCharPos )
1054 continue;
1055 // clip "drop range" to visual item's char range
1056 if( nMinDropPos <= rVisualItem.mnMinCharPos )
1058 nMinDropPos = rVisualItem.mnMinCharPos;
1059 // drop the whole visual item if possible
1060 if( nEndDropPos >= rVisualItem.mnEndCharPos )
1062 rVisualItem.mnEndGlyphPos = 0;
1063 break;
1066 if( nEndDropPos > rVisualItem.mnEndCharPos )
1067 nEndDropPos = rVisualItem.mnEndCharPos;
1069 // drop the glyphs which correspond to the charpos range
1070 // drop the corresponding glyphs in the cluster
1071 for( int c = nMinDropPos; c < nEndDropPos; ++c )
1073 int nGlyphPos = mpLogClusters[c] + rVisualItem.mnMinGlyphPos;
1074 // no need to bother when the cluster was already dropped
1075 if( mpOutGlyphs[ nGlyphPos ] != DROPPED_OUTGLYPH )
1077 for(;;)
1079 mpOutGlyphs[ nGlyphPos ] = DROPPED_OUTGLYPH;
1080 // until the end of visual item
1081 if( ++nGlyphPos >= rVisualItem.mnEndGlyphPos )
1082 break;
1083 // until the next cluster start
1084 if( mpVisualAttrs[ nGlyphPos ].fClusterStart )
1085 break;
1092 // scale layout metrics if needed
1093 // TODO: does it make the code more simple if the metric scaling
1094 // is moved to the methods that need metric scaling (e.g. FillDXArray())?
1095 if( mfFontScale != 1.0 )
1097 mnBaseAdv = (int)((double)mnBaseAdv*mfFontScale);
1099 for( i = 0; i < mnItemCount; ++i )
1100 mpVisualItems[i].mnXOffset = (int)((double)mpVisualItems[i].mnXOffset*mfFontScale);
1102 mnBaseAdv = (int)((double)mnBaseAdv*mfFontScale);
1103 for( i = 0; i < mnGlyphCount; ++i )
1105 mpGlyphAdvances[i] = (int)(mpGlyphAdvances[i] * mfFontScale);
1106 mpGlyphOffsets[i].du = (LONG)(mpGlyphOffsets[i].du * mfFontScale);
1107 mpGlyphOffsets[i].dv = (LONG)(mpGlyphOffsets[i].dv * mfFontScale);
1108 // mpJustifications are still NULL
1111 for( i = mnSubStringMin; i < nSubStringEnd; ++i )
1112 mpCharWidths[i] = (int)(mpCharWidths[i] * mfFontScale);
1115 return true;
1118 // calculate the range of relevant glyphs for this visual item
1119 bool UniscribeLayout::GetItemSubrange( const VisualItem& rVisualItem,
1120 int& rMinGlyphPos, int& rEndGlyphPos ) const
1122 // return early when nothing of interest in this item
1123 if( rVisualItem.IsEmpty()
1124 || (rVisualItem.mnEndCharPos <= mnMinCharPos)
1125 || (mnEndCharPos <= rVisualItem.mnMinCharPos) )
1126 return false;
1128 // default: subrange is complete range
1129 rMinGlyphPos = rVisualItem.mnMinGlyphPos;
1130 rEndGlyphPos = rVisualItem.mnEndGlyphPos;
1132 // return early when the whole item is of interest
1133 if( (mnMinCharPos <= rVisualItem.mnMinCharPos)
1134 && (rVisualItem.mnEndCharPos <= mnEndCharPos ) )
1135 return true;
1137 // get glyph range from char range by looking at cluster boundries
1138 // TODO: optimize for case that LTR/RTL correspond to monotonous glyph indexes
1139 rMinGlyphPos = rVisualItem.mnEndGlyphPos;
1140 int nMaxGlyphPos = 0;
1142 int i = mnMinCharPos;
1143 if( i < rVisualItem.mnMinCharPos )
1144 i = rVisualItem.mnMinCharPos;
1145 int nCharPosLimit = rVisualItem.mnEndCharPos;
1146 if( nCharPosLimit > mnEndCharPos )
1147 nCharPosLimit = mnEndCharPos;
1148 for(; i < nCharPosLimit; ++i )
1150 int n = mpLogClusters[ i ] + rVisualItem.mnMinGlyphPos;
1151 if( rMinGlyphPos > n )
1152 rMinGlyphPos = n;
1153 if( nMaxGlyphPos < n )
1154 nMaxGlyphPos = n;
1156 if (nMaxGlyphPos > rVisualItem.mnEndGlyphPos)
1157 nMaxGlyphPos = rVisualItem.mnEndGlyphPos - 1;
1159 // extend the glyph range to account for all glyphs in referenced clusters
1160 if( !rVisualItem.IsRTL() ) // LTR-item
1162 // extend to rightmost glyph of rightmost referenced cluster
1163 for( i = nMaxGlyphPos; ++i < rVisualItem.mnEndGlyphPos; nMaxGlyphPos = i )
1164 if( mpVisualAttrs[i].fClusterStart )
1165 break;
1167 else // RTL-item
1169 // extend to leftmost glyph of leftmost referenced cluster
1170 for( i = rMinGlyphPos; --i >= rVisualItem.mnMinGlyphPos; rMinGlyphPos = i )
1171 if( mpVisualAttrs[i].fClusterStart )
1172 break;
1174 rEndGlyphPos = nMaxGlyphPos + 1;
1176 return true;
1179 int UniscribeLayout::GetNextGlyphs( int nLen, sal_GlyphId* pGlyphs, Point& rPos,
1180 int& nStartx8, DeviceCoordinate* pGlyphAdvances, int* pCharPosAry,
1181 const PhysicalFontFace** /*pFallbackFonts*/ ) const
1183 // HACK to allow fake-glyph insertion (e.g. for kashidas)
1184 // TODO: use iterator idiom instead of GetNextGlyphs(...)
1185 // TODO: else make sure that the limit for glyph injection is sufficient (currently 256)
1186 int nSubIter = nStartx8 & 0xff;
1187 int nStart = nStartx8 >> 8;
1189 // check the glyph iterator
1190 if( nStart > mnGlyphCount ) // nStart>MAX means no more glyphs
1191 return 0;
1193 // find the visual item for the nStart glyph position
1194 int nItem = 0;
1195 const VisualItem* pVI = mpVisualItems;
1196 if( nStart <= 0 ) // nStart<=0 requests the first visible glyph
1198 // find first visible item
1199 for(; nItem < mnItemCount; ++nItem, ++pVI )
1200 if( !pVI->IsEmpty() )
1201 break;
1202 // it is possible that there are glyphs but no valid visual item
1203 // TODO: get rid of these visual items more early
1204 if( nItem < mnItemCount )
1205 nStart = pVI->mnMinGlyphPos;
1207 else //if( nStart > 0 ) // nStart>0 means absolute glyph pos +1
1209 --nStart;
1211 // find matching item
1212 for(; nItem < mnItemCount; ++nItem, ++pVI )
1213 if( (nStart >= pVI->mnMinGlyphPos)
1214 && (nStart < pVI->mnEndGlyphPos) )
1215 break;
1218 // after the last visual item there are no more glyphs
1219 if( (nItem >= mnItemCount) || (nStart < 0) )
1221 nStartx8 = (mnGlyphCount + 1) << 8;
1222 return 0;
1225 // calculate the first glyph in the next visual item
1226 int nNextItemStart = mnGlyphCount;
1227 while( ++nItem < mnItemCount )
1229 if( mpVisualItems[nItem].IsEmpty() )
1230 continue;
1231 nNextItemStart = mpVisualItems[nItem].mnMinGlyphPos;
1232 break;
1235 // get the range of relevant glyphs in this visual item
1236 int nMinGlyphPos, nEndGlyphPos;
1237 bool bRC = GetItemSubrange( *pVI, nMinGlyphPos, nEndGlyphPos );
1238 DBG_ASSERT( bRC, "USPLayout::GNG GISR() returned false" );
1239 if( !bRC )
1241 nStartx8 = (mnGlyphCount + 1) << 8;
1242 return 0;
1245 // make sure nStart is inside the range of relevant glyphs
1246 if( nStart < nMinGlyphPos )
1247 nStart = nMinGlyphPos;
1249 // calculate the start glyph xoffset relative to layout's base position,
1250 // advance to next visual glyph position by using adjusted glyph widths
1251 // TODO: speed up the calculation for nStart!=0 case by using rPos as a cache
1252 long nXOffset = pVI->mnXOffset;
1253 const int* pGlyphWidths = mpJustifications ? mpJustifications : mpGlyphAdvances;
1254 for( int i = nMinGlyphPos; i < nStart; ++i )
1255 nXOffset += pGlyphWidths[ i ];
1257 // adjust the nXOffset relative to glyph cluster start
1258 int c = mnMinCharPos;
1259 if( !pVI->IsRTL() ) // LTR-case
1261 // LTR case: subtract the remainder of the cell from xoffset
1262 int nTmpIndex = mpLogClusters[c];
1263 while( (--c >= pVI->mnMinCharPos)
1264 && (nTmpIndex == mpLogClusters[c]) )
1265 nXOffset -= mpCharWidths[c];
1267 else // RTL-case
1269 // RTL case: add the remainder of the cell from xoffset
1270 int nTmpIndex = mpLogClusters[ pVI->mnEndCharPos - 1 ];
1271 while( (--c >= pVI->mnMinCharPos)
1272 && (nTmpIndex == mpLogClusters[c]) )
1273 nXOffset += mpCharWidths[c];
1275 // adjust the xoffset if justified glyphs are not positioned at their justified positions yet
1276 if( mpJustifications && !bManualCellAlign )
1277 nXOffset += mpJustifications[ nStart ] - mpGlyphAdvances[ nStart ];
1280 // create mpGlyphs2Chars[] if it is needed later
1281 if( pCharPosAry && !mpGlyphs2Chars )
1283 // create and reset the new array
1284 mpGlyphs2Chars = new int[ mnGlyphCapacity ];
1285 static const int CHARPOS_NONE = -1;
1286 for( int i = 0; i < mnGlyphCount; ++i )
1287 mpGlyphs2Chars[i] = CHARPOS_NONE;
1288 // calculate the char->glyph mapping
1289 for( nItem = 0; nItem < mnItemCount; ++nItem )
1291 // ignore invisible visual items
1292 const VisualItem& rVI = mpVisualItems[ nItem ];
1293 if( rVI.IsEmpty() )
1294 continue;
1296 //Resolves: fdo#33090 Ensure that all glyph slots, even if 0-width
1297 //or empty due to combining chars etc, map back to a character
1298 //position so that iterating over glyph slots one at a time for
1299 //glyph fallback can keep context as to what characters are the
1300 //inputs that caused a missing glyph in a given font.
1302 //See: fdo#46923/fdo#46896/fdo#46750 for extra complexities
1304 int dir = 1;
1305 int out = rVI.mnMinCharPos;
1306 if (rVI.IsRTL())
1308 dir = -1;
1309 out = rVI.mnEndCharPos-1;
1311 for(c = rVI.mnMinCharPos; c < rVI.mnEndCharPos; ++c)
1313 int i = out - mnSubStringMin;
1314 mpGlyphs2Chars[i] = c;
1315 out += dir;
1319 // calculate the mapping by using mpLogClusters[]
1320 // mpGlyphs2Chars[] should obey the logical order
1321 // => reversing the loop does this by overwriting higher logicals
1322 for( c = rVI.mnEndCharPos; --c >= rVI.mnMinCharPos; )
1324 int i = mpLogClusters[c] + rVI.mnMinGlyphPos;
1325 mpGlyphs2Chars[i] = c;
1327 // use a heuristic to fill the gaps in the glyphs2chars array
1328 c = !rVI.IsRTL() ? rVI.mnMinCharPos : rVI.mnEndCharPos - 1;
1329 for( int i = rVI.mnMinGlyphPos; i < rVI.mnEndGlyphPos; ++i ) {
1330 if( mpGlyphs2Chars[i] == CHARPOS_NONE )
1331 mpGlyphs2Chars[i] = c;
1332 else
1333 c = mpGlyphs2Chars[i];
1338 // calculate the absolute position of the first result glyph in pixel units
1339 const GOFFSET aGOffset = mpGlyphOffsets[ nStart ];
1340 Point aRelativePos( nXOffset + aGOffset.du, -aGOffset.dv );
1341 rPos = GetDrawPosition( aRelativePos );
1343 // fill the result arrays
1344 int nCount = 0;
1345 while( nCount < nLen )
1347 // prepare return values
1348 sal_GlyphId aGlyphId = mpOutGlyphs[ nStart ];
1349 int nGlyphWidth = pGlyphWidths[ nStart ];
1350 int nCharPos = -1; // no need to determine charpos
1351 if( mpGlyphs2Chars ) // unless explicitly requested+provided
1353 nCharPos = mpGlyphs2Chars[ nStart ];
1354 assert(-1 != nCharPos);
1357 // inject kashida glyphs if needed
1358 if( !mbDisableGlyphInjection
1359 && mpJustifications
1360 && mnMinKashidaWidth
1361 && mpVisualAttrs[nStart].uJustification >= SCRIPT_JUSTIFY_ARABIC_NORMAL )
1363 // prepare draw position adjustment
1364 int nExtraOfs = (nSubIter++) * mnMinKashidaWidth;
1365 // calculate space available for the injected glyphs
1366 nGlyphWidth = mpGlyphAdvances[ nStart ];
1367 const int nExtraWidth = mpJustifications[ nStart ] - nGlyphWidth;
1368 const int nToFillWidth = nExtraWidth - nExtraOfs;
1369 if( (4*nToFillWidth >= mnMinKashidaWidth) // prevent glyph-injection if there is no room
1370 || ((nSubIter > 1) && (nToFillWidth > 0)) ) // unless they can overlap with others
1372 // handle if there is not sufficient room for a full glyph
1373 if( nToFillWidth < mnMinKashidaWidth )
1375 // overlap it with the previously injected glyph if possible
1376 int nOverlap = mnMinKashidaWidth - nToFillWidth;
1377 // else overlap it with both neighboring glyphs
1378 if( nSubIter <= 1 )
1379 nOverlap /= 2;
1380 nExtraOfs -= nOverlap;
1382 nGlyphWidth = mnMinKashidaWidth;
1383 aGlyphId = mnMinKashidaGlyph;
1384 nCharPos = -1;
1386 else
1388 nExtraOfs += nToFillWidth; // at right of cell
1389 nSubIter = 0; // done with glyph injection
1391 if( !bManualCellAlign )
1392 nExtraOfs -= nExtraWidth; // adjust for right-aligned cells
1394 // adjust the draw position for the injected-glyphs case
1395 if( nExtraOfs )
1397 aRelativePos.X() += nExtraOfs;
1398 rPos = GetDrawPosition( aRelativePos );
1402 // update return values
1403 *(pGlyphs++) = aGlyphId;
1404 if( pGlyphAdvances )
1405 *(pGlyphAdvances++) = nGlyphWidth;
1406 if( pCharPosAry )
1407 *(pCharPosAry++) = nCharPos;
1409 // increment counter of returned glyphs
1410 ++nCount;
1412 // reduce code complexity by returning early in glyph-injection case
1413 if( nSubIter != 0 )
1414 break;
1416 // stop after the last visible glyph in this visual item
1417 if( ++nStart >= nEndGlyphPos )
1419 nStart = nNextItemStart;
1420 break;
1423 // RTL-justified glyph positioning is not easy
1424 // simplify the code by just returning only one glyph at a time
1425 if( mpJustifications && pVI->IsRTL() )
1426 break;
1428 // stop when the x-position of the next glyph is unexpected
1429 if( !pGlyphAdvances )
1430 if( (mpGlyphOffsets && (mpGlyphOffsets[nStart].du != aGOffset.du) )
1431 || (mpJustifications && (mpJustifications[nStart] != mpGlyphAdvances[nStart]) ) )
1432 break;
1434 // stop when the y-position of the next glyph is unexpected
1435 if( mpGlyphOffsets && (mpGlyphOffsets[nStart].dv != aGOffset.dv) )
1436 break;
1439 ++nStart;
1440 nStartx8 = (nStart << 8) + nSubIter;
1441 return nCount;
1444 void UniscribeLayout::MoveGlyph( int nStartx8, long nNewXPos )
1446 DBG_ASSERT( !(nStartx8 & 0xff), "USP::MoveGlyph(): glyph injection not disabled!" );
1447 int nStart = nStartx8 >> 8;
1448 if( nStart > mnGlyphCount )
1449 return;
1451 VisualItem* pVI = mpVisualItems;
1452 int nMinGlyphPos = 0, nEndGlyphPos;
1453 if( nStart == 0 ) // nStart==0 for first visible glyph
1455 for( int i = mnItemCount; --i >= 0; ++pVI )
1456 if( GetItemSubrange( *pVI, nMinGlyphPos, nEndGlyphPos ) )
1457 break;
1458 nStart = nMinGlyphPos;
1459 DBG_ASSERT( nStart <= mnGlyphCount, "USPLayout::MoveG overflow" );
1461 else //if( nStart > 0 ) // nStart>0 means absolute_glyphpos+1
1463 --nStart;
1464 for( int i = mnItemCount; --i >= 0; ++pVI )
1465 if( (nStart >= pVI->mnMinGlyphPos) && (nStart < pVI->mnEndGlyphPos) )
1466 break;
1467 bool bRC = GetItemSubrange( *pVI, nMinGlyphPos, nEndGlyphPos );
1468 (void)bRC; // avoid var-not-used warning
1469 DBG_ASSERT( bRC, "USPLayout::MoveG GISR() returned false" );
1472 long nDelta = nNewXPos - pVI->mnXOffset;
1473 if( nStart > nMinGlyphPos )
1475 // move the glyph by expanding its left glyph but ignore dropped glyphs
1476 int i, nLastUndropped = nMinGlyphPos - 1;
1477 for( i = nMinGlyphPos; i < nStart; ++i )
1479 if (mpOutGlyphs[i] != DROPPED_OUTGLYPH)
1481 nDelta -= (mpJustifications)? mpJustifications[ i ] : mpGlyphAdvances[ i ];
1482 nLastUndropped = i;
1485 if (nLastUndropped >= nMinGlyphPos)
1487 mpGlyphAdvances[ nLastUndropped ] += nDelta;
1488 if (mpJustifications) mpJustifications[ nLastUndropped ] += nDelta;
1490 else
1492 pVI->mnXOffset += nDelta;
1495 else
1497 // move the visual item by having an offset
1498 pVI->mnXOffset += nDelta;
1500 // move subsequent items - this often isn't necessary because subsequent
1501 // moves will correct subsequent items. However, if there is a contiguous
1502 // range not involving fallback which spans items, this will be needed
1503 while (++pVI - mpVisualItems < mnItemCount)
1505 pVI->mnXOffset += nDelta;
1509 void UniscribeLayout::DropGlyph( int nStartx8 )
1511 DBG_ASSERT( !(nStartx8 & 0xff), "USP::DropGlyph(): glyph injection not disabled!" );
1512 int nStart = nStartx8 >> 8;
1513 DBG_ASSERT( nStart<=mnGlyphCount, "USPLayout::MoveG nStart overflow" );
1515 if( nStart > 0 ) // nStart>0 means absolute glyph pos + 1
1516 --nStart;
1517 else // nStart<=0 for first visible glyph
1519 VisualItem* pVI = mpVisualItems;
1520 for( int i = mnItemCount, nDummy; --i >= 0; ++pVI )
1521 if( GetItemSubrange( *pVI, nStart, nDummy ) )
1522 break;
1523 DBG_ASSERT( nStart <= mnGlyphCount, "USPLayout::DropG overflow" );
1525 int j = pVI->mnMinGlyphPos;
1526 while (j < mnGlyphCount && mpOutGlyphs[j] == DROPPED_OUTGLYPH) j++;
1527 if (j == nStart)
1529 pVI->mnXOffset += ((mpJustifications)? mpJustifications[nStart] : mpGlyphAdvances[nStart]);
1533 mpOutGlyphs[ nStart ] = DROPPED_OUTGLYPH;
1536 void UniscribeLayout::Simplify( bool /*bIsBase*/ )
1538 static const WCHAR cDroppedGlyph = DROPPED_OUTGLYPH;
1539 int i;
1540 // if there are no dropped glyphs don't bother
1541 for( i = 0; i < mnGlyphCount; ++i )
1542 if( mpOutGlyphs[ i ] == cDroppedGlyph )
1543 break;
1544 if( i >= mnGlyphCount )
1545 return;
1547 // prepare for sparse layout
1548 // => make sure mpGlyphs2Chars[] exists
1549 if( !mpGlyphs2Chars )
1551 mpGlyphs2Chars = new int[ mnGlyphCapacity ];
1552 for( i = 0; i < mnGlyphCount; ++i )
1553 mpGlyphs2Chars[ i ] = -1;
1554 for( int nItem = 0; nItem < mnItemCount; ++nItem )
1556 // skip invisible items
1557 VisualItem& rVI = mpVisualItems[ nItem ];
1558 if( rVI.IsEmpty() )
1559 continue;
1560 for( i = rVI.mnEndCharPos; --i >= rVI.mnMinCharPos; )
1562 int j = mpLogClusters[ i ] + rVI.mnMinGlyphPos;
1563 mpGlyphs2Chars[ j ] = i;
1568 // remove the dropped glyphs
1569 for( int nItem = 0; nItem < mnItemCount; ++nItem )
1571 VisualItem& rVI = mpVisualItems[ nItem ];
1572 if( rVI.IsEmpty() )
1573 continue;
1575 // mark replaced character widths
1576 for( i = rVI.mnMinCharPos; i < rVI.mnEndCharPos; ++i )
1578 int j = mpLogClusters[ i ] + rVI.mnMinGlyphPos;
1579 if( mpOutGlyphs[ j ] == cDroppedGlyph )
1580 mpCharWidths[ i ] = 0;
1583 // handle dropped glyphs at start of visual item
1584 int nMinGlyphPos, nEndGlyphPos, nOrigMinGlyphPos = rVI.mnMinGlyphPos;
1585 GetItemSubrange( rVI, nMinGlyphPos, nEndGlyphPos );
1586 i = nMinGlyphPos;
1587 while( (i < nEndGlyphPos) && (mpOutGlyphs[i] == cDroppedGlyph) )
1589 rVI.mnMinGlyphPos = ++i;
1592 // when all glyphs in item got dropped mark it as empty
1593 if( i >= nEndGlyphPos )
1595 rVI.mnEndGlyphPos = 0;
1596 continue;
1598 // If there are still glyphs in the cluster and mnMinGlyphPos
1599 // has changed then we need to remove the dropped glyphs at start
1600 // to correct logClusters, which is unsigned and relative to the
1601 // item start.
1602 if (rVI.mnMinGlyphPos != nOrigMinGlyphPos)
1604 // drop any glyphs in the visual item outside the range
1605 for (i = nOrigMinGlyphPos; i < nMinGlyphPos; i++)
1606 mpOutGlyphs[ i ] = cDroppedGlyph;
1607 rVI.mnMinGlyphPos = i = nOrigMinGlyphPos;
1610 // handle dropped glyphs in the middle of visual item
1611 for(; i < nEndGlyphPos; ++i )
1612 if( mpOutGlyphs[ i ] == cDroppedGlyph )
1613 break;
1614 int j = i;
1615 while( ++i < nEndGlyphPos )
1617 if( mpOutGlyphs[ i ] == cDroppedGlyph )
1618 continue;
1619 mpOutGlyphs[ j ] = mpOutGlyphs[ i ];
1620 mpGlyphOffsets[ j ] = mpGlyphOffsets[ i ];
1621 mpVisualAttrs[ j ] = mpVisualAttrs[ i ];
1622 mpGlyphAdvances[ j ] = mpGlyphAdvances[ i ];
1623 if( mpJustifications )
1624 mpJustifications[ j ] = mpJustifications[ i ];
1625 const int k = mpGlyphs2Chars[ i ];
1626 mpGlyphs2Chars[ j ] = k;
1627 const int nRelGlyphPos = (j++) - rVI.mnMinGlyphPos;
1628 if( k < 0) // extra glyphs are already mapped
1629 continue;
1630 mpLogClusters[ k ] = static_cast<WORD>(nRelGlyphPos);
1633 rVI.mnEndGlyphPos = j;
1637 void UniscribeLayout::DrawTextImpl(HDC hDC) const
1639 HFONT hOrigFont = DisableFontScaling();
1641 int nBaseClusterOffset = 0;
1642 int nBaseGlyphPos = -1;
1643 for( int nItem = 0; nItem < mnItemCount; ++nItem )
1645 const VisualItem& rVisualItem = mpVisualItems[ nItem ];
1647 // skip if there is nothing to display
1648 int nMinGlyphPos, nEndGlyphPos;
1649 if( !GetItemSubrange( rVisualItem, nMinGlyphPos, nEndGlyphPos ) )
1650 continue;
1652 if( nBaseGlyphPos < 0 )
1654 // adjust draw position relative to cluster start
1655 if( rVisualItem.IsRTL() )
1656 nBaseGlyphPos = nEndGlyphPos - 1;
1657 else
1658 nBaseGlyphPos = nMinGlyphPos;
1660 int i = mnMinCharPos;
1661 while( (--i >= rVisualItem.mnMinCharPos)
1662 && (nBaseGlyphPos == mpLogClusters[i]) )
1663 nBaseClusterOffset += mpCharWidths[i];
1665 if( !rVisualItem.IsRTL() )
1666 nBaseClusterOffset = -nBaseClusterOffset;
1669 // now draw the matching glyphs in this item
1670 Point aRelPos( rVisualItem.mnXOffset + nBaseClusterOffset, 0 );
1671 Point aPos = GetDrawPosition( aRelPos );
1672 SCRIPT_CACHE& rScriptCache = GetScriptCache();
1673 ScriptTextOut(hDC, &rScriptCache,
1674 aPos.X(), aPos.Y(), 0, NULL,
1675 &rVisualItem.mpScriptItem->a, NULL, 0,
1676 mpOutGlyphs + nMinGlyphPos,
1677 nEndGlyphPos - nMinGlyphPos,
1678 mpGlyphAdvances + nMinGlyphPos,
1679 mpJustifications ? mpJustifications + nMinGlyphPos : NULL,
1680 mpGlyphOffsets + nMinGlyphPos);
1683 if( hOrigFont )
1684 DeleteFont(SelectFont(hDC, hOrigFont));
1687 bool UniscribeLayout::CacheGlyphs(SalGraphics& rGraphics) const
1689 static bool bDoGlyphCaching = (std::getenv("SAL_DISABLE_GLYPH_CACHING") == NULL);
1691 if (!bDoGlyphCaching)
1692 return false;
1694 for (int i = 0; i < mnGlyphCount; i++)
1696 if (mrWinFontEntry.GlyphIsCached(mpOutGlyphs[i]))
1697 continue;
1699 if (!mrWinFontEntry.AddChunkOfGlyphs(mpOutGlyphs[i], *this, rGraphics))
1700 return false;
1703 return true;
1706 bool UniscribeLayout::DrawCachedGlyphs(SalGraphics& rGraphics) const
1708 WinSalGraphics& rWinGraphics = static_cast<WinSalGraphics&>(rGraphics);
1709 HDC hDC = rWinGraphics.getHDC();
1711 Rectangle aRect;
1712 GetBoundRect(rGraphics, aRect);
1714 COLORREF color = GetTextColor(hDC);
1715 SalColor salColor = MAKE_SALCOLOR(GetRValue(color), GetGValue(color), GetBValue(color));
1717 WinOpenGLSalGraphicsImpl *pImpl = dynamic_cast<WinOpenGLSalGraphicsImpl*>(rWinGraphics.mpImpl.get());
1718 if (!pImpl)
1719 return false;
1721 pImpl->PreDraw();
1723 // FIXME: This code snippet is mostly copied from the one in
1724 // UniscribeLayout::DrawTextImpl. Should be factored out.
1725 int nBaseClusterOffset = 0;
1726 int nBaseGlyphPos = -1;
1727 for( int nItem = 0; nItem < mnItemCount; ++nItem )
1729 const VisualItem& rVisualItem = mpVisualItems[ nItem ];
1731 // skip if there is nothing to display
1732 int nMinGlyphPos, nEndGlyphPos;
1733 if( !GetItemSubrange( rVisualItem, nMinGlyphPos, nEndGlyphPos ) )
1734 continue;
1736 if( nBaseGlyphPos < 0 )
1738 // adjust draw position relative to cluster start
1739 if( rVisualItem.IsRTL() )
1740 nBaseGlyphPos = nEndGlyphPos - 1;
1741 else
1742 nBaseGlyphPos = nMinGlyphPos;
1744 int i = mnMinCharPos;
1745 while( (--i >= rVisualItem.mnMinCharPos)
1746 && (nBaseGlyphPos == mpLogClusters[i]) )
1747 nBaseClusterOffset += mpCharWidths[i];
1749 if( !rVisualItem.IsRTL() )
1750 nBaseClusterOffset = -nBaseClusterOffset;
1753 // now draw the matching glyphs in this item
1754 Point aRelPos( rVisualItem.mnXOffset + nBaseClusterOffset, 0 );
1755 Point aPos = GetDrawPosition( aRelPos );
1757 int nAdvance = 0;
1759 // This has to be in sync with UniscribeLayout::FillDXArray(), so that
1760 // actual and reported glyph positions (used for e.g. cursor caret
1761 // positioning) match.
1762 const int* pGlyphWidths = mpJustifications ? mpJustifications : mpGlyphAdvances;
1764 for (int i = nMinGlyphPos; i < nEndGlyphPos; i++)
1766 // Ignore dropped glyphs.
1767 if (mpOutGlyphs[i] == DROPPED_OUTGLYPH)
1768 continue;
1770 assert(mrWinFontEntry.GlyphIsCached(mpOutGlyphs[i]));
1772 const OpenGLGlyphCacheChunk& rChunk = mrWinFontEntry.GetCachedGlyphChunkFor(mpOutGlyphs[i]);
1773 const int n = mpOutGlyphs[i] - rChunk.mnFirstGlyph;
1775 if (rChunk.mbVertical)
1777 SalTwoRect a2Rects(rChunk.maLocation[n].Left(), rChunk.maLocation[n].Top(),
1778 rChunk.maLocation[n].getWidth(), rChunk.maLocation[n].getHeight(),
1779 aPos.X(), nAdvance + aPos.Y(),
1780 rChunk.maLocation[n].getWidth(), rChunk.maLocation[n].getHeight()); // ???
1781 pImpl->DrawMask(*rChunk.mpTexture, salColor, a2Rects);
1783 else
1785 SalTwoRect a2Rects(rChunk.maLocation[n].Left(), rChunk.maLocation[n].Top(),
1786 rChunk.maLocation[n].getWidth(), rChunk.maLocation[n].getHeight(),
1787 nAdvance + aPos.X() + mpGlyphOffsets[i].du - rChunk.getExtraOffset(), aPos.Y() + mpGlyphOffsets[i].dv - rChunk.mnAscent - rChunk.getExtraOffset(),
1788 rChunk.maLocation[n].getWidth(), rChunk.maLocation[n].getHeight()); // ???
1789 pImpl->DrawMask(*rChunk.mpTexture, salColor, a2Rects);
1791 nAdvance += pGlyphWidths[i];
1794 pImpl->PostDraw();
1796 return true;
1799 DeviceCoordinate UniscribeLayout::FillDXArray( DeviceCoordinate* pDXArray ) const
1801 // calculate width of the complete layout
1802 long nWidth = mnBaseAdv;
1803 for( int nItem = mnItemCount; --nItem >= 0; )
1805 const VisualItem& rVI = mpVisualItems[ nItem ];
1807 // skip if there is nothing to display
1808 int nMinGlyphPos, nEndGlyphPos;
1809 if( !GetItemSubrange( rVI, nMinGlyphPos, nEndGlyphPos ) )
1810 continue;
1812 // width = xoffset + width of last item
1813 nWidth = rVI.mnXOffset;
1814 const int* pGlyphWidths = mpJustifications ? mpJustifications : mpGlyphAdvances;
1815 for( int i = nMinGlyphPos; i < nEndGlyphPos; ++i )
1816 nWidth += pGlyphWidths[i];
1817 break;
1820 // copy the virtual char widths into pDXArray[]
1821 if( pDXArray )
1822 for( int i = mnMinCharPos; i < mnEndCharPos; ++i )
1823 pDXArray[ i - mnMinCharPos ] = mpCharWidths[ i ];
1825 return nWidth;
1828 sal_Int32 UniscribeLayout::GetTextBreak( DeviceCoordinate nMaxWidth, DeviceCoordinate nCharExtra, int nFactor ) const
1830 long nWidth = 0;
1831 for( int i = mnMinCharPos; i < mnEndCharPos; ++i )
1833 nWidth += mpCharWidths[ i ] * nFactor;
1835 // check if the nMaxWidth still fits the current sub-layout
1836 if( nWidth >= nMaxWidth )
1838 // go back to cluster start
1839 // we have to find the visual item first since the mpLogClusters[]
1840 // needed to find the cluster start is relative to the visual item
1841 int nMinGlyphIndex = 0;
1842 for( int nItem = 0; nItem < mnItemCount; ++nItem )
1844 const VisualItem& rVisualItem = mpVisualItems[ nItem ];
1845 nMinGlyphIndex = rVisualItem.mnMinGlyphPos;
1846 if( (i >= rVisualItem.mnMinCharPos)
1847 && (i < rVisualItem.mnEndCharPos) )
1848 break;
1850 // now go back to the matching cluster start
1853 int nGlyphPos = mpLogClusters[i] + nMinGlyphIndex;
1854 if( 0 != mpVisualAttrs[ nGlyphPos ].fClusterStart )
1855 return i;
1856 } while( --i >= mnMinCharPos );
1858 // if the cluster starts before the start of the visual item
1859 // then set the visual breakpoint before this item
1860 return mnMinCharPos;
1863 // the visual break also depends on the nCharExtra between the characters
1864 nWidth += nCharExtra;
1867 // the whole layout did fit inside the nMaxWidth
1868 return -1;
1871 void UniscribeLayout::GetCaretPositions( int nMaxIdx, long* pCaretXArray ) const
1873 int i;
1874 for( i = 0; i < nMaxIdx; ++i )
1875 pCaretXArray[ i ] = -1;
1876 long* const pGlyphPos = (long*)alloca( (mnGlyphCount+1) * sizeof(long) );
1877 for( i = 0; i <= mnGlyphCount; ++i )
1878 pGlyphPos[ i ] = -1;
1880 long nXPos = 0;
1881 for( int nItem = 0; nItem < mnItemCount; ++nItem )
1883 const VisualItem& rVisualItem = mpVisualItems[ nItem ];
1884 if( rVisualItem.IsEmpty() )
1885 continue;
1887 if (mnLayoutFlags & SalLayoutFlags::ForFallback)
1889 nXPos = rVisualItem.mnXOffset;
1891 // get glyph positions
1892 // TODO: handle when rVisualItem's glyph range is only partially used
1893 for( i = rVisualItem.mnMinGlyphPos; i < rVisualItem.mnEndGlyphPos; ++i )
1895 pGlyphPos[ i ] = nXPos;
1896 nXPos += mpGlyphAdvances[ i ];
1898 // rightmost position of this visualitem
1899 pGlyphPos[ i ] = nXPos;
1901 // convert glyph positions to character positions
1902 i = rVisualItem.mnMinCharPos;
1903 if( i < mnMinCharPos )
1904 i = mnMinCharPos;
1905 for(; (i < rVisualItem.mnEndCharPos) && (i < mnEndCharPos); ++i )
1907 int j = mpLogClusters[ i ] + rVisualItem.mnMinGlyphPos;
1908 int nCurrIdx = (i - mnMinCharPos) * 2;
1909 if( !rVisualItem.IsRTL() )
1911 // normal positions for LTR case
1912 pCaretXArray[ nCurrIdx ] = pGlyphPos[ j ];
1913 pCaretXArray[ nCurrIdx+1 ] = pGlyphPos[ j+1 ];
1915 else
1917 // reverse positions for RTL case
1918 pCaretXArray[ nCurrIdx ] = pGlyphPos[ j+1 ];
1919 pCaretXArray[ nCurrIdx+1 ] = pGlyphPos[ j ];
1924 if (!(mnLayoutFlags & SalLayoutFlags::ForFallback))
1926 nXPos = 0;
1927 // fixup unknown character positions to neighbor
1928 for( i = 0; i < nMaxIdx; ++i )
1930 if( pCaretXArray[ i ] >= 0 )
1931 nXPos = pCaretXArray[ i ];
1932 else
1933 pCaretXArray[ i ] = nXPos;
1938 void UniscribeLayout::AdjustLayout( ImplLayoutArgs& rArgs )
1940 SalLayout::AdjustLayout( rArgs );
1942 // adjust positions if requested
1943 if( rArgs.mpDXArray )
1944 ApplyDXArray( rArgs );
1945 else if( rArgs.mnLayoutWidth )
1946 Justify( rArgs.mnLayoutWidth );
1949 void UniscribeLayout::ApplyDXArray( const ImplLayoutArgs& rArgs )
1951 const long* pDXArray = rArgs.mpDXArray;
1953 // increase char widths in string range to desired values
1954 bool bModified = false;
1955 int nOldWidth = 0;
1956 DBG_ASSERT( mnUnitsPerPixel==1, "UniscribeLayout.mnUnitsPerPixel != 1" );
1957 int i,j;
1958 for( i = mnMinCharPos, j = 0; i < mnEndCharPos; ++i, ++j )
1960 int nNewCharWidth = (pDXArray[j] - nOldWidth);
1961 // TODO: nNewCharWidth *= mnUnitsPerPixel;
1962 if( mpCharWidths[i] != nNewCharWidth )
1964 mpCharWidths[i] = nNewCharWidth;
1965 bModified = true;
1967 nOldWidth = pDXArray[j];
1970 if( !bModified )
1971 return;
1973 // initialize justifications array
1974 mpJustifications = new int[ mnGlyphCapacity ];
1975 for( i = 0; i < mnGlyphCount; ++i )
1976 mpJustifications[ i ] = mpGlyphAdvances[ i ];
1978 // apply new widths to script items
1979 long nXOffset = 0;
1980 for( int nItem = 0; nItem < mnItemCount; ++nItem )
1982 VisualItem& rVisualItem = mpVisualItems[ nItem ];
1984 // set the position of this visual item
1985 rVisualItem.mnXOffset = nXOffset;
1987 // ignore empty visual items
1988 if( rVisualItem.IsEmpty() )
1990 for (i = rVisualItem.mnMinCharPos; i < rVisualItem.mnEndCharPos; i++)
1991 nXOffset += mpCharWidths[i];
1992 continue;
1994 // ignore irrelevant visual items
1995 if( (rVisualItem.mnMinCharPos >= mnEndCharPos)
1996 || (rVisualItem.mnEndCharPos <= mnMinCharPos) )
1997 continue;
1999 // if needed prepare special handling for arabic justification
2000 rVisualItem.mbHasKashidas = false;
2001 if( rVisualItem.IsRTL() )
2003 for( i = rVisualItem.mnMinGlyphPos; i < rVisualItem.mnEndGlyphPos; ++i )
2004 if ( (1U << mpVisualAttrs[i].uJustification) & 0xFF82 ) // any Arabic justification
2005 { // excluding SCRIPT_JUSTIFY_NONE
2006 // yes
2007 rVisualItem.mbHasKashidas = true;
2008 // so prepare for kashida handling
2009 InitKashidaHandling();
2010 break;
2013 if( rVisualItem.HasKashidas() )
2014 for( i = rVisualItem.mnMinGlyphPos; i < rVisualItem.mnEndGlyphPos; ++i )
2016 // TODO: check if we still need this hack after correction of kashida placing?
2017 // (i87688): apparently yes, we still need it!
2018 if ( mpVisualAttrs[i].uJustification == SCRIPT_JUSTIFY_NONE )
2019 // usp decided that justification can't be applied here
2020 // but maybe our Kashida algorithm thinks differently.
2021 // To avoid trouble (gaps within words, last character of
2022 // a word gets a Kashida appended) override this.
2024 // I chose SCRIPT_JUSTIFY_ARABIC_KASHIDA to replace SCRIPT_JUSTIFY_NONE
2025 // just because this previous hack (which I haven't understand, sorry) used
2026 // the same value to replace. Don't know if this is really the best
2027 // thing to do, but it seems to fix things
2028 mpVisualAttrs[i].uJustification = SCRIPT_JUSTIFY_ARABIC_KASHIDA;
2032 // convert virtual charwidths to glyph justification values
2033 HRESULT nRC = ScriptApplyLogicalWidth(
2034 mpCharWidths + rVisualItem.mnMinCharPos,
2035 rVisualItem.mnEndCharPos - rVisualItem.mnMinCharPos,
2036 rVisualItem.mnEndGlyphPos - rVisualItem.mnMinGlyphPos,
2037 mpLogClusters + rVisualItem.mnMinCharPos,
2038 mpVisualAttrs + rVisualItem.mnMinGlyphPos,
2039 mpGlyphAdvances + rVisualItem.mnMinGlyphPos,
2040 &rVisualItem.mpScriptItem->a,
2041 &rVisualItem.maABCWidths,
2042 mpJustifications + rVisualItem.mnMinGlyphPos );
2044 if( nRC != 0 )
2046 delete[] mpJustifications;
2047 mpJustifications = NULL;
2048 break;
2051 // to prepare for the next visual item
2052 // update nXOffset to the next items position
2053 // before the mpJustifications[] array gets modified
2054 int nMinGlyphPos, nEndGlyphPos;
2055 if( GetItemSubrange( rVisualItem, nMinGlyphPos, nEndGlyphPos ) )
2057 for( i = nMinGlyphPos; i < nEndGlyphPos; ++i )
2058 nXOffset += mpJustifications[ i ];
2060 if( rVisualItem.mbHasKashidas )
2061 KashidaItemFix( nMinGlyphPos, nEndGlyphPos );
2064 // workaround needed for older USP versions:
2065 // right align the justification-adjusted glyphs in their cells for RTL-items
2066 // unless the right alignment is done by inserting kashidas
2067 if( bManualCellAlign && rVisualItem.IsRTL() && !rVisualItem.HasKashidas() )
2069 for( i = nMinGlyphPos; i < nEndGlyphPos; ++i )
2071 const int nXOffsetAdjust = mpJustifications[i] - mpGlyphAdvances[i];
2072 // #i99862# skip diacritics, we mustn't add extra justification to diacritics
2073 int nIdxAdd = i - 1;
2074 while( (nIdxAdd >= nMinGlyphPos) && !mpGlyphAdvances[nIdxAdd] )
2075 --nIdxAdd;
2076 if( nIdxAdd < nMinGlyphPos )
2077 rVisualItem.mnXOffset += nXOffsetAdjust;
2078 else
2079 mpJustifications[nIdxAdd] += nXOffsetAdjust;
2080 mpJustifications[i] -= nXOffsetAdjust;
2086 void UniscribeLayout::InitKashidaHandling()
2088 if( mnMinKashidaGlyph != 0 ) // already initialized
2089 return;
2091 mrWinFontEntry.InitKashidaHandling( mhDC );
2092 mnMinKashidaWidth = static_cast<int>(mfFontScale * mrWinFontEntry.GetMinKashidaWidth());
2093 mnMinKashidaGlyph = mrWinFontEntry.GetMinKashidaGlyph();
2096 // adjust the kashida placement matching to the WriterEngine
2097 void UniscribeLayout::KashidaItemFix( int nMinGlyphPos, int nEndGlyphPos )
2099 // workaround needed for all known USP versions:
2100 // ApplyLogicalWidth does not match ScriptJustify behaviour
2101 for( int i = nMinGlyphPos; i < nEndGlyphPos; ++i )
2103 // check for vowels
2104 if( (i > nMinGlyphPos && !mpGlyphAdvances[ i-1 ])
2105 && (1U << mpVisualAttrs[i].uJustification) & 0xFF83 ) // all Arabic justifiction types
2106 { // including SCRIPT_JUSTIFY_NONE
2107 // vowel, we do it like ScriptJustify does
2108 // the vowel gets the extra width
2109 long nSpaceAdded = mpJustifications[ i ] - mpGlyphAdvances[ i ];
2110 mpJustifications [ i ] = mpGlyphAdvances [ i ];
2111 mpJustifications [ i - 1 ] += nSpaceAdded;
2115 // redistribute the widths for kashidas
2116 for( int i = nMinGlyphPos; i < nEndGlyphPos; )
2117 KashidaWordFix ( nMinGlyphPos, nEndGlyphPos, &i );
2120 bool UniscribeLayout::KashidaWordFix ( int nMinGlyphPos, int nEndGlyphPos, int* pnCurrentPos )
2122 // doing pixel work within a word.
2123 // sometimes we have extra pixels and sometimes we miss some pixels to get to mnMinKashidaWidth
2125 // find the next kashida
2126 int nMinPos = *pnCurrentPos;
2127 int nMaxPos = *pnCurrentPos;
2128 for( int i = nMaxPos; i < nEndGlyphPos; ++i )
2130 if( (mpVisualAttrs[ i ].uJustification >= SCRIPT_JUSTIFY_ARABIC_BLANK)
2131 && (mpVisualAttrs[ i ].uJustification < SCRIPT_JUSTIFY_ARABIC_NORMAL) )
2132 break;
2133 nMaxPos = i;
2135 *pnCurrentPos = nMaxPos + 1;
2136 if( nMinPos == nMaxPos )
2137 return false;
2139 // calculate the available space for an extra kashida
2140 long nMaxAdded = 0;
2141 int nKashPos = -1;
2142 for( int i = nMaxPos; i >= nMinPos; --i )
2144 long nSpaceAdded = mpJustifications[ i ] - mpGlyphAdvances[ i ];
2145 if( nSpaceAdded > nMaxAdded )
2147 nKashPos = i;
2148 nMaxAdded = nSpaceAdded;
2152 // return early if there is no need for an extra kashida
2153 if ( nMaxAdded <= 0 )
2154 return false;
2155 // return early if there is not enough space for an extra kashida
2156 if( 2*nMaxAdded < mnMinKashidaWidth )
2157 return false;
2159 // redistribute the extra spacing to the kashida position
2160 for( int i = nMinPos; i <= nMaxPos; ++i )
2162 if( i == nKashPos )
2163 continue;
2164 // everything else should not have extra spacing
2165 long nSpaceAdded = mpJustifications[ i ] - mpGlyphAdvances[ i ];
2166 if( nSpaceAdded > 0 )
2168 mpJustifications[ i ] -= nSpaceAdded;
2169 mpJustifications[ nKashPos ] += nSpaceAdded;
2173 // check if we fulfill minimal kashida width
2174 long nSpaceAdded = mpJustifications[ nKashPos ] - mpGlyphAdvances[ nKashPos ];
2175 if( nSpaceAdded < mnMinKashidaWidth )
2177 // ugly: steal some pixels
2178 long nSteal = 1;
2179 if ( nMaxPos - nMinPos > 0 && ((mnMinKashidaWidth - nSpaceAdded) > (nMaxPos - nMinPos)))
2180 nSteal = (mnMinKashidaWidth - nSpaceAdded) / (nMaxPos - nMinPos);
2181 for( int i = nMinPos; i <= nMaxPos; ++i )
2183 if( i == nKashPos )
2184 continue;
2185 nSteal = std::min( mnMinKashidaWidth - nSpaceAdded, nSteal );
2186 if ( nSteal > 0 )
2188 mpJustifications [ i ] -= nSteal;
2189 mpJustifications [ nKashPos ] += nSteal;
2190 nSpaceAdded += nSteal;
2192 if( nSpaceAdded >= mnMinKashidaWidth )
2193 return true;
2197 // blank padding
2198 long nSpaceMissing = mnMinKashidaWidth - nSpaceAdded;
2199 if( nSpaceMissing > 0 )
2201 // inner glyph: distribute extra space evenly
2202 if( (nMinPos > nMinGlyphPos) && (nMaxPos < nEndGlyphPos - 1) )
2204 mpJustifications [ nKashPos ] += nSpaceMissing;
2205 long nHalfSpace = nSpaceMissing / 2;
2206 mpJustifications [ nMinPos - 1 ] -= nHalfSpace;
2207 mpJustifications [ nMaxPos + 1 ] -= nSpaceMissing - nHalfSpace;
2209 // rightmost: left glyph gets extra space
2210 else if( nMinPos > nMinGlyphPos )
2212 mpJustifications [ nMinPos - 1 ] -= nSpaceMissing;
2213 mpJustifications [ nKashPos ] += nSpaceMissing;
2215 // leftmost: right glyph gets extra space
2216 else if( nMaxPos < nEndGlyphPos - 1 )
2218 mpJustifications [ nKashPos ] += nSpaceMissing;
2219 mpJustifications [ nMaxPos + 1 ] -= nSpaceMissing;
2221 else
2222 return false;
2225 return true;
2228 void UniscribeLayout::Justify( DeviceCoordinate nNewWidth )
2230 DeviceCoordinate nOldWidth = 0;
2231 int i;
2232 for( i = mnMinCharPos; i < mnEndCharPos; ++i )
2233 nOldWidth += mpCharWidths[ i ];
2234 if( nOldWidth <= 0 )
2235 return;
2237 nNewWidth *= mnUnitsPerPixel; // convert into font units
2238 if( nNewWidth == nOldWidth )
2239 return;
2240 // prepare to distribute the extra width evenly among the visual items
2241 const double fStretch = (double)nNewWidth / nOldWidth;
2243 // initialize justifications array
2244 mpJustifications = new int[ mnGlyphCapacity ];
2245 for( i = 0; i < mnGlyphCapacity; ++i )
2246 mpJustifications[ i ] = mpGlyphAdvances[ i ];
2248 // justify stretched script items
2249 long nXOffset = 0;
2250 for( int nItem = 0; nItem < mnItemCount; ++nItem )
2252 VisualItem& rVisualItem = mpVisualItems[ nItem ];
2253 if( rVisualItem.IsEmpty() )
2254 continue;
2256 if( (rVisualItem.mnMinCharPos < mnEndCharPos)
2257 && (rVisualItem.mnEndCharPos > mnMinCharPos) )
2259 long nItemWidth = 0;
2260 for( i = rVisualItem.mnMinCharPos; i < rVisualItem.mnEndCharPos; ++i )
2261 nItemWidth += mpCharWidths[ i ];
2262 nItemWidth = (int)((fStretch - 1.0) * nItemWidth + 0.5);
2264 ScriptJustify(
2265 mpVisualAttrs + rVisualItem.mnMinGlyphPos,
2266 mpGlyphAdvances + rVisualItem.mnMinGlyphPos,
2267 rVisualItem.mnEndGlyphPos - rVisualItem.mnMinGlyphPos,
2268 nItemWidth,
2269 mnMinKashidaWidth,
2270 mpJustifications + rVisualItem.mnMinGlyphPos );
2272 rVisualItem.mnXOffset = nXOffset;
2273 nXOffset += nItemWidth;
2278 bool UniscribeLayout::IsKashidaPosValid ( int nCharPos ) const
2280 // we have to find the visual item first since the mpLogClusters[]
2281 // needed to find the cluster start is relative to to the visual item
2282 int nMinGlyphIndex = -1;
2283 for( int nItem = 0; nItem < mnItemCount; ++nItem )
2285 const VisualItem& rVisualItem = mpVisualItems[ nItem ];
2286 if( (nCharPos >= rVisualItem.mnMinCharPos)
2287 && (nCharPos < rVisualItem.mnEndCharPos) )
2289 nMinGlyphIndex = rVisualItem.mnMinGlyphPos;
2290 break;
2293 // Invalid char pos or leftmost glyph in visual item
2294 if ( nMinGlyphIndex == -1 || !mpLogClusters[ nCharPos ] )
2295 return false;
2297 // This test didn't give the expected results
2298 /* if( mpLogClusters[ nCharPos+1 ] == mpLogClusters[ nCharPos ])
2299 // two chars, one glyph
2300 return false;*/
2302 const int nGlyphPos = mpLogClusters[ nCharPos ] + nMinGlyphIndex;
2303 if( nGlyphPos <= 0 )
2304 return true;
2305 // justification is only allowed if the glyph to the left has not SCRIPT_JUSTIFY_NONE
2306 // and not SCRIPT_JUSTIFY_ARABIC_BLANK
2307 // special case: glyph to the left is vowel (no advance width)
2308 if ( mpVisualAttrs[ nGlyphPos-1 ].uJustification == SCRIPT_JUSTIFY_ARABIC_BLANK
2309 || ( mpVisualAttrs[ nGlyphPos-1 ].uJustification == SCRIPT_JUSTIFY_NONE
2310 && mpGlyphAdvances [ nGlyphPos-1 ] ))
2311 return false;
2312 return true;
2315 #if ENABLE_GRAPHITE
2317 sal_GlyphId GraphiteLayoutWinImpl::getKashidaGlyph(int & rWidth)
2319 rWidth = mrFont.GetMinKashidaWidth();
2320 return mrFont.GetMinKashidaGlyph();
2323 float gr_fontAdvance(const void* appFontHandle, gr_uint16 glyphId)
2325 HDC hDC = reinterpret_cast<HDC>(const_cast<void*>(appFontHandle));
2326 GLYPHMETRICS gm;
2327 const MAT2 mat2 = {{0,1}, {0,0}, {0,0}, {0,1}};
2328 if (GDI_ERROR == GetGlyphOutlineW(hDC, glyphId, GGO_GLYPH_INDEX | GGO_METRICS,
2329 &gm, 0, NULL, &mat2))
2331 return .0f;
2333 return gm.gmCellIncX;
2336 GraphiteWinLayout::GraphiteWinLayout(HDC hDC, const ImplWinFontData& rWFD, ImplWinFontEntry& rWFE, bool bUseOpenGL) throw()
2337 : WinLayout(hDC, rWFD, rWFE, bUseOpenGL), mpFont(NULL),
2338 maImpl(rWFD.GraphiteFace(), rWFE)
2340 // the log font size may differ from the font entry size if scaling is used for large fonts
2341 LOGFONTW aLogFont;
2342 GetObjectW( mhFont, sizeof(LOGFONTW), &aLogFont);
2343 mpFont = gr_make_font_with_advance_fn(static_cast<float>(-aLogFont.lfHeight),
2344 hDC, gr_fontAdvance, rWFD.GraphiteFace());
2345 maImpl.SetFont(mpFont);
2346 const OString aLang = OUStringToOString( LanguageTag::convertToBcp47( rWFE.maFontSelData.meLanguage ),
2347 RTL_TEXTENCODING_ASCII_US);
2348 OString name = OUStringToOString(
2349 rWFE.maFontSelData.maTargetName, RTL_TEXTENCODING_UTF8 );
2350 sal_Int32 nFeat = name.indexOf(grutils::GrFeatureParser::FEAT_PREFIX) + 1;
2351 if (nFeat > 0)
2353 OString aFeat = name.copy(nFeat, name.getLength() - nFeat);
2354 mpFeatures = new grutils::GrFeatureParser(rWFD.GraphiteFace(), aFeat.getStr(), aLang.getStr());
2356 else
2358 mpFeatures = new grutils::GrFeatureParser(rWFD.GraphiteFace(), aLang.getStr());
2360 maImpl.SetFeatures(mpFeatures);
2363 GraphiteWinLayout::~GraphiteWinLayout()
2365 delete mpFeatures;
2366 gr_font_destroy(maImpl.GetFont());
2369 bool GraphiteWinLayout::LayoutText( ImplLayoutArgs & args)
2371 if (args.mnMinCharPos >= args.mnEndCharPos)
2373 maImpl.clear();
2374 return true;
2376 HFONT hUnRotatedFont = 0;
2377 if (args.mnOrientation)
2379 // Graphite gets very confused if the font is rotated
2380 LOGFONTW aLogFont;
2381 GetObjectW( mhFont, sizeof(LOGFONTW), &aLogFont);
2382 aLogFont.lfEscapement = 0;
2383 aLogFont.lfOrientation = 0;
2384 hUnRotatedFont = CreateFontIndirectW( &aLogFont);
2385 SelectFont(mhDC, hUnRotatedFont);
2387 WinLayout::AdjustLayout(args);
2388 maImpl.SetFontScale(WinLayout::mfFontScale);
2389 gr_segment * pSegment = maImpl.CreateSegment(args);
2390 bool bSucceeded = false;
2391 if (pSegment)
2393 // replace the DC on the font within the segment
2394 // create glyph vectors
2395 bSucceeded = maImpl.LayoutGlyphs(args, pSegment);
2396 gr_seg_destroy(pSegment);
2398 if (args.mnOrientation)
2400 // restore the rotated font
2401 SelectFont(mhDC, mhFont);
2402 DeleteObject(hUnRotatedFont);
2404 return bSucceeded;
2407 void GraphiteWinLayout::AdjustLayout(ImplLayoutArgs& rArgs)
2409 WinLayout::AdjustLayout(rArgs);
2410 maImpl.DrawBase() = WinLayout::maDrawBase;
2411 maImpl.DrawOffset() = WinLayout::maDrawOffset;
2412 if ( (rArgs.mnFlags & SalLayoutFlags::BiDiRtl) && rArgs.mpDXArray)
2414 mrWinFontEntry.InitKashidaHandling(mhDC);
2416 maImpl.AdjustLayout(rArgs);
2419 void GraphiteWinLayout::DrawTextImpl(HDC hDC) const
2421 HFONT hOrigFont = DisableFontScaling();
2422 maImpl.DrawBase() = WinLayout::maDrawBase;
2423 maImpl.DrawOffset() = WinLayout::maDrawOffset;
2424 const int MAX_GLYPHS = 2;
2425 sal_GlyphId glyphIntStr[MAX_GLYPHS];
2426 WORD glyphWStr[MAX_GLYPHS];
2427 int glyphIndex = 0;
2428 Point aPos(0,0);
2429 int nGlyphs = 0;
2432 nGlyphs = maImpl.GetNextGlyphs(1, glyphIntStr, aPos, glyphIndex);
2433 if (nGlyphs < 1)
2434 break;
2435 std::copy(glyphIntStr, glyphIntStr + nGlyphs, glyphWStr);
2436 ExtTextOutW(hDC, aPos.X(), aPos.Y(), ETO_GLYPH_INDEX, NULL, (LPCWSTR)&(glyphWStr), nGlyphs, NULL);
2437 } while (nGlyphs);
2438 if( hOrigFont )
2439 DeleteFont(SelectFont(hDC, hOrigFont));
2442 bool GraphiteWinLayout::CacheGlyphs(SalGraphics& /*rGraphics*/) const
2444 return false;
2447 bool GraphiteWinLayout::DrawCachedGlyphs(SalGraphics& /*rGraphics*/) const
2449 return false;
2452 sal_Int32 GraphiteWinLayout::GetTextBreak(DeviceCoordinate nMaxWidth, DeviceCoordinate nCharExtra, int nFactor) const
2454 sal_Int32 nBreak = maImpl.GetTextBreak(nMaxWidth, nCharExtra, nFactor);
2455 return nBreak;
2458 DeviceCoordinate GraphiteWinLayout::FillDXArray( DeviceCoordinate* pDXArray ) const
2460 return maImpl.FillDXArray(pDXArray);
2463 void GraphiteWinLayout::GetCaretPositions( int nArraySize, long* pCaretXArray ) const
2465 maImpl.GetCaretPositions(nArraySize, pCaretXArray);
2468 int GraphiteWinLayout::GetNextGlyphs( int length, sal_GlyphId* glyph_out,
2469 Point& pos_out, int& glyph_slot, DeviceCoordinate* glyph_adv, int* char_index,
2470 const PhysicalFontFace** pFallbackFonts ) const
2472 maImpl.DrawBase() = WinLayout::maDrawBase;
2473 maImpl.DrawOffset() = WinLayout::maDrawOffset;
2474 return maImpl.GetNextGlyphs(length, glyph_out, pos_out, glyph_slot, glyph_adv, char_index, pFallbackFonts);
2477 void GraphiteWinLayout::MoveGlyph( int glyph_idx, long new_x_pos )
2479 maImpl.MoveGlyph(glyph_idx, new_x_pos);
2482 void GraphiteWinLayout::DropGlyph( int glyph_idx )
2484 maImpl.DropGlyph(glyph_idx);
2487 void GraphiteWinLayout::Simplify( bool is_base )
2489 maImpl.Simplify(is_base);
2491 #endif // ENABLE_GRAPHITE
2493 SalLayout* WinSalGraphics::GetTextLayout( ImplLayoutArgs& /*rArgs*/, int nFallbackLevel )
2495 if (!mpWinFontEntry[nFallbackLevel]) return nullptr;
2497 assert(mpWinFontData[nFallbackLevel]);
2499 WinLayout* pWinLayout = NULL;
2501 const ImplWinFontData& rFontFace = *mpWinFontData[ nFallbackLevel ];
2502 ImplWinFontEntry& rFontInstance = *mpWinFontEntry[ nFallbackLevel ];
2504 bool bUseOpenGL = OpenGLHelper::isVCLOpenGLEnabled() && !mbPrinter;
2506 if (!bUspInited)
2507 InitUSP();
2508 #if ENABLE_GRAPHITE
2509 if (rFontFace.SupportsGraphite())
2511 pWinLayout = new GraphiteWinLayout(getHDC(), rFontFace, rFontInstance, bUseOpenGL);
2513 else
2514 #endif // ENABLE_GRAPHITE
2516 pWinLayout = new UniscribeLayout(getHDC(), rFontFace, rFontInstance, bUseOpenGL);
2517 // NOTE: it must be guaranteed that the WinSalGraphics lives longer than
2518 // the created UniscribeLayout, otherwise the data passed into the
2519 // constructor might become invalid too early
2522 if( mfFontScale[nFallbackLevel] != 1.0 )
2523 pWinLayout->SetFontScale( mfFontScale[nFallbackLevel] );
2525 return pWinLayout;
2528 int WinSalGraphics::GetMinKashidaWidth()
2530 if( !mpWinFontEntry[0] )
2531 return 0;
2532 mpWinFontEntry[0]->InitKashidaHandling( getHDC() );
2533 int nMinKashida = static_cast<int>(mfFontScale[0] * mpWinFontEntry[0]->GetMinKashidaWidth());
2534 return nMinKashida;
2537 ImplWinFontEntry::ImplWinFontEntry( FontSelectPattern& rFSD )
2538 : ImplFontEntry( rFSD )
2539 , maWidthMap( 512 )
2540 , mnMinKashidaWidth( -1 )
2541 , mnMinKashidaGlyph( -1 )
2543 maScriptCache = NULL;
2546 ImplWinFontEntry::~ImplWinFontEntry()
2548 if( maScriptCache != NULL )
2549 ScriptFreeCache( &maScriptCache );
2552 bool ImplWinFontEntry::InitKashidaHandling( HDC hDC )
2554 if( mnMinKashidaWidth >= 0 ) // already cached?
2555 return mnMinKashidaWidth;
2557 // initialize the kashida width
2558 mnMinKashidaWidth = 0;
2559 mnMinKashidaGlyph = 0;
2560 if (!bUspInited)
2561 InitUSP();
2563 SCRIPT_FONTPROPERTIES aFontProperties;
2564 aFontProperties.cBytes = sizeof (aFontProperties);
2565 SCRIPT_CACHE& rScriptCache = GetScriptCache();
2566 HRESULT nRC = ScriptGetFontProperties( hDC, &rScriptCache, &aFontProperties );
2567 if( nRC != 0 )
2568 return false;
2569 mnMinKashidaWidth = aFontProperties.iKashidaWidth;
2570 mnMinKashidaGlyph = aFontProperties.wgKashida;
2572 return true;
2575 PhysicalFontFace* ImplWinFontData::Clone() const
2577 #if ENABLE_GRAPHITE
2578 if ( mpGraphiteData )
2579 mpGraphiteData->AddReference();
2580 #endif
2581 PhysicalFontFace* pClone = new ImplWinFontData( *this );
2582 return pClone;
2585 ImplFontEntry* ImplWinFontData::CreateFontInstance( FontSelectPattern& rFSD ) const
2587 ImplFontEntry* pEntry = new ImplWinFontEntry( rFSD );
2588 return pEntry;
2591 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */