Skip to content
Permalink
Browse files

[needs-docs] Use QgsTextRender to power up copyright decoration styli…

…ng (#6684)

- we gain all of the styling capabilities of our text renderer engine (i.e. what powers
the rendering of labels)
- HTML support is gone for now, with virtually all of styling possibilities
covered by the above text renderer
  • Loading branch information
nirvn committed Mar 28, 2018
1 parent 721095e commit 93a6115c9e20d08aad22fd9438d2c2fae9364acb
@@ -1495,6 +1495,13 @@ and background shapes.
%End
public:

enum DrawMode
{
Rect,
Point,
Label,
};

enum TextPart
{
Text,
@@ -1594,6 +1601,29 @@ with the text or background parts)
:param drawAsOutlines: set to false to render text as text. This allows outputs to
formats like SVG to maintain text as text objects, but at the cost of degraded
rendering and may result in side effects like misaligned text buffers.
%End

static double textWidth( const QgsRenderContext &context, const QgsTextFormat &format, const QStringList &textLines,
QFontMetricsF *fontMetrics = 0 );
%Docstring
Returns the width of a text based on a given format.

:param context: render context
:param format: text format
:param textLines: list of lines of text to calculate width from
:param fontMetrics: font metrics
%End

static double textHeight( const QgsRenderContext &context, const QgsTextFormat &format, const QStringList &textLines, DrawMode mode,
QFontMetricsF *fontMetrics = 0 );
%Docstring
Returns the height of a text based on a given format.

:param context: render context
:param format: text format
:param textLines: list of lines of text to calculate width from
:param mode: draw mode
:param fontMetrics: font metrics
%End

};
@@ -31,12 +31,13 @@ email : tim@linfiniti.com
#include "qgslogger.h"
#include "qgsmapcanvas.h"
#include "qgsproject.h"
#include "qgsreadwritecontext.h"
#include "qgssymbollayerutils.h"

#include <QPainter>
#include <QMenu>
#include <QDate>
#include <QTextDocument>
#include <QDomDocument>
#include <QMatrix>
#include <QFile>

@@ -59,25 +60,43 @@ void QgsDecorationCopyright::projectRead()
{
QgsDecorationItem::projectRead();

// there is no font setting in the UI, so just use the Qt/QGIS default font (what mQFont gets when created)
// mQFont.setFamily( QgsProject::instance()->readEntry( "CopyrightLabel", "/FontName", "Sans Serif" ) );
// mQFont.setPointSize( QgsProject::instance()->readNumEntry( "CopyrightLabel", "/FontSize", 9 ) );

mLabelText = QgsProject::instance()->readEntry( mNameConfig, QStringLiteral( "/Label" ), QString() );
mMarginHorizontal = QgsProject::instance()->readNumEntry( mNameConfig, QStringLiteral( "/MarginH" ), 0 );
mMarginVertical = QgsProject::instance()->readNumEntry( mNameConfig, QStringLiteral( "/MarginV" ), 0 );
mColor = QgsSymbolLayerUtils::decodeColor( QgsProject::instance()->readEntry( mNameConfig, QStringLiteral( "/Color" ), QStringLiteral( "#000000" ) ) );

QDomDocument doc;
QDomElement elem;
QString textXml = QgsProject::instance()->readEntry( mNameConfig, QStringLiteral( "/Font" ) );
if ( !textXml.isEmpty() )
{
doc.setContent( textXml );
elem = doc.documentElement();
QgsReadWriteContext rwContext;
rwContext.setPathResolver( QgsProject::instance()->pathResolver() );
mTextFormat.readXml( elem, rwContext );
}

// Migratation for pre QGIS 3.2 settings
QColor oldColor = QgsSymbolLayerUtils::decodeColor( QgsProject::instance()->readEntry( mNameConfig, QStringLiteral( "/Color" ) ) );
if ( oldColor.isValid() )
{
mTextFormat.setColor( oldColor );
}
}

void QgsDecorationCopyright::saveToProject()
{
QgsDecorationItem::saveToProject();
QgsProject::instance()->writeEntry( mNameConfig, QStringLiteral( "/FontName" ), mQFont.family() );
QgsProject::instance()->writeEntry( mNameConfig, QStringLiteral( "/FontSize" ), mQFont.pointSize() );
QgsProject::instance()->writeEntry( mNameConfig, QStringLiteral( "/Label" ), mLabelText );
QgsProject::instance()->writeEntry( mNameConfig, QStringLiteral( "/Color" ), QgsSymbolLayerUtils::encodeColor( mColor ) );
QgsProject::instance()->writeEntry( mNameConfig, QStringLiteral( "/MarginH" ), mMarginHorizontal );
QgsProject::instance()->writeEntry( mNameConfig, QStringLiteral( "/MarginV" ), mMarginVertical );

QDomDocument textDoc;
QgsReadWriteContext rwContext;
rwContext.setPathResolver( QgsProject::instance()->pathResolver() );
QDomElement textElem = mTextFormat.writeXml( textDoc, rwContext );
textDoc.appendChild( textElem );
QgsProject::instance()->writeEntry( mNameConfig, QStringLiteral( "/Font" ), textDoc.toString() );
}

// Slot called when the buffer menu item is activated
@@ -91,77 +110,82 @@ void QgsDecorationCopyright::run()
void QgsDecorationCopyright::render( const QgsMapSettings &mapSettings, QgsRenderContext &context )
{
Q_UNUSED( mapSettings );
//Large IF statement to enable/disable copyright label
if ( enabled() )
{
QString displayString = QgsExpression::replaceExpressionText( mLabelText, &context.expressionContext() );
if ( !enabled() )
return;

// need width/height of paint device
int myHeight = context.painter()->device()->height();
int myWidth = context.painter()->device()->width();
context.painter()->save();
context.painter()->setRenderHint( QPainter::Antialiasing, true );

QTextDocument text;
text.setDefaultFont( mQFont );
// To set the text color in a QTextDocument we use a CSS style
QString displayString = QgsExpression::replaceExpressionText( mLabelText, &context.expressionContext() );
QStringList displayStringList = displayString.split( "\n" );

QString style = "<style type=\"text/css\"> p {color: " +
QStringLiteral( "rgba( %1, %2, %3, %4 )" ).arg( mColor.red() ).arg( mColor.green() ).arg( mColor.blue() ).arg( QString::number( mColor.alphaF(), 'f', 2 ) ) + "}</style>";
text.setHtml( style + "<p>" + displayString + "</p>" );
QSizeF size = text.size();
QFontMetricsF fm( mTextFormat.scaledFont( context ) );
double textWidth = QgsTextRenderer::textWidth( context, mTextFormat, displayStringList, &fm );
double textHeight = QgsTextRenderer::textHeight( context, mTextFormat, displayStringList, QgsTextRenderer::Point, &fm );

float myXOffset( 0 ), myYOffset( 0 );
int deviceHeight = context.painter()->device()->height();
int deviceWidth = context.painter()->device()->width();

// Set margin according to selected units
switch ( mMarginUnit )
float xOffset( 0 ), yOffset( 0 );

// Set margin according to selected units
switch ( mMarginUnit )
{
case QgsUnitTypes::RenderMillimeters:
{
int pixelsInchX = context.painter()->device()->logicalDpiX();
int pixelsInchY = context.painter()->device()->logicalDpiY();
xOffset = pixelsInchX * INCHES_TO_MM * mMarginHorizontal;
yOffset = pixelsInchY * INCHES_TO_MM * mMarginVertical;
break;
}
case QgsUnitTypes::RenderPixels:
{
case QgsUnitTypes::RenderMillimeters:
{
int myPixelsInchX = context.painter()->device()->logicalDpiX();
int myPixelsInchY = context.painter()->device()->logicalDpiY();
myXOffset = myPixelsInchX * INCHES_TO_MM * mMarginHorizontal;
myYOffset = myPixelsInchY * INCHES_TO_MM * mMarginVertical;
break;
}

case QgsUnitTypes::RenderPixels:
myXOffset = mMarginHorizontal;
myYOffset = mMarginVertical;
break;

case QgsUnitTypes::RenderPercentage:
myXOffset = ( ( myWidth - size.width() ) / 100. ) * mMarginHorizontal;
myYOffset = ( ( myHeight - size.height() ) / 100. ) * mMarginVertical;
break;

default: // Use default of top left
break;
xOffset = mMarginHorizontal;
yOffset = mMarginVertical;
break;
}
//Determine placement of label from form combo box
switch ( mPlacement )
case QgsUnitTypes::RenderPercentage:
{
case BottomLeft: // Bottom Left. myXOffset is set above
myYOffset = myHeight - myYOffset - size.height();
break;
case TopLeft: // Top left. Already setup above
break;
case TopRight: // Top Right. myYOffset is set above
myXOffset = myWidth - myXOffset - size.width();
break;
case BottomRight: // Bottom Right
//Define bottom right hand corner start point
myYOffset = myHeight - myYOffset - size.height();
myXOffset = myWidth - myXOffset - size.width();
break;
default:
QgsDebugMsg( QString( "Unknown placement index of %1" ).arg( static_cast<int>( mPlacement ) ) );
xOffset = ( ( deviceWidth - textWidth ) / 100. ) * mMarginHorizontal;
yOffset = ( ( deviceHeight - textHeight ) / 100. ) * mMarginVertical;
break;
}
case QgsUnitTypes::RenderMapUnits:
case QgsUnitTypes::RenderPoints:
case QgsUnitTypes::RenderInches:
case QgsUnitTypes::RenderUnknownUnit:
case QgsUnitTypes::RenderMetersInMapUnits:
break;
}

//Paint label to canvas
QMatrix worldMatrix = context.painter()->worldMatrix();
context.painter()->translate( myXOffset, myYOffset );
text.drawContents( context.painter() );
// Put things back how they were
context.painter()->setWorldMatrix( worldMatrix );
// Determine placement of label from form combo box
QgsTextRenderer::HAlignment horizontalAlignment = QgsTextRenderer::AlignLeft;
switch ( mPlacement )
{
case BottomLeft: // Bottom Left, xOffset is set above
yOffset = deviceHeight - yOffset;
break;
case TopLeft: // Top left, xOffset is set above
yOffset = yOffset + textHeight;
break;
case TopRight: // Top Right
yOffset = yOffset + textHeight;
xOffset = deviceWidth - xOffset;
horizontalAlignment = QgsTextRenderer::AlignRight;
break;
case BottomRight: // Bottom Right
yOffset = deviceHeight - yOffset;
xOffset = deviceWidth - xOffset;
horizontalAlignment = QgsTextRenderer::AlignRight;
break;
default:
QgsDebugMsg( QString( "Unknown placement index of %1" ).arg( static_cast<int>( mPlacement ) ) );
}

//Paint label to canvas
QgsTextRenderer::drawText( QPointF( xOffset, yOffset ), 0.0, horizontalAlignment, displayStringList, context, mTextFormat );

context.painter()->restore();
}

@@ -20,6 +20,7 @@
#define QGSCOPYRIGHTLABELPLUGIN

#include "qgsdecorationitem.h"
#include "qgstextrenderer.h"

#include <QColor>
#include <QFont>
@@ -49,19 +50,32 @@ class APP_EXPORT QgsDecorationCopyright : public QgsDecorationItem
//! render the copyright label
void render( const QgsMapSettings &mapSettings, QgsRenderContext &context ) override;

/**
* Returns the text format for extent labels.
* \see setTextFormat()
* \see labelExtents()
* \since QGIS 3.2
*/
QgsTextFormat textFormat() const { return mTextFormat; }

/**
* Sets the text \a format for extent labels.
* \see textFormat()
* \see setLabelExtents()
* \since QGIS 3.2
*/
void setTextFormat( const QgsTextFormat &format ) { mTextFormat = format; }

private:
//! This is the font that will be used for the copyright label
QFont mQFont;
//! This is the string that will be used for the copyright label
QString mLabelText;

//! This is the color for the copyright label
QColor mColor;

//! enable or disable use of position percentage for placement
int mMarginHorizontal = 0;
int mMarginVertical = 0;

QgsTextFormat mTextFormat;

friend class QgsDecorationCopyrightDialog;
};

