19f62ea84SAndrew Rist /**************************************************************
2cdf0e10cSrcweir *
39f62ea84SAndrew Rist * Licensed to the Apache Software Foundation (ASF) under one
49f62ea84SAndrew Rist * or more contributor license agreements. See the NOTICE file
59f62ea84SAndrew Rist * distributed with this work for additional information
69f62ea84SAndrew Rist * regarding copyright ownership. The ASF licenses this file
79f62ea84SAndrew Rist * to you under the Apache License, Version 2.0 (the
89f62ea84SAndrew Rist * "License"); you may not use this file except in compliance
99f62ea84SAndrew Rist * with the License. You may obtain a copy of the License at
109f62ea84SAndrew Rist *
119f62ea84SAndrew Rist * http://www.apache.org/licenses/LICENSE-2.0
129f62ea84SAndrew Rist *
139f62ea84SAndrew Rist * Unless required by applicable law or agreed to in writing,
149f62ea84SAndrew Rist * software distributed under the License is distributed on an
159f62ea84SAndrew Rist * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
169f62ea84SAndrew Rist * KIND, either express or implied. See the License for the
179f62ea84SAndrew Rist * specific language governing permissions and limitations
189f62ea84SAndrew Rist * under the License.
199f62ea84SAndrew Rist *
209f62ea84SAndrew Rist *************************************************************/
219f62ea84SAndrew Rist
229f62ea84SAndrew Rist
23cdf0e10cSrcweir
24cdf0e10cSrcweir // MARKER(update_precomp.py): autogen include statement, do not remove
25cdf0e10cSrcweir #include "precompiled_vcl.hxx"
26cdf0e10cSrcweir
27cdf0e10cSrcweir #include "rtl/ustring.hxx"
28cdf0e10cSrcweir
29cdf0e10cSrcweir #include "osl/module.h"
30cdf0e10cSrcweir #include "osl/file.h"
31cdf0e10cSrcweir
32cdf0e10cSrcweir #include "tools/svwin.h"
33cdf0e10cSrcweir
34cdf0e10cSrcweir #include "vcl/svapp.hxx"
35cdf0e10cSrcweir
36cdf0e10cSrcweir #include "win/salgdi.h"
37cdf0e10cSrcweir #include "win/saldata.hxx"
38cdf0e10cSrcweir
39cdf0e10cSrcweir // for GetMirroredChar
40cdf0e10cSrcweir #include "sft.hxx"
41cdf0e10cSrcweir #include "sallayout.hxx"
42cdf0e10cSrcweir
43cdf0e10cSrcweir #include <cstdio>
44cdf0e10cSrcweir #include <malloc.h>
45cdf0e10cSrcweir #ifndef __MINGW32__
46cdf0e10cSrcweir #define alloca _alloca
47cdf0e10cSrcweir #endif
48cdf0e10cSrcweir
49cdf0e10cSrcweir #ifdef GCP_KERN_HACK
50cdf0e10cSrcweir #include <algorithm>
51cdf0e10cSrcweir #endif // GCP_KERN_HACK
52cdf0e10cSrcweir
53cdf0e10cSrcweir
54cdf0e10cSrcweir #define USE_UNISCRIBE
55cdf0e10cSrcweir #ifdef USE_UNISCRIBE
56cdf0e10cSrcweir #include <Usp10.h>
57cdf0e10cSrcweir #include <ShLwApi.h>
58cdf0e10cSrcweir #include <winver.h>
59cdf0e10cSrcweir #endif // USE_UNISCRIBE
60cdf0e10cSrcweir
61cdf0e10cSrcweir #include <hash_map>
62cdf0e10cSrcweir #include <set>
63cdf0e10cSrcweir
64cdf0e10cSrcweir typedef std::hash_map<int,int> IntMap;
65cdf0e10cSrcweir typedef std::set<int> IntSet;
66cdf0e10cSrcweir
67cdf0e10cSrcweir // Graphite headers
68cdf0e10cSrcweir #ifdef ENABLE_GRAPHITE
69cdf0e10cSrcweir #include <i18npool/mslangid.hxx>
70cdf0e10cSrcweir #include <graphite/GrClient.h>
71cdf0e10cSrcweir #include <graphite/WinFont.h>
72cdf0e10cSrcweir #include <graphite/Segment.h>
73cdf0e10cSrcweir #include <graphite_layout.hxx>
74cdf0e10cSrcweir #include <graphite_cache.hxx>
75cdf0e10cSrcweir #include <graphite_features.hxx>
76cdf0e10cSrcweir #endif
77cdf0e10cSrcweir
78cdf0e10cSrcweir #define DROPPED_OUTGLYPH 0xFFFF
79cdf0e10cSrcweir
80cdf0e10cSrcweir using namespace rtl;
81cdf0e10cSrcweir
82cdf0e10cSrcweir // =======================================================================
83cdf0e10cSrcweir
84cdf0e10cSrcweir // win32 specific physical font instance
85cdf0e10cSrcweir class ImplWinFontEntry : public ImplFontEntry
86cdf0e10cSrcweir {
87cdf0e10cSrcweir public:
88fed8d294SHerbert Dürr explicit ImplWinFontEntry( ImplFontSelectData& );
89fed8d294SHerbert Dürr virtual ~ImplWinFontEntry();
90cdf0e10cSrcweir
91cdf0e10cSrcweir private:
92cdf0e10cSrcweir // TODO: also add HFONT??? Watch out for issues with too many active fonts...
93cdf0e10cSrcweir
94cdf0e10cSrcweir #ifdef GCP_KERN_HACK
95cdf0e10cSrcweir public:
96cdf0e10cSrcweir bool HasKernData() const;
97cdf0e10cSrcweir void SetKernData( int, const KERNINGPAIR* );
98cdf0e10cSrcweir int GetKerning( sal_Unicode, sal_Unicode ) const;
99cdf0e10cSrcweir private:
100cdf0e10cSrcweir KERNINGPAIR* mpKerningPairs;
101cdf0e10cSrcweir int mnKerningPairs;
102cdf0e10cSrcweir #endif // GCP_KERN_HACK
103cdf0e10cSrcweir
104cdf0e10cSrcweir #ifdef USE_UNISCRIBE
105cdf0e10cSrcweir public:
GetScriptCache() const106cdf0e10cSrcweir SCRIPT_CACHE& GetScriptCache() const
107cdf0e10cSrcweir { return maScriptCache; }
108cdf0e10cSrcweir private:
109cdf0e10cSrcweir mutable SCRIPT_CACHE maScriptCache;
110cdf0e10cSrcweir #endif // USE_UNISCRIBE
111cdf0e10cSrcweir
112cdf0e10cSrcweir public:
113cdf0e10cSrcweir int GetCachedGlyphWidth( int nCharCode ) const;
114cdf0e10cSrcweir void CacheGlyphWidth( int nCharCode, int nCharWidth );
115cdf0e10cSrcweir
116cdf0e10cSrcweir bool InitKashidaHandling( HDC );
GetMinKashidaWidth() const117cdf0e10cSrcweir int GetMinKashidaWidth() const { return mnMinKashidaWidth; }
GetMinKashidaGlyph() const118cdf0e10cSrcweir int GetMinKashidaGlyph() const { return mnMinKashidaGlyph; }
119cdf0e10cSrcweir
120cdf0e10cSrcweir private:
121cdf0e10cSrcweir IntMap maWidthMap;
122cdf0e10cSrcweir mutable int mnMinKashidaWidth;
123cdf0e10cSrcweir mutable int mnMinKashidaGlyph;
124cdf0e10cSrcweir };
125cdf0e10cSrcweir
126cdf0e10cSrcweir // -----------------------------------------------------------------------
127cdf0e10cSrcweir
CacheGlyphWidth(int nCharCode,int nCharWidth)128cdf0e10cSrcweir inline void ImplWinFontEntry::CacheGlyphWidth( int nCharCode, int nCharWidth )
129cdf0e10cSrcweir {
130cdf0e10cSrcweir maWidthMap[ nCharCode ] = nCharWidth;
131cdf0e10cSrcweir }
132cdf0e10cSrcweir
GetCachedGlyphWidth(int nCharCode) const133cdf0e10cSrcweir inline int ImplWinFontEntry::GetCachedGlyphWidth( int nCharCode ) const
134cdf0e10cSrcweir {
135cdf0e10cSrcweir IntMap::const_iterator it = maWidthMap.find( nCharCode );
136cdf0e10cSrcweir if( it == maWidthMap.end() )
137cdf0e10cSrcweir return -1;
138cdf0e10cSrcweir return it->second;
139cdf0e10cSrcweir }
140cdf0e10cSrcweir
141cdf0e10cSrcweir // =======================================================================
142cdf0e10cSrcweir
143cdf0e10cSrcweir class WinLayout : public SalLayout
144cdf0e10cSrcweir {
145cdf0e10cSrcweir public:
146cdf0e10cSrcweir WinLayout( HDC, const ImplWinFontData&, ImplWinFontEntry& );
147cdf0e10cSrcweir virtual void InitFont() const;
SetFontScale(float f)148cdf0e10cSrcweir void SetFontScale( float f ) { mfFontScale = f; }
GetFontScale() const149cdf0e10cSrcweir float GetFontScale() const { return mfFontScale; }
150cdf0e10cSrcweir HFONT DisableFontScaling( void) const;
151cdf0e10cSrcweir
152cdf0e10cSrcweir #ifdef USE_UNISCRIBE
GetScriptCache() const153cdf0e10cSrcweir SCRIPT_CACHE& GetScriptCache() const
154cdf0e10cSrcweir { return mrWinFontEntry.GetScriptCache(); }
155cdf0e10cSrcweir #endif // USE_UNISCRIBE
156cdf0e10cSrcweir
157cdf0e10cSrcweir protected:
158cdf0e10cSrcweir HDC mhDC; // WIN32 device handle
159cdf0e10cSrcweir HFONT mhFont; // WIN32 font handle
160cdf0e10cSrcweir int mnBaseAdv; // x-offset relative to Layout origin
161cdf0e10cSrcweir float mfFontScale; // allows metrics emulation of huge font sizes
162cdf0e10cSrcweir
163cdf0e10cSrcweir const ImplWinFontData& mrWinFontData;
164cdf0e10cSrcweir ImplWinFontEntry& mrWinFontEntry;
165cdf0e10cSrcweir };
166cdf0e10cSrcweir
167cdf0e10cSrcweir // =======================================================================
168cdf0e10cSrcweir
169cdf0e10cSrcweir class SimpleWinLayout : public WinLayout
170cdf0e10cSrcweir {
171cdf0e10cSrcweir public:
172cdf0e10cSrcweir SimpleWinLayout( HDC, BYTE nCharSet, const ImplWinFontData&, ImplWinFontEntry& );
173cdf0e10cSrcweir virtual ~SimpleWinLayout();
174cdf0e10cSrcweir
175cdf0e10cSrcweir virtual bool LayoutText( ImplLayoutArgs& );
176cdf0e10cSrcweir virtual void AdjustLayout( ImplLayoutArgs& );
177cdf0e10cSrcweir virtual void DrawText( SalGraphics& ) const;
178cdf0e10cSrcweir
179cdf0e10cSrcweir virtual int GetNextGlyphs( int nLen, sal_GlyphId* pGlyphs, Point& rPos, int&,
180cdf0e10cSrcweir sal_Int32* pGlyphAdvances, int* pCharIndexes ) const;
181cdf0e10cSrcweir
182cdf0e10cSrcweir virtual long FillDXArray( long* pDXArray ) const;
183cdf0e10cSrcweir virtual int GetTextBreak( long nMaxWidth, long nCharExtra, int nFactor ) const;
184cdf0e10cSrcweir virtual void GetCaretPositions( int nArraySize, long* pCaretXArray ) const;
185cdf0e10cSrcweir
186cdf0e10cSrcweir // for glyph+font+script fallback
187cdf0e10cSrcweir virtual void MoveGlyph( int nStart, long nNewXPos );
188cdf0e10cSrcweir virtual void DropGlyph( int nStart );
189cdf0e10cSrcweir virtual void Simplify( bool bIsBase );
190cdf0e10cSrcweir
191cdf0e10cSrcweir protected:
192cdf0e10cSrcweir void Justify( long nNewWidth );
193cdf0e10cSrcweir void ApplyDXArray( const ImplLayoutArgs& );
194cdf0e10cSrcweir
195cdf0e10cSrcweir private:
196cdf0e10cSrcweir int mnGlyphCount;
197cdf0e10cSrcweir int mnCharCount;
198cdf0e10cSrcweir WCHAR* mpOutGlyphs;
199cdf0e10cSrcweir int* mpGlyphAdvances; // if possible this is shared with mpGlyphAdvances[]
200cdf0e10cSrcweir int* mpGlyphOrigAdvs;
201cdf0e10cSrcweir int* mpCharWidths; // map rel char pos to char width
202cdf0e10cSrcweir int* mpChars2Glyphs; // map rel char pos to abs glyph pos
203cdf0e10cSrcweir int* mpGlyphs2Chars; // map abs glyph pos to abs char pos
204cdf0e10cSrcweir bool* mpGlyphRTLFlags; // BiDi status for glyphs: true=>RTL
205cdf0e10cSrcweir mutable long mnWidth;
206cdf0e10cSrcweir bool mbDisableGlyphs;
207cdf0e10cSrcweir
208cdf0e10cSrcweir int mnNotdefWidth;
209cdf0e10cSrcweir BYTE mnCharSet;
210cdf0e10cSrcweir };
211cdf0e10cSrcweir
212cdf0e10cSrcweir // =======================================================================
213cdf0e10cSrcweir
WinLayout(HDC hDC,const ImplWinFontData & rWFD,ImplWinFontEntry & rWFE)214cdf0e10cSrcweir WinLayout::WinLayout( HDC hDC, const ImplWinFontData& rWFD, ImplWinFontEntry& rWFE )
215cdf0e10cSrcweir : mhDC( hDC ),
216cdf0e10cSrcweir mhFont( (HFONT)::GetCurrentObject(hDC,OBJ_FONT) ),
217cdf0e10cSrcweir mnBaseAdv( 0 ),
218cdf0e10cSrcweir mfFontScale( 1.0 ),
219cdf0e10cSrcweir mrWinFontData( rWFD ),
220cdf0e10cSrcweir mrWinFontEntry( rWFE )
221cdf0e10cSrcweir {}
222cdf0e10cSrcweir
223cdf0e10cSrcweir // -----------------------------------------------------------------------
224cdf0e10cSrcweir
InitFont() const225cdf0e10cSrcweir void WinLayout::InitFont() const
226cdf0e10cSrcweir {
227cdf0e10cSrcweir ::SelectObject( mhDC, mhFont );
228cdf0e10cSrcweir }
229cdf0e10cSrcweir
230cdf0e10cSrcweir // -----------------------------------------------------------------------
231cdf0e10cSrcweir
232cdf0e10cSrcweir // Using reasonably sized fonts to emulate huge fonts works around
233cdf0e10cSrcweir // a lot of problems in printer and display drivers. Huge fonts are
234cdf0e10cSrcweir // mostly used by high resolution reference devices which are never
235cdf0e10cSrcweir // painted to anyway. In the rare case that a huge font needs to be
236cdf0e10cSrcweir // displayed somewhere then the workaround doesn't help anymore.
237cdf0e10cSrcweir // If the drivers fail silently for huge fonts, so be it...
DisableFontScaling() const238cdf0e10cSrcweir HFONT WinLayout::DisableFontScaling() const
239cdf0e10cSrcweir {
240cdf0e10cSrcweir if( mfFontScale == 1.0 )
241cdf0e10cSrcweir return 0;
242cdf0e10cSrcweir
243cdf0e10cSrcweir LOGFONTW aLogFont;
244cdf0e10cSrcweir ::GetObjectW( mhFont, sizeof(LOGFONTW), &aLogFont);
245cdf0e10cSrcweir aLogFont.lfHeight = (LONG)(mfFontScale * aLogFont.lfHeight);
246cdf0e10cSrcweir aLogFont.lfWidth = (LONG)(mfFontScale * aLogFont.lfWidth);
247cdf0e10cSrcweir HFONT hHugeFont = ::CreateFontIndirectW( &aLogFont);
248cdf0e10cSrcweir if( !hHugeFont )
249cdf0e10cSrcweir return 0;
250cdf0e10cSrcweir
251cdf0e10cSrcweir return SelectFont( mhDC, hHugeFont );
252cdf0e10cSrcweir }
253cdf0e10cSrcweir
254cdf0e10cSrcweir // =======================================================================
255cdf0e10cSrcweir
SimpleWinLayout(HDC hDC,BYTE nCharSet,const ImplWinFontData & rWinFontData,ImplWinFontEntry & rWinFontEntry)256cdf0e10cSrcweir SimpleWinLayout::SimpleWinLayout( HDC hDC, BYTE nCharSet,
257cdf0e10cSrcweir const ImplWinFontData& rWinFontData, ImplWinFontEntry& rWinFontEntry )
258cdf0e10cSrcweir : WinLayout( hDC, rWinFontData, rWinFontEntry ),
259cdf0e10cSrcweir mnGlyphCount( 0 ),
260cdf0e10cSrcweir mnCharCount( 0 ),
261cdf0e10cSrcweir mpOutGlyphs( NULL ),
262cdf0e10cSrcweir mpGlyphAdvances( NULL ),
263cdf0e10cSrcweir mpGlyphOrigAdvs( NULL ),
264cdf0e10cSrcweir mpCharWidths( NULL ),
265cdf0e10cSrcweir mpChars2Glyphs( NULL ),
266cdf0e10cSrcweir mpGlyphs2Chars( NULL ),
267cdf0e10cSrcweir mpGlyphRTLFlags( NULL ),
268cdf0e10cSrcweir mnWidth( 0 ),
269cdf0e10cSrcweir mnNotdefWidth( -1 ),
270cdf0e10cSrcweir mnCharSet( nCharSet ),
271cdf0e10cSrcweir mbDisableGlyphs( false )
272cdf0e10cSrcweir {
273cdf0e10cSrcweir mbDisableGlyphs = true;
274cdf0e10cSrcweir }
275cdf0e10cSrcweir
276cdf0e10cSrcweir // -----------------------------------------------------------------------
277cdf0e10cSrcweir
~SimpleWinLayout()278cdf0e10cSrcweir SimpleWinLayout::~SimpleWinLayout()
279cdf0e10cSrcweir {
280cdf0e10cSrcweir delete[] mpGlyphRTLFlags;
281cdf0e10cSrcweir delete[] mpGlyphs2Chars;
282cdf0e10cSrcweir delete[] mpChars2Glyphs;
283cdf0e10cSrcweir if( mpCharWidths != mpGlyphAdvances )
284cdf0e10cSrcweir delete[] mpCharWidths;
285cdf0e10cSrcweir delete[] mpGlyphOrigAdvs;
286cdf0e10cSrcweir delete[] mpGlyphAdvances;
287cdf0e10cSrcweir delete[] mpOutGlyphs;
288cdf0e10cSrcweir }
289cdf0e10cSrcweir
290cdf0e10cSrcweir // -----------------------------------------------------------------------
291cdf0e10cSrcweir
LayoutText(ImplLayoutArgs & rArgs)292cdf0e10cSrcweir bool SimpleWinLayout::LayoutText( ImplLayoutArgs& rArgs )
293cdf0e10cSrcweir {
294cdf0e10cSrcweir // prepare layout
295cdf0e10cSrcweir // TODO: fix case when recyclying old SimpleWinLayout object
296cdf0e10cSrcweir mbDisableGlyphs |= ((rArgs.mnFlags & SAL_LAYOUT_DISABLE_GLYPH_PROCESSING) != 0);
297cdf0e10cSrcweir mnCharCount = rArgs.mnEndCharPos - rArgs.mnMinCharPos;
298cdf0e10cSrcweir
299cdf0e10cSrcweir if( !mbDisableGlyphs )
300cdf0e10cSrcweir {
301cdf0e10cSrcweir // Win32 glyph APIs have serious problems with vertical layout
302cdf0e10cSrcweir // => workaround is to use the unicode methods then
303cdf0e10cSrcweir if( rArgs.mnFlags & SAL_LAYOUT_VERTICAL )
304cdf0e10cSrcweir mbDisableGlyphs = true;
305cdf0e10cSrcweir else
306cdf0e10cSrcweir // use cached value from font face
307cdf0e10cSrcweir mbDisableGlyphs = mrWinFontData.IsGlyphApiDisabled();
308cdf0e10cSrcweir }
309cdf0e10cSrcweir
310cdf0e10cSrcweir // TODO: use a cached value for bDisableAsianKern from upper layers
311cdf0e10cSrcweir if( rArgs.mnFlags & SAL_LAYOUT_KERNING_ASIAN )
312cdf0e10cSrcweir {
313cdf0e10cSrcweir TEXTMETRICA aTextMetricA;
314cdf0e10cSrcweir if( ::GetTextMetricsA( mhDC, &aTextMetricA )
315cdf0e10cSrcweir && !(aTextMetricA.tmPitchAndFamily & TMPF_FIXED_PITCH) && !(aTextMetricA.tmCharSet == 0x86) )
316cdf0e10cSrcweir rArgs.mnFlags &= ~SAL_LAYOUT_KERNING_ASIAN;
317cdf0e10cSrcweir }
318cdf0e10cSrcweir
319cdf0e10cSrcweir // layout text
320cdf0e10cSrcweir int i, j;
321cdf0e10cSrcweir
322cdf0e10cSrcweir mnGlyphCount = 0;
323cdf0e10cSrcweir bool bVertical = (rArgs.mnFlags & SAL_LAYOUT_VERTICAL) != 0;
324cdf0e10cSrcweir
325cdf0e10cSrcweir // count the number of chars to process if no RTL run
326cdf0e10cSrcweir rArgs.ResetPos();
327cdf0e10cSrcweir bool bHasRTL = false;
328cdf0e10cSrcweir while( rArgs.GetNextRun( &i, &j, &bHasRTL ) && !bHasRTL )
329cdf0e10cSrcweir mnGlyphCount += j - i;
330cdf0e10cSrcweir
331cdf0e10cSrcweir // if there are RTL runs we need room to remember individual BiDi flags
332cdf0e10cSrcweir if( bHasRTL )
333cdf0e10cSrcweir {
334cdf0e10cSrcweir mpGlyphRTLFlags = new bool[ mnCharCount ];
335cdf0e10cSrcweir for( i = 0; i < mnCharCount; ++i )
336cdf0e10cSrcweir mpGlyphRTLFlags[i] = false;
337cdf0e10cSrcweir }
338cdf0e10cSrcweir
339cdf0e10cSrcweir // rewrite the logical string if needed to prepare for the API calls
340cdf0e10cSrcweir const sal_Unicode* pBidiStr = rArgs.mpStr + rArgs.mnMinCharPos;
341cdf0e10cSrcweir if( (mnGlyphCount != mnCharCount) || bVertical )
342cdf0e10cSrcweir {
343cdf0e10cSrcweir // we need to rewrite the pBidiStr when any of
344cdf0e10cSrcweir // - BiDirectional layout
345cdf0e10cSrcweir // - vertical layout
346cdf0e10cSrcweir // - partial runs (e.g. with control chars or for glyph fallback)
347cdf0e10cSrcweir // are involved
348cdf0e10cSrcweir sal_Unicode* pRewrittenStr = (sal_Unicode*)alloca( mnCharCount * sizeof(sal_Unicode) );
349cdf0e10cSrcweir pBidiStr = pRewrittenStr;
350cdf0e10cSrcweir
351cdf0e10cSrcweir // note: glyph to char mapping is relative to first character
352cdf0e10cSrcweir mpChars2Glyphs = new int[ mnCharCount ];
353cdf0e10cSrcweir mpGlyphs2Chars = new int[ mnCharCount ];
354cdf0e10cSrcweir for( i = 0; i < mnCharCount; ++i )
355cdf0e10cSrcweir mpChars2Glyphs[i] = mpGlyphs2Chars[i] = -1;
356cdf0e10cSrcweir
357cdf0e10cSrcweir mnGlyphCount = 0;
358cdf0e10cSrcweir rArgs.ResetPos();
359cdf0e10cSrcweir bool bIsRTL = false;
360cdf0e10cSrcweir while( rArgs.GetNextRun( &i, &j, &bIsRTL ) )
361cdf0e10cSrcweir {
362cdf0e10cSrcweir do
363cdf0e10cSrcweir {
364cdf0e10cSrcweir // get the next leftmost character in this run
365cdf0e10cSrcweir int nCharPos = bIsRTL ? --j : i++;
366cdf0e10cSrcweir sal_UCS4 cChar = rArgs.mpStr[ nCharPos ];
367cdf0e10cSrcweir
368cdf0e10cSrcweir // in the RTL case mirror the character and remember its RTL status
369cdf0e10cSrcweir if( bIsRTL )
370cdf0e10cSrcweir {
371cdf0e10cSrcweir cChar = ::GetMirroredChar( cChar );
372cdf0e10cSrcweir mpGlyphRTLFlags[ mnGlyphCount ] = true;
373cdf0e10cSrcweir }
374cdf0e10cSrcweir
375cdf0e10cSrcweir // for vertical writing use vertical alternatives
376cdf0e10cSrcweir if( bVertical )
377cdf0e10cSrcweir {
378cdf0e10cSrcweir sal_UCS4 cVert = ::GetVerticalChar( cChar );
379cdf0e10cSrcweir if( cVert )
380cdf0e10cSrcweir cChar = cVert;
381cdf0e10cSrcweir }
382cdf0e10cSrcweir
383cdf0e10cSrcweir // rewrite the original string
384cdf0e10cSrcweir // update the mappings between original and rewritten string
385cdf0e10cSrcweir // TODO: support surrogates in rewritten strings
386cdf0e10cSrcweir pRewrittenStr[ mnGlyphCount ] = static_cast<sal_Unicode>(cChar);
387cdf0e10cSrcweir mpGlyphs2Chars[ mnGlyphCount ] = nCharPos;
388cdf0e10cSrcweir mpChars2Glyphs[ nCharPos - rArgs.mnMinCharPos ] = mnGlyphCount;
389cdf0e10cSrcweir ++mnGlyphCount;
390cdf0e10cSrcweir } while( i < j );
391cdf0e10cSrcweir }
392cdf0e10cSrcweir }
393cdf0e10cSrcweir
394cdf0e10cSrcweir mpOutGlyphs = new WCHAR[ mnGlyphCount ];
395cdf0e10cSrcweir mpGlyphAdvances = new int[ mnGlyphCount ];
396cdf0e10cSrcweir
397cdf0e10cSrcweir if( rArgs.mnFlags & (SAL_LAYOUT_KERNING_PAIRS | SAL_LAYOUT_KERNING_ASIAN) )
398cdf0e10cSrcweir mpGlyphOrigAdvs = new int[ mnGlyphCount ];
399cdf0e10cSrcweir
400cdf0e10cSrcweir #ifndef GCP_KERN_HACK
401cdf0e10cSrcweir DWORD nGcpOption = 0;
402cdf0e10cSrcweir // enable kerning if requested
403cdf0e10cSrcweir if( rArgs.mnFlags & SAL_LAYOUT_KERNING_PAIRS )
404cdf0e10cSrcweir nGcpOption |= GCP_USEKERNING;
405cdf0e10cSrcweir #endif // GCP_KERN_HACK
406cdf0e10cSrcweir
407cdf0e10cSrcweir for( i = 0; i < mnGlyphCount; ++i )
408cdf0e10cSrcweir mpOutGlyphs[i] = pBidiStr[ i ];
409cdf0e10cSrcweir mnWidth = 0;
410cdf0e10cSrcweir for( i = 0; i < mnGlyphCount; ++i )
411cdf0e10cSrcweir {
412cdf0e10cSrcweir // get the current UCS-4 code point, check for surrogate pairs
413cdf0e10cSrcweir const WCHAR* pCodes = reinterpret_cast<LPCWSTR>(&pBidiStr[i]);
414cdf0e10cSrcweir unsigned nCharCode = pCodes[0];
415cdf0e10cSrcweir bool bSurrogate = ((nCharCode >= 0xD800) && (nCharCode <= 0xDFFF));
416cdf0e10cSrcweir if( bSurrogate )
417cdf0e10cSrcweir {
418*438f3012SJürgen Schmidt // ignore high surrogates, they were already processed with their low surrogates
419*438f3012SJürgen Schmidt if( nCharCode >= 0xDC00 )
420cdf0e10cSrcweir continue;
421*438f3012SJürgen Schmidt // check the second half of the surrogate pair
422*438f3012SJürgen Schmidt bSurrogate &= (0xDC00 <= pCodes[1]) && (pCodes[1] <= 0xDFFF);
423*438f3012SJürgen Schmidt // calculate the UTF-32 code of valid surrogate pairs
424*438f3012SJürgen Schmidt if( bSurrogate )
425*438f3012SJürgen Schmidt nCharCode = 0x10000 + ((pCodes[0] - 0xD800) << 10) + (pCodes[1] - 0xDC00);
426*438f3012SJürgen Schmidt else // or fall back to a replacement character
427*438f3012SJürgen Schmidt nCharCode = '?';
428*438f3012SJürgen Schmidt }
429cdf0e10cSrcweir
430*438f3012SJürgen Schmidt // get the advance width for the current UTF-32 code point
431cdf0e10cSrcweir int nGlyphWidth = mrWinFontEntry.GetCachedGlyphWidth( nCharCode );
432cdf0e10cSrcweir if( nGlyphWidth == -1 )
433cdf0e10cSrcweir {
434cdf0e10cSrcweir ABC aABC;
435cdf0e10cSrcweir SIZE aExtent;
436cdf0e10cSrcweir if( ::GetTextExtentPoint32W( mhDC, &pCodes[0], bSurrogate ? 2 : 1, &aExtent) )
437cdf0e10cSrcweir nGlyphWidth = aExtent.cx;
438cdf0e10cSrcweir else if( ::GetCharABCWidthsW( mhDC, nCharCode, nCharCode, &aABC ) )
439cdf0e10cSrcweir nGlyphWidth = aABC.abcA + aABC.abcB + aABC.abcC;
440cdf0e10cSrcweir else if( !::GetCharWidth32W( mhDC, nCharCode, nCharCode, &nGlyphWidth )
441cdf0e10cSrcweir && !::GetCharWidthW( mhDC, nCharCode, nCharCode, &nGlyphWidth ) )
442cdf0e10cSrcweir nGlyphWidth = 0;
443cdf0e10cSrcweir mrWinFontEntry.CacheGlyphWidth( nCharCode, nGlyphWidth );
444cdf0e10cSrcweir }
445cdf0e10cSrcweir mpGlyphAdvances[ i ] = nGlyphWidth;
446cdf0e10cSrcweir mnWidth += nGlyphWidth;
447cdf0e10cSrcweir
448*438f3012SJürgen Schmidt // the second half of surrogate pair gets a zero width
449cdf0e10cSrcweir if( bSurrogate && ((i+1) < mnGlyphCount) )
450cdf0e10cSrcweir mpGlyphAdvances[ i+1 ] = 0;
451cdf0e10cSrcweir
452cdf0e10cSrcweir // check with the font face if glyph fallback is needed
453cdf0e10cSrcweir if( mrWinFontData.HasChar( nCharCode ) )
454cdf0e10cSrcweir continue;
455cdf0e10cSrcweir
456cdf0e10cSrcweir // request glyph fallback at this position in the string
457cdf0e10cSrcweir bool bRTL = mpGlyphRTLFlags ? mpGlyphRTLFlags[i] : false;
458cdf0e10cSrcweir int nCharPos = mpGlyphs2Chars ? mpGlyphs2Chars[i]: i + rArgs.mnMinCharPos;
459cdf0e10cSrcweir rArgs.NeedFallback( nCharPos, bRTL );
460cdf0e10cSrcweir if( bSurrogate && ((nCharPos+1) < rArgs.mnLength) )
461cdf0e10cSrcweir rArgs.NeedFallback( nCharPos+1, bRTL );
462cdf0e10cSrcweir
463cdf0e10cSrcweir // replace the current glyph shape with the NotDef glyph shape
464cdf0e10cSrcweir if( rArgs.mnFlags & SAL_LAYOUT_FOR_FALLBACK )
465cdf0e10cSrcweir {
466cdf0e10cSrcweir // when we already are layouting for glyph fallback
467cdf0e10cSrcweir // then a new unresolved glyph is not interesting
468cdf0e10cSrcweir mnNotdefWidth = 0;
469cdf0e10cSrcweir mpOutGlyphs[i] = DROPPED_OUTGLYPH;
470cdf0e10cSrcweir }
471cdf0e10cSrcweir else
472cdf0e10cSrcweir {
473cdf0e10cSrcweir if( mnNotdefWidth < 0 )
474cdf0e10cSrcweir {
475cdf0e10cSrcweir // get the width of the NotDef glyph
476cdf0e10cSrcweir SIZE aExtent;
477cdf0e10cSrcweir WCHAR cNotDef = rArgs.mpStr[ nCharPos ];
478cdf0e10cSrcweir mnNotdefWidth = 0;
479cdf0e10cSrcweir if( ::GetTextExtentPoint32W( mhDC, &cNotDef, 1, &aExtent) )
480cdf0e10cSrcweir mnNotdefWidth = aExtent.cx;
481cdf0e10cSrcweir }
482cdf0e10cSrcweir // use a better NotDef glyph
483cdf0e10cSrcweir if( !mbDisableGlyphs && !bSurrogate )
484cdf0e10cSrcweir mpOutGlyphs[i] = 0;
485cdf0e10cSrcweir }
486cdf0e10cSrcweir if( bSurrogate && ((i+1) < mnGlyphCount) )
487cdf0e10cSrcweir mpOutGlyphs[i+1] = DROPPED_OUTGLYPH;
488cdf0e10cSrcweir
489cdf0e10cSrcweir // adjust the current glyph width to the NotDef glyph width
490cdf0e10cSrcweir mnWidth += mnNotdefWidth - mpGlyphAdvances[i];
491cdf0e10cSrcweir mpGlyphAdvances[i] = mnNotdefWidth;
492cdf0e10cSrcweir if( mpGlyphOrigAdvs )
493cdf0e10cSrcweir mpGlyphOrigAdvs[i] = mnNotdefWidth;
494cdf0e10cSrcweir }
495cdf0e10cSrcweir
496cdf0e10cSrcweir #ifdef GCP_KERN_HACK
497cdf0e10cSrcweir // apply kerning if the layout engine has not yet done it
498cdf0e10cSrcweir if( rArgs.mnFlags & (SAL_LAYOUT_KERNING_ASIAN|SAL_LAYOUT_KERNING_PAIRS) )
499cdf0e10cSrcweir {
500cdf0e10cSrcweir #else // GCP_KERN_HACK
501cdf0e10cSrcweir // apply just asian kerning
502cdf0e10cSrcweir if( rArgs.mnFlags & SAL_LAYOUT_KERNING_ASIAN )
503cdf0e10cSrcweir {
504cdf0e10cSrcweir if( !(rArgs.mnFlags & SAL_LAYOUT_KERNING_PAIRS) )
505cdf0e10cSrcweir #endif // GCP_KERN_HACK
506cdf0e10cSrcweir for( i = 0; i < mnGlyphCount; ++i )
507cdf0e10cSrcweir mpGlyphOrigAdvs[i] = mpGlyphAdvances[i];
508cdf0e10cSrcweir
509cdf0e10cSrcweir // #99658# also apply asian kerning on the substring border
510cdf0e10cSrcweir int nLen = mnGlyphCount;
511cdf0e10cSrcweir if( rArgs.mnMinCharPos + nLen < rArgs.mnLength )
512cdf0e10cSrcweir ++nLen;
513cdf0e10cSrcweir for( i = 1; i < nLen; ++i )
514cdf0e10cSrcweir {
515cdf0e10cSrcweir #ifdef GCP_KERN_HACK
516cdf0e10cSrcweir if( rArgs.mnFlags & SAL_LAYOUT_KERNING_PAIRS )
517cdf0e10cSrcweir {
518cdf0e10cSrcweir int nKernAmount = mrWinFontEntry.GetKerning( pBidiStr[i-1], pBidiStr[i] );
519cdf0e10cSrcweir mpGlyphAdvances[ i-1 ] += nKernAmount;
520cdf0e10cSrcweir mnWidth += nKernAmount;
521cdf0e10cSrcweir }
522cdf0e10cSrcweir else if( rArgs.mnFlags & SAL_LAYOUT_KERNING_ASIAN )
523cdf0e10cSrcweir #endif // GCP_KERN_HACK
524cdf0e10cSrcweir
525cdf0e10cSrcweir if( ( (0x3000 == (0xFF00 & pBidiStr[i-1])) || (0x2010 == (0xFFF0 & pBidiStr[i-1])) || (0xFF00 == (0xFF00 & pBidiStr[i-1])))
526cdf0e10cSrcweir && ( (0x3000 == (0xFF00 & pBidiStr[i])) || (0x2010 == (0xFFF0 & pBidiStr[i])) || (0xFF00 == (0xFF00 & pBidiStr[i])) ) )
527cdf0e10cSrcweir {
528cdf0e10cSrcweir long nKernFirst = +CalcAsianKerning( pBidiStr[i-1], true, bVertical );
529cdf0e10cSrcweir long nKernNext = -CalcAsianKerning( pBidiStr[i], false, bVertical );
530cdf0e10cSrcweir
531cdf0e10cSrcweir long nDelta = (nKernFirst < nKernNext) ? nKernFirst : nKernNext;
532cdf0e10cSrcweir if( nDelta<0 && nKernFirst!=0 && nKernNext!=0 )
533cdf0e10cSrcweir {
534cdf0e10cSrcweir nDelta = (nDelta * mpGlyphAdvances[i-1] + 2) / 4;
535cdf0e10cSrcweir mpGlyphAdvances[i-1] += nDelta;
536cdf0e10cSrcweir mnWidth += nDelta;
537cdf0e10cSrcweir }
538cdf0e10cSrcweir }
539cdf0e10cSrcweir }
540cdf0e10cSrcweir }
541cdf0e10cSrcweir
542cdf0e10cSrcweir // calculate virtual char widths
543cdf0e10cSrcweir if( !mpGlyphs2Chars )
544cdf0e10cSrcweir mpCharWidths = mpGlyphAdvances;
545cdf0e10cSrcweir else
546cdf0e10cSrcweir {
547cdf0e10cSrcweir mpCharWidths = new int[ mnCharCount ];
548cdf0e10cSrcweir for( i = 0; i < mnCharCount; ++i )
549cdf0e10cSrcweir mpCharWidths[ i ] = 0;
550cdf0e10cSrcweir for( i = 0; i < mnGlyphCount; ++i )
551cdf0e10cSrcweir {
552cdf0e10cSrcweir int j = mpGlyphs2Chars[ i ] - rArgs.mnMinCharPos;
553cdf0e10cSrcweir if( j >= 0 )
554cdf0e10cSrcweir mpCharWidths[ j ] += mpGlyphAdvances[ i ];
555cdf0e10cSrcweir }
556cdf0e10cSrcweir }
557cdf0e10cSrcweir
558cdf0e10cSrcweir // scale layout metrics if needed
559cdf0e10cSrcweir // TODO: does it make the code more simple if the metric scaling
560cdf0e10cSrcweir // is moved to the methods that need metric scaling (e.g. FillDXArray())?
561cdf0e10cSrcweir if( mfFontScale != 1.0 )
562cdf0e10cSrcweir {
563cdf0e10cSrcweir mnWidth = (long)(mnWidth * mfFontScale);
564cdf0e10cSrcweir mnBaseAdv = (int)(mnBaseAdv * mfFontScale);
565cdf0e10cSrcweir for( i = 0; i < mnCharCount; ++i )
566cdf0e10cSrcweir mpCharWidths[i] = (int)(mpCharWidths[i] * mfFontScale);
567cdf0e10cSrcweir if( mpGlyphAdvances != mpCharWidths )
568cdf0e10cSrcweir for( i = 0; i < mnGlyphCount; ++i )
569cdf0e10cSrcweir mpGlyphAdvances[i] = (int)(mpGlyphAdvances[i] * mfFontScale);
570cdf0e10cSrcweir if( mpGlyphOrigAdvs && (mpGlyphOrigAdvs != mpGlyphAdvances) )
571cdf0e10cSrcweir for( i = 0; i < mnGlyphCount; ++i )
572cdf0e10cSrcweir mpGlyphOrigAdvs[i] = (int)(mpGlyphOrigAdvs[i] * mfFontScale);
573cdf0e10cSrcweir }
574cdf0e10cSrcweir
575cdf0e10cSrcweir return true;
576cdf0e10cSrcweir }
577cdf0e10cSrcweir
578cdf0e10cSrcweir // -----------------------------------------------------------------------
579cdf0e10cSrcweir
580248a599fSHerbert Dürr int SimpleWinLayout::GetNextGlyphs( int nLen, sal_GlyphId* pGlyphIds, Point& rPos, int& nStart,
581cdf0e10cSrcweir long* pGlyphAdvances, int* pCharIndexes ) const
582cdf0e10cSrcweir {
583cdf0e10cSrcweir // return zero if no more glyph found
584cdf0e10cSrcweir if( nStart >= mnGlyphCount )
585cdf0e10cSrcweir return 0;
586cdf0e10cSrcweir
587cdf0e10cSrcweir // calculate glyph position relative to layout base
588cdf0e10cSrcweir // TODO: avoid for nStart!=0 case by reusing rPos
589cdf0e10cSrcweir long nXOffset = mnBaseAdv;
590cdf0e10cSrcweir for( int i = 0; i < nStart; ++i )
591cdf0e10cSrcweir nXOffset += mpGlyphAdvances[ i ];
592cdf0e10cSrcweir
593cdf0e10cSrcweir // calculate absolute position in pixel units
594cdf0e10cSrcweir Point aRelativePos( nXOffset, 0 );
595cdf0e10cSrcweir rPos = GetDrawPosition( aRelativePos );
596cdf0e10cSrcweir
597cdf0e10cSrcweir int nCount = 0;
598cdf0e10cSrcweir while( nCount < nLen )
599cdf0e10cSrcweir {
600248a599fSHerbert Dürr // update return values {aGlyphId,nCharPos,nGlyphAdvance}
601248a599fSHerbert Dürr sal_GlyphId aGlyphId = mpOutGlyphs[ nStart ];
602cdf0e10cSrcweir if( mbDisableGlyphs )
603cdf0e10cSrcweir {
604cdf0e10cSrcweir if( mnLayoutFlags & SAL_LAYOUT_VERTICAL )
605cdf0e10cSrcweir {
606248a599fSHerbert Dürr const sal_UCS4 cChar = static_cast<sal_UCS4>(aGlyphId & GF_IDXMASK);
607cdf0e10cSrcweir if( mrWinFontData.HasGSUBstitutions( mhDC )
608cdf0e10cSrcweir && mrWinFontData.IsGSUBstituted( cChar ) )
609248a599fSHerbert Dürr aGlyphId |= GF_GSUB | GF_ROTL;
610cdf0e10cSrcweir else
611cdf0e10cSrcweir {
612248a599fSHerbert Dürr aGlyphId |= GetVerticalFlags( cChar );
613248a599fSHerbert Dürr if( (aGlyphId & GF_ROTMASK) == 0 )
614248a599fSHerbert Dürr aGlyphId |= GF_VERT;
615cdf0e10cSrcweir }
616cdf0e10cSrcweir }
617248a599fSHerbert Dürr aGlyphId |= GF_ISCHAR;
618cdf0e10cSrcweir }
619cdf0e10cSrcweir ++nCount;
620248a599fSHerbert Dürr *(pGlyphIds++) = aGlyphId;
621cdf0e10cSrcweir if( pGlyphAdvances )
622cdf0e10cSrcweir *(pGlyphAdvances++) = mpGlyphAdvances[ nStart ];
623cdf0e10cSrcweir if( pCharIndexes )
624cdf0e10cSrcweir {
625cdf0e10cSrcweir int nCharPos;
626cdf0e10cSrcweir if( !mpGlyphs2Chars )
627cdf0e10cSrcweir nCharPos = nStart + mnMinCharPos;
628cdf0e10cSrcweir else
629cdf0e10cSrcweir nCharPos = mpGlyphs2Chars[nStart];
630cdf0e10cSrcweir *(pCharIndexes++) = nCharPos;
631cdf0e10cSrcweir }
632cdf0e10cSrcweir
633cdf0e10cSrcweir // stop at last glyph
634cdf0e10cSrcweir if( ++nStart >= mnGlyphCount )
635cdf0e10cSrcweir break;
636cdf0e10cSrcweir
637cdf0e10cSrcweir // stop when next x-position is unexpected
638cdf0e10cSrcweir if( !pGlyphAdvances && mpGlyphOrigAdvs )
639cdf0e10cSrcweir if( mpGlyphAdvances[nStart-1] != mpGlyphOrigAdvs[nStart-1] )
640cdf0e10cSrcweir break;
641cdf0e10cSrcweir }
642cdf0e10cSrcweir
643cdf0e10cSrcweir return nCount;
644cdf0e10cSrcweir }
645cdf0e10cSrcweir
646cdf0e10cSrcweir // -----------------------------------------------------------------------
647cdf0e10cSrcweir
648cdf0e10cSrcweir void SimpleWinLayout::DrawText( SalGraphics& rGraphics ) const
649cdf0e10cSrcweir {
650cdf0e10cSrcweir if( mnGlyphCount <= 0 )
651cdf0e10cSrcweir return;
652cdf0e10cSrcweir
653cdf0e10cSrcweir WinSalGraphics& rWinGraphics = static_cast<WinSalGraphics&>(rGraphics);
6545f27b83cSArmin Le Grand HDC aHDC = rWinGraphics.getHDC();
655cdf0e10cSrcweir
656cdf0e10cSrcweir HFONT hOrigFont = DisableFontScaling();
657cdf0e10cSrcweir
658cdf0e10cSrcweir UINT mnDrawOptions = ETO_GLYPH_INDEX;
659cdf0e10cSrcweir if( mbDisableGlyphs )
660cdf0e10cSrcweir mnDrawOptions = 0;
661cdf0e10cSrcweir
662cdf0e10cSrcweir Point aPos = GetDrawPosition( Point( mnBaseAdv, 0 ) );
663cdf0e10cSrcweir
664cdf0e10cSrcweir // #108267#, break up into glyph portions of a limited size required by Win32 API
665cdf0e10cSrcweir const unsigned int maxGlyphCount = 8192;
666cdf0e10cSrcweir UINT numGlyphPortions = mnGlyphCount / maxGlyphCount;
667cdf0e10cSrcweir UINT remainingGlyphs = mnGlyphCount % maxGlyphCount;
668cdf0e10cSrcweir
669cdf0e10cSrcweir if( numGlyphPortions )
670cdf0e10cSrcweir {
671cdf0e10cSrcweir // #108267#,#109387# break up string into smaller chunks
672cdf0e10cSrcweir // the output positions will be updated by windows (SetTextAlign)
673cdf0e10cSrcweir POINT oldPos;
674cdf0e10cSrcweir UINT oldTa = ::GetTextAlign( aHDC );
675cdf0e10cSrcweir ::SetTextAlign( aHDC, (oldTa & ~TA_NOUPDATECP) | TA_UPDATECP );
676cdf0e10cSrcweir ::MoveToEx( aHDC, aPos.X(), aPos.Y(), &oldPos );
677cdf0e10cSrcweir unsigned int i = 0;
678cdf0e10cSrcweir for( unsigned int n = 0; n < numGlyphPortions; ++n, i+=maxGlyphCount )
679cdf0e10cSrcweir ::ExtTextOutW( aHDC, 0, 0, mnDrawOptions, NULL,
680cdf0e10cSrcweir mpOutGlyphs+i, maxGlyphCount, mpGlyphAdvances+i );
681cdf0e10cSrcweir ::ExtTextOutW( aHDC, 0, 0, mnDrawOptions, NULL,
682cdf0e10cSrcweir mpOutGlyphs+i, remainingGlyphs, mpGlyphAdvances+i );
683cdf0e10cSrcweir ::MoveToEx( aHDC, oldPos.x, oldPos.y, (LPPOINT) NULL);
684cdf0e10cSrcweir ::SetTextAlign( aHDC, oldTa );
685cdf0e10cSrcweir }
686cdf0e10cSrcweir else
687cdf0e10cSrcweir ::ExtTextOutW( aHDC, aPos.X(), aPos.Y(), mnDrawOptions, NULL,
688cdf0e10cSrcweir mpOutGlyphs, mnGlyphCount, mpGlyphAdvances );
689cdf0e10cSrcweir
690cdf0e10cSrcweir if( hOrigFont )
691cdf0e10cSrcweir DeleteFont( SelectFont( aHDC, hOrigFont ) );
692cdf0e10cSrcweir }
693cdf0e10cSrcweir
694cdf0e10cSrcweir // -----------------------------------------------------------------------
695cdf0e10cSrcweir
696cdf0e10cSrcweir long SimpleWinLayout::FillDXArray( long* pDXArray ) const
697cdf0e10cSrcweir {
698cdf0e10cSrcweir if( !mnWidth )
699cdf0e10cSrcweir {
700cdf0e10cSrcweir long mnWidth = mnBaseAdv;
701cdf0e10cSrcweir for( int i = 0; i < mnGlyphCount; ++i )
702cdf0e10cSrcweir mnWidth += mpGlyphAdvances[ i ];
703cdf0e10cSrcweir }
704cdf0e10cSrcweir
705cdf0e10cSrcweir if( pDXArray != NULL )
706cdf0e10cSrcweir {
707cdf0e10cSrcweir for( int i = 0; i < mnCharCount; ++i )
708cdf0e10cSrcweir pDXArray[ i ] = mpCharWidths[ i ];
709cdf0e10cSrcweir }
710cdf0e10cSrcweir
711cdf0e10cSrcweir return mnWidth;
712cdf0e10cSrcweir }
713cdf0e10cSrcweir
714cdf0e10cSrcweir // -----------------------------------------------------------------------
715cdf0e10cSrcweir
716cdf0e10cSrcweir int SimpleWinLayout::GetTextBreak( long nMaxWidth, long nCharExtra, int nFactor ) const
717cdf0e10cSrcweir // NOTE: the nFactor is used to prevent rounding errors for small nCharExtra values
718cdf0e10cSrcweir {
719cdf0e10cSrcweir if( mnWidth )
720cdf0e10cSrcweir if( (mnWidth * nFactor + mnCharCount * nCharExtra) <= nMaxWidth )
721cdf0e10cSrcweir return STRING_LEN;
722cdf0e10cSrcweir
723cdf0e10cSrcweir long nExtraWidth = mnBaseAdv * nFactor;
724cdf0e10cSrcweir for( int n = 0; n < mnCharCount; ++n )
725cdf0e10cSrcweir {
726cdf0e10cSrcweir // skip unused characters
727cdf0e10cSrcweir if( mpChars2Glyphs && (mpChars2Glyphs[n] < 0) )
728cdf0e10cSrcweir continue;
729cdf0e10cSrcweir // add char widths until max
730cdf0e10cSrcweir nExtraWidth += mpCharWidths[ n ] * nFactor;
731cdf0e10cSrcweir if( nExtraWidth >= nMaxWidth )
732cdf0e10cSrcweir return (mnMinCharPos + n);
733cdf0e10cSrcweir nExtraWidth += nCharExtra;
734cdf0e10cSrcweir }
735cdf0e10cSrcweir
736cdf0e10cSrcweir return STRING_LEN;
737cdf0e10cSrcweir }
738cdf0e10cSrcweir
739cdf0e10cSrcweir // -----------------------------------------------------------------------
740cdf0e10cSrcweir
741cdf0e10cSrcweir void SimpleWinLayout::GetCaretPositions( int nMaxIdx, long* pCaretXArray ) const
742cdf0e10cSrcweir {
743cdf0e10cSrcweir long nXPos = mnBaseAdv;
744cdf0e10cSrcweir
745cdf0e10cSrcweir if( !mpGlyphs2Chars )
746cdf0e10cSrcweir {
747cdf0e10cSrcweir for( int i = 0; i < nMaxIdx; i += 2 )
748cdf0e10cSrcweir {
749cdf0e10cSrcweir pCaretXArray[ i ] = nXPos;
750cdf0e10cSrcweir nXPos += mpGlyphAdvances[ i>>1 ];
751cdf0e10cSrcweir pCaretXArray[ i+1 ] = nXPos;
752cdf0e10cSrcweir }
753cdf0e10cSrcweir }
754cdf0e10cSrcweir else
755cdf0e10cSrcweir {
756cdf0e10cSrcweir int i;
757cdf0e10cSrcweir for( i = 0; i < nMaxIdx; ++i )
758cdf0e10cSrcweir pCaretXArray[ i ] = -1;
759cdf0e10cSrcweir
760cdf0e10cSrcweir // assign glyph positions to character positions
761cdf0e10cSrcweir for( i = 0; i < mnGlyphCount; ++i )
762cdf0e10cSrcweir {
763cdf0e10cSrcweir int nCurrIdx = mpGlyphs2Chars[ i ] - mnMinCharPos;
764cdf0e10cSrcweir long nXRight = nXPos + mpCharWidths[ nCurrIdx ];
765cdf0e10cSrcweir nCurrIdx *= 2;
766cdf0e10cSrcweir if( !(mpGlyphRTLFlags && mpGlyphRTLFlags[i]) )
767cdf0e10cSrcweir {
768cdf0e10cSrcweir // normal positions for LTR case
769cdf0e10cSrcweir pCaretXArray[ nCurrIdx ] = nXPos;
770cdf0e10cSrcweir pCaretXArray[ nCurrIdx+1 ] = nXRight;
771cdf0e10cSrcweir }
772cdf0e10cSrcweir else
773cdf0e10cSrcweir {
774cdf0e10cSrcweir // reverse positions for RTL case
775cdf0e10cSrcweir pCaretXArray[ nCurrIdx ] = nXRight;
776cdf0e10cSrcweir pCaretXArray[ nCurrIdx+1 ] = nXPos;
777cdf0e10cSrcweir }
778cdf0e10cSrcweir nXPos += mpGlyphAdvances[ i ];
779cdf0e10cSrcweir }
780cdf0e10cSrcweir }
781cdf0e10cSrcweir }
782cdf0e10cSrcweir
783cdf0e10cSrcweir // -----------------------------------------------------------------------
784cdf0e10cSrcweir
785cdf0e10cSrcweir void SimpleWinLayout::Justify( long nNewWidth )
786cdf0e10cSrcweir {
787cdf0e10cSrcweir long nOldWidth = mnWidth;
788cdf0e10cSrcweir mnWidth = nNewWidth;
789cdf0e10cSrcweir
790cdf0e10cSrcweir if( mnGlyphCount <= 0 )
791cdf0e10cSrcweir return;
792cdf0e10cSrcweir
793cdf0e10cSrcweir if( nNewWidth == nOldWidth )
794cdf0e10cSrcweir return;
795cdf0e10cSrcweir
796cdf0e10cSrcweir // the rightmost glyph cannot be stretched
797cdf0e10cSrcweir const int nRight = mnGlyphCount - 1;
798cdf0e10cSrcweir nOldWidth -= mpGlyphAdvances[ nRight ];
799cdf0e10cSrcweir nNewWidth -= mpGlyphAdvances[ nRight ];
800cdf0e10cSrcweir
801cdf0e10cSrcweir // count stretchable glyphs
802cdf0e10cSrcweir int nStretchable = 0, i;
803cdf0e10cSrcweir for( i = 0; i < nRight; ++i )
804cdf0e10cSrcweir if( mpGlyphAdvances[i] >= 0 )
805cdf0e10cSrcweir ++nStretchable;
806cdf0e10cSrcweir
807cdf0e10cSrcweir // stretch these glyphs
808cdf0e10cSrcweir int nDiffWidth = nNewWidth - nOldWidth;
809cdf0e10cSrcweir for( i = 0; (i < nRight) && (nStretchable > 0); ++i )
810cdf0e10cSrcweir {
811cdf0e10cSrcweir if( mpGlyphAdvances[i] <= 0 )
812cdf0e10cSrcweir continue;
813cdf0e10cSrcweir int nDeltaWidth = nDiffWidth / nStretchable;
814cdf0e10cSrcweir mpGlyphAdvances[i] += nDeltaWidth;
815cdf0e10cSrcweir --nStretchable;
816cdf0e10cSrcweir nDiffWidth -= nDeltaWidth;
817cdf0e10cSrcweir }
818cdf0e10cSrcweir }
819cdf0e10cSrcweir
820cdf0e10cSrcweir // -----------------------------------------------------------------------
821cdf0e10cSrcweir
822cdf0e10cSrcweir void SimpleWinLayout::AdjustLayout( ImplLayoutArgs& rArgs )
823cdf0e10cSrcweir {
824cdf0e10cSrcweir SalLayout::AdjustLayout( rArgs );
825cdf0e10cSrcweir
826cdf0e10cSrcweir // adjust positions if requested
827cdf0e10cSrcweir if( rArgs.mpDXArray )
828cdf0e10cSrcweir ApplyDXArray( rArgs );
829cdf0e10cSrcweir else if( rArgs.mnLayoutWidth )
830cdf0e10cSrcweir Justify( rArgs.mnLayoutWidth );
831cdf0e10cSrcweir else
832cdf0e10cSrcweir return;
833cdf0e10cSrcweir
834cdf0e10cSrcweir // recalculate virtual char widths if they were changed
835cdf0e10cSrcweir if( mpCharWidths != mpGlyphAdvances )
836cdf0e10cSrcweir {
837cdf0e10cSrcweir int i;
838cdf0e10cSrcweir if( !mpGlyphs2Chars )
839cdf0e10cSrcweir {
840cdf0e10cSrcweir // standard LTR case
841cdf0e10cSrcweir for( i = 0; i < mnGlyphCount; ++i )
842cdf0e10cSrcweir mpCharWidths[ i ] = mpGlyphAdvances[ i ];
843cdf0e10cSrcweir }
844cdf0e10cSrcweir else
845cdf0e10cSrcweir {
846cdf0e10cSrcweir // BiDi or complex case
847cdf0e10cSrcweir for( i = 0; i < mnCharCount; ++i )
848cdf0e10cSrcweir mpCharWidths[ i ] = 0;
849cdf0e10cSrcweir for( i = 0; i < mnGlyphCount; ++i )
850cdf0e10cSrcweir {
851cdf0e10cSrcweir int j = mpGlyphs2Chars[ i ] - rArgs.mnMinCharPos;
852cdf0e10cSrcweir if( j >= 0 )
853cdf0e10cSrcweir mpCharWidths[ j ] += mpGlyphAdvances[ i ];
854cdf0e10cSrcweir }
855cdf0e10cSrcweir }
856cdf0e10cSrcweir }
857cdf0e10cSrcweir }
858cdf0e10cSrcweir
859cdf0e10cSrcweir // -----------------------------------------------------------------------
860cdf0e10cSrcweir
861cdf0e10cSrcweir void SimpleWinLayout::ApplyDXArray( const ImplLayoutArgs& rArgs )
862cdf0e10cSrcweir {
863cdf0e10cSrcweir // try to avoid disturbance of text flow for LSB rounding case;
864cdf0e10cSrcweir const long* pDXArray = rArgs.mpDXArray;
865cdf0e10cSrcweir
866cdf0e10cSrcweir int i = 0;
867cdf0e10cSrcweir long nOldWidth = mnBaseAdv;
868cdf0e10cSrcweir for(; i < mnCharCount; ++i )
869cdf0e10cSrcweir {
870cdf0e10cSrcweir int j = !mpChars2Glyphs ? i : mpChars2Glyphs[i];
871cdf0e10cSrcweir if( j >= 0 )
872cdf0e10cSrcweir {
873cdf0e10cSrcweir nOldWidth += mpGlyphAdvances[ j ];
874cdf0e10cSrcweir int nDiff = nOldWidth - pDXArray[ i ];
875cdf0e10cSrcweir
876cdf0e10cSrcweir // disabled because of #104768#
877cdf0e10cSrcweir // works great for static text, but problems when typing
878cdf0e10cSrcweir // if( nDiff>+1 || nDiff<-1 )
879cdf0e10cSrcweir // only bother with changing anything when something moved
880cdf0e10cSrcweir if( nDiff != 0 )
881cdf0e10cSrcweir break;
882cdf0e10cSrcweir }
883cdf0e10cSrcweir }
884cdf0e10cSrcweir if( i >= mnCharCount )
885cdf0e10cSrcweir return;
886cdf0e10cSrcweir
887cdf0e10cSrcweir if( !mpGlyphOrigAdvs )
888cdf0e10cSrcweir {
889cdf0e10cSrcweir mpGlyphOrigAdvs = new int[ mnGlyphCount ];
890cdf0e10cSrcweir for( i = 0; i < mnGlyphCount; ++i )
891cdf0e10cSrcweir mpGlyphOrigAdvs[ i ] = mpGlyphAdvances[ i ];
892cdf0e10cSrcweir }
893cdf0e10cSrcweir
894cdf0e10cSrcweir mnWidth = mnBaseAdv;
895cdf0e10cSrcweir for( i = 0; i < mnCharCount; ++i )
896cdf0e10cSrcweir {
897cdf0e10cSrcweir int j = !mpChars2Glyphs ? i : mpChars2Glyphs[i];
898cdf0e10cSrcweir if( j >= 0 )
899cdf0e10cSrcweir mpGlyphAdvances[j] = pDXArray[i] - mnWidth;
900cdf0e10cSrcweir mnWidth = pDXArray[i];
901cdf0e10cSrcweir }
902cdf0e10cSrcweir }
903cdf0e10cSrcweir
904cdf0e10cSrcweir // -----------------------------------------------------------------------
905cdf0e10cSrcweir
906cdf0e10cSrcweir void SimpleWinLayout::MoveGlyph( int nStart, long nNewXPos )
907cdf0e10cSrcweir {
908cdf0e10cSrcweir if( nStart > mnGlyphCount )
909cdf0e10cSrcweir return;
910cdf0e10cSrcweir
911cdf0e10cSrcweir // calculate the current x-position of the requested glyph
912cdf0e10cSrcweir // TODO: cache absolute positions
913cdf0e10cSrcweir int nXPos = mnBaseAdv;
914cdf0e10cSrcweir for( int i = 0; i < nStart; ++i )
915cdf0e10cSrcweir nXPos += mpGlyphAdvances[i];
916cdf0e10cSrcweir
917cdf0e10cSrcweir // calculate the difference to the current glyph position
918cdf0e10cSrcweir int nDelta = nNewXPos - nXPos;
919cdf0e10cSrcweir
920cdf0e10cSrcweir // adjust the width of the layout if it was already cached
921cdf0e10cSrcweir if( mnWidth )
922cdf0e10cSrcweir mnWidth += nDelta;
923cdf0e10cSrcweir
924cdf0e10cSrcweir // depending on whether the requested glyph is leftmost in the layout
925cdf0e10cSrcweir // adjust either the layout's or the requested glyph's relative position
926cdf0e10cSrcweir if( nStart > 0 )
927cdf0e10cSrcweir mpGlyphAdvances[ nStart-1 ] += nDelta;
928cdf0e10cSrcweir else
929cdf0e10cSrcweir mnBaseAdv += nDelta;
930cdf0e10cSrcweir }
931cdf0e10cSrcweir
932cdf0e10cSrcweir // -----------------------------------------------------------------------
933cdf0e10cSrcweir
934cdf0e10cSrcweir void SimpleWinLayout::DropGlyph( int nStart )
935cdf0e10cSrcweir {
936cdf0e10cSrcweir mpOutGlyphs[ nStart ] = DROPPED_OUTGLYPH;
937cdf0e10cSrcweir }
938cdf0e10cSrcweir
939cdf0e10cSrcweir // -----------------------------------------------------------------------
940cdf0e10cSrcweir
941cdf0e10cSrcweir void SimpleWinLayout::Simplify( bool /*bIsBase*/ )
942cdf0e10cSrcweir {
943cdf0e10cSrcweir // return early if no glyph has been dropped
944cdf0e10cSrcweir int i = mnGlyphCount;
945cdf0e10cSrcweir while( (--i >= 0) && (mpOutGlyphs[ i ] != DROPPED_OUTGLYPH) );
946cdf0e10cSrcweir if( i < 0 )
947cdf0e10cSrcweir return;
948cdf0e10cSrcweir
949cdf0e10cSrcweir // convert the layout to a sparse layout if it is not already
950cdf0e10cSrcweir if( !mpGlyphs2Chars )
951cdf0e10cSrcweir {
952cdf0e10cSrcweir mpGlyphs2Chars = new int[ mnGlyphCount ];
953cdf0e10cSrcweir mpCharWidths = new int[ mnCharCount ];
954cdf0e10cSrcweir // assertion: mnGlyphCount == mnCharCount
955cdf0e10cSrcweir for( int k = 0; k < mnGlyphCount; ++k )
956cdf0e10cSrcweir {
957cdf0e10cSrcweir mpGlyphs2Chars[ k ] = mnMinCharPos + k;
958cdf0e10cSrcweir mpCharWidths[ k ] = mpGlyphAdvances[ k ];
959cdf0e10cSrcweir }
960cdf0e10cSrcweir }
961cdf0e10cSrcweir
962cdf0e10cSrcweir // remove dropped glyphs that are rightmost in the layout
963cdf0e10cSrcweir for( i = mnGlyphCount; --i >= 0; )
964cdf0e10cSrcweir {
965cdf0e10cSrcweir if( mpOutGlyphs[ i ] != DROPPED_OUTGLYPH )
966cdf0e10cSrcweir break;
967cdf0e10cSrcweir if( mnWidth )
968cdf0e10cSrcweir mnWidth -= mpGlyphAdvances[ i ];
969cdf0e10cSrcweir int nRelCharPos = mpGlyphs2Chars[ i ] - mnMinCharPos;
970cdf0e10cSrcweir if( nRelCharPos >= 0 )
971cdf0e10cSrcweir mpCharWidths[ nRelCharPos ] = 0;
972cdf0e10cSrcweir }
973cdf0e10cSrcweir mnGlyphCount = i + 1;
974cdf0e10cSrcweir
975cdf0e10cSrcweir // keep original glyph widths around
976cdf0e10cSrcweir if( !mpGlyphOrigAdvs )
977cdf0e10cSrcweir {
978cdf0e10cSrcweir mpGlyphOrigAdvs = new int[ mnGlyphCount ];
979cdf0e10cSrcweir for( int k = 0; k < mnGlyphCount; ++k )
980cdf0e10cSrcweir mpGlyphOrigAdvs[ k ] = mpGlyphAdvances[ k ];
981cdf0e10cSrcweir }
982cdf0e10cSrcweir
983cdf0e10cSrcweir // remove dropped glyphs inside the layout
984cdf0e10cSrcweir int nNewGC = 0;
985cdf0e10cSrcweir for( i = 0; i < mnGlyphCount; ++i )
986cdf0e10cSrcweir {
987cdf0e10cSrcweir if( mpOutGlyphs[ i ] == DROPPED_OUTGLYPH )
988cdf0e10cSrcweir {
989cdf0e10cSrcweir // adjust relative position to last valid glyph
990cdf0e10cSrcweir int nDroppedWidth = mpGlyphAdvances[ i ];
991cdf0e10cSrcweir mpGlyphAdvances[ i ] = 0;
992cdf0e10cSrcweir if( nNewGC > 0 )
993cdf0e10cSrcweir mpGlyphAdvances[ nNewGC-1 ] += nDroppedWidth;
994cdf0e10cSrcweir else
995cdf0e10cSrcweir mnBaseAdv += nDroppedWidth;
996cdf0e10cSrcweir
997cdf0e10cSrcweir // zero the virtual char width for the char that has a fallback
998cdf0e10cSrcweir int nRelCharPos = mpGlyphs2Chars[ i ] - mnMinCharPos;
999cdf0e10cSrcweir if( nRelCharPos >= 0 )
1000cdf0e10cSrcweir mpCharWidths[ nRelCharPos ] = 0;
1001cdf0e10cSrcweir }
1002cdf0e10cSrcweir else
1003cdf0e10cSrcweir {
1004cdf0e10cSrcweir if( nNewGC != i )
1005cdf0e10cSrcweir {
1006cdf0e10cSrcweir // rearrange the glyph array to get rid of the dropped glyph
1007cdf0e10cSrcweir mpOutGlyphs[ nNewGC ] = mpOutGlyphs[ i ];
1008cdf0e10cSrcweir mpGlyphAdvances[ nNewGC ] = mpGlyphAdvances[ i ];
1009cdf0e10cSrcweir mpGlyphOrigAdvs[ nNewGC ] = mpGlyphOrigAdvs[ i ];
1010cdf0e10cSrcweir mpGlyphs2Chars[ nNewGC ] = mpGlyphs2Chars[ i ];
1011cdf0e10cSrcweir }
1012cdf0e10cSrcweir ++nNewGC;
1013cdf0e10cSrcweir }
1014cdf0e10cSrcweir }
1015cdf0e10cSrcweir
1016cdf0e10cSrcweir mnGlyphCount = nNewGC;
1017cdf0e10cSrcweir if( mnGlyphCount <= 0 )
1018cdf0e10cSrcweir mnWidth = mnBaseAdv = 0;
1019cdf0e10cSrcweir }
1020cdf0e10cSrcweir
1021cdf0e10cSrcweir // =======================================================================
1022cdf0e10cSrcweir
1023cdf0e10cSrcweir #ifdef USE_UNISCRIBE
1024cdf0e10cSrcweir
1025cdf0e10cSrcweir struct VisualItem
1026cdf0e10cSrcweir {
1027cdf0e10cSrcweir public:
1028cdf0e10cSrcweir SCRIPT_ITEM* mpScriptItem;
1029cdf0e10cSrcweir int mnMinGlyphPos;
1030cdf0e10cSrcweir int mnEndGlyphPos;
1031cdf0e10cSrcweir int mnMinCharPos;
1032cdf0e10cSrcweir int mnEndCharPos;
1033cdf0e10cSrcweir //long mnPixelWidth;
1034cdf0e10cSrcweir int mnXOffset;
1035cdf0e10cSrcweir ABC maABCWidths;
1036cdf0e10cSrcweir bool mbHasKashidas;
1037cdf0e10cSrcweir
1038cdf0e10cSrcweir public:
1039cdf0e10cSrcweir bool IsEmpty() const { return (mnEndGlyphPos <= 0); }
1040cdf0e10cSrcweir bool IsRTL() const { return mpScriptItem->a.fRTL; }
1041cdf0e10cSrcweir bool HasKashidas() const { return mbHasKashidas; }
1042cdf0e10cSrcweir };
1043cdf0e10cSrcweir
1044cdf0e10cSrcweir // -----------------------------------------------------------------------
1045cdf0e10cSrcweir
1046cdf0e10cSrcweir class UniscribeLayout : public WinLayout
1047cdf0e10cSrcweir {
1048cdf0e10cSrcweir public:
1049cdf0e10cSrcweir UniscribeLayout( HDC, const ImplWinFontData&, ImplWinFontEntry& );
1050cdf0e10cSrcweir
1051cdf0e10cSrcweir virtual bool LayoutText( ImplLayoutArgs& );
1052cdf0e10cSrcweir virtual void AdjustLayout( ImplLayoutArgs& );
1053cdf0e10cSrcweir virtual void DrawText( SalGraphics& ) const;
1054cdf0e10cSrcweir virtual int GetNextGlyphs( int nLen, sal_GlyphId* pGlyphs, Point& rPos, int&,
1055cdf0e10cSrcweir sal_Int32* pGlyphAdvances, int* pCharPosAry ) const;
1056cdf0e10cSrcweir
1057cdf0e10cSrcweir virtual long FillDXArray( long* pDXArray ) const;
1058cdf0e10cSrcweir virtual int GetTextBreak( long nMaxWidth, long nCharExtra, int nFactor ) const;
1059cdf0e10cSrcweir virtual void GetCaretPositions( int nArraySize, long* pCaretXArray ) const;
1060cdf0e10cSrcweir virtual bool IsKashidaPosValid ( int nCharPos ) const;
1061cdf0e10cSrcweir
1062cdf0e10cSrcweir // for glyph+font+script fallback
1063cdf0e10cSrcweir virtual void MoveGlyph( int nStart, long nNewXPos );
1064cdf0e10cSrcweir virtual void DropGlyph( int nStart );
1065cdf0e10cSrcweir virtual void Simplify( bool bIsBase );
1066cdf0e10cSrcweir virtual void DisableGlyphInjection( bool bDisable ) { mbDisableGlyphInjection = bDisable; }
1067cdf0e10cSrcweir
1068cdf0e10cSrcweir protected:
1069cdf0e10cSrcweir virtual ~UniscribeLayout();
1070cdf0e10cSrcweir
1071cdf0e10cSrcweir void Justify( long nNewWidth );
1072cdf0e10cSrcweir void ApplyDXArray( const ImplLayoutArgs& );
1073cdf0e10cSrcweir
1074cdf0e10cSrcweir bool GetItemSubrange( const VisualItem&,
1075cdf0e10cSrcweir int& rMinIndex, int& rEndIndex ) const;
1076cdf0e10cSrcweir
1077cdf0e10cSrcweir private:
1078cdf0e10cSrcweir // item specific info
1079cdf0e10cSrcweir SCRIPT_ITEM* mpScriptItems; // in logical order
1080cdf0e10cSrcweir VisualItem* mpVisualItems; // in visual order
1081cdf0e10cSrcweir int mnItemCount; // number of visual items
1082cdf0e10cSrcweir
1083cdf0e10cSrcweir // string specific info
1084cdf0e10cSrcweir // everything is in logical order
1085cdf0e10cSrcweir int mnCharCapacity;
1086cdf0e10cSrcweir WORD* mpLogClusters; // map from absolute_char_pos to relative_glyph_pos
1087cdf0e10cSrcweir int* mpCharWidths; // map from absolute_char_pos to char_width
1088cdf0e10cSrcweir int mnSubStringMin; // char_pos of first char in context
1089cdf0e10cSrcweir
1090cdf0e10cSrcweir // glyph specific info
1091cdf0e10cSrcweir // everything is in visual order
1092cdf0e10cSrcweir int mnGlyphCount;
1093cdf0e10cSrcweir int mnGlyphCapacity;
1094cdf0e10cSrcweir int* mpGlyphAdvances; // glyph advance width before justification
1095cdf0e10cSrcweir int* mpJustifications; // glyph advance width after justification
1096cdf0e10cSrcweir WORD* mpOutGlyphs; // glyphids in visual order
1097cdf0e10cSrcweir GOFFSET* mpGlyphOffsets; // glyph offsets to the "naive" layout
1098cdf0e10cSrcweir SCRIPT_VISATTR* mpVisualAttrs; // glyph visual attributes
1099cdf0e10cSrcweir mutable int* mpGlyphs2Chars; // map from absolute_glyph_pos to absolute_char_pos
1100cdf0e10cSrcweir
1101cdf0e10cSrcweir // kashida stuff
1102cdf0e10cSrcweir void InitKashidaHandling();
1103cdf0e10cSrcweir void KashidaItemFix( int nMinGlyphPos, int nEndGlyphPos );
1104cdf0e10cSrcweir bool KashidaWordFix( int nMinGlyphPos, int nEndGlyphPos, int* pnCurrentPos );
1105cdf0e10cSrcweir
1106cdf0e10cSrcweir int mnMinKashidaWidth;
1107cdf0e10cSrcweir int mnMinKashidaGlyph;
1108cdf0e10cSrcweir bool mbDisableGlyphInjection;
1109cdf0e10cSrcweir };
1110cdf0e10cSrcweir
1111cdf0e10cSrcweir // -----------------------------------------------------------------------
1112cdf0e10cSrcweir // dynamic loading of usp library
1113cdf0e10cSrcweir
1114cdf0e10cSrcweir static oslModule aUspModule = NULL;
1115cdf0e10cSrcweir static bool bUspEnabled = true;
1116cdf0e10cSrcweir
1117cdf0e10cSrcweir static HRESULT ((WINAPI *pScriptIsComplex)( const WCHAR*, int, DWORD ));
1118cdf0e10cSrcweir static HRESULT ((WINAPI *pScriptItemize)( const WCHAR*, int, int,
1119cdf0e10cSrcweir const SCRIPT_CONTROL*, const SCRIPT_STATE*, SCRIPT_ITEM*, int* ));
1120cdf0e10cSrcweir static HRESULT ((WINAPI *pScriptShape)( HDC, SCRIPT_CACHE*, const WCHAR*,
1121cdf0e10cSrcweir int, int, SCRIPT_ANALYSIS*, WORD*, WORD*, SCRIPT_VISATTR*, int* ));
1122cdf0e10cSrcweir static HRESULT ((WINAPI *pScriptPlace)( HDC, SCRIPT_CACHE*, const WORD*, int,
1123cdf0e10cSrcweir const SCRIPT_VISATTR*, SCRIPT_ANALYSIS*, int*, GOFFSET*, ABC* ));
1124cdf0e10cSrcweir static HRESULT ((WINAPI *pScriptGetLogicalWidths)( const SCRIPT_ANALYSIS*,
1125cdf0e10cSrcweir int, int, const int*, const WORD*, const SCRIPT_VISATTR*, int* ));
1126cdf0e10cSrcweir static HRESULT ((WINAPI *pScriptApplyLogicalWidth)( const int*, int, int, const WORD*,
1127cdf0e10cSrcweir const SCRIPT_VISATTR*, const int*, const SCRIPT_ANALYSIS*, ABC*, int* ));
1128cdf0e10cSrcweir static HRESULT ((WINAPI *pScriptJustify)( const SCRIPT_VISATTR*,
1129cdf0e10cSrcweir const int*, int, int, int, int* ));
1130cdf0e10cSrcweir static HRESULT ((WINAPI *pScriptTextOut)( const HDC, SCRIPT_CACHE*,
1131cdf0e10cSrcweir int, int, UINT, const RECT*, const SCRIPT_ANALYSIS*, const WCHAR*,
1132cdf0e10cSrcweir int, const WORD*, int, const int*, const int*, const GOFFSET* ));
1133cdf0e10cSrcweir static HRESULT ((WINAPI *pScriptGetFontProperties)( HDC, SCRIPT_CACHE*, SCRIPT_FONTPROPERTIES* ));
1134cdf0e10cSrcweir static HRESULT ((WINAPI *pScriptFreeCache)( SCRIPT_CACHE* ));
1135cdf0e10cSrcweir
1136cdf0e10cSrcweir static bool bManualCellAlign = true;
1137cdf0e10cSrcweir
1138cdf0e10cSrcweir // -----------------------------------------------------------------------
1139cdf0e10cSrcweir
1140cdf0e10cSrcweir static bool InitUSP()
1141cdf0e10cSrcweir {
1142aa150a94SHerbert Dürr aUspModule = osl_loadAsciiModule( "usp10", SAL_LOADMODULE_DEFAULT );
1143cdf0e10cSrcweir if( !aUspModule )
1144cdf0e10cSrcweir return (bUspEnabled = false);
1145cdf0e10cSrcweir
1146cdf0e10cSrcweir pScriptIsComplex = (HRESULT (WINAPI*)(const WCHAR*,int,DWORD))
1147cdf0e10cSrcweir osl_getAsciiFunctionSymbol( aUspModule, "ScriptIsComplex" );
1148cdf0e10cSrcweir bUspEnabled &= (NULL != pScriptIsComplex);
1149cdf0e10cSrcweir
1150cdf0e10cSrcweir pScriptItemize = (HRESULT (WINAPI*)(const WCHAR*,int,int,
1151cdf0e10cSrcweir const SCRIPT_CONTROL*,const SCRIPT_STATE*,SCRIPT_ITEM*,int*))
1152cdf0e10cSrcweir osl_getAsciiFunctionSymbol( aUspModule, "ScriptItemize" );
1153cdf0e10cSrcweir bUspEnabled &= (NULL != pScriptItemize);
1154cdf0e10cSrcweir
1155cdf0e10cSrcweir pScriptShape = (HRESULT (WINAPI*)(HDC,SCRIPT_CACHE*,const WCHAR*,
1156cdf0e10cSrcweir int,int,SCRIPT_ANALYSIS*,WORD*,WORD*,SCRIPT_VISATTR*,int*))
1157cdf0e10cSrcweir osl_getAsciiFunctionSymbol( aUspModule, "ScriptShape" );
1158cdf0e10cSrcweir bUspEnabled &= (NULL != pScriptShape);
1159cdf0e10cSrcweir
1160cdf0e10cSrcweir pScriptPlace = (HRESULT (WINAPI*)(HDC, SCRIPT_CACHE*, const WORD*, int,
1161cdf0e10cSrcweir const SCRIPT_VISATTR*,SCRIPT_ANALYSIS*,int*,GOFFSET*,ABC*))
1162cdf0e10cSrcweir osl_getAsciiFunctionSymbol( aUspModule, "ScriptPlace" );
1163cdf0e10cSrcweir bUspEnabled &= (NULL != pScriptPlace);
1164cdf0e10cSrcweir
1165cdf0e10cSrcweir pScriptGetLogicalWidths = (HRESULT (WINAPI*)(const SCRIPT_ANALYSIS*,
1166cdf0e10cSrcweir int,int,const int*,const WORD*,const SCRIPT_VISATTR*,int*))
1167cdf0e10cSrcweir osl_getAsciiFunctionSymbol( aUspModule, "ScriptGetLogicalWidths" );
1168cdf0e10cSrcweir bUspEnabled &= (NULL != pScriptGetLogicalWidths);
1169cdf0e10cSrcweir
1170cdf0e10cSrcweir pScriptApplyLogicalWidth = (HRESULT (WINAPI*)(const int*,int,int,const WORD*,
1171cdf0e10cSrcweir const SCRIPT_VISATTR*,const int*,const SCRIPT_ANALYSIS*,ABC*,int*))
1172cdf0e10cSrcweir osl_getAsciiFunctionSymbol( aUspModule, "ScriptApplyLogicalWidth" );
1173cdf0e10cSrcweir bUspEnabled &= (NULL != pScriptApplyLogicalWidth);
1174cdf0e10cSrcweir
1175cdf0e10cSrcweir pScriptJustify = (HRESULT (WINAPI*)(const SCRIPT_VISATTR*,const int*,
1176cdf0e10cSrcweir int,int,int,int*))
1177cdf0e10cSrcweir osl_getAsciiFunctionSymbol( aUspModule, "ScriptJustify" );
1178cdf0e10cSrcweir bUspEnabled &= (NULL != pScriptJustify);
1179cdf0e10cSrcweir
1180cdf0e10cSrcweir pScriptGetFontProperties = (HRESULT (WINAPI*)( HDC,SCRIPT_CACHE*,SCRIPT_FONTPROPERTIES*))
1181cdf0e10cSrcweir osl_getAsciiFunctionSymbol( aUspModule, "ScriptGetFontProperties" );
1182cdf0e10cSrcweir bUspEnabled &= (NULL != pScriptGetFontProperties);
1183cdf0e10cSrcweir
1184cdf0e10cSrcweir pScriptTextOut = (HRESULT (WINAPI*)(const HDC,SCRIPT_CACHE*,
1185cdf0e10cSrcweir int,int,UINT,const RECT*,const SCRIPT_ANALYSIS*,const WCHAR*,
1186cdf0e10cSrcweir int,const WORD*,int,const int*,const int*,const GOFFSET*))
1187cdf0e10cSrcweir osl_getAsciiFunctionSymbol( aUspModule, "ScriptTextOut" );
1188cdf0e10cSrcweir bUspEnabled &= (NULL != pScriptTextOut);
1189cdf0e10cSrcweir
1190cdf0e10cSrcweir pScriptFreeCache = (HRESULT (WINAPI*)(SCRIPT_CACHE*))
1191cdf0e10cSrcweir osl_getAsciiFunctionSymbol( aUspModule, "ScriptFreeCache" );
1192cdf0e10cSrcweir bUspEnabled &= (NULL != pScriptFreeCache);
1193cdf0e10cSrcweir
1194cdf0e10cSrcweir if( !bUspEnabled )
1195cdf0e10cSrcweir {
1196cdf0e10cSrcweir osl_unloadModule( aUspModule );
1197cdf0e10cSrcweir aUspModule = NULL;
1198cdf0e10cSrcweir }
1199cdf0e10cSrcweir
1200cdf0e10cSrcweir // get the DLL version info
1201cdf0e10cSrcweir int nUspVersion = 0;
1202cdf0e10cSrcweir // TODO: there must be a simpler way to get the friggin version info from OSL?
1203cdf0e10cSrcweir rtl_uString* pModuleURL = NULL;
1204cdf0e10cSrcweir osl_getModuleURLFromAddress( (void*)pScriptIsComplex, &pModuleURL );
1205cdf0e10cSrcweir rtl_uString* pModuleFileName = NULL;
1206cdf0e10cSrcweir if( pModuleURL )
1207cdf0e10cSrcweir osl_getSystemPathFromFileURL( pModuleURL, &pModuleFileName );
1208cdf0e10cSrcweir const sal_Unicode* pModuleFileCStr = NULL;
1209cdf0e10cSrcweir if( pModuleFileName )
1210cdf0e10cSrcweir pModuleFileCStr = rtl_uString_getStr( pModuleFileName );
1211cdf0e10cSrcweir if( pModuleFileCStr )
1212cdf0e10cSrcweir {
1213cdf0e10cSrcweir DWORD nHandle;
1214cdf0e10cSrcweir DWORD nBufSize = ::GetFileVersionInfoSizeW( const_cast<LPWSTR>(reinterpret_cast<LPCWSTR>(pModuleFileCStr)), &nHandle );
1215cdf0e10cSrcweir char* pBuffer = (char*)alloca( nBufSize );
1216cdf0e10cSrcweir BOOL bRC = ::GetFileVersionInfoW( const_cast<LPWSTR>(reinterpret_cast<LPCWSTR>(pModuleFileCStr)), nHandle, nBufSize, pBuffer );
1217cdf0e10cSrcweir VS_FIXEDFILEINFO* pFixedFileInfo = NULL;
1218cdf0e10cSrcweir UINT nFixedFileSize = 0;
1219cdf0e10cSrcweir if( bRC )
1220cdf0e10cSrcweir ::VerQueryValueW( pBuffer, const_cast<LPWSTR>(L"\\"), (void**)&pFixedFileInfo, &nFixedFileSize );
1221cdf0e10cSrcweir if( pFixedFileInfo && pFixedFileInfo->dwSignature == 0xFEEF04BD )
1222cdf0e10cSrcweir nUspVersion = HIWORD(pFixedFileInfo->dwProductVersionMS) * 10000
1223cdf0e10cSrcweir + LOWORD(pFixedFileInfo->dwProductVersionMS);
1224cdf0e10cSrcweir }
1225cdf0e10cSrcweir
1226cdf0e10cSrcweir // #i77976# USP>=1.0600 changed the need to manually align glyphs in their cells
1227cdf0e10cSrcweir if( nUspVersion >= 10600 )
1228cdf0e10cSrcweir bManualCellAlign = false;
1229cdf0e10cSrcweir
1230cdf0e10cSrcweir return bUspEnabled;
1231cdf0e10cSrcweir }
1232cdf0e10cSrcweir
1233cdf0e10cSrcweir // -----------------------------------------------------------------------
1234cdf0e10cSrcweir
1235cdf0e10cSrcweir UniscribeLayout::UniscribeLayout( HDC hDC,
1236cdf0e10cSrcweir const ImplWinFontData& rWinFontData, ImplWinFontEntry& rWinFontEntry )
1237cdf0e10cSrcweir : WinLayout( hDC, rWinFontData, rWinFontEntry ),
1238cdf0e10cSrcweir mnItemCount( 0 ),
1239cdf0e10cSrcweir mpScriptItems( NULL ),
1240cdf0e10cSrcweir mpVisualItems( NULL ),
1241cdf0e10cSrcweir mpLogClusters( NULL ),
1242cdf0e10cSrcweir mpCharWidths( NULL ),
1243cdf0e10cSrcweir mnCharCapacity( 0 ),
1244cdf0e10cSrcweir mnSubStringMin( 0 ),
1245cdf0e10cSrcweir mnGlyphCapacity( 0 ),
1246cdf0e10cSrcweir mnGlyphCount( 0 ),
1247cdf0e10cSrcweir mpOutGlyphs( NULL ),
1248cdf0e10cSrcweir mpGlyphAdvances( NULL ),
1249cdf0e10cSrcweir mpJustifications( NULL ),
1250cdf0e10cSrcweir mpGlyphOffsets( NULL ),
1251cdf0e10cSrcweir mpVisualAttrs( NULL ),
1252cdf0e10cSrcweir mpGlyphs2Chars( NULL ),
1253cdf0e10cSrcweir mnMinKashidaGlyph( 0 ),
1254cdf0e10cSrcweir mbDisableGlyphInjection( false )
1255cdf0e10cSrcweir {}
1256cdf0e10cSrcweir
1257cdf0e10cSrcweir // -----------------------------------------------------------------------
1258cdf0e10cSrcweir
1259cdf0e10cSrcweir UniscribeLayout::~UniscribeLayout()
1260cdf0e10cSrcweir {
1261cdf0e10cSrcweir delete[] mpScriptItems;
1262cdf0e10cSrcweir delete[] mpVisualItems;
1263cdf0e10cSrcweir delete[] mpLogClusters;
1264cdf0e10cSrcweir delete[] mpCharWidths;
1265cdf0e10cSrcweir delete[] mpOutGlyphs;
1266cdf0e10cSrcweir delete[] mpGlyphAdvances;
1267cdf0e10cSrcweir delete[] mpJustifications;
1268cdf0e10cSrcweir delete[] mpGlyphOffsets;
1269cdf0e10cSrcweir delete[] mpVisualAttrs;
1270cdf0e10cSrcweir delete[] mpGlyphs2Chars;
1271cdf0e10cSrcweir }
1272cdf0e10cSrcweir
1273cdf0e10cSrcweir // -----------------------------------------------------------------------
1274cdf0e10cSrcweir
1275cdf0e10cSrcweir bool UniscribeLayout::LayoutText( ImplLayoutArgs& rArgs )
1276cdf0e10cSrcweir {
1277cdf0e10cSrcweir // for a base layout only the context glyphs have to be dropped
1278cdf0e10cSrcweir // => when the whole string is involved there is no extra context
1279cdf0e10cSrcweir typedef std::vector<int> TIntVector;
1280cdf0e10cSrcweir TIntVector aDropChars;
1281cdf0e10cSrcweir if( rArgs.mnFlags & SAL_LAYOUT_FOR_FALLBACK )
1282cdf0e10cSrcweir {
1283cdf0e10cSrcweir // calculate superfluous context char positions
1284cdf0e10cSrcweir aDropChars.push_back( 0 );
1285cdf0e10cSrcweir aDropChars.push_back( rArgs.mnLength );
1286cdf0e10cSrcweir int nMin, nEnd;
1287cdf0e10cSrcweir bool bRTL;
1288cdf0e10cSrcweir for( rArgs.ResetPos(); rArgs.GetNextRun( &nMin, &nEnd, &bRTL ); )
1289cdf0e10cSrcweir {
1290cdf0e10cSrcweir aDropChars.push_back( nMin );
1291cdf0e10cSrcweir aDropChars.push_back( nEnd );
1292cdf0e10cSrcweir }
1293cdf0e10cSrcweir // prepare aDropChars for binary search which will allow to
1294cdf0e10cSrcweir // not bother with visual items that will be dropped anyway
1295cdf0e10cSrcweir std::sort( aDropChars.begin(), aDropChars.end() );
1296cdf0e10cSrcweir }
1297cdf0e10cSrcweir
1298cdf0e10cSrcweir // prepare layout
1299cdf0e10cSrcweir // TODO: fix case when recyclying old UniscribeLayout object
1300cdf0e10cSrcweir mnMinCharPos = rArgs.mnMinCharPos;
1301cdf0e10cSrcweir mnEndCharPos = rArgs.mnEndCharPos;
1302cdf0e10cSrcweir
1303cdf0e10cSrcweir // determine script items from string
1304cdf0e10cSrcweir
1305cdf0e10cSrcweir // prepare itemization
1306cdf0e10cSrcweir // TODO: try to avoid itemization since it costs a lot of performance
1307cdf0e10cSrcweir SCRIPT_STATE aScriptState = {0,false,false,false,false,false,false,false,false,0,0};
1308cdf0e10cSrcweir aScriptState.uBidiLevel = (0 != (rArgs.mnFlags & SAL_LAYOUT_BIDI_RTL));
1309cdf0e10cSrcweir aScriptState.fOverrideDirection = (0 != (rArgs.mnFlags & SAL_LAYOUT_BIDI_STRONG));
1310cdf0e10cSrcweir aScriptState.fDigitSubstitute = (0 != (rArgs.mnFlags & SAL_LAYOUT_SUBSTITUTE_DIGITS));
1311cdf0e10cSrcweir aScriptState.fArabicNumContext = aScriptState.fDigitSubstitute & aScriptState.uBidiLevel;
1312cdf0e10cSrcweir DWORD nLangId = 0; // TODO: get language from font
1313cdf0e10cSrcweir SCRIPT_CONTROL aScriptControl = {nLangId,false,false,false,false,false,false,false,false,0};
1314cdf0e10cSrcweir aScriptControl.fNeutralOverride = aScriptState.fOverrideDirection;
1315cdf0e10cSrcweir aScriptControl.fContextDigits = (0 != (rArgs.mnFlags & SAL_LAYOUT_SUBSTITUTE_DIGITS));
1316fed8d294SHerbert Dürr aScriptControl.fMergeNeutralItems = true;
1317cdf0e10cSrcweir // determine relevant substring and work only on it
1318cdf0e10cSrcweir // when Bidi status is unknown we need to look at the whole string though
1319cdf0e10cSrcweir mnSubStringMin = 0;
1320cdf0e10cSrcweir int nSubStringEnd = rArgs.mnLength;
1321cdf0e10cSrcweir if( aScriptState.fOverrideDirection )
1322cdf0e10cSrcweir {
1323cdf0e10cSrcweir // TODO: limit substring to portion limits
1324cdf0e10cSrcweir mnSubStringMin = rArgs.mnMinCharPos - 8;
1325cdf0e10cSrcweir if( mnSubStringMin < 0 )
1326cdf0e10cSrcweir mnSubStringMin = 0;
1327cdf0e10cSrcweir nSubStringEnd = rArgs.mnEndCharPos + 8;
1328cdf0e10cSrcweir if( nSubStringEnd > rArgs.mnLength )
1329cdf0e10cSrcweir nSubStringEnd = rArgs.mnLength;
1330cdf0e10cSrcweir
1331cdf0e10cSrcweir }
1332cdf0e10cSrcweir
1333cdf0e10cSrcweir // now itemize the substring with its context
1334cdf0e10cSrcweir for( int nItemCapacity = 16;; nItemCapacity *= 8 )
1335cdf0e10cSrcweir {
1336cdf0e10cSrcweir mpScriptItems = new SCRIPT_ITEM[ nItemCapacity ];
1337cdf0e10cSrcweir HRESULT nRC = (*pScriptItemize)(
1338cdf0e10cSrcweir reinterpret_cast<LPCWSTR>(rArgs.mpStr + mnSubStringMin), nSubStringEnd - mnSubStringMin,
1339cdf0e10cSrcweir nItemCapacity - 1, &aScriptControl, &aScriptState,
1340cdf0e10cSrcweir mpScriptItems, &mnItemCount );
1341cdf0e10cSrcweir if( !nRC ) // break loop when everything is correctly itemized
1342cdf0e10cSrcweir break;
1343cdf0e10cSrcweir
1344cdf0e10cSrcweir // prepare bigger buffers for another itemization round
1345cdf0e10cSrcweir delete[] mpScriptItems;
1346cdf0e10cSrcweir mpScriptItems = NULL;
1347cdf0e10cSrcweir if( nRC != E_OUTOFMEMORY )
1348cdf0e10cSrcweir return false;
1349cdf0e10cSrcweir if( nItemCapacity > (nSubStringEnd - mnSubStringMin) + 16 )
1350cdf0e10cSrcweir return false;
1351cdf0e10cSrcweir }
1352cdf0e10cSrcweir
1353cdf0e10cSrcweir // calculate the order of visual items
1354cdf0e10cSrcweir int nItem, i;
1355cdf0e10cSrcweir
1356cdf0e10cSrcweir // adjust char positions by substring offset
1357cdf0e10cSrcweir for( nItem = 0; nItem <= mnItemCount; ++nItem )
1358cdf0e10cSrcweir mpScriptItems[ nItem ].iCharPos += mnSubStringMin;
1359cdf0e10cSrcweir // default visual item ordering
1360cdf0e10cSrcweir mpVisualItems = new VisualItem[ mnItemCount ];
1361cdf0e10cSrcweir for( nItem = 0; nItem < mnItemCount; ++nItem )
1362cdf0e10cSrcweir {
1363cdf0e10cSrcweir // initialize char specific item info
1364cdf0e10cSrcweir VisualItem& rVisualItem = mpVisualItems[ nItem ];
1365cdf0e10cSrcweir SCRIPT_ITEM* pScriptItem = &mpScriptItems[ nItem ];
1366cdf0e10cSrcweir rVisualItem.mpScriptItem = pScriptItem;
1367cdf0e10cSrcweir rVisualItem.mnMinCharPos = pScriptItem[0].iCharPos;
1368cdf0e10cSrcweir rVisualItem.mnEndCharPos = pScriptItem[1].iCharPos;
1369cdf0e10cSrcweir }
1370cdf0e10cSrcweir
1371cdf0e10cSrcweir // reorder visual item order if needed
1372cdf0e10cSrcweir if( rArgs.mnFlags & SAL_LAYOUT_BIDI_STRONG )
1373cdf0e10cSrcweir {
1374cdf0e10cSrcweir // force RTL item ordering if requested
1375cdf0e10cSrcweir if( rArgs.mnFlags & SAL_LAYOUT_BIDI_RTL )
1376cdf0e10cSrcweir {
1377cdf0e10cSrcweir VisualItem* pVI0 = &mpVisualItems[ 0 ];
1378cdf0e10cSrcweir VisualItem* pVI1 = &mpVisualItems[ mnItemCount ];
1379cdf0e10cSrcweir while( pVI0 < --pVI1 )
1380cdf0e10cSrcweir {
1381cdf0e10cSrcweir VisualItem aVtmp = *pVI0;
1382cdf0e10cSrcweir *(pVI0++) = *pVI1;
1383cdf0e10cSrcweir *pVI1 = aVtmp;
1384cdf0e10cSrcweir }
1385cdf0e10cSrcweir }
1386cdf0e10cSrcweir }
1387cdf0e10cSrcweir else if( mnItemCount > 1 )
1388cdf0e10cSrcweir {
1389cdf0e10cSrcweir // apply bidi algorithm's rule L2 on item level
1390cdf0e10cSrcweir // TODO: use faster L2 algorithm
1391cdf0e10cSrcweir int nMaxBidiLevel = 0;
1392cdf0e10cSrcweir VisualItem* pVI = &mpVisualItems[0];
1393cdf0e10cSrcweir VisualItem* const pVIend = pVI + mnItemCount;
1394cdf0e10cSrcweir for(; pVI < pVIend; ++pVI )
1395cdf0e10cSrcweir if( nMaxBidiLevel < pVI->mpScriptItem->a.s.uBidiLevel )
1396cdf0e10cSrcweir nMaxBidiLevel = pVI->mpScriptItem->a.s.uBidiLevel;
1397cdf0e10cSrcweir
1398cdf0e10cSrcweir while( --nMaxBidiLevel >= 0 )
1399cdf0e10cSrcweir {
1400cdf0e10cSrcweir for( pVI = &mpVisualItems[0]; pVI < pVIend; )
1401cdf0e10cSrcweir {
1402cdf0e10cSrcweir // find item range that needs reordering
1403cdf0e10cSrcweir for(; pVI < pVIend; ++pVI )
1404cdf0e10cSrcweir if( nMaxBidiLevel < pVI->mpScriptItem->a.s.uBidiLevel )
1405cdf0e10cSrcweir break;
1406cdf0e10cSrcweir VisualItem* pVImin = pVI++;
1407cdf0e10cSrcweir for(; pVI < pVIend; ++pVI )
1408cdf0e10cSrcweir if( nMaxBidiLevel >= pVI->mpScriptItem->a.s.uBidiLevel )
1409cdf0e10cSrcweir break;
1410cdf0e10cSrcweir VisualItem* pVImax = pVI++;
1411cdf0e10cSrcweir
1412cdf0e10cSrcweir // reverse order of items in this range
1413cdf0e10cSrcweir while( pVImin < --pVImax )
1414cdf0e10cSrcweir {
1415cdf0e10cSrcweir VisualItem aVtmp = *pVImin;
1416cdf0e10cSrcweir *(pVImin++) = *pVImax;
1417cdf0e10cSrcweir *pVImax = aVtmp;
1418cdf0e10cSrcweir }
1419cdf0e10cSrcweir }
1420cdf0e10cSrcweir }
1421cdf0e10cSrcweir }
1422cdf0e10cSrcweir
1423cdf0e10cSrcweir // allocate arrays
1424cdf0e10cSrcweir // TODO: when reusing object reuse old allocations or delete them
1425cdf0e10cSrcweir // TODO: use only [nSubStringMin..nSubStringEnd) instead of [0..nSubStringEnd)
1426cdf0e10cSrcweir mnCharCapacity = nSubStringEnd;
1427cdf0e10cSrcweir mpLogClusters = new WORD[ mnCharCapacity ];
1428cdf0e10cSrcweir mpCharWidths = new int[ mnCharCapacity ];
1429cdf0e10cSrcweir
1430cdf0e10cSrcweir mnGlyphCount = 0;
1431cdf0e10cSrcweir mnGlyphCapacity = 16 + 4 * (nSubStringEnd - mnSubStringMin); // worst case assumption
1432cdf0e10cSrcweir mpGlyphAdvances = new int[ mnGlyphCapacity ];
1433cdf0e10cSrcweir mpOutGlyphs = new WORD[ mnGlyphCapacity ];
1434cdf0e10cSrcweir mpGlyphOffsets = new GOFFSET[ mnGlyphCapacity ];
1435cdf0e10cSrcweir mpVisualAttrs = new SCRIPT_VISATTR[ mnGlyphCapacity ];
1436cdf0e10cSrcweir
1437cdf0e10cSrcweir long nXOffset = 0;
1438cdf0e10cSrcweir for( int j = mnSubStringMin; j < nSubStringEnd; ++j )
1439cdf0e10cSrcweir mpCharWidths[j] = 0;
1440cdf0e10cSrcweir
1441cdf0e10cSrcweir // layout script items
1442cdf0e10cSrcweir SCRIPT_CACHE& rScriptCache = GetScriptCache();
1443cdf0e10cSrcweir for( nItem = 0; nItem < mnItemCount; ++nItem )
1444cdf0e10cSrcweir {
1445cdf0e10cSrcweir VisualItem& rVisualItem = mpVisualItems[ nItem ];
1446cdf0e10cSrcweir
1447cdf0e10cSrcweir // initialize glyph specific item info
1448cdf0e10cSrcweir rVisualItem.mnMinGlyphPos = mnGlyphCount;
1449cdf0e10cSrcweir rVisualItem.mnEndGlyphPos = 0;
1450cdf0e10cSrcweir rVisualItem.mnXOffset = nXOffset;
1451cdf0e10cSrcweir //rVisualItem.mnPixelWidth = 0;
1452cdf0e10cSrcweir
1453cdf0e10cSrcweir // shortcut ignorable items
1454cdf0e10cSrcweir if( (rArgs.mnEndCharPos <= rVisualItem.mnMinCharPos)
1455cdf0e10cSrcweir || (rArgs.mnMinCharPos >= rVisualItem.mnEndCharPos) )
1456cdf0e10cSrcweir {
1457cdf0e10cSrcweir for( int i = rVisualItem.mnMinCharPos; i < rVisualItem.mnEndCharPos; ++i )
1458cdf0e10cSrcweir mpLogClusters[i] = sal::static_int_cast<WORD>(~0U);
1459cdf0e10cSrcweir continue;
1460cdf0e10cSrcweir }
1461cdf0e10cSrcweir
1462cdf0e10cSrcweir // override bidi analysis if requested
1463cdf0e10cSrcweir if( rArgs.mnFlags & SAL_LAYOUT_BIDI_STRONG )
1464cdf0e10cSrcweir {
1465cdf0e10cSrcweir // FIXME: is this intended ?
1466cdf0e10cSrcweir rVisualItem.mpScriptItem->a.fRTL = (aScriptState.uBidiLevel & 1);
1467cdf0e10cSrcweir rVisualItem.mpScriptItem->a.s.uBidiLevel = aScriptState.uBidiLevel;
1468cdf0e10cSrcweir rVisualItem.mpScriptItem->a.s.fOverrideDirection = aScriptState.fOverrideDirection;
1469cdf0e10cSrcweir }
1470cdf0e10cSrcweir
1471cdf0e10cSrcweir // convert the unicodes to glyphs
1472cdf0e10cSrcweir int nGlyphCount = 0;
1473cdf0e10cSrcweir int nCharCount = rVisualItem.mnEndCharPos - rVisualItem.mnMinCharPos;
1474cdf0e10cSrcweir HRESULT nRC = (*pScriptShape)( mhDC, &rScriptCache,
1475cdf0e10cSrcweir reinterpret_cast<LPCWSTR>(rArgs.mpStr + rVisualItem.mnMinCharPos),
1476cdf0e10cSrcweir nCharCount,
1477cdf0e10cSrcweir mnGlyphCapacity - rVisualItem.mnMinGlyphPos, // problem when >0xFFFF
1478cdf0e10cSrcweir &rVisualItem.mpScriptItem->a,
1479cdf0e10cSrcweir mpOutGlyphs + rVisualItem.mnMinGlyphPos,
1480cdf0e10cSrcweir mpLogClusters + rVisualItem.mnMinCharPos,
1481cdf0e10cSrcweir mpVisualAttrs + rVisualItem.mnMinGlyphPos,
1482cdf0e10cSrcweir &nGlyphCount );
1483cdf0e10cSrcweir
1484cdf0e10cSrcweir // find and handle problems in the unicode to glyph conversion
1485cdf0e10cSrcweir if( nRC == USP_E_SCRIPT_NOT_IN_FONT )
1486cdf0e10cSrcweir {
1487cdf0e10cSrcweir // the whole visual item needs a fallback, but make sure that the next
1488cdf0e10cSrcweir // fallback request is limited to the characters in the original request
1489cdf0e10cSrcweir // => this is handled in ImplLayoutArgs::PrepareFallback()
1490cdf0e10cSrcweir rArgs.NeedFallback( rVisualItem.mnMinCharPos, rVisualItem.mnEndCharPos,
1491cdf0e10cSrcweir rVisualItem.IsRTL() );
1492cdf0e10cSrcweir
1493cdf0e10cSrcweir // don't bother to do a default layout in a fallback level
1494cdf0e10cSrcweir if( 0 != (rArgs.mnFlags & SAL_LAYOUT_FOR_FALLBACK) )
1495cdf0e10cSrcweir continue;
1496cdf0e10cSrcweir
1497cdf0e10cSrcweir // the primitive layout engine is good enough for the default layout
1498cdf0e10cSrcweir rVisualItem.mpScriptItem->a.eScript = SCRIPT_UNDEFINED;
1499cdf0e10cSrcweir nRC = (*pScriptShape)( mhDC, &rScriptCache,
1500cdf0e10cSrcweir reinterpret_cast<LPCWSTR>(rArgs.mpStr + rVisualItem.mnMinCharPos),
1501cdf0e10cSrcweir nCharCount,
1502cdf0e10cSrcweir mnGlyphCapacity - rVisualItem.mnMinGlyphPos,
1503cdf0e10cSrcweir &rVisualItem.mpScriptItem->a,
1504cdf0e10cSrcweir mpOutGlyphs + rVisualItem.mnMinGlyphPos,
1505cdf0e10cSrcweir mpLogClusters + rVisualItem.mnMinCharPos,
1506cdf0e10cSrcweir mpVisualAttrs + rVisualItem.mnMinGlyphPos,
1507cdf0e10cSrcweir &nGlyphCount );
1508cdf0e10cSrcweir
1509cdf0e10cSrcweir if( nRC != 0 )
1510cdf0e10cSrcweir continue;
1511cdf0e10cSrcweir
1512cdf0e10cSrcweir #if 0 // keep the glyphs for now because they are better than nothing
1513cdf0e10cSrcweir // mark as NotDef glyphs
1514cdf0e10cSrcweir for( i = 0; i < nGlyphCount; ++i )
1515cdf0e10cSrcweir mpOutGlyphs[ i + rVisualItem.mnMinGlyphPos ] = 0;
1516cdf0e10cSrcweir #endif
1517cdf0e10cSrcweir }
1518cdf0e10cSrcweir else if( nRC != 0 )
1519cdf0e10cSrcweir // something undefined happened => give up for this visual item
1520cdf0e10cSrcweir continue;
1521cdf0e10cSrcweir else // if( nRC == 0 )
1522cdf0e10cSrcweir {
1523cdf0e10cSrcweir // check if there are any NotDef glyphs
1524cdf0e10cSrcweir for( i = 0; i < nGlyphCount; ++i )
1525cdf0e10cSrcweir if( 0 == mpOutGlyphs[ i + rVisualItem.mnMinGlyphPos ] )
1526cdf0e10cSrcweir break;
1527cdf0e10cSrcweir if( i < nGlyphCount )
1528cdf0e10cSrcweir {
1529cdf0e10cSrcweir // clip charpos limits to the layout string without context
1530cdf0e10cSrcweir int nMinCharPos = rVisualItem.mnMinCharPos;
1531cdf0e10cSrcweir if( nMinCharPos < rArgs.mnMinCharPos )
1532cdf0e10cSrcweir nMinCharPos = rArgs.mnMinCharPos;
1533cdf0e10cSrcweir int nEndCharPos = rVisualItem.mnEndCharPos;
1534cdf0e10cSrcweir if( nEndCharPos > rArgs.mnEndCharPos )
1535cdf0e10cSrcweir nEndCharPos = rArgs.mnEndCharPos;
1536cdf0e10cSrcweir // request fallback for individual NotDef glyphs
1537cdf0e10cSrcweir do
1538cdf0e10cSrcweir {
1539cdf0e10cSrcweir // ignore non-NotDef glyphs
1540cdf0e10cSrcweir if( 0 != mpOutGlyphs[ i + rVisualItem.mnMinGlyphPos ] )
1541cdf0e10cSrcweir continue;
1542cdf0e10cSrcweir mpOutGlyphs[ i + rVisualItem.mnMinGlyphPos ] = DROPPED_OUTGLYPH;
1543cdf0e10cSrcweir // request fallback for the whole cell that resulted in a NotDef glyph
1544cdf0e10cSrcweir // TODO: optimize algorithm
1545cdf0e10cSrcweir const bool bRTL = rVisualItem.IsRTL();
1546cdf0e10cSrcweir if( !bRTL )
1547cdf0e10cSrcweir {
1548cdf0e10cSrcweir // request fallback for the left-to-right cell
1549cdf0e10cSrcweir for( int c = nMinCharPos; c < nEndCharPos; ++c )
1550cdf0e10cSrcweir {
1551cdf0e10cSrcweir if( mpLogClusters[ c ] == i )
1552cdf0e10cSrcweir {
1553fed8d294SHerbert Dürr // #i55716# skip WORDJOINER
1554cdf0e10cSrcweir if( rArgs.mpStr[ c ] == 0x2060 )
1555cdf0e10cSrcweir mpOutGlyphs[ i + rVisualItem.mnMinGlyphPos ] = 1;
1556cdf0e10cSrcweir else
1557cdf0e10cSrcweir rArgs.NeedFallback( c, false );
1558cdf0e10cSrcweir }
1559cdf0e10cSrcweir }
1560cdf0e10cSrcweir }
1561cdf0e10cSrcweir else
1562cdf0e10cSrcweir {
1563cdf0e10cSrcweir // request fallback for the right to left cell
1564cdf0e10cSrcweir for( int c = nEndCharPos; --c >= nMinCharPos; )
1565cdf0e10cSrcweir {
1566cdf0e10cSrcweir if( mpLogClusters[ c ] == i )
1567cdf0e10cSrcweir {
1568fed8d294SHerbert Dürr // #i55716# skip WORDJOINER
1569cdf0e10cSrcweir if( rArgs.mpStr[ c ] == 0x2060 )
1570cdf0e10cSrcweir mpOutGlyphs[ i + rVisualItem.mnMinGlyphPos ] = 1;
1571cdf0e10cSrcweir else
1572cdf0e10cSrcweir rArgs.NeedFallback( c, true );
1573cdf0e10cSrcweir }
1574cdf0e10cSrcweir }
1575cdf0e10cSrcweir }
1576cdf0e10cSrcweir } while( ++i < nGlyphCount );
1577cdf0e10cSrcweir }
1578cdf0e10cSrcweir }
1579cdf0e10cSrcweir
1580cdf0e10cSrcweir // now place the glyphs
1581cdf0e10cSrcweir nRC = (*pScriptPlace)( mhDC, &rScriptCache,
1582cdf0e10cSrcweir mpOutGlyphs + rVisualItem.mnMinGlyphPos,
1583cdf0e10cSrcweir nGlyphCount,
1584cdf0e10cSrcweir mpVisualAttrs + rVisualItem.mnMinGlyphPos,
1585cdf0e10cSrcweir &rVisualItem.mpScriptItem->a,
1586cdf0e10cSrcweir mpGlyphAdvances + rVisualItem.mnMinGlyphPos,
1587cdf0e10cSrcweir mpGlyphOffsets + rVisualItem.mnMinGlyphPos,
1588cdf0e10cSrcweir &rVisualItem.maABCWidths );
1589cdf0e10cSrcweir
1590cdf0e10cSrcweir if( nRC != 0 )
1591cdf0e10cSrcweir continue;
1592cdf0e10cSrcweir
1593cdf0e10cSrcweir // calculate the logical char widths from the glyph layout
1594cdf0e10cSrcweir nRC = (*pScriptGetLogicalWidths)(
1595cdf0e10cSrcweir &rVisualItem.mpScriptItem->a,
1596cdf0e10cSrcweir nCharCount, nGlyphCount,
1597cdf0e10cSrcweir mpGlyphAdvances + rVisualItem.mnMinGlyphPos,
1598cdf0e10cSrcweir mpLogClusters + rVisualItem.mnMinCharPos,
1599cdf0e10cSrcweir mpVisualAttrs + rVisualItem.mnMinGlyphPos,
1600cdf0e10cSrcweir mpCharWidths + rVisualItem.mnMinCharPos );
1601cdf0e10cSrcweir
1602cdf0e10cSrcweir // update the glyph counters
1603cdf0e10cSrcweir mnGlyphCount += nGlyphCount;
1604cdf0e10cSrcweir rVisualItem.mnEndGlyphPos = mnGlyphCount;
1605cdf0e10cSrcweir
1606cdf0e10cSrcweir // update nXOffset
1607cdf0e10cSrcweir int nEndGlyphPos;
1608cdf0e10cSrcweir if( GetItemSubrange( rVisualItem, i, nEndGlyphPos ) )
1609cdf0e10cSrcweir for(; i < nEndGlyphPos; ++i )
1610cdf0e10cSrcweir nXOffset += mpGlyphAdvances[ i ];
1611cdf0e10cSrcweir
1612cdf0e10cSrcweir // TODO: shrink glyphpos limits to match charpos/fallback limits
1613cdf0e10cSrcweir //pVI->mnMinGlyphPos = nMinGlyphPos;
1614cdf0e10cSrcweir //pVI->mnEndGlyphPos = nEndGlyphPos;
1615cdf0e10cSrcweir
1616cdf0e10cSrcweir // drop the superfluous context glyphs
1617cdf0e10cSrcweir TIntVector::const_iterator it = aDropChars.begin();
1618cdf0e10cSrcweir while( it != aDropChars.end() )
1619cdf0e10cSrcweir {
1620cdf0e10cSrcweir // find matching "drop range"
1621cdf0e10cSrcweir int nMinDropPos = *(it++); // begin of drop range
1622cdf0e10cSrcweir if( nMinDropPos >= rVisualItem.mnEndCharPos )
1623cdf0e10cSrcweir break;
1624cdf0e10cSrcweir int nEndDropPos = *(it++); // end of drop range
1625cdf0e10cSrcweir if( nEndDropPos <= rVisualItem.mnMinCharPos )
1626cdf0e10cSrcweir continue;
1627cdf0e10cSrcweir // clip "drop range" to visual item's char range
1628cdf0e10cSrcweir if( nMinDropPos <= rVisualItem.mnMinCharPos )
1629cdf0e10cSrcweir {
1630cdf0e10cSrcweir nMinDropPos = rVisualItem.mnMinCharPos;
1631cdf0e10cSrcweir // drop the whole visual item if possible
1632cdf0e10cSrcweir if( nEndDropPos >= rVisualItem.mnEndCharPos )
1633cdf0e10cSrcweir {
1634cdf0e10cSrcweir rVisualItem.mnEndGlyphPos = 0;
1635cdf0e10cSrcweir break;
1636cdf0e10cSrcweir }
1637cdf0e10cSrcweir }
1638cdf0e10cSrcweir if( nEndDropPos > rVisualItem.mnEndCharPos )
1639cdf0e10cSrcweir nEndDropPos = rVisualItem.mnEndCharPos;
1640cdf0e10cSrcweir
1641cdf0e10cSrcweir // drop the glyphs which correspond to the charpos range
1642cdf0e10cSrcweir // drop the corresponding glyphs in the cluster
1643cdf0e10cSrcweir for( int c = nMinDropPos; c < nEndDropPos; ++c )
1644cdf0e10cSrcweir {
1645cdf0e10cSrcweir int nGlyphPos = mpLogClusters[c] + rVisualItem.mnMinGlyphPos;
1646cdf0e10cSrcweir // no need to bother when the cluster was already dropped
1647cdf0e10cSrcweir if( mpOutGlyphs[ nGlyphPos ] != DROPPED_OUTGLYPH )
1648cdf0e10cSrcweir {
1649cdf0e10cSrcweir for(;;)
1650cdf0e10cSrcweir {
1651cdf0e10cSrcweir mpOutGlyphs[ nGlyphPos ] = DROPPED_OUTGLYPH;
1652cdf0e10cSrcweir // until the end of visual item
1653cdf0e10cSrcweir if( ++nGlyphPos >= rVisualItem.mnEndGlyphPos )
1654cdf0e10cSrcweir break;
1655cdf0e10cSrcweir // until the next cluster start
1656cdf0e10cSrcweir if( mpVisualAttrs[ nGlyphPos ].fClusterStart )
1657cdf0e10cSrcweir break;
1658cdf0e10cSrcweir }
1659cdf0e10cSrcweir }
1660cdf0e10cSrcweir }
1661cdf0e10cSrcweir }
1662cdf0e10cSrcweir }
1663cdf0e10cSrcweir
1664cdf0e10cSrcweir // scale layout metrics if needed
1665cdf0e10cSrcweir // TODO: does it make the code more simple if the metric scaling
1666cdf0e10cSrcweir // is moved to the methods that need metric scaling (e.g. FillDXArray())?
1667cdf0e10cSrcweir if( mfFontScale != 1.0 )
1668cdf0e10cSrcweir {
1669cdf0e10cSrcweir mnBaseAdv = (int)((double)mnBaseAdv*mfFontScale);
1670cdf0e10cSrcweir
1671cdf0e10cSrcweir for( i = 0; i < mnItemCount; ++i )
1672cdf0e10cSrcweir mpVisualItems[i].mnXOffset = (int)((double)mpVisualItems[i].mnXOffset*mfFontScale);
1673cdf0e10cSrcweir
1674cdf0e10cSrcweir mnBaseAdv = (int)((double)mnBaseAdv*mfFontScale);
1675cdf0e10cSrcweir for( i = 0; i < mnGlyphCount; ++i )
1676cdf0e10cSrcweir {
1677cdf0e10cSrcweir mpGlyphAdvances[i] = (int)(mpGlyphAdvances[i] * mfFontScale);
1678cdf0e10cSrcweir mpGlyphOffsets[i].du = (LONG)(mpGlyphOffsets[i].du * mfFontScale);
1679cdf0e10cSrcweir mpGlyphOffsets[i].dv = (LONG)(mpGlyphOffsets[i].dv * mfFontScale);
1680cdf0e10cSrcweir // mpJustifications are still NULL
1681cdf0e10cSrcweir }
1682cdf0e10cSrcweir
1683cdf0e10cSrcweir for( i = mnSubStringMin; i < nSubStringEnd; ++i )
1684cdf0e10cSrcweir mpCharWidths[i] = (int)(mpCharWidths[i] * mfFontScale);
1685cdf0e10cSrcweir }
1686cdf0e10cSrcweir
1687cdf0e10cSrcweir return true;
1688cdf0e10cSrcweir }
1689cdf0e10cSrcweir
1690cdf0e10cSrcweir // -----------------------------------------------------------------------
1691cdf0e10cSrcweir
1692cdf0e10cSrcweir // calculate the range of relevant glyphs for this visual item
1693cdf0e10cSrcweir bool UniscribeLayout::GetItemSubrange( const VisualItem& rVisualItem,
1694cdf0e10cSrcweir int& rMinGlyphPos, int& rEndGlyphPos ) const
1695cdf0e10cSrcweir {
1696cdf0e10cSrcweir // return early when nothing of interest in this item
1697cdf0e10cSrcweir if( rVisualItem.IsEmpty()
1698cdf0e10cSrcweir || (rVisualItem.mnEndCharPos <= mnMinCharPos)
1699cdf0e10cSrcweir || (mnEndCharPos <= rVisualItem.mnMinCharPos) )
1700cdf0e10cSrcweir return false;
1701cdf0e10cSrcweir
1702cdf0e10cSrcweir // default: subrange is complete range
1703cdf0e10cSrcweir rMinGlyphPos = rVisualItem.mnMinGlyphPos;
1704cdf0e10cSrcweir rEndGlyphPos = rVisualItem.mnEndGlyphPos;
1705cdf0e10cSrcweir
1706cdf0e10cSrcweir // return early when the whole item is of interest
1707cdf0e10cSrcweir if( (mnMinCharPos <= rVisualItem.mnMinCharPos)
1708cdf0e10cSrcweir && (rVisualItem.mnEndCharPos <= mnEndCharPos ) )
1709cdf0e10cSrcweir return true;
1710cdf0e10cSrcweir
1711cdf0e10cSrcweir // get glyph range from char range by looking at cluster boundries
1712cdf0e10cSrcweir // TODO: optimize for case that LTR/RTL correspond to monotonous glyph indexes
1713cdf0e10cSrcweir rMinGlyphPos = rVisualItem.mnEndGlyphPos;
1714cdf0e10cSrcweir int nMaxGlyphPos = 0;
1715cdf0e10cSrcweir
1716cdf0e10cSrcweir int i = mnMinCharPos;
1717cdf0e10cSrcweir if( i < rVisualItem.mnMinCharPos )
1718cdf0e10cSrcweir i = rVisualItem.mnMinCharPos;
1719cdf0e10cSrcweir int nCharPosLimit = rVisualItem.mnEndCharPos;
1720cdf0e10cSrcweir if( nCharPosLimit > mnEndCharPos )
1721cdf0e10cSrcweir nCharPosLimit = mnEndCharPos;
1722cdf0e10cSrcweir for(; i < nCharPosLimit; ++i )
1723cdf0e10cSrcweir {
1724cdf0e10cSrcweir int n = mpLogClusters[ i ] + rVisualItem.mnMinGlyphPos;
1725cdf0e10cSrcweir if( rMinGlyphPos > n )
1726cdf0e10cSrcweir rMinGlyphPos = n;
1727cdf0e10cSrcweir if( nMaxGlyphPos < n )
1728cdf0e10cSrcweir nMaxGlyphPos = n;
1729cdf0e10cSrcweir }
1730cdf0e10cSrcweir if (nMaxGlyphPos > rVisualItem.mnEndGlyphPos)
1731cdf0e10cSrcweir nMaxGlyphPos = rVisualItem.mnEndGlyphPos - 1;
1732cdf0e10cSrcweir
1733cdf0e10cSrcweir // extend the glyph range to account for all glyphs in referenced clusters
1734cdf0e10cSrcweir if( !rVisualItem.IsRTL() ) // LTR-item
1735cdf0e10cSrcweir {
1736cdf0e10cSrcweir // extend to rightmost glyph of rightmost referenced cluster
1737cdf0e10cSrcweir for( i = nMaxGlyphPos; ++i < rVisualItem.mnEndGlyphPos; nMaxGlyphPos = i )
1738cdf0e10cSrcweir if( mpVisualAttrs[i].fClusterStart )
1739cdf0e10cSrcweir break;
1740cdf0e10cSrcweir }
1741cdf0e10cSrcweir else // RTL-item
1742cdf0e10cSrcweir {
1743cdf0e10cSrcweir // extend to leftmost glyph of leftmost referenced cluster
1744cdf0e10cSrcweir for( i = rMinGlyphPos; --i >= rVisualItem.mnMinGlyphPos; rMinGlyphPos = i )
1745cdf0e10cSrcweir if( mpVisualAttrs[i].fClusterStart )
1746cdf0e10cSrcweir break;
1747cdf0e10cSrcweir }
1748cdf0e10cSrcweir rEndGlyphPos = nMaxGlyphPos + 1;
1749cdf0e10cSrcweir
1750cdf0e10cSrcweir return true;
1751cdf0e10cSrcweir }
1752cdf0e10cSrcweir
1753cdf0e10cSrcweir // -----------------------------------------------------------------------
1754cdf0e10cSrcweir
1755cdf0e10cSrcweir int UniscribeLayout::GetNextGlyphs( int nLen, sal_GlyphId* pGlyphs, Point& rPos,
1756cdf0e10cSrcweir int& nStartx8, sal_Int32* pGlyphAdvances, int* pCharPosAry ) const
1757cdf0e10cSrcweir {
1758cdf0e10cSrcweir // HACK to allow fake-glyph insertion (e.g. for kashidas)
1759cdf0e10cSrcweir // TODO: use iterator idiom instead of GetNextGlyphs(...)
1760cdf0e10cSrcweir // TODO: else make sure that the limit for glyph injection is sufficient (currently 256)
1761cdf0e10cSrcweir int nSubIter = nStartx8 & 0xff;
1762cdf0e10cSrcweir int nStart = nStartx8 >> 8;
1763cdf0e10cSrcweir
1764cdf0e10cSrcweir // check the glyph iterator
1765cdf0e10cSrcweir if( nStart > mnGlyphCount ) // nStart>MAX means no more glyphs
1766cdf0e10cSrcweir return 0;
1767cdf0e10cSrcweir
1768cdf0e10cSrcweir // find the visual item for the nStart glyph position
1769cdf0e10cSrcweir int nItem = 0;
1770cdf0e10cSrcweir const VisualItem* pVI = mpVisualItems;
1771cdf0e10cSrcweir if( nStart <= 0 ) // nStart<=0 requests the first visible glyph
1772cdf0e10cSrcweir {
1773cdf0e10cSrcweir // find first visible item
1774cdf0e10cSrcweir for(; nItem < mnItemCount; ++nItem, ++pVI )
1775cdf0e10cSrcweir if( !pVI->IsEmpty() )
1776cdf0e10cSrcweir break;
1777cdf0e10cSrcweir // it is possible that there are glyphs but no valid visual item
1778cdf0e10cSrcweir // TODO: get rid of these visual items more early
1779cdf0e10cSrcweir if( nItem < mnItemCount )
1780cdf0e10cSrcweir nStart = pVI->mnMinGlyphPos;
1781cdf0e10cSrcweir }
1782cdf0e10cSrcweir else //if( nStart > 0 ) // nStart>0 means absolute glyph pos +1
1783cdf0e10cSrcweir {
1784cdf0e10cSrcweir --nStart;
1785cdf0e10cSrcweir
1786cdf0e10cSrcweir // find matching item
1787cdf0e10cSrcweir for(; nItem < mnItemCount; ++nItem, ++pVI )
1788cdf0e10cSrcweir if( (nStart >= pVI->mnMinGlyphPos)
1789cdf0e10cSrcweir && (nStart < pVI->mnEndGlyphPos) )
1790cdf0e10cSrcweir break;
1791cdf0e10cSrcweir }
1792cdf0e10cSrcweir
1793cdf0e10cSrcweir // after the last visual item there are no more glyphs
1794cdf0e10cSrcweir if( (nItem >= mnItemCount) || (nStart < 0) )
1795cdf0e10cSrcweir {
1796cdf0e10cSrcweir nStartx8 = (mnGlyphCount + 1) << 8;
1797cdf0e10cSrcweir return 0;
1798cdf0e10cSrcweir }
1799cdf0e10cSrcweir
1800cdf0e10cSrcweir // calculate the first glyph in the next visual item
1801cdf0e10cSrcweir int nNextItemStart = mnGlyphCount;
1802cdf0e10cSrcweir while( ++nItem < mnItemCount )
1803cdf0e10cSrcweir {
1804cdf0e10cSrcweir if( mpVisualItems[nItem].IsEmpty() )
1805cdf0e10cSrcweir continue;
1806cdf0e10cSrcweir nNextItemStart = mpVisualItems[nItem].mnMinGlyphPos;
1807cdf0e10cSrcweir break;
1808cdf0e10cSrcweir }
1809cdf0e10cSrcweir
1810cdf0e10cSrcweir // get the range of relevant glyphs in this visual item
1811cdf0e10cSrcweir int nMinGlyphPos, nEndGlyphPos;
1812cdf0e10cSrcweir bool bRC = GetItemSubrange( *pVI, nMinGlyphPos, nEndGlyphPos );
1813cdf0e10cSrcweir DBG_ASSERT( bRC, "USPLayout::GNG GISR() returned false" );
1814cdf0e10cSrcweir if( !bRC )
1815cdf0e10cSrcweir {
1816cdf0e10cSrcweir nStartx8 = (mnGlyphCount + 1) << 8;
1817cdf0e10cSrcweir return 0;
1818cdf0e10cSrcweir }
1819cdf0e10cSrcweir
1820cdf0e10cSrcweir // make sure nStart is inside the range of relevant glyphs
1821cdf0e10cSrcweir if( nStart < nMinGlyphPos )
1822cdf0e10cSrcweir nStart = nMinGlyphPos;
1823cdf0e10cSrcweir
1824cdf0e10cSrcweir // calculate the start glyph xoffset relative to layout's base position,
1825cdf0e10cSrcweir // advance to next visual glyph position by using adjusted glyph widths
1826cdf0e10cSrcweir // TODO: speed up the calculation for nStart!=0 case by using rPos as a cache
1827cdf0e10cSrcweir long nXOffset = pVI->mnXOffset;
1828cdf0e10cSrcweir const int* pGlyphWidths = mpJustifications ? mpJustifications : mpGlyphAdvances;
1829cdf0e10cSrcweir for( int i = nMinGlyphPos; i < nStart; ++i )
1830cdf0e10cSrcweir nXOffset += pGlyphWidths[ i ];
1831cdf0e10cSrcweir
1832cdf0e10cSrcweir // adjust the nXOffset relative to glyph cluster start
1833cdf0e10cSrcweir int c = mnMinCharPos;
1834cdf0e10cSrcweir if( !pVI->IsRTL() ) // LTR-case
1835cdf0e10cSrcweir {
1836cdf0e10cSrcweir // LTR case: subtract the remainder of the cell from xoffset
1837cdf0e10cSrcweir int nTmpIndex = mpLogClusters[c];
1838cdf0e10cSrcweir while( (--c >= pVI->mnMinCharPos)
1839cdf0e10cSrcweir && (nTmpIndex == mpLogClusters[c]) )
1840cdf0e10cSrcweir nXOffset -= mpCharWidths[c];
1841cdf0e10cSrcweir }
1842cdf0e10cSrcweir else // RTL-case
1843cdf0e10cSrcweir {
1844cdf0e10cSrcweir // RTL case: add the remainder of the cell from xoffset
1845cdf0e10cSrcweir int nTmpIndex = mpLogClusters[ pVI->mnEndCharPos - 1 ];
1846cdf0e10cSrcweir while( (--c >= pVI->mnMinCharPos)
1847cdf0e10cSrcweir && (nTmpIndex == mpLogClusters[c]) )
1848cdf0e10cSrcweir nXOffset += mpCharWidths[c];
1849cdf0e10cSrcweir
1850cdf0e10cSrcweir // adjust the xoffset if justified glyphs are not positioned at their justified positions yet
1851cdf0e10cSrcweir if( mpJustifications && !bManualCellAlign )
1852cdf0e10cSrcweir nXOffset += mpJustifications[ nStart ] - mpGlyphAdvances[ nStart ];
1853cdf0e10cSrcweir }
1854cdf0e10cSrcweir
18558ff75a7eSHerbert Dürr // create mpGlyphs2Chars[] if it is needed later
18568ff75a7eSHerbert Dürr if( pCharPosAry && !mpGlyphs2Chars )
18578ff75a7eSHerbert Dürr {
18588ff75a7eSHerbert Dürr // create and reset the new array
18598ff75a7eSHerbert Dürr mpGlyphs2Chars = new int[ mnGlyphCapacity ];
18608ff75a7eSHerbert Dürr static const int CHARPOS_NONE = -1;
18618ff75a7eSHerbert Dürr for( int i = 0; i < mnGlyphCount; ++i )
18628ff75a7eSHerbert Dürr mpGlyphs2Chars[i] = CHARPOS_NONE;
18638ff75a7eSHerbert Dürr // calculate the char->glyph mapping
18648ff75a7eSHerbert Dürr for( nItem = 0; nItem < mnItemCount; ++nItem )
18658ff75a7eSHerbert Dürr {
18668ff75a7eSHerbert Dürr // ignore invisible visual items
18678ff75a7eSHerbert Dürr const VisualItem& rVI = mpVisualItems[ nItem ];
18688ff75a7eSHerbert Dürr if( rVI.IsEmpty() )
18698ff75a7eSHerbert Dürr continue;
18708ff75a7eSHerbert Dürr // calculate the mapping by using mpLogClusters[]
18718ff75a7eSHerbert Dürr // mpGlyphs2Chars[] should obey the logical order
18728ff75a7eSHerbert Dürr // => reversing the loop does this by overwriting higher logicals
18738ff75a7eSHerbert Dürr for( c = rVI.mnEndCharPos; --c >= rVI.mnMinCharPos; )
18748ff75a7eSHerbert Dürr {
18758ff75a7eSHerbert Dürr int i = mpLogClusters[c] + rVI.mnMinGlyphPos;
18768ff75a7eSHerbert Dürr mpGlyphs2Chars[i] = c;
18778ff75a7eSHerbert Dürr }
18788ff75a7eSHerbert Dürr // use a heuristic to fill the gaps in the glyphs2chars array
18798ff75a7eSHerbert Dürr c = !rVI.IsRTL() ? rVI.mnMinCharPos : rVI.mnEndCharPos - 1;
18808ff75a7eSHerbert Dürr for( int i = rVI.mnMinGlyphPos; i < rVI.mnEndGlyphPos; ++i ) {
18818ff75a7eSHerbert Dürr if( mpGlyphs2Chars[i] == CHARPOS_NONE )
18828ff75a7eSHerbert Dürr mpGlyphs2Chars[i] = c;
18838ff75a7eSHerbert Dürr else
18848ff75a7eSHerbert Dürr c = mpGlyphs2Chars[i];
18858ff75a7eSHerbert Dürr }
18868ff75a7eSHerbert Dürr }
18878ff75a7eSHerbert Dürr }
1888cdf0e10cSrcweir
1889cdf0e10cSrcweir // calculate the absolute position of the first result glyph in pixel units
1890cdf0e10cSrcweir const GOFFSET aGOffset = mpGlyphOffsets[ nStart ];
1891cdf0e10cSrcweir Point aRelativePos( nXOffset + aGOffset.du, -aGOffset.dv );
1892cdf0e10cSrcweir rPos = GetDrawPosition( aRelativePos );
1893cdf0e10cSrcweir
1894cdf0e10cSrcweir // fill the result arrays
1895cdf0e10cSrcweir int nCount = 0;
1896cdf0e10cSrcweir while( nCount < nLen )
1897cdf0e10cSrcweir {
1898cdf0e10cSrcweir // prepare return values
1899cdf0e10cSrcweir sal_GlyphId aGlyphId = mpOutGlyphs[ nStart ];
1900cdf0e10cSrcweir int nGlyphWidth = pGlyphWidths[ nStart ];
1901cdf0e10cSrcweir int nCharPos = -1; // no need to determine charpos
1902cdf0e10cSrcweir if( mpGlyphs2Chars ) // unless explicitly requested+provided
1903cdf0e10cSrcweir nCharPos = mpGlyphs2Chars[ nStart ];
1904cdf0e10cSrcweir
1905cdf0e10cSrcweir // inject kashida glyphs if needed
1906cdf0e10cSrcweir if( !mbDisableGlyphInjection
1907cdf0e10cSrcweir && mpJustifications
1908cdf0e10cSrcweir && mnMinKashidaWidth
1909cdf0e10cSrcweir && mpVisualAttrs[nStart].uJustification >= SCRIPT_JUSTIFY_ARABIC_NORMAL )
1910cdf0e10cSrcweir {
1911cdf0e10cSrcweir // prepare draw position adjustment
1912cdf0e10cSrcweir int nExtraOfs = (nSubIter++) * mnMinKashidaWidth;
1913cdf0e10cSrcweir // calculate space available for the injected glyphs
1914cdf0e10cSrcweir nGlyphWidth = mpGlyphAdvances[ nStart ];
1915cdf0e10cSrcweir const int nExtraWidth = mpJustifications[ nStart ] - nGlyphWidth;
1916cdf0e10cSrcweir const int nToFillWidth = nExtraWidth - nExtraOfs;
1917cdf0e10cSrcweir if( (4*nToFillWidth >= mnMinKashidaWidth) // prevent glyph-injection if there is no room
1918cdf0e10cSrcweir || ((nSubIter > 1) && (nToFillWidth > 0)) ) // unless they can overlap with others
1919cdf0e10cSrcweir {
1920cdf0e10cSrcweir // handle if there is not sufficient room for a full glyph
1921cdf0e10cSrcweir if( nToFillWidth < mnMinKashidaWidth )
1922cdf0e10cSrcweir {
1923cdf0e10cSrcweir // overlap it with the previously injected glyph if possible
1924cdf0e10cSrcweir int nOverlap = mnMinKashidaWidth - nToFillWidth;
1925cdf0e10cSrcweir // else overlap it with both neighboring glyphs
1926cdf0e10cSrcweir if( nSubIter <= 1 )
1927cdf0e10cSrcweir nOverlap /= 2;
1928cdf0e10cSrcweir nExtraOfs -= nOverlap;
1929cdf0e10cSrcweir }
1930cdf0e10cSrcweir nGlyphWidth = mnMinKashidaWidth;
1931cdf0e10cSrcweir aGlyphId = mnMinKashidaGlyph;
1932cdf0e10cSrcweir nCharPos = -1;
1933cdf0e10cSrcweir }
1934cdf0e10cSrcweir else
1935cdf0e10cSrcweir {
1936cdf0e10cSrcweir nExtraOfs += nToFillWidth; // at right of cell
1937cdf0e10cSrcweir nSubIter = 0; // done with glyph injection
1938cdf0e10cSrcweir }
1939cdf0e10cSrcweir if( !bManualCellAlign )
1940cdf0e10cSrcweir nExtraOfs -= nExtraWidth; // adjust for right-aligned cells
1941cdf0e10cSrcweir
1942cdf0e10cSrcweir // adjust the draw position for the injected-glyphs case
1943cdf0e10cSrcweir if( nExtraOfs )
1944cdf0e10cSrcweir {
1945cdf0e10cSrcweir aRelativePos.X() += nExtraOfs;
1946cdf0e10cSrcweir rPos = GetDrawPosition( aRelativePos );
1947cdf0e10cSrcweir }
1948cdf0e10cSrcweir }
1949cdf0e10cSrcweir
1950cdf0e10cSrcweir // update return values
1951cdf0e10cSrcweir *(pGlyphs++) = aGlyphId;
1952cdf0e10cSrcweir if( pGlyphAdvances )
1953cdf0e10cSrcweir *(pGlyphAdvances++) = nGlyphWidth;
1954cdf0e10cSrcweir if( pCharPosAry )
1955cdf0e10cSrcweir *(pCharPosAry++) = nCharPos;
1956cdf0e10cSrcweir
1957cdf0e10cSrcweir // increment counter of returned glyphs
1958cdf0e10cSrcweir ++nCount;
1959cdf0e10cSrcweir
1960cdf0e10cSrcweir // reduce code complexity by returning early in glyph-injection case
1961cdf0e10cSrcweir if( nSubIter != 0 )
1962cdf0e10cSrcweir break;
1963cdf0e10cSrcweir
1964cdf0e10cSrcweir // stop after the last visible glyph in this visual item
1965cdf0e10cSrcweir if( ++nStart >= nEndGlyphPos )
1966cdf0e10cSrcweir {
1967cdf0e10cSrcweir nStart = nNextItemStart;
1968cdf0e10cSrcweir break;
1969cdf0e10cSrcweir }
1970cdf0e10cSrcweir
1971cdf0e10cSrcweir // RTL-justified glyph positioning is not easy
1972cdf0e10cSrcweir // simplify the code by just returning only one glyph at a time
1973cdf0e10cSrcweir if( mpJustifications && pVI->IsRTL() )
1974cdf0e10cSrcweir break;
1975cdf0e10cSrcweir
1976cdf0e10cSrcweir // stop when the x-position of the next glyph is unexpected
1977cdf0e10cSrcweir if( !pGlyphAdvances )
1978cdf0e10cSrcweir if( (mpGlyphOffsets && (mpGlyphOffsets[nStart].du != aGOffset.du) )
1979cdf0e10cSrcweir || (mpJustifications && (mpJustifications[nStart] != mpGlyphAdvances[nStart]) ) )
1980cdf0e10cSrcweir break;
1981cdf0e10cSrcweir
1982cdf0e10cSrcweir // stop when the y-position of the next glyph is unexpected
1983cdf0e10cSrcweir if( mpGlyphOffsets && (mpGlyphOffsets[nStart].dv != aGOffset.dv) )
1984cdf0e10cSrcweir break;
1985cdf0e10cSrcweir }
1986cdf0e10cSrcweir
1987cdf0e10cSrcweir ++nStart;
1988cdf0e10cSrcweir nStartx8 = (nStart << 8) + nSubIter;
1989cdf0e10cSrcweir return nCount;
1990cdf0e10cSrcweir }
1991cdf0e10cSrcweir
1992cdf0e10cSrcweir // -----------------------------------------------------------------------
1993cdf0e10cSrcweir
1994cdf0e10cSrcweir void UniscribeLayout::MoveGlyph( int nStartx8, long nNewXPos )
1995cdf0e10cSrcweir {
1996cdf0e10cSrcweir DBG_ASSERT( !(nStartx8 & 0xff), "USP::MoveGlyph(): glyph injection not disabled!" );
1997cdf0e10cSrcweir int nStart = nStartx8 >> 8;
1998cdf0e10cSrcweir if( nStart > mnGlyphCount )
1999cdf0e10cSrcweir return;
2000cdf0e10cSrcweir
2001cdf0e10cSrcweir VisualItem* pVI = mpVisualItems;
2002cdf0e10cSrcweir int nMinGlyphPos = 0, nEndGlyphPos;
2003cdf0e10cSrcweir if( nStart == 0 ) // nStart==0 for first visible glyph
2004cdf0e10cSrcweir {
2005cdf0e10cSrcweir for( int i = mnItemCount; --i >= 0; ++pVI )
2006cdf0e10cSrcweir if( GetItemSubrange( *pVI, nMinGlyphPos, nEndGlyphPos ) )
2007cdf0e10cSrcweir break;
2008cdf0e10cSrcweir nStart = nMinGlyphPos;
2009cdf0e10cSrcweir DBG_ASSERT( nStart <= mnGlyphCount, "USPLayout::MoveG overflow" );
2010cdf0e10cSrcweir }
2011cdf0e10cSrcweir else //if( nStart > 0 ) // nStart>0 means absolute_glyphpos+1
2012cdf0e10cSrcweir {
2013cdf0e10cSrcweir --nStart;
2014cdf0e10cSrcweir for( int i = mnItemCount; --i >= 0; ++pVI )
2015cdf0e10cSrcweir if( (nStart >= pVI->mnMinGlyphPos) && (nStart < pVI->mnEndGlyphPos) )
2016cdf0e10cSrcweir break;
2017cdf0e10cSrcweir bool bRC = GetItemSubrange( *pVI, nMinGlyphPos, nEndGlyphPos );
2018cdf0e10cSrcweir (void)bRC; // avoid var-not-used warning
2019cdf0e10cSrcweir DBG_ASSERT( bRC, "USPLayout::MoveG GISR() returned false" );
2020cdf0e10cSrcweir }
2021cdf0e10cSrcweir
2022cdf0e10cSrcweir long nDelta = nNewXPos - pVI->mnXOffset;
2023cdf0e10cSrcweir if( nStart > nMinGlyphPos )
2024cdf0e10cSrcweir {
2025cdf0e10cSrcweir // move the glyph by expanding its left glyph but ignore dropped glyphs
2026cdf0e10cSrcweir int i, nLastUndropped = nMinGlyphPos - 1;
2027cdf0e10cSrcweir for( i = nMinGlyphPos; i < nStart; ++i )
2028cdf0e10cSrcweir {
2029cdf0e10cSrcweir if (mpOutGlyphs[i] != DROPPED_OUTGLYPH)
2030cdf0e10cSrcweir {
2031cdf0e10cSrcweir nDelta -= (mpJustifications)? mpJustifications[ i ] : mpGlyphAdvances[ i ];
2032cdf0e10cSrcweir nLastUndropped = i;
2033cdf0e10cSrcweir }
2034cdf0e10cSrcweir }
2035cdf0e10cSrcweir if (nLastUndropped >= nMinGlyphPos)
2036cdf0e10cSrcweir {
2037cdf0e10cSrcweir mpGlyphAdvances[ nLastUndropped ] += nDelta;
2038cdf0e10cSrcweir if (mpJustifications) mpJustifications[ nLastUndropped ] += nDelta;
2039cdf0e10cSrcweir }
2040cdf0e10cSrcweir else
2041cdf0e10cSrcweir {
2042cdf0e10cSrcweir pVI->mnXOffset += nDelta;
2043cdf0e10cSrcweir }
2044cdf0e10cSrcweir }
2045cdf0e10cSrcweir else
2046cdf0e10cSrcweir {
2047cdf0e10cSrcweir // move the visual item by having an offset
2048cdf0e10cSrcweir pVI->mnXOffset += nDelta;
2049cdf0e10cSrcweir }
2050cdf0e10cSrcweir // move subsequent items - this often isn't necessary because subsequent
2051cdf0e10cSrcweir // moves will correct subsequent items. However, if there is a contiguous
2052cdf0e10cSrcweir // range not involving fallback which spans items, this will be needed
2053cdf0e10cSrcweir while (++pVI - mpVisualItems < mnItemCount)
2054cdf0e10cSrcweir {
2055cdf0e10cSrcweir pVI->mnXOffset += nDelta;
2056cdf0e10cSrcweir }
2057cdf0e10cSrcweir }
2058cdf0e10cSrcweir
2059cdf0e10cSrcweir // -----------------------------------------------------------------------
2060cdf0e10cSrcweir
2061cdf0e10cSrcweir void UniscribeLayout::DropGlyph( int nStartx8 )
2062cdf0e10cSrcweir {
2063cdf0e10cSrcweir DBG_ASSERT( !(nStartx8 & 0xff), "USP::DropGlyph(): glyph injection not disabled!" );
2064cdf0e10cSrcweir int nStart = nStartx8 >> 8;
2065cdf0e10cSrcweir DBG_ASSERT( nStart<=mnGlyphCount, "USPLayout::MoveG nStart overflow" );
2066cdf0e10cSrcweir
2067cdf0e10cSrcweir if( nStart > 0 ) // nStart>0 means absolute glyph pos + 1
2068cdf0e10cSrcweir --nStart;
2069cdf0e10cSrcweir else // nStart<=0 for first visible glyph
2070cdf0e10cSrcweir {
2071cdf0e10cSrcweir VisualItem* pVI = mpVisualItems;
2072cdf0e10cSrcweir for( int i = mnItemCount, nDummy; --i >= 0; ++pVI )
2073cdf0e10cSrcweir if( GetItemSubrange( *pVI, nStart, nDummy ) )
2074cdf0e10cSrcweir break;
2075cdf0e10cSrcweir DBG_ASSERT( nStart <= mnGlyphCount, "USPLayout::DropG overflow" );
2076cdf0e10cSrcweir int nOffset = 0;
2077cdf0e10cSrcweir int j = pVI->mnMinGlyphPos;
2078cdf0e10cSrcweir while (mpOutGlyphs[j] == DROPPED_OUTGLYPH) j++;
2079cdf0e10cSrcweir if (j == nStart)
2080cdf0e10cSrcweir {
2081cdf0e10cSrcweir pVI->mnXOffset += ((mpJustifications)? mpJustifications[nStart] : mpGlyphAdvances[nStart]);
2082cdf0e10cSrcweir }
2083cdf0e10cSrcweir }
2084cdf0e10cSrcweir
2085cdf0e10cSrcweir mpOutGlyphs[ nStart ] = DROPPED_OUTGLYPH;
2086cdf0e10cSrcweir }
2087cdf0e10cSrcweir
2088cdf0e10cSrcweir // -----------------------------------------------------------------------
2089cdf0e10cSrcweir
2090cdf0e10cSrcweir void UniscribeLayout::Simplify( bool /*bIsBase*/ )
2091cdf0e10cSrcweir {
2092cdf0e10cSrcweir static const WCHAR cDroppedGlyph = DROPPED_OUTGLYPH;
2093cdf0e10cSrcweir int i;
2094cdf0e10cSrcweir // if there are no dropped glyphs don't bother
2095cdf0e10cSrcweir for( i = 0; i < mnGlyphCount; ++i )
2096cdf0e10cSrcweir if( mpOutGlyphs[ i ] == cDroppedGlyph )
2097cdf0e10cSrcweir break;
2098cdf0e10cSrcweir if( i >= mnGlyphCount )
2099cdf0e10cSrcweir return;
2100cdf0e10cSrcweir
2101cdf0e10cSrcweir // prepare for sparse layout
2102cdf0e10cSrcweir // => make sure mpGlyphs2Chars[] exists
2103cdf0e10cSrcweir if( !mpGlyphs2Chars )
2104cdf0e10cSrcweir {
2105cdf0e10cSrcweir mpGlyphs2Chars = new int[ mnGlyphCapacity ];
2106cdf0e10cSrcweir for( i = 0; i < mnGlyphCount; ++i )
2107cdf0e10cSrcweir mpGlyphs2Chars[ i ] = -1;
2108cdf0e10cSrcweir for( int nItem = 0; nItem < mnItemCount; ++nItem )
2109cdf0e10cSrcweir {
2110cdf0e10cSrcweir // skip invisible items
2111cdf0e10cSrcweir VisualItem& rVI = mpVisualItems[ nItem ];
2112cdf0e10cSrcweir if( rVI.IsEmpty() )
2113cdf0e10cSrcweir continue;
2114cdf0e10cSrcweir for( i = rVI.mnEndCharPos; --i >= rVI.mnMinCharPos; )
2115cdf0e10cSrcweir {
2116cdf0e10cSrcweir int j = mpLogClusters[ i ] + rVI.mnMinGlyphPos;
2117cdf0e10cSrcweir mpGlyphs2Chars[ j ] = i;
2118cdf0e10cSrcweir }
2119cdf0e10cSrcweir }
2120cdf0e10cSrcweir }
2121cdf0e10cSrcweir
2122cdf0e10cSrcweir // remove the dropped glyphs
2123cdf0e10cSrcweir const int* pGlyphWidths = mpJustifications ? mpJustifications : mpGlyphAdvances;
2124cdf0e10cSrcweir for( int nItem = 0; nItem < mnItemCount; ++nItem )
2125cdf0e10cSrcweir {
2126cdf0e10cSrcweir VisualItem& rVI = mpVisualItems[ nItem ];
2127cdf0e10cSrcweir if( rVI.IsEmpty() )
2128cdf0e10cSrcweir continue;
2129cdf0e10cSrcweir
2130cdf0e10cSrcweir // mark replaced character widths
2131cdf0e10cSrcweir for( i = rVI.mnMinCharPos; i < rVI.mnEndCharPos; ++i )
2132cdf0e10cSrcweir {
2133cdf0e10cSrcweir int j = mpLogClusters[ i ] + rVI.mnMinGlyphPos;
2134cdf0e10cSrcweir if( mpOutGlyphs[ j ] == cDroppedGlyph )
2135cdf0e10cSrcweir mpCharWidths[ i ] = 0;
2136cdf0e10cSrcweir }
2137cdf0e10cSrcweir
2138cdf0e10cSrcweir // handle dropped glyphs at start of visual item
2139cdf0e10cSrcweir int nMinGlyphPos, nEndGlyphPos, nOrigMinGlyphPos = rVI.mnMinGlyphPos;
2140cdf0e10cSrcweir GetItemSubrange( rVI, nMinGlyphPos, nEndGlyphPos );
2141cdf0e10cSrcweir i = nMinGlyphPos;
2142cdf0e10cSrcweir while( (mpOutGlyphs[i] == cDroppedGlyph) && (i < nEndGlyphPos) )
2143cdf0e10cSrcweir {
2144cdf0e10cSrcweir //rVI.mnXOffset += pGlyphWidths[ i ];
2145cdf0e10cSrcweir rVI.mnMinGlyphPos = ++i;
2146cdf0e10cSrcweir }
2147cdf0e10cSrcweir
2148cdf0e10cSrcweir // when all glyphs in item got dropped mark it as empty
2149cdf0e10cSrcweir if( i >= nEndGlyphPos )
2150cdf0e10cSrcweir {
2151cdf0e10cSrcweir rVI.mnEndGlyphPos = 0;
2152cdf0e10cSrcweir continue;
2153cdf0e10cSrcweir }
2154cdf0e10cSrcweir // If there are still glyphs in the cluster and mnMinGlyphPos
2155cdf0e10cSrcweir // has changed then we need to remove the dropped glyphs at start
2156cdf0e10cSrcweir // to correct logClusters, which is unsigned and relative to the
2157cdf0e10cSrcweir // item start.
2158cdf0e10cSrcweir if (rVI.mnMinGlyphPos != nOrigMinGlyphPos)
2159cdf0e10cSrcweir {
2160cdf0e10cSrcweir // drop any glyphs in the visual item outside the range
2161cdf0e10cSrcweir for (i = nOrigMinGlyphPos; i < nMinGlyphPos; i++)
2162cdf0e10cSrcweir mpOutGlyphs[ i ] = cDroppedGlyph;
2163cdf0e10cSrcweir rVI.mnMinGlyphPos = i = nOrigMinGlyphPos;
2164cdf0e10cSrcweir }
2165cdf0e10cSrcweir
2166cdf0e10cSrcweir // handle dropped glyphs in the middle of visual item
2167cdf0e10cSrcweir for(; i < nEndGlyphPos; ++i )
2168cdf0e10cSrcweir if( mpOutGlyphs[ i ] == cDroppedGlyph )
2169cdf0e10cSrcweir break;
2170cdf0e10cSrcweir int j = i;
2171cdf0e10cSrcweir while( ++i < nEndGlyphPos )
2172cdf0e10cSrcweir {
2173cdf0e10cSrcweir if( mpOutGlyphs[ i ] == cDroppedGlyph )
2174cdf0e10cSrcweir continue;
2175cdf0e10cSrcweir mpOutGlyphs[ j ] = mpOutGlyphs[ i ];
2176cdf0e10cSrcweir mpGlyphOffsets[ j ] = mpGlyphOffsets[ i ];
2177cdf0e10cSrcweir mpVisualAttrs[ j ] = mpVisualAttrs[ i ];
2178cdf0e10cSrcweir mpGlyphAdvances[ j ] = mpGlyphAdvances[ i ];
2179cdf0e10cSrcweir if( mpJustifications )
2180cdf0e10cSrcweir mpJustifications[ j ] = mpJustifications[ i ];
2181cdf0e10cSrcweir const int k = mpGlyphs2Chars[ i ];
2182cdf0e10cSrcweir mpGlyphs2Chars[ j ] = k;
2183cdf0e10cSrcweir const int nRelGlyphPos = (j++) - rVI.mnMinGlyphPos;
2184cdf0e10cSrcweir if( k < 0) // extra glyphs are already mapped
2185cdf0e10cSrcweir continue;
2186cdf0e10cSrcweir mpLogClusters[ k ] = static_cast<WORD>(nRelGlyphPos);
2187cdf0e10cSrcweir }
2188cdf0e10cSrcweir
2189cdf0e10cSrcweir rVI.mnEndGlyphPos = j;
2190cdf0e10cSrcweir }
2191cdf0e10cSrcweir }
2192cdf0e10cSrcweir
2193cdf0e10cSrcweir // -----------------------------------------------------------------------
2194cdf0e10cSrcweir
2195cdf0e10cSrcweir void UniscribeLayout::DrawText( SalGraphics& ) const
2196cdf0e10cSrcweir {
2197cdf0e10cSrcweir HFONT hOrigFont = DisableFontScaling();
2198cdf0e10cSrcweir
2199cdf0e10cSrcweir int nBaseClusterOffset = 0;
2200cdf0e10cSrcweir int nBaseGlyphPos = -1;
2201cdf0e10cSrcweir for( int nItem = 0; nItem < mnItemCount; ++nItem )
2202cdf0e10cSrcweir {
2203cdf0e10cSrcweir const VisualItem& rVisualItem = mpVisualItems[ nItem ];
2204cdf0e10cSrcweir
2205cdf0e10cSrcweir // skip if there is nothing to display
2206cdf0e10cSrcweir int nMinGlyphPos, nEndGlyphPos;
2207cdf0e10cSrcweir if( !GetItemSubrange( rVisualItem, nMinGlyphPos, nEndGlyphPos ) )
2208cdf0e10cSrcweir continue;
2209cdf0e10cSrcweir
2210cdf0e10cSrcweir if( nBaseGlyphPos < 0 )
2211cdf0e10cSrcweir {
2212cdf0e10cSrcweir // adjust draw position relative to cluster start
2213cdf0e10cSrcweir if( rVisualItem.IsRTL() )
2214cdf0e10cSrcweir nBaseGlyphPos = nEndGlyphPos - 1;
2215cdf0e10cSrcweir else
2216cdf0e10cSrcweir nBaseGlyphPos = nMinGlyphPos;
2217cdf0e10cSrcweir
2218cdf0e10cSrcweir const int* pGlyphWidths;
2219cdf0e10cSrcweir if( mpJustifications )
2220cdf0e10cSrcweir pGlyphWidths = mpJustifications;
2221cdf0e10cSrcweir else
2222cdf0e10cSrcweir pGlyphWidths = mpGlyphAdvances;
2223cdf0e10cSrcweir
2224cdf0e10cSrcweir int i = mnMinCharPos;
2225cdf0e10cSrcweir while( (--i >= rVisualItem.mnMinCharPos)
2226cdf0e10cSrcweir && (nBaseGlyphPos == mpLogClusters[i]) )
2227cdf0e10cSrcweir nBaseClusterOffset += mpCharWidths[i];
2228cdf0e10cSrcweir
2229cdf0e10cSrcweir if( !rVisualItem.IsRTL() )
2230cdf0e10cSrcweir nBaseClusterOffset = -nBaseClusterOffset;
2231cdf0e10cSrcweir }
2232cdf0e10cSrcweir
2233cdf0e10cSrcweir // now draw the matching glyphs in this item
2234cdf0e10cSrcweir Point aRelPos( rVisualItem.mnXOffset + nBaseClusterOffset, 0 );
2235cdf0e10cSrcweir Point aPos = GetDrawPosition( aRelPos );
2236cdf0e10cSrcweir SCRIPT_CACHE& rScriptCache = GetScriptCache();
2237cdf0e10cSrcweir (*pScriptTextOut)( mhDC, &rScriptCache,
2238cdf0e10cSrcweir aPos.X(), aPos.Y(), 0, NULL,
2239cdf0e10cSrcweir &rVisualItem.mpScriptItem->a, NULL, 0,
2240cdf0e10cSrcweir mpOutGlyphs + nMinGlyphPos,
2241cdf0e10cSrcweir nEndGlyphPos - nMinGlyphPos,
2242cdf0e10cSrcweir mpGlyphAdvances + nMinGlyphPos,
2243cdf0e10cSrcweir mpJustifications ? mpJustifications + nMinGlyphPos : NULL,
2244cdf0e10cSrcweir mpGlyphOffsets + nMinGlyphPos );
2245cdf0e10cSrcweir }
2246cdf0e10cSrcweir
2247cdf0e10cSrcweir if( hOrigFont )
2248cdf0e10cSrcweir DeleteFont( SelectFont( mhDC, hOrigFont ) );
2249cdf0e10cSrcweir }
2250cdf0e10cSrcweir
2251cdf0e10cSrcweir // -----------------------------------------------------------------------
2252cdf0e10cSrcweir
2253cdf0e10cSrcweir long UniscribeLayout::FillDXArray( long* pDXArray ) const
2254cdf0e10cSrcweir {
2255cdf0e10cSrcweir // calculate width of the complete layout
2256cdf0e10cSrcweir long nWidth = mnBaseAdv;
2257cdf0e10cSrcweir for( int nItem = mnItemCount; --nItem >= 0; )
2258cdf0e10cSrcweir {
2259cdf0e10cSrcweir const VisualItem& rVI = mpVisualItems[ nItem ];
2260cdf0e10cSrcweir
2261cdf0e10cSrcweir // skip if there is nothing to display
2262cdf0e10cSrcweir int nMinGlyphPos, nEndGlyphPos;
2263cdf0e10cSrcweir if( !GetItemSubrange( rVI, nMinGlyphPos, nEndGlyphPos ) )
2264cdf0e10cSrcweir continue;
2265cdf0e10cSrcweir
2266cdf0e10cSrcweir // width = xoffset + width of last item
2267cdf0e10cSrcweir nWidth = rVI.mnXOffset;
2268cdf0e10cSrcweir const int* pGlyphWidths = mpJustifications ? mpJustifications : mpGlyphAdvances;
2269cdf0e10cSrcweir for( int i = nMinGlyphPos; i < nEndGlyphPos; ++i )
2270cdf0e10cSrcweir nWidth += pGlyphWidths[i];
2271cdf0e10cSrcweir break;
2272cdf0e10cSrcweir }
2273cdf0e10cSrcweir
2274cdf0e10cSrcweir // copy the virtual char widths into pDXArray[]
2275cdf0e10cSrcweir if( pDXArray )
2276cdf0e10cSrcweir for( int i = mnMinCharPos; i < mnEndCharPos; ++i )
2277cdf0e10cSrcweir pDXArray[ i - mnMinCharPos ] = mpCharWidths[ i ];
2278cdf0e10cSrcweir
2279cdf0e10cSrcweir return nWidth;
2280cdf0e10cSrcweir }
2281cdf0e10cSrcweir
2282cdf0e10cSrcweir // -----------------------------------------------------------------------
2283cdf0e10cSrcweir
2284cdf0e10cSrcweir int UniscribeLayout::GetTextBreak( long nMaxWidth, long nCharExtra, int nFactor ) const
2285cdf0e10cSrcweir {
2286cdf0e10cSrcweir long nWidth = 0;
2287cdf0e10cSrcweir for( int i = mnMinCharPos; i < mnEndCharPos; ++i )
2288cdf0e10cSrcweir {
2289cdf0e10cSrcweir nWidth += mpCharWidths[ i ] * nFactor;
2290cdf0e10cSrcweir
2291cdf0e10cSrcweir // check if the nMaxWidth still fits the current sub-layout
2292cdf0e10cSrcweir if( nWidth >= nMaxWidth )
2293cdf0e10cSrcweir {
2294cdf0e10cSrcweir // go back to cluster start
2295cdf0e10cSrcweir // we have to find the visual item first since the mpLogClusters[]
2296cdf0e10cSrcweir // needed to find the cluster start is relative to to the visual item
2297cdf0e10cSrcweir int nMinGlyphIndex = 0;
2298cdf0e10cSrcweir for( int nItem = 0; nItem < mnItemCount; ++nItem )
2299cdf0e10cSrcweir {
2300cdf0e10cSrcweir const VisualItem& rVisualItem = mpVisualItems[ nItem ];
2301cdf0e10cSrcweir nMinGlyphIndex = rVisualItem.mnMinGlyphPos;
2302cdf0e10cSrcweir if( (i >= rVisualItem.mnMinCharPos)
2303cdf0e10cSrcweir && (i < rVisualItem.mnEndCharPos) )
2304cdf0e10cSrcweir break;
2305cdf0e10cSrcweir }
2306cdf0e10cSrcweir // now go back to the matching cluster start
2307cdf0e10cSrcweir do
2308cdf0e10cSrcweir {
2309cdf0e10cSrcweir int nGlyphPos = mpLogClusters[i] + nMinGlyphIndex;
2310cdf0e10cSrcweir if( 0 != mpVisualAttrs[ nGlyphPos ].fClusterStart )
2311cdf0e10cSrcweir return i;
2312cdf0e10cSrcweir } while( --i >= mnMinCharPos );
2313cdf0e10cSrcweir
2314cdf0e10cSrcweir // if the cluster starts before the start of the visual item
2315cdf0e10cSrcweir // then set the visual breakpoint before this item
2316cdf0e10cSrcweir return mnMinCharPos;
2317cdf0e10cSrcweir }
2318cdf0e10cSrcweir
2319cdf0e10cSrcweir // the visual break also depends on the nCharExtra between the characters
2320cdf0e10cSrcweir nWidth += nCharExtra;
2321cdf0e10cSrcweir }
2322cdf0e10cSrcweir
2323cdf0e10cSrcweir // the whole layout did fit inside the nMaxWidth
2324cdf0e10cSrcweir return STRING_LEN;
2325cdf0e10cSrcweir }
2326cdf0e10cSrcweir
2327cdf0e10cSrcweir // -----------------------------------------------------------------------
2328cdf0e10cSrcweir
2329cdf0e10cSrcweir void UniscribeLayout::GetCaretPositions( int nMaxIdx, long* pCaretXArray ) const
2330cdf0e10cSrcweir {
2331cdf0e10cSrcweir int i;
2332cdf0e10cSrcweir for( i = 0; i < nMaxIdx; ++i )
2333cdf0e10cSrcweir pCaretXArray[ i ] = -1;
2334cdf0e10cSrcweir long* const pGlyphPos = (long*)alloca( (mnGlyphCount+1) * sizeof(long) );
2335cdf0e10cSrcweir for( i = 0; i <= mnGlyphCount; ++i )
2336cdf0e10cSrcweir pGlyphPos[ i ] = -1;
2337cdf0e10cSrcweir
2338cdf0e10cSrcweir long nXPos = 0;
2339cdf0e10cSrcweir for( int nItem = 0; nItem < mnItemCount; ++nItem )
2340cdf0e10cSrcweir {
2341cdf0e10cSrcweir const VisualItem& rVisualItem = mpVisualItems[ nItem ];
2342cdf0e10cSrcweir if( rVisualItem.IsEmpty() )
2343cdf0e10cSrcweir continue;
2344cdf0e10cSrcweir
2345cdf0e10cSrcweir if (mnLayoutFlags & SAL_LAYOUT_FOR_FALLBACK)
2346cdf0e10cSrcweir {
2347cdf0e10cSrcweir nXPos = rVisualItem.mnXOffset;
2348cdf0e10cSrcweir }
2349cdf0e10cSrcweir // get glyph positions
2350cdf0e10cSrcweir // TODO: handle when rVisualItem's glyph range is only partially used
2351cdf0e10cSrcweir for( i = rVisualItem.mnMinGlyphPos; i < rVisualItem.mnEndGlyphPos; ++i )
2352cdf0e10cSrcweir {
2353cdf0e10cSrcweir pGlyphPos[ i ] = nXPos;
2354cdf0e10cSrcweir nXPos += mpGlyphAdvances[ i ];
2355cdf0e10cSrcweir }
2356cdf0e10cSrcweir // rightmost position of this visualitem
2357cdf0e10cSrcweir pGlyphPos[ i ] = nXPos;
2358cdf0e10cSrcweir
2359cdf0e10cSrcweir // convert glyph positions to character positions
2360cdf0e10cSrcweir i = rVisualItem.mnMinCharPos;
2361cdf0e10cSrcweir if( i < mnMinCharPos )
2362cdf0e10cSrcweir i = mnMinCharPos;
2363cdf0e10cSrcweir for(; (i < rVisualItem.mnEndCharPos) && (i < mnEndCharPos); ++i )
2364cdf0e10cSrcweir {
2365cdf0e10cSrcweir int j = mpLogClusters[ i ] + rVisualItem.mnMinGlyphPos;
2366cdf0e10cSrcweir int nCurrIdx = i * 2;
2367cdf0e10cSrcweir if( !rVisualItem.IsRTL() )
2368cdf0e10cSrcweir {
2369cdf0e10cSrcweir // normal positions for LTR case
2370cdf0e10cSrcweir pCaretXArray[ nCurrIdx ] = pGlyphPos[ j ];
2371cdf0e10cSrcweir pCaretXArray[ nCurrIdx+1 ] = pGlyphPos[ j+1 ];
2372cdf0e10cSrcweir }
2373cdf0e10cSrcweir else
2374cdf0e10cSrcweir {
2375cdf0e10cSrcweir // reverse positions for RTL case
2376cdf0e10cSrcweir pCaretXArray[ nCurrIdx ] = pGlyphPos[ j+1 ];
2377cdf0e10cSrcweir pCaretXArray[ nCurrIdx+1 ] = pGlyphPos[ j ];
2378cdf0e10cSrcweir }
2379cdf0e10cSrcweir }
2380cdf0e10cSrcweir }
2381cdf0e10cSrcweir
2382cdf0e10cSrcweir if (!(mnLayoutFlags & SAL_LAYOUT_FOR_FALLBACK))
2383cdf0e10cSrcweir {
2384cdf0e10cSrcweir nXPos = 0;
2385cdf0e10cSrcweir // fixup unknown character positions to neighbor
2386cdf0e10cSrcweir for( i = 0; i < nMaxIdx; ++i )
2387cdf0e10cSrcweir {
2388cdf0e10cSrcweir if( pCaretXArray[ i ] >= 0 )
2389cdf0e10cSrcweir nXPos = pCaretXArray[ i ];
2390cdf0e10cSrcweir else
2391cdf0e10cSrcweir pCaretXArray[ i ] = nXPos;
2392cdf0e10cSrcweir }
2393cdf0e10cSrcweir }
2394cdf0e10cSrcweir }
2395cdf0e10cSrcweir
2396cdf0e10cSrcweir // -----------------------------------------------------------------------
2397cdf0e10cSrcweir
2398cdf0e10cSrcweir void UniscribeLayout::AdjustLayout( ImplLayoutArgs& rArgs )
2399cdf0e10cSrcweir {
2400cdf0e10cSrcweir SalLayout::AdjustLayout( rArgs );
2401cdf0e10cSrcweir
2402cdf0e10cSrcweir // adjust positions if requested
2403cdf0e10cSrcweir if( rArgs.mpDXArray )
2404cdf0e10cSrcweir ApplyDXArray( rArgs );
2405cdf0e10cSrcweir else if( rArgs.mnLayoutWidth )
2406cdf0e10cSrcweir Justify( rArgs.mnLayoutWidth );
2407cdf0e10cSrcweir }
2408cdf0e10cSrcweir
2409cdf0e10cSrcweir // -----------------------------------------------------------------------
2410cdf0e10cSrcweir
2411cdf0e10cSrcweir void UniscribeLayout::ApplyDXArray( const ImplLayoutArgs& rArgs )
2412cdf0e10cSrcweir {
2413cdf0e10cSrcweir const long* pDXArray = rArgs.mpDXArray;
2414cdf0e10cSrcweir
2415cdf0e10cSrcweir // increase char widths in string range to desired values
2416cdf0e10cSrcweir bool bModified = false;
2417cdf0e10cSrcweir int nOldWidth = 0;
2418cdf0e10cSrcweir DBG_ASSERT( mnUnitsPerPixel==1, "UniscribeLayout.mnUnitsPerPixel != 1" );
2419cdf0e10cSrcweir int i,j;
2420cdf0e10cSrcweir for( i = mnMinCharPos, j = 0; i < mnEndCharPos; ++i, ++j )
2421cdf0e10cSrcweir {
2422cdf0e10cSrcweir int nNewCharWidth = (pDXArray[j] - nOldWidth);
2423cdf0e10cSrcweir // TODO: nNewCharWidth *= mnUnitsPerPixel;
2424cdf0e10cSrcweir if( mpCharWidths[i] != nNewCharWidth )
2425cdf0e10cSrcweir {
2426cdf0e10cSrcweir mpCharWidths[i] = nNewCharWidth;
2427cdf0e10cSrcweir bModified = true;
2428cdf0e10cSrcweir }
2429cdf0e10cSrcweir nOldWidth = pDXArray[j];
2430cdf0e10cSrcweir }
2431cdf0e10cSrcweir
2432cdf0e10cSrcweir if( !bModified )
2433cdf0e10cSrcweir return;
2434cdf0e10cSrcweir
2435cdf0e10cSrcweir // initialize justifications array
2436cdf0e10cSrcweir mpJustifications = new int[ mnGlyphCapacity ];
2437cdf0e10cSrcweir for( i = 0; i < mnGlyphCount; ++i )
2438cdf0e10cSrcweir mpJustifications[ i ] = mpGlyphAdvances[ i ];
2439cdf0e10cSrcweir
2440cdf0e10cSrcweir // apply new widths to script items
2441cdf0e10cSrcweir long nXOffset = 0;
2442cdf0e10cSrcweir for( int nItem = 0; nItem < mnItemCount; ++nItem )
2443cdf0e10cSrcweir {
2444cdf0e10cSrcweir VisualItem& rVisualItem = mpVisualItems[ nItem ];
2445cdf0e10cSrcweir
2446cdf0e10cSrcweir // set the position of this visual item
2447cdf0e10cSrcweir rVisualItem.mnXOffset = nXOffset;
2448cdf0e10cSrcweir
2449cdf0e10cSrcweir // ignore empty visual items
2450cdf0e10cSrcweir if( rVisualItem.IsEmpty() )
2451cdf0e10cSrcweir {
2452cdf0e10cSrcweir for (i = rVisualItem.mnMinCharPos; i < rVisualItem.mnEndCharPos; i++)
2453cdf0e10cSrcweir nXOffset += mpCharWidths[i];
2454cdf0e10cSrcweir continue;
2455cdf0e10cSrcweir }
2456cdf0e10cSrcweir // ignore irrelevant visual items
2457cdf0e10cSrcweir if( (rVisualItem.mnMinCharPos >= mnEndCharPos)
2458cdf0e10cSrcweir || (rVisualItem.mnEndCharPos <= mnMinCharPos) )
2459cdf0e10cSrcweir continue;
2460cdf0e10cSrcweir
2461cdf0e10cSrcweir // if needed prepare special handling for arabic justification
2462cdf0e10cSrcweir rVisualItem.mbHasKashidas = false;
2463cdf0e10cSrcweir if( rVisualItem.IsRTL() )
2464cdf0e10cSrcweir {
2465cdf0e10cSrcweir for( i = rVisualItem.mnMinGlyphPos; i < rVisualItem.mnEndGlyphPos; ++i )
2466cdf0e10cSrcweir if ( (1U << mpVisualAttrs[i].uJustification) & 0xFF82 ) // any Arabic justification
2467cdf0e10cSrcweir { // excluding SCRIPT_JUSTIFY_NONE
2468cdf0e10cSrcweir // yes
2469cdf0e10cSrcweir rVisualItem.mbHasKashidas = true;
2470cdf0e10cSrcweir // so prepare for kashida handling
2471cdf0e10cSrcweir InitKashidaHandling();
2472cdf0e10cSrcweir break;
2473cdf0e10cSrcweir }
2474cdf0e10cSrcweir
2475cdf0e10cSrcweir if( rVisualItem.HasKashidas() )
2476cdf0e10cSrcweir for( i = rVisualItem.mnMinGlyphPos; i < rVisualItem.mnEndGlyphPos; ++i )
2477cdf0e10cSrcweir {
2478cdf0e10cSrcweir // TODO: check if we still need this hack after correction of kashida placing?
2479cdf0e10cSrcweir // (i87688): apparently yes, we still need it!
2480cdf0e10cSrcweir if ( mpVisualAttrs[i].uJustification == SCRIPT_JUSTIFY_NONE )
2481cdf0e10cSrcweir // usp decided that justification can't be applied here
2482cdf0e10cSrcweir // but maybe our Kashida algorithm thinks differently.
2483cdf0e10cSrcweir // To avoid trouble (gaps within words, last character of
2484cdf0e10cSrcweir // a word gets a Kashida appended) override this.
2485cdf0e10cSrcweir
2486cdf0e10cSrcweir // I chose SCRIPT_JUSTIFY_ARABIC_KASHIDA to replace SCRIPT_JUSTIFY_NONE
2487cdf0e10cSrcweir // just because this previous hack (which I haven't understand, sorry) used
2488cdf0e10cSrcweir // the same value to replace. Don't know if this is really the best
2489cdf0e10cSrcweir // thing to do, but it seems to fix things
2490cdf0e10cSrcweir mpVisualAttrs[i].uJustification = SCRIPT_JUSTIFY_ARABIC_KASHIDA;
2491cdf0e10cSrcweir }
2492cdf0e10cSrcweir }
2493cdf0e10cSrcweir
2494cdf0e10cSrcweir // convert virtual charwidths to glyph justification values
2495cdf0e10cSrcweir HRESULT nRC = (*pScriptApplyLogicalWidth)(
2496cdf0e10cSrcweir mpCharWidths + rVisualItem.mnMinCharPos,
2497cdf0e10cSrcweir rVisualItem.mnEndCharPos - rVisualItem.mnMinCharPos,
2498cdf0e10cSrcweir rVisualItem.mnEndGlyphPos - rVisualItem.mnMinGlyphPos,
2499cdf0e10cSrcweir mpLogClusters + rVisualItem.mnMinCharPos,
2500cdf0e10cSrcweir mpVisualAttrs + rVisualItem.mnMinGlyphPos,
2501cdf0e10cSrcweir mpGlyphAdvances + rVisualItem.mnMinGlyphPos,
2502cdf0e10cSrcweir &rVisualItem.mpScriptItem->a,
2503cdf0e10cSrcweir &rVisualItem.maABCWidths,
2504cdf0e10cSrcweir mpJustifications + rVisualItem.mnMinGlyphPos );
2505cdf0e10cSrcweir
2506cdf0e10cSrcweir if( nRC != 0 )
2507cdf0e10cSrcweir {
2508cdf0e10cSrcweir delete[] mpJustifications;
2509cdf0e10cSrcweir mpJustifications = NULL;
2510cdf0e10cSrcweir break;
2511cdf0e10cSrcweir }
2512cdf0e10cSrcweir
2513cdf0e10cSrcweir // to prepare for the next visual item
2514cdf0e10cSrcweir // update nXOffset to the next items position
2515cdf0e10cSrcweir // before the mpJustifications[] array gets modified
2516cdf0e10cSrcweir int nMinGlyphPos, nEndGlyphPos;
2517cdf0e10cSrcweir if( GetItemSubrange( rVisualItem, nMinGlyphPos, nEndGlyphPos ) )
2518cdf0e10cSrcweir {
2519cdf0e10cSrcweir for( i = nMinGlyphPos; i < nEndGlyphPos; ++i )
2520cdf0e10cSrcweir nXOffset += mpJustifications[ i ];
2521cdf0e10cSrcweir
2522cdf0e10cSrcweir if( rVisualItem.mbHasKashidas )
2523cdf0e10cSrcweir KashidaItemFix( nMinGlyphPos, nEndGlyphPos );
2524cdf0e10cSrcweir }
2525cdf0e10cSrcweir
2526cdf0e10cSrcweir // workaround needed for older USP versions:
2527cdf0e10cSrcweir // right align the justification-adjusted glyphs in their cells for RTL-items
2528cdf0e10cSrcweir // unless the right alignment is done by inserting kashidas
2529cdf0e10cSrcweir if( bManualCellAlign && rVisualItem.IsRTL() && !rVisualItem.HasKashidas() )
2530cdf0e10cSrcweir {
2531cdf0e10cSrcweir for( i = nMinGlyphPos; i < nEndGlyphPos; ++i )
2532cdf0e10cSrcweir {
2533cdf0e10cSrcweir const int nXOffsetAdjust = mpJustifications[i] - mpGlyphAdvances[i];
2534cdf0e10cSrcweir // #i99862# skip diacritics, we mustn't add extra justification to diacritics
2535cdf0e10cSrcweir int nIdxAdd = i - 1;
2536cdf0e10cSrcweir while( (nIdxAdd >= nMinGlyphPos) && !mpGlyphAdvances[nIdxAdd] )
2537cdf0e10cSrcweir --nIdxAdd;
2538cdf0e10cSrcweir if( nIdxAdd < nMinGlyphPos )
2539cdf0e10cSrcweir rVisualItem.mnXOffset += nXOffsetAdjust;
2540cdf0e10cSrcweir else
2541cdf0e10cSrcweir mpJustifications[nIdxAdd] += nXOffsetAdjust;
2542cdf0e10cSrcweir mpJustifications[i] -= nXOffsetAdjust;
2543cdf0e10cSrcweir }
2544cdf0e10cSrcweir }
2545cdf0e10cSrcweir }
2546cdf0e10cSrcweir }
2547cdf0e10cSrcweir
2548cdf0e10cSrcweir // -----------------------------------------------------------------------
2549cdf0e10cSrcweir
2550cdf0e10cSrcweir void UniscribeLayout::InitKashidaHandling()
2551cdf0e10cSrcweir {
2552cdf0e10cSrcweir if( mnMinKashidaGlyph != 0 ) // already initialized
2553cdf0e10cSrcweir return;
2554cdf0e10cSrcweir
2555cdf0e10cSrcweir mrWinFontEntry.InitKashidaHandling( mhDC );
2556cdf0e10cSrcweir mnMinKashidaWidth = static_cast<int>(mfFontScale * mrWinFontEntry.GetMinKashidaWidth());
2557cdf0e10cSrcweir mnMinKashidaGlyph = mrWinFontEntry.GetMinKashidaGlyph();
2558cdf0e10cSrcweir }
2559cdf0e10cSrcweir
2560cdf0e10cSrcweir // adjust the kashida placement matching to the WriterEngine
2561cdf0e10cSrcweir void UniscribeLayout::KashidaItemFix( int nMinGlyphPos, int nEndGlyphPos )
2562cdf0e10cSrcweir {
2563cdf0e10cSrcweir // workaround needed for all known USP versions:
2564cdf0e10cSrcweir // ApplyLogicalWidth does not match ScriptJustify behaviour
2565cdf0e10cSrcweir for( int i = nMinGlyphPos; i < nEndGlyphPos; ++i )
2566cdf0e10cSrcweir {
2567cdf0e10cSrcweir // check for vowels
2568cdf0e10cSrcweir if( (i > nMinGlyphPos && !mpGlyphAdvances[ i-1 ])
2569cdf0e10cSrcweir && (1U << mpVisualAttrs[i].uJustification) & 0xFF83 ) // all Arabic justifiction types
2570cdf0e10cSrcweir { // including SCRIPT_JUSTIFY_NONE
2571cdf0e10cSrcweir // vowel, we do it like ScriptJustify does
2572cdf0e10cSrcweir // the vowel gets the extra width
2573cdf0e10cSrcweir long nSpaceAdded = mpJustifications[ i ] - mpGlyphAdvances[ i ];
2574cdf0e10cSrcweir mpJustifications [ i ] = mpGlyphAdvances [ i ];
2575cdf0e10cSrcweir mpJustifications [ i - 1 ] += nSpaceAdded;
2576cdf0e10cSrcweir }
2577cdf0e10cSrcweir }
2578cdf0e10cSrcweir
2579cdf0e10cSrcweir // redistribute the widths for kashidas
2580cdf0e10cSrcweir for( int i = nMinGlyphPos; i < nEndGlyphPos; )
2581cdf0e10cSrcweir KashidaWordFix ( nMinGlyphPos, nEndGlyphPos, &i );
2582cdf0e10cSrcweir }
2583cdf0e10cSrcweir
2584cdf0e10cSrcweir bool UniscribeLayout::KashidaWordFix ( int nMinGlyphPos, int nEndGlyphPos, int* pnCurrentPos )
2585cdf0e10cSrcweir {
2586cdf0e10cSrcweir // doing pixel work within a word.
2587cdf0e10cSrcweir // sometimes we have extra pixels and sometimes we miss some pixels to get to mnMinKashidaWidth
2588cdf0e10cSrcweir
2589cdf0e10cSrcweir // find the next kashida
2590cdf0e10cSrcweir int nMinPos = *pnCurrentPos;
2591cdf0e10cSrcweir int nMaxPos = *pnCurrentPos;
2592cdf0e10cSrcweir for( int i = nMaxPos; i < nEndGlyphPos; ++i )
2593cdf0e10cSrcweir {
2594cdf0e10cSrcweir if( (mpVisualAttrs[ i ].uJustification >= SCRIPT_JUSTIFY_ARABIC_BLANK)
2595cdf0e10cSrcweir && (mpVisualAttrs[ i ].uJustification < SCRIPT_JUSTIFY_ARABIC_NORMAL) )
2596cdf0e10cSrcweir break;
2597cdf0e10cSrcweir nMaxPos = i;
2598cdf0e10cSrcweir }
2599cdf0e10cSrcweir *pnCurrentPos = nMaxPos + 1;
2600cdf0e10cSrcweir if( nMinPos == nMaxPos )
2601cdf0e10cSrcweir return false;
2602cdf0e10cSrcweir
2603cdf0e10cSrcweir // calculate the available space for an extra kashida
2604cdf0e10cSrcweir long nMaxAdded = 0;
2605cdf0e10cSrcweir int nKashPos = -1;
2606cdf0e10cSrcweir for( int i = nMaxPos; i >= nMinPos; --i )
2607cdf0e10cSrcweir {
2608cdf0e10cSrcweir long nSpaceAdded = mpJustifications[ i ] - mpGlyphAdvances[ i ];
2609cdf0e10cSrcweir if( nSpaceAdded > nMaxAdded )
2610cdf0e10cSrcweir {
2611cdf0e10cSrcweir nKashPos = i;
2612cdf0e10cSrcweir nMaxAdded = nSpaceAdded;
2613cdf0e10cSrcweir }
2614cdf0e10cSrcweir }
2615cdf0e10cSrcweir
2616cdf0e10cSrcweir // return early if there is no need for an extra kashida
2617cdf0e10cSrcweir if ( nMaxAdded <= 0 )
2618cdf0e10cSrcweir return false;
2619cdf0e10cSrcweir // return early if there is not enough space for an extra kashida
2620cdf0e10cSrcweir if( 2*nMaxAdded < mnMinKashidaWidth )
2621cdf0e10cSrcweir return false;
2622cdf0e10cSrcweir
2623cdf0e10cSrcweir // redistribute the extra spacing to the kashida position
2624cdf0e10cSrcweir for( int i = nMinPos; i <= nMaxPos; ++i )
2625cdf0e10cSrcweir {
2626cdf0e10cSrcweir if( i == nKashPos )
2627cdf0e10cSrcweir continue;
2628cdf0e10cSrcweir // everything else should not have extra spacing
2629cdf0e10cSrcweir long nSpaceAdded = mpJustifications[ i ] - mpGlyphAdvances[ i ];
2630cdf0e10cSrcweir if( nSpaceAdded > 0 )
2631cdf0e10cSrcweir {
2632cdf0e10cSrcweir mpJustifications[ i ] -= nSpaceAdded;
2633cdf0e10cSrcweir mpJustifications[ nKashPos ] += nSpaceAdded;
2634cdf0e10cSrcweir }
2635cdf0e10cSrcweir }
2636cdf0e10cSrcweir
2637cdf0e10cSrcweir // check if we fulfill minimal kashida width
2638cdf0e10cSrcweir long nSpaceAdded = mpJustifications[ nKashPos ] - mpGlyphAdvances[ nKashPos ];
2639cdf0e10cSrcweir if( nSpaceAdded < mnMinKashidaWidth )
2640cdf0e10cSrcweir {
2641cdf0e10cSrcweir // ugly: steal some pixels
2642cdf0e10cSrcweir long nSteal = 1;
2643cdf0e10cSrcweir if ( nMaxPos - nMinPos > 0 && ((mnMinKashidaWidth - nSpaceAdded) > (nMaxPos - nMinPos)))
2644cdf0e10cSrcweir nSteal = (mnMinKashidaWidth - nSpaceAdded) / (nMaxPos - nMinPos);
2645cdf0e10cSrcweir for( int i = nMinPos; i <= nMaxPos; ++i )
2646cdf0e10cSrcweir {
2647cdf0e10cSrcweir if( i == nKashPos )
2648cdf0e10cSrcweir continue;
2649cdf0e10cSrcweir nSteal = Min( mnMinKashidaWidth - nSpaceAdded, nSteal );
2650cdf0e10cSrcweir if ( nSteal > 0 )
2651cdf0e10cSrcweir {
2652cdf0e10cSrcweir mpJustifications [ i ] -= nSteal;
2653cdf0e10cSrcweir mpJustifications [ nKashPos ] += nSteal;
2654cdf0e10cSrcweir nSpaceAdded += nSteal;
2655cdf0e10cSrcweir }
2656cdf0e10cSrcweir if( nSpaceAdded >= mnMinKashidaWidth )
2657cdf0e10cSrcweir return true;
2658cdf0e10cSrcweir }
2659cdf0e10cSrcweir }
2660cdf0e10cSrcweir
2661cdf0e10cSrcweir // blank padding
2662cdf0e10cSrcweir long nSpaceMissing = mnMinKashidaWidth - nSpaceAdded;
2663cdf0e10cSrcweir if( nSpaceMissing > 0 )
2664cdf0e10cSrcweir {
2665cdf0e10cSrcweir // inner glyph: distribute extra space evenly
2666cdf0e10cSrcweir if( (nMinPos > nMinGlyphPos) && (nMaxPos < nEndGlyphPos - 1) )
2667cdf0e10cSrcweir {
2668cdf0e10cSrcweir mpJustifications [ nKashPos ] += nSpaceMissing;
2669cdf0e10cSrcweir long nHalfSpace = nSpaceMissing / 2;
2670cdf0e10cSrcweir mpJustifications [ nMinPos - 1 ] -= nHalfSpace;
2671cdf0e10cSrcweir mpJustifications [ nMaxPos + 1 ] -= nSpaceMissing - nHalfSpace;
2672cdf0e10cSrcweir }
2673cdf0e10cSrcweir // rightmost: left glyph gets extra space
2674cdf0e10cSrcweir else if( nMinPos > nMinGlyphPos )
2675cdf0e10cSrcweir {
2676cdf0e10cSrcweir mpJustifications [ nMinPos - 1 ] -= nSpaceMissing;
2677cdf0e10cSrcweir mpJustifications [ nKashPos ] += nSpaceMissing;
2678cdf0e10cSrcweir }
2679cdf0e10cSrcweir // leftmost: right glyph gets extra space
2680cdf0e10cSrcweir else if( nMaxPos < nEndGlyphPos - 1 )
2681cdf0e10cSrcweir {
2682cdf0e10cSrcweir mpJustifications [ nKashPos ] += nSpaceMissing;
2683cdf0e10cSrcweir mpJustifications [ nMaxPos + 1 ] -= nSpaceMissing;
2684cdf0e10cSrcweir }
2685cdf0e10cSrcweir else
2686cdf0e10cSrcweir return false;
2687cdf0e10cSrcweir }
2688cdf0e10cSrcweir
2689cdf0e10cSrcweir return true;
2690cdf0e10cSrcweir }
2691cdf0e10cSrcweir
2692cdf0e10cSrcweir // -----------------------------------------------------------------------
2693cdf0e10cSrcweir
2694cdf0e10cSrcweir void UniscribeLayout::Justify( long nNewWidth )
2695cdf0e10cSrcweir {
2696cdf0e10cSrcweir long nOldWidth = 0;
2697cdf0e10cSrcweir int i;
2698cdf0e10cSrcweir for( i = mnMinCharPos; i < mnEndCharPos; ++i )
2699cdf0e10cSrcweir nOldWidth += mpCharWidths[ i ];
2700cdf0e10cSrcweir if( nOldWidth <= 0 )
2701cdf0e10cSrcweir return;
2702cdf0e10cSrcweir
2703cdf0e10cSrcweir nNewWidth *= mnUnitsPerPixel; // convert into font units
2704cdf0e10cSrcweir if( nNewWidth == nOldWidth )
2705cdf0e10cSrcweir return;
2706cdf0e10cSrcweir // prepare to distribute the extra width evenly among the visual items
2707cdf0e10cSrcweir const double fStretch = (double)nNewWidth / nOldWidth;
2708cdf0e10cSrcweir
2709cdf0e10cSrcweir // initialize justifications array
2710cdf0e10cSrcweir mpJustifications = new int[ mnGlyphCapacity ];
2711cdf0e10cSrcweir for( i = 0; i < mnGlyphCapacity; ++i )
2712cdf0e10cSrcweir mpJustifications[ i ] = mpGlyphAdvances[ i ];
2713cdf0e10cSrcweir
2714cdf0e10cSrcweir // justify stretched script items
2715cdf0e10cSrcweir long nXOffset = 0;
2716cdf0e10cSrcweir SCRIPT_CACHE& rScriptCache = GetScriptCache();
2717cdf0e10cSrcweir for( int nItem = 0; nItem < mnItemCount; ++nItem )
2718cdf0e10cSrcweir {
2719cdf0e10cSrcweir VisualItem& rVisualItem = mpVisualItems[ nItem ];
2720cdf0e10cSrcweir if( rVisualItem.IsEmpty() )
2721cdf0e10cSrcweir continue;
2722cdf0e10cSrcweir
2723cdf0e10cSrcweir if( (rVisualItem.mnMinCharPos < mnEndCharPos)
2724cdf0e10cSrcweir && (rVisualItem.mnEndCharPos > mnMinCharPos) )
2725cdf0e10cSrcweir {
2726cdf0e10cSrcweir long nItemWidth = 0;
2727cdf0e10cSrcweir for( i = rVisualItem.mnMinCharPos; i < rVisualItem.mnEndCharPos; ++i )
2728cdf0e10cSrcweir nItemWidth += mpCharWidths[ i ];
2729cdf0e10cSrcweir nItemWidth = (int)((fStretch - 1.0) * nItemWidth + 0.5);
2730cdf0e10cSrcweir
2731cdf0e10cSrcweir HRESULT nRC = (*pScriptJustify) (
2732cdf0e10cSrcweir mpVisualAttrs + rVisualItem.mnMinGlyphPos,
2733cdf0e10cSrcweir mpGlyphAdvances + rVisualItem.mnMinGlyphPos,
2734cdf0e10cSrcweir rVisualItem.mnEndGlyphPos - rVisualItem.mnMinGlyphPos,
2735cdf0e10cSrcweir nItemWidth,
2736cdf0e10cSrcweir mnMinKashidaWidth,
2737cdf0e10cSrcweir mpJustifications + rVisualItem.mnMinGlyphPos );
2738cdf0e10cSrcweir
2739cdf0e10cSrcweir rVisualItem.mnXOffset = nXOffset;
2740cdf0e10cSrcweir nXOffset += nItemWidth;
2741cdf0e10cSrcweir }
2742cdf0e10cSrcweir }
2743cdf0e10cSrcweir }
2744cdf0e10cSrcweir
2745cdf0e10cSrcweir // -----------------------------------------------------------------------
2746cdf0e10cSrcweir
2747cdf0e10cSrcweir bool UniscribeLayout::IsKashidaPosValid ( int nCharPos ) const
2748cdf0e10cSrcweir {
2749cdf0e10cSrcweir // we have to find the visual item first since the mpLogClusters[]
2750cdf0e10cSrcweir // needed to find the cluster start is relative to to the visual item
2751cdf0e10cSrcweir int nMinGlyphIndex = -1;
2752cdf0e10cSrcweir for( int nItem = 0; nItem < mnItemCount; ++nItem )
2753cdf0e10cSrcweir {
2754cdf0e10cSrcweir const VisualItem& rVisualItem = mpVisualItems[ nItem ];
2755cdf0e10cSrcweir if( (nCharPos >= rVisualItem.mnMinCharPos)
2756cdf0e10cSrcweir && (nCharPos < rVisualItem.mnEndCharPos) )
2757cdf0e10cSrcweir {
2758cdf0e10cSrcweir nMinGlyphIndex = rVisualItem.mnMinGlyphPos;
2759cdf0e10cSrcweir break;
2760cdf0e10cSrcweir }
2761cdf0e10cSrcweir }
2762cdf0e10cSrcweir // Invalid char pos or leftmost glyph in visual item
2763cdf0e10cSrcweir if ( nMinGlyphIndex == -1 || !mpLogClusters[ nCharPos ] )
2764cdf0e10cSrcweir return false;
2765cdf0e10cSrcweir
2766cdf0e10cSrcweir // This test didn't give the expected results
2767cdf0e10cSrcweir /* if( mpLogClusters[ nCharPos+1 ] == mpLogClusters[ nCharPos ])
2768cdf0e10cSrcweir // two chars, one glyph
2769cdf0e10cSrcweir return false;*/
2770cdf0e10cSrcweir
2771cdf0e10cSrcweir const int nGlyphPos = mpLogClusters[ nCharPos ] + nMinGlyphIndex;
2772cdf0e10cSrcweir if( nGlyphPos <= 0 )
2773cdf0e10cSrcweir return true;
2774cdf0e10cSrcweir // justification is only allowed if the glyph to the left has not SCRIPT_JUSTIFY_NONE
2775cdf0e10cSrcweir // and not SCRIPT_JUSTIFY_ARABIC_BLANK
2776cdf0e10cSrcweir // special case: glyph to the left is vowel (no advance width)
2777cdf0e10cSrcweir if ( mpVisualAttrs[ nGlyphPos-1 ].uJustification == SCRIPT_JUSTIFY_ARABIC_BLANK
2778cdf0e10cSrcweir || ( mpVisualAttrs[ nGlyphPos-1 ].uJustification == SCRIPT_JUSTIFY_NONE
2779cdf0e10cSrcweir && mpGlyphAdvances [ nGlyphPos-1 ] ))
2780cdf0e10cSrcweir return false;
2781cdf0e10cSrcweir return true;
2782cdf0e10cSrcweir }
2783cdf0e10cSrcweir
2784cdf0e10cSrcweir #endif // USE_UNISCRIBE
2785cdf0e10cSrcweir
2786cdf0e10cSrcweir #ifdef ENABLE_GRAPHITE
2787cdf0e10cSrcweir
2788cdf0e10cSrcweir class GraphiteLayoutWinImpl : public GraphiteLayout
2789cdf0e10cSrcweir {
2790cdf0e10cSrcweir public:
2791cdf0e10cSrcweir GraphiteLayoutWinImpl(const gr::Font & font, ImplWinFontEntry & rFont)
2792cdf0e10cSrcweir throw()
2793cdf0e10cSrcweir : GraphiteLayout(font), mrFont(rFont) {};
2794cdf0e10cSrcweir virtual ~GraphiteLayoutWinImpl() throw() {};
2795cdf0e10cSrcweir virtual sal_GlyphId getKashidaGlyph(int & rWidth);
2796cdf0e10cSrcweir private:
2797cdf0e10cSrcweir ImplWinFontEntry & mrFont;
2798cdf0e10cSrcweir };
2799cdf0e10cSrcweir
2800cdf0e10cSrcweir sal_GlyphId GraphiteLayoutWinImpl::getKashidaGlyph(int & rWidth)
2801cdf0e10cSrcweir {
2802cdf0e10cSrcweir rWidth = mrFont.GetMinKashidaWidth();
2803cdf0e10cSrcweir return mrFont.GetMinKashidaGlyph();
2804cdf0e10cSrcweir }
2805cdf0e10cSrcweir
2806cdf0e10cSrcweir // This class uses the SIL Graphite engine to provide complex text layout services to the VCL
2807cdf0e10cSrcweir // @author tse
2808cdf0e10cSrcweir //
2809cdf0e10cSrcweir class GraphiteWinLayout : public WinLayout
2810cdf0e10cSrcweir {
2811cdf0e10cSrcweir private:
2812cdf0e10cSrcweir mutable GraphiteWinFont mpFont;
2813cdf0e10cSrcweir grutils::GrFeatureParser * mpFeatures;
2814cdf0e10cSrcweir mutable GraphiteLayoutWinImpl maImpl;
2815cdf0e10cSrcweir public:
28167d9c290fSMichael Stahl GraphiteWinLayout(HDC hDC, const ImplWinFontData& rWFD, ImplWinFontEntry& rWFE) throw();
2817cdf0e10cSrcweir
2818cdf0e10cSrcweir static bool IsGraphiteEnabledFont(HDC hDC) throw();
2819cdf0e10cSrcweir
2820cdf0e10cSrcweir // used by upper layers
2821cdf0e10cSrcweir virtual bool LayoutText( ImplLayoutArgs& ); // first step of layout
2822cdf0e10cSrcweir virtual void AdjustLayout( ImplLayoutArgs& ); // adjusting after fallback etc.
2823cdf0e10cSrcweir // virtual void InitFont() const;
2824cdf0e10cSrcweir virtual void DrawText( SalGraphics& ) const;
2825cdf0e10cSrcweir
2826cdf0e10cSrcweir // methods using string indexing
2827cdf0e10cSrcweir virtual int GetTextBreak( long nMaxWidth, long nCharExtra=0, int nFactor=1 ) const;
2828cdf0e10cSrcweir virtual long FillDXArray( long* pDXArray ) const;
2829cdf0e10cSrcweir
2830cdf0e10cSrcweir virtual void GetCaretPositions( int nArraySize, long* pCaretXArray ) const;
2831cdf0e10cSrcweir
2832cdf0e10cSrcweir // methods using glyph indexing
2833cdf0e10cSrcweir virtual int GetNextGlyphs(int nLen, sal_GlyphId* pGlyphIdxAry, ::Point & rPos, int&,
2834cdf0e10cSrcweir long* pGlyphAdvAry = 0, int* pCharPosAry = 0 ) const;
2835cdf0e10cSrcweir
2836cdf0e10cSrcweir // used by glyph+font+script fallback
2837cdf0e10cSrcweir virtual void MoveGlyph( int nStart, long nNewXPos );
2838cdf0e10cSrcweir virtual void DropGlyph( int nStart );
2839cdf0e10cSrcweir virtual void Simplify( bool bIsBase );
2840cdf0e10cSrcweir ~GraphiteWinLayout() { delete mpFeatures; mpFeatures = NULL; };
2841cdf0e10cSrcweir protected:
2842cdf0e10cSrcweir virtual void ReplaceDC(gr::Segment & segment) const;
2843cdf0e10cSrcweir virtual void RestoreDC(gr::Segment & segment) const;
2844cdf0e10cSrcweir };
2845cdf0e10cSrcweir
2846cdf0e10cSrcweir bool GraphiteWinLayout::IsGraphiteEnabledFont(HDC hDC) throw()
2847cdf0e10cSrcweir {
2848cdf0e10cSrcweir return gr::WinFont::FontHasGraphiteTables(hDC);
2849cdf0e10cSrcweir }
2850cdf0e10cSrcweir
2851cdf0e10cSrcweir GraphiteWinLayout::GraphiteWinLayout(HDC hDC, const ImplWinFontData& rWFD, ImplWinFontEntry& rWFE) throw()
2852cdf0e10cSrcweir : WinLayout(hDC, rWFD, rWFE), mpFont(hDC),
2853cdf0e10cSrcweir maImpl(mpFont, rWFE)
2854cdf0e10cSrcweir {
2855cdf0e10cSrcweir const rtl::OString aLang = MsLangId::convertLanguageToIsoByteString( rWFE.maFontSelData.meLanguage );
2856cdf0e10cSrcweir rtl::OString name = rtl::OUStringToOString(
2857cdf0e10cSrcweir rWFE.maFontSelData.maTargetName, RTL_TEXTENCODING_UTF8 );
2858cdf0e10cSrcweir sal_Int32 nFeat = name.indexOf(grutils::GrFeatureParser::FEAT_PREFIX) + 1;
2859cdf0e10cSrcweir if (nFeat > 0)
2860cdf0e10cSrcweir {
2861cdf0e10cSrcweir rtl::OString aFeat = name.copy(nFeat, name.getLength() - nFeat);
2862cdf0e10cSrcweir mpFeatures = new grutils::GrFeatureParser(mpFont, aFeat.getStr(), aLang.getStr());
2863cdf0e10cSrcweir }
2864cdf0e10cSrcweir else
2865cdf0e10cSrcweir {
2866cdf0e10cSrcweir mpFeatures = new grutils::GrFeatureParser(mpFont, aLang.getStr());
2867cdf0e10cSrcweir }
2868cdf0e10cSrcweir maImpl.SetFeatures(mpFeatures);
2869cdf0e10cSrcweir }
2870cdf0e10cSrcweir
2871cdf0e10cSrcweir void GraphiteWinLayout::ReplaceDC(gr::Segment & segment) const
2872cdf0e10cSrcweir {
2873cdf0e10cSrcweir COLORREF color = GetTextColor(mhDC);
2874cdf0e10cSrcweir dynamic_cast<gr::WinFont&>(segment.getFont()).replaceDC(mhDC);
2875cdf0e10cSrcweir SetTextColor(mhDC, color);
2876cdf0e10cSrcweir }
2877cdf0e10cSrcweir
2878cdf0e10cSrcweir void GraphiteWinLayout::RestoreDC(gr::Segment & segment) const
2879cdf0e10cSrcweir {
2880cdf0e10cSrcweir dynamic_cast<gr::WinFont&>(segment.getFont()).restoreDC();
2881cdf0e10cSrcweir }
2882cdf0e10cSrcweir
2883cdf0e10cSrcweir bool GraphiteWinLayout::LayoutText( ImplLayoutArgs & args)
2884cdf0e10cSrcweir {
2885cdf0e10cSrcweir if (args.mnMinCharPos >= args.mnEndCharPos)
2886cdf0e10cSrcweir {
2887cdf0e10cSrcweir maImpl.clear();
2888cdf0e10cSrcweir return true;
2889cdf0e10cSrcweir }
2890cdf0e10cSrcweir HFONT hUnRotatedFont;
2891cdf0e10cSrcweir if (args.mnOrientation)
2892cdf0e10cSrcweir {
2893cdf0e10cSrcweir // Graphite gets very confused if the font is rotated
2894cdf0e10cSrcweir LOGFONTW aLogFont;
2895cdf0e10cSrcweir ::GetObjectW( mhFont, sizeof(LOGFONTW), &aLogFont);
2896cdf0e10cSrcweir aLogFont.lfEscapement = 0;
2897cdf0e10cSrcweir aLogFont.lfOrientation = 0;
2898cdf0e10cSrcweir hUnRotatedFont = ::CreateFontIndirectW( &aLogFont);
2899cdf0e10cSrcweir ::SelectFont(mhDC, hUnRotatedFont);
2900cdf0e10cSrcweir }
2901cdf0e10cSrcweir WinLayout::AdjustLayout(args);
2902cdf0e10cSrcweir mpFont.replaceDC(mhDC);
2903cdf0e10cSrcweir maImpl.SetFontScale(WinLayout::mfFontScale);
2904cdf0e10cSrcweir //bool succeeded = maImpl.LayoutText(args);
2905cdf0e10cSrcweir #ifdef GRCACHE
2906cdf0e10cSrcweir GrSegRecord * pSegRecord = NULL;
2907cdf0e10cSrcweir gr::Segment * pSegment = maImpl.CreateSegment(args, &pSegRecord);
2908cdf0e10cSrcweir #else
2909cdf0e10cSrcweir gr::Segment * pSegment = maImpl.CreateSegment(args);
2910cdf0e10cSrcweir #endif
2911cdf0e10cSrcweir bool bSucceeded = false;
2912cdf0e10cSrcweir if (pSegment)
2913cdf0e10cSrcweir {
2914cdf0e10cSrcweir // replace the DC on the font within the segment
2915cdf0e10cSrcweir ReplaceDC(*pSegment);
2916cdf0e10cSrcweir // create glyph vectors
2917cdf0e10cSrcweir #ifdef GRCACHE
2918cdf0e10cSrcweir bSucceeded = maImpl.LayoutGlyphs(args, pSegment, pSegRecord);
2919cdf0e10cSrcweir #else
2920cdf0e10cSrcweir bSucceeded = maImpl.LayoutGlyphs(args, pSegment);
2921cdf0e10cSrcweir #endif
2922cdf0e10cSrcweir // restore original DC
2923cdf0e10cSrcweir RestoreDC(*pSegment);
2924cdf0e10cSrcweir #ifdef GRCACHE
2925cdf0e10cSrcweir if (pSegRecord) pSegRecord->unlock();
2926cdf0e10cSrcweir else delete pSegment;
2927cdf0e10cSrcweir #else
2928cdf0e10cSrcweir delete pSegment;
2929cdf0e10cSrcweir #endif
2930cdf0e10cSrcweir }
2931cdf0e10cSrcweir mpFont.restoreDC();
2932cdf0e10cSrcweir if (args.mnOrientation)
2933cdf0e10cSrcweir {
2934cdf0e10cSrcweir // restore the rotated font
2935cdf0e10cSrcweir ::SelectFont(mhDC, mhFont);
2936cdf0e10cSrcweir ::DeleteObject(hUnRotatedFont);
2937cdf0e10cSrcweir }
2938cdf0e10cSrcweir return bSucceeded;
2939cdf0e10cSrcweir }
2940cdf0e10cSrcweir
2941cdf0e10cSrcweir void GraphiteWinLayout::AdjustLayout(ImplLayoutArgs& rArgs)
2942cdf0e10cSrcweir {
2943cdf0e10cSrcweir WinLayout::AdjustLayout(rArgs);
2944cdf0e10cSrcweir maImpl.DrawBase() = WinLayout::maDrawBase;
2945cdf0e10cSrcweir maImpl.DrawOffset() = WinLayout::maDrawOffset;
2946cdf0e10cSrcweir if ( (rArgs.mnFlags & SAL_LAYOUT_BIDI_RTL) && rArgs.mpDXArray)
2947cdf0e10cSrcweir {
2948cdf0e10cSrcweir mrWinFontEntry.InitKashidaHandling(mhDC);
2949cdf0e10cSrcweir }
2950cdf0e10cSrcweir maImpl.AdjustLayout(rArgs);
2951cdf0e10cSrcweir }
2952cdf0e10cSrcweir
2953cdf0e10cSrcweir void GraphiteWinLayout::DrawText(SalGraphics &sal_graphics) const
2954cdf0e10cSrcweir {
2955cdf0e10cSrcweir HFONT hOrigFont = DisableFontScaling();
29562a6d8217SHerbert Dürr const HDC aHDC = static_cast<WinSalGraphics&>(sal_graphics).getHDC();
2957cdf0e10cSrcweir maImpl.DrawBase() = WinLayout::maDrawBase;
2958cdf0e10cSrcweir maImpl.DrawOffset() = WinLayout::maDrawOffset;
2959cdf0e10cSrcweir const int MAX_GLYPHS = 2;
2960cdf0e10cSrcweir sal_GlyphId glyphIntStr[MAX_GLYPHS];
2961cdf0e10cSrcweir WORD glyphWStr[MAX_GLYPHS];
2962cdf0e10cSrcweir int glyphIndex = 0;
2963cdf0e10cSrcweir Point aPos(0,0);
2964cdf0e10cSrcweir int nGlyphs = 0;
2965cdf0e10cSrcweir do
2966cdf0e10cSrcweir {
2967cdf0e10cSrcweir nGlyphs = maImpl.GetNextGlyphs(1, glyphIntStr, aPos, glyphIndex);
2968cdf0e10cSrcweir if (nGlyphs < 1)
2969cdf0e10cSrcweir break;
2970cdf0e10cSrcweir std::copy(glyphIntStr, glyphIntStr + nGlyphs, glyphWStr);
2971cdf0e10cSrcweir ::ExtTextOutW(aHDC, aPos.X(), aPos.Y(), ETO_GLYPH_INDEX,
2972cdf0e10cSrcweir NULL, (LPCWSTR)&(glyphWStr), nGlyphs, NULL);
2973cdf0e10cSrcweir } while (nGlyphs);
2974cdf0e10cSrcweir if( hOrigFont )
29752a6d8217SHerbert Dürr DeleteFont( SelectFont( aHDC, hOrigFont ) );
2976cdf0e10cSrcweir }
2977cdf0e10cSrcweir
2978cdf0e10cSrcweir int GraphiteWinLayout::GetTextBreak( long nMaxWidth, long nCharExtra, int nFactor ) const
2979cdf0e10cSrcweir {
2980cdf0e10cSrcweir mpFont.replaceDC(mhDC);
2981cdf0e10cSrcweir int nBreak = maImpl.GetTextBreak(nMaxWidth, nCharExtra, nFactor);
2982cdf0e10cSrcweir mpFont.restoreDC();
2983cdf0e10cSrcweir return nBreak;
2984cdf0e10cSrcweir }
2985cdf0e10cSrcweir
2986cdf0e10cSrcweir long GraphiteWinLayout::FillDXArray( long* pDXArray ) const
2987cdf0e10cSrcweir {
2988cdf0e10cSrcweir return maImpl.FillDXArray(pDXArray);
2989cdf0e10cSrcweir }
2990cdf0e10cSrcweir
2991cdf0e10cSrcweir void GraphiteWinLayout::GetCaretPositions( int nArraySize, long* pCaretXArray ) const
2992cdf0e10cSrcweir {
2993cdf0e10cSrcweir maImpl.GetCaretPositions(nArraySize, pCaretXArray);
2994cdf0e10cSrcweir }
2995cdf0e10cSrcweir
2996cdf0e10cSrcweir int GraphiteWinLayout::GetNextGlyphs( int length, sal_GlyphId* glyph_out,
2997cdf0e10cSrcweir ::Point & pos_out, int &glyph_slot, long * glyph_adv, int *char_index) const
2998cdf0e10cSrcweir {
2999cdf0e10cSrcweir maImpl.DrawBase() = WinLayout::maDrawBase;
3000cdf0e10cSrcweir maImpl.DrawOffset() = WinLayout::maDrawOffset;
3001cdf0e10cSrcweir return maImpl.GetNextGlyphs(length, glyph_out, pos_out, glyph_slot, glyph_adv, char_index);
3002cdf0e10cSrcweir }
3003cdf0e10cSrcweir
3004cdf0e10cSrcweir void GraphiteWinLayout::MoveGlyph( int glyph_idx, long new_x_pos )
3005cdf0e10cSrcweir {
3006cdf0e10cSrcweir maImpl.MoveGlyph(glyph_idx, new_x_pos);
3007cdf0e10cSrcweir }
3008cdf0e10cSrcweir
3009cdf0e10cSrcweir void GraphiteWinLayout::DropGlyph( int glyph_idx )
3010cdf0e10cSrcweir {
3011cdf0e10cSrcweir maImpl.DropGlyph(glyph_idx);
3012cdf0e10cSrcweir }
3013cdf0e10cSrcweir
3014cdf0e10cSrcweir void GraphiteWinLayout::Simplify( bool is_base )
3015cdf0e10cSrcweir {
3016cdf0e10cSrcweir maImpl.Simplify(is_base);
3017cdf0e10cSrcweir }
3018cdf0e10cSrcweir #endif // ENABLE_GRAPHITE
3019cdf0e10cSrcweir // =======================================================================
3020cdf0e10cSrcweir
3021cdf0e10cSrcweir SalLayout* WinSalGraphics::GetTextLayout( ImplLayoutArgs& rArgs, int nFallbackLevel )
3022cdf0e10cSrcweir {
3023cdf0e10cSrcweir DBG_ASSERT( mpWinFontEntry[nFallbackLevel], "WinSalGraphics mpWinFontEntry==NULL");
3024cdf0e10cSrcweir
3025cdf0e10cSrcweir WinLayout* pWinLayout = NULL;
3026cdf0e10cSrcweir
3027cdf0e10cSrcweir const ImplWinFontData& rFontFace = *mpWinFontData[ nFallbackLevel ];
3028cdf0e10cSrcweir ImplWinFontEntry& rFontInstance = *mpWinFontEntry[ nFallbackLevel ];
3029cdf0e10cSrcweir
3030cdf0e10cSrcweir #if defined( USE_UNISCRIBE )
3031cdf0e10cSrcweir if( !(rArgs.mnFlags & SAL_LAYOUT_COMPLEX_DISABLED)
3032cdf0e10cSrcweir && (aUspModule || (bUspEnabled && InitUSP())) ) // CTL layout engine
3033cdf0e10cSrcweir {
3034cdf0e10cSrcweir #ifdef ENABLE_GRAPHITE
3035cdf0e10cSrcweir if (rFontFace.SupportsGraphite())
30362a6d8217SHerbert Dürr pWinLayout = new GraphiteWinLayout( getHDC(), rFontFace, rFontInstance);
3037cdf0e10cSrcweir else
3038cdf0e10cSrcweir #endif // ENABLE_GRAPHITE
3039cdf0e10cSrcweir // script complexity is determined in upper layers
30405f27b83cSArmin Le Grand pWinLayout = new UniscribeLayout( getHDC(), rFontFace, rFontInstance );
3041cdf0e10cSrcweir // NOTE: it must be guaranteed that the WinSalGraphics lives longer than
3042cdf0e10cSrcweir // the created UniscribeLayout, otherwise the data passed into the
3043cdf0e10cSrcweir // constructor might become invalid too early
3044cdf0e10cSrcweir }
3045cdf0e10cSrcweir else
3046cdf0e10cSrcweir #endif // USE_UNISCRIBE
3047cdf0e10cSrcweir {
3048cdf0e10cSrcweir #ifdef GCP_KERN_HACK
3049cdf0e10cSrcweir if( (rArgs.mnFlags & SAL_LAYOUT_KERNING_PAIRS) && !rFontInstance.HasKernData() )
3050cdf0e10cSrcweir {
3051cdf0e10cSrcweir // TODO: directly cache kerning info in the rFontInstance
3052cdf0e10cSrcweir // TODO: get rid of kerning methods+data in WinSalGraphics object
3053cdf0e10cSrcweir GetKernPairs( 0, NULL );
3054cdf0e10cSrcweir rFontInstance.SetKernData( mnFontKernPairCount, mpFontKernPairs );
3055cdf0e10cSrcweir }
3056cdf0e10cSrcweir #endif // GCP_KERN_HACK
3057cdf0e10cSrcweir
3058cdf0e10cSrcweir BYTE eCharSet = ANSI_CHARSET;
3059cdf0e10cSrcweir if( mpLogFont )
3060cdf0e10cSrcweir eCharSet = mpLogFont->lfCharSet;
3061cdf0e10cSrcweir #ifdef ENABLE_GRAPHITE
3062cdf0e10cSrcweir if (rFontFace.SupportsGraphite())
30632a6d8217SHerbert Dürr pWinLayout = new GraphiteWinLayout( getHDC(), rFontFace, rFontInstance);
3064cdf0e10cSrcweir else
3065cdf0e10cSrcweir #endif // ENABLE_GRAPHITE
30665f27b83cSArmin Le Grand pWinLayout = new SimpleWinLayout( getHDC(), eCharSet, rFontFace, rFontInstance );
3067cdf0e10cSrcweir }
3068cdf0e10cSrcweir
3069cdf0e10cSrcweir if( mfFontScale != 1.0 )
3070cdf0e10cSrcweir pWinLayout->SetFontScale( mfFontScale );
3071cdf0e10cSrcweir
3072cdf0e10cSrcweir return pWinLayout;
3073cdf0e10cSrcweir }
3074cdf0e10cSrcweir
3075cdf0e10cSrcweir // -----------------------------------------------------------------------
3076cdf0e10cSrcweir
3077cdf0e10cSrcweir int WinSalGraphics::GetMinKashidaWidth()
3078cdf0e10cSrcweir {
3079cdf0e10cSrcweir if( !mpWinFontEntry[0] )
3080cdf0e10cSrcweir return 0;
30815f27b83cSArmin Le Grand mpWinFontEntry[0]->InitKashidaHandling( getHDC() );
3082cdf0e10cSrcweir int nMinKashida = static_cast<int>(mfFontScale * mpWinFontEntry[0]->GetMinKashidaWidth());
3083cdf0e10cSrcweir return nMinKashida;
3084cdf0e10cSrcweir }
3085cdf0e10cSrcweir
3086cdf0e10cSrcweir // =======================================================================
3087cdf0e10cSrcweir
3088cdf0e10cSrcweir ImplWinFontEntry::ImplWinFontEntry( ImplFontSelectData& rFSD )
3089cdf0e10cSrcweir : ImplFontEntry( rFSD )
3090cdf0e10cSrcweir , maWidthMap( 512 )
3091cdf0e10cSrcweir , mpKerningPairs( NULL )
3092cdf0e10cSrcweir , mnKerningPairs( -1 )
3093cdf0e10cSrcweir , mnMinKashidaWidth( -1 )
3094cdf0e10cSrcweir , mnMinKashidaGlyph( -1 )
3095cdf0e10cSrcweir {
3096cdf0e10cSrcweir #ifdef USE_UNISCRIBE
3097cdf0e10cSrcweir maScriptCache = NULL;
3098cdf0e10cSrcweir #endif // USE_UNISCRIBE
3099cdf0e10cSrcweir }
3100cdf0e10cSrcweir
3101cdf0e10cSrcweir // -----------------------------------------------------------------------
3102cdf0e10cSrcweir
3103cdf0e10cSrcweir ImplWinFontEntry::~ImplWinFontEntry()
3104cdf0e10cSrcweir {
3105cdf0e10cSrcweir #ifdef USE_UNISCRIBE
3106cdf0e10cSrcweir if( maScriptCache != NULL )
3107cdf0e10cSrcweir (*pScriptFreeCache)( &maScriptCache );
3108cdf0e10cSrcweir #endif // USE_UNISCRIBE
3109cdf0e10cSrcweir #ifdef GCP_KERN_HACK
3110cdf0e10cSrcweir delete[] mpKerningPairs;
3111cdf0e10cSrcweir #endif // GCP_KERN_HACK
3112cdf0e10cSrcweir }
3113cdf0e10cSrcweir
3114cdf0e10cSrcweir // -----------------------------------------------------------------------
3115cdf0e10cSrcweir
3116cdf0e10cSrcweir bool ImplWinFontEntry::HasKernData() const
3117cdf0e10cSrcweir {
3118cdf0e10cSrcweir return (mnKerningPairs >= 0);
3119cdf0e10cSrcweir }
3120cdf0e10cSrcweir
3121cdf0e10cSrcweir // -----------------------------------------------------------------------
3122cdf0e10cSrcweir
3123cdf0e10cSrcweir void ImplWinFontEntry::SetKernData( int nPairCount, const KERNINGPAIR* pPairData )
3124cdf0e10cSrcweir {
3125cdf0e10cSrcweir mnKerningPairs = nPairCount;
3126cdf0e10cSrcweir mpKerningPairs = new KERNINGPAIR[ mnKerningPairs ];
3127cdf0e10cSrcweir ::memcpy( mpKerningPairs, (const void*)pPairData, nPairCount*sizeof(KERNINGPAIR) );
3128cdf0e10cSrcweir }
3129cdf0e10cSrcweir
3130cdf0e10cSrcweir // -----------------------------------------------------------------------
3131cdf0e10cSrcweir
3132cdf0e10cSrcweir int ImplWinFontEntry::GetKerning( sal_Unicode cLeft, sal_Unicode cRight ) const
3133cdf0e10cSrcweir {
3134cdf0e10cSrcweir int nKernAmount = 0;
3135cdf0e10cSrcweir if( mpKerningPairs )
3136cdf0e10cSrcweir {
3137cdf0e10cSrcweir const KERNINGPAIR aRefPair = { cLeft, cRight, 0 };
3138cdf0e10cSrcweir const KERNINGPAIR* pFirstPair = mpKerningPairs;
3139cdf0e10cSrcweir const KERNINGPAIR* pEndPair = mpKerningPairs + mnKerningPairs;
3140cdf0e10cSrcweir const KERNINGPAIR* pPair = std::lower_bound( pFirstPair,
3141cdf0e10cSrcweir pEndPair, aRefPair, ImplCmpKernData );
3142cdf0e10cSrcweir if( (pPair != pEndPair)
3143cdf0e10cSrcweir && (pPair->wFirst == aRefPair.wFirst)
3144cdf0e10cSrcweir && (pPair->wSecond == aRefPair.wSecond) )
3145cdf0e10cSrcweir nKernAmount = pPair->iKernAmount;
3146cdf0e10cSrcweir }
3147cdf0e10cSrcweir
3148cdf0e10cSrcweir return nKernAmount;
3149cdf0e10cSrcweir }
3150cdf0e10cSrcweir
3151cdf0e10cSrcweir // -----------------------------------------------------------------------
3152cdf0e10cSrcweir
3153cdf0e10cSrcweir bool ImplWinFontEntry::InitKashidaHandling( HDC hDC )
3154cdf0e10cSrcweir {
3155cdf0e10cSrcweir if( mnMinKashidaWidth >= 0 ) // already cached?
3156cdf0e10cSrcweir return mnMinKashidaWidth;
3157cdf0e10cSrcweir
3158cdf0e10cSrcweir // initialize the kashida width
3159cdf0e10cSrcweir mnMinKashidaWidth = 0;
3160cdf0e10cSrcweir mnMinKashidaGlyph = 0;
3161cdf0e10cSrcweir #ifdef USE_UNISCRIBE
3162cdf0e10cSrcweir if (aUspModule || (bUspEnabled && InitUSP()))
3163cdf0e10cSrcweir {
3164cdf0e10cSrcweir SCRIPT_FONTPROPERTIES aFontProperties;
3165cdf0e10cSrcweir aFontProperties.cBytes = sizeof (aFontProperties);
3166cdf0e10cSrcweir SCRIPT_CACHE& rScriptCache = GetScriptCache();
3167cdf0e10cSrcweir HRESULT nRC = (*pScriptGetFontProperties)( hDC, &rScriptCache, &aFontProperties );
3168cdf0e10cSrcweir if( nRC != 0 )
3169cdf0e10cSrcweir return false;
3170cdf0e10cSrcweir mnMinKashidaWidth = aFontProperties.iKashidaWidth;
3171cdf0e10cSrcweir mnMinKashidaGlyph = aFontProperties.wgKashida;
3172cdf0e10cSrcweir }
3173cdf0e10cSrcweir #endif // USE_UNISCRIBE
3174cdf0e10cSrcweir
3175cdf0e10cSrcweir return true;
3176cdf0e10cSrcweir }
3177cdf0e10cSrcweir
3178cdf0e10cSrcweir // =======================================================================
3179cdf0e10cSrcweir
3180cdf0e10cSrcweir ImplFontData* ImplWinFontData::Clone() const
3181cdf0e10cSrcweir {
3182cdf0e10cSrcweir if( mpUnicodeMap )
3183cdf0e10cSrcweir mpUnicodeMap->AddReference();
3184cdf0e10cSrcweir ImplFontData* pClone = new ImplWinFontData( *this );
3185cdf0e10cSrcweir return pClone;
3186cdf0e10cSrcweir }
3187cdf0e10cSrcweir
3188cdf0e10cSrcweir // -----------------------------------------------------------------------
3189cdf0e10cSrcweir
3190cdf0e10cSrcweir ImplFontEntry* ImplWinFontData::CreateFontInstance( ImplFontSelectData& rFSD ) const
3191cdf0e10cSrcweir {
3192cdf0e10cSrcweir ImplFontEntry* pEntry = new ImplWinFontEntry( rFSD );
3193cdf0e10cSrcweir return pEntry;
3194cdf0e10cSrcweir }
3195cdf0e10cSrcweir
3196cdf0e10cSrcweir // =======================================================================
3197