Skip to content

Commit

Permalink
Browse files Browse the repository at this point in the history
[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 93a6115
Show file tree
Hide file tree
Showing 8 changed files with 202 additions and 143 deletions.
30 changes: 30 additions & 0 deletions python/core/qgstextrenderer.sip.in
Expand Up @@ -1495,6 +1495,13 @@ and background shapes.
%End
public:

enum DrawMode
{
Rect,
Point,
Label,
};

enum TextPart
{
Text,
Expand Down Expand Up @@ -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

};
Expand Down
164 changes: 94 additions & 70 deletions src/app/qgsdecorationcopyright.cpp
Expand Up @@ -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>

Expand All @@ -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
Expand All @@ -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();
}

24 changes: 19 additions & 5 deletions src/app/qgsdecorationcopyright.h
Expand Up @@ -20,6 +20,7 @@
#define QGSCOPYRIGHTLABELPLUGIN

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

#include <QColor>
#include <QFont>
Expand Down Expand Up @@ -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;
};

Expand Down
27 changes: 6 additions & 21 deletions src/app/qgsdecorationcopyrightdialog.cpp
Expand Up @@ -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;
Expand All @@ -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();
Expand All @@ -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()
Expand Down Expand Up @@ -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();
Expand Down
1 change: 0 additions & 1 deletion src/app/qgsdecorationcopyrightdialog.h
Expand Up @@ -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:
Expand Down

0 comments on commit 93a6115

Please sign in to comment.