int QFontProto::capitalization() const { QFont *item = qscriptvalue_cast<QFont*>(thisObject()); if (item) return item->capitalization(); return 0; }
void QgsAbstractVectorLayerLabeling::writeTextSymbolizer( QDomNode &parent, QgsPalLayerSettings &settings, const QgsStringMap &props ) const { QDomDocument doc = parent.ownerDocument(); // text symbolizer QDomElement textSymbolizerElement = doc.createElement( QStringLiteral( "se:TextSymbolizer" ) ); parent.appendChild( textSymbolizerElement ); // label QgsTextFormat format = settings.format(); QFont font = format.font(); QDomElement labelElement = doc.createElement( QStringLiteral( "se:Label" ) ); textSymbolizerElement.appendChild( labelElement ); if ( settings.isExpression ) { labelElement.appendChild( doc.createComment( QStringLiteral( "SE Export for %1 not implemented yet" ).arg( settings.getLabelExpression()->dump() ) ) ); labelElement.appendChild( doc.createTextNode( "Placeholder" ) ); } else { if ( font.capitalization() == QFont::AllUppercase ) { appendSimpleFunction( doc, labelElement, QStringLiteral( "strToUpperCase" ), settings.fieldName ); } else if ( font.capitalization() == QFont::AllLowercase ) { appendSimpleFunction( doc, labelElement, QStringLiteral( "strToLowerCase" ), settings.fieldName ); } else if ( font.capitalization() == QFont::Capitalize ) { appendSimpleFunction( doc, labelElement, QStringLiteral( "strCapitalize" ), settings.fieldName ); } else { QDomElement propertyNameElement = doc.createElement( QStringLiteral( "ogc:PropertyName" ) ); propertyNameElement.appendChild( doc.createTextNode( settings.fieldName ) ); labelElement.appendChild( propertyNameElement ); } } // font QDomElement fontElement = doc.createElement( QStringLiteral( "se:Font" ) ); textSymbolizerElement.appendChild( fontElement ); fontElement.appendChild( QgsSymbolLayerUtils::createSvgParameterElement( doc, QStringLiteral( "font-family" ), font.family() ) ); double fontSize = QgsSymbolLayerUtils::rescaleUom( format.size(), format.sizeUnit(), props ); fontElement.appendChild( QgsSymbolLayerUtils::createSvgParameterElement( doc, QStringLiteral( "font-size" ), QString::number( fontSize ) ) ); if ( format.font().italic() ) { fontElement.appendChild( QgsSymbolLayerUtils::createSvgParameterElement( doc, QStringLiteral( "font-style" ), QStringLiteral( "italic" ) ) ); } if ( format.font().bold() ) { fontElement.appendChild( QgsSymbolLayerUtils::createSvgParameterElement( doc, QStringLiteral( "font-weight" ), QStringLiteral( "bold" ) ) ); } // label placement QDomElement labelPlacement = doc.createElement( QStringLiteral( "se:LabelPlacement" ) ); textSymbolizerElement.appendChild( labelPlacement ); double maxDisplacement = 0; double repeatDistance = 0; switch ( settings.placement ) { case QgsPalLayerSettings::OverPoint: { QDomElement pointPlacement = doc.createElement( "se:PointPlacement" ); labelPlacement.appendChild( pointPlacement ); // anchor point QPointF anchor = quadOffsetToSldAnchor( settings.quadOffset ); QgsSymbolLayerUtils::createAnchorPointElement( doc, pointPlacement, anchor ); // displacement if ( settings.xOffset > 0 || settings.yOffset > 0 ) { QgsUnitTypes::RenderUnit offsetUnit = settings.offsetUnits; double dx = QgsSymbolLayerUtils::rescaleUom( settings.xOffset, offsetUnit, props ); double dy = QgsSymbolLayerUtils::rescaleUom( settings.yOffset, offsetUnit, props ); QgsSymbolLayerUtils::createDisplacementElement( doc, pointPlacement, QPointF( dx, dy ) ); } // rotation if ( settings.angleOffset != 0 ) { QDomElement rotation = doc.createElement( "se:Rotation" ); pointPlacement.appendChild( rotation ); rotation.appendChild( doc.createTextNode( QString::number( settings.angleOffset ) ) ); } } break; case QgsPalLayerSettings::AroundPoint: case QgsPalLayerSettings::OrderedPositionsAroundPoint: { QDomElement pointPlacement = doc.createElement( "se:PointPlacement" ); labelPlacement.appendChild( pointPlacement ); // SLD cannot do either, but let's do a best effort setting the distance using // anchor point and displacement QgsSymbolLayerUtils::createAnchorPointElement( doc, pointPlacement, QPointF( 0, 0.5 ) ); QgsUnitTypes::RenderUnit distUnit = settings.distUnits; double radius = QgsSymbolLayerUtils::rescaleUom( settings.dist, distUnit, props ); double offset = std::sqrt( radius * radius / 2 ); // make it start top/right maxDisplacement = radius + 1; // lock the distance QgsSymbolLayerUtils::createDisplacementElement( doc, pointPlacement, QPointF( offset, offset ) ); } break; case QgsPalLayerSettings::Horizontal: case QgsPalLayerSettings::Free: { // still a point placement (for "free" it's a fallback, there is no SLD equivalent) QDomElement pointPlacement = doc.createElement( "se:PointPlacement" ); labelPlacement.appendChild( pointPlacement ); QgsSymbolLayerUtils::createAnchorPointElement( doc, pointPlacement, QPointF( 0.5, 0.5 ) ); QgsUnitTypes::RenderUnit distUnit = settings.distUnits; double dist = QgsSymbolLayerUtils::rescaleUom( settings.dist, distUnit, props ); QgsSymbolLayerUtils::createDisplacementElement( doc, pointPlacement, QPointF( 0, dist ) ); break; } case QgsPalLayerSettings::Line: case QgsPalLayerSettings::Curved: case QgsPalLayerSettings::PerimeterCurved: { QDomElement linePlacement = doc.createElement( "se:LinePlacement" ); labelPlacement.appendChild( linePlacement ); // perpendicular distance if required if ( settings.dist > 0 ) { QgsUnitTypes::RenderUnit distUnit = settings.distUnits; double dist = QgsSymbolLayerUtils::rescaleUom( settings.dist, distUnit, props ); QDomElement perpendicular = doc.createElement( "se:PerpendicularOffset" ); linePlacement.appendChild( perpendicular ); perpendicular.appendChild( doc.createTextNode( qgsDoubleToString( dist, 2 ) ) ); } // repeat distance if required if ( settings.repeatDistance > 0 ) { QDomElement repeat = doc.createElement( "se:Repeat" ); linePlacement.appendChild( repeat ); repeat.appendChild( doc.createTextNode( QStringLiteral( "true" ) ) ); QDomElement gap = doc.createElement( "se:Gap" ); linePlacement.appendChild( gap ); repeatDistance = QgsSymbolLayerUtils::rescaleUom( settings.repeatDistance, settings.repeatDistanceUnit, props ); gap.appendChild( doc.createTextNode( qgsDoubleToString( repeatDistance, 2 ) ) ); } // always generalized QDomElement generalize = doc.createElement( "se:GeneralizeLine" ); linePlacement.appendChild( generalize ); generalize.appendChild( doc.createTextNode( QStringLiteral( "true" ) ) ); } break; } // halo QgsTextBufferSettings buffer = format.buffer(); if ( buffer.enabled() ) { QDomElement haloElement = doc.createElement( QStringLiteral( "se:Halo" ) ); textSymbolizerElement.appendChild( haloElement ); QDomElement radiusElement = doc.createElement( QStringLiteral( "se:Radius" ) ); haloElement.appendChild( radiusElement ); // the SLD uses a radius, which is actually half of the link thickness the buffer size specifies double radius = QgsSymbolLayerUtils::rescaleUom( buffer.size(), buffer.sizeUnit(), props ) / 2; radiusElement.appendChild( doc.createTextNode( qgsDoubleToString( radius ) ) ); QDomElement fillElement = doc.createElement( QStringLiteral( "se:Fill" ) ); haloElement.appendChild( fillElement ); fillElement.appendChild( QgsSymbolLayerUtils::createSvgParameterElement( doc, QStringLiteral( "fill" ), buffer.color().name() ) ); if ( buffer.opacity() != 1 ) { fillElement.appendChild( QgsSymbolLayerUtils::createSvgParameterElement( doc, QStringLiteral( "fill-opacity" ), QString::number( buffer.opacity() ) ) ); } } // fill QDomElement fillElement = doc.createElement( QStringLiteral( "se:Fill" ) ); textSymbolizerElement.appendChild( fillElement ); fillElement.appendChild( QgsSymbolLayerUtils::createSvgParameterElement( doc, QStringLiteral( "fill" ), format.color().name() ) ); if ( format.opacity() != 1 ) { fillElement.appendChild( QgsSymbolLayerUtils::createSvgParameterElement( doc, QStringLiteral( "fill-opacity" ), QString::number( format.opacity() ) ) ); } // background graphic (not supported by SE 1.1, but supported by the GeoTools ecosystem as an extension) QgsTextBackgroundSettings background = format.background(); if ( background.enabled() ) { std::unique_ptr<QgsMarkerSymbolLayer> layer = backgroundToMarkerLayer( background ); layer->writeSldMarker( doc, textSymbolizerElement, props ); } // priority and zIndex, the default values are 0 and 5 in qgis (and between 0 and 10), // in the GeoTools ecosystem there is a single priority value set at 1000 by default if ( settings.priority != 5 || settings.zIndex > 0 ) { QDomElement priorityElement = doc.createElement( QStringLiteral( "se:Priority" ) ); textSymbolizerElement.appendChild( priorityElement ); int priority = 500 + 1000 * settings.zIndex + ( settings.priority - 5 ) * 100; if ( settings.priority == 0 && settings.zIndex > 0 ) { // small adjustment to make sure labels in z index n+1 are all above level n despite the priority value priority += 1; } priorityElement.appendChild( doc.createTextNode( QString::number( priority ) ) ); } // vendor options for text appearance if ( font.underline() ) { QDomElement vo = QgsSymbolLayerUtils::createVendorOptionElement( doc, QStringLiteral( "underlineText" ), QStringLiteral( "true" ) ); textSymbolizerElement.appendChild( vo ); } if ( font.strikeOut() ) { QDomElement vo = QgsSymbolLayerUtils::createVendorOptionElement( doc, QStringLiteral( "strikethroughText" ), QStringLiteral( "true" ) ); textSymbolizerElement.appendChild( vo ); } // vendor options for text positioning if ( maxDisplacement > 0 ) { QDomElement vo = QgsSymbolLayerUtils::createVendorOptionElement( doc, QStringLiteral( "maxDisplacement" ), qgsDoubleToString( maxDisplacement, 2 ) ); textSymbolizerElement.appendChild( vo ); } if ( settings.placement == QgsPalLayerSettings::Curved || settings.placement == QgsPalLayerSettings::PerimeterCurved ) { QDomElement vo = QgsSymbolLayerUtils::createVendorOptionElement( doc, QStringLiteral( "followLine" ), QStringLiteral( "true" ) ); textSymbolizerElement.appendChild( vo ); if ( settings.maxCurvedCharAngleIn > 0 || settings.maxCurvedCharAngleOut > 0 ) { // SLD has no notion for this, the GeoTools ecosystem can only do a single angle double angle = std::min( std::fabs( settings.maxCurvedCharAngleIn ), std::fabs( settings.maxCurvedCharAngleOut ) ); QDomElement vo = QgsSymbolLayerUtils::createVendorOptionElement( doc, QStringLiteral( "maxAngleDelta" ), qgsDoubleToString( angle ) ); textSymbolizerElement.appendChild( vo ); } } if ( repeatDistance > 0 ) { QDomElement vo = QgsSymbolLayerUtils::createVendorOptionElement( doc, QStringLiteral( "repeat" ), qgsDoubleToString( repeatDistance, 2 ) ); textSymbolizerElement.appendChild( vo ); } // miscellaneous options if ( settings.displayAll ) { QDomElement vo = QgsSymbolLayerUtils::createVendorOptionElement( doc, QStringLiteral( "conflictResolution" ), QStringLiteral( "false" ) ); textSymbolizerElement.appendChild( vo ); } if ( settings.upsidedownLabels == QgsPalLayerSettings::ShowAll ) { QDomElement vo = QgsSymbolLayerUtils::createVendorOptionElement( doc, QStringLiteral( "forceLeftToRight" ), QStringLiteral( "false" ) ); textSymbolizerElement.appendChild( vo ); } if ( settings.mergeLines ) { QDomElement vo = QgsSymbolLayerUtils::createVendorOptionElement( doc, QStringLiteral( "group" ), QStringLiteral( "yes" ) ); textSymbolizerElement.appendChild( vo ); if ( settings.labelPerPart ) { QDomElement vo = QgsSymbolLayerUtils::createVendorOptionElement( doc, QStringLiteral( "labelAllGroup" ), QStringLiteral( "true" ) ); textSymbolizerElement.appendChild( vo ); } } // background symbol resize handling if ( background.enabled() ) { // enable resizing if needed switch ( background.sizeType() ) { case QgsTextBackgroundSettings::SizeBuffer: { QString resizeType; if ( background.type() == QgsTextBackgroundSettings::ShapeRectangle || background.type() == QgsTextBackgroundSettings::ShapeEllipse ) { resizeType = QStringLiteral( "stretch" ); } else { resizeType = QStringLiteral( "proportional" ); } QDomElement voResize = QgsSymbolLayerUtils::createVendorOptionElement( doc, QStringLiteral( "graphic-resize" ), resizeType ); textSymbolizerElement.appendChild( voResize ); // now hadle margin QSizeF size = background.size(); if ( size.width() > 0 || size.height() > 0 ) { double x = QgsSymbolLayerUtils::rescaleUom( size.width(), background.sizeUnit(), props ); double y = QgsSymbolLayerUtils::rescaleUom( size.height(), background.sizeUnit(), props ); // in case of ellipse qgis pads the size generously to make sure the text is inside the ellipse // the following seems to do the trick and keep visual output similar if ( background.type() == QgsTextBackgroundSettings::ShapeEllipse ) { x += fontSize / 2; y += fontSize; } QString resizeSpec = QString( "%1 %2" ).arg( qgsDoubleToString( x, 2 ), qgsDoubleToString( y, 2 ) ); QDomElement voMargin = QgsSymbolLayerUtils::createVendorOptionElement( doc, QStringLiteral( "graphic-margin" ), resizeSpec ); textSymbolizerElement.appendChild( voMargin ); } break; } case QgsTextBackgroundSettings::SizeFixed: case QgsTextBackgroundSettings::SizePercent: // nothing to do here break; } } }