View | Details | Raw Unified | Return to issue 60594
Collapse All | Expand All

(-)inc/vcl/outdev.hxx (+10 lines)
Lines 1034-1039 Link Here
1034
    long                GetMinKashida() const;
1034
    long                GetMinKashida() const;
1035
    long                GetMinKashida( const Font& rFont ) const;
1035
    long                GetMinKashida( const Font& rFont ) const;
1036
1036
1037
	// i60594
1038
	// validate kashida positions against the current font 
1039
	// returns count of invalid kashida positions
1040
	xub_StrLen			ValidateKashidas ( const String& rTxt, 
1041
											xub_StrLen nIdx, xub_StrLen nLen,
1042
											xub_StrLen nKashCount, // number of suggested kashida positions (in)
1043
											const xub_StrLen* pKashidaPos, // suggested kashida positions (in)
1044
											xub_StrLen* pKashidaPosDropped // invalid kashida positions (out)
1045
											) const;
1046
1037
    USHORT              GetBitCount() const;
1047
    USHORT              GetBitCount() const;
1038
1048
1039
    BOOL                GetTextIsRTL( const String&, xub_StrLen nIndex,
1049
    BOOL                GetTextIsRTL( const String&, xub_StrLen nIndex,
(-)inc/vcl/sallayout.hxx (+1 lines)
Lines 206-211 Link Here
206
    virtual long    FillDXArray( sal_Int32* pDXArray ) const = 0;
206
    virtual long    FillDXArray( sal_Int32* pDXArray ) const = 0;
207
    virtual long    GetTextWidth() const { return FillDXArray( NULL ); }
207
    virtual long    GetTextWidth() const { return FillDXArray( NULL ); }
208
    virtual void    GetCaretPositions( int nArraySize, sal_Int32* pCaretXArray ) const = 0;
208
    virtual void    GetCaretPositions( int nArraySize, sal_Int32* pCaretXArray ) const = 0;
209
	virtual bool	IsKashidaPosValid ( int /* nCharPos */ ) const { return true; } // i60594
209
210
210
    // methods using glyph indexing
211
    // methods using glyph indexing
211
    virtual int     GetNextGlyphs( int nLen, sal_GlyphId* pGlyphIdAry, Point& rPos, int&,
212
    virtual int     GetNextGlyphs( int nLen, sal_GlyphId* pGlyphIdAry, Point& rPos, int&,
(-)source/gdi/outdev3.cxx (+27 lines)
Lines 7438-7443 Link Here
7438
}
7438
}
7439
7439
7440
// -----------------------------------------------------------------------
7440
// -----------------------------------------------------------------------
7441
xub_StrLen OutputDevice::ValidateKashidas ( const String& rTxt, 
7442
											xub_StrLen nIdx, xub_StrLen nLen,
7443
											xub_StrLen nKashCount, 
7444
											const xub_StrLen* pKashidaPos,
7445
											xub_StrLen* pKashidaPosDropped ) const
7446
{
7447
   // do layout
7448
    SalLayout* pSalLayout = ImplLayout( rTxt, nIdx, nLen );
7449
    if( !pSalLayout )
7450
        return 0;
7451
	xub_StrLen nDropped = 0;
7452
	for( int i = 0; i < nKashCount; ++i )
7453
	{
7454
		if( !pSalLayout->IsKashidaPosValid( pKashidaPos[ i ] ))
7455
		{
7456
			pKashidaPosDropped[ nDropped ] = pKashidaPos [ i ];
7457
			++nDropped;
7458
		}
7459
	}
7460
	pSalLayout->Release();
7461
	return nDropped;
7462
}
7463
7464
7465
7466
// -----------------------------------------------------------------------
7467
7441
7468
7442
// TODO: best is to get rid of this method completely
7469
// TODO: best is to get rid of this method completely
7443
ULONG OutputDevice::GetKerningPairCount() const
7470
ULONG OutputDevice::GetKerningPairCount() const
(-)win/source/gdi/winlayout.cxx (-60 / +193 lines)
Lines 1034-1039 Link Here
1034
    virtual long    FillDXArray( long* pDXArray ) const;
1034
    virtual long    FillDXArray( long* pDXArray ) const;
1035
    virtual int     GetTextBreak( long nMaxWidth, long nCharExtra, int nFactor ) const;
1035
    virtual int     GetTextBreak( long nMaxWidth, long nCharExtra, int nFactor ) const;
1036
    virtual void    GetCaretPositions( int nArraySize, long* pCaretXArray ) const;
1036
    virtual void    GetCaretPositions( int nArraySize, long* pCaretXArray ) const;
1037
	virtual bool	IsKashidaPosValid ( int nCharPos ) const;
1037
1038
1038
    // for glyph+font+script fallback
1039
    // for glyph+font+script fallback
1039
    virtual void    MoveGlyph( int nStart, long nNewXPos );
1040
    virtual void    MoveGlyph( int nStart, long nNewXPos );
Lines 1075-1080 Link Here
1075
1076
1076
    // font specific info
1077
    // font specific info
1077
    int             mnMinKashidaWidth;  // minimal Kashida width allowed by font
1078
    int             mnMinKashidaWidth;  // minimal Kashida width allowed by font
1079
	
1080
	// kashida stuff
1081
	void KashidaItemFix( int nMinGlyphPos, int nEndGlyphPos );
1082
1083
	bool KashidaWordFix ( int nMinGlyphPos, int nEndGlyphPos, int* pnCurrentPos );
1084
1085
1078
};
1086
};
1079
1087
1080
// -----------------------------------------------------------------------
1088
// -----------------------------------------------------------------------
Lines 2368-2433 Link Here
2368
        {
2376
        {
2369
            for( i = nMinGlyphPos; i < nEndGlyphPos; ++i )
2377
            for( i = nMinGlyphPos; i < nEndGlyphPos; ++i )
2370
                nXOffset += mpJustifications[ i ];
2378
                nXOffset += mpJustifications[ i ];
2371
2379
           
2372
            // TODO: for kashida justification
2380
            if ( bHasKashida )
2373
            // check the widths which are added to mpJustification
2381
				KashidaItemFix ( nMinGlyphPos, nEndGlyphPos );
2374
            // if added width is smaller than iKashidaWidth returned by
2375
            // ScriptGetFontProperties, do something (either enlarge to
2376
            // iKashidaWidth, or reduce to original width).
2377
            // Need to think of a way to compensate the change in overall
2378
            // width.
2379
2380
            if ( bHasKashida && mnMinKashidaWidth )
2381
            {
2382
                int nSpaceAdded;
2383
                for ( i = nMinGlyphPos; i < nEndGlyphPos; ++i )
2384
                {
2385
                    nSpaceAdded =  mpJustifications[ i ] - mpGlyphAdvances[ i ];
2386
                    if ( nSpaceAdded && ((1U << mpVisualAttrs[i].uJustification) & 0xFF89 ))
2387
                    {
2388
                        if ( i > nMinGlyphPos && ! mpGlyphAdvances [ i - 1 ] && nSpaceAdded >= mnMinKashidaWidth)
2389
                        {
2390
                            // vowel, we do it like ScriptJustify does
2391
                            // this works great !!!
2392
                            mpJustifications [ i ] = mpGlyphAdvances [ i ];
2393
                            mpJustifications [ i - 1 ] += nSpaceAdded;
2394
                        }
2395
2396
                        if ( nSpaceAdded < mnMinKashidaWidth )
2397
                        {
2398
                            mpJustifications[ i ] = mpGlyphAdvances[ i ]; // overriding;
2399
                            /*for ( int j = i; j >= nMinGlyphPos; --j )
2400
                            {
2401
                                if ( mpVisualAttrs[ j ].uJustification == SCRIPT_JUSTIFY_ARABIC_BLANK )
2402
                                {
2403
                                    mpJustifications [ j ] += nSpaceAdded; // padding a blank instead
2404
                                    nSpaceAdded = 0;
2405
                                    break;
2406
                                }
2407
                            }*/
2408
2409
                            // no blank found
2410
                            // try the other side
2411
                            for ( int j = i; j < nEndGlyphPos; ++j )
2412
                            {
2413
                                if ( mpVisualAttrs[ j ].uJustification == SCRIPT_JUSTIFY_ARABIC_BLANK )
2414
                                {
2415
                                    mpJustifications [ j ] += nSpaceAdded; // padding a blank instead
2416
                                    nSpaceAdded = 0;
2417
                                    break;
2418
                                }
2419
                            }
2420
2421
                            if ( nSpaceAdded )
2422
                            {
2423
                                // no blank found
2424
                                // right align
2425
                                // rVisualItem.mnXOffset += nSpaceAdded;
2426
                            }
2427
                        }
2428
                    }
2429
                }
2430
            }
2431
        }
2382
        }
