Skip to content

Commit 430f0fc

Browse files
committed
Add user notification when defined labeling font is not found on system, then substituted
- [API] add signal to QgsVectorLayer that is emitted when its labeling font is not found - Message bar notification on first rendering of layer offers link to open layer's labeling dialog - Add 'font not found' notification to labeling dialog - Labeling dialog defaults to 'Text style' section when font is not found, to show notice - Substituted font is not saved with project, unless user applies changes. - Change labeling gui font selector dialog to font family combobox (since only family is being chosen)
1 parent 44d89a4 commit 430f0fc

11 files changed

+252
-159
lines changed

python/core/qgspallabeling.sip

+2
Original file line numberDiff line numberDiff line change
@@ -222,7 +222,9 @@ class QgsPalLayerSettings
222222
QgsExpression* getLabelExpression();
223223

224224
QFont textFont;
225+
//QString textFontFamily;
225226
QString textNamedStyle;
227+
//bool textFontFound;
226228
bool fontSizeInMapUnits; //true if font size is in map units (otherwise in points)
227229
QColor textColor;
228230
int textTransp;

python/core/qgsvectorlayer.sip

+5
Original file line numberDiff line numberDiff line change
@@ -1047,6 +1047,11 @@ class QgsVectorLayer : QgsMapLayer
10471047
void committedAttributeValuesChanges( const QString& layerId, const QgsChangedAttributesMap& changedAttributesValues );
10481048
void committedGeometriesChanges( const QString& layerId, const QgsGeometryMap& changedGeometries );
10491049

1050+
/** Emitted when the font family defined for labeling layer is not found on system
1051+
* @note added in 1.9
1052+
*/
1053+
void labelingFontNotFound( QgsVectorLayer* layer, const QString& fontfamily );
1054+
10501055
protected:
10511056
/** Set the extent */
10521057
void setExtent( const QgsRectangle &rect );

src/app/qgisapp.cpp

+60
Original file line numberDiff line numberDiff line change
@@ -4279,6 +4279,63 @@ void QgisApp::modifyAnnotation()
42794279
mMapCanvas->setMapTool( mMapTools.mAnnotation );
42804280
}
42814281