@@ -36,7 +36,6 @@ QgsDecorationCopyrightDialog::QgsDecorationCopyrightDialog( QgsDecorationCopyrig
connect( buttonBox, &QDialogButtonBox::accepted, this, &QgsDecorationCopyrightDialog::buttonBox_accepted );
connect( buttonBox, &QDialogButtonBox::rejected, this, &QgsDecorationCopyrightDialog::buttonBox_rejected );
connect( mInsertExpressionButton, &QPushButton::clicked, this, &QgsDecorationCopyrightDialog::mInsertExpressionButton_clicked );
connect( pbnColorChooser, &QgsColorButton::colorChanged, this, &QgsDecorationCopyrightDialog::pbnColorChooser_colorChanged );
connect( buttonBox, &QDialogButtonBox::helpRequested, this, &QgsDecorationCopyrightDialog::showHelp );

QgsSettings settings;
@@ -48,6 +47,7 @@ QgsDecorationCopyrightDialog::QgsDecorationCopyrightDialog( QgsDecorationCopyrig
grpEnable->setChecked( mDeco.enabled() );

// label text
txtCopyrightText->setAcceptRichText( false );
if ( !mDeco.enabled() && mDeco.mLabelText.isEmpty() )
{
QDate now = QDate::currentDate();
@@ -70,16 +70,10 @@ QgsDecorationCopyrightDialog::QgsDecorationCopyrightDialog( QgsDecorationCopyrig
wgtUnitSelection->setUnits( QgsUnitTypes::RenderUnitList() << QgsUnitTypes::RenderMillimeters << QgsUnitTypes::RenderPercentage << QgsUnitTypes::RenderPixels );
wgtUnitSelection->setUnit( mDeco.mMarginUnit );

// color
pbnColorChooser->setAllowOpacity( true );
pbnColorChooser->setColor( mDeco.mColor );
pbnColorChooser->setContext( QStringLiteral( "gui" ) );
pbnColorChooser->setColorDialogTitle( tr( "Select Text color" ) );

QTextCursor cursor = txtCopyrightText->textCursor();
txtCopyrightText->selectAll();
txtCopyrightText->setTextColor( mDeco.mColor );
txtCopyrightText->setTextCursor( cursor );
// font settings
mButtonFontStyle->setDialogTitle( tr( "Copyright Label Text Format" ) );
mButtonFontStyle->setMapCanvas( QgisApp::instance()->mapCanvas() );
mButtonFontStyle->setTextFormat( mDeco.textFormat() );
}

QgsDecorationCopyrightDialog::~QgsDecorationCopyrightDialog()
@@ -120,19 +114,10 @@ void QgsDecorationCopyrightDialog::mInsertExpressionButton_clicked()
}
}

void QgsDecorationCopyrightDialog::pbnColorChooser_colorChanged( const QColor &c )
{
QTextCursor cursor = txtCopyrightText->textCursor();
txtCopyrightText->selectAll();
txtCopyrightText->setTextColor( c );
txtCopyrightText->setTextCursor( cursor );
}

void QgsDecorationCopyrightDialog::apply()
{
mDeco.mQFont = txtCopyrightText->currentFont();
mDeco.setTextFormat( mButtonFontStyle->textFormat() );
mDeco.mLabelText = txtCopyrightText->toPlainText();
mDeco.mColor = pbnColorChooser->color();
mDeco.setPlacement( static_cast< QgsDecorationItem::Placement>( cboPlacement->currentData().toInt() ) );
mDeco.mMarginUnit = wgtUnitSelection->unit();
mDeco.mMarginHorizontal = spnHorizontal->value();
@@ -33,7 +33,6 @@ class APP_EXPORT QgsDecorationCopyrightDialog : public QDialog, private Ui::QgsD
void buttonBox_rejected();
void mInsertExpressionButton_clicked();
void showHelp();
void pbnColorChooser_colorChanged( const QColor &c );
void apply();

protected:

0 comments on commit 93a6115

Please sign in to comment.
You can’t perform that action at this time.