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
|
Code: https://github.com/mrbvrz/segoe-ui-linux
|
||||||
License: https://github.com/mrbvrz/segoe-ui-linux/blob/master/license.txt
|
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(dialogbuttons)
|
||||||
add_subdirectory(fonts)
|
add_subdirectory(fonts)
|
||||||
add_subdirectory(gradients)
|
add_subdirectory(gradients)
|
||||||
|
add_subdirectory(iconbrowser)
|
||||||
add_subdirectory(invoker)
|
add_subdirectory(invoker)
|
||||||
add_subdirectory(shadows)
|
add_subdirectory(shadows)
|
||||||
add_subdirectory(shapes)
|
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
|
list(APPEND HEADERS
|
||||||
graphic/QskColorFilter.h
|
graphic/QskColorFilter.h
|
||||||
|
graphic/QskGlyphGraphicProvider.h
|
||||||
|
graphic/QskGlyphTable.h
|
||||||
graphic/QskGraphic.h
|
graphic/QskGraphic.h
|
||||||
graphic/QskGraphicImageProvider.h
|
graphic/QskGraphicImageProvider.h
|
||||||
graphic/QskGraphicIO.h
|
graphic/QskGraphicIO.h
|
||||||
|
@ -91,6 +93,8 @@ list(APPEND HEADERS
|
||||||
|
|
||||||
list(APPEND SOURCES
|
list(APPEND SOURCES
|
||||||
graphic/QskColorFilter.cpp
|
graphic/QskColorFilter.cpp
|
||||||
|
graphic/QskGlyphGraphicProvider.cpp
|
||||||
|
graphic/QskGlyphTable.cpp
|
||||||
graphic/QskGraphic.cpp
|
graphic/QskGraphic.cpp
|
||||||
graphic/QskGraphicImageProvider.cpp
|
graphic/QskGraphicImageProvider.cpp
|
||||||
graphic/QskGraphicIO.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