2432
2383
2433
        // right align the justification-adjusted glyphs in their cells for RTL-items
2384
        // right align the justification-adjusted glyphs in their cells for RTL-items
Lines 2447-2452 Link Here
2447
}
2398
}
2448
2399
2449
// -----------------------------------------------------------------------
2400
// -----------------------------------------------------------------------
2401
void UniscribeLayout::KashidaItemFix( int nMinGlyphPos, int nEndGlyphPos )
2402
{
2403
	// check for vowels
2404
	for ( int i = nMinGlyphPos; i < nEndGlyphPos; ++i )
2405
	{
2406
		if ( i > nMinGlyphPos && !mpGlyphAdvances[ i-1 ] && (1U << mpVisualAttrs[i].uJustification) & 0xFF89 )
2407
		{
2408
			// vowel, we do it like ScriptJustify does
2409
			// the vowel gets the extra width
2410
			long nSpaceAdded =  mpJustifications[ i ] - mpGlyphAdvances[ i ];
2411
			mpJustifications [ i ] = mpGlyphAdvances [ i ];
2412
			mpJustifications [ i - 1 ] += nSpaceAdded;
2413
		}
2414
	}
2415
2416
	int i = nMinGlyphPos;
2417
	while ( i < nEndGlyphPos )
2418
	{
2419
		KashidaWordFix ( nMinGlyphPos, nEndGlyphPos, &i );
2420
	}
2421
}
2422
2423
bool UniscribeLayout::KashidaWordFix ( int nMinGlyphPos, int nEndGlyphPos, int* pnCurrentPos )
2424
{
2425
	// doing pixel work within a word.
2426
	// sometimes we have extra pixels and sometimes we miss some pixels to get to mnMinKashidaWidth
2427
2428
	int nMinPos = *pnCurrentPos;
2429
	int nMaxPos = *pnCurrentPos;
2430
	for ( int i = nMaxPos; i < nEndGlyphPos; ++i )
2431
	{
2432
		if ( mpVisualAttrs [ i ].uJustification >= SCRIPT_JUSTIFY_ARABIC_BLANK && 
2433
			mpVisualAttrs [ i ].uJustification < SCRIPT_JUSTIFY_ARABIC_NORMAL )
2434
			break;
2435
		nMaxPos = i;
2436
	}
2437
	*pnCurrentPos = nMaxPos + 1;
2438
	if ( nMinPos == nMaxPos )
2439
		return false;
2440
2441
	long nMaxAdded = 0;
2442
	int nKashPos = -1;
2443
	for( int i = nMaxPos; i >= nMinPos; --i )
2444
	{
2445
		long nSpaceAdded = mpJustifications[ i ] - mpGlyphAdvances[ i ];
2446
		if( nSpaceAdded > nMaxAdded )
2447
		{
2448
			nKashPos = i;
2449
			nMaxAdded = nSpaceAdded;
2450
		}
2451
	}
2452
	if ( nMaxAdded <= 0 )
2453
		return false;
2454
	if( nMaxAdded < mnMinKashidaWidth / 2 ) 
2455
		// too small for a kashida
2456
		nKashPos = -1;
2457
2458
	//long nTooMuch = 0;	
2459
	if ( nKashPos != -1 )
2460
		for( int i = nMinPos; i <= nMaxPos; ++i )
2461
		{
2462
			if( i==nKashPos ) continue;
2463
			// everything else should not have extra spacing
2464
			long nSpaceAdded = mpJustifications[ i ] - mpGlyphAdvances[ i ];
2465
			if ( nSpaceAdded > 0 ) 
2466
			{
2467
				mpJustifications[ i ] -= nSpaceAdded;
2468
				mpJustifications[ nKashPos ] += nSpaceAdded;
2469
			}
2470
		}
2471
2472
/*	if( nTooMuch > 0 )
2473
	{
2474
		if ( nMinPos > nMinGlyphPos && nMaxPos < nEndGlyphPos - 1)
2475
		{
2476
			long nHalfSpace = nTooMuch / 2;
2477
			mpJustifications [ nMinPos - 1 ] += nHalfSpace;
2478
			mpJustifications [ nMaxPos + 1 ] += nTooMuch - nHalfSpace;
2479
		}
2480
		else if ( nMinPos > nMinGlyphPos )
2481
		{
2482
			mpJustifications [ nMinPos - 1 ] += nTooMuch;
2483
		}
2484
		else if ( nMaxPos < nEndGlyphPos - 1 )
2485
		{
2486
			mpJustifications [ nMaxPos + 1 ] += nTooMuch;
2487
		}
2488
		else return false;
2489
	}*/
2490
	
2491
	// check if we fulfill minimal kashida width
2492
	if( nKashPos != -1 )
2493
	{
2494
		long nSpaceAdded = mpJustifications[ nKashPos ] - mpGlyphAdvances[ nKashPos ];
2495
		if( nSpaceAdded < mnMinKashidaWidth )
2496
		{
2497
			// ugly: steal some pixels
2498
			long nSteal = 1;
2499
			if ( nMaxPos - nMinPos > 0 && ((mnMinKashidaWidth - nSpaceAdded) > ( nMaxPos - nMinPos )))
2500
				nSteal = (mnMinKashidaWidth - nSpaceAdded) / ( nMaxPos - nMinPos ); 
2501
			for ( int i = nMinPos; i <= nMaxPos; ++i )
2502
			{
2503
				if ( i == nKashPos ) continue;
2504
				nSteal = Min ( mnMinKashidaWidth - nSpaceAdded, nSteal );
2505
				if ( nSteal > 0 )
2506
				{
2507
					mpJustifications [ i ] -= nSteal;
2508
					mpJustifications [ nKashPos ] += nSteal;
2509
					nSpaceAdded += nSteal;
2510
				}
2511
				if ( nSpaceAdded >= mnMinKashidaWidth )
2512
					return true;
2513
			}
2514
		}
2515
		long nSpaceMissing = mnMinKashidaWidth - nSpaceAdded;
2516
	
2517
		// blank padding
2518
		if ( nSpaceMissing > 0 )
2519
		{
2520
			if ( nMinPos > nMinGlyphPos && nMaxPos < nEndGlyphPos - 1)
2521
			{
2522
				mpJustifications [ nKashPos ] += nSpaceMissing;
2523
				long nHalfSpace = nSpaceMissing / 2;
2524
				mpJustifications [ nMinPos - 1 ] -= nHalfSpace;
2525
				mpJustifications [ nMaxPos + 1 ] -= nSpaceMissing - nHalfSpace;
2526
			}
2527
			else if ( nMinPos > nMinGlyphPos )
2528
			{
2529
				mpJustifications [ nMinPos - 1 ] -= nSpaceMissing;
2530
				mpJustifications [ nKashPos ] += nSpaceMissing;
2531
			}
2532
			else if ( nMaxPos < nEndGlyphPos - 1 )
2533
			{
2534
				mpJustifications [ nKashPos ] += nSpaceMissing;
2535
				mpJustifications [ nMaxPos + 1 ] -= nSpaceMissing;
2536
			}
2537
			else return false;
2538
		}
2539
	}
2540
	return true;
2541
}
2542
2543
// -----------------------------------------------------------------------
2450
2544
2451
void UniscribeLayout::Justify( long nNewWidth )
2545
void UniscribeLayout::Justify( long nNewWidth )
2452
{
2546
{
Lines 2496-2501 Link Here
2496
    }
2590
    }
