1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
3 * This file is part of the LibreOffice project.
5 * This Source Code Form is subject to the terms of the Mozilla Public
6 * License, v. 2.0. If a copy of the MPL was not distributed with this
7 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
9 * This file incorporates work covered by the following license notice:
11 * Licensed to the Apache Software Foundation (ASF) under one or more
12 * contributor license agreements. See the NOTICE file distributed
13 * with this work for additional information regarding copyright
14 * ownership. The ASF licenses this file to you under the Apache
15 * License, Version 2.0 (the "License"); you may not use this file
16 * except in compliance with the License. You may obtain a copy of
17 * the License at http://www.apache.org/licenses/LICENSE-2.0 .
20 #include "winlayout.hxx"
22 #include "osl/module.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>
35 #include "sallayout.hxx"
40 #include <sal/alloca.h>
47 #include <unordered_map>
49 typedef std::unordered_map
<int,int> IntMap
;
52 #include <config_graphite.h>
54 #include <i18nlangtag/languagetag.hxx>
55 #include <graphite_features.hxx>
58 #define DROPPED_OUTGLYPH 0xFFFF
60 #include <config_mingw.h>
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
74 std::vector
<Rectangle
> maLocation
;
75 std::shared_ptr
<OpenGLTexture
> mpTexture
;
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
95 explicit ImplWinFontEntry( FontSelectPattern
& );
96 virtual ~ImplWinFontEntry();
99 // TODO: also add HFONT??? Watch out for issues with too many active fonts...
102 SCRIPT_CACHE
& GetScriptCache() const
103 { return maScriptCache
; }
105 mutable SCRIPT_CACHE maScriptCache
;
106 std::vector
<OpenGLGlyphCacheChunk
> maOpenGLGlyphCache
;
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
; }
118 mutable int mnMinKashidaWidth
;
119 mutable int mnMinKashidaGlyph
;
122 bool GlyphIsCached(int nGlyphIndex
) const;
123 bool AddChunkOfGlyphs(int nGlyphIndex
, const WinLayout
& rLayout
, SalGraphics
& rGraphics
);
124 const OpenGLGlyphCacheChunk
& GetCachedGlyphChunkFor(int nGlyphIndex
) const;
131 char ColorFor(COLORREF aColor
)
133 if (aColor
== RGB(0xFF, 0xFF, 0xFF))
135 else if (aColor
== RGB(0x00, 0x00, 0x00))
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
));
146 SAL_WARN("vcl.gdi", "GetCurrentObject failed: " << WindowsErrorString(GetLastError()));
151 if (!GetObjectW(hBitmap
, sizeof(aBitmap
), &aBitmap
))
153 SAL_WARN("vcl.gdi", "GetObjectW failed: " << WindowsErrorString(GetLastError()));
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)
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
)
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);
185 if (i
+1 != rCache
.cend())
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() )
208 bool ImplWinFontEntry::GlyphIsCached(int nGlyphIndex
) const
210 if (nGlyphIndex
== DROPPED_OUTGLYPH
)
213 for (size_t i
= 0; i
< maOpenGLGlyphCache
.size(); i
++)
214 if (nGlyphIndex
>= maOpenGLGlyphCache
[i
].mnFirstGlyph
&&
215 nGlyphIndex
< maOpenGLGlyphCache
[i
].mnFirstGlyph
+ maOpenGLGlyphCache
[i
].mnGlyphCount
)
221 bool ImplWinFontEntry::AddChunkOfGlyphs(int nGlyphIndex
, const WinLayout
& rLayout
, SalGraphics
& rGraphics
)
223 const int DEFAULT_CHUNK_SIZE
= 20;
225 if (nGlyphIndex
== DROPPED_OUTGLYPH
)
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
)
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
);
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
);
265 SAL_WARN("vcl.gdi", "CreateCompatibleDC failed: " << WindowsErrorString(GetLastError()));
268 HFONT hOrigFont
= static_cast<HFONT
>(SelectObject(hDC
, rLayout
.mhFont
));
269 if (hOrigFont
== NULL
)
271 SAL_WARN("vcl.gdi", "SelectObject failed: " << WindowsErrorString(GetLastError()));
278 if (!GetTextExtentExPointI(hDC
, aGlyphIndices
.data(), nCount
, 0, NULL
, NULL
, &aSize
))
280 SAL_WARN("vcl.gdi", "GetTextExtentExPointI failed: " << WindowsErrorString(GetLastError()));
281 SelectObject(hDC
, hOrigFont
);
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
);
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
);
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
);
317 for (int i
= 0; i
< nCount
; i
++)
319 aDX
[i
] = aABC
[i
].abcB
+ std::abs(aABC
[i
].abcC
);
321 aDX
[0] += std::abs(aABC
[0].abcA
);
323 aDX
[i
] += std::abs(aABC
[i
+1].abcA
);
324 aDX
[i
] += aChunk
.getExtraSpace();
329 if (!GetObjectW(rLayout
.mhFont
, sizeof(aLogfont
), &aLogfont
))
331 SAL_WARN("vcl.gdi", "GetObject failed: " << WindowsErrorString(GetLastError()));
332 SelectObject(hDC
, hOrigFont
);
337 wchar_t sFaceName
[200];
338 int nFaceNameLen
= GetTextFaceW(hDC
, SAL_N_ELEMENTS(sFaceName
), sFaceName
);
341 SAL_WARN("vcl.gdi", "GetTextFace failed: " << WindowsErrorString(GetLastError()));
342 SelectObject(hDC
, hOrigFont
);
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()));
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;
369 nBitmapWidth
= totWidth
;
370 nBitmapHeight
= aSize
.cy
+ aChunk
.getExtraSpace();
371 aChunk
.mbVertical
= false;
374 if (aChunk
.mbVertical
&& aLogfont
.lfEscapement
!= 2700)
377 OpenGLCompatibleDC
aDC(rGraphics
, 0, 0, nBitmapWidth
, nBitmapHeight
);
379 HFONT hNonAntialiasedFont
= NULL
;
382 static bool bNoAntialias
= (std::getenv("VCL_GLYPH_CACHING_HACK_NO_ANTIALIAS") != NULL
);
385 aLogfont
.lfQuality
= NONANTIALIASED_QUALITY
;
386 hNonAntialiasedFont
= CreateFontIndirectW(&aLogfont
);
387 if (hNonAntialiasedFont
== NULL
)
389 SAL_WARN("vcl.gdi", "CreateFontIndirect failed: " << WindowsErrorString(GetLastError()));
395 hOrigFont
= SelectFont(aDC
.getCompatibleHDC(), hNonAntialiasedFont
!= NULL
? hNonAntialiasedFont
: rLayout
.mhFont
);
396 if (hOrigFont
== NULL
)
398 SAL_WARN("vcl.gdi", "SelectObject failed: " << WindowsErrorString(GetLastError()));
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();
409 if (aChunk
.mbVertical
)
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
);
420 aChunk
.maLocation
.resize(nCount
);
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();
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
);
451 SAL_INFO("vcl.gdi.opengl", "this=" << this << " now: " << maOpenGLGlyphCache
);
452 DumpGlyphBitmap(aChunk
, aDC
.getCompatibleHDC());
458 const OpenGLGlyphCacheChunk
& ImplWinFontEntry::GetCachedGlyphChunkFor(int nGlyphIndex
) const
460 auto i
= maOpenGLGlyphCache
.cbegin();
461 while (i
!= maOpenGLGlyphCache
.cend() && nGlyphIndex
>= i
->mnFirstGlyph
+ i
->mnGlyphCount
)
463 assert(i
!= maOpenGLGlyphCache
.cend());
464 assert(nGlyphIndex
>= i
->mnFirstGlyph
&& nGlyphIndex
< i
->mnFirstGlyph
+ i
->mnGlyphCount
);
468 WinLayout::WinLayout(HDC hDC
, const ImplWinFontData
& rWFD
, ImplWinFontEntry
& rWFE
, bool bUseOpenGL
)
470 mhFont( (HFONT
)GetCurrentObject(hDC
,OBJ_FONT
) ),
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 )
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
);
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();
525 // no OpenGL, just classic rendering
528 else if (CacheGlyphs(rGraphics
) &&
529 DrawCachedGlyphs(rGraphics
))
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,
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
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.
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());
593 std::unique_ptr
<OpenGLTexture
> xTexture(aDC
.getTexture());
595 pImpl
->DrawMask(*xTexture
, salColor
, aDC
.getTwoRect());
600 SelectFont(aDC
.getCompatibleHDC(), hOrigFont
);
607 SCRIPT_ITEM
* mpScriptItem
;
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"));
633 rtl_uString
* pModuleURL
= NULL
;
634 osl_getModuleURLFromAddress( pScriptIsComplex
, &pModuleURL
);
635 rtl_uString
* pModuleFileName
= NULL
;
637 osl_getSystemPathFromFileURL( pModuleURL
, &pModuleFileName
);
638 const sal_Unicode
* pModuleFileCStr
= NULL
;
639 if( pModuleFileName
)
640 pModuleFileCStr
= rtl_uString_getStr( pModuleFileName
);
641 if( pModuleFileCStr
)
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;
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;
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
),
670 mpLogClusters( NULL
),
671 mpCharWidths( NULL
),
674 mnGlyphCapacity( 0 ),
675 mpGlyphAdvances( NULL
),
676 mpJustifications( 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
);
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() );
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;
744 // determine relevant substring and work only on it
745 // when Bidi status is unknown we need to look at the whole string though
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 )
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
771 // prepare bigger buffers for another itemization round
772 delete[] mpScriptItems
;
773 mpScriptItems
= NULL
;
774 if( nRC
!= E_OUTOFMEMORY
)
776 if( nItemCapacity
> (nSubStringEnd
- mnSubStringMin
) + 16 )
780 // calculate the order of visual items
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
;
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
)
833 VisualItem
* pVImin
= pVI
++;
834 for(; pVI
< pVIend
; ++pVI
)
835 if( nMaxBidiLevel
>= pVI
->mpScriptItem
->a
.s
.uBidiLevel
)
837 VisualItem
* pVImax
= pVI
++;
839 // reverse order of items in this range
840 while( pVImin
< --pVImax
)
842 VisualItem aVtmp
= *pVImin
;
843 *(pVImin
++) = *pVImax
;
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
];
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
];
865 for( int j
= mnSubStringMin
; j
< nSubStringEnd
; ++j
)
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
;
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
906 int nCharCount
= rVisualItem
.mnEndCharPos
- rVisualItem
.mnMinCharPos
;
907 HRESULT nRC
= ScriptShape( mhDC
, &rScriptCache
,
908 reinterpret_cast<LPCWSTR
>(rArgs
.mpStr
+ rVisualItem
.mnMinCharPos
),
910 mnGlyphCapacity
- rVisualItem
.mnMinGlyphPos
, // problem when >0xFFFF
911 &rVisualItem
.mpScriptItem
->a
,
912 mpOutGlyphs
+ rVisualItem
.mnMinGlyphPos
,
913 mpLogClusters
+ rVisualItem
.mnMinCharPos
,
914 mpVisualAttrs
+ rVisualItem
.mnMinGlyphPos
,
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
)
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
),
935 mnGlyphCapacity
- rVisualItem
.mnMinGlyphPos
,
936 &rVisualItem
.mpScriptItem
->a
,
937 mpOutGlyphs
+ rVisualItem
.mnMinGlyphPos
,
938 mpLogClusters
+ rVisualItem
.mnMinCharPos
,
939 mpVisualAttrs
+ rVisualItem
.mnMinGlyphPos
,
947 // something undefined happened => give up for this visual item
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
] )
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
] )
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();
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;
985 rArgs
.NeedFallback( c
, false );
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;
1000 rArgs
.NeedFallback( c
, true );
1004 } while( ++i
< nGlyphCount
);
1008 // now place the glyphs
1009 nRC
= ScriptPlace( mhDC
, &rScriptCache
,
1010 mpOutGlyphs
+ rVisualItem
.mnMinGlyphPos
,
1012 mpVisualAttrs
+ rVisualItem
.mnMinGlyphPos
,
1013 &rVisualItem
.mpScriptItem
->a
,
1014 mpGlyphAdvances
+ rVisualItem
.mnMinGlyphPos
,
1015 mpGlyphOffsets
+ rVisualItem
.mnMinGlyphPos
,
1016 &rVisualItem
.maABCWidths
);
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
;
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
)
1052 int nEndDropPos
= *(it
++); // end of drop range
1053 if( nEndDropPos
<= rVisualItem
.mnMinCharPos
)
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;
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
)
1079 mpOutGlyphs
[ nGlyphPos
] = DROPPED_OUTGLYPH
;
1080 // until the end of visual item
1081 if( ++nGlyphPos
>= rVisualItem
.mnEndGlyphPos
)
1083 // until the next cluster start
1084 if( mpVisualAttrs
[ nGlyphPos
].fClusterStart
)
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
);
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
) )
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
) )
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
)
1153 if( 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
)
1169 // extend to leftmost glyph of leftmost referenced cluster
1170 for( i
= rMinGlyphPos
; --i
>= rVisualItem
.mnMinGlyphPos
; rMinGlyphPos
= i
)
1171 if( mpVisualAttrs
[i
].fClusterStart
)
1174 rEndGlyphPos
= nMaxGlyphPos
+ 1;
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
1193 // find the visual item for the nStart glyph position
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() )
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
1211 // find matching item
1212 for(; nItem
< mnItemCount
; ++nItem
, ++pVI
)
1213 if( (nStart
>= pVI
->mnMinGlyphPos
)
1214 && (nStart
< pVI
->mnEndGlyphPos
) )
1218 // after the last visual item there are no more glyphs
1219 if( (nItem
>= mnItemCount
) || (nStart
< 0) )
1221 nStartx8
= (mnGlyphCount
+ 1) << 8;
1225 // calculate the first glyph in the next visual item
1226 int nNextItemStart
= mnGlyphCount
;
1227 while( ++nItem
< mnItemCount
)
1229 if( mpVisualItems
[nItem
].IsEmpty() )
1231 nNextItemStart
= mpVisualItems
[nItem
].mnMinGlyphPos
;
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" );
1241 nStartx8
= (mnGlyphCount
+ 1) << 8;
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
];
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
];
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
1305 int out
= rVI
.mnMinCharPos
;
1309 out
= rVI
.mnEndCharPos
-1;
1311 for(c
= rVI
.mnMinCharPos
; c
< rVI
.mnEndCharPos
; ++c
)
1313 int i
= out
- mnSubStringMin
;
1314 mpGlyphs2Chars
[i
] = c
;
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
;
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
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
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
1380 nExtraOfs
-= nOverlap
;
1382 nGlyphWidth
= mnMinKashidaWidth
;
1383 aGlyphId
= mnMinKashidaGlyph
;
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
1397 aRelativePos
.X() += nExtraOfs
;
1398 rPos
= GetDrawPosition( aRelativePos
);
1402 // update return values
1403 *(pGlyphs
++) = aGlyphId
;
1404 if( pGlyphAdvances
)
1405 *(pGlyphAdvances
++) = nGlyphWidth
;
1407 *(pCharPosAry
++) = nCharPos
;
1409 // increment counter of returned glyphs
1412 // reduce code complexity by returning early in glyph-injection case
1416 // stop after the last visible glyph in this visual item
1417 if( ++nStart
>= nEndGlyphPos
)
1419 nStart
= nNextItemStart
;
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() )
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
]) ) )
1434 // stop when the y-position of the next glyph is unexpected
1435 if( mpGlyphOffsets
&& (mpGlyphOffsets
[nStart
].dv
!= aGOffset
.dv
) )
1440 nStartx8
= (nStart
<< 8) + nSubIter
;
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
)
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
) )
1458 nStart
= nMinGlyphPos
;
1459 DBG_ASSERT( nStart
<= mnGlyphCount
, "USPLayout::MoveG overflow" );
1461 else //if( nStart > 0 ) // nStart>0 means absolute_glyphpos+1
1464 for( int i
= mnItemCount
; --i
>= 0; ++pVI
)
1465 if( (nStart
>= pVI
->mnMinGlyphPos
) && (nStart
< pVI
->mnEndGlyphPos
) )
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
];
1485 if (nLastUndropped
>= nMinGlyphPos
)
1487 mpGlyphAdvances
[ nLastUndropped
] += nDelta
;
1488 if (mpJustifications
) mpJustifications
[ nLastUndropped
] += nDelta
;
1492 pVI
->mnXOffset
+= nDelta
;
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
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
) )
1523 DBG_ASSERT( nStart
<= mnGlyphCount
, "USPLayout::DropG overflow" );
1525 int j
= pVI
->mnMinGlyphPos
;
1526 while (j
< mnGlyphCount
&& mpOutGlyphs
[j
] == DROPPED_OUTGLYPH
) j
++;
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
;
1540 // if there are no dropped glyphs don't bother
1541 for( i
= 0; i
< mnGlyphCount
; ++i
)
1542 if( mpOutGlyphs
[ i
] == cDroppedGlyph
)
1544 if( i
>= mnGlyphCount
)
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
];
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
];
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
);
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;
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
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
)
1615 while( ++i
< nEndGlyphPos
)
1617 if( mpOutGlyphs
[ i
] == cDroppedGlyph
)
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
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
) )
1652 if( nBaseGlyphPos
< 0 )
1654 // adjust draw position relative to cluster start
1655 if( rVisualItem
.IsRTL() )
1656 nBaseGlyphPos
= nEndGlyphPos
- 1;
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
);
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
)
1694 for (int i
= 0; i
< mnGlyphCount
; i
++)
1696 if (mrWinFontEntry
.GlyphIsCached(mpOutGlyphs
[i
]))
1699 if (!mrWinFontEntry
.AddChunkOfGlyphs(mpOutGlyphs
[i
], *this, rGraphics
))
1706 bool UniscribeLayout::DrawCachedGlyphs(SalGraphics
& rGraphics
) const
1708 WinSalGraphics
& rWinGraphics
= static_cast<WinSalGraphics
&>(rGraphics
);
1709 HDC hDC
= rWinGraphics
.getHDC();
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());
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
) )
1736 if( nBaseGlyphPos
< 0 )
1738 // adjust draw position relative to cluster start
1739 if( rVisualItem
.IsRTL() )
1740 nBaseGlyphPos
= nEndGlyphPos
- 1;
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
);
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
)
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
);
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
];
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
) )
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
];
1820 // copy the virtual char widths into pDXArray[]
1822 for( int i
= mnMinCharPos
; i
< mnEndCharPos
; ++i
)
1823 pDXArray
[ i
- mnMinCharPos
] = mpCharWidths
[ i
];
1828 sal_Int32
UniscribeLayout::GetTextBreak( DeviceCoordinate nMaxWidth
, DeviceCoordinate nCharExtra
, int nFactor
) const
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
) )
1850 // now go back to the matching cluster start
1853 int nGlyphPos
= mpLogClusters
[i
] + nMinGlyphIndex
;
1854 if( 0 != mpVisualAttrs
[ nGlyphPos
].fClusterStart
)
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
1871 void UniscribeLayout::GetCaretPositions( int nMaxIdx
, long* pCaretXArray
) const
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;
1881 for( int nItem
= 0; nItem
< mnItemCount
; ++nItem
)
1883 const VisualItem
& rVisualItem
= mpVisualItems
[ nItem
];
1884 if( rVisualItem
.IsEmpty() )
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
)
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 ];
1917 // reverse positions for RTL case
1918 pCaretXArray
[ nCurrIdx
] = pGlyphPos
[ j
+1 ];
1919 pCaretXArray
[ nCurrIdx
+1 ] = pGlyphPos
[ j
];
1924 if (!(mnLayoutFlags
& SalLayoutFlags::ForFallback
))
1927 // fixup unknown character positions to neighbor
1928 for( i
= 0; i
< nMaxIdx
; ++i
)
1930 if( pCaretXArray
[ i
] >= 0 )
1931 nXPos
= pCaretXArray
[ i
];
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;
1956 DBG_ASSERT( mnUnitsPerPixel
==1, "UniscribeLayout.mnUnitsPerPixel != 1" );
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
;
1967 nOldWidth
= pDXArray
[j
];
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
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
];
1994 // ignore irrelevant visual items
1995 if( (rVisualItem
.mnMinCharPos
>= mnEndCharPos
)
1996 || (rVisualItem
.mnEndCharPos
<= mnMinCharPos
) )
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
2007 rVisualItem
.mbHasKashidas
= true;
2008 // so prepare for kashida handling
2009 InitKashidaHandling();
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
);
2046 delete[] mpJustifications
;
2047 mpJustifications
= NULL
;
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
] )
2076 if( nIdxAdd
< nMinGlyphPos
)
2077 rVisualItem
.mnXOffset
+= nXOffsetAdjust
;
2079 mpJustifications
[nIdxAdd
] += nXOffsetAdjust
;
2080 mpJustifications
[i
] -= nXOffsetAdjust
;
2086 void UniscribeLayout::InitKashidaHandling()
2088 if( mnMinKashidaGlyph
!= 0 ) // already initialized
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
)
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
) )
2135 *pnCurrentPos
= nMaxPos
+ 1;
2136 if( nMinPos
== nMaxPos
)
2139 // calculate the available space for an extra kashida
2142 for( int i
= nMaxPos
; i
>= nMinPos
; --i
)
2144 long nSpaceAdded
= mpJustifications
[ i
] - mpGlyphAdvances
[ i
];
2145 if( nSpaceAdded
> nMaxAdded
)
2148 nMaxAdded
= nSpaceAdded
;
2152 // return early if there is no need for an extra kashida
2153 if ( nMaxAdded
<= 0 )
2155 // return early if there is not enough space for an extra kashida
2156 if( 2*nMaxAdded
< mnMinKashidaWidth
)
2159 // redistribute the extra spacing to the kashida position
2160 for( int i
= nMinPos
; i
<= nMaxPos
; ++i
)
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
2179 if ( nMaxPos
- nMinPos
> 0 && ((mnMinKashidaWidth
- nSpaceAdded
) > (nMaxPos
- nMinPos
)))
2180 nSteal
= (mnMinKashidaWidth
- nSpaceAdded
) / (nMaxPos
- nMinPos
);
2181 for( int i
= nMinPos
; i
<= nMaxPos
; ++i
)
2185 nSteal
= std::min( mnMinKashidaWidth
- nSpaceAdded
, nSteal
);
2188 mpJustifications
[ i
] -= nSteal
;
2189 mpJustifications
[ nKashPos
] += nSteal
;
2190 nSpaceAdded
+= nSteal
;
2192 if( nSpaceAdded
>= mnMinKashidaWidth
)
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
;
2228 void UniscribeLayout::Justify( DeviceCoordinate nNewWidth
)
2230 DeviceCoordinate nOldWidth
= 0;
2232 for( i
= mnMinCharPos
; i
< mnEndCharPos
; ++i
)
2233 nOldWidth
+= mpCharWidths
[ i
];
2234 if( nOldWidth
<= 0 )
2237 nNewWidth
*= mnUnitsPerPixel
; // convert into font units
2238 if( nNewWidth
== nOldWidth
)
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
2250 for( int nItem
= 0; nItem
< mnItemCount
; ++nItem
)
2252 VisualItem
& rVisualItem
= mpVisualItems
[ nItem
];
2253 if( rVisualItem
.IsEmpty() )
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);
2265 mpVisualAttrs
+ rVisualItem
.mnMinGlyphPos
,
2266 mpGlyphAdvances
+ rVisualItem
.mnMinGlyphPos
,
2267 rVisualItem
.mnEndGlyphPos
- rVisualItem
.mnMinGlyphPos
,
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
;
2293 // Invalid char pos or leftmost glyph in visual item
2294 if ( nMinGlyphIndex
== -1 || !mpLogClusters
[ nCharPos
] )
2297 // This test didn't give the expected results
2298 /* if( mpLogClusters[ nCharPos+1 ] == mpLogClusters[ nCharPos ])
2299 // two chars, one glyph
2302 const int nGlyphPos
= mpLogClusters
[ nCharPos
] + nMinGlyphIndex
;
2303 if( nGlyphPos
<= 0 )
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 ] ))
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
));
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
))
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
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;
2353 OString aFeat
= name
.copy(nFeat
, name
.getLength() - nFeat
);
2354 mpFeatures
= new grutils::GrFeatureParser(rWFD
.GraphiteFace(), aFeat
.getStr(), aLang
.getStr());
2358 mpFeatures
= new grutils::GrFeatureParser(rWFD
.GraphiteFace(), aLang
.getStr());
2360 maImpl
.SetFeatures(mpFeatures
);
2363 GraphiteWinLayout::~GraphiteWinLayout()
2366 gr_font_destroy(maImpl
.GetFont());
2369 bool GraphiteWinLayout::LayoutText( ImplLayoutArgs
& args
)
2371 if (args
.mnMinCharPos
>= args
.mnEndCharPos
)
2376 HFONT hUnRotatedFont
= 0;
2377 if (args
.mnOrientation
)
2379 // Graphite gets very confused if the font is rotated
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;
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
);
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
];
2432 nGlyphs
= maImpl
.GetNextGlyphs(1, glyphIntStr
, aPos
, glyphIndex
);
2435 std::copy(glyphIntStr
, glyphIntStr
+ nGlyphs
, glyphWStr
);
2436 ExtTextOutW(hDC
, aPos
.X(), aPos
.Y(), ETO_GLYPH_INDEX
, NULL
, (LPCWSTR
)&(glyphWStr
), nGlyphs
, NULL
);
2439 DeleteFont(SelectFont(hDC
, hOrigFont
));
2442 bool GraphiteWinLayout::CacheGlyphs(SalGraphics
& /*rGraphics*/) const
2447 bool GraphiteWinLayout::DrawCachedGlyphs(SalGraphics
& /*rGraphics*/) const
2452 sal_Int32
GraphiteWinLayout::GetTextBreak(DeviceCoordinate nMaxWidth
, DeviceCoordinate nCharExtra
, int nFactor
) const
2454 sal_Int32 nBreak
= maImpl
.GetTextBreak(nMaxWidth
, nCharExtra
, nFactor
);
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
;
2509 if (rFontFace
.SupportsGraphite())
2511 pWinLayout
= new GraphiteWinLayout(getHDC(), rFontFace
, rFontInstance
, bUseOpenGL
);
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
] );
2528 int WinSalGraphics::GetMinKashidaWidth()
2530 if( !mpWinFontEntry
[0] )
2532 mpWinFontEntry
[0]->InitKashidaHandling( getHDC() );
2533 int nMinKashida
= static_cast<int>(mfFontScale
[0] * mpWinFontEntry
[0]->GetMinKashidaWidth());
2537 ImplWinFontEntry::ImplWinFontEntry( FontSelectPattern
& rFSD
)
2538 : ImplFontEntry( rFSD
)
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;
2563 SCRIPT_FONTPROPERTIES aFontProperties
;
2564 aFontProperties
.cBytes
= sizeof (aFontProperties
);
2565 SCRIPT_CACHE
& rScriptCache
= GetScriptCache();
2566 HRESULT nRC
= ScriptGetFontProperties( hDC
, &rScriptCache
, &aFontProperties
);
2569 mnMinKashidaWidth
= aFontProperties
.iKashidaWidth
;
2570 mnMinKashidaGlyph
= aFontProperties
.wgKashida
;
2575 PhysicalFontFace
* ImplWinFontData::Clone() const
2578 if ( mpGraphiteData
)
2579 mpGraphiteData
->AddReference();
2581 PhysicalFontFace
* pClone
= new ImplWinFontData( *this );
2585 ImplFontEntry
* ImplWinFontData::CreateFontInstance( FontSelectPattern
& rFSD
) const
2587 ImplFontEntry
* pEntry
= new ImplWinFontEntry( rFSD
);
2591 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */