Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support img tags in HTML label text #58716

Merged
merged 4 commits into from
Sep 28, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions python/PyQt6/core/auto_additions/qgsabstractcontentcache.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@
pass
try:
QgsAbstractContentCacheBase.__attribute_docs__ = {'remoteContentFetched': 'Emitted when the cache has finished retrieving content from a remote ``url``.\n'}
QgsAbstractContentCacheBase.parseBase64DataUrl = staticmethod(QgsAbstractContentCacheBase.parseBase64DataUrl)
QgsAbstractContentCacheBase.isBase64Data = staticmethod(QgsAbstractContentCacheBase.isBase64Data)
QgsAbstractContentCacheBase.__signal_arguments__ = {'remoteContentFetched': ['url: str']}
except NameError:
pass
23 changes: 23 additions & 0 deletions python/PyQt6/core/auto_generated/qgsabstractcontentcache.sip.in
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,29 @@ Required because template based class (such as :py:class:`QgsAbstractContentCach
QgsAbstractContentCacheBase( QObject *parent );
%Docstring
Constructor for QgsAbstractContentCacheBase, with the specified ``parent`` object.
%End

static bool parseBase64DataUrl( const QString &path, QString *mimeType /Out/ = 0, QString *data /Out/ = 0 );
%Docstring
Parses a ``path`` to determine if it represents a base 64 encoded HTML data URL, and if so, extracts the components
of the URL.

Data URLs are of the form ``data:[<mediatype>;]base64,<data>``.

:param path: path to test

:return: - ``True`` if ``path`` is a base 64 encoded data URL
- mimeType: the extracted mime type if the ``path`` is a data URL
- data: the extracted base64 data if the ``path`` is a data URL

.. versionadded:: 3.40
%End

static bool isBase64Data( const QString &path );
%Docstring
Returns ``True`` if ``path`` represents base64 encoded data.

.. versionadded:: 3.40
%End

signals:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -262,6 +262,62 @@ Returns whether the format has overline enabled.
Sets whether the format has overline ``enabled``.

.. seealso:: :py:func:`overline`
%End

QString imagePath() const;
%Docstring
Returns the path to the image to render, if the format applies to a document image fragment.

.. seealso:: :py:func:`QgsTextFragment.isImage`

.. seealso:: :py:func:`imageSize`

.. seealso:: :py:func:`setImagePath`

.. versionadded:: 3.40
%End

void setImagePath( const QString &path );
%Docstring
Sets the ``path`` to the image to render, if the format applies to a document image fragment.

.. seealso:: :py:func:`QgsTextFragment.isImage`

.. seealso:: :py:func:`setImageSize`

.. seealso:: :py:func:`imagePath`

.. versionadded:: 3.40
%End

QSizeF imageSize() const;
%Docstring
Returns the image size, if the format applies to a document image fragment.

The image size is always considered to be in :py:class:`Qgis`.RenderUnit.Points.

.. seealso:: :py:func:`QgsTextFragment.isImage`

.. seealso:: :py:func:`imagePath`

.. seealso:: :py:func:`setImageSize`

.. versionadded:: 3.40
%End

void setImageSize( const QSizeF &size );
%Docstring
Sets the image ``size``, if the format applies to a document image fragment.

The image size is always considered to be in :py:class:`Qgis`.RenderUnit.Points.

.. seealso:: :py:func:`QgsTextFragment.isImage`

.. seealso:: :py:func:`setImagePath`

.. seealso:: :py:func:`imageSize`

.. versionadded:: 3.40
%End

bool hasVerticalAlignmentSet() const;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -99,6 +99,13 @@ Returns the vertical offset from a text block's baseline which should be applied
to the fragment at the specified index within that block.

.. versionadded:: 3.30
%End

double fragmentFixedHeight( int blockIndex, int fragmentIndex, Qgis::TextLayoutMode mode ) const;
%Docstring
Returns the fixed height of the fragment at the specified block and fragment index, or -1 if the fragment does not have a fixed height.

.. versionadded:: 3.40
%End

double verticalOrientationXOffset( int blockIndex ) const;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,13 @@
class QgsTextFragment
{
%Docstring(signature="appended")
Stores a fragment of text along with formatting overrides to be used when rendering the fragment.
Stores a fragment of document along with formatting overrides to be used when rendering the fragment.

Text fragments consist of either a block of text or another atomic component of a document (such as an image).

Each fragment has an associated :py:func:`~characterFormat`, which specifies the text formatting overrides
to use when rendering the fragment. Additionally, the :py:func:`~characterFormat` may contain properties
for other fragment types, such as image paths and sizes for image fragments.

.. warning::

Expand Down Expand Up @@ -77,6 +83,13 @@ Returns the character formatting for the fragment.
Sets the character ``format`` for the fragment.

.. seealso:: :py:func:`characterFormat`
%End

bool isImage() const;
%Docstring
Returns ``True`` if the fragment represents an image.

.. versionadded:: 3.40
%End

double horizontalAdvance( const QFont &font, const QgsRenderContext &context, bool fontHasBeenUpdatedForFragment = false, double scaleFactor = 1.0 ) const;
Expand Down
2 changes: 2 additions & 0 deletions python/core/auto_additions/qgsabstractcontentcache.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@
pass
try:
QgsAbstractContentCacheBase.__attribute_docs__ = {'remoteContentFetched': 'Emitted when the cache has finished retrieving content from a remote ``url``.\n'}
QgsAbstractContentCacheBase.parseBase64DataUrl = staticmethod(QgsAbstractContentCacheBase.parseBase64DataUrl)
QgsAbstractContentCacheBase.isBase64Data = staticmethod(QgsAbstractContentCacheBase.isBase64Data)
QgsAbstractContentCacheBase.__signal_arguments__ = {'remoteContentFetched': ['url: str']}
except NameError:
pass
23 changes: 23 additions & 0 deletions python/core/auto_generated/qgsabstractcontentcache.sip.in
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,29 @@ Required because template based class (such as :py:class:`QgsAbstractContentCach
QgsAbstractContentCacheBase( QObject *parent );
%Docstring
Constructor for QgsAbstractContentCacheBase, with the specified ``parent`` object.
%End

static bool parseBase64DataUrl( const QString &path, QString *mimeType /Out/ = 0, QString *data /Out/ = 0 );
%Docstring
Parses a ``path`` to determine if it represents a base 64 encoded HTML data URL, and if so, extracts the components
of the URL.

Data URLs are of the form ``data:[<mediatype>;]base64,<data>``.

:param path: path to test

:return: - ``True`` if ``path`` is a base 64 encoded data URL
- mimeType: the extracted mime type if the ``path`` is a data URL
- data: the extracted base64 data if the ``path`` is a data URL

.. versionadded:: 3.40
%End

static bool isBase64Data( const QString &path );
%Docstring
Returns ``True`` if ``path`` represents base64 encoded data.

.. versionadded:: 3.40
%End

signals:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -262,6 +262,62 @@ Returns whether the format has overline enabled.
Sets whether the format has overline ``enabled``.

.. seealso:: :py:func:`overline`
%End

QString imagePath() const;
%Docstring
Returns the path to the image to render, if the format applies to a document image fragment.

.. seealso:: :py:func:`QgsTextFragment.isImage`

.. seealso:: :py:func:`imageSize`

.. seealso:: :py:func:`setImagePath`

.. versionadded:: 3.40
%End

void setImagePath( const QString &path );
%Docstring
Sets the ``path`` to the image to render, if the format applies to a document image fragment.

.. seealso:: :py:func:`QgsTextFragment.isImage`

.. seealso:: :py:func:`setImageSize`

.. seealso:: :py:func:`imagePath`

.. versionadded:: 3.40
%End

QSizeF imageSize() const;
%Docstring
Returns the image size, if the format applies to a document image fragment.

The image size is always considered to be in :py:class:`Qgis`.RenderUnit.Points.

.. seealso:: :py:func:`QgsTextFragment.isImage`

.. seealso:: :py:func:`imagePath`

.. seealso:: :py:func:`setImageSize`

.. versionadded:: 3.40
%End

void setImageSize( const QSizeF &size );
%Docstring
Sets the image ``size``, if the format applies to a document image fragment.

The image size is always considered to be in :py:class:`Qgis`.RenderUnit.Points.

.. seealso:: :py:func:`QgsTextFragment.isImage`

.. seealso:: :py:func:`setImagePath`

.. seealso:: :py:func:`imageSize`

.. versionadded:: 3.40
%End

bool hasVerticalAlignmentSet() const;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -99,6 +99,13 @@ Returns the vertical offset from a text block's baseline which should be applied
to the fragment at the specified index within that block.

.. versionadded:: 3.30
%End

double fragmentFixedHeight( int blockIndex, int fragmentIndex, Qgis::TextLayoutMode mode ) const;
%Docstring
Returns the fixed height of the fragment at the specified block and fragment index, or -1 if the fragment does not have a fixed height.

.. versionadded:: 3.40
%End

double verticalOrientationXOffset( int blockIndex ) const;
Expand Down
15 changes: 14 additions & 1 deletion python/core/auto_generated/textrenderer/qgstextfragment.sip.in
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,13 @@
class QgsTextFragment
{
%Docstring(signature="appended")
Stores a fragment of text along with formatting overrides to be used when rendering the fragment.
Stores a fragment of document along with formatting overrides to be used when rendering the fragment.

Text fragments consist of either a block of text or another atomic component of a document (such as an image).

Each fragment has an associated :py:func:`~characterFormat`, which specifies the text formatting overrides
to use when rendering the fragment. Additionally, the :py:func:`~characterFormat` may contain properties
for other fragment types, such as image paths and sizes for image fragments.

.. warning::

Expand Down Expand Up @@ -77,6 +83,13 @@ Returns the character formatting for the fragment.
Sets the character ``format`` for the fragment.

.. seealso:: :py:func:`characterFormat`
%End

bool isImage() const;
%Docstring
Returns ``True`` if the fragment represents an image.

.. versionadded:: 3.40
%End

double horizontalAdvance( const QFont &font, const QgsRenderContext &context, bool fontHasBeenUpdatedForFragment = false, double scaleFactor = 1.0 ) const;
Expand Down
21 changes: 21 additions & 0 deletions src/core/qgsabstractcontentcache.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@

#include "qgsabstractcontentcache.h"
#include "qgssetrequestinitiator_p.h"
#include <QRegularExpression>

//
// QgsAbstractContentCacheEntry
Expand All @@ -40,3 +41,23 @@ void QgsAbstractContentCacheBase::onRemoteContentFetched( const QString &, bool
{

}

bool QgsAbstractContentCacheBase::parseBase64DataUrl( const QString &path, QString *mimeType, QString *data )
{
const thread_local QRegularExpression sRx( QStringLiteral( "^data:(.*/.*?);?(?:base64)?,(.*)$" ) );
const QRegularExpressionMatch base64Match = sRx.match( path );
if ( !base64Match.hasMatch() )
return false;

if ( mimeType )
*mimeType = base64Match.captured( 1 );
if ( data )
*data = base64Match.captured( 2 );
return true;
}

bool QgsAbstractContentCacheBase::isBase64Data( const QString &path )
{
return path.startsWith( QLatin1String( "base64:" ) )
|| parseBase64DataUrl( path );
}
23 changes: 23 additions & 0 deletions src/core/qgsabstractcontentcache.h
Original file line number Diff line number Diff line change
Expand Up @@ -140,6 +140,29 @@ class CORE_EXPORT QgsAbstractContentCacheBase: public QObject
*/
QgsAbstractContentCacheBase( QObject *parent );

/**
* Parses a \a path to determine if it represents a base 64 encoded HTML data URL, and if so, extracts the components
* of the URL.
*
* Data URLs are of the form ``data:[<mediatype>;]base64,<data>``.
*
* \param path path to test
* \param mimeType will be set to the extracted mime type if the \a path is a data URL
* \param data will be set to the extracted base64 data if the \a path is a data URL
*
* \returns TRUE if \a path is a base 64 encoded data URL
*
* \since QGIS 3.40
*/
static bool parseBase64DataUrl( const QString &path, QString *mimeType SIP_OUT = nullptr, QString *data SIP_OUT = nullptr );

/**
* Returns TRUE if \a path represents base64 encoded data.
*
* \since QGIS 3.40
*/
static bool isBase64Data( const QString &path );

signals:

/**
Expand Down
10 changes: 10 additions & 0 deletions src/core/qgsabstractcontentcache_p.h
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@

#include "qgsabstractcontentcache.h"
#include "qgssetrequestinitiator_p.h"
#include <QRegularExpression>

template<class T>
QByteArray QgsAbstractContentCache<T>::getContent( const QString &path, const QByteArray &missingContent, const QByteArray &fetchingContent, bool blocking ) const
Expand All @@ -44,6 +45,15 @@ QByteArray QgsAbstractContentCache<T>::getContent( const QString &path, const QB
const QByteArray base64 = path.mid( 7 ).toLocal8Bit(); // strip 'base64:' prefix
return QByteArray::fromBase64( base64, QByteArray::OmitTrailingEquals );
}
else
{
// maybe a HTML data URL
QString base64String;
if ( parseBase64DataUrl( path, nullptr, &base64String ) )
{
return QByteArray::fromBase64( base64String.toLocal8Bit(), QByteArray::OmitTrailingEquals );
}
}

// maybe it's a url...
if ( !path.contains( QLatin1String( "://" ) ) ) // otherwise short, relative SVG paths might be considered URLs
Expand Down
Loading
Loading