summaryrefslogtreecommitdiffstats
path: root/src/kernel/qfont_x11.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/kernel/qfont_x11.cpp')
-rw-r--r--src/kernel/qfont_x11.cpp737
1 files changed, 737 insertions, 0 deletions
diff --git a/src/kernel/qfont_x11.cpp b/src/kernel/qfont_x11.cpp
new file mode 100644
index 0000000..fea0b58
--- /dev/null
+++ b/src/kernel/qfont_x11.cpp
@@ -0,0 +1,737 @@
+/****************************************************************************
+**
+** Implementation of QFont, QFontMetrics and QFontInfo classes for X11
+**
+** Created : 940515
+**
+** Copyright (C) 1992-2008 Trolltech ASA. All rights reserved.
+**
+** This file is part of the kernel module of the Qt GUI Toolkit.
+**
+** This file may be used under the terms of the GNU General
+** Public License versions 2.0 or 3.0 as published by the Free
+** Software Foundation and appearing in the files LICENSE.GPL2
+** and LICENSE.GPL3 included in the packaging of this file.
+** Alternatively you may (at your option) use any later version
+** of the GNU General Public License if such license has been
+** publicly approved by Trolltech ASA (or its successors, if any)
+** and the KDE Free Qt Foundation.
+**
+** Please review the following information to ensure GNU General
+** Public Licensing requirements will be met:
+** http://trolltech.com/products/qt/licenses/licensing/opensource/.
+** If you are unsure which license is appropriate for your use, please
+** review the following information:
+** http://trolltech.com/products/qt/licenses/licensing/licensingoverview
+** or contact the sales department at [email protected].
+**
+** This file may be used under the terms of the Q Public License as
+** defined by Trolltech ASA and appearing in the file LICENSE.QPL
+** included in the packaging of this file. Licensees holding valid Qt
+** Commercial licenses may use this file in accordance with the Qt
+** Commercial License Agreement provided with the Software.
+**
+** This file is provided "AS IS" with NO WARRANTY OF ANY KIND,
+** INCLUDING THE WARRANTIES OF DESIGN, MERCHANTABILITY AND FITNESS FOR
+** A PARTICULAR PURPOSE. Trolltech reserves all rights not granted
+** herein.
+**
+**********************************************************************/
+
+#define QT_FATAL_ASSERT
+
+// REVISED: brad
+
+#include "qplatformdefs.h"
+
+#include "qfont.h"
+#include "qapplication.h"
+#include "qcleanuphandler.h"
+#include "qfontinfo.h"
+#include "qfontdatabase.h"
+#include "qfontmetrics.h"
+#include "qpaintdevice.h"
+#include "qpaintdevicemetrics.h"
+#include "qtextcodec.h"
+
+#include <private/qfontcodecs_p.h>
+#include <private/qunicodetables_p.h>
+#include "qfontdata_p.h"
+#include "qfontengine_p.h"
+#include "qtextengine_p.h"
+
+#include "qt_x11_p.h"
+
+#include <time.h>
+#include <stdlib.h>
+#include <ctype.h>
+
+#define QFONTLOADER_DEBUG
+#define QFONTLOADER_DEBUG_VERBOSE
+
+Q_EXPORT bool qt_has_xft = FALSE;
+
+#ifndef QT_NO_XFTFREETYPE
+Qt::HANDLE qt_xft_handle(const QFont &font)
+{
+ QFontEngine *engine = font.d->engineForScript( QFontPrivate::defaultScript );
+ if (!engine->type() == QFontEngine::Xft)
+ return 0;
+ return (long)static_cast<QFontEngineXft *>(engine)->font();
+}
+#endif
+
+double qt_pixelSize(double pointSize, QPaintDevice *paintdevice, int scr)
+{
+ if (pointSize < 0) return -1.;
+
+ double result = pointSize;
+ if (paintdevice && QPaintDeviceMetrics( paintdevice ).logicalDpiY() != 75)
+ result *= QPaintDeviceMetrics( paintdevice ).logicalDpiY() / 72.;
+ else if (QPaintDevice::x11AppDpiY( scr ) != 75)
+ result *= QPaintDevice::x11AppDpiY( scr ) / 72.;
+
+ return result;
+}
+
+double qt_pointSize(double pixelSize, QPaintDevice *paintdevice, int scr)
+{
+ if (pixelSize < 0) return -1.;
+
+ double result = pixelSize;
+ if ( paintdevice && QPaintDeviceMetrics( paintdevice ).logicalDpiY() != 75)
+ result *= 72. / QPaintDeviceMetrics( paintdevice ).logicalDpiY();
+ else if (QPaintDevice::x11AppDpiY(scr) != 75)
+ result *= 72. / QPaintDevice::x11AppDpiY( scr );
+
+ return result;
+}
+
+static inline double pixelSize( const QFontDef &request, QPaintDevice *paintdevice,
+ int scr )
+{
+ return ((request.pointSize != -1) ?
+ qt_pixelSize(request.pointSize / 10., paintdevice, scr) :
+ (double)request.pixelSize);
+}
+
+static inline double pointSize( const QFontDef &request, QPaintDevice *paintdevice,
+ int scr )
+{
+ return ((request.pixelSize != -1) ?
+ qt_pointSize(request.pixelSize, paintdevice, scr) * 10.:
+ (double)request.pointSize);
+}
+
+
+/*
+ Removes wildcards from an XLFD.
+
+ Returns \a xlfd with all wildcards removed if a match for \a xlfd is
+ found, otherwise it returns \a xlfd.
+*/
+static QCString qt_fixXLFD( const QCString &xlfd )
+{
+ QCString ret = xlfd;
+ int count = 0;
+ char **fontNames =
+ XListFonts( QPaintDevice::x11AppDisplay(), xlfd, 32768, &count );
+ if ( count > 0 )
+ ret = fontNames[0];
+ XFreeFontNames( fontNames );
+ return ret ;
+}
+
+typedef QMap<QFont::Script,QString> FallbackMap;
+static FallbackMap *fallbackMap = 0;
+static QSingleCleanupHandler<FallbackMap> qt_fallback_font_family_cleanup;
+
+static void ensure_fallback_map()
+{
+ if ( fallbackMap ) return;
+ fallbackMap = new FallbackMap;
+ qt_fallback_font_family_cleanup.set( &fallbackMap );
+}
+
+// Returns the user-configured fallback family for the specified script.
+QString qt_fallback_font_family( QFont::Script script )
+{
+ QString ret;
+
+ if ( fallbackMap ) {
+ FallbackMap::ConstIterator it, end = fallbackMap->end();
+ it = fallbackMap->find( script );
+ if ( it != end )
+ ret = it.data();
+ }
+
+ return ret;
+}
+
+// Sets the fallback family for the specified script.
+void qt_set_fallback_font_family( QFont::Script script, const QString &family )
+{
+ ensure_fallback_map();
+
+ if ( ! family.isEmpty() )
+ fallbackMap->insert( script, family );
+ else
+ fallbackMap->remove( script );
+}
+
+
+QFont::Script QFontPrivate::defaultScript = QFont::UnknownScript;
+int QFontPrivate::defaultEncodingID = -1;
+
+/*!
+ Internal function that initializes the font system.
+
+ \internal
+ The font cache and font dict do not alloc the keys. The key is a QString
+ which is shared between QFontPrivate and QXFontName.
+*/
+void QFont::initialize()
+{
+ // create global font cache
+ if ( ! QFontCache::instance ) (void) new QFontCache;
+
+#ifndef QT_NO_CODECS
+#ifndef QT_NO_BIG_CODECS
+ static bool codecs_once = FALSE;
+ if ( ! codecs_once ) {
+ (void) new QFontJis0201Codec;
+ (void) new QFontJis0208Codec;
+ (void) new QFontKsc5601Codec;
+ (void) new QFontGb2312Codec;
+ (void) new QFontGbkCodec;
+ (void) new QFontGb18030_0Codec;
+ (void) new QFontBig5Codec;
+ (void) new QFontBig5hkscsCodec;
+ (void) new QFontLaoCodec;
+ codecs_once = TRUE;
+ }
+#endif // QT_NO_BIG_CODECS
+#endif // QT_NO_CODECS
+
+ extern int qt_encoding_id_for_mib( int mib ); // from qfontdatabase_x11.cpp
+ QTextCodec *codec = QTextCodec::codecForLocale();
+ // determine the default encoding id using the locale, otherwise
+ // fallback to latin1 ( mib == 4 )
+ int mib = codec ? codec->mibEnum() : 4;
+
+ // for asian locales, use the mib for the font codec instead of the locale codec
+ switch (mib) {
+ case 38: // eucKR
+ mib = 36;
+ break;
+
+ case 2025: // GB2312
+ mib = 57;
+ break;
+
+ case 113: // GBK
+ mib = -113;
+ break;
+
+ case 114: // GB18030
+ mib = -114;
+ break;
+
+ case 2026: // Big5
+ mib = -2026;
+ break;
+
+ case 2101: // Big5-HKSCS
+ mib = -2101;
+ break;
+
+ case 16: // JIS7
+ mib = 15;
+ break;
+
+ case 17: // SJIS
+ case 18: // eucJP
+ mib = 63;
+ break;
+ }
+
+ // get the default encoding id for the locale encoding...
+ QFontPrivate::defaultEncodingID = qt_encoding_id_for_mib( mib );
+
+ // get some sample text based on the users locale. we use this to determine the
+ // default script for the font system
+ QCString oldlctime = setlocale(LC_TIME, 0);
+ QCString lctime = setlocale(LC_TIME, "");
+
+ time_t ttmp = time(0);
+ struct tm *tt = 0;
+ char samp[64];
+ QString sample;
+
+ if ( ttmp != -1 ) {
+#if defined(QT_THREAD_SUPPORT) && defined(_POSIX_THREAD_SAFE_FUNCTIONS)
+ // use the reentrant versions of localtime() where available
+ tm res;
+ tt = localtime_r( &ttmp, &res );
+#else
+ tt = localtime( &ttmp );
+#endif // QT_THREAD_SUPPORT && _POSIX_THREAD_SAFE_FUNCTIONS
+
+ if ( tt != 0 && strftime( samp, 64, "%A%B", tt ) > 0 )
+ if ( codec )
+ sample = codec->toUnicode( samp );
+ }
+
+ if ( ! sample.isNull() && ! sample.isEmpty() ) {
+ QFont::Script cs = QFont::NoScript, tmp;
+ const QChar *uc = sample.unicode();
+ QFontPrivate *priv = new QFontPrivate;
+
+ for ( uint i = 0; i < sample.length(); i++ ) {
+ SCRIPT_FOR_CHAR( tmp, *uc );
+ uc++;
+ if ( tmp != cs && tmp != QFont::UnknownScript ) {
+ cs = tmp;
+ break;
+ }
+ }
+ delete priv;
+
+ if ( cs != QFont::UnknownScript )
+ QFontPrivate::defaultScript = cs;
+ }
+
+ setlocale( LC_TIME, oldlctime.data() );
+}
+
+/*! \internal
+
+ Internal function that cleans up the font system.
+*/
+void QFont::cleanup()
+{
+ // delete the global font cache
+ delete QFontCache::instance;
+}
+
+/*!
+ \internal
+ X11 Only: Returns the screen with which this font is associated.
+*/
+int QFont::x11Screen() const
+{
+ return d->screen;
+}
+
+/*! \internal
+ X11 Only: Associate the font with the specified \a screen.
+*/
+void QFont::x11SetScreen( int screen )
+{
+ if ( screen < 0 ) // assume default
+ screen = QPaintDevice::x11AppScreen();
+
+ if ( screen == d->screen )
+ return; // nothing to do
+
+ detach();
+ d->screen = screen;
+}
+
+/*! \internal
+ Returns a QFontEngine for the specified \a script that matches the
+ QFontDef \e request member variable.
+*/
+void QFontPrivate::load( QFont::Script script )
+{
+ // NOTE: the X11 and Windows implementations of this function are
+ // identical... if you change one, change both.
+
+#ifdef QT_CHECK_STATE
+ // sanity checks
+ if (!QFontCache::instance)
+ qWarning("Must construct a QApplication before a QFont");
+ Q_ASSERT( script >= 0 && script < QFont::LastPrivateScript );
+#endif // QT_CHECK_STATE
+
+ QFontDef req = request;
+ req.pixelSize = qRound(pixelSize(req, paintdevice, screen));
+ req.pointSize = 0;
+
+ if ( ! engineData ) {
+ QFontCache::Key key( req, QFont::NoScript, screen, paintdevice );
+
+ // look for the requested font in the engine data cache
+ engineData = QFontCache::instance->findEngineData( key );
+
+ if ( ! engineData ) {
+ // create a new one
+ engineData = new QFontEngineData;
+ QFontCache::instance->insertEngineData( key, engineData );
+ } else {
+ engineData->ref();
+ }
+ }
+
+ // the cached engineData could have already loaded the engine we want
+ if ( engineData->engines[script] ) return;
+
+ // load the font
+ QFontEngine *engine = 0;
+ // double scale = 1.0; // ### TODO: fix the scale calculations
+
+ // list of families to try
+ QStringList family_list;
+
+ if (!req.family.isEmpty()) {
+ family_list = QStringList::split( ',', req.family );
+
+ // append the substitute list for each family in family_list
+ QStringList subs_list;
+ QStringList::ConstIterator it = family_list.begin(), end = family_list.end();
+ for ( ; it != end; ++it )
+ subs_list += QFont::substitutes( *it );
+ family_list += subs_list;
+
+#ifndef QT_XFT2
+ // with Xft2, we want to use fontconfig to determine better fallbacks,
+ // otherwise we might run into trouble with default fonts as "serif"
+
+ // append the default fallback font for the specified script
+ QString fallback = qt_fallback_font_family( script );
+ if ( ! fallback.isEmpty() && ! family_list.contains( fallback ) )
+ family_list << fallback;
+
+ // add the default family
+ QString defaultFamily = QApplication::font().family();
+ if ( ! family_list.contains( defaultFamily ) )
+ family_list << defaultFamily;
+
+ // add QFont::defaultFamily() to the list, for compatibility with
+ // previous versions
+ family_list << QApplication::font().defaultFamily();
+#endif // QT_XFT2
+ }
+
+ // null family means find the first font matching the specified script
+ family_list << QString::null;
+
+ QStringList::ConstIterator it = family_list.begin(), end = family_list.end();
+ for ( ; ! engine && it != end; ++it ) {
+ req.family = *it;
+
+ engine = QFontDatabase::findFont( script, this, req );
+ if ( engine ) {
+ if ( engine->type() != QFontEngine::Box )
+ break;
+
+ if ( ! req.family.isEmpty() )
+ engine = 0;
+
+ continue;
+ }
+ }
+
+ engine->ref();
+ engineData->engines[script] = engine;
+}
+
+/*!
+ Returns TRUE if the font attributes have been changed and the font
+ has to be (re)loaded; otherwise returns FALSE.
+*/
+bool QFont::dirty() const
+{
+ return d->engineData == 0;
+}
+
+/*!
+ Returns the window system handle to the font, for low-level
+ access. Using this function is \e not portable.
+*/
+Qt::HANDLE QFont::handle() const
+{
+ QFontEngine *engine = d->engineForScript( QFontPrivate::defaultScript );
+#ifdef QT_CHECK_STATE
+ Q_ASSERT( engine != 0 );
+#endif // QT_CHECK_STATE
+
+ switch ( engine->type() ) {
+ case QFontEngine::XLFD:
+ return ((QFontEngineXLFD *) engine)->handle();
+ case QFontEngine::LatinXLFD:
+ return ((QFontEngineLatinXLFD *) engine)->handle();
+
+ default: break;
+ }
+ return 0;
+}
+
+/*!
+ Returns the name of the font within the underlying window system.
+
+ On Windows, this is usually just the family name of a TrueType
+ font.
+
+ On X11, it is an XLFD (X Logical Font Description). When Qt is
+ build with Xft support on X11, the return value can be an Xft
+ pattern or an XLFD.
+
+ Using the return value of this function is usually \e not \e
+ portable.
+
+ \sa setRawName()
+*/
+QString QFont::rawName() const
+{
+ QFontEngine *engine = d->engineForScript( QFontPrivate::defaultScript );
+#ifdef QT_CHECK_STATE
+ Q_ASSERT( engine != 0 );
+#endif // QT_CHECK_STATE
+
+ return QString::fromLatin1( engine->name() );
+}
+
+/*!
+ Sets a font by its system specific name. The function is
+ particularly useful under X, where system font settings (for
+ example X resources) are usually available in XLFD (X Logical Font
+ Description) form only. You can pass an XLFD as \a name to this
+ function.
+
+ A font set with setRawName() is still a full-featured QFont. It can
+ be queried (for example with italic()) or modified (for example with
+ setItalic()) and is therefore also suitable for rendering rich text.
+
+ If Qt's internal font database cannot resolve the raw name, the
+ font becomes a raw font with \a name as its family.
+
+ Note that the present implementation does not handle wildcards in
+ XLFDs well, and that font aliases (file \c fonts.alias in the font
+ directory on X11) are not supported.
+
+ \sa rawName(), setRawMode(), setFamily()
+*/
+void QFont::setRawName( const QString &name )
+{
+ detach();
+
+ // from qfontdatabase_x11.cpp
+ extern bool qt_fillFontDef( const QCString &xlfd, QFontDef *fd, int screen );
+
+ if ( ! qt_fillFontDef( qt_fixXLFD( name.latin1() ), &d->request, d->screen ) ) {
+#ifdef QT_CHECK_STATE
+ qWarning("QFont::setRawName(): Invalid XLFD: \"%s\"", name.latin1());
+#endif // QT_CHECK_STATE
+
+ setFamily( name );
+ setRawMode( TRUE );
+ } else {
+ d->mask = QFontPrivate::Complete;
+ }
+}
+
+/*!
+ Returns the "last resort" font family name.
+
+ The current implementation tries a wide variety of common fonts,
+ returning the first one it finds. Is is possible that no family is
+ found in which case a null string is returned.
+
+ \sa lastResortFont()
+*/
+QString QFont::lastResortFamily() const
+{
+ return QString::fromLatin1( "Helvetica" );
+}
+
+/*!
+ Returns the family name that corresponds to the current style
+ hint.
+
+ \sa StyleHint styleHint() setStyleHint()
+*/
+QString QFont::defaultFamily() const
+{
+ switch ( d->request.styleHint ) {
+ case QFont::Times:
+ return QString::fromLatin1( "Times" );
+
+ case QFont::Courier:
+ return QString::fromLatin1( "Courier" );
+
+ case QFont::Decorative:
+ return QString::fromLatin1( "Old English" );
+
+ case QFont::Helvetica:
+ case QFont::System:
+ default:
+ return QString::fromLatin1( "Helvetica" );
+ }
+}
+
+/*
+ Returns a last resort raw font name for the font matching algorithm.
+ This is used if even the last resort family is not available. It
+ returns \e something, almost no matter what. The current
+ implementation tries a wide variety of common fonts, returning the
+ first one it finds. The implementation may change at any time.
+*/
+static const char * const tryFonts[] = {
+ "-*-helvetica-medium-r-*-*-*-120-*-*-*-*-*-*",
+ "-*-courier-medium-r-*-*-*-120-*-*-*-*-*-*",
+ "-*-times-medium-r-*-*-*-120-*-*-*-*-*-*",
+ "-*-lucida-medium-r-*-*-*-120-*-*-*-*-*-*",
+ "-*-helvetica-*-*-*-*-*-120-*-*-*-*-*-*",
+ "-*-courier-*-*-*-*-*-120-*-*-*-*-*-*",
+ "-*-times-*-*-*-*-*-120-*-*-*-*-*-*",
+ "-*-lucida-*-*-*-*-*-120-*-*-*-*-*-*",
+ "-*-helvetica-*-*-*-*-*-*-*-*-*-*-*-*",
+ "-*-courier-*-*-*-*-*-*-*-*-*-*-*-*",
+ "-*-times-*-*-*-*-*-*-*-*-*-*-*-*",
+ "-*-lucida-*-*-*-*-*-*-*-*-*-*-*-*",
+ "-*-fixed-*-*-*-*-*-*-*-*-*-*-*-*",
+ "6x13",
+ "7x13",
+ "8x13",
+ "9x15",
+ "fixed",
+ 0
+};
+
+// Returns TRUE if the font exists, FALSE otherwise
+static bool fontExists( const QString &fontName )
+{
+ int count;
+ char **fontNames = XListFonts( QPaintDevice::x11AppDisplay(),
+ (char*)fontName.latin1(), 32768, &count );
+ if ( fontNames ) XFreeFontNames( fontNames );
+
+ return count != 0;
+}
+
+/*!
+ Returns a "last resort" font name for the font matching algorithm.
+ This is used if the last resort family is not available. It will
+ always return a name, if necessary returning something like
+ "fixed" or "system".
+
+ The current implementation tries a wide variety of common fonts,
+ returning the first one it finds. The implementation may change
+ at any time, but this function will always return a string
+ containing something.
+
+ It is theoretically possible that there really isn't a
+ lastResortFont() in which case Qt will abort with an error
+ message. We have not been able to identify a case where this
+ happens. Please \link bughowto.html report it as a bug\endlink if
+ it does, preferably with a list of the fonts you have installed.
+
+ \sa lastResortFamily() rawName()
+*/
+QString QFont::lastResortFont() const
+{
+ static QString last;
+
+ // already found
+ if ( ! last.isNull() )
+ return last;
+
+ int i = 0;
+ const char* f;
+
+ while ( ( f = tryFonts[i] ) ) {
+ last = QString::fromLatin1( f );
+
+ if ( fontExists( last ) )
+ return last;
+
+ i++;
+ }
+
+#if defined(CHECK_NULL)
+ qFatal( "QFontPrivate::lastResortFont: Cannot find any reasonable font" );
+#endif
+
+ return last;
+}
+
+
+
+
+// **********************************************************************
+// QFontMetrics member methods
+// **********************************************************************
+
+int QFontMetrics::width( QChar ch ) const
+{
+ unsigned short uc = ch.unicode();
+ if ( uc < QFontEngineData::widthCacheSize &&
+ d->engineData && d->engineData->widthCache[ uc ] )
+ return d->engineData->widthCache[ uc ];
+
+ if ( ::category( ch ) == QChar::Mark_NonSpacing || qIsZeroWidthChar(ch.unicode()))
+ return 0;
+
+ QFont::Script script;
+ SCRIPT_FOR_CHAR( script, ch );
+
+ QFontEngine *engine = d->engineForScript( script );
+#ifdef QT_CHECK_STATE
+ Q_ASSERT( engine != 0 );
+#endif // QT_CHECK_STATE
+
+ glyph_t glyphs[8];
+ advance_t advances[8];
+ int nglyphs = 7;
+ engine->stringToCMap( &ch, 1, glyphs, advances, &nglyphs, FALSE );
+
+ // ### can nglyphs != 1 happen at all? Not currently I think
+ if ( uc < QFontEngineData::widthCacheSize && advances[0] > 0 && advances[0] < 0x100 )
+ d->engineData->widthCache[ uc ] = advances[0];
+
+ return advances[0];
+}
+
+
+int QFontMetrics::charWidth( const QString &str, int pos ) const
+{
+ if ( pos < 0 || pos > (int)str.length() )
+ return 0;
+
+ const QChar &ch = str.unicode()[ pos ];
+ if ( ch.unicode() < QFontEngineData::widthCacheSize &&
+ d->engineData && d->engineData->widthCache[ ch.unicode() ] )
+ return d->engineData->widthCache[ ch.unicode() ];
+
+ QFont::Script script;
+ SCRIPT_FOR_CHAR( script, ch );
+
+ int width;
+
+ if ( script >= QFont::Arabic && script <= QFont::Khmer ) {
+ // complex script shaping. Have to do some hard work
+ int from = QMAX( 0, pos - 8 );
+ int to = QMIN( (int)str.length(), pos + 8 );
+ QConstString cstr( str.unicode()+from, to-from);
+ QTextEngine layout( cstr.string(), d );
+ layout.itemize( QTextEngine::WidthOnly );
+ width = layout.width( pos-from, 1 );
+ } else if ( ::category( ch ) == QChar::Mark_NonSpacing || qIsZeroWidthChar(ch.unicode())) {
+ width = 0;
+ } else {
+ QFontEngine *engine = d->engineForScript( script );
+#ifdef QT_CHECK_STATE
+ Q_ASSERT( engine != 0 );
+#endif // QT_CHECK_STATE
+
+ glyph_t glyphs[8];
+ advance_t advances[8];
+ int nglyphs = 7;
+ engine->stringToCMap( &ch, 1, glyphs, advances, &nglyphs, FALSE );
+ width = advances[0];
+ }
+ if ( ch.unicode() < QFontEngineData::widthCacheSize && width > 0 && width < 0x100 )
+ d->engineData->widthCache[ ch.unicode() ] = width;
+ return width;
+}