summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rwxr-xr-xvcl/aqua/source/gdi/salatslayout.cxx35
-rwxr-xr-xvcl/inc/vcl/sallayout.hxx1
-rw-r--r--vcl/source/gdi/outdev6.cxx1
-rwxr-xr-xvcl/source/gdi/sallayout.cxx144
-rw-r--r--vcl/source/glyphs/gcach_ftyp.cxx11
-rwxr-xr-xvcl/source/glyphs/gcach_layout.cxx67
-rwxr-xr-xvcl/win/source/gdi/winlayout.cxx50
7 files changed, 205 insertions, 104 deletions
diff --git a/vcl/aqua/source/gdi/salatslayout.cxx b/vcl/aqua/source/gdi/salatslayout.cxx
index ca1a38c94043..01808d4d2a0d 100755
--- a/vcl/aqua/source/gdi/salatslayout.cxx
+++ b/vcl/aqua/source/gdi/salatslayout.cxx
@@ -86,6 +86,7 @@ private:
// mutable members since these details are all lazy initialized
mutable int mnGlyphCount; // glyph count
mutable Fixed mnCachedWidth; // cached value of resulting typographical width
+ int mnTrailingSpaceWidth; // in Pixels
mutable ATSGlyphRef* mpGlyphIds; // ATSU glyph ids
mutable Fixed* mpCharWidths; // map relative charpos to charwidth
@@ -104,8 +105,8 @@ private:
mutable class FallbackInfo* mpFallbackInfo;
// x-offset relative to layout origin
- // currently always zero since we use native glyph fallback
- static const Fixed mnBaseAdv = 0;
+ // currently only used in RTL-layouts
+ mutable Fixed mnBaseAdv;
};
class FallbackInfo
@@ -130,6 +131,7 @@ ATSLayout::ATSLayout( ATSUStyle& rATSUStyle, float fFontScale )
mfFontScale( fFontScale ),
mnGlyphCount( -1 ),
mnCachedWidth( 0 ),
+ mnTrailingSpaceWidth( 0 ),
mpGlyphIds( NULL ),
mpCharWidths( NULL ),
mpChars2Glyphs( NULL ),
@@ -138,7 +140,8 @@ ATSLayout::ATSLayout( ATSUStyle& rATSUStyle, float fFontScale )
mpGlyphAdvances( NULL ),
mpGlyphOrigAdvs( NULL ),
mpDeltaY( NULL ),
- mpFallbackInfo( NULL )
+ mpFallbackInfo( NULL ),
+ mnBaseAdv( 0 )
{}
// -----------------------------------------------------------------------
@@ -277,12 +280,16 @@ void ATSLayout::AdjustLayout( ImplLayoutArgs& rArgs )
nPixelWidth = rArgs.mpDXArray[ mnCharCount - 1 ];
// workaround for ATSUI not using trailing spaces for justification
- int nTrailingSpaceWidth = 0;
+ mnTrailingSpaceWidth = 0;
int i = mnCharCount;
while( (--i > 0) && IsSpacingGlyph( rArgs.mpStr[mnMinCharPos+i]|GF_ISCHAR ) )
- nTrailingSpaceWidth += rArgs.mpDXArray[i] - rArgs.mpDXArray[i-1];
- nOrigWidth -= nTrailingSpaceWidth;
- nPixelWidth -= nTrailingSpaceWidth;
+ mnTrailingSpaceWidth += rArgs.mpDXArray[i] - rArgs.mpDXArray[i-1];
+ nOrigWidth -= mnTrailingSpaceWidth;
+ nPixelWidth -= mnTrailingSpaceWidth;
+ // trailing spaces can be leftmost spaces in RTL-layouts
+ // TODO: use BiDi-algorithm to thoroughly check this assumption
+ if( rArgs.mnFlags & SAL_LAYOUT_BIDI_RTL)
+ mnBaseAdv = mnTrailingSpaceWidth;
// TODO: use all mpDXArray elements for layouting
}
@@ -606,9 +613,11 @@ long ATSLayout::GetTextWidth() const
// measure the bound extremas
mnCachedWidth = nRightBound - nLeftBound;
+ // adjust for eliminated trailing space widths
}
- const int nScaledWidth = Fixed2Vcl( mnCachedWidth );
+ int nScaledWidth = Fixed2Vcl( mnCachedWidth );
+ nScaledWidth += mnTrailingSpaceWidth;
return nScaledWidth;
}
@@ -628,6 +637,9 @@ long ATSLayout::FillDXArray( long* pDXArray ) const
if( !pDXArray )
return GetTextWidth();
+ // check assumptions
+ DBG_ASSERT( !nTrailingSpaceWidth, "ATSLayout::FillDXArray() with nTSW!=0" );
+
// initialize details about the resulting layout
InitGIA();
@@ -667,6 +679,10 @@ int ATSLayout::GetTextBreak( long nMaxWidth, long nCharExtra, int nFactor ) cons
// get a quick overview on what could fit
const long nPixelWidth = (nMaxWidth - (nCharExtra * mnCharCount)) / nFactor;
+ // check assumptions
+ DBG_ASSERT( !nTrailingSpaceWidth, "ATSLayout::GetTextBreak() with nTSW!=0" );
+
+ // initial measurement of text break position
UniCharArrayOffset nBreakPos = mnMinCharPos;
const ATSUTextMeasurement nATSUMaxWidth = Vcl2Fixed( nPixelWidth );
OSStatus nStatus = ATSUBreakLine( maATSULayout, mnMinCharPos,
@@ -894,7 +910,8 @@ bool ATSLayout::InitGIA( ImplLayoutArgs* pArgs ) const
if( pArgs && pArgs->mpDXArray )
{
// TODO: non-strong-LTR case cases should be handled too
- if( 0 == (~pArgs->mnFlags & (TEXT_LAYOUT_BIDI_STRONG|TEXT_LAYOUT_BIDI_LTR)) )
+ if( (pArgs->mnFlags & TEXT_LAYOUT_BIDI_STRONG)
+ && !(pArgs->mnFlags & TEXT_LAYOUT_BIDI_RTL) )
{
Fixed nSumCharWidths = 0;
SubPortion aSubPortion = { mnMinCharPos, 0, 0 };
diff --git a/vcl/inc/vcl/sallayout.hxx b/vcl/inc/vcl/sallayout.hxx
index d40dde9014bc..fe7cee130a3a 100755
--- a/vcl/inc/vcl/sallayout.hxx
+++ b/vcl/inc/vcl/sallayout.hxx
@@ -333,6 +333,7 @@ public:
bool IsClusterStart() const { return !(mnFlags & IS_IN_CLUSTER); }
bool IsRTLGlyph() const { return ((mnFlags & IS_RTL_GLYPH) != 0); }
+ bool IsDiacritic() const { return (mnOrigWidth <= 0); } // TODO: better heuristic
};
// ---------------
diff --git a/vcl/source/gdi/outdev6.cxx b/vcl/source/gdi/outdev6.cxx
index d4c24ff44e20..f76aa6b1dd55 100644
--- a/vcl/source/gdi/outdev6.cxx
+++ b/vcl/source/gdi/outdev6.cxx
@@ -166,7 +166,6 @@ void OutputDevice::DrawTransparent( const basegfx::B2DPolyPolygon& rB2DPolyPoly,
DBG_TRACE( "OutputDevice::DrawTransparent(B2D&,transparency)" );
DBG_CHKTHIS( OutputDevice, ImplDbgCheckOutputDevice );
-fprintf(stderr,"OD::DT( fT=%f, bAA=%d)\n",fTransparency,mnAntialiasing);//##############
// AW: Do NOT paint empty PolyPolygons
if(!rB2DPolyPoly.count())
return;
diff --git a/vcl/source/gdi/sallayout.cxx b/vcl/source/gdi/sallayout.cxx
index 6b695b8a22f6..0ac6610ab784 100755
--- a/vcl/source/gdi/sallayout.cxx
+++ b/vcl/source/gdi/sallayout.cxx
@@ -254,6 +254,7 @@ sal_UCS4 GetLocalizedChar( sal_UCS4 nChar, LanguageType eLang )
case LANGUAGE_ARABIC_SAUDI_ARABIA & LANGUAGE_MASK_PRIMARY:
nOffset = 0x0660 - '0'; // arabic-indic digits
break;
+ case LANGUAGE_FARSI & LANGUAGE_MASK_PRIMARY:
case LANGUAGE_URDU & LANGUAGE_MASK_PRIMARY:
case LANGUAGE_PUNJABI & LANGUAGE_MASK_PRIMARY: //???
case LANGUAGE_SINDHI & LANGUAGE_MASK_PRIMARY:
@@ -548,7 +549,7 @@ ImplLayoutArgs::ImplLayoutArgs( const xub_Unicode* pStr, int nLen,
UBiDiLevel nLevel = UBIDI_DEFAULT_LTR;
if( mnFlags & SAL_LAYOUT_BIDI_RTL )
- nLevel = UBIDI_RTL;
+ nLevel = UBIDI_DEFAULT_RTL;
// prepare substring for BiDi analysis
// TODO: reuse allocated pParaBidi
@@ -567,20 +568,16 @@ ImplLayoutArgs::ImplLayoutArgs( const xub_Unicode* pStr, int nLen,
}
// run BiDi algorithm
- int nRunCount = ubidi_countRuns( pLineBidi, &rcI18n);
+ const int nRunCount = ubidi_countRuns( pLineBidi, &rcI18n );
//maRuns.resize( 2 * nRunCount );
- // TODO: see comment about #110273# below, remove when external issue fixed
- const UBiDiLevel* pParaLevels = ubidi_getLevels( pParaBidi, &rcI18n);
for( int i = 0; i < nRunCount; ++i )
{
int32_t nMinPos, nLength;
- ubidi_getVisualRun( pLineBidi, i, &nMinPos, &nLength );
- int nPos0 = nMinPos + mnMinCharPos;
- int nPos1 = nPos0 + nLength;
+ const UBiDiDirection nDir = ubidi_getVisualRun( pLineBidi, i, &nMinPos, &nLength );
+ const int nPos0 = nMinPos + mnMinCharPos;
+ const int nPos1 = nPos0 + nLength;
- // bool bRTL = (nDir == UBIDI_RTL);
- // workaround for #110273# (probably ICU problem TODO: analyze there)
- bool bRTL = ((pParaLevels[ nPos0 ] & 1) != 0);
+ const bool bRTL = (nDir == UBIDI_RTL);
AddRun( nPos0, nPos1, bRTL );
}
@@ -953,8 +950,8 @@ bool GenericSalLayout::GetCharWidths( sal_Int32* pCharWidths ) const
pCharWidths[n] = 0;
// determine cluster extents
- const GlyphItem* pG = mpGlyphItems;
- for( int i = mnGlyphCount; --i >= 0; ++pG )
+ const GlyphItem* const pEnd = mpGlyphItems + mnGlyphCount;
+ for( const GlyphItem* pG = mpGlyphItems; pG < pEnd; ++pG )
{
// use cluster start to get char index
if( !pG->IsClusterStart() )
@@ -974,11 +971,13 @@ bool GenericSalLayout::GetCharWidths( sal_Int32* pCharWidths ) const
// calculate right x-position for this glyph cluster
// break if no more glyphs in layout
// break at next glyph cluster start
- for(; (i > 0) && !pG[1].IsClusterStart(); --i )
+ while( (pG+1 < pEnd) && !pG[1].IsClusterStart() )
{
// advance to next glyph in cluster
++pG;
+ if( pG->IsDiacritic() )
+ continue; // ignore diacritics
// get leftmost x-extent of this glyph
long nXPos = pG->maLinearPos.X();
if( nXPosMin > nXPos )
@@ -992,8 +991,19 @@ bool GenericSalLayout::GetCharWidths( sal_Int32* pCharWidths ) const
// when the current cluster overlaps with the next one assume
// rightmost cluster edge is the leftmost edge of next cluster
- if( (i > 0) && (nXPosMax > pG[1].maLinearPos.X()) )
- nXPosMax = pG[1].maLinearPos.X();
+ // for clusters that do not have x-sorted glyphs
+ // TODO: avoid recalculation of left bound in next cluster iteration
+ for( const GlyphItem* pN = pG; ++pN < pEnd; )
+ {
+ if( pN->IsClusterStart() )
+ break;
+ if( pN->IsDiacritic() )
+ continue; // ignore diacritics
+ if( nXPosMax > pN->maLinearPos.X() )
+ nXPosMax = pN->maLinearPos.X();
+ }
+ if( nXPosMax < nXPosMin )
+ nXPosMin = nXPosMax = 0;
// character width is sum of glyph cluster widths
pCharWidths[n] += nXPosMax - nXPosMin;
@@ -1130,15 +1140,14 @@ void GenericSalLayout::ApplyDXArray( ImplLayoutArgs& rArgs )
// adjust cluster glyph widths and positions
nDelta = nBasePointX + (nNewPos - pG->maLinearPos.X());
- if( !pG->IsRTLGlyph()
- || (rArgs.mnFlags & SAL_LAYOUT_KASHIDA_JUSTIFICATON) )
+ if( !pG->IsRTLGlyph() )
{
- // for (LTR || KASHIDA) case extend rightmost glyph in cluster
+ // for LTR case extend rightmost glyph in cluster
pClusterG[-1].mnNewWidth += nDiff;
}
else
{
- // right align cluster in new space for (RTL && !KASHIDA) case
+ // right align cluster in new space for RTL case
pG->mnNewWidth += nDiff;
nDelta += nDiff;
}
@@ -1168,7 +1177,7 @@ void GenericSalLayout::Justify( long nNewWidth )
int nMaxGlyphWidth = 0;
for( pG = mpGlyphItems; pG < pGRight; ++pG )
{
- if( pG->mnOrigWidth > 0 )
+ if( !pG->IsDiacritic() )
++nStretchable;
if( nMaxGlyphWidth < pG->mnOrigWidth)
nMaxGlyphWidth = pG->mnOrigWidth;
@@ -1195,7 +1204,7 @@ void GenericSalLayout::Justify( long nNewWidth )
pG->maLinearPos.X() += nDeltaSum;
// do not stretch non-stretchable glyphs
- if( (pG->mnOrigWidth <= 0) || (nStretchable <= 0) )
+ if( pG->IsDiacritic() || (nStretchable <= 0) )
continue;
// distribute extra space equally to stretchable glyphs
@@ -1279,13 +1288,18 @@ void GenericSalLayout::KashidaJustify( long nKashidaIndex, int nKashidaWidth )
int nKashidaCount = 0, i;
for( i = 0; i < mnGlyphCount; ++i, ++pG1 )
{
+ // only inject kashidas in RTL contexts
if( !pG1->IsRTLGlyph() )
continue;
+ // no kashida-injection for blank justified expansion either
+ if( IsSpacingGlyph( pG1->mnGlyphIndex ) )
+ continue;
- int nDelta = pG1->mnNewWidth - pG1->mnOrigWidth;
+ // calculate gap, ignore if too small
+ const int nGapWidth = pG1->mnNewWidth - pG1->mnOrigWidth;
// worst case is one kashida even for mini-gaps
- if( nDelta > 0 )
- nKashidaCount += 1 + (nDelta / nKashidaWidth);
+ if( 3 * nGapWidth >= nKashidaWidth )
+ nKashidaCount += 1 + (nGapWidth / nKashidaWidth);
}
if( !nKashidaCount )
@@ -1302,19 +1316,23 @@ void GenericSalLayout::KashidaJustify( long nKashidaIndex, int nKashidaWidth )
// default action is to copy array element
*pG2 = *pG1;
- // only apply kashida in a RTL context
+ // only inject kashida in RTL contexts
if( !pG1->IsRTLGlyph() )
continue;
+ // no kashida-injection for blank justified expansion either
+ if( IsSpacingGlyph( pG1->mnGlyphIndex ) )
+ continue;
// calculate gap, skip if too small
- int nDelta = pG1->mnNewWidth - pG1->mnOrigWidth;
- if( 3*nDelta < nKashidaWidth )
+ int nGapWidth = pG1->mnNewWidth - pG1->mnOrigWidth;
+ if( 3*nGapWidth < nKashidaWidth )
continue;
// fill gap with kashidas
nKashidaCount = 0;
Point aPos = pG1->maLinearPos;
- for(; nDelta > 0; nDelta -= nKashidaWidth, ++nKashidaCount )
+ aPos.X() -= nGapWidth; // cluster is already right aligned
+ for(; nGapWidth > 0; nGapWidth -= nKashidaWidth, ++nKashidaCount )
{
*(pG2++) = GlyphItem( pG1->mnCharPos, nKashidaIndex, aPos,
GlyphItem::IS_IN_CLUSTER|GlyphItem::IS_RTL_GLYPH, nKashidaWidth );
@@ -1322,21 +1340,21 @@ void GenericSalLayout::KashidaJustify( long nKashidaIndex, int nKashidaWidth )
}
// fixup rightmost kashida for gap remainder
- if( nDelta < 0 )
+ if( nGapWidth < 0 )
{
- aPos.X() += nDelta;
+ aPos.X() += nGapWidth;
if( nKashidaCount <= 1 )
- nDelta /= 2; // for small gap move kashida to middle
- pG2[-1].mnNewWidth += nDelta; // adjust kashida width to gap width
- pG2[-1].maLinearPos.X() += nDelta;
+ nGapWidth /= 2; // for small gap move kashida to middle
+ pG2[-1].mnNewWidth += nGapWidth; // adjust kashida width to gap width
+ pG2[-1].maLinearPos.X() += nGapWidth;
}
- // when kashidas were used move the original glyph
+ // when kashidas were inserted move the original cluster
// to the right and shrink it to it's original width
*pG2 = *pG1;
pG2->maLinearPos.X() = aPos.X();
pG2->mnNewWidth = pG2->mnOrigWidth;
- }
+ }
// use the new glyph array
DBG_ASSERT( mnGlyphCapacity >= pG2-pNewGlyphItems, "KashidaJustify overflow" );
@@ -1487,8 +1505,16 @@ void GenericSalLayout::MoveGlyph( int nStart, long nNewXPos )
{
if( nStart >= mnGlyphCount )
return;
+
GlyphItem* pG = mpGlyphItems + nStart;
+ // the nNewXPos argument determines the new cell position
+ // as RTL-glyphs are right justified in their cell
+ // the cell position needs to be adjusted to the glyph position
+ if( pG->IsRTLGlyph() )
+ nNewXPos += pG->mnNewWidth - pG->mnOrigWidth;
+ // calculate the x-offset to the old position
long nXDelta = nNewXPos - pG->maLinearPos.X();
+ // adjust all following glyph positions if needed
if( nXDelta != 0 )
{
GlyphItem* const pGEnd = mpGlyphItems + mnGlyphCount;
@@ -1534,27 +1560,37 @@ void GenericSalLayout::Simplify( bool bIsBase )
// make sure GlyphItems are sorted left to right
void GenericSalLayout::SortGlyphItems()
{
+ // move cluster components behind their cluster start (especially for RTL)
// using insertion sort because the glyph items are "almost sorted"
- GlyphItem* pGL = mpGlyphItems;
- const GlyphItem* pGEnd = mpGlyphItems + mnGlyphCount;
- for( GlyphItem* pGR = pGL; ++pGR < pGEnd; pGL = pGR )
+ const GlyphItem* const pGEnd = mpGlyphItems + mnGlyphCount;
+ for( GlyphItem* pG = mpGlyphItems; pG < pGEnd; ++pG )
{
- // nothing to do when already in correct order
- int nXPos = pGR->maLinearPos.X();
- if( pGL->maLinearPos.X() <= nXPos )
+ // find a cluster starting with a diacritic
+ if( !pG->IsDiacritic() )
continue;
-
- // keep data of misplaced item
- GlyphItem aGI = *pGR;
- // make room for misplaced item
- do {
- pGL[1] = pGL[0];
- pGL[1].mnFlags |= GlyphItem::IS_IN_CLUSTER;
- } while( (--pGL >= mpGlyphItems) && (nXPos < pGL->maLinearPos.X()) );
- // move misplaced item to proper slot
- pGL[1] = aGI;
- // TODO: fix glyph cluster start flags
- pGL[1].mnFlags &= ~GlyphItem::IS_IN_CLUSTER;
+ if( !pG->IsClusterStart() )
+ continue;
+ for( GlyphItem* pBaseGlyph = pG; ++pBaseGlyph < pGEnd; )
+ {
+ // find the base glyph matching to the misplaced diacritic
+ if( pBaseGlyph->IsClusterStart() )
+ break;
+ if( pBaseGlyph->IsDiacritic() )
+ continue;
+
+ // found the matching base glyph
+ // => this base glyph becomes the new cluster start
+ const GlyphItem aDiacritic = *pG;
+ *pG = *pBaseGlyph;
+ *pBaseGlyph = aDiacritic;
+
+ // update glyph flags of swapped glyphitems
+ pG->mnFlags &= ~GlyphItem::IS_IN_CLUSTER;
+ pBaseGlyph->mnFlags |= GlyphItem::IS_IN_CLUSTER;
+ // prepare for checking next cluster
+ pG = pBaseGlyph;
+ break;
+ }
}
}
@@ -1871,7 +1907,7 @@ void MultiSalLayout::AdjustLayout( ImplLayoutArgs& rArgs )
// the measured width is still in fallback font units
// => convert it to base level font units
if( n > 0 ) // optimization: because (fUnitMul==1.0) for (n==0)
- nRunAdvance = static_cast<long>(nRunAdvance*fUnitMul + 0.5);
+ nRunAdvance = static_cast<long>(nRunAdvance*fUnitMul + 0.5);
}
// calculate new x position (in base level units)
diff --git a/vcl/source/glyphs/gcach_ftyp.cxx b/vcl/source/glyphs/gcach_ftyp.cxx
index f6d93963540b..3fe2f7cd0ec2 100644
--- a/vcl/source/glyphs/gcach_ftyp.cxx
+++ b/vcl/source/glyphs/gcach_ftyp.cxx
@@ -1031,6 +1031,17 @@ void FreetypeServerFont::FetchFontMetric( ImplFontMetricData& rTo, long& rFactor
rTo.mnDescent += nOtherHalfTmpExtLeading;
}
}
+
+ // initialize kashida width
+ // TODO: what if there are different versions of this glyph available
+ rTo.mnMinKashida = rTo.mnAscent / 4; // a reasonable default
+ const int nKashidaGlyphId = GetRawGlyphIndex( 0x0640 );
+ if( nKashidaGlyphId )
+ {
+ GlyphData aGlyphData;
+ InitGlyphData( nKashidaGlyphId, aGlyphData );
+ rTo.mnMinKashida = aGlyphData.GetMetric().GetCharWidth();
+ }
}
// -----------------------------------------------------------------------
diff --git a/vcl/source/glyphs/gcach_layout.cxx b/vcl/source/glyphs/gcach_layout.cxx
index bdd2444013c0..1e2f83f5f128 100755
--- a/vcl/source/glyphs/gcach_layout.cxx
+++ b/vcl/source/glyphs/gcach_layout.cxx
@@ -60,8 +60,6 @@ void ServerFontLayout::DrawText( SalGraphics& rSalGraphics ) const
rSalGraphics.DrawServerFontLayout( *this );
}
-//--------------------------------------------------------------------------
-
// -----------------------------------------------------------------------
bool ServerFontLayout::LayoutText( ImplLayoutArgs& rArgs )
@@ -481,6 +479,9 @@ bool IcuLayoutEngine::operator()( ServerFontLayout& rLayout, ImplLayoutArgs& rAr
// layout bidi/script runs and export them to a ServerFontLayout
// convert results to GlyphItems
int nLastCharPos = -1;
+ int nClusterMinPos = -1;
+ int nClusterMaxPos = -1;
+ bool bClusterStart = true;
int nFilteredRunGlyphCount = 0;
const IcuPosition* pPos = pGlyphPositions;
for( int i = 0; i < nRawRunGlyphCount; ++i, ++pPos )
@@ -518,14 +519,6 @@ bool IcuLayoutEngine::operator()( ServerFontLayout& rLayout, ImplLayoutArgs& rAr
continue;
}
- // if ICU feeds us a character index sequence like [1,0,1] (which
- // is completely valid), smooth out the sequence so that our cluster
- // detection routines work better. The best knowledge where the
- // cluster boundaries are should be provided by the layout engine...
- if( nLastCharPos != -1 )
- if( (nCharPos < nLastCharPos) ^ bRightToLeft )
- nCharPos = nLastCharPos;
-
// apply vertical flags, etc.
if( nCharPos >= 0 )
{
@@ -549,25 +542,68 @@ bool IcuLayoutEngine::operator()( ServerFontLayout& rLayout, ImplLayoutArgs& rAr
const GlyphMetric& rGM = rFont.GetGlyphMetric( nGlyphIndex );
int nGlyphWidth = rGM.GetCharWidth();
- // heuristic to detect group clusters using "smoothed" char positions
+ // heuristic to detect glyph clusters
+ bool bInCluster = true;
+ if( nLastCharPos == -1 )
+ {
+ nClusterMinPos = nClusterMaxPos = nCharPos;
+ bInCluster = false;
+ }
+ else if( !bRightToLeft )
+ {
+ // left-to-right case
+ if( nClusterMinPos > nCharPos )
+ nClusterMinPos = nCharPos; // extend cluster
+ else if( nCharPos <= nClusterMaxPos )
+ /*NOTHING*/; // inside cluster
+ else if( nGlyphWidth <= 0 )
+ nClusterMaxPos = nCharPos; // add diacritic to cluster
+ else {
+ nClusterMinPos = nClusterMaxPos = nCharPos; // new cluster
+ bInCluster = false;
+ }
+ }
+ else
+ {
+ // right-to-left case
+ if( nClusterMaxPos < nCharPos )
+ nClusterMaxPos = nCharPos; // extend cluster
+ else if( nCharPos >= nClusterMinPos )
+ /*NOTHING*/; // inside cluster
+ else if( nGlyphWidth <= 0 )
+ {
+ nClusterMinPos = nCharPos; // ICU often has [diacritic* baseglyph*]
+ if( bClusterStart ) {
+ nClusterMaxPos = nCharPos;
+ bInCluster = false;
+ }
+ }
+ else
+ {
+ nClusterMinPos = nClusterMaxPos = nCharPos; // new cluster
+ bInCluster = !bClusterStart;
+ }
+ }
+
long nGlyphFlags = 0;
- if( nLastCharPos != -1 )
- if( (nCharPos == nLastCharPos) || (nGlyphWidth <= 0) )
- nGlyphFlags = GlyphItem::IS_IN_CLUSTER;
+ if( bInCluster )
+ nGlyphFlags |= GlyphItem::IS_IN_CLUSTER;
if( bRightToLeft )
nGlyphFlags |= GlyphItem::IS_RTL_GLYPH;
// add resulting glyph item to layout
- GlyphItem aGI( nCharPos, nGlyphIndex, aNewPos, nGlyphFlags, nGlyphWidth );
+ const GlyphItem aGI( nCharPos, nGlyphIndex, aNewPos, nGlyphFlags, nGlyphWidth );
rLayout.AppendGlyph( aGI );
++nFilteredRunGlyphCount;
nLastCharPos = nCharPos;
+ bClusterStart = !aGI.IsDiacritic(); // TODO: only needed in RTL-codepath
}
aNewPos = Point( (int)(pPos->fX+0.5), (int)(pPos->fY+0.5) );
nGlyphCount += nFilteredRunGlyphCount;
}
// sort glyphs in visual order
+ // and then in logical order (e.g. diacritics after cluster start)
rLayout.SortGlyphItems();
// determine need for kashida justification
@@ -594,3 +630,4 @@ ServerFontLayoutEngine* FreetypeServerFont::GetLayoutEngine()
}
// =======================================================================
+
diff --git a/vcl/win/source/gdi/winlayout.cxx b/vcl/win/source/gdi/winlayout.cxx
index 24f45d6cba1e..046dcf17171c 100755
--- a/vcl/win/source/gdi/winlayout.cxx
+++ b/vcl/win/source/gdi/winlayout.cxx
@@ -1060,6 +1060,7 @@ public:
public:
bool IsEmpty() const { return (mnEndGlyphPos <= 0); }
+ bool IsRTL() const { return mpScriptItem->a.fRTL; }
bool HasKashidas() const { return mbHasKashidas; }
};
@@ -1520,7 +1521,7 @@ bool UniscribeLayout::LayoutText( ImplLayoutArgs& rArgs )
// fallback request is limited to the characters in the original request
// => this is handled in ImplLayoutArgs::PrepareFallback()
rArgs.NeedFallback( rVisualItem.mnMinCharPos, rVisualItem.mnEndCharPos,
- rVisualItem.mpScriptItem->a.fRTL );
+ rVisualItem.IsRTL() );
// don't bother to do a default layout in a fallback level
if( 0 != (rArgs.mnFlags & SAL_LAYOUT_FOR_FALLBACK) )
@@ -1574,7 +1575,7 @@ bool UniscribeLayout::LayoutText( ImplLayoutArgs& rArgs )
mpOutGlyphs[ i + rVisualItem.mnMinGlyphPos ] = DROPPED_OUTGLYPH;
// request fallback for the whole cell that resulted in a NotDef glyph
// TODO: optimize algorithm
- bool bRTL = rVisualItem.mpScriptItem->a.fRTL;
+ const bool bRTL = rVisualItem.IsRTL();
if( !bRTL )
{
// request fallback for the left-to-right cell
@@ -1762,23 +1763,22 @@ bool UniscribeLayout::GetItemSubrange( const VisualItem& rVisualItem,
nMaxGlyphPos = n;
}
- // account for multiple glyphs at rightmost character
- // test only needed when rightmost glyph isn't referenced
- if( rEndGlyphPos > nMaxGlyphPos + 1 )
+ // extend the glyph range to account for all glyphs in referenced clusters
+ if( !rVisualItem.IsRTL() ) // LTR-item
{
- // find the end of the glyph cluster
- // TODO: optimize for case when LTR/RTL correspond to monotonous glyph indexes
- for( int i = rVisualItem.mnMinCharPos; i < rVisualItem.mnEndCharPos; ++i )
- {
- int n = mpLogClusters[ i ] + rVisualItem.mnMinGlyphPos;
- if( (rEndGlyphPos > n) && (n > nMaxGlyphPos) )
- {
- rEndGlyphPos = n;
- if( n-1 <= nMaxGlyphPos )
- break;
- }
- }
+ // extend to rightmost glyph of rightmost referenced cluster
+ for( i = nMaxGlyphPos; ++i < rVisualItem.mnEndGlyphPos; nMaxGlyphPos = i )
+ if( mpVisualAttrs[i].fClusterStart )
+ break;
}
+ else // RTL-item
+ {
+ // extend to leftmost glyph of leftmost referenced cluster
+ for( i = rMinGlyphPos; --i >= rVisualItem.mnMinGlyphPos; rMinGlyphPos = i )
+ if( mpVisualAttrs[i].fClusterStart )
+ break;
+ }
+ rEndGlyphPos = nMaxGlyphPos + 1;
return true;
}
@@ -1864,7 +1864,7 @@ int UniscribeLayout::GetNextGlyphs( int nLen, sal_GlyphId* pGlyphs, Point& rPos,
// adjust the nXOffset relative to glyph cluster start
int c = mnMinCharPos;
- if( !pVI->mpScriptItem->a.fRTL )
+ if( !pVI->IsRTL() ) // LTR-case
{
// LTR case: subtract the remainder of the cell from xoffset
int nTmpIndex = mpLogClusters[c];
@@ -1872,7 +1872,7 @@ int UniscribeLayout::GetNextGlyphs( int nLen, sal_GlyphId* pGlyphs, Point& rPos,
&& (nTmpIndex == mpLogClusters[c]) )
nXOffset -= mpCharWidths[c];
}
- else
+ else // RTL-case
{
// RTL case: add the remainder of the cell from xoffset
int nTmpIndex = mpLogClusters[ pVI->mnEndCharPos - 1 ];
@@ -1994,7 +1994,7 @@ int UniscribeLayout::GetNextGlyphs( int nLen, sal_GlyphId* pGlyphs, Point& rPos,
// RTL-justified glyph positioning is not easy
// simplify the code by just returning only one glyph at a time
- if( mpJustifications && pVI->mpScriptItem->a.fRTL )
+ if( mpJustifications && pVI->IsRTL() )
break;
// stop when the x-position of the next glyph is unexpected
@@ -2191,7 +2191,7 @@ void UniscribeLayout::DrawText( SalGraphics& ) const
if( nBaseGlyphPos < 0 )
{
// adjust draw position relative to cluster start
- if( rVisualItem.mpScriptItem->a.fRTL )
+ if( rVisualItem.IsRTL() )
nBaseGlyphPos = nEndGlyphPos - 1;
else
nBaseGlyphPos = nMinGlyphPos;
@@ -2207,7 +2207,7 @@ void UniscribeLayout::DrawText( SalGraphics& ) const
&& (nBaseGlyphPos == mpLogClusters[i]) )
nBaseClusterOffset += mpCharWidths[i];
- if( !rVisualItem.mpScriptItem->a.fRTL )
+ if( !rVisualItem.IsRTL() )
nBaseClusterOffset = -nBaseClusterOffset;
}
@@ -2341,7 +2341,7 @@ void UniscribeLayout::GetCaretPositions( int nMaxIdx, long* pCaretXArray ) const
{
int j = mpLogClusters[ i ] + rVisualItem.mnMinGlyphPos;
int nCurrIdx = i * 2;
- if( !rVisualItem.mpScriptItem->a.fRTL )
+ if( !rVisualItem.IsRTL() )
{
// normal positions for LTR case
pCaretXArray[ nCurrIdx ] = pGlyphPos[ j ];
@@ -2433,7 +2433,7 @@ void UniscribeLayout::ApplyDXArray( const ImplLayoutArgs& rArgs )
// if needed prepare special handling for arabic justification
rVisualItem.mbHasKashidas = false;
- if( rVisualItem.mpScriptItem->a.fRTL )
+ if( rVisualItem.IsRTL() )
{
for( i = rVisualItem.mnMinGlyphPos; i < rVisualItem.mnEndGlyphPos; ++i )
if ( (1U << mpVisualAttrs[i].uJustification) & 0xFF89 ) // any Arabic justification ?
@@ -2499,7 +2499,7 @@ void UniscribeLayout::ApplyDXArray( const ImplLayoutArgs& rArgs )
// workaround needed for older USP versions:
// right align the justification-adjusted glyphs in their cells for RTL-items
// unless the right alignment is done by inserting kashidas
- if( bManualCellAlign && rVisualItem.mpScriptItem->a.fRTL && !rVisualItem.HasKashidas() )
+ if( bManualCellAlign && rVisualItem.IsRTL() && !rVisualItem.HasKashidas() )
{
for( i = nMinGlyphPos; i < nEndGlyphPos; ++i )
{