2002-02-18 08:08:18 +00:00
|
|
|
/*************************************************************************
|
|
|
|
*
|
|
|
|
* $RCSfile: sallayout.cxx,v $
|
|
|
|
*
|
2004-07-05 08:19:01 +00:00
|
|
|
* $Revision: 1.59 $
|
2002-02-18 08:08:18 +00:00
|
|
|
*
|
2004-07-05 08:19:01 +00:00
|
|
|
* last change: $Author: obo $ $Date: 2004-07-05 09:19:01 $
|
2002-02-18 08:08:18 +00:00
|
|
|
*
|
|
|
|
* The Contents of this file are made available subject to the terms of
|
|
|
|
* either of the following licenses
|
|
|
|
*
|
|
|
|
* - GNU Lesser General Public License Version 2.1
|
|
|
|
* - Sun Industry Standards Source License Version 1.1
|
|
|
|
*
|
|
|
|
* Sun Microsystems Inc., October, 2000
|
|
|
|
*
|
|
|
|
* GNU Lesser General Public License Version 2.1
|
|
|
|
* =============================================
|
|
|
|
* Copyright 2000 by Sun Microsystems, Inc.
|
|
|
|
* 901 San Antonio Road, Palo Alto, CA 94303, USA
|
|
|
|
*
|
|
|
|
* This library is free software; you can redistribute it and/or
|
|
|
|
* modify it under the terms of the GNU Lesser General Public
|
|
|
|
* License version 2.1, as published by the Free Software Foundation.
|
|
|
|
*
|
|
|
|
* This library is distributed in the hope that it will be useful,
|
|
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
|
|
* Lesser General Public License for more details.
|
|
|
|
*
|
|
|
|
* You should have received a copy of the GNU Lesser General Public
|
|
|
|
* License along with this library; if not, write to the Free Software
|
|
|
|
* Foundation, Inc., 59 Temple Place, Suite 330, Boston,
|
|
|
|
* MA 02111-1307 USA
|
|
|
|
*
|
|
|
|
*
|
|
|
|
* Sun Industry Standards Source License Version 1.1
|
|
|
|
* =================================================
|
|
|
|
* The contents of this file are subject to the Sun Industry Standards
|
|
|
|
* Source License Version 1.1 (the "License"); You may not use this file
|
|
|
|
* except in compliance with the License. You may obtain a copy of the
|
|
|
|
* License at http://www.openoffice.org/license.html.
|
|
|
|
*
|
|
|
|
* Software provided under this License is provided on an "AS IS" basis,
|
|
|
|
* WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING,
|
|
|
|
* WITHOUT LIMITATION, WARRANTIES THAT THE SOFTWARE IS FREE OF DEFECTS,
|
|
|
|
* MERCHANTABLE, FIT FOR A PARTICULAR PURPOSE, OR NON-INFRINGING.
|
|
|
|
* See the License for the specific provisions governing your rights and
|
|
|
|
* obligations concerning the Software.
|
|
|
|
*
|
|
|
|
* The Initial Developer of the Original Code is: Sun Microsystems, Inc.
|
|
|
|
*
|
|
|
|
* Copyright: 2000 by Sun Microsystems, Inc.
|
|
|
|
*
|
|
|
|
* All Rights Reserved.
|
|
|
|
*
|
|
|
|
* Contributor(s): _______________________________________
|
|
|
|
*
|
|
|
|
*
|
|
|
|
************************************************************************/
|
|
|
|
|
|
|
|
#include <cstdio>
|
2004-01-06 12:55:38 +00:00
|
|
|
|
|
|
|
#define _USE_MATH_DEFINES
|
2002-02-18 08:08:18 +00:00
|
|
|
#include <math.h>
|
|
|
|
|
2004-01-06 12:55:38 +00:00
|
|
|
#if defined(SOLARIS) || defined(IRIX)
|
|
|
|
#include <alloca.h>
|
|
|
|
#else
|
2004-03-09 11:15:19 +00:00
|
|
|
#ifndef MACOSX
|
2004-01-06 12:55:38 +00:00
|
|
|
#include <malloc.h>
|
2002-02-18 08:08:18 +00:00
|
|
|
#endif
|
2004-03-09 11:15:19 +00:00
|
|
|
#endif
|
2002-02-18 08:08:18 +00:00
|
|
|
|
|
|
|
#ifndef _SV_SVSYS_HXX
|
|
|
|
#include <svsys.h>
|
|
|
|
#endif
|
|
|
|
|
|
|
|
#ifndef _SV_SALGDI_HXX
|
|
|
|
#include <salgdi.hxx>
|
|
|
|
#endif
|
|
|
|
|
|
|
|
#ifndef _SV_SALLAYOUT_HXX
|
|
|
|
#include <sallayout.hxx>
|
|
|
|
#endif // _SV_SALLAYOUT_HXX
|
|
|
|
|
2004-01-06 12:55:38 +00:00
|
|
|
#ifndef _TL_POLY_HXX
|
|
|
|
#include <tools/poly.hxx>
|
|
|
|
#endif
|
2002-08-01 12:29:41 +00:00
|
|
|
|
2003-04-17 14:18:29 +00:00
|
|
|
#include <tools/lang.hxx>
|
|
|
|
|
2002-02-18 08:08:18 +00:00
|
|
|
#include <limits.h>
|
2002-10-29 12:09:45 +00:00
|
|
|
#include <unicode/ubidi.h>
|
2002-11-21 13:01:05 +00:00
|
|
|
#include <unicode/uchar.h>
|
2002-02-18 08:08:18 +00:00
|
|
|
|
2002-11-21 13:01:05 +00:00
|
|
|
// =======================================================================
|
2002-11-18 13:30:50 +00:00
|
|
|
|
|
|
|
int GetVerticalFlags( sal_Unicode nChar )
|
|
|
|
{
|
|
|
|
if( (nChar >= 0x1100 && nChar <= 0x11f9) // Hangul Jamo
|
|
|
|
|| (nChar == 0x2030 || nChar == 0x2031) // per mille sign
|
|
|
|
|| (nChar >= 0x3000 && nChar <= 0xfaff) // unified CJK
|
|
|
|
|| (nChar >= 0xfe20 && nChar <= 0xfe6f) // CJK compatibility
|
|
|
|
|| (nChar >= 0xff00 && nChar <= 0xfffd) ) // other CJK
|
|
|
|
{
|
|
|
|
if( nChar == 0x2010 || nChar == 0x2015
|
|
|
|
|| nChar == 0x2016 || nChar == 0x2026
|
|
|
|
|| (nChar >= 0x3008 && nChar <= 0x301C && nChar != 0x3012)
|
|
|
|
|| nChar == 0xFF3B || nChar == 0xFF3D
|
2002-11-20 12:58:12 +00:00
|
|
|
|| (nChar >= 0xFF5B && nChar <= 0xFF9F) // halfwidth forms
|
2002-11-18 13:30:50 +00:00
|
|
|
|| nChar == 0xFFE3 )
|
2002-11-21 13:01:05 +00:00
|
|
|
return GF_NONE; // not rotated
|
2002-11-18 13:30:50 +00:00
|
|
|
else if( nChar == 0x30fc )
|
2002-11-21 13:01:05 +00:00
|
|
|
return GF_ROTR; // right
|
|
|
|
return GF_ROTL; // left
|
2002-11-18 13:30:50 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
return GF_NONE;
|
|
|
|
}
|
|
|
|
|
2002-11-21 13:01:05 +00:00
|
|
|
// -----------------------------------------------------------------------
|
|
|
|
|
|
|
|
sal_Unicode GetVerticalChar( sal_Unicode nChar )
|
|
|
|
{
|
2003-07-02 13:29:18 +00:00
|
|
|
return 0; // #i14788# input method is responsible vertical char changes
|
2003-03-27 16:59:30 +00:00
|
|
|
|
2003-07-02 13:29:18 +00:00
|
|
|
int nVert = 0;
|
2002-11-21 13:01:05 +00:00
|
|
|
switch( nChar )
|
|
|
|
{
|
2003-04-24 09:27:30 +00:00
|
|
|
// #104627# special treatment for some unicodes
|
|
|
|
case 0x002C: nVert = 0x3001; break;
|
|
|
|
case 0x002E: nVert = 0x3002; break;
|
2004-01-06 12:55:38 +00:00
|
|
|
/*
|
|
|
|
// to few fonts have the compatibility forms, using
|
2003-07-02 13:29:18 +00:00
|
|
|
// them will then cause more trouble than good
|
|
|
|
// TODO: decide on a font specific basis
|
2003-04-24 09:27:30 +00:00
|
|
|
case 0x2018: nVert = 0xFE41; break;
|
|
|
|
case 0x2019: nVert = 0xFE42; break;
|
|
|
|
case 0x201C: nVert = 0xFE43; break;
|
|
|
|
case 0x201D: nVert = 0xFE44; break;
|
2002-11-21 13:01:05 +00:00
|
|
|
// CJK compatibility forms
|
|
|
|
case 0x2025: nVert = 0xFE30; break;
|
|
|
|
case 0x2014: nVert = 0xFE31; break;
|
|
|
|
case 0x2013: nVert = 0xFE32; break;
|
|
|
|
case 0x005F: nVert = 0xFE33; break;
|
|
|
|
case 0x0028: nVert = 0xFE35; break;
|
|
|
|
case 0x0029: nVert = 0xFE36; break;
|
|
|
|
case 0x007B: nVert = 0xFE37; break;
|
|
|
|
case 0x007D: nVert = 0xFE38; break;
|
|
|
|
case 0x3014: nVert = 0xFE39; break;
|
|
|
|
case 0x3015: nVert = 0xFE3A; break;
|
|
|
|
case 0x3010: nVert = 0xFE3B; break;
|
|
|
|
case 0x3011: nVert = 0xFE3C; break;
|
|
|
|
case 0x300A: nVert = 0xFE3D; break;
|
|
|
|
case 0x300B: nVert = 0xFE3E; break;
|
|
|
|
case 0x3008: nVert = 0xFE3F; break;
|
|
|
|
case 0x3009: nVert = 0xFE40; break;
|
|
|
|
case 0x300C: nVert = 0xFE41; break;
|
|
|
|
case 0x300D: nVert = 0xFE42; break;
|
|
|
|
case 0x300E: nVert = 0xFE43; break;
|
|
|
|
case 0x300F: nVert = 0xFE44; break;
|
2004-01-06 12:55:38 +00:00
|
|
|
*/
|
2002-11-21 13:01:05 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
return nVert;
|
|
|
|
}
|
|
|
|
|
|
|
|
// -----------------------------------------------------------------------
|
|
|
|
|
|
|
|
sal_Unicode GetMirroredChar( sal_Unicode nChar )
|
|
|
|
{
|
2002-11-22 16:24:25 +00:00
|
|
|
nChar = (sal_Unicode)u_charMirror( nChar );
|
2002-11-21 13:01:05 +00:00
|
|
|
return nChar;
|
|
|
|
}
|
|
|
|
|
2003-03-27 16:59:30 +00:00
|
|
|
// -----------------------------------------------------------------------
|
|
|
|
|
2003-04-17 14:18:29 +00:00
|
|
|
sal_Unicode GetLocalizedChar( sal_Unicode nChar, LanguageType eLang )
|
2003-03-27 16:59:30 +00:00
|
|
|
{
|
|
|
|
// currently only conversion from ASCII digits is interesting
|
|
|
|
if( (nChar < '0') || ('9' < nChar) )
|
|
|
|
return nChar;
|
|
|
|
|
|
|
|
sal_Unicode nOffset;
|
2003-04-17 14:18:29 +00:00
|
|
|
switch( eLang )
|
2003-03-27 16:59:30 +00:00
|
|
|
{
|
2003-04-17 14:18:29 +00:00
|
|
|
default:
|
|
|
|
nOffset = 0;
|
|
|
|
break;
|
|
|
|
case LANGUAGE_ARABIC:
|
|
|
|
case LANGUAGE_ARABIC_SAUDI_ARABIA:
|
|
|
|
case LANGUAGE_ARABIC_IRAQ:
|
|
|
|
case LANGUAGE_ARABIC_EGYPT:
|
|
|
|
case LANGUAGE_ARABIC_LIBYA:
|
|
|
|
case LANGUAGE_ARABIC_ALGERIA:
|
|
|
|
case LANGUAGE_ARABIC_MOROCCO:
|
|
|
|
case LANGUAGE_ARABIC_TUNISIA:
|
|
|
|
case LANGUAGE_ARABIC_OMAN:
|
|
|
|
case LANGUAGE_ARABIC_YEMEN:
|
|
|
|
case LANGUAGE_ARABIC_SYRIA:
|
|
|
|
case LANGUAGE_ARABIC_JORDAN:
|
|
|
|
case LANGUAGE_ARABIC_LEBANON:
|
|
|
|
case LANGUAGE_ARABIC_KUWAIT:
|
|
|
|
case LANGUAGE_ARABIC_UAE:
|
|
|
|
case LANGUAGE_ARABIC_BAHRAIN:
|
|
|
|
case LANGUAGE_ARABIC_QATAR:
|
|
|
|
case LANGUAGE_URDU:
|
|
|
|
case LANGUAGE_URDU_PAKISTAN:
|
|
|
|
case LANGUAGE_URDU_INDIA:
|
|
|
|
case LANGUAGE_PUNJABI: //???
|
|
|
|
nOffset = 0x0660 - '0'; // arabic/persian/urdu
|
|
|
|
break;
|
|
|
|
case LANGUAGE_BENGALI:
|
|
|
|
nOffset = 0x09E6 - '0'; // bengali
|
|
|
|
break;
|
|
|
|
case LANGUAGE_HINDI:
|
|
|
|
nOffset = 0x0966 - '0'; // devanagari
|
|
|
|
break;
|
|
|
|
// TODO case:
|
|
|
|
nOffset = 0x1369 - '0'; // ethiopic
|
|
|
|
break;
|
|
|
|
case LANGUAGE_GUJARATI:
|
|
|
|
nOffset = 0x0AE6 - '0'; // gujarati
|
|
|
|
break;
|
|
|
|
// TODO case:
|
|
|
|
nOffset = 0x0A66 - '0'; // gurmukhi
|
|
|
|
break;
|
|
|
|
case LANGUAGE_KANNADA:
|
|
|
|
nOffset = 0x0CE6 - '0'; // kannada
|
|
|
|
break;
|
|
|
|
case LANGUAGE_KHMER:
|
|
|
|
nOffset = 0x17E0 - '0'; // khmer
|
|
|
|
break;
|
|
|
|
case LANGUAGE_LAO:
|
|
|
|
nOffset = 0x0ED0 - '0'; // lao
|
|
|
|
break;
|
|
|
|
case LANGUAGE_MALAYALAM:
|
|
|
|
nOffset = 0x0D66 - '0'; // malayalam
|
|
|
|
break;
|
|
|
|
case LANGUAGE_MONGOLIAN:
|
|
|
|
nOffset = 0x1810 - '0'; // mongolian
|
|
|
|
break;
|
|
|
|
// TODO case:
|
|
|
|
nOffset = 0x1040 - '0'; // myanmar
|
|
|
|
break;
|
|
|
|
case LANGUAGE_ORIYA:
|
|
|
|
nOffset = 0x0B66 - '0'; // oriya
|
|
|
|
break;
|
|
|
|
case LANGUAGE_TAMIL:
|
|
|
|
nOffset = 0x0BE7 - '0'; // tamil
|
|
|
|
break;
|
|
|
|
case LANGUAGE_TELUGU:
|
|
|
|
nOffset = 0x0C66 - '0'; // telugu
|
|
|
|
break;
|
|
|
|
case LANGUAGE_THAI:
|
|
|
|
nOffset = 0x0E50 - '0'; // thai
|
|
|
|
break;
|
|
|
|
case LANGUAGE_TIBETAN:
|
|
|
|
nOffset = 0x0F20 - '0'; // tibetan
|
|
|
|
break;
|
|
|
|
// TODO case:
|
|
|
|
nOffset = 0x2776 - '0'; // dingbat circled
|
|
|
|
break;
|
|
|
|
// TODO case:
|
|
|
|
nOffset = 0x2070 - '0'; // superscript
|
|
|
|
break;
|
|
|
|
// TODO case:
|
|
|
|
nOffset = 0x2080 - '0'; // subscript
|
|
|
|
break;
|
2003-03-27 16:59:30 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
nChar += nOffset;
|
|
|
|
return nChar;
|
|
|
|
}
|
|
|
|
|
2003-05-28 11:31:35 +00:00
|
|
|
// -----------------------------------------------------------------------
|
|
|
|
|
|
|
|
inline bool IsControlChar( sal_Unicode cChar )
|
|
|
|
{
|
|
|
|
if( (0x200C <= cChar) && (cChar <= 0x200F) )
|
|
|
|
return true;
|
|
|
|
if( (0x2028 <= cChar) && (cChar <= 0x202E) )
|
|
|
|
return true;
|
2004-02-02 17:22:42 +00:00
|
|
|
if( (0xFEFF == cChar) || (0xFFFE <= cChar) )
|
|
|
|
return true;
|
2003-05-28 11:31:35 +00:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2002-02-18 08:08:18 +00:00
|
|
|
// =======================================================================
|
|
|
|
|
2003-05-28 11:31:35 +00:00
|
|
|
bool ImplLayoutRuns::AddPos( int nCharPos, bool bRTL )
|
2002-10-29 12:09:45 +00:00
|
|
|
{
|
2004-07-05 08:19:01 +00:00
|
|
|
// check if charpos could extend current run
|
2003-05-28 11:31:35 +00:00
|
|
|
int nIndex = maRuns.size();
|
|
|
|
if( nIndex >= 2 )
|
|
|
|
{
|
|
|
|
int nRunPos0 = maRuns[ nIndex-2 ];
|
|
|
|
int nRunPos1 = maRuns[ nIndex-1 ];
|
2004-07-05 08:19:01 +00:00
|
|
|
if( ((nCharPos + bRTL) == nRunPos1)
|
|
|
|
&& ((nRunPos0 > nRunPos1) == bRTL) )
|
2003-05-28 11:31:35 +00:00
|
|
|
{
|
2004-07-05 08:19:01 +00:00
|
|
|
// extend current run by new charpos
|
2003-05-28 11:31:35 +00:00
|
|
|
maRuns[ nIndex-1 ] = nCharPos + !bRTL;
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
// ignore new charpos when it is in current run
|
|
|
|
if( (nRunPos0 <= nCharPos) && (nCharPos < nRunPos1) )
|
|
|
|
return false;
|
|
|
|
if( (nRunPos1 <= nCharPos) && (nCharPos < nRunPos0) )
|
|
|
|
return false;
|
|
|
|
}
|
2002-10-29 12:09:45 +00:00
|
|
|
|
2004-07-05 08:19:01 +00:00
|
|
|
// else append a new run consisting of the new charpos
|
2003-05-28 11:31:35 +00:00
|
|
|
maRuns.push_back( nCharPos + (bRTL ? 1 : 0) );
|
|
|
|
maRuns.push_back( nCharPos + (bRTL ? 0 : 1) );
|
|
|
|
return true;
|
|
|
|
}
|
2002-10-29 12:09:45 +00:00
|
|
|
|
2003-05-28 11:31:35 +00:00
|
|
|
// -----------------------------------------------------------------------
|
2002-10-29 12:09:45 +00:00
|
|
|
|
2003-05-28 11:31:35 +00:00
|
|
|
bool ImplLayoutRuns::AddRun( int nCharPos0, int nCharPos1, bool bRTL )
|
|
|
|
{
|
2004-02-02 17:22:42 +00:00
|
|
|
if( nCharPos0 == nCharPos1 )
|
|
|
|
return false;
|
|
|
|
|
2003-05-28 11:31:35 +00:00
|
|
|
// swap if needed
|
|
|
|
if( bRTL == (nCharPos0 < nCharPos1) )
|
|
|
|
{
|
|
|
|
int nTemp = nCharPos0;
|
|
|
|
nCharPos0 = nCharPos1;
|
|
|
|
nCharPos1 = nTemp;
|
|
|
|
}
|
|
|
|
|
|
|
|
// append new run
|
|
|
|
maRuns.push_back( nCharPos0 );
|
|
|
|
maRuns.push_back( nCharPos1 );
|
2002-10-29 12:09:45 +00:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
// -----------------------------------------------------------------------
|
|
|
|
|
2003-05-28 11:31:35 +00:00
|
|
|
bool ImplLayoutRuns::PosIsInRun( int nCharPos ) const
|
2002-10-29 12:09:45 +00:00
|
|
|
{
|
2003-03-27 16:59:30 +00:00
|
|
|
if( mnRunIndex >= (int)maRuns.size() )
|
2002-10-29 12:09:45 +00:00
|
|
|
return false;
|
|
|
|
|
2003-05-28 11:31:35 +00:00
|
|
|
int nMinCharPos = maRuns[ mnRunIndex+0 ];
|
|
|
|
int nEndCharPos = maRuns[ mnRunIndex+1 ];
|
|
|
|
if( nMinCharPos > nEndCharPos ) // reversed in RTL case
|
2002-10-29 12:09:45 +00:00
|
|
|
{
|
2003-05-28 11:31:35 +00:00
|
|
|
int nTemp = nMinCharPos;
|
|
|
|
nMinCharPos = nEndCharPos;
|
|
|
|
nEndCharPos = nTemp;
|
2002-10-29 12:09:45 +00:00
|
|
|
}
|
|
|
|
|
2003-05-28 11:31:35 +00:00
|
|
|
if( nCharPos < nMinCharPos )
|
|
|
|
return false;
|
|
|
|
if( nCharPos >= nEndCharPos )
|
|
|
|
return false;
|
2002-10-29 12:09:45 +00:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
// -----------------------------------------------------------------------
|
|
|
|
|
2003-05-28 11:31:35 +00:00
|
|
|
bool ImplLayoutRuns::GetNextPos( int* nCharPos, bool* bRightToLeft )
|
2002-10-29 12:09:45 +00:00
|
|
|
{
|
2003-05-28 11:31:35 +00:00
|
|
|
// negative nCharPos => reset to first run
|
|
|
|
if( *nCharPos < 0 )
|
|
|
|
mnRunIndex = 0;
|
|
|
|
|
|
|
|
// return false when all runs completed
|
|
|
|
if( mnRunIndex >= (int)maRuns.size() )
|
|
|
|
return false;
|
|
|
|
|
|
|
|
int nRunPos0 = maRuns[ mnRunIndex+0 ];
|
|
|
|
int nRunPos1 = maRuns[ mnRunIndex+1 ];
|
|
|
|
*bRightToLeft = (nRunPos0 > nRunPos1);
|
|
|
|
|
|
|
|
if( *nCharPos < 0 )
|
2002-10-29 12:09:45 +00:00
|
|
|
{
|
2003-05-28 11:31:35 +00:00
|
|
|
// get first valid nCharPos in run
|
|
|
|
*nCharPos = nRunPos0;
|
2003-03-27 16:59:30 +00:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2003-05-28 11:31:35 +00:00
|
|
|
// advance to next nCharPos for LTR case
|
|
|
|
if( !*bRightToLeft )
|
|
|
|
++(*nCharPos);
|
|
|
|
|
|
|
|
// advance to next run if current run is completed
|
|
|
|
if( *nCharPos == nRunPos1 )
|
|
|
|
{
|
|
|
|
if( (mnRunIndex += 2) >= (int)maRuns.size() )
|
|
|
|
return false;
|
|
|
|
nRunPos0 = maRuns[ mnRunIndex+0 ];
|
|
|
|
nRunPos1 = maRuns[ mnRunIndex+1 ];
|
|
|
|
*bRightToLeft = (nRunPos0 > nRunPos1);
|
|
|
|
*nCharPos = nRunPos0;
|
|
|
|
}
|
2002-10-29 12:09:45 +00:00
|
|
|
}
|
|
|
|
|
2003-05-28 11:31:35 +00:00
|
|
|
// advance to next nCharPos for RTL case
|
|
|
|
if( *bRightToLeft )
|
|
|
|
--(*nCharPos);
|
|
|
|
|
2003-03-27 16:59:30 +00:00
|
|
|
return true;
|
2002-10-29 12:09:45 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// -----------------------------------------------------------------------
|
|
|
|
|
2003-05-28 11:31:35 +00:00
|
|
|
bool ImplLayoutRuns::GetRun( int* nMinRunPos, int* nEndRunPos, bool* bRightToLeft ) const
|
2002-10-29 12:09:45 +00:00
|
|
|
{
|
2003-05-28 11:31:35 +00:00
|
|
|
if( mnRunIndex >= (int)maRuns.size() )
|
|
|
|
return false;
|
|
|
|
|
|
|
|
int nRunPos0 = maRuns[ mnRunIndex+0 ];
|
|
|
|
int nRunPos1 = maRuns[ mnRunIndex+1 ];
|
|
|
|
*bRightToLeft = (nRunPos1 < nRunPos0) ;
|
|
|
|
if( !*bRightToLeft )
|
2002-11-21 13:01:05 +00:00
|
|
|
{
|
2003-05-28 11:31:35 +00:00
|
|
|
*nMinRunPos = nRunPos0;
|
|
|
|
*nEndRunPos = nRunPos1;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
*nMinRunPos = nRunPos1;
|
|
|
|
*nEndRunPos = nRunPos0;
|
2002-11-21 13:01:05 +00:00
|
|
|
}
|
2003-03-27 16:59:30 +00:00
|
|
|
return true;
|
2002-10-29 12:09:45 +00:00
|
|
|
}
|
|
|
|
|
2003-04-24 09:27:30 +00:00
|
|
|
// =======================================================================
|
|
|
|
|
|
|
|
ImplLayoutArgs::ImplLayoutArgs( const xub_Unicode* pStr, int nLength,
|
|
|
|
int nMinCharPos, int nEndCharPos, int nFlags )
|
2004-01-06 12:55:38 +00:00
|
|
|
:
|
|
|
|
mnFlags( nFlags ),
|
2003-04-24 09:27:30 +00:00
|
|
|
mnLength( nLength ),
|
|
|
|
mnMinCharPos( nMinCharPos ),
|
|
|
|
mnEndCharPos( nEndCharPos ),
|
2004-01-06 12:55:38 +00:00
|
|
|
mpStr( pStr ),
|
2003-04-24 09:27:30 +00:00
|
|
|
mpDXArray( NULL ),
|
2004-01-06 12:55:38 +00:00
|
|
|
mnLayoutWidth( 0 ),
|
2003-04-24 09:27:30 +00:00
|
|
|
mnOrientation( 0 )
|
|
|
|
{
|
|
|
|
if( mnFlags & SAL_LAYOUT_BIDI_STRONG )
|
|
|
|
{
|
|
|
|
// do not bother to BiDi analyze strong LTR/RTL
|
2003-05-28 11:31:35 +00:00
|
|
|
// TODO: can we assume these strings do not have unicode control chars?
|
|
|
|
// if not remove the control characters from the runs
|
2003-04-24 09:27:30 +00:00
|
|
|
bool bRTL = ((mnFlags & SAL_LAYOUT_BIDI_RTL) != 0);
|
|
|
|
maRuns.AddRun( mnMinCharPos, mnEndCharPos, bRTL );
|
|
|
|
maRuns.ResetPos();
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
UBiDiLevel nLevel = UBIDI_DEFAULT_LTR;
|
|
|
|
if( mnFlags & SAL_LAYOUT_BIDI_RTL )
|
2003-06-30 13:29:45 +00:00
|
|
|
nLevel = UBIDI_RTL;
|
2003-04-24 09:27:30 +00:00
|
|
|
|
|
|
|
// prepare substring for BiDi analysis
|
|
|
|
UErrorCode rcI18n = U_ZERO_ERROR;
|
|
|
|
UBiDi* pParaBidi = ubidi_openSized( mnLength, 0, &rcI18n );
|
|
|
|
if( !pParaBidi )
|
|
|
|
return;
|
|
|
|
ubidi_setPara( pParaBidi, mpStr, mnLength, nLevel, NULL, &rcI18n );
|
|
|
|
|
|
|
|
UBiDi* pLineBidi = pParaBidi;
|
|
|
|
int nSubLength = mnEndCharPos - mnMinCharPos;
|
|
|
|
if( nSubLength != mnLength )
|
|
|
|
{
|
|
|
|
pLineBidi = ubidi_openSized( nSubLength, 0, &rcI18n );
|
|
|
|
ubidi_setLine( pParaBidi, mnMinCharPos, mnEndCharPos, pLineBidi, &rcI18n );
|
|
|
|
}
|
|
|
|
|
2003-05-28 11:31:35 +00:00
|
|
|
// run BiDi algorithm
|
2003-06-30 13:29:45 +00:00
|
|
|
int nRunCount = ubidi_countRuns( pLineBidi, &rcI18n);
|
2003-04-24 09:27:30 +00:00
|
|
|
//maRuns.resize( 2 * nRunCount );
|
2003-06-30 13:29:45 +00:00
|
|
|
// TODO: see comment about #110273# below, remove when external issue fixed
|
|
|
|
const UBiDiLevel* pParaLevels = ubidi_getLevels( pParaBidi, &rcI18n);
|
2003-04-24 09:27:30 +00:00
|
|
|
for( int i = 0; i < nRunCount; ++i )
|
|
|
|
{
|
|
|
|
int32_t nMinPos, nLength;
|
2004-01-06 12:55:38 +00:00
|
|
|
ubidi_getVisualRun( pLineBidi, i, &nMinPos, &nLength );
|
2003-05-28 11:31:35 +00:00
|
|
|
int nPos0 = nMinPos + mnMinCharPos;
|
|
|
|
int nPos1 = nPos0 + nLength;
|
2004-01-06 12:55:38 +00:00
|
|
|
|
|
|
|
// bool bRTL = (nDir == UBIDI_RTL);
|
|
|
|
// workaround for #110273# (probably ICU problem TODO: analyze there)
|
2003-06-30 13:29:45 +00:00
|
|
|
bool bRTL = ((pParaLevels[ nPos0 ] & 1) != 0);
|
2003-05-28 11:31:35 +00:00
|
|
|
|
2004-02-02 17:22:42 +00:00
|
|
|
// remove control characters from runs by splitting them up
|
2003-05-28 11:31:35 +00:00
|
|
|
if( !bRTL )
|
|
|
|
{
|
|
|
|
for( int j = nPos0; j < nPos1; ++j )
|
|
|
|
if( IsControlChar( mpStr[j] ) )
|
|
|
|
{
|
2004-02-02 17:22:42 +00:00
|
|
|
maRuns.AddRun( nPos0, j, bRTL );
|
2003-05-28 11:31:35 +00:00
|
|
|
nPos0 = j + 1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
for( int j = nPos1; --j >= nPos0; )
|
|
|
|
if( IsControlChar( mpStr[j] ) )
|
|
|
|
{
|
2004-02-02 17:22:42 +00:00
|
|
|
maRuns.AddRun( j+1, nPos1, bRTL );
|
2003-05-28 11:31:35 +00:00
|
|
|
nPos1 = j;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
maRuns.AddRun( nPos0, nPos1, bRTL );
|
2003-04-24 09:27:30 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// cleanup BiDi engine
|
|
|
|
if( pLineBidi != pParaBidi )
|
|
|
|
ubidi_close( pLineBidi );
|
|
|
|
ubidi_close( pParaBidi );
|
|
|
|
|
|
|
|
// prepare calls to GetNextPos/GetNextRun
|
|
|
|
maRuns.ResetPos();
|
|
|
|
}
|
|
|
|
|
2002-10-29 12:09:45 +00:00
|
|
|
// -----------------------------------------------------------------------
|
|
|
|
|
2003-03-27 16:59:30 +00:00
|
|
|
bool ImplLayoutArgs::PrepareFallback()
|
2002-10-29 12:09:45 +00:00
|
|
|
{
|
2003-03-27 16:59:30 +00:00
|
|
|
// TODO: sort out chars that were not requested anyway
|
2002-10-29 12:09:45 +00:00
|
|
|
maRuns = maReruns;
|
2003-04-24 09:27:30 +00:00
|
|
|
maRuns.ResetPos();
|
|
|
|
maReruns.Clear();
|
2003-05-28 11:31:35 +00:00
|
|
|
return !maRuns.IsEmpty();
|
|
|
|
}
|
|
|
|
|
|
|
|
// -----------------------------------------------------------------------
|
|
|
|
|
|
|
|
bool ImplLayoutArgs::GetNextRun( int* nMinRunPos, int* nEndRunPos, bool* bRTL )
|
|
|
|
{
|
|
|
|
bool bValid = maRuns.GetRun( nMinRunPos, nEndRunPos, bRTL );
|
|
|
|
maRuns.NextRun();
|
|
|
|
return bValid;
|
2002-10-29 12:09:45 +00:00
|
|
|
}
|
|
|
|
|
2002-02-18 08:08:18 +00:00
|
|
|
// =======================================================================
|
|
|
|
|
2003-03-27 16:59:30 +00:00
|
|
|
SalLayout::SalLayout()
|
|
|
|
: mnMinCharPos( -1 ),
|
|
|
|
mnEndCharPos( -1 ),
|
|
|
|
mnLayoutFlags( 0 ),
|
2002-02-26 12:18:51 +00:00
|
|
|
mnUnitsPerPixel( 1 ),
|
2004-01-06 12:55:38 +00:00
|
|
|
mnOrientation( 0 ),
|
|
|
|
mnRefCount( 1 ),
|
|
|
|
maDrawOffset( 0, 0 )
|
2002-02-18 08:08:18 +00:00
|
|
|
{}
|
|
|
|
|
|
|
|
// -----------------------------------------------------------------------
|
|
|
|
|
|
|
|
SalLayout::~SalLayout()
|
|
|
|
{}
|
|
|
|
|
|
|
|
// -----------------------------------------------------------------------
|
|
|
|
|
2003-03-27 16:59:30 +00:00
|
|
|
void SalLayout::AdjustLayout( ImplLayoutArgs& rArgs )
|
|
|
|
{
|
|
|
|
mnMinCharPos = rArgs.mnMinCharPos;
|
|
|
|
mnEndCharPos = rArgs.mnEndCharPos;
|
|
|
|
mnLayoutFlags = rArgs.mnFlags;
|
|
|
|
mnOrientation = rArgs.mnOrientation;
|
|
|
|
}
|
|
|
|
|
|
|
|
// -----------------------------------------------------------------------
|
|
|
|
|
2002-02-18 08:08:18 +00:00
|
|
|
void SalLayout::Reference() const
|
|
|
|
{
|
2002-09-04 16:12:08 +00:00
|
|
|
// TODO: protect when multiple threads can access this
|
2002-02-18 08:08:18 +00:00
|
|
|
++mnRefCount;
|
|
|
|
}
|
|
|
|
|
|
|
|
// -----------------------------------------------------------------------
|
|
|
|
|
|
|
|
void SalLayout::Release() const
|
|
|
|
{
|
2002-09-04 16:12:08 +00:00
|
|
|
// TODO: protect when multiple threads can access this
|
2003-05-28 11:31:35 +00:00
|
|
|
if( --mnRefCount > 0 )
|
|
|
|
return;
|
|
|
|
// const_cast because some compilers violate ANSI C++ spec
|
|
|
|
delete const_cast<SalLayout*>(this);
|
2002-02-18 08:08:18 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// -----------------------------------------------------------------------
|
|
|
|
|
|
|
|
Point SalLayout::GetDrawPosition( const Point& rRelative ) const
|
|
|
|
{
|
2002-09-04 16:12:08 +00:00
|
|
|
Point aPos = maDrawBase;
|
|
|
|
Point aOfs = rRelative + maDrawOffset;
|
2002-02-18 08:08:18 +00:00
|
|
|
|
|
|
|
if( mnOrientation == 0 )
|
2002-09-04 16:12:08 +00:00
|
|
|
aPos += aOfs;
|
2002-02-18 08:08:18 +00:00
|
|
|
else
|
|
|
|
{
|
|
|
|
// cache trigonometric results
|
|
|
|
static int nOldOrientation = 0;
|
|
|
|
static double fCos = 1.0, fSin = 0.0;
|
|
|
|
if( nOldOrientation != mnOrientation )
|
|
|
|
{
|
|
|
|
nOldOrientation = mnOrientation;
|
|
|
|
double fRad = mnOrientation * (M_PI / 1800.0);
|
|
|
|
fCos = cos( fRad );
|
2002-05-08 11:30:51 +00:00
|
|
|
fSin = sin( fRad );
|
2002-02-18 08:08:18 +00:00
|
|
|
}
|
|
|
|
|
2002-09-04 16:12:08 +00:00
|
|
|
double fX = aOfs.X();
|
|
|
|
double fY = aOfs.Y();
|
|
|
|
long nX = static_cast<long>( +fCos * fX + fSin * fY );
|
|
|
|
long nY = static_cast<long>( +fCos * fY - fSin * fX );
|
2002-02-18 08:08:18 +00:00
|
|
|
aPos += Point( nX, nY );
|
|
|
|
}
|
|
|
|
|
|
|
|
return aPos;
|
|
|
|
}
|
|
|
|
|
2002-05-28 17:17:41 +00:00
|
|
|
// -----------------------------------------------------------------------
|
|
|
|
|
|
|
|
// returns asian kerning values in quarter of character width units
|
|
|
|
// to enable automatic halfwidth substitution for fullwidth punctuation
|
|
|
|
// return value is negative for l, positive for r, zero for neutral
|
|
|
|
|
|
|
|
// If the range doesn't match in 0x3000 and 0x30FB, please change
|
|
|
|
// also ImplCalcKerning.
|
|
|
|
|
|
|
|
int SalLayout::CalcAsianKerning( sal_Unicode c, bool bLeft, bool bVertical )
|
|
|
|
{
|
|
|
|
// http://www.asahi-net.or.jp/~sd5a-ucd/freetexts/jis/x4051/1995/appendix.html
|
|
|
|
static signed char nTable[0x30] =
|
|
|
|
{
|
|
|
|
0, -2, -2, 0, 0, 0, 0, 0, +2, -2, +2, -2, +2, -2, +2, -2,
|
|
|
|
+2, -2, 0, 0, +2, -2, +2, -2, 0, 0, 0, 0, 0, +2, -2, -2,
|
|
|
|
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -2, -2, +2, +2, -2, -2
|
|
|
|
};
|
|
|
|
|
|
|
|
int nResult = 0;
|
|
|
|
if( c>=0x3000 && c<0x3030 )
|
|
|
|
nResult = nTable[ c - 0x3000 ];
|
|
|
|
else switch( c )
|
|
|
|
{
|
|
|
|
case ':': case ';': case '!':
|
|
|
|
if( !bVertical )
|
2002-09-04 16:12:08 +00:00
|
|
|
nResult = bLeft ? -1 : +1; // 25% left and right
|
2002-05-28 17:17:41 +00:00
|
|
|
break;
|
|
|
|
case 0x30FB:
|
|
|
|
nResult = bLeft ? -1 : +1; // 25% left/right/top/bottom
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
return nResult;
|
|
|
|
}
|
|
|
|
|
2002-08-01 12:29:41 +00:00
|
|
|
// -----------------------------------------------------------------------
|
|
|
|
|
2002-08-15 10:16:53 +00:00
|
|
|
bool SalLayout::GetOutline( SalGraphics& rSalGraphics, PolyPolyVector& rVector ) const
|
2002-08-01 12:29:41 +00:00
|
|
|
{
|
2002-08-21 09:35:15 +00:00
|
|
|
bool bRet = true;
|
2002-09-04 16:12:08 +00:00
|
|
|
|
|
|
|
Point aPos;
|
|
|
|
PolyPolygon aGlyphOutline;
|
2002-08-01 12:29:41 +00:00
|
|
|
for( int nStart = 0;;)
|
|
|
|
{
|
2004-06-17 11:20:59 +00:00
|
|
|
sal_Int32 nLGlyph;
|
2002-09-04 16:12:08 +00:00
|
|
|
if( !GetNextGlyphs( 1, &nLGlyph, aPos, nStart ) )
|
2002-08-01 12:29:41 +00:00
|
|
|
break;
|
|
|
|
|
2002-08-21 09:35:15 +00:00
|
|
|
// get outline of individual glyph, ignoring "empty" glyphs
|
2003-11-18 13:34:51 +00:00
|
|
|
bool bSuccess = rSalGraphics.GetGlyphOutline( nLGlyph, aGlyphOutline );
|
2002-09-04 16:12:08 +00:00
|
|
|
bRet &= bSuccess;
|
|
|
|
// only add non-empty outlines
|
|
|
|
if( bSuccess && (aGlyphOutline.Count() > 0) )
|
2002-08-16 06:14:56 +00:00
|
|
|
{
|
|
|
|
// insert outline at correct position
|
|
|
|
rVector.push_back( aGlyphOutline );
|
2002-09-12 06:34:04 +00:00
|
|
|
rVector.back().Move( aPos.X(), aPos.Y() );
|
2002-08-21 09:35:15 +00:00
|
|
|
}
|
2002-08-16 06:14:56 +00:00
|
|
|
}
|
2002-09-04 16:12:08 +00:00
|
|
|
|
2002-08-21 09:35:15 +00:00
|
|
|
return bRet;
|
2002-08-01 12:29:41 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// -----------------------------------------------------------------------
|
|
|
|
|
2003-03-27 16:59:30 +00:00
|
|
|
bool SalLayout::GetBoundRect( SalGraphics& rSalGraphics, Rectangle& rRect ) const
|
2002-08-01 12:29:41 +00:00
|
|
|
{
|
|
|
|
bool bRet = false;
|
2003-03-27 16:59:30 +00:00
|
|
|
rRect.SetEmpty();
|
2002-08-01 12:29:41 +00:00
|
|
|
|
2002-09-12 06:34:04 +00:00
|
|
|
Point aPos;
|
2002-09-04 16:12:08 +00:00
|
|
|
Rectangle aRectangle;
|
2002-08-01 12:29:41 +00:00
|
|
|
for( int nStart = 0;;)
|
|
|
|
{
|
2004-06-17 11:20:59 +00:00
|
|
|
sal_Int32 nLGlyph;
|
2002-09-04 16:12:08 +00:00
|
|
|
if( !GetNextGlyphs( 1, &nLGlyph, aPos, nStart ) )
|
2002-08-01 12:29:41 +00:00
|
|
|
break;
|
|
|
|
|
|
|
|
// get bounding rectangle of individual glyph
|
2003-11-18 13:34:51 +00:00
|
|
|
if( rSalGraphics.GetGlyphBoundRect( nLGlyph, aRectangle ) )
|
2002-09-04 16:12:08 +00:00
|
|
|
{
|
|
|
|
// merge rectangle
|
|
|
|
aRectangle += aPos;
|
2003-03-27 16:59:30 +00:00
|
|
|
rRect.Union( aRectangle );
|
2002-09-04 16:12:08 +00:00
|
|
|
bRet = true;
|
|
|
|
}
|
2002-08-01 12:29:41 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
return bRet;
|
|
|
|
}
|
|
|
|
|
2002-08-02 16:44:48 +00:00
|
|
|
// -----------------------------------------------------------------------
|
|
|
|
|
|
|
|
bool SalLayout::IsSpacingGlyph( long nGlyph ) const
|
|
|
|
{
|
|
|
|
bool bRet = false;
|
2002-09-04 16:12:08 +00:00
|
|
|
if( nGlyph & GF_ISCHAR )
|
2002-08-02 16:44:48 +00:00
|
|
|
{
|
2002-09-04 16:12:08 +00:00
|
|
|
long nChar = nGlyph & GF_IDXMASK;
|
|
|
|
bRet = (nChar <= 0x0020) // blank
|
|
|
|
//|| (nChar == 0x00A0) // non breaking space
|
|
|
|
|| (nChar >= 0x2000 && nChar <= 0x200F) // whitespace
|
|
|
|
|| (nChar == 0x3000); // ideographic space
|
2002-08-02 16:44:48 +00:00
|
|
|
}
|
2002-09-04 16:12:08 +00:00
|
|
|
else
|
|
|
|
bRet = ((nGlyph & GF_IDXMASK) == 3);
|
2002-08-02 16:44:48 +00:00
|
|
|
return bRet;
|
|
|
|
}
|
|
|
|
|
2002-02-18 08:08:18 +00:00
|
|
|
// =======================================================================
|
|
|
|
|
2003-03-27 16:59:30 +00:00
|
|
|
GenericSalLayout::GenericSalLayout()
|
2004-01-06 12:55:38 +00:00
|
|
|
: mpGlyphItems(0),
|
|
|
|
mnGlyphCount(0),
|
|
|
|
mnGlyphCapacity(0)
|
|
|
|
|
2003-05-28 11:31:35 +00:00
|
|
|
{}
|
2002-02-18 08:08:18 +00:00
|
|
|
|
|
|
|
// -----------------------------------------------------------------------
|
|
|
|
|
|
|
|
GenericSalLayout::~GenericSalLayout()
|
|
|
|
{
|
2002-09-25 16:59:34 +00:00
|
|
|
delete[] mpGlyphItems;
|
2002-02-18 08:08:18 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// -----------------------------------------------------------------------
|
|
|
|
|
2002-10-29 12:09:45 +00:00
|
|
|
void GenericSalLayout::AppendGlyph( const GlyphItem& rGlyphItem )
|
2002-02-18 08:08:18 +00:00
|
|
|
{
|
2002-10-29 12:09:45 +00:00
|
|
|
// TODO: use std::list<GlyphItem>
|
|
|
|
if( mnGlyphCount >= mnGlyphCapacity )
|
|
|
|
{
|
2003-05-28 11:31:35 +00:00
|
|
|
mnGlyphCapacity += 16 + 3 * mnGlyphCount;
|
2002-10-29 12:09:45 +00:00
|
|
|
GlyphItem* pNewGI = new GlyphItem[ mnGlyphCapacity ];
|
|
|
|
if( mpGlyphItems )
|
|
|
|
{
|
|
|
|
for( int i = 0; i < mnGlyphCount; ++i )
|
|
|
|
pNewGI[ i ] = mpGlyphItems[ i ];
|
|
|
|
delete[] mpGlyphItems;
|
|
|
|
}
|
|
|
|
mpGlyphItems = pNewGI;
|
|
|
|
}
|
|
|
|
|
|
|
|
mpGlyphItems[ mnGlyphCount++ ] = rGlyphItem;
|
|
|
|
}
|
|
|
|
|
|
|
|
// -----------------------------------------------------------------------
|
|
|
|
|
2004-06-17 11:20:59 +00:00
|
|
|
bool GenericSalLayout::GetCharWidths( sal_Int32* pCharWidths ) const
|
2002-04-03 16:25:23 +00:00
|
|
|
{
|
2002-02-18 08:08:18 +00:00
|
|
|
// initialize character extents buffer
|
2003-12-01 08:55:04 +00:00
|
|
|
int nCharCount = mnEndCharPos - mnMinCharPos;
|
|
|
|
for( int n = 0; n < nCharCount; ++n )
|
|
|
|
pCharWidths[n] = 0;
|
2002-02-18 08:08:18 +00:00
|
|
|
|
|
|
|
// determine cluster extents
|
|
|
|
const GlyphItem* pG = mpGlyphItems;
|
2003-12-01 08:55:04 +00:00
|
|
|
for( int i = mnGlyphCount; --i >= 0; ++pG )
|
2002-02-18 08:08:18 +00:00
|
|
|
{
|
|
|
|
// use cluster start to get char index
|
2002-06-13 19:26:21 +00:00
|
|
|
if( !pG->IsClusterStart() )
|
|
|
|
continue;
|
2002-02-18 08:08:18 +00:00
|
|
|
|
2002-09-04 16:12:08 +00:00
|
|
|
int n = pG->mnCharPos;
|
|
|
|
if( n >= mnEndCharPos )
|
2002-06-13 19:26:21 +00:00
|
|
|
continue;
|
2002-09-04 16:12:08 +00:00
|
|
|
n -= mnMinCharPos;
|
2002-06-13 19:26:21 +00:00
|
|
|
if( n < 0 )
|
2002-02-18 08:08:18 +00:00
|
|
|
continue;
|
|
|
|
|
2003-12-01 08:55:04 +00:00
|
|
|
// left glyph in cluster defines default extent
|
|
|
|
long nXPosMin = pG->maLinearPos.X();
|
|
|
|
long nXPosMax = nXPosMin + pG->mnNewWidth;
|
2002-02-18 08:08:18 +00:00
|
|
|
|
2003-12-01 08:55:04 +00:00
|
|
|
// 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 )
|
2002-11-19 15:13:35 +00:00
|
|
|
{
|
2003-12-01 08:55:04 +00:00
|
|
|
// advance to next glyph in cluster
|
|
|
|
++pG;
|
|
|
|
|
|
|
|
// get leftmost x-extent of this glyph
|
|
|
|
long nXPos = pG->maLinearPos.X();
|
|
|
|
if( nXPosMin > nXPos )
|
|
|
|
nXPosMin = nXPos;
|
|
|
|
|
|
|
|
// get rightmost x-extent of this glyph
|
|
|
|
nXPos += pG->mnNewWidth;
|
|
|
|
if( nXPosMax < nXPos )
|
|
|
|
nXPosMax = nXPos;
|
2002-11-19 15:13:35 +00:00
|
|
|
}
|
2003-12-01 08:55:04 +00:00
|
|
|
|
2004-07-05 08:19:01 +00:00
|
|
|
// when the current cluster overlaps with the next one assume
|
|
|
|
// rightmost cluster edge is the leftmost edge of next cluster
|
2003-12-01 08:55:04 +00:00
|
|
|
if( (i > 0) && (nXPosMax > pG[1].maLinearPos.X()) )
|
2004-07-05 08:19:01 +00:00
|
|
|
nXPosMax = pG[1].maLinearPos.X();
|
2003-12-01 08:55:04 +00:00
|
|
|
|
|
|
|
// character width is sum of glyph cluster widths
|
|
|
|
pCharWidths[n] += nXPosMax - nXPosMin;
|
2002-02-18 08:08:18 +00:00
|
|
|
}
|
|
|
|
|
2003-12-01 08:55:04 +00:00
|
|
|
// TODO: distribute the cluster width proportionally to the characters
|
2002-06-13 19:26:21 +00:00
|
|
|
// clusters (e.g. ligatures) correspond to more than one char index,
|
|
|
|
// so some character widths are still uninitialized. This is solved
|
|
|
|
// by setting the first charwidth of the cluster to the cluster width
|
2002-02-18 08:08:18 +00:00
|
|
|
|
2002-04-03 16:25:23 +00:00
|
|
|
return true;
|
2002-02-18 08:08:18 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// -----------------------------------------------------------------------
|
|
|
|
|
2004-06-17 11:20:59 +00:00
|
|
|
long GenericSalLayout::FillDXArray( sal_Int32* pCharWidths ) const
|
2002-02-18 08:08:18 +00:00
|
|
|
{
|
2003-03-27 16:59:30 +00:00
|
|
|
if( pCharWidths )
|
|
|
|
if( !GetCharWidths( pCharWidths ) )
|
|
|
|
return 0;
|
2002-02-18 08:08:18 +00:00
|
|
|
|
2003-03-27 16:59:30 +00:00
|
|
|
long nWidth = GetTextWidth();
|
2002-02-18 08:08:18 +00:00
|
|
|
return nWidth;
|
|
|
|
}
|
|
|
|
|
|
|
|
// -----------------------------------------------------------------------
|
|
|
|
|
2002-08-16 06:14:56 +00:00
|
|
|
long GenericSalLayout::GetTextWidth() const
|
|
|
|
{
|
|
|
|
if( mnGlyphCount <= 0 )
|
2002-12-12 12:52:57 +00:00
|
|
|
return 0;
|
2002-08-16 06:14:56 +00:00
|
|
|
|
|
|
|
const GlyphItem* pG = mpGlyphItems;
|
2003-03-27 16:59:30 +00:00
|
|
|
long nMinPos = 0;
|
|
|
|
long nMaxPos = pG->maLinearPos.X() + pG->mnNewWidth;
|
2002-08-16 06:14:56 +00:00
|
|
|
for( int i = 1; i < mnGlyphCount; ++i )
|
|
|
|
{
|
|
|
|
++pG;
|
|
|
|
long nXPos = pG->maLinearPos.X();
|
|
|
|
if( nMinPos > nXPos )
|
|
|
|
nMinPos = nXPos;
|
|
|
|
nXPos += pG->mnNewWidth;
|
2003-03-27 16:59:30 +00:00
|
|
|
if( nMaxPos < nXPos )
|
2002-08-16 06:14:56 +00:00
|
|
|
nMaxPos = nXPos;
|
|
|
|
}
|
|
|
|
|
|
|
|
long nWidth = nMaxPos - nMinPos;
|
|
|
|
return nWidth;
|
|
|
|
}
|
|
|
|
|
|
|
|
// -----------------------------------------------------------------------
|
|
|
|
|
2003-03-27 16:59:30 +00:00
|
|
|
void GenericSalLayout::AdjustLayout( ImplLayoutArgs& rArgs )
|
|
|
|
{
|
|
|
|
SalLayout::AdjustLayout( rArgs );
|
|
|
|
|
|
|
|
if( rArgs.mpDXArray )
|
|
|
|
ApplyDXArray( rArgs );
|
|
|
|
else if( rArgs.mnLayoutWidth )
|
|
|
|
Justify( rArgs.mnLayoutWidth );
|
|
|
|
}
|
|
|
|
|
|
|
|
// -----------------------------------------------------------------------
|
|
|
|
|
|
|
|
void GenericSalLayout::ApplyDXArray( ImplLayoutArgs& rArgs )
|
2002-02-18 08:08:18 +00:00
|
|
|
{
|
2002-09-26 09:28:36 +00:00
|
|
|
if( mnGlyphCount <= 0 )
|
|
|
|
return;
|
|
|
|
|
2002-06-13 19:26:21 +00:00
|
|
|
// determine cluster boundaries and x base offset
|
2003-03-27 16:59:30 +00:00
|
|
|
int nCharCount = rArgs.mnEndCharPos - rArgs.mnMinCharPos;
|
|
|
|
int* pLogCluster = (int*)alloca( nCharCount * sizeof(int) );
|
2002-06-13 19:26:21 +00:00
|
|
|
int i, n;
|
2002-02-18 08:08:18 +00:00
|
|
|
long nBasePointX = -1;
|
2003-03-27 16:59:30 +00:00
|
|
|
if( mnLayoutFlags & SAL_LAYOUT_FOR_FALLBACK )
|
|
|
|
nBasePointX = 0;
|
|
|
|
for( i = 0; i < nCharCount; ++i )
|
|
|
|
pLogCluster[ i ] = -1;
|
2002-06-13 19:26:21 +00:00
|
|
|
GlyphItem* pG = mpGlyphItems;
|
2002-02-18 08:08:18 +00:00
|
|
|
for( i = 0; i < mnGlyphCount; ++i, ++pG )
|
|
|
|
{
|
2003-03-27 16:59:30 +00:00
|
|
|
n = pG->mnCharPos - rArgs.mnMinCharPos;
|
|
|
|
if( (n < 0) || (nCharCount <= n) )
|
|
|
|
continue;
|
|
|
|
pLogCluster[ n ] = i;
|
|
|
|
if( nBasePointX < 0 )
|
|
|
|
nBasePointX = pG->maLinearPos.X();
|
2002-06-13 19:26:21 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// calculate adjusted cluster widths
|
2004-06-17 11:20:59 +00:00
|
|
|
sal_Int32* pNewGlyphWidths = (sal_Int32*)alloca( mnGlyphCount * sizeof(long) );
|
2002-06-13 19:26:21 +00:00
|
|
|
for( i = 0; i < mnGlyphCount; ++i )
|
2003-05-28 11:31:35 +00:00
|
|
|
pNewGlyphWidths[ i ] = 0;
|
2002-02-18 08:08:18 +00:00
|
|
|
|
2003-03-27 16:59:30 +00:00
|
|
|
bool bRTL;
|
2003-05-28 11:31:35 +00:00
|
|
|
for( int nCharPos = -1; rArgs.GetNextPos( &nCharPos, &bRTL ); )
|
2002-06-13 19:26:21 +00:00
|
|
|
{
|
2003-05-28 11:31:35 +00:00
|
|
|
n = nCharPos - rArgs.mnMinCharPos;
|
2003-03-27 16:59:30 +00:00
|
|
|
i = pLogCluster[ n ];
|
|
|
|
if( i >= 0 )
|
|
|
|
{
|
|
|
|
long nDelta = rArgs.mpDXArray[ n ] ;
|
|
|
|
if( n > 0 )
|
|
|
|
nDelta -= rArgs.mpDXArray[ n-1 ];
|
2003-05-28 11:31:35 +00:00
|
|
|
pNewGlyphWidths[ i ] += nDelta * mnUnitsPerPixel;
|
2003-03-27 16:59:30 +00:00
|
|
|
}
|
2002-02-18 08:08:18 +00:00
|
|
|
}
|
|
|
|
|
2002-06-13 19:26:21 +00:00
|
|
|
// move cluster positions using the adjusted widths
|
|
|
|
long nDelta = 0;
|
2002-09-04 16:12:08 +00:00
|
|
|
long nNewPos = 0;
|
2002-06-13 19:26:21 +00:00
|
|
|
pG = mpGlyphItems;
|
|
|
|
for( i = 0; i < mnGlyphCount; ++i, ++pG )
|
|
|
|
{
|
|
|
|
if( pG->IsClusterStart() )
|
2002-09-26 09:28:36 +00:00
|
|
|
{
|
2003-05-28 11:31:35 +00:00
|
|
|
// calculate original and adjusted cluster width
|
|
|
|
int nOldClusterWidth = pG->mnOrigWidth;
|
|
|
|
int nNewClusterWidth = pNewGlyphWidths[i];
|
|
|
|
GlyphItem* pClusterG = pG + 1;
|
|
|
|
for( int j = i; ++j < mnGlyphCount; ++pClusterG )
|
|
|
|
{
|
|
|
|
if( pClusterG->IsClusterStart() )
|
|
|
|
break;
|
|
|
|
nOldClusterWidth += pClusterG->mnOrigWidth;
|
|
|
|
nNewClusterWidth += pNewGlyphWidths[j];
|
|
|
|
}
|
|
|
|
int nDiff = nNewClusterWidth - nOldClusterWidth;
|
|
|
|
|
|
|
|
// adjust cluster glyph widths and positions
|
2002-09-20 09:44:55 +00:00
|
|
|
nDelta = nBasePointX + (nNewPos - pG->maLinearPos.X());
|
2003-05-28 11:31:35 +00:00
|
|
|
if( !pG->IsRTLGlyph()
|
|
|
|
|| (rArgs.mnFlags & SAL_LAYOUT_KASHIDA_JUSTIFICATON) )
|
|
|
|
{
|
|
|
|
// for (LTR || KASHIDA) case extend rightmost glyph in cluster
|
|
|
|
pClusterG[-1].mnNewWidth += nDiff;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
// right align cluster in new space for (RTL && !KASHIDA) case
|
|
|
|
pG->mnNewWidth += nDiff;
|
|
|
|
nDelta += nDiff;
|
|
|
|
}
|
|
|
|
|
|
|
|
nNewPos += nNewClusterWidth;
|
2002-09-26 09:28:36 +00:00
|
|
|
}
|
2002-06-13 19:26:21 +00:00
|
|
|
|
|
|
|
pG->maLinearPos.X() += nDelta;
|
|
|
|
}
|
2002-02-18 08:08:18 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// -----------------------------------------------------------------------
|
|
|
|
|
|
|
|
void GenericSalLayout::Justify( long nNewWidth )
|
|
|
|
{
|
2002-09-04 16:12:08 +00:00
|
|
|
nNewWidth *= mnUnitsPerPixel;
|
2003-03-27 16:59:30 +00:00
|
|
|
int nOldWidth = GetTextWidth();
|
2002-02-18 08:08:18 +00:00
|
|
|
if( !nOldWidth || nNewWidth==nOldWidth )
|
|
|
|
return;
|
|
|
|
|
2002-08-16 06:14:56 +00:00
|
|
|
// find rightmost glyph, it won't get stretched
|
2003-03-27 16:59:30 +00:00
|
|
|
GlyphItem* pGRight = mpGlyphItems + mnGlyphCount - 1;
|
2002-08-16 06:14:56 +00:00
|
|
|
|
|
|
|
// move rightmost glyph to requested position, correct adjustment widths
|
|
|
|
nOldWidth -= pGRight->mnOrigWidth;
|
|
|
|
nNewWidth -= pGRight->mnOrigWidth;
|
|
|
|
if( (nOldWidth < 0) || (nNewWidth < 0) )
|
|
|
|
return;
|
2002-02-18 08:08:18 +00:00
|
|
|
const long nBasePos = maBasePoint.X();
|
2002-08-16 06:14:56 +00:00
|
|
|
pGRight->maLinearPos.X() = nBasePos + nNewWidth;
|
|
|
|
|
2004-03-30 12:42:40 +00:00
|
|
|
// count stretchable glyphs
|
2003-03-27 16:59:30 +00:00
|
|
|
GlyphItem* pG;
|
2004-03-30 12:42:40 +00:00
|
|
|
int nStretchable = 0;
|
2002-08-16 06:14:56 +00:00
|
|
|
for( pG = mpGlyphItems; pG < pGRight; ++pG )
|
2004-03-30 12:42:40 +00:00
|
|
|
if( pG->mnOrigWidth > 0 )
|
|
|
|
++nStretchable;
|
|
|
|
|
|
|
|
// interpolate inbetween glyph positions
|
|
|
|
int nDiffWidth = nNewWidth - nOldWidth;
|
|
|
|
int nDeltaSum = 0;
|
|
|
|
for( pG = mpGlyphItems; (pG < pGRight) && (nStretchable > 0); ++pG )
|
2002-02-18 08:08:18 +00:00
|
|
|
{
|
2004-03-30 12:42:40 +00:00
|
|
|
if( pG->mnOrigWidth <= 0 )
|
|
|
|
continue;
|
2002-08-16 06:14:56 +00:00
|
|
|
|
2004-03-30 12:42:40 +00:00
|
|
|
int nDeltaWidth = nDiffWidth / nStretchable;
|
|
|
|
nDiffWidth -= nDeltaWidth;
|
|
|
|
--nStretchable;
|
2002-02-18 08:08:18 +00:00
|
|
|
|
2004-03-30 12:42:40 +00:00
|
|
|
pG->mnNewWidth += nDeltaWidth;
|
|
|
|
pG->maLinearPos.X() += nDeltaSum;
|
|
|
|
nDeltaSum += nDeltaWidth;
|
|
|
|
}
|
|
|
|
}
|
2002-10-29 12:09:45 +00:00
|
|
|
|
|
|
|
// -----------------------------------------------------------------------
|
|
|
|
|
|
|
|
void GenericSalLayout::ApplyAsianKerning( const sal_Unicode* pStr, int nLength )
|
|
|
|
{
|
|
|
|
long nOffset = 0;
|
|
|
|
|
|
|
|
GlyphItem* pGEnd = mpGlyphItems + mnGlyphCount;
|
|
|
|
for( GlyphItem* pG = mpGlyphItems; pG < pGEnd; ++pG )
|
|
|
|
{
|
|
|
|
int n = pG->mnCharPos;
|
|
|
|
if( (n < nLength - 1)
|
|
|
|
&& (0x3000 == (0xFF00 & pStr[n]))
|
|
|
|
&& (0x3000 == (0xFF00 & pStr[n+1])) )
|
|
|
|
{
|
|
|
|
const bool bVertical = false;
|
|
|
|
long nKernFirst = +CalcAsianKerning( pStr[n], true, bVertical );
|
|
|
|
long nKernNext = -CalcAsianKerning( pStr[n+1], false, bVertical );
|
|
|
|
|
|
|
|
long nDelta = (nKernFirst < nKernNext) ? nKernFirst : nKernNext;
|
|
|
|
if( nDelta<0 && nKernFirst!=0 && nKernNext!=0 )
|
|
|
|
{
|
|
|
|
int nGlyphWidth = pG->mnOrigWidth;
|
|
|
|
nDelta = (nDelta * nGlyphWidth + 2) / 4;
|
|
|
|
if( pG+1 == pGEnd )
|
|
|
|
pG->mnNewWidth += nDelta;
|
|
|
|
nOffset += nDelta;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if( pG+1 != pGEnd )
|
|
|
|
pG->maLinearPos.X() += nOffset;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2002-02-18 08:08:18 +00:00
|
|
|
// -----------------------------------------------------------------------
|
|
|
|
|
2002-09-25 16:59:34 +00:00
|
|
|
void GenericSalLayout::KashidaJustify( long nKashidaIndex, int nKashidaWidth )
|
|
|
|
{
|
2003-05-28 11:31:35 +00:00
|
|
|
// TODO: reimplement method when container type for GlyphItems changes
|
|
|
|
|
|
|
|
// skip if the kashida glyph in the font looks suspicious
|
|
|
|
if( nKashidaWidth <= 0 )
|
|
|
|
return;
|
2002-11-19 15:13:35 +00:00
|
|
|
|
2003-05-28 11:31:35 +00:00
|
|
|
// calculate max number of needed kashidas
|
|
|
|
const GlyphItem* pG1 = mpGlyphItems;
|
2002-09-25 16:59:34 +00:00
|
|
|
int nKashidaCount = 0, i;
|
|
|
|
for( i = 0; i < mnGlyphCount; ++i, ++pG1 )
|
|
|
|
{
|
2003-05-28 11:31:35 +00:00
|
|
|
if( !pG1->IsRTLGlyph() )
|
|
|
|
continue;
|
|
|
|
|
|
|
|
int nDelta = pG1->mnNewWidth - pG1->mnOrigWidth;
|
|
|
|
// worst case is one kashida even for mini-gaps
|
|
|
|
if( nDelta > 0 )
|
|
|
|
nKashidaCount += 1 + (nDelta / nKashidaWidth);
|
2002-09-25 16:59:34 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
if( !nKashidaCount )
|
|
|
|
return;
|
|
|
|
|
2003-05-28 11:31:35 +00:00
|
|
|
// reallocate glyph array for additional kashidas
|
|
|
|
// TODO: reuse array if additional glyphs would fit
|
2002-09-26 09:28:36 +00:00
|
|
|
mnGlyphCapacity = mnGlyphCount + nKashidaCount;
|
|
|
|
GlyphItem* pNewGlyphItems = new GlyphItem[ mnGlyphCapacity ];
|
2002-09-25 16:59:34 +00:00
|
|
|
GlyphItem* pG2 = pNewGlyphItems;
|
|
|
|
pG1 = mpGlyphItems;
|
2003-05-28 11:31:35 +00:00
|
|
|
for( i = mnGlyphCount; --i >= 0; ++pG1, ++pG2 )
|
2002-09-25 16:59:34 +00:00
|
|
|
{
|
2003-05-28 11:31:35 +00:00
|
|
|
// default action is to copy array element
|
|
|
|
*pG2 = *pG1;
|
2002-09-26 09:28:36 +00:00
|
|
|
|
2003-05-28 11:31:35 +00:00
|
|
|
// only apply kashida in a RTL context
|
|
|
|
if( !pG1->IsRTLGlyph() )
|
|
|
|
continue;
|
2002-09-25 16:59:34 +00:00
|
|
|
|
2003-05-28 11:31:35 +00:00
|
|
|
// calculate gap, skip if too small
|
|
|
|
int nDelta = pG1->mnNewWidth - pG1->mnOrigWidth;
|
|
|
|
if( 3*nDelta < nKashidaWidth )
|
|
|
|
continue;
|
|
|
|
|
|
|
|
// fill gap with kashidas
|
|
|
|
nKashidaCount = 0;
|
|
|
|
Point aPos = pG1->maLinearPos;
|
|
|
|
for(; nDelta > 0; nDelta -= nKashidaWidth, ++nKashidaCount )
|
|
|
|
{
|
|
|
|
*(pG2++) = GlyphItem( pG1->mnCharPos, nKashidaIndex, aPos,
|
|
|
|
GlyphItem::IS_IN_CLUSTER|GlyphItem::IS_RTL_GLYPH, nKashidaWidth );
|
|
|
|
aPos.X() += nKashidaWidth;
|
|
|
|
}
|
|
|
|
|
|
|
|
// fixup rightmost kashida for gap remainder
|
|
|
|
if( nDelta < 0 )
|
|
|
|
{
|
|
|
|
aPos.X() += nDelta;
|
|
|
|
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;
|
2002-09-25 16:59:34 +00:00
|
|
|
}
|
2003-05-28 11:31:35 +00:00
|
|
|
|
|
|
|
// when kashidas were used move the original glyph
|
|
|
|
// to the right and shrink it to it's original width
|
|
|
|
*pG2 = *pG1;
|
|
|
|
pG2->maLinearPos.X() = aPos.X();
|
|
|
|
pG2->mnNewWidth = pG2->mnOrigWidth;
|
2002-09-25 16:59:34 +00:00
|
|
|
}
|
|
|
|
|
2003-05-28 11:31:35 +00:00
|
|
|
// use the new glyph array
|
|
|
|
DBG_ASSERT( mnGlyphCapacity >= pG2-pNewGlyphItems, "KashidaJustify overflow" );
|
2002-09-25 16:59:34 +00:00
|
|
|
delete[] mpGlyphItems;
|
|
|
|
mpGlyphItems = pNewGlyphItems;
|
|
|
|
mnGlyphCount = pG2 - pNewGlyphItems;
|
|
|
|
}
|
|
|
|
|
|
|
|
// -----------------------------------------------------------------------
|
|
|
|
|
2004-06-17 11:20:59 +00:00
|
|
|
void GenericSalLayout::GetCaretPositions( int nMaxIndex, sal_Int32* pCaretXArray ) const
|
2002-08-23 14:59:34 +00:00
|
|
|
{
|
2002-09-04 16:12:08 +00:00
|
|
|
// initialize result array
|
|
|
|
long nXPos = -1;
|
2002-08-29 15:56:49 +00:00
|
|
|
int i;
|
2003-03-27 16:59:30 +00:00
|
|
|
for( i = 0; i < nMaxIndex; ++i )
|
2002-09-04 16:12:08 +00:00
|
|
|
pCaretXArray[ i ] = nXPos;
|
2002-08-23 14:59:34 +00:00
|
|
|
|
2002-09-04 16:12:08 +00:00
|
|
|
// calculate caret positions using glyph array
|
2002-08-23 14:59:34 +00:00
|
|
|
const GlyphItem* pG = mpGlyphItems;
|
2002-09-04 16:12:08 +00:00
|
|
|
for( i = mnGlyphCount; --i >= 0; ++pG )
|
2002-08-23 14:59:34 +00:00
|
|
|
{
|
2002-08-29 15:56:49 +00:00
|
|
|
nXPos = pG->maLinearPos.X();
|
2002-09-04 16:12:08 +00:00
|
|
|
long nXRight = nXPos + pG->mnOrigWidth;
|
|
|
|
int n = pG->mnCharPos;
|
2003-03-27 16:59:30 +00:00
|
|
|
int nCurrIdx = 2 * (n - mnMinCharPos);
|
|
|
|
if( !pG->IsRTLGlyph() )
|
2002-08-29 15:56:49 +00:00
|
|
|
{
|
2003-03-27 16:59:30 +00:00
|
|
|
// normal positions for LTR case
|
|
|
|
pCaretXArray[ nCurrIdx ] = nXPos;
|
|
|
|
pCaretXArray[ nCurrIdx+1 ] = nXRight;
|
2002-08-29 15:56:49 +00:00
|
|
|
}
|
2002-08-23 14:59:34 +00:00
|
|
|
else
|
2003-03-27 16:59:30 +00:00
|
|
|
{
|
|
|
|
// reverse positions for RTL case
|
|
|
|
pCaretXArray[ nCurrIdx ] = nXRight;
|
|
|
|
pCaretXArray[ nCurrIdx+1 ] = nXPos;
|
|
|
|
}
|
2002-08-23 14:59:34 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// -----------------------------------------------------------------------
|
|
|
|
|
2002-08-26 15:14:09 +00:00
|
|
|
int GenericSalLayout::GetTextBreak( long nMaxWidth, long nCharExtra, int nFactor ) const
|
2002-02-18 08:08:18 +00:00
|
|
|
{
|
2002-09-04 16:12:08 +00:00
|
|
|
int nCharCapacity = mnEndCharPos - mnMinCharPos;
|
2004-06-17 11:20:59 +00:00
|
|
|
sal_Int32* pCharWidths = (sal_Int32*)alloca( nCharCapacity * sizeof(sal_Int32) );
|
2002-04-03 16:25:23 +00:00
|
|
|
if( !GetCharWidths( pCharWidths ) )
|
|
|
|
return STRING_LEN;
|
2002-02-18 08:08:18 +00:00
|
|
|
|
|
|
|
long nWidth = 0;
|
2002-09-04 16:12:08 +00:00
|
|
|
for( int i = mnMinCharPos; i < mnEndCharPos; ++i )
|
2002-02-18 08:08:18 +00:00
|
|
|
{
|
2002-09-04 16:12:08 +00:00
|
|
|
nWidth += pCharWidths[ i - mnMinCharPos ] * nFactor;
|
2002-02-18 08:08:18 +00:00
|
|
|
if( nWidth >= nMaxWidth )
|
|
|
|
return i;
|
2002-08-01 12:29:41 +00:00
|
|
|
nWidth += nCharExtra;
|
2002-02-18 08:08:18 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
return STRING_LEN;
|
|
|
|
}
|
|
|
|
|
|
|
|
// -----------------------------------------------------------------------
|
|
|
|
|
2004-06-17 11:20:59 +00:00
|
|
|
int GenericSalLayout::GetNextGlyphs( int nLen, sal_Int32* pGlyphs, Point& rPos,
|
|
|
|
int& nStart, sal_Int32* pGlyphAdvAry, int* pCharPosAry ) const
|
2002-02-18 08:08:18 +00:00
|
|
|
{
|
|
|
|
const GlyphItem* pG = mpGlyphItems + nStart;
|
|
|
|
|
|
|
|
// find next glyph in substring
|
|
|
|
for(; nStart < mnGlyphCount; ++nStart, ++pG )
|
|
|
|
{
|
2002-09-04 16:12:08 +00:00
|
|
|
int n = pG->mnCharPos;
|
2003-03-27 16:59:30 +00:00
|
|
|
if( (mnMinCharPos <= n) && (n < mnEndCharPos) )
|
|
|
|
break;
|
2002-02-18 08:08:18 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// return zero if no more glyph found
|
|
|
|
if( nStart >= mnGlyphCount )
|
|
|
|
return 0;
|
|
|
|
|
2002-02-26 12:18:51 +00:00
|
|
|
// calculate absolute position in pixel units
|
|
|
|
Point aRelativePos = pG->maLinearPos - maBasePoint;
|
2002-02-18 08:08:18 +00:00
|
|
|
|
|
|
|
// find more glyphs which can be merged into one drawing instruction
|
|
|
|
int nCount = 0;
|
2002-07-19 16:19:35 +00:00
|
|
|
long nYPos = pG->maLinearPos.Y();
|
2002-08-01 12:29:41 +00:00
|
|
|
long nOldFlags = pG->mnGlyphIndex;
|
2003-07-21 10:22:01 +00:00
|
|
|
for(;;)
|
2002-02-18 08:08:18 +00:00
|
|
|
{
|
2003-03-27 16:59:30 +00:00
|
|
|
// update return data with glyph info
|
|
|
|
++nCount;
|
2002-02-18 08:08:18 +00:00
|
|
|
*(pGlyphs++) = pG->mnGlyphIndex;
|
2002-09-04 16:12:08 +00:00
|
|
|
if( pCharPosAry )
|
|
|
|
*(pCharPosAry++) = pG->mnCharPos;
|
2003-03-27 16:59:30 +00:00
|
|
|
if( pGlyphAdvAry )
|
2003-05-28 11:31:35 +00:00
|
|
|
*pGlyphAdvAry = pG->mnNewWidth;
|
2002-02-18 08:08:18 +00:00
|
|
|
|
2003-05-28 11:31:35 +00:00
|
|
|
// break at end of glyph list
|
2002-02-18 08:08:18 +00:00
|
|
|
if( ++nStart >= mnGlyphCount )
|
|
|
|
break;
|
2003-07-21 10:22:01 +00:00
|
|
|
// break when enough glyphs
|
|
|
|
if( nCount >= nLen )
|
|
|
|
break;
|
2002-02-18 08:08:18 +00:00
|
|
|
|
2003-05-28 11:31:35 +00:00
|
|
|
long nGlyphAdvance = pG[1].maLinearPos.X() - pG->maLinearPos.X();
|
|
|
|
if( pGlyphAdvAry )
|
2003-05-02 13:36:32 +00:00
|
|
|
{
|
2003-05-28 11:31:35 +00:00
|
|
|
// override default advance with correct value
|
|
|
|
*(pGlyphAdvAry++) = nGlyphAdvance;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
// stop when next x-position is unexpected
|
|
|
|
if( pG->mnOrigWidth != nGlyphAdvance )
|
2003-03-27 16:59:30 +00:00
|
|
|
break;
|
2003-05-02 13:36:32 +00:00
|
|
|
}
|
|
|
|
|
2003-05-28 11:31:35 +00:00
|
|
|
// advance to next glyph
|
2003-05-02 13:36:32 +00:00
|
|
|
++pG;
|
2002-02-26 12:18:51 +00:00
|
|
|
|
2003-05-02 13:36:32 +00:00
|
|
|
// stop when next y-position is unexpected
|
2002-07-19 16:19:35 +00:00
|
|
|
if( nYPos != pG->maLinearPos.Y() )
|
|
|
|
break;
|
|
|
|
|
2002-05-17 11:17:45 +00:00
|
|
|
// stop when no longer in string
|
2002-09-04 16:12:08 +00:00
|
|
|
int n = pG->mnCharPos;
|
2003-03-27 16:59:30 +00:00
|
|
|
if( (n < mnMinCharPos) || (mnEndCharPos <= n) )
|
2002-05-17 11:17:45 +00:00
|
|
|
break;
|
2002-08-01 12:29:41 +00:00
|
|
|
|
|
|
|
// stop when glyph flags change
|
2002-09-04 16:12:08 +00:00
|
|
|
if( (nOldFlags ^ pG->mnGlyphIndex) & GF_FLAGMASK )
|
2002-08-01 12:29:41 +00:00
|
|
|
break;
|
2002-10-29 12:09:45 +00:00
|
|
|
|
2003-05-28 11:31:35 +00:00
|
|
|
nOldFlags = pG->mnGlyphIndex; // &GF_FLAGMASK not needed for test above
|
2002-02-18 08:08:18 +00:00
|
|
|
}
|
|
|
|
|
2002-06-11 15:35:49 +00:00
|
|
|
aRelativePos.X() /= mnUnitsPerPixel;
|
|
|
|
aRelativePos.Y() /= mnUnitsPerPixel;
|
|
|
|
rPos = GetDrawPosition( aRelativePos );
|
|
|
|
|
2002-02-18 08:08:18 +00:00
|
|
|
return nCount;
|
|
|
|
}
|
|
|
|
|
2003-03-27 16:59:30 +00:00
|
|
|
// -----------------------------------------------------------------------
|
|
|
|
|
|
|
|
void GenericSalLayout::MoveGlyph( int nStart, long nNewXPos )
|
|
|
|
{
|
|
|
|
if( nStart >= mnGlyphCount )
|
|
|
|
return;
|
|
|
|
GlyphItem* pG = mpGlyphItems + nStart;
|
|
|
|
long nXDelta = nNewXPos - pG->maLinearPos.X();
|
|
|
|
if( nXDelta != 0 )
|
|
|
|
{
|
|
|
|
GlyphItem* const pGEnd = mpGlyphItems + mnGlyphCount;
|
|
|
|
for(; pG < pGEnd; ++pG )
|
|
|
|
pG->maLinearPos.X() += nXDelta;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// -----------------------------------------------------------------------
|
|
|
|
|
|
|
|
void GenericSalLayout::DropGlyph( int nStart )
|
|
|
|
{
|
|
|
|
if( nStart >= mnGlyphCount )
|
|
|
|
return;
|
|
|
|
GlyphItem* pG = mpGlyphItems + nStart;
|
|
|
|
pG->mnGlyphIndex = GF_DROPPED;
|
|
|
|
pG->mnCharPos = -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
// -----------------------------------------------------------------------
|
|
|
|
|
|
|
|
void GenericSalLayout::Simplify( bool bIsBase )
|
|
|
|
{
|
|
|
|
long nDropMarker = bIsBase ? GF_DROPPED : 0;
|
|
|
|
|
|
|
|
// remove dropped glyphs inplace
|
|
|
|
GlyphItem* pGDst = mpGlyphItems;
|
|
|
|
const GlyphItem* pGSrc = mpGlyphItems;
|
|
|
|
const GlyphItem* pGEnd = mpGlyphItems + mnGlyphCount;
|
|
|
|
for(; pGSrc < pGEnd; ++pGSrc )
|
|
|
|
{
|
|
|
|
if( pGSrc->mnGlyphIndex == nDropMarker )
|
|
|
|
continue;
|
|
|
|
if( pGDst != pGSrc )
|
|
|
|
*pGDst = *pGSrc;
|
|
|
|
++pGDst;
|
|
|
|
}
|
|
|
|
mnGlyphCount = pGDst - mpGlyphItems;
|
|
|
|
}
|
|
|
|
|
2003-12-01 08:55:04 +00:00
|
|
|
// -----------------------------------------------------------------------
|
|
|
|
|
|
|
|
// make sure GlyphItems are sorted left to right
|
|
|
|
void GenericSalLayout::SortGlyphItems()
|
|
|
|
{
|
|
|
|
// 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 )
|
|
|
|
{
|
|
|
|
// nothing to do when already in correct order
|
|
|
|
int nXPos = pGR->maLinearPos.X();
|
|
|
|
if( pGL->maLinearPos.X() <= nXPos )
|
|
|
|
continue;
|
|
|
|
|
|
|
|
// keep data of misplaced item
|
|
|
|
GlyphItem aGI = *pGR;
|
|
|
|
// make room for misplaced item
|
|
|
|
do pGL[1] = pGL[0];
|
|
|
|
while( (--pGL >= mpGlyphItems) && (nXPos < pGL->maLinearPos.X()) );
|
|
|
|
// move misplaced item to proper slot
|
|
|
|
pGL[1] = aGI;
|
|
|
|
// TODO: fix glyph cluster start flags
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2002-02-18 08:08:18 +00:00
|
|
|
// =======================================================================
|
2002-10-29 12:09:45 +00:00
|
|
|
|
2003-03-27 16:59:30 +00:00
|
|
|
MultiSalLayout::MultiSalLayout( SalLayout& rBaseLayout )
|
|
|
|
: SalLayout(),
|
2003-05-28 11:31:35 +00:00
|
|
|
mnLevel( 1 )
|
2002-10-29 12:09:45 +00:00
|
|
|
{
|
2003-05-28 11:31:35 +00:00
|
|
|
//maFallbackRuns[0].Clear();
|
|
|
|
mpLayouts[ 0 ] = &rBaseLayout;
|
2003-03-27 16:59:30 +00:00
|
|
|
mnUnitsPerPixel = rBaseLayout.GetUnitsPerPixel();
|
2002-10-29 12:09:45 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// -----------------------------------------------------------------------
|
|
|
|
|
|
|
|
MultiSalLayout::~MultiSalLayout()
|
|
|
|
{
|
|
|
|
for( int i = 0; i < mnLevel; ++i )
|
|
|
|
mpLayouts[ i ]->Release();
|
|
|
|
}
|
|
|
|
|
|
|
|
// -----------------------------------------------------------------------
|
|
|
|
|
2003-05-28 11:31:35 +00:00
|
|
|
bool MultiSalLayout::AddFallback( SalLayout& rFallback,
|
|
|
|
ImplLayoutRuns& rFallbackRuns, ImplFontData* pFallbackFont )
|
2002-10-29 12:09:45 +00:00
|
|
|
{
|
2002-12-12 12:52:57 +00:00
|
|
|
if( mnLevel >= MAX_FALLBACK )
|
|
|
|
return false;
|
2002-10-29 12:09:45 +00:00
|
|
|
|
2003-05-28 11:31:35 +00:00
|
|
|
mpFallbackFonts[ mnLevel ] = pFallbackFont;
|
|
|
|
mpLayouts[ mnLevel ] = &rFallback;
|
|
|
|
maFallbackRuns[ mnLevel-1 ] = rFallbackRuns;
|
|
|
|
++mnLevel;
|
2003-03-27 16:59:30 +00:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
// -----------------------------------------------------------------------
|
|
|
|
|
|
|
|
bool MultiSalLayout::LayoutText( ImplLayoutArgs& rArgs )
|
|
|
|
{
|
|
|
|
if( mnLevel <= 1 )
|
|
|
|
return false;
|
2003-07-21 10:22:01 +00:00
|
|
|
maFallbackRuns[ mnLevel-1 ] = rArgs.maRuns;
|
2003-05-28 11:31:35 +00:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
// -----------------------------------------------------------------------
|
|
|
|
|
|
|
|
void MultiSalLayout::AdjustLayout( ImplLayoutArgs& rArgs )
|
|
|
|
{
|
|
|
|
SalLayout::AdjustLayout( rArgs );
|
2004-02-04 10:02:51 +00:00
|
|
|
ImplLayoutArgs aMultiArgs = rArgs;
|
2003-05-28 11:31:35 +00:00
|
|
|
|
|
|
|
if( !rArgs.mpDXArray && rArgs.mnLayoutWidth )
|
|
|
|
{
|
2004-02-04 10:02:51 +00:00
|
|
|
// for stretched text in a MultiSalLayout the target width needs to be
|
|
|
|
// distributed by individually adjusting its virtual character widths
|
|
|
|
long nTargetWidth = aMultiArgs.mnLayoutWidth;
|
2004-02-02 17:22:42 +00:00
|
|
|
aMultiArgs.mnLayoutWidth = 0;
|
2004-02-04 10:02:51 +00:00
|
|
|
|
|
|
|
// we need to get the original unmodified layouts ready
|
2004-02-02 17:22:42 +00:00
|
|
|
for( int n = 0; n < mnLevel; ++n )
|
2004-02-04 10:02:51 +00:00
|
|
|
mpLayouts[n]->SalLayout::AdjustLayout( aMultiArgs );
|
|
|
|
// then we can measure the unmodified metrics
|
2003-05-28 11:31:35 +00:00
|
|
|
int nCharCount = rArgs.mnEndCharPos - rArgs.mnMinCharPos;
|
2004-06-17 11:20:59 +00:00
|
|
|
sal_Int32* pJustificationArray = (sal_Int32*)alloca( nCharCount * sizeof(sal_Int32) );
|
2004-02-04 10:02:51 +00:00
|
|
|
FillDXArray( pJustificationArray );
|
2004-03-30 12:42:40 +00:00
|
|
|
// #i17359# multilayout is not simplified yet, so calculating the
|
|
|
|
// unjustified width needs handholding; also count the number of
|
|
|
|
// stretchable virtual char widths
|
2004-02-04 10:02:51 +00:00
|
|
|
long nOrigWidth = 0;
|
2004-03-30 12:42:40 +00:00
|
|
|
int nStretchable = 0;
|
2004-02-04 10:02:51 +00:00
|
|
|
for( int i = 0; i < nCharCount; ++i )
|
|
|
|
{
|
|
|
|
// convert array from widths to sum of widths
|
|
|
|
nOrigWidth += pJustificationArray[i];
|
2004-03-30 12:42:40 +00:00
|
|
|
if( pJustificationArray[i] > 0 )
|
|
|
|
++nStretchable;
|
2004-02-04 10:02:51 +00:00
|
|
|
}
|
|
|
|
|
2004-03-30 12:42:40 +00:00
|
|
|
// now we are able to distribute the extra width over the virtual char widths
|
2004-02-04 10:02:51 +00:00
|
|
|
if( nOrigWidth && (nTargetWidth != nOrigWidth) )
|
2003-05-28 11:31:35 +00:00
|
|
|
{
|
2004-03-30 12:42:40 +00:00
|
|
|
int nDiffWidth = nTargetWidth - nOrigWidth;
|
|
|
|
int nWidthSum = 0;
|
2003-05-28 11:31:35 +00:00
|
|
|
for( int i = 0; i < nCharCount; ++i )
|
2004-03-30 12:42:40 +00:00
|
|
|
{
|
|
|
|
int nJustWidth = pJustificationArray[i];
|
|
|
|
if( (nJustWidth > 0) && (nStretchable > 0) )
|
|
|
|
{
|
|
|
|
int nDeltaWidth = nDiffWidth / nStretchable;
|
|
|
|
nJustWidth += nDeltaWidth;
|
|
|
|
nDiffWidth -= nDeltaWidth;
|
|
|
|
--nStretchable;
|
|
|
|
}
|
|
|
|
nWidthSum += nJustWidth;
|
|
|
|
pJustificationArray[i] = nWidthSum;
|
|
|
|
}
|
|
|
|
if( nWidthSum != nTargetWidth )
|
|
|
|
pJustificationArray[ nCharCount-1 ] = nTargetWidth;
|
|
|
|
|
2003-05-28 11:31:35 +00:00
|
|
|
// temporarily change the pDXArray
|
|
|
|
aMultiArgs.mpDXArray = pJustificationArray;
|
|
|
|
}
|
|
|
|
}
|
2003-03-27 16:59:30 +00:00
|
|
|
|
|
|
|
// prepare "merge sort"
|
|
|
|
int nStartOld[ MAX_FALLBACK ];
|
|
|
|
int nStartNew[ MAX_FALLBACK ];
|
|
|
|
int nCharPos[ MAX_FALLBACK ];
|
2004-06-17 11:20:59 +00:00
|
|
|
sal_Int32 nGlyphAdv[ MAX_FALLBACK ];
|
2003-03-27 16:59:30 +00:00
|
|
|
int nValid[ MAX_FALLBACK ];
|
2003-05-28 11:31:35 +00:00
|
|
|
const ImplLayoutRuns& rLastLevelRuns = maFallbackRuns[ mnLevel-1 ];
|
2003-03-27 16:59:30 +00:00
|
|
|
|
2004-06-17 11:20:59 +00:00
|
|
|
sal_Int32 nDummy;
|
2003-03-27 16:59:30 +00:00
|
|
|
Point aPos;
|
|
|
|
int nLevel = 0, n;
|
|
|
|
for( n = 0; n < mnLevel; ++n )
|
2002-10-29 12:09:45 +00:00
|
|
|
{
|
2003-05-28 11:31:35 +00:00
|
|
|
// now adjust the individual components
|
|
|
|
if( n > 0 )
|
|
|
|
aMultiArgs.maRuns = maFallbackRuns[ n-1 ];
|
|
|
|
mpLayouts[n]->AdjustLayout( aMultiArgs );
|
|
|
|
|
|
|
|
// remove unused parts of component
|
2003-03-27 16:59:30 +00:00
|
|
|
if( n > 0 )
|
|
|
|
mpLayouts[n]->Simplify( false );
|
2003-05-28 11:31:35 +00:00
|
|
|
|
|
|
|
// prepare merging components
|
2003-03-27 16:59:30 +00:00
|
|
|
nStartNew[ nLevel ] = nStartOld[ nLevel ] = 0;
|
|
|
|
nValid[ nLevel ] = mpLayouts[n]->GetNextGlyphs( 1, &nDummy, aPos,
|
|
|
|
nStartNew[ nLevel ], &nGlyphAdv[ nLevel ], &nCharPos[ nLevel ] );
|
2003-05-28 11:31:35 +00:00
|
|
|
if( (n > 0) && !nValid[ nLevel ] )
|
2003-04-11 16:29:32 +00:00
|
|
|
{
|
2003-05-28 11:31:35 +00:00
|
|
|
// release unused fallbacks
|
|
|
|
mpLayouts[n]->Release();
|
2003-04-11 16:29:32 +00:00
|
|
|
}
|
2003-03-27 16:59:30 +00:00
|
|
|
else
|
2003-05-28 11:31:35 +00:00
|
|
|
{
|
|
|
|
// reshuffle used fallbacks if needed
|
|
|
|
if( nLevel != n )
|
|
|
|
{
|
2003-07-21 10:22:01 +00:00
|
|
|
mpLayouts[ nLevel ] = mpLayouts[ n ];
|
2003-05-28 11:31:35 +00:00
|
|
|
mpFallbackFonts[ nLevel ] = mpFallbackFonts[ n ];
|
|
|
|
maFallbackRuns[ nLevel ] = maFallbackRuns[ n ];
|
|
|
|
}
|
|
|
|
++nLevel;
|
|
|
|
}
|
2003-03-27 16:59:30 +00:00
|
|
|
}
|
|
|
|
mnLevel = nLevel;
|
|
|
|
if( mnLevel <= 1 )
|
2003-05-28 11:31:35 +00:00
|
|
|
return;
|
2003-03-27 16:59:30 +00:00
|
|
|
|
|
|
|
// merge the fallback levels
|
|
|
|
long nXPos = 0;
|
2004-02-20 07:51:39 +00:00
|
|
|
int nFallbackUnitsPerPixel = 1;
|
2003-05-28 11:31:35 +00:00
|
|
|
for( n = 0; n < nLevel; ++n )
|
|
|
|
maFallbackRuns[n].ResetPos();
|
2003-03-27 16:59:30 +00:00
|
|
|
while( nValid[0] )
|
|
|
|
{
|
2003-05-28 11:31:35 +00:00
|
|
|
// find best fallback level
|
|
|
|
for( n = 0; n < nLevel; ++n )
|
|
|
|
if( nValid[n] && !maFallbackRuns[n].PosIsInRun( nCharPos[0] ) )
|
|
|
|
// fallback level n wins when it requested no further fallback
|
|
|
|
break;
|
|
|
|
|
|
|
|
if( n < nLevel )
|
2002-10-29 12:09:45 +00:00
|
|
|
{
|
2003-05-28 11:31:35 +00:00
|
|
|
// use base(n==0) or fallback(n>=1) level
|
2004-02-20 07:51:39 +00:00
|
|
|
long nNewPos = nXPos;
|
|
|
|
nFallbackUnitsPerPixel = mpLayouts[n]->GetUnitsPerPixel();
|
|
|
|
if( nFallbackUnitsPerPixel != mnUnitsPerPixel )
|
|
|
|
{
|
|
|
|
nNewPos *= nFallbackUnitsPerPixel;
|
|
|
|
nNewPos /= mnUnitsPerPixel;
|
|
|
|
}
|
2003-06-30 13:29:45 +00:00
|
|
|
mpLayouts[n]->MoveGlyph( nStartOld[n], nNewPos );
|
2003-03-27 16:59:30 +00:00
|
|
|
}
|
2003-05-28 11:31:35 +00:00
|
|
|
else
|
2003-03-27 16:59:30 +00:00
|
|
|
{
|
2003-05-28 11:31:35 +00:00
|
|
|
// if no fallback level has been found and the charpos in question
|
|
|
|
// has been resolved/unresolved then drop/keep the NotDef glyph
|
|
|
|
if( rLastLevelRuns.PosIsInRun( nCharPos[0] ) )
|
|
|
|
n = 0; // keep NotDef in base level
|
|
|
|
else
|
|
|
|
n = -1; // drop NotDef in base level
|
2004-02-20 07:51:39 +00:00
|
|
|
nFallbackUnitsPerPixel = mnUnitsPerPixel;
|
2003-05-28 11:31:35 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
if( n >= 0 )
|
|
|
|
{
|
|
|
|
// use glyph from best matching layout
|
2004-02-20 07:51:39 +00:00
|
|
|
int nCurrentGlyphAdv = nGlyphAdv[n];
|
|
|
|
if( nFallbackUnitsPerPixel != mnUnitsPerPixel )
|
|
|
|
{
|
|
|
|
nCurrentGlyphAdv *= mnUnitsPerPixel;
|
|
|
|
nCurrentGlyphAdv /= nFallbackUnitsPerPixel;
|
|
|
|
}
|
|
|
|
nXPos += nCurrentGlyphAdv;
|
2003-03-27 16:59:30 +00:00
|
|
|
|
2003-05-28 11:31:35 +00:00
|
|
|
// complete this glyph cluster, then advance to next
|
|
|
|
for( int nActivePos = nCharPos[0];; )
|
2003-03-27 16:59:30 +00:00
|
|
|
{
|
2003-05-28 11:31:35 +00:00
|
|
|
nStartOld[n] = nStartNew[n];
|
|
|
|
nValid[n] = mpLayouts[n]->GetNextGlyphs( 1, &nDummy, aPos,
|
|
|
|
nStartNew[n], &nGlyphAdv[n], &nCharPos[n] );
|
|
|
|
if( !nValid[n] || (nCharPos[n] != nActivePos) )
|
|
|
|
break;
|
2004-02-20 07:51:39 +00:00
|
|
|
int nCurrentGlyphAdv = nGlyphAdv[n];
|
|
|
|
if( nFallbackUnitsPerPixel != mnUnitsPerPixel )
|
|
|
|
{
|
|
|
|
nCurrentGlyphAdv *= mnUnitsPerPixel;
|
|
|
|
nCurrentGlyphAdv /= nFallbackUnitsPerPixel;
|
|
|
|
}
|
|
|
|
nXPos += nCurrentGlyphAdv;
|
2003-03-27 16:59:30 +00:00
|
|
|
}
|
|
|
|
|
2003-05-28 11:31:35 +00:00
|
|
|
// performance optimization (fallback level is completed)
|
|
|
|
if( !nValid[n] && (n >= nLevel-1) )
|
|
|
|
--nLevel;
|
|
|
|
}
|
2003-03-27 16:59:30 +00:00
|
|
|
|
2003-05-28 11:31:35 +00:00
|
|
|
if( n != 0 ) // glyph fallback was successful
|
2003-03-27 16:59:30 +00:00
|
|
|
{
|
|
|
|
// drop NotDef glyph from base layout
|
|
|
|
mpLayouts[0]->DropGlyph( nStartOld[0] );
|
2004-02-20 07:51:39 +00:00
|
|
|
mpLayouts[0]->MoveGlyph( nStartNew[0], nXPos );
|
2003-05-28 11:31:35 +00:00
|
|
|
|
|
|
|
// get next glyph in base layout
|
2003-03-27 16:59:30 +00:00
|
|
|
nStartOld[0] = nStartNew[0];
|
|
|
|
nValid[0] = mpLayouts[0]->GetNextGlyphs( 1, &nDummy, aPos,
|
|
|
|
nStartNew[0], &nGlyphAdv[0], &nCharPos[0] );
|
|
|
|
|
2003-05-28 11:31:35 +00:00
|
|
|
// advance runs if necessary
|
|
|
|
if( n < 0 )
|
2003-07-21 10:22:01 +00:00
|
|
|
n = nLevel;
|
|
|
|
while( --n >= 0 )
|
2003-05-28 11:31:35 +00:00
|
|
|
{
|
|
|
|
// if no more overlap with base level get next run
|
|
|
|
if( !maFallbackRuns[n].PosIsInRun( nCharPos[0] ) )
|
|
|
|
maFallbackRuns[n].NextRun();
|
|
|
|
}
|
2003-03-27 16:59:30 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2003-05-28 11:31:35 +00:00
|
|
|
mpLayouts[0]->Simplify( true );
|
2003-03-27 16:59:30 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// -----------------------------------------------------------------------
|
|
|
|
|
|
|
|
void MultiSalLayout::InitFont() const
|
|
|
|
{
|
|
|
|
if( mnLevel > 0 )
|
|
|
|
mpLayouts[0]->InitFont();
|
|
|
|
}
|
|
|
|
|
|
|
|
// -----------------------------------------------------------------------
|
|
|
|
|
2002-10-29 12:09:45 +00:00
|
|
|
void MultiSalLayout::DrawText( SalGraphics& rGraphics ) const
|
|
|
|
{
|
2002-11-22 16:24:25 +00:00
|
|
|
for( int i = mnLevel; --i >= 0; )
|
2002-10-29 12:09:45 +00:00
|
|
|
{
|
2002-11-22 16:24:25 +00:00
|
|
|
SalLayout& rLayout = *mpLayouts[ i ];
|
|
|
|
rLayout.DrawBase() = maDrawBase;
|
|
|
|
rLayout.DrawOffset() += maDrawOffset;
|
|
|
|
rLayout.InitFont();
|
|
|
|
rLayout.DrawText( rGraphics );
|
|
|
|
rLayout.DrawOffset() -= maDrawOffset;
|
2002-10-29 12:09:45 +00:00
|
|
|
}
|
2003-03-27 16:59:30 +00:00
|
|
|
// NOTE: now the baselevel font is active again
|
2002-10-29 12:09:45 +00:00
|
|
|
}
|
|
|
|
|
2003-03-27 16:59:30 +00:00
|
|
|
// -----------------------------------------------------------------------
|
2002-10-29 12:09:45 +00:00
|
|
|
|
|
|
|
int MultiSalLayout::GetTextBreak( long nMaxWidth, long nCharExtra, int nFactor ) const
|
|
|
|
{
|
2003-03-27 16:59:30 +00:00
|
|
|
if( mnLevel <= 0 )
|
|
|
|
return STRING_LEN;
|
|
|
|
if( mnLevel == 1 )
|
|
|
|
return mpLayouts[0]->GetTextBreak( nMaxWidth, nCharExtra, nFactor );
|
|
|
|
|
|
|
|
int nCharCount = mnEndCharPos - mnMinCharPos;
|
2004-06-17 11:20:59 +00:00
|
|
|
sal_Int32* pCharWidths = (sal_Int32*)alloca( 2*nCharCount * sizeof(sal_Int32) );
|
2003-03-27 16:59:30 +00:00
|
|
|
mpLayouts[0]->FillDXArray( pCharWidths );
|
|
|
|
|
|
|
|
for( int n = 1; n < mnLevel; ++n )
|
|
|
|
{
|
|
|
|
SalLayout& rLayout = *mpLayouts[ n ];
|
|
|
|
rLayout.FillDXArray( pCharWidths + nCharCount );
|
|
|
|
for( int i = 0; i < nCharCount; ++i )
|
2003-06-30 13:29:45 +00:00
|
|
|
{
|
|
|
|
long w = pCharWidths[i+nCharCount] * mnUnitsPerPixel;
|
|
|
|
w /= rLayout.GetUnitsPerPixel();
|
|
|
|
pCharWidths[ i ] += w;
|
|
|
|
}
|
2003-03-27 16:59:30 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
long nWidth = 0;
|
|
|
|
for( int i = 0; i < nCharCount; ++i )
|
|
|
|
{
|
|
|
|
nWidth += pCharWidths[ i ] * nFactor;
|
|
|
|
if( nWidth > nMaxWidth )
|
|
|
|
return (i + mnMinCharPos);
|
|
|
|
nWidth += nCharExtra;
|
|
|
|
}
|
|
|
|
|
|
|
|
return STRING_LEN;
|
2002-10-29 12:09:45 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// -----------------------------------------------------------------------
|
|
|
|
|
2004-06-17 11:20:59 +00:00
|
|
|
long MultiSalLayout::FillDXArray( sal_Int32* pCharWidths ) const
|
2002-10-29 12:09:45 +00:00
|
|
|
{
|
2004-02-04 10:02:51 +00:00
|
|
|
long nMaxWidth = 0;
|
2002-10-29 12:09:45 +00:00
|
|
|
|
2004-02-04 10:02:51 +00:00
|
|
|
// prepare merging of fallback levels
|
2004-06-17 11:20:59 +00:00
|
|
|
sal_Int32* pTempWidths = NULL;
|
2004-02-04 10:02:51 +00:00
|
|
|
const int nCharCount = mnEndCharPos - mnMinCharPos;
|
|
|
|
if( pCharWidths )
|
2002-10-29 12:09:45 +00:00
|
|
|
{
|
2004-02-04 10:02:51 +00:00
|
|
|
for( int i = 0; i < nCharCount; ++i )
|
|
|
|
pCharWidths[i] = 0;
|
2004-06-17 11:20:59 +00:00
|
|
|
pTempWidths = (sal_Int32*)alloca( nCharCount * sizeof(sal_Int32) );
|
2004-02-04 10:02:51 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
for( int n = mnLevel; --n >= 0; )
|
|
|
|
{
|
|
|
|
// query every fallback level
|
|
|
|
long nWidth = mpLayouts[n]->FillDXArray( pTempWidths );
|
|
|
|
if( !nWidth )
|
|
|
|
continue;
|
|
|
|
// merge results from current level
|
|
|
|
nWidth *= mnUnitsPerPixel;
|
|
|
|
nWidth /= mpLayouts[n]->GetUnitsPerPixel();
|
|
|
|
if( nMaxWidth < nWidth )
|
|
|
|
nMaxWidth = nWidth;
|
|
|
|
if( !pCharWidths )
|
|
|
|
continue;
|
|
|
|
// calculate virtual char widths using most probable fallback layout
|
|
|
|
for( int i = 0; i < nCharCount; ++i )
|
2002-10-29 12:09:45 +00:00
|
|
|
{
|
2004-02-04 10:02:51 +00:00
|
|
|
// #i17359# restriction:
|
|
|
|
// one char cannot be resolved from different fallbacks
|
|
|
|
if( pCharWidths[i] != 0 )
|
|
|
|
continue;
|
|
|
|
long nCharWidth = pTempWidths[i];
|
|
|
|
if( !nCharWidth )
|
|
|
|
continue;
|
|
|
|
nCharWidth *= mnUnitsPerPixel;
|
|
|
|
nCharWidth /= mpLayouts[n]->GetUnitsPerPixel();
|
|
|
|
pCharWidths[i] = nCharWidth;
|
2002-10-29 12:09:45 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2003-03-27 16:59:30 +00:00
|
|
|
return nMaxWidth;
|
2002-10-29 12:09:45 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// -----------------------------------------------------------------------
|
|
|
|
|
2004-06-17 11:20:59 +00:00
|
|
|
void MultiSalLayout::GetCaretPositions( int nMaxIndex, sal_Int32* pCaretXArray ) const
|
2002-10-29 12:09:45 +00:00
|
|
|
{
|
2002-11-22 16:24:25 +00:00
|
|
|
SalLayout& rLayout = *mpLayouts[ 0 ];
|
2003-03-27 16:59:30 +00:00
|
|
|
rLayout.GetCaretPositions( nMaxIndex, pCaretXArray );
|
2002-10-29 12:09:45 +00:00
|
|
|
|
2003-03-27 16:59:30 +00:00
|
|
|
if( mnLevel > 1 )
|
|
|
|
{
|
2004-06-17 11:20:59 +00:00
|
|
|
sal_Int32* pTempPos = (sal_Int32*)alloca( nMaxIndex * sizeof(sal_Int32) );
|
2003-03-27 16:59:30 +00:00
|
|
|
for( int n = 1; n < mnLevel; ++n )
|
|
|
|
{
|
|
|
|
mpLayouts[ n ]->GetCaretPositions( nMaxIndex, pTempPos );
|
2004-02-02 17:22:42 +00:00
|
|
|
long nDivisor = mpLayouts[n]->GetUnitsPerPixel();
|
2003-03-27 16:59:30 +00:00
|
|
|
for( int i = 0; i < nMaxIndex; ++i )
|
|
|
|
if( pTempPos[i] >= 0 )
|
2003-06-30 13:29:45 +00:00
|
|
|
{
|
2004-02-02 17:22:42 +00:00
|
|
|
long w = pTempPos[i];
|
|
|
|
w = (w * mnUnitsPerPixel) / nDivisor;
|
2003-06-30 13:29:45 +00:00
|
|
|
pCaretXArray[i] = w;
|
|
|
|
}
|
2003-03-27 16:59:30 +00:00
|
|
|
}
|
|
|
|
}
|
2002-10-29 12:09:45 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// -----------------------------------------------------------------------
|
|
|
|
|
2004-06-17 11:20:59 +00:00
|
|
|
int MultiSalLayout::GetNextGlyphs( int nLen, sal_Int32* pGlyphIdxAry, Point& rPos,
|
|
|
|
int& nStart, sal_Int32* pGlyphAdvAry, int* pCharPosAry ) const
|
2002-10-29 12:09:45 +00:00
|
|
|
{
|
2002-12-12 12:52:57 +00:00
|
|
|
// for multi-level fallback only single glyphs should be used
|
|
|
|
if( mnLevel > 1 && nLen > 1 )
|
|
|
|
nLen = 1;
|
|
|
|
|
|
|
|
// NOTE: nStart is tagged with current font index
|
|
|
|
int nLevel = nStart >> GF_FONTSHIFT;
|
|
|
|
nStart &= ~GF_FONTMASK;
|
|
|
|
for(; nLevel < mnLevel; ++nLevel, nStart=0 )
|
|
|
|
{
|
|
|
|
SalLayout& rLayout = *mpLayouts[ nLevel ];
|
2003-03-27 16:59:30 +00:00
|
|
|
rLayout.InitFont();
|
2002-12-12 12:52:57 +00:00
|
|
|
int nRetVal = rLayout.GetNextGlyphs( nLen, pGlyphIdxAry, rPos,
|
|
|
|
nStart, pGlyphAdvAry, pCharPosAry );
|
|
|
|
if( nRetVal )
|
|
|
|
{
|
|
|
|
int nFontTag = nLevel << GF_FONTSHIFT;
|
|
|
|
nStart |= nFontTag;
|
|
|
|
for( int i = 0; i < nRetVal; ++i )
|
2003-06-30 13:29:45 +00:00
|
|
|
{
|
|
|
|
if( pGlyphAdvAry )
|
|
|
|
{
|
|
|
|
long w = pGlyphAdvAry[i] * mnUnitsPerPixel;
|
|
|
|
w /= mpLayouts[nLevel]->GetUnitsPerPixel();
|
|
|
|
pGlyphAdvAry[i] = w;
|
|
|
|
}
|
2002-12-12 12:52:57 +00:00
|
|
|
pGlyphIdxAry[ i ] |= nFontTag;
|
2003-06-30 13:29:45 +00:00
|
|
|
}
|
2003-04-11 16:29:32 +00:00
|
|
|
rPos += maDrawBase;
|
2003-05-28 11:31:35 +00:00
|
|
|
rPos += maDrawOffset;
|
2002-12-12 12:52:57 +00:00
|
|
|
return nRetVal;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2003-08-20 14:03:44 +00:00
|
|
|
// #111016# reset to base level font when done
|
|
|
|
mpLayouts[0]->InitFont();
|
2002-12-12 12:52:57 +00:00
|
|
|
return 0;
|
2002-10-29 12:09:45 +00:00
|
|
|
}
|
|
|
|
|
2002-11-22 16:24:25 +00:00
|
|
|
// -----------------------------------------------------------------------
|
|
|
|
|
|
|
|
bool MultiSalLayout::GetOutline( SalGraphics& rGraphics, PolyPolyVector& rPPV ) const
|
|
|
|
{
|
|
|
|
bool bRet = false;
|
|
|
|
|
|
|
|
for( int i = mnLevel; --i >= 0; )
|
|
|
|
{
|
|
|
|
SalLayout& rLayout = *mpLayouts[ i ];
|
|
|
|
rLayout.DrawBase() = maDrawBase;
|
|
|
|
rLayout.DrawOffset() += maDrawOffset;
|
|
|
|
rLayout.InitFont();
|
|
|
|
bRet |= rLayout.GetOutline( rGraphics, rPPV );
|
|
|
|
rLayout.DrawOffset() -= maDrawOffset;
|
|
|
|
}
|
|
|
|
|
|
|
|
return bRet;
|
|
|
|
}
|
|
|
|
|
|
|
|
// -----------------------------------------------------------------------
|
|
|
|
|
|
|
|
bool MultiSalLayout::GetBoundRect( SalGraphics& rGraphics, Rectangle& rRect ) const
|
|
|
|
{
|
|
|
|
bool bRet = false;
|
|
|
|
|
|
|
|
Rectangle aRectangle;
|
|
|
|
for( int i = mnLevel; --i >= 0; )
|
|
|
|
{
|
|
|
|
SalLayout& rLayout = *mpLayouts[ i ];
|
|
|
|
rLayout.DrawBase() = maDrawBase;
|
|
|
|
rLayout.DrawOffset() += maDrawOffset;
|
|
|
|
rLayout.InitFont();
|
2003-03-27 16:59:30 +00:00
|
|
|
if( rLayout.GetBoundRect( rGraphics, aRectangle ) )
|
|
|
|
{
|
|
|
|
rRect.Union( aRectangle );
|
|
|
|
bRet = true;
|
|
|
|
}
|
2002-11-22 16:24:25 +00:00
|
|
|
rLayout.DrawOffset() -= maDrawOffset;
|
|
|
|
}
|
|
|
|
|
|
|
|
return bRet;
|
|
|
|
}
|
|
|
|
|
2002-10-29 12:09:45 +00:00
|
|
|
// =======================================================================
|