Merge branch 'master' into features/drawer
This commit is contained in:
commit
3244e6e7e2
6
LICENSES
6
LICENSES
|
@ -36,3 +36,9 @@ c) Segoe-UI Fonts
|
|||
|
||||
Code: https://github.com/mrbvrz/segoe-ui-linux
|
||||
License: https://github.com/mrbvrz/segoe-ui-linux/blob/master/license.txt
|
||||
|
||||
d) Font Awesome
|
||||
|
||||
Code: https://fontawesome.com/
|
||||
SPDX-License-Identifier: OFL-1.1-RFN
|
||||
Copyright (c) 2024 Fonticons, Inc.
|
||||
|
|
|
@ -3,6 +3,7 @@ add_subdirectory(dials)
|
|||
add_subdirectory(dialogbuttons)
|
||||
add_subdirectory(fonts)
|
||||
add_subdirectory(gradients)
|
||||
add_subdirectory(iconbrowser)
|
||||
add_subdirectory(invoker)
|
||||
add_subdirectory(shadows)
|
||||
add_subdirectory(shapes)
|
||||
|
|
|
@ -0,0 +1,15 @@
|
|||
############################################################################
|
||||
# QSkinny - Copyright (C) The authors
|
||||
# SPDX-License-Identifier: BSD-3-Clause
|
||||
############################################################################
|
||||
|
||||
set(SOURCES
|
||||
GlyphListView.h GlyphListView.cpp
|
||||
main.cpp
|
||||
)
|
||||
|
||||
qt_add_resources(SOURCES
|
||||
fonts.qrc
|
||||
)
|
||||
|
||||
qsk_add_example(iconbrowser ${SOURCES})
|
|
@ -0,0 +1,120 @@
|
|||
/******************************************************************************
|
||||
* QSkinny - Copyright (C) The authors
|
||||
* SPDX-License-Identifier: BSD-3-Clause
|
||||
*****************************************************************************/
|
||||
|
||||
#include "GlyphListView.h"
|
||||
|
||||
#include <QskFunctions.h>
|
||||
#include <QskGraphic.h>
|
||||
#include <QskFontRole.h>
|
||||
|
||||
#include <QFontMetricsF>
|
||||
#include <QRawFont>
|
||||
|
||||
constexpr int glyphSize = 100;
|
||||
|
||||
GlyphListView::GlyphListView( QQuickItem* parentItem )
|
||||
: Inherited( parentItem )
|
||||
{
|
||||
}
|
||||
|
||||
GlyphListView::GlyphListView( const QString& fontName, QQuickItem* parentItem )
|
||||
: GlyphListView( parentItem )
|
||||
{
|
||||
setFont( QRawFont( fontName, 16 ) );
|
||||
setFontRoleHint( Text, QskFontRole::Title );
|
||||
}
|
||||
|
||||
void GlyphListView::setFontPath( const QString& fontPath )
|
||||
{
|
||||
setFont( QRawFont( fontPath, 16 ) );
|
||||
}
|
||||
|
||||
void GlyphListView::setFont( const QRawFont& font )
|
||||
{
|
||||
m_glyphTable.setIconFont( font );
|
||||
|
||||
const auto names = m_glyphTable.nameTable();
|
||||
|
||||
m_nameTable.clear();
|
||||
m_nameTable.reserve( names.size() );
|
||||
|
||||
m_maxNameWidth = 0;
|
||||
|
||||
{
|
||||
const QFontMetricsF fm( effectiveFont( Text ) );
|
||||
|
||||
for ( auto it = names.constBegin(); it != names.constEnd(); ++it )
|
||||
{
|
||||
m_nameTable.insert( it.value(), it.key() );
|
||||
|
||||
const qreal w = qskHorizontalAdvance( fm, it.key() );
|
||||
if ( w > m_maxNameWidth )
|
||||
m_maxNameWidth = w;
|
||||
}
|
||||
}
|
||||
|
||||
updateScrollableSize();
|
||||
update();
|
||||
}
|
||||
|
||||
QRawFont GlyphListView::font() const
|
||||
{
|
||||
return m_glyphTable.iconFont();
|
||||
}
|
||||
|
||||
int GlyphListView::rowCount() const
|
||||
{
|
||||
return m_glyphTable.glyphCount() - 1;
|
||||
}
|
||||
|
||||
int GlyphListView::columnCount() const
|
||||
{
|
||||
return 3;
|
||||
}
|
||||
|
||||
qreal GlyphListView::columnWidth( int col ) const
|
||||
{
|
||||
switch( col )
|
||||
{
|
||||
case 0:
|
||||
{
|
||||
const QFontMetricsF fm( effectiveFont( Text ) );
|
||||
return qskHorizontalAdvance( fm, "999999" );
|
||||
}
|
||||
|
||||
case 1:
|
||||
return glyphSize;
|
||||
|
||||
case 2:
|
||||
return m_maxNameWidth;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
qreal GlyphListView::rowHeight() const
|
||||
{
|
||||
return glyphSize + 20;
|
||||
}
|
||||
|
||||
QVariant GlyphListView::valueAt( int row, int col ) const
|
||||
{
|
||||
const auto glyphIndex = row + 1;
|
||||
switch( col )
|
||||
{
|
||||
case 0:
|
||||
return QVariant::fromValue( QString::number( glyphIndex ) );
|
||||
|
||||
case 1:
|
||||
return QVariant::fromValue( m_glyphTable.glyphGraphic( glyphIndex ) );
|
||||
|
||||
case 2:
|
||||
return QVariant::fromValue( m_nameTable.value( glyphIndex ) );
|
||||
}
|
||||
|
||||
return QVariant();
|
||||
}
|
||||
|
||||
#include "moc_GlyphListView.cpp"
|
|
@ -0,0 +1,38 @@
|
|||
/******************************************************************************
|
||||
* QSkinny - Copyright (C) The authors
|
||||
* SPDX-License-Identifier: BSD-3-Clause
|
||||
*****************************************************************************/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <QskListView.h>
|
||||
#include <QskGlyphTable.h>
|
||||
#include <QHash>
|
||||
|
||||
class GlyphListView : public QskListView
|
||||
{
|
||||
Q_OBJECT
|
||||
using Inherited = QskListView;
|
||||
|
||||
public:
|
||||
GlyphListView( QQuickItem* = nullptr);
|
||||
GlyphListView( const QString&, QQuickItem* = nullptr);
|
||||
|
||||
void setFontPath( const QString& );
|
||||
void setFont( const QRawFont& );
|
||||
QRawFont font() const;
|
||||
|
||||
int rowCount() const override;
|
||||
int columnCount() const override;
|
||||
|
||||
virtual qreal columnWidth( int col ) const override;
|
||||
virtual qreal rowHeight() const override;
|
||||
|
||||
QVariant valueAt( int row, int col ) const override;
|
||||
|
||||
private:
|
||||
QskGlyphTable m_glyphTable;
|
||||
|
||||
QHash< uint, QString > m_nameTable;
|
||||
int m_maxNameWidth = 0;
|
||||
};
|
|
@ -0,0 +1,11 @@
|
|||
<RCC>
|
||||
<qresource prefix="/iconfonts">
|
||||
|
||||
<file alias="FluentIcons.ttf">fonts/FluentSystemIcons-Resizable.ttf</file>
|
||||
<file alias="FontAwesomeIcons.otf">fonts/Font Awesome 6 Free-Regular-400.otf</file>
|
||||
<file alias="IcoMoonIcons.ttf">fonts/IcoMoon-Free.ttf</file>
|
||||
<file alias="MaterialIcons.ttf">fonts/MaterialSymbolsOutlined.ttf</file>
|
||||
<file alias="OctIcons.ttf">fonts/Octicons.ttf</file>
|
||||
|
||||
</qresource>
|
||||
</RCC>
|
Binary file not shown.
Binary file not shown.
Binary file not shown.
|
@ -0,0 +1,26 @@
|
|||
a) Material3
|
||||
|
||||
Code: https://github.com/google/material-design-icons
|
||||
SPDX-License-Identifier: Apache License 2.0
|
||||
|
||||
b) Fluent2
|
||||
|
||||
Code: https://github.com/microsoft/fluentui-system-icons
|
||||
SPDX-License-Identifier: MIT License
|
||||
|
||||
c) Font Awesome
|
||||
|
||||
Code: https://fontawesome.com/
|
||||
SPDX-License-Identifier: OFL-1.1-RFN
|
||||
Copyright (c) 2024 Fonticons, Inc.
|
||||
|
||||
d) IcoMoon
|
||||
|
||||
Code: https://github.com/Keyamoon/IcoMoon-Free
|
||||
SPDX-License-Identifier: CC-BY-4.0
|
||||
|
||||
e) Octicons
|
||||
|
||||
Code: https://github.com/primer/octicons
|
||||
SPDX-License-Identifier: MIT License
|
||||
|
Binary file not shown.
Binary file not shown.
|
@ -0,0 +1,100 @@
|
|||
/******************************************************************************
|
||||
* QSkinny - Copyright (C) The authors
|
||||
* SPDX-License-Identifier: BSD-3-Clause
|
||||
*****************************************************************************/
|
||||
|
||||
#include "GlyphListView.h"
|
||||
|
||||
#include <SkinnyShortcut.h>
|
||||
|
||||
#include <QskMainView.h>
|
||||
#include <QskFocusIndicator.h>
|
||||
#include <QskObjectCounter.h>
|
||||
#include <QskWindow.h>
|
||||
#include <QskComboBox.h>
|
||||
|
||||
#include <QGuiApplication>
|
||||
#include <QRawFont>
|
||||
#include <QDir>
|
||||
|
||||
namespace
|
||||
{
|
||||
class Header : public QskLinearBox
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
Header( QQuickItem* parent = nullptr )
|
||||
: QskLinearBox( Qt::Horizontal, parent )
|
||||
{
|
||||
setPaddingHint( QskBox::Panel, 5 );
|
||||
|
||||
initSizePolicy( QskSizePolicy::Ignored, QskSizePolicy::Fixed );
|
||||
setPanel( true );
|
||||
|
||||
m_comboBox = new QskComboBox( this );
|
||||
|
||||
const auto entries = QDir( ":iconfonts" ).entryInfoList();
|
||||
for ( const auto& entry : entries )
|
||||
m_comboBox->addOption( QUrl(), entry.absoluteFilePath() );
|
||||
|
||||
m_comboBox->setCurrentIndex( 0 );
|
||||
|
||||
connect( m_comboBox, &QskComboBox::currentIndexChanged,
|
||||
this, &Header::comboBoxChanged );
|
||||
}
|
||||
|
||||
QString fontPath() const { return m_comboBox->currentText(); }
|
||||
|
||||
Q_SIGNALS:
|
||||
void fontChanged( const QString& );
|
||||
|
||||
private:
|
||||
void comboBoxChanged() { Q_EMIT fontChanged( fontPath() ); }
|
||||
|
||||
QskComboBox* m_comboBox;
|
||||
};
|
||||
|
||||
class MainView : public QskMainView
|
||||
{
|
||||
public:
|
||||
MainView( QQuickItem* parent = nullptr )
|
||||
: QskMainView( parent )
|
||||
{
|
||||
auto listView = new GlyphListView( this );
|
||||
auto header = new Header( this );
|
||||
|
||||
setHeader( header );
|
||||
setBody( listView );
|
||||
|
||||
listView->setFontPath( header->fontPath() );
|
||||
|
||||
connect( header, &Header::fontChanged,
|
||||
listView, &GlyphListView::setFontPath );
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
int main( int argc, char* argv[] )
|
||||
{
|
||||
#ifdef ITEM_STATISTICS
|
||||
QskObjectCounter counter( true );
|
||||
#endif
|
||||
|
||||
QGuiApplication app( argc, argv );
|
||||
|
||||
SkinnyShortcut::enable( SkinnyShortcut::AllShortcuts );
|
||||
|
||||
auto mainView = new QskMainView();
|
||||
mainView->setBody( new GlyphListView( ":/fonts/Octicons.ttf" ) );
|
||||
|
||||
QskWindow window;
|
||||
window.addItem( new MainView() );
|
||||
window.addItem( new QskFocusIndicator() );
|
||||
window.resize( 600, 400 );
|
||||
window.show();
|
||||
|
||||
return app.exec();
|
||||
}
|
||||
|
||||
#include "main.moc"
|
|
@ -77,6 +77,8 @@ list(APPEND PRIVATE_HEADERS
|
|||
|
||||
list(APPEND HEADERS
|
||||
graphic/QskColorFilter.h
|
||||
graphic/QskGlyphGraphicProvider.h
|
||||
graphic/QskGlyphTable.h
|
||||
graphic/QskGraphic.h
|
||||
graphic/QskGraphicImageProvider.h
|
||||
graphic/QskGraphicIO.h
|
||||
|
@ -91,6 +93,8 @@ list(APPEND HEADERS
|
|||
|
||||
list(APPEND SOURCES
|
||||
graphic/QskColorFilter.cpp
|
||||
graphic/QskGlyphGraphicProvider.cpp
|
||||
graphic/QskGlyphTable.cpp
|
||||
graphic/QskGraphic.cpp
|
||||
graphic/QskGraphicImageProvider.cpp
|
||||
graphic/QskGraphicIO.cpp
|
||||
|
|
|
@ -0,0 +1,84 @@
|
|||
/******************************************************************************
|
||||
* QSkinny - Copyright (C) The authors
|
||||
* SPDX-License-Identifier: BSD-3-Clause
|
||||
*****************************************************************************/
|
||||
|
||||
#include "QskGlyphGraphicProvider.h"
|
||||
#include "QskGraphic.h"
|
||||
#include "QskGlyphTable.h"
|
||||
|
||||
#include <qrawfont.h>
|
||||
#include <qpainter.h>
|
||||
#include <qpainterpath.h>
|
||||
|
||||
class QskGlyphGraphicProvider::PrivateData
|
||||
{
|
||||
public:
|
||||
QskGlyphTable glyphTable;
|
||||
};
|
||||
|
||||
QskGlyphGraphicProvider::QskGlyphGraphicProvider( QObject* parent )
|
||||
: QskGraphicProvider( parent )
|
||||
, m_data( new PrivateData )
|
||||
{
|
||||
}
|
||||
|
||||
QskGlyphGraphicProvider::~QskGlyphGraphicProvider()
|
||||
{
|
||||
}
|
||||
|
||||
void QskGlyphGraphicProvider::setIconFont( const QRawFont& font )
|
||||
{
|
||||
m_data->glyphTable.setIconFont( font );
|
||||
}
|
||||
|
||||
QRawFont QskGlyphGraphicProvider::iconFont() const
|
||||
{
|
||||
return m_data->glyphTable.iconFont();
|
||||
}
|
||||
|
||||
QskGraphic QskGlyphGraphicProvider::glyphGraphic( uint index ) const
|
||||
{
|
||||
return m_data->glyphTable.glyphGraphic( index );
|
||||
}
|
||||
|
||||
const QskGraphic* QskGlyphGraphicProvider::loadGraphic( const QString& key ) const
|
||||
{
|
||||
if ( const auto index = glyphIndex( key ) )
|
||||
{
|
||||
const auto graphic = glyphGraphic( index );
|
||||
if ( !graphic.isNull() )
|
||||
return new QskGraphic( graphic );
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
uint QskGlyphGraphicProvider::glyphIndex( const QString& key ) const
|
||||
{
|
||||
const auto& table = m_data->glyphTable;
|
||||
|
||||
if ( ( table.glyphCount() > 0 ) && !key.isEmpty() )
|
||||
{
|
||||
if ( key.startsWith( '#' ) )
|
||||
{
|
||||
bool ok;
|
||||
const auto glyphIndex = key.toUInt( &ok );
|
||||
if ( ok )
|
||||
return glyphIndex;
|
||||
}
|
||||
else if ( key.startsWith( "U+" ) )
|
||||
{
|
||||
bool ok;
|
||||
const char32_t code = key.mid( 2 ).toUInt( &ok, 16 );
|
||||
if ( ok )
|
||||
return table.codeToIndex( code );
|
||||
}
|
||||
else
|
||||
{
|
||||
return table.nameToIndex( key );
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
|
@ -0,0 +1,35 @@
|
|||
/******************************************************************************
|
||||
* QSkinny - Copyright (C) The authors
|
||||
* SPDX-License-Identifier: BSD-3-Clause
|
||||
*****************************************************************************/
|
||||
|
||||
#ifndef QSK_GLYPH_GRAPHIC_PROVIDER_H
|
||||
#define QSK_GLYPH_GRAPHIC_PROVIDER_H
|
||||
|
||||
#include "QskGraphicProvider.h"
|
||||
|
||||
class QRawFont;
|
||||
|
||||
class QSK_EXPORT QskGlyphGraphicProvider : public QskGraphicProvider
|
||||
{
|
||||
using Inherited = QskGraphicProvider;
|
||||
|
||||
public:
|
||||
QskGlyphGraphicProvider( QObject* parent = nullptr );
|
||||
~QskGlyphGraphicProvider() override;
|
||||
|
||||
void setIconFont( const QRawFont& );
|
||||
QRawFont iconFont() const;
|
||||
|
||||
QskGraphic glyphGraphic( uint glyphIndex ) const;
|
||||
|
||||
protected:
|
||||
const QskGraphic* loadGraphic( const QString& ) const override final;
|
||||
virtual uint glyphIndex( const QString& ) const;
|
||||
|
||||
private:
|
||||
class PrivateData;
|
||||
std::unique_ptr< PrivateData > m_data;
|
||||
};
|
||||
|
||||
#endif
|
|
@ -0,0 +1,489 @@
|
|||
/******************************************************************************
|
||||
* QSkinny - Copyright (C) The authors
|
||||
* SPDX-License-Identifier: BSD-3-Clause
|
||||
*****************************************************************************/
|
||||
|
||||
#include "QskGlyphTable.h"
|
||||
#include "QskGraphic.h"
|
||||
#include "QskInternalMacros.h"
|
||||
|
||||
#include <qrawfont.h>
|
||||
#include <qpainter.h>
|
||||
#include <qpainterpath.h>
|
||||
#include <qendian.h>
|
||||
|
||||
QSK_QT_PRIVATE_BEGIN
|
||||
#include <private/qrawfont_p.h>
|
||||
QSK_QT_PRIVATE_END
|
||||
|
||||
typedef QHash< QString, uint > GlyphNameTable;
|
||||
|
||||
/*
|
||||
The "parsers" below do no validation checks as the font has
|
||||
already been validated by QRawFont
|
||||
|
||||
Hope QRawFont will extend its API some day ( the underlying
|
||||
font libraries do support glyph names ) and we can remove the nasty
|
||||
code below. see https://bugreports.qt.io/browse/QTBUG-132629
|
||||
*/
|
||||
namespace PostTableParser
|
||||
{
|
||||
// https://learn.microsoft.com/en-us/typography/opentype/spec/post
|
||||
|
||||
static inline QString toString( const uint8_t* s )
|
||||
{
|
||||
// Pascal string. Valid characters are: [A–Z] [a–z] [0–9] '.' '_'
|
||||
return QString::fromUtf8(
|
||||
reinterpret_cast< const char* > ( s + 1 ), *s );
|
||||
}
|
||||
|
||||
static GlyphNameTable glyphNames( const QByteArray& blob )
|
||||
{
|
||||
GlyphNameTable names;
|
||||
|
||||
const auto* glyphData = reinterpret_cast< const uint16_t* >( blob.constData() + 32 );
|
||||
if ( const auto nglyphs = qFromBigEndian( *glyphData++ ) )
|
||||
{
|
||||
QVarLengthArray< const uint8_t* > strings;
|
||||
strings.reserve( nglyphs );
|
||||
|
||||
const auto from = reinterpret_cast< const uint8_t* >( glyphData + nglyphs );
|
||||
const auto to = reinterpret_cast< const uint8_t* >( blob.data() + blob.size() );
|
||||
|
||||
for ( auto s = from; s < to; s += *s + 1 )
|
||||
strings += s;
|
||||
|
||||
for ( int i = 0; i < nglyphs; i++ )
|
||||
{
|
||||
const int idx = qFromBigEndian( glyphData[i] ) - 258;
|
||||
|
||||
if ( idx >= 0 )
|
||||
names.insert( toString( strings[idx] ), i );
|
||||
}
|
||||
}
|
||||
|
||||
return names;
|
||||
}
|
||||
}
|
||||
|
||||
namespace CFFTableParser
|
||||
{
|
||||
// https://adobe-type-tools.github.io/font-tech-notes/pdfs/5176.CFF.pdf
|
||||
|
||||
static uint32_t offsetAt( const uint8_t* d, int size )
|
||||
{
|
||||
switch (size)
|
||||
{
|
||||
case 0:
|
||||
return 0;
|
||||
case 1:
|
||||
return d[0];
|
||||
case 2:
|
||||
return ( d[0] << 8 ) | d[1];
|
||||
case 3:
|
||||
return ( d[0] << 16 ) | ( d[1] << 8 ) | d[2];
|
||||
default:
|
||||
return ( d[0] << 24 ) | ( d[1] << 16 ) | ( d[2] << 8 ) | d[3];
|
||||
}
|
||||
}
|
||||
|
||||
static int indexDataSize( const uint8_t* d )
|
||||
{
|
||||
const int nitems = ( d[0] << 8 ) | d[1];
|
||||
if ( nitems == 0 )
|
||||
return 2;
|
||||
|
||||
const int size = d[2];
|
||||
|
||||
const uint8_t* lx = d + 3 + nitems * size;
|
||||
return lx + size - 1 + offsetAt( lx, size ) - d;
|
||||
}
|
||||
|
||||
static const uint8_t* indexData( const uint8_t* d )
|
||||
{
|
||||
const int nitems = ( d[0] << 8 ) | d[1];
|
||||
d += 2;
|
||||
|
||||
if ( nitems == 0 )
|
||||
return d;
|
||||
|
||||
const int size = d[0];
|
||||
|
||||
const uint8_t* _contents = d + ( nitems + 1 ) * size;
|
||||
return _contents + offsetAt(d + 1, size);
|
||||
}
|
||||
|
||||
static const uint8_t* skipHeader( const uint8_t* d )
|
||||
{
|
||||
return d + d[2];
|
||||
}
|
||||
|
||||
static const uint8_t* skipIndex( const uint8_t* d )
|
||||
{
|
||||
return d + indexDataSize( d );
|
||||
}
|
||||
|
||||
static QVector< int > stringIds( const uint8_t* data, int nglyphs )
|
||||
{
|
||||
const int format = data[0];
|
||||
|
||||
QVector< int > _sids;
|
||||
_sids += 0;
|
||||
|
||||
const uint8_t* p = data + 1;
|
||||
switch( format )
|
||||
{
|
||||
case 0:
|
||||
{
|
||||
for (; _sids.size() < nglyphs; p += 2)
|
||||
{
|
||||
int sid = ( p[0] << 8 ) | p[1];
|
||||
_sids += sid;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case 1:
|
||||
{
|
||||
for (; _sids.size() < nglyphs; p += 3)
|
||||
{
|
||||
const int sid = ( p[0] << 8 ) | p[1];
|
||||
const int n = p[2];
|
||||
|
||||
for ( int i = 0; i <= n; i++ )
|
||||
_sids += sid + i;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case 2:
|
||||
{
|
||||
for (; _sids.size() < nglyphs; p += 4)
|
||||
{
|
||||
const int sid = ( p[0] << 8 ) | p[1];
|
||||
const int n = ( p[2] << 8 ) | p[3];
|
||||
|
||||
for ( int i = 0; i <= n; i++ )
|
||||
_sids += sid + i;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return _sids;
|
||||
}
|
||||
|
||||
static int dictValue( const uint8_t* d, int op )
|
||||
{
|
||||
/*
|
||||
see https://adobe-type-tools.github.io/font-tech-notes/pdfs/5176.CFF.pdf
|
||||
We are intersted in the offset ( operator 15 ).
|
||||
*/
|
||||
|
||||
const uint8_t* data = indexData( d );
|
||||
|
||||
int value = 0;
|
||||
|
||||
Q_FOREVER
|
||||
{
|
||||
// operand
|
||||
const auto valueType = data[0];
|
||||
|
||||
if ( valueType == 28 )
|
||||
{
|
||||
value = ( data[1] << 8 ) | data[2];
|
||||
data += 3;
|
||||
}
|
||||
else if ( valueType == 29 )
|
||||
{
|
||||
value = ( data[1] << 24 ) | ( data[2] << 16 )
|
||||
| ( data[3] << 8 ) | data[4];
|
||||
|
||||
data += 5;
|
||||
}
|
||||
else if ( valueType == 30 )
|
||||
{
|
||||
/*
|
||||
Assuming, that an offset is never a double
|
||||
we skip the operand without reading it
|
||||
*/
|
||||
while( ++data )
|
||||
{
|
||||
const int b = *data;
|
||||
if ( ( b & 0x0f ) == 0x0f || ( b & 0xf0 ) == 0xf0 )
|
||||
break; // 0xf nibble indicating the end
|
||||
}
|
||||
|
||||
data++;
|
||||
}
|
||||
else if ( valueType >= 31 && valueType <= 246 )
|
||||
{
|
||||
value = data[0] - 139;
|
||||
data++;
|
||||
}
|
||||
else if ( valueType >= 247 && valueType <= 250 )
|
||||
{
|
||||
value = ( ( data[0] - 247 ) << 8 ) + data[1] + 108;
|
||||
data += 2;
|
||||
}
|
||||
else if ( valueType >= 251 && valueType <= 254 )
|
||||
{
|
||||
value = -( ( data[0] - 251 ) << 8 ) - data[1] - 108;
|
||||
data += 2;
|
||||
}
|
||||
|
||||
// operator
|
||||
|
||||
if ( op == data[0] )
|
||||
return value;
|
||||
|
||||
data += ( data[0] == 12 ) ? 2 : 1;
|
||||
}
|
||||
|
||||
return 0; // default value
|
||||
}
|
||||
|
||||
class StringTable
|
||||
{
|
||||
public:
|
||||
StringTable( const uint8_t* data )
|
||||
{
|
||||
const int nitems = ( data[0] << 8 ) | data[1];
|
||||
if ( nitems == 0 )
|
||||
{
|
||||
_contents = data + 2;
|
||||
_offset = nullptr;
|
||||
_offsize = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
_offsize = data[2];
|
||||
_offset = data + 3;
|
||||
|
||||
_contents = _offset + nitems * _offsize + _offsize - 1;
|
||||
}
|
||||
}
|
||||
|
||||
inline QString operator[]( int which ) const
|
||||
{
|
||||
const auto x = _offset + which * _offsize;
|
||||
|
||||
const auto d1 = _contents + offsetAt(x, _offsize);
|
||||
const auto d2 = _contents + offsetAt(x + _offsize, _offsize);
|
||||
|
||||
return QString::fromUtf8(
|
||||
reinterpret_cast< const char* >( d1 ), d2 - d1 );
|
||||
}
|
||||
|
||||
private:
|
||||
const uint8_t* _contents = nullptr;
|
||||
const uint8_t* _offset = nullptr;
|
||||
int _offsize = -1;
|
||||
};
|
||||
|
||||
static GlyphNameTable glyphNames( const QByteArray& blob, int glyphCount )
|
||||
{
|
||||
auto data = reinterpret_cast< const uint8_t* >( blob.constData() );
|
||||
|
||||
auto nameIndex = skipHeader( data );
|
||||
auto dictIndex = skipIndex( nameIndex );
|
||||
auto stringIndex = skipIndex( dictIndex );
|
||||
|
||||
auto charset = data + dictValue( dictIndex, 15 ); // 15: charset offset
|
||||
|
||||
const QVector< int > sids = stringIds( charset, glyphCount );
|
||||
|
||||
const StringTable table( stringIndex );
|
||||
|
||||
GlyphNameTable names;
|
||||
|
||||
for ( int i = 0; i < glyphCount; i++ )
|
||||
{
|
||||
/*
|
||||
The first 391 SIDs are reserved for standard strings
|
||||
( Appendix A ) that are not from the charset table.
|
||||
*/
|
||||
|
||||
const auto idx = sids[i] - 391;
|
||||
|
||||
if ( idx >= 0 )
|
||||
names.insert( table[idx], i );
|
||||
}
|
||||
|
||||
return names;
|
||||
}
|
||||
}
|
||||
|
||||
static uint qskGlyphCount( const QRawFont& font )
|
||||
{
|
||||
/*
|
||||
we could also read the count from the "maxp" table:
|
||||
https://learn.microsoft.com/en-us/typography/opentype/spec/maxp
|
||||
*/
|
||||
|
||||
const auto fontEngine = QRawFontPrivate::get( font )->fontEngine;
|
||||
return fontEngine ? fontEngine->glyphCount() : 0;
|
||||
}
|
||||
|
||||
static GlyphNameTable qskGlyphNameTable( const QRawFont& font )
|
||||
{
|
||||
const auto count = qskGlyphCount( font );
|
||||
if ( count > 0 )
|
||||
{
|
||||
/*
|
||||
The Compact Font Format ( CFF ) table has been introduced with
|
||||
the OpenType specification. For not complying fonts the names
|
||||
might be found in the Post table
|
||||
*/
|
||||
auto blob = font.fontTable( "CFF ");
|
||||
if ( !blob.isEmpty() )
|
||||
return CFFTableParser::glyphNames( blob, count );
|
||||
|
||||
blob = font.fontTable( "post" );
|
||||
if ( !blob.isEmpty() )
|
||||
return PostTableParser::glyphNames( blob );
|
||||
}
|
||||
|
||||
return GlyphNameTable();
|
||||
}
|
||||
|
||||
static inline quint32 qskGlyphIndex( const QRawFont& font, char32_t ucs4 )
|
||||
{
|
||||
if ( qskGlyphCount( font ) == 0 )
|
||||
return 0;
|
||||
|
||||
const auto idxs = font.glyphIndexesForString(
|
||||
QString::fromUcs4( &ucs4, 1 ) );
|
||||
|
||||
// fingers crossed: icon fonts will map code points to single glyphs only
|
||||
Q_ASSERT( idxs.size() == 1 );
|
||||
|
||||
return idxs.size() == 1 ? idxs[0] : 0;
|
||||
}
|
||||
|
||||
class QskGlyphTable::PrivateData
|
||||
{
|
||||
public:
|
||||
inline const GlyphNameTable& glyphNameTable() const
|
||||
{
|
||||
if ( !validNames )
|
||||
{
|
||||
auto that = const_cast< PrivateData* >( this );
|
||||
that->nameTable = qskGlyphNameTable( font );
|
||||
that->validNames = true;
|
||||
}
|
||||
|
||||
return nameTable;
|
||||
}
|
||||
|
||||
QRawFont font;
|
||||
GlyphNameTable nameTable;
|
||||
bool validNames = false;
|
||||
};
|
||||
|
||||
QskGlyphTable::QskGlyphTable()
|
||||
: m_data( new PrivateData )
|
||||
{
|
||||
}
|
||||
|
||||
QskGlyphTable::QskGlyphTable( const QRawFont& font )
|
||||
: QskGlyphTable()
|
||||
{
|
||||
m_data->font = font;
|
||||
}
|
||||
|
||||
QskGlyphTable::QskGlyphTable( const QskGlyphTable& other )
|
||||
: QskGlyphTable()
|
||||
{
|
||||
*m_data = *other.m_data;
|
||||
}
|
||||
|
||||
QskGlyphTable::~QskGlyphTable()
|
||||
{
|
||||
}
|
||||
|
||||
QskGlyphTable& QskGlyphTable::operator=( const QskGlyphTable& other )
|
||||
{
|
||||
if ( m_data != other.m_data )
|
||||
*m_data = *other.m_data;
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
void QskGlyphTable::setIconFont( const QRawFont& font )
|
||||
{
|
||||
if ( font != m_data->font )
|
||||
{
|
||||
m_data->font = font;
|
||||
m_data->nameTable.clear();
|
||||
m_data->validNames = false;
|
||||
}
|
||||
}
|
||||
|
||||
QRawFont QskGlyphTable::iconFont() const
|
||||
{
|
||||
return m_data->font;
|
||||
}
|
||||
|
||||
QPainterPath QskGlyphTable::glyphPath( uint glyphIndex ) const
|
||||
{
|
||||
QPainterPath path;
|
||||
|
||||
/*
|
||||
Unfortunately QRawFont::pathForGlyph runs into failing checks
|
||||
when being called from a different thread - f.e the scene graph thread.
|
||||
So we need to bypass QRawFont and retrieve from its fontEngine.
|
||||
*/
|
||||
if ( auto fontEngine = QRawFontPrivate::get( m_data->font )->fontEngine )
|
||||
{
|
||||
QFixedPoint position;
|
||||
quint32 idx = glyphIndex;
|
||||
|
||||
fontEngine->addGlyphsToPath( &idx, &position, 1, &path, {} );
|
||||
}
|
||||
|
||||
return path;
|
||||
}
|
||||
|
||||
QskGraphic QskGlyphTable::glyphGraphic( uint glyphIndex ) const
|
||||
{
|
||||
QskGraphic graphic;
|
||||
|
||||
if ( glyphIndex > 0 && qskGlyphCount( m_data->font ) > 0 )
|
||||
{
|
||||
const auto path = glyphPath( glyphIndex );
|
||||
|
||||
if ( !path.isEmpty() )
|
||||
{
|
||||
QPainter painter( &graphic );
|
||||
painter.setRenderHint( QPainter::Antialiasing, true );
|
||||
painter.fillPath( path, Qt::black );
|
||||
}
|
||||
}
|
||||
|
||||
return graphic;
|
||||
}
|
||||
|
||||
uint QskGlyphTable::glyphCount() const
|
||||
{
|
||||
return qskGlyphCount( m_data->font );
|
||||
}
|
||||
|
||||
uint QskGlyphTable::codeToIndex( char32_t ucs4 ) const
|
||||
{
|
||||
return qskGlyphIndex( m_data->font, ucs4 );
|
||||
}
|
||||
|
||||
uint QskGlyphTable::nameToIndex( const QString& name ) const
|
||||
{
|
||||
/*
|
||||
Names/Codes that do not correspond to any glyph in should
|
||||
be mapped to glyph index 0.
|
||||
|
||||
see https://learn.microsoft.com/en-us/typography/opentype/spec/cmap
|
||||
*/
|
||||
return m_data->glyphNameTable().value( name.toLatin1(), 0 );
|
||||
}
|
||||
|
||||
QHash< QString, uint > QskGlyphTable::nameTable() const
|
||||
{
|
||||
return m_data->glyphNameTable();
|
||||
}
|
|
@ -0,0 +1,72 @@
|
|||
/******************************************************************************
|
||||
* QSkinny - Copyright (C) The authors
|
||||
* SPDX-License-Identifier: BSD-3-Clause
|
||||
*****************************************************************************/
|
||||
|
||||
#ifndef QSK_GLYPH_TABLE_H
|
||||
#define QSK_GLYPH_TABLE_H
|
||||
|
||||
#include "QskGlobal.h"
|
||||
#include <memory>
|
||||
|
||||
class QskGraphic;
|
||||
class QRawFont;
|
||||
class QString;
|
||||
class QPainterPath;
|
||||
class QByteArray;
|
||||
class QChar;
|
||||
|
||||
template< typename Key, typename T > class QHash;
|
||||
|
||||
class QSK_EXPORT QskGlyphTable
|
||||
{
|
||||
public:
|
||||
QskGlyphTable();
|
||||
QskGlyphTable( const QRawFont& );
|
||||
QskGlyphTable( const QskGlyphTable& );
|
||||
|
||||
~QskGlyphTable();
|
||||
|
||||
QskGlyphTable& operator=( const QskGlyphTable& );
|
||||
|
||||
bool isValid() const;
|
||||
|
||||
void setIconFont( const QRawFont& );
|
||||
QRawFont iconFont() const;
|
||||
|
||||
uint glyphCount() const;
|
||||
|
||||
QPainterPath glyphPath( uint glyphIndex ) const;
|
||||
QskGraphic glyphGraphic( uint glyphIndex ) const;
|
||||
|
||||
/*
|
||||
Most icon fonts use code points from the Unicode Private Use Areas (PUA)
|
||||
( see https://en.wikipedia.org/wiki/Private_Use_Areas ):
|
||||
|
||||
- [0x00e000, 0x00f8ff]
|
||||
- [0x0f0000, 0x0ffffd]
|
||||
- [0x100000, 0x10fffd]
|
||||
|
||||
Note that QChar is 16-bit entity only that does not cover the higher PUA blocks.
|
||||
*/
|
||||
uint codeToIndex( char32_t ) const;
|
||||
|
||||
/*
|
||||
True/OpenType fonts often include a table with glyph names, that can be used
|
||||
instead of the glyph index
|
||||
*/
|
||||
uint nameToIndex( const QString& ) const;
|
||||
|
||||
QHash< QString, uint > nameTable() const;
|
||||
|
||||
private:
|
||||
class PrivateData;
|
||||
std::unique_ptr< PrivateData > m_data;
|
||||
};
|
||||
|
||||
inline bool QskGlyphTable::isValid() const
|
||||
{
|
||||
return glyphCount() > 0;
|
||||
}
|
||||
|
||||
#endif
|
Loading…
Reference in New Issue