Skip to content

Commit

Permalink
[symbology] Fix font marker inserting illegal characters in a saved p…
Browse files Browse the repository at this point in the history
…roject's XML document
  • Loading branch information
nirvn committed Oct 25, 2023
1 parent 7b2d4a9 commit 62ef8a3
Show file tree
Hide file tree
Showing 5 changed files with 125 additions and 6 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@




typedef QMap<QString, QString> QgsStringMap;
typedef QMap<QString, QgsSymbol * > QgsSymbolMap;

Expand Down
23 changes: 17 additions & 6 deletions src/core/project/qgsproject.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1831,19 +1831,30 @@ bool QgsProject::readProjectFile( const QString &filename, Qgis::ProjectReadFlag
return false;
}

QTextStream textStream( &projectFile );
#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
textStream.setCodec( "UTF-8" );
#endif
QString projectString = textStream.readAll();
projectFile.close();

for ( int i = 0; i < 32; i++ )
{
if ( i == 9 || i == 10 || i == 13 )
{
continue;
}
projectString.replace( QChar( i ), QStringLiteral( "%1%2%1" ).arg( FONTMARKER_CHR_FIX, QString::number( i ) ) );
}

// location of problem associated with errorMsg
int line, column;
QString errorMsg;

if ( !doc->setContent( &projectFile, &errorMsg, &line, &column ) )
if ( !doc->setContent( projectString, &errorMsg, &line, &column ) )
{
const QString errorString = tr( "Project file read error in file %1: %2 at line %3 column %4" )
.arg( projectFile.fileName(), errorMsg ).arg( line ).arg( column );

QgsDebugError( errorString );

projectFile.close();

setError( errorString );

return false;
Expand Down
35 changes: 35 additions & 0 deletions src/core/symbology/qgssymbollayerutils.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1349,6 +1349,28 @@ QgsSymbolLayer *QgsSymbolLayerUtils::loadSymbolLayer( QDomElement &element, cons
// parse properties
QVariantMap props = parseProperties( element );

if ( layerClass == QStringLiteral( "FontMarker" ) )
{
if ( props.contains( QStringLiteral( "chrIsDecimal" ) ) )
{
if ( props["chrIsDecimal"].toBool() )
{
props["chr"] = QChar( props["chr"].toString().toUShort() );
}
}
else
{
// Old font marker properties restored from XML may contain XML-breaking characters that have been transformed during project load, restore accordingly
const QString chr = props["chr"].toString();
const thread_local QRegularExpression charRegExp( QStringLiteral( "%1([0-9]+)%1" ).arg( FONTMARKER_CHR_FIX ) );
const QRegularExpressionMatch charMatch = charRegExp.match( chr );
if ( charMatch.hasMatch() )
{
props["chr"] = QChar( charMatch.captured( 1 ).toUShort() );
}
}
}

// if there are any paths stored in properties, convert them from relative to absolute
QgsApplication::symbolLayerRegistry()->resolvePaths( layerClass, props, context.pathResolver(), false );

Expand Down Expand Up @@ -1452,6 +1474,19 @@ QDomElement QgsSymbolLayerUtils::saveSymbol( const QString &name, const QgsSymbo
layerEl.setAttribute( QStringLiteral( "userFlags" ), qgsFlagValueToKeys( layer->userFlags() ) );

QVariantMap props = layer->properties();
if ( dynamic_cast< const QgsFontMarkerSymbolLayer * >( layer ) )
{
const QString chr = props["chr"].toString();
if ( chr.size() == 1 )
{
props["chr"] = QString::number( chr.at( 0 ).unicode() );
props["chrIsDecimal"] = true;
}
else
{
props["chrIsDecimal"] = false;
}
}

// if there are any paths in properties, convert them from absolute to relative
QgsApplication::symbolLayerRegistry()->resolvePaths( layer->layerType(), props, context.pathResolver(), true );
Expand Down
2 changes: 2 additions & 0 deletions src/core/symbology/qgssymbollayerutils.h
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,8 @@

#include <QFile>

#define FONTMARKER_CHR_FIX "~!_#!#_!~"

class QgsExpression;
class QgsPathResolver;
class QgsReadWriteContext;
Expand Down
70 changes: 70 additions & 0 deletions tests/src/python/test_qgssymbollayerutils.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@

import qgis # NOQA
from qgis.PyQt.QtCore import QDir, QMimeData, QPointF, QSize, QSizeF, Qt
from qgis.PyQt.QtXml import QDomDocument, QDomElement
from qgis.PyQt.QtGui import QColor, QImage, QPolygonF
from qgis.core import (
Qgis,
Expand All @@ -25,6 +26,7 @@
QgsMarkerLineSymbolLayer,
QgsMarkerSymbol,
QgsProperty,
QgsReadWriteContext,
QgsShapeburstFillSymbolLayer,
QgsSimpleFillSymbolLayer,
QgsSimpleLineSymbolLayer,
Expand Down Expand Up @@ -753,6 +755,74 @@ def test_clear_symbollayer_ids(self):
QgsSymbolLayerUtils.clearSymbolLayerIds(fill_symbol)
self.assertFalse(child_sl.id())

def test_font_marker_load(self):
"""
Test the loading of font marker from XML
"""

font_marker_xml_string = """<symbol clip_to_extent="1" type="marker" is_animated="0" alpha="1" name="symbol" frame_rate="10" force_rhr="0">
<layer pass="0" id="{2aefc556-4eb1-4f56-b96b-e1dea6b58f69}" locked="0" class="FontMarker" enabled="1">
<Option type="Map">
<Option value="0" type="QString" name="angle"/>
<Option value="14" type="int" name="chr"/>
<Option value="true" type="bool" name="chrIsDecimal"/>
<Option value="0,0,255,255" type="QString" name="color"/>
<Option value="Arial" type="QString" name="font"/>
<Option value="Italic" type="QString" name="font_style"/>
<Option value="1" type="QString" name="horizontal_anchor_point"/>
<Option value="miter" type="QString" name="joinstyle"/>
<Option value="0,0" type="QString" name="offset"/>
<Option value="3x:0,0,0,0,0,0" type="QString" name="offset_map_unit_scale"/>
<Option value="Point" type="QString" name="offset_unit"/>
<Option value="255,255,255,255" type="QString" name="outline_color"/>
<Option value="0" type="QString" name="outline_width"/>
<Option value="3x:0,0,0,0,0,0" type="QString" name="outline_width_map_unit_scale"/>
<Option value="MM" type="QString" name="outline_width_unit"/>
<Option value="42.4" type="QString" name="size"/>
<Option value="3x:0,0,0,0,0,0" type="QString" name="size_map_unit_scale"/>
<Option value="Point" type="QString" name="size_unit"/>
<Option value="1" type="QString" name="vertical_anchor_point"/>
</Option>
</layer>
</symbol>"""

doc = QDomDocument()
elem = QDomElement()
doc.setContent(font_marker_xml_string)
elem = doc.documentElement()
font_marker = QgsSymbolLayerUtils.loadSymbol(elem, QgsReadWriteContext())
self.assertEqual(font_marker.symbolLayers()[0].character(), chr(14))

font_marker_xml_string = """<symbol clip_to_extent="1" type="marker" is_animated="0" alpha="1" name="symbol" frame_rate="10" force_rhr="0">
<layer pass="0" id="{2aefc556-4eb1-4f56-b96b-e1dea6b58f69}" locked="0" class="FontMarker" enabled="1">
<Option type="Map">
<Option value="0" type="QString" name="angle"/>
<Option value="~!_#!#_!~14~!_#!#_!~" type="QString" name="chr"/>
<Option value="0,0,255,255" type="QString" name="color"/>
<Option value="Arial" type="QString" name="font"/>
<Option value="Italic" type="QString" name="font_style"/>
<Option value="1" type="QString" name="horizontal_anchor_point"/>
<Option value="miter" type="QString" name="joinstyle"/>
<Option value="0,0" type="QString" name="offset"/>
<Option value="3x:0,0,0,0,0,0" type="QString" name="offset_map_unit_scale"/>
<Option value="Point" type="QString" name="offset_unit"/>
<Option value="255,255,255,255" type="QString" name="outline_color"/>
<Option value="0" type="QString" name="outline_width"/>
<Option value="3x:0,0,0,0,0,0" type="QString" name="outline_width_map_unit_scale"/>
<Option value="MM" type="QString" name="outline_width_unit"/>
<Option value="42.4" type="QString" name="size"/>
<Option value="3x:0,0,0,0,0,0" type="QString" name="size_map_unit_scale"/>
<Option value="Point" type="QString" name="size_unit"/>
<Option value="1" type="QString" name="vertical_anchor_point"/>
</Option>
</layer>
</symbol>"""

doc.setContent(font_marker_xml_string)
elem = doc.documentElement()
font_marker = QgsSymbolLayerUtils.loadSymbol(elem, QgsReadWriteContext())
self.assertEqual(font_marker.symbolLayers()[0].character(), chr(14))


if __name__ == '__main__':
unittest.main()

0 comments on commit 62ef8a3

Please sign in to comment.