2497
}
2591
}
2498
2592
2593
bool UniscribeLayout::IsKashidaPosValid ( int nCharPos ) const
2594
{
2595
	// we have to find the visual item first since the mpLogClusters[]
2596
    // needed to find the cluster start is relative to to the visual item
2597
    int nMinGlyphIndex = -1;
2598
    for( int nItem = 0; nItem < mnItemCount; ++nItem )
2599
    {
2600
        const VisualItem& rVisualItem = mpVisualItems[ nItem ];
2601
        if( (nCharPos >= rVisualItem.mnMinCharPos)
2602
        &&  (nCharPos < rVisualItem.mnEndCharPos) )
2603
		{
2604
			if ( nCharPos < rVisualItem.mnEndCharPos - 1 )
2605
			// our nCharPos mustn't be the last char in the
2606
			// item
2607
				nMinGlyphIndex = rVisualItem.mnMinGlyphPos;
2608
            break;
2609
		}
2610
    }
2611
	// Invalid char pos or leftmost glyph in visual item
2612
    if ( nMinGlyphIndex == -1 || !mpLogClusters[ nCharPos ] ) 
2613
		return false;
2614
2615
//	This test didn't give the expected results
2616
/*	if( mpLogClusters[ nCharPos+1 ] == mpLogClusters[ nCharPos ]) 
2617
	// two chars, one glyph
2618
		return false;*/
2619
2620
	
2621
	const int nGlyphPos = mpLogClusters[ nCharPos ] + nMinGlyphIndex;
2622
	// justification is only allowed if the glyph to the left has not SCRIPT_JUSTIFY_NONE
2623
	// and not SCRIPT_JUSTIFY_ARABIC_BLANK
2624
	// special case: glyph to the left is vowel (no advance width)
2625
	if ( mpVisualAttrs[ nGlyphPos-1 ].uJustification == SCRIPT_JUSTIFY_ARABIC_BLANK 
2626
		|| ( mpVisualAttrs[ nGlyphPos-1 ].uJustification == SCRIPT_JUSTIFY_NONE 
2627
			&& mpGlyphAdvances [ nGlyphPos-1 ] ))	
2628
		return false;
2629
	return true;
2630
}
2631
2499
#endif // USE_UNISCRIBE
2632
#endif // USE_UNISCRIBE
2500
2633
2501
// =======================================================================
2634
// =======================================================================

Return to issue 60594