4282+
void QgisApp::labelingFontNotFound( QgsVectorLayer* vlayer, const QString& fontfamily )
4283+
{
4284+
// TODO: update when pref for how to resolve missing family (use matching algorithm or just default font) is implemented
4285+
QString substitute = tr( "Default system font substituted." );
4286+
4287+
QWidget* fontMsg = QgsMessageBar::createMessage(
4288+
tr( "Labeling" ),
4289+
tr( "Font for layer <b><u>%1</u></b> was not found (<i>%2</i>). %3" ).arg( vlayer->name() ).arg( fontfamily ).arg( substitute ),
4290+
QgsApplication::getThemeIcon( "/mIconWarn.png" ),
4291+
messageBar() );
4292+
4293+
QToolButton* btnOpenPrefs = new QToolButton( fontMsg );
4294+
btnOpenPrefs->setStyleSheet( "QToolButton{ background-color: rgba(255, 255, 255, 0); color: black; text-decoration: underline; }" );
4295+
btnOpenPrefs->setCursor( Qt::PointingHandCursor );
4296+
btnOpenPrefs->setSizePolicy( QSizePolicy::Maximum, QSizePolicy::Preferred );
4297+
btnOpenPrefs->setToolButtonStyle( Qt::ToolButtonTextOnly );
4298+
4299+
// store pointer to vlayer in data of QAction
4300+
QAction* act = new QAction( fontMsg );
4301+
act->setData( QVariant( QMetaType::QObjectStar, &vlayer ) );
4302+
act->setText( tr( "Open labeling dialog" ) );
4303+
btnOpenPrefs->addAction( act );
4304+
btnOpenPrefs->setDefaultAction( act );
4305+
btnOpenPrefs->setToolTip( "" );
4306+
4307+
connect( btnOpenPrefs, SIGNAL( triggered( QAction* ) ), this, SLOT( labelingDialogFontNotFound( QAction* ) ) );
4308+
fontMsg->layout()->addWidget( btnOpenPrefs );
4309+
4310+
// no timeout set, since notice needs attention and is only shown first time layer is labeled
4311+
messageBar()->pushWidget( fontMsg, QgsMessageBar::WARNING );
4312+
}
4313+
4314+
void QgisApp::labelingDialogFontNotFound( QAction* act )
4315+
{
4316+
if ( !act )
4317+
{
4318+
return;
4319+
}
4320+
4321+
// get base pointer to layer
4322+
QObject* obj = qvariant_cast<QObject*>( act->data() );
4323+
4324+
// remove calling messagebar widget
4325+
messageBar()->popWidget();
4326+
4327+
if ( !obj )
4328+
{
4329+
return;
4330+
}
4331+
4332+
QgsMapLayer* layer = qobject_cast<QgsMapLayer*>( obj );
4333+
if ( layer && setActiveLayer( layer ) )
4334+
{
4335+
labeling();
4336+
}
4337+
}
4338+
42824339
void QgisApp::labeling()
42834340
{
42844341
QgsVectorLayer *vlayer = qobject_cast<QgsVectorLayer*>( activeLayer() );
@@ -7658,6 +7715,9 @@ void QgisApp::layersWereAdded( QList<QgsMapLayer *> theLayers )
76587715
QgsVectorLayer *vlayer = qobject_cast<QgsVectorLayer *>( layer );
76597716
if ( vlayer )
76607717
{
7718+
// notify user about any font family substitution, but only when rendering labels (i.e. not when opening settings dialog)
7719+
connect( vlayer, SIGNAL( labelingFontNotFound( QgsVectorLayer*, QString ) ), this, SLOT( labelingFontNotFound( QgsVectorLayer*, QString ) ) );
7720+
76617721
QgsVectorDataProvider* vProvider = vlayer->dataProvider();
76627722
if ( vProvider && vProvider->capabilities() & QgsVectorDataProvider::EditingCapabilities )
76637723
{

src/app/qgisapp.h

+10
Original file line numberDiff line numberDiff line change
@@ -1020,6 +1020,16 @@ class QgisApp : public QMainWindow, private Ui::MainWindow
10201020
void addSvgAnnotation();
10211021
void modifyAnnotation();
10221022

1023+
/** Alerts user when labeling font for layer has not been found on system
1024+
* @note added in 1.9
1025+
*/
1026+
void labelingFontNotFound( QgsVectorLayer* vlayer, const QString& fontfamily );
1027+
1028+
/** Opens the labeling dialog for a layer when called from labelingFontNotFound alert
1029+
* @note added in 1.9
1030+
*/
1031+
void labelingDialogFontNotFound( QAction* act );
1032+
10231033
//! shows label settings dialog (for labeling-ng)
10241034
void labeling();
10251035

src/app/qgslabelinggui.cpp

+35-58
Original file line numberDiff line numberDiff line change
@@ -73,7 +73,6 @@ QgsLabelingGui::QgsLabelingGui( QgsPalLabeling* lbl, QgsVectorLayer* layer, QgsM
7373

7474
// preview and basic option connections
7575
connect( btnTextColor, SIGNAL( colorChanged( const QColor& ) ), this, SLOT( changeTextColor( const QColor& ) ) );
76-
connect( btnChangeFont, SIGNAL( clicked() ), this, SLOT( changeTextFont() ) );
7776
connect( mFontTranspSpinBox, SIGNAL( valueChanged( int ) ), this, SLOT( updatePreview() ) );
7877
connect( mBufferDrawChkBx, SIGNAL( toggled( bool ) ), this, SLOT( updatePreview() ) );
7978
connect( btnBufferColor, SIGNAL( colorChanged( const QColor& ) ), this, SLOT( changeBufferColor( const QColor& ) ) );
@@ -366,6 +365,22 @@ void QgsLabelingGui::init()
366365
QgsFontUtils::updateFontViaStyle( mRefFont, lyr.textNamedStyle );
367366
updateFont( mRefFont );
368367

368+
// show 'font not found' if substitution has occurred (should come after updateFont())
369+
if ( !lyr.mTextFontFound )
370+
{
371+
mFontMissingLabel->setVisible( true );
372+
QString missingTxt = tr( "%1 not found. Default substituted." );
373+
QString txtPrepend = tr( "Chosen font" );
374+
if ( !lyr.mTextFontFamily.isEmpty() )
375+
{
376+
txtPrepend = QString( "'%1'" ).arg( lyr.mTextFontFamily );
377+
}
378+
mFontMissingLabel->setText( missingTxt.arg( txtPrepend ) );
379+
380+
// ensure user is sent to 'Text style' section to see notice
381+
mLabelingOptionsListWidget->setCurrentRow( 0 );
382+
}
383+
369384
// shape background
370385
mShapeDrawChkBx->setChecked( lyr.shapeDraw );
371386
mShapeTypeCmbBx->blockSignals( true );
@@ -474,6 +489,7 @@ void QgsLabelingGui::collapseSample( bool collapse )
474489
void QgsLabelingGui::apply()
475490
{
476491
writeSettingsToLayer();
492+
mFontMissingLabel->setVisible( false );
477493
QgisApp::instance()->markDirty();
478494
// trigger refresh
479495
if ( mMapCanvas )
@@ -1019,41 +1035,6 @@ void QgsLabelingGui::changeTextColor( const QColor &color )
10191035
updatePreview();
10201036
}
10211037

1022-
void QgsLabelingGui::changeTextFont()
1023-
{
1024-
// store properties of QFont that might be stripped by font dialog
1025-
QFont::Capitalization captials = mRefFont.capitalization();
1026-
double wordspacing = mRefFont.wordSpacing();
1027-
double letterspacing = mRefFont.letterSpacing();
1028-
1029-
bool ok;
1030-
#if defined(Q_WS_MAC) && QT_VERSION >= 0x040500 && defined(QT_MAC_USE_COCOA)
1031-
// Native Mac dialog works only for Qt Carbon
1032-
QFont font = QFontDialog::getFont( &ok, mRefFont, 0, QString(), QFontDialog::DontUseNativeDialog );
1033-
#else
1034-
QFont font = QFontDialog::getFont( &ok, mRefFont );
1035-
#endif
1036-
if ( ok )
1037-
{
1038-
if ( mFontSizeUnitComboBox->currentIndex() == 1 )
1039-
{
1040-
// don't override map units size with selected size from font dialog
1041-
font.setPointSizeF( mFontSizeSpinBox->value() );
1042-
}
1043-
else
1044-
{
1045-
mFontSizeSpinBox->setValue( font.pointSizeF() );
1046-
}
1047-
1048-
// reassign possibly stripped QFont properties
1049-
font.setCapitalization( captials );
1050-
font.setWordSpacing( wordspacing );
1051-
font.setLetterSpacing( QFont::AbsoluteSpacing, letterspacing );
1052-
1053-
updateFont( font );
1054-
}
1055-
}
1056-
10571038
void QgsLabelingGui::updateFont( QFont font )
10581039
{
10591040
// update background reference font
@@ -1063,31 +1044,13 @@ void QgsLabelingGui::updateFont( QFont font )
10631044
}
10641045

10651046
// test if font is actually available
1066-
QString missingtxt = QString( "" );
1067-
bool missing = false;
1068-
if ( QApplication::font().toString() != mRefFont.toString() )
1069-
{
1070-
QFont testfont = mFontDB.font( mRefFont.family(), mFontDB.styleString( mRefFont ), 12 );
1071-
if ( QApplication::font().toString() == testfont.toString() )
1072-
{
1073-
missing = true;
1074-
}
1075-
}
1076-
if ( missing )
1077-
{
1078-
missingtxt = tr( " (not found!)" );
1079-
lblFontName->setStyleSheet( "color: #990000;" );
1080-
}
1081-
else
1082-
{
1083-
lblFontName->setStyleSheet( "color: #000000;" );
1084-
}
1047+
mFontMissingLabel->setVisible( QgsFontUtils::fontMatchOnSystem( mRefFont ) );
10851048

1086-
lblFontName->setText( QString( "%1%2" ).arg( mRefFont.family() ).arg( missingtxt ) );
10871049
mDirectSymbLeftLineEdit->setFont( mRefFont );
10881050
mDirectSymbRightLineEdit->setFont( mRefFont );
10891051

10901052
blockFontChangeSignals( true );
1053+
mFontFamilyCmbBx->setCurrentFont( mRefFont );
10911054
populateFontStyleComboBox();
10921055
int idx = mFontCapitalsComboBox->findData( QVariant(( unsigned int ) mRefFont.capitalization() ) );
10931056
mFontCapitalsComboBox->setCurrentIndex( idx == -1 ? 0 : idx );
@@ -1097,13 +1060,13 @@ void QgsLabelingGui::updateFont( QFont font )
10971060

10981061
// update font name with font face
10991062
// font.setPixelSize( 24 );
1100-
// lblFontName->setFont( QFont( font ) );
11011063

11021064
updatePreview();
11031065
}
11041066

11051067
void QgsLabelingGui::blockFontChangeSignals( bool blk )
11061068
{
1069+
mFontFamilyCmbBx->blockSignals( blk );
11071070
mFontStyleComboBox->blockSignals( blk );
11081071
mFontCapitalsComboBox->blockSignals( blk );
11091072
mFontUnderlineBtn->blockSignals( blk );
@@ -1333,7 +1296,15 @@ void QgsLabelingGui::populateFontStyleComboBox()
13331296
{
13341297
mFontStyleComboBox->addItem( style );
13351298
}
1336-
mFontStyleComboBox->setCurrentIndex( mFontStyleComboBox->findText( mFontDB.styleString( mRefFont ) ) );
1299+
1300+
int curIndx = 0;
1301+
int stylIndx = mFontStyleComboBox->findText( mFontDB.styleString( mRefFont ) );
1302+
if ( stylIndx > -1 )
1303+
{
1304+
curIndx = stylIndx;
1305+
}
1306+
1307+
mFontStyleComboBox->setCurrentIndex( curIndx );
13371308
}
13381309

13391310
void QgsLabelingGui::on_mPreviewSizeSlider_valueChanged( int i )
@@ -1355,6 +1326,12 @@ void QgsLabelingGui::on_mFontCapitalsComboBox_currentIndexChanged( int index )
13551326
updateFont( mRefFont );
13561327
}
13571328

1329+
void QgsLabelingGui::on_mFontFamilyCmbBx_currentFontChanged( const QFont& f )
1330+
{
1331+
mRefFont.setFamily( f.family() );
1332+
updateFont( mRefFont );
1333+
}
1334+
13581335
void QgsLabelingGui::on_mFontStyleComboBox_currentIndexChanged( const QString & text )
13591336
{
13601337
QgsFontUtils::updateFontViaStyle( mRefFont, text );

src/app/qgslabelinggui.h

+1-1
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,6 @@ class QgsLabelingGui : public QWidget, private Ui::QgsLabelingGuiBase
4444
void collapseSample( bool collapse );
4545
void apply();
4646
void changeTextColor( const QColor &color );
47-
void changeTextFont();
4847
void showEngineConfigDialog();
4948
void showExpressionDialog();
5049
void changeBufferColor( const QColor &color );
@@ -58,6 +57,7 @@ class QgsLabelingGui : public QWidget, private Ui::QgsLabelingGuiBase
5857
void on_mPreviewSizeSlider_valueChanged( int i );
5958
void on_mFontSizeSpinBox_valueChanged( double d );
6059
void on_mFontCapitalsComboBox_currentIndexChanged( int index );
60+
void on_mFontFamilyCmbBx_currentFontChanged( const QFont& f );
6161
void on_mFontStyleComboBox_currentIndexChanged( const QString & text );
6262
void on_mFontUnderlineBtn_toggled( bool ckd );
6363
void on_mFontStrikethroughBtn_toggled( bool ckd );

src/core/qgspallabeling.cpp

+23-3
Original file line numberDiff line numberDiff line change
@@ -212,12 +212,15 @@ QgsPalLayerSettings::QgsPalLayerSettings()
212212

213213
// text style
214214
textFont = QApplication::font();
215-
fontSizeInMapUnits = false;
216215
textNamedStyle = QString( "" );
216+
fontSizeInMapUnits = false;
217217
textColor = Qt::black;
218218
textTransp = 0;
219219
blendMode = QPainter::CompositionMode_SourceOver;
220220
previewBkgrdColor = Qt::white;
221+
// font processing info
222+
mTextFontFound = true;
223+
mTextFontFamily = QApplication::font().family();
221224

222225
// text formatting
223226
wrapChar = "";
@@ -432,12 +435,15 @@ QgsPalLayerSettings::QgsPalLayerSettings( const QgsPalLayerSettings& s )
432435
fieldName = s.fieldName;
433436
isExpression = s.isExpression;
434437
textFont = s.textFont;
435-
fontSizeInMapUnits = s.fontSizeInMapUnits;
436438
textNamedStyle = s.textNamedStyle;
439+
fontSizeInMapUnits = s.fontSizeInMapUnits;
437440
textColor = s.textColor;
438441
textTransp = s.textTransp;
439442
blendMode = s.blendMode;
440443
previewBkgrdColor = s.previewBkgrdColor;
444+
// font processing info
445+
mTextFontFound = s.mTextFontFound;
446+
mTextFontFamily = s.mTextFontFamily;
441447

442448
// text formatting
443449
wrapChar = s.wrapChar;
@@ -796,7 +802,21 @@ void QgsPalLayerSettings::readFromLayer( QgsVectorLayer* layer )
796802
// text style
797803
fieldName = layer->customProperty( "labeling/fieldName" ).toString();
798804
isExpression = layer->customProperty( "labeling/isExpression" ).toBool();
799-
QString fontFamily = layer->customProperty( "labeling/fontFamily", QVariant( QApplication::font().family() ) ).toString();
805+
QFont appFont = QApplication::font();
806+
mTextFontFamily = layer->customProperty( "labeling/fontFamily", QVariant( appFont.family() ) ).toString();
807+
QString fontFamily = mTextFontFamily;
808+
if ( mTextFontFamily != appFont.family() && !QgsFontUtils::fontFamilyMatchOnSystem( mTextFontFamily ) )
809+
{
810+
// trigger to notify user about font family substitution (signal emitted in QgsVectorLayer::prepareLabelingAndDiagrams)
811+
mTextFontFound = false;
812+
813+
// TODO: update when pref for how to resolve missing family (use matching algorithm or just default font) is implemented
814+
// currently only defaults to matching algorithm for resolving [foundry], if a font of similar family is found (default for QFont)
815+
816+
// for now, do not use matching algorithm for substitution if family not found, substitute default instead
817+
fontFamily = appFont.family();
818+
}
819+
800820
double fontSize = layer->customProperty( "labeling/fontSize" ).toDouble();
801821
fontSizeInMapUnits = layer->customProperty( "labeling/fontSizeInMapUnits" ).toBool();
802822
int fontWeight = layer->customProperty( "labeling/fontWeight" ).toInt();

src/core/qgspallabeling.h

+4
Original file line numberDiff line numberDiff line change
@@ -492,6 +492,7 @@ class CORE_EXPORT QgsPalLayerSettings
492492
QMap<QgsPalLayerSettings::DataDefinedProperties, QPair<QString, int> > dataDefinedNames() const { return mDataDefinedNames; }
493493

494494
// temporary stuff: set when layer gets prepared or labeled
495+
// NOTE: not in Python binding
495496
pal::Layer* palLayer;
496497
QgsFeature* mCurFeat;
497498
const QgsFields* mCurFields;
@@ -505,6 +506,9 @@ class CORE_EXPORT QgsPalLayerSettings
505506
int mFeatsSendingToPal; // total features tested for sending into PAL (relative to maxNumLabels)
506507
int mFeatsRegPal; // number of features registered in PAL, when using limitNumLabels
507508

509+
QString mTextFontFamily;
510+
bool mTextFontFound;
511+
508512
bool showingShadowRects; // whether to show debug rectangles for drop shadows
509513

510514
private:

src/core/qgsvectorlayer.cpp

+10-1
Original file line numberDiff line numberDiff line change
@@ -122,6 +122,7 @@ QgsVectorLayer::QgsVectorLayer( QString vectorLayerPath,
122122
, mRendererV2( NULL )
123123
, mLabel( 0 )
124124
, mLabelOn( false )
125+
, mLabelFontNotFoundNotified( false )
125126
, mFeatureBlendMode( QPainter::CompositionMode_SourceOver ) // Default to normal feature blending
126127
, mLayerTransparency( 0 )
127128
, mVertexMarkerOnlyForSelection( false )
@@ -3477,8 +3478,9 @@ void QgsVectorLayer::prepareLabelingAndDiagrams( QgsRenderContext& rendererConte
34773478

34783479
if ( labeling )
34793480
{
3480-
// see if feature count limit is set for labeling
34813481
QgsPalLayerSettings& palyr = rendererContext.labelingEngine()->layer( this->id() );
3482+
3483+
// see if feature count limit is set for labeling
34823484
if ( palyr.limitNumLabels && palyr.maxNumLabels > 0 )
34833485
{
34843486
QgsFeatureIterator fit = getFeatures( QgsFeatureRequest()
@@ -3494,6 +3496,13 @@ void QgsVectorLayer::prepareLabelingAndDiagrams( QgsRenderContext& rendererConte
34943496
}
34953497
palyr.mFeaturesToLabel = nFeatsToLabel;
34963498
}
3499+
3500+
// notify user about any font substitution
3501+
if ( !palyr.mTextFontFound && !mLabelFontNotFoundNotified )
3502+
{
3503+
emit labelingFontNotFound( this, palyr.mTextFontFamily );
3504+
mLabelFontNotFoundNotified = true;
3505+
}
34973506
}
34983507

34993508
//register diagram layers

0 commit comments

Comments
 (0)