diff --git a/src/core/qgsogrutils.cpp b/src/core/qgsogrutils.cpp index a751de54da8..8c6800457b4 100644 --- a/src/core/qgsogrutils.cpp +++ b/src/core/qgsogrutils.cpp @@ -26,6 +26,8 @@ #include "qgspolygon.h" #include "qgsmultipolygon.h" #include "qgsmapinfosymbolconverter.h" +#include "qgsfillsymbollayer.h" +#include "qgssymbollayerutils.h" #include #include @@ -1234,6 +1236,9 @@ std::unique_ptr QgsOgrUtils::symbolFromStyleString( const QString &st auto convertColor = []( const QString & string ) -> QColor { + if ( string.isEmpty() ) + return QColor(); + const thread_local QRegularExpression sColorWithAlphaRx = QRegularExpression( QStringLiteral( "^#([0-9a-fA-F]{6})([0-9a-fA-F]{2})$" ) ); const QRegularExpressionMatch match = sColorWithAlphaRx.match( string ); if ( match.hasMatch() ) @@ -1247,10 +1252,8 @@ std::unique_ptr QgsOgrUtils::symbolFromStyleString( const QString &st } }; - if ( type == QgsSymbol::Line && styles.contains( QStringLiteral( "pen" ) ) ) + auto convertPen = [&convertColor, &convertSize, string]( const QVariantMap & lineStyle ) -> std::unique_ptr< QgsSymbol > { - // line symbol type - const QVariantMap lineStyle = styles.value( QStringLiteral( "pen" ) ).toMap(); QColor color = convertColor( lineStyle.value( QStringLiteral( "c" ), QStringLiteral( "#000000" ) ).toString() ); double lineWidth = DEFAULT_SIMPLELINE_WIDTH; @@ -1347,6 +1350,157 @@ std::unique_ptr QgsOgrUtils::symbolFromStyleString( const QString &st simpleLine->setRenderingPass( priority.toInt() ); } return std::make_unique< QgsLineSymbol >( QgsSymbolLayerList() << simpleLine.release() ); + }; + + auto convertBrush = [&convertColor]( const QVariantMap & brushStyle ) -> std::unique_ptr< QgsSymbol > + { + const QColor foreColor = convertColor( brushStyle.value( QStringLiteral( "fc" ), QStringLiteral( "#000000" ) ).toString() ); + const QColor backColor = convertColor( brushStyle.value( QStringLiteral( "bc" ), QString() ).toString() ); + + const QString id = brushStyle.value( QStringLiteral( "id" ) ).toString(); + + // if the pen is a mapinfo brush, use dedicated converter for more accurate results + const thread_local QRegularExpression sMapInfoId = QRegularExpression( QStringLiteral( "mapinfo-brush-(\\d+)" ) ); + const QRegularExpressionMatch match = sMapInfoId.match( id ); + if ( match.hasMatch() ) + { + const int brushId = match.captured( 1 ).toInt(); + QgsMapInfoSymbolConversionContext context; + std::unique_ptr res( QgsMapInfoSymbolConverter::convertFillSymbol( brushId, context, foreColor, backColor ) ); + if ( res ) + return res; + } + + const thread_local QRegularExpression sOgrId = QRegularExpression( QStringLiteral( "ogr-brush-(\\d+)" ) ); + const QRegularExpressionMatch ogrMatch = sOgrId.match( id ); + + Qt::BrushStyle style = Qt::SolidPattern; + if ( ogrMatch.hasMatch() ) + { + const int brushId = ogrMatch.captured( 1 ).toInt(); + switch ( brushId ) + { + case 0: + style = Qt::SolidPattern; + break; + + case 1: + style = Qt::NoBrush; + break; + + case 2: + style = Qt::HorPattern; + break; + + case 3: + style = Qt::VerPattern; + break; + + case 4: + style = Qt::FDiagPattern; + break; + + case 5: + style = Qt::BDiagPattern; + break; + + case 6: + style = Qt::CrossPattern; + break; + + case 7: + style = Qt::DiagCrossPattern; + break; + } + } + + QgsSymbolLayerList layers; + if ( backColor.isValid() && style != Qt::SolidPattern && style != Qt::NoBrush ) + { + std::unique_ptr< QgsSimpleFillSymbolLayer > backgroundFill = std::make_unique< QgsSimpleFillSymbolLayer >( backColor ); + backgroundFill->setLocked( true ); + backgroundFill->setStrokeStyle( Qt::NoPen ); + layers << backgroundFill.release(); + } + + std::unique_ptr< QgsSimpleFillSymbolLayer > foregroundFill = std::make_unique< QgsSimpleFillSymbolLayer >( foreColor ); + foregroundFill->setBrushStyle( style ); + foregroundFill->setStrokeStyle( Qt::NoPen ); + + const QString priority = brushStyle.value( QStringLiteral( "l" ) ).toString(); + if ( !priority.isEmpty() ) + { + foregroundFill->setRenderingPass( priority.toInt() ); + } + layers << foregroundFill.release(); + return std::make_unique< QgsFillSymbol >( layers ); + }; + + switch ( type ) + { + case QgsSymbol::Marker: + break; + + case QgsSymbol::Line: + if ( styles.contains( QStringLiteral( "pen" ) ) ) + { + // line symbol type + const QVariantMap lineStyle = styles.value( QStringLiteral( "pen" ) ).toMap(); + return convertPen( lineStyle ); + } + else + { + return nullptr; + } + + case QgsSymbol::Fill: + { + std::unique_ptr< QgsSymbol > fillSymbol = std::make_unique< QgsFillSymbol >(); + if ( styles.contains( QStringLiteral( "brush" ) ) ) + { + const QVariantMap brushStyle = styles.value( QStringLiteral( "brush" ) ).toMap(); + fillSymbol = convertBrush( brushStyle ); + } + else + { + std::unique_ptr< QgsSimpleFillSymbolLayer > emptyFill = std::make_unique< QgsSimpleFillSymbolLayer >(); + emptyFill->setBrushStyle( Qt::NoBrush ); + fillSymbol = std::make_unique< QgsFillSymbol >( QgsSymbolLayerList() << emptyFill.release() ); + } + + std::unique_ptr< QgsSymbol > penSymbol; + if ( styles.contains( QStringLiteral( "pen" ) ) ) + { + const QVariantMap lineStyle = styles.value( QStringLiteral( "pen" ) ).toMap(); + penSymbol = convertPen( lineStyle ); + } + + if ( penSymbol ) + { + const int count = penSymbol->symbolLayerCount(); + + if ( count == 1 ) + { + // if only one pen symbol layer, let's try and combine it with the topmost brush layer, so that the resultant QGIS symbol is simpler + if ( QgsSymbolLayerUtils::condenseFillAndOutline( dynamic_cast< QgsFillSymbolLayer * >( fillSymbol->symbolLayer( fillSymbol->symbolLayerCount() - 1 ) ), + dynamic_cast< QgsLineSymbolLayer * >( penSymbol->symbolLayer( 0 ) ) ) ) + return fillSymbol; + } + + for ( int i = 0; i < count; ++i ) + { + std::unique_ptr< QgsSymbolLayer > layer( penSymbol->takeSymbolLayer( 0 ) ); + layer->setLocked( true ); + fillSymbol->appendSymbolLayer( layer.release() ); + } + } + + return fillSymbol; + } + + case QgsSymbol::Hybrid: + break; } + return nullptr; } diff --git a/src/core/symbology/qgsmapinfosymbolconverter.cpp b/src/core/symbology/qgsmapinfosymbolconverter.cpp index a97f4933a25..4e7b2736956 100644 --- a/src/core/symbology/qgsmapinfosymbolconverter.cpp +++ b/src/core/symbology/qgsmapinfosymbolconverter.cpp @@ -17,6 +17,7 @@ #include "qgslogger.h" #include "qgslinesymbollayer.h" #include "qgsmarkersymbollayer.h" +#include "qgsfillsymbollayer.h" // // QgsMapInfoSymbolConversionContext @@ -1094,3 +1095,294 @@ QgsLineSymbol *QgsMapInfoSymbolConverter::convertLineSymbol( int identifier, Qgs return symbol.release(); } + +QgsFillSymbol *QgsMapInfoSymbolConverter::convertFillSymbol( int identifier, QgsMapInfoSymbolConversionContext &context, const QColor &foreColor, const QColor &backColor ) +{ + Qt::BrushStyle style = Qt::SolidPattern; + + bool useLineFill = false; + bool crossFill = false; + double lineAngle = 0; + double lineWidth = 0; + double lineSpacing = 1; + switch ( identifier ) + { + case 0: + case 1: + style = Qt::NoBrush; + break; + + case 2: + style = Qt::SolidPattern; + break; + + case 3: + case 19: + style = Qt::HorPattern; + break; + + case 4: + case 24: + style = Qt::VerPattern; + break; + + case 5: + case 34: + style = Qt::FDiagPattern; + break; + + case 6: + case 29: + style = Qt::BDiagPattern; + break; + + case 7: + case 39: + style = Qt::CrossPattern; + break; + + case 8: + case 44: + style = Qt::DiagCrossPattern; + break; + + case 12: + style = Qt::Dense1Pattern; + break; + + case 13: + style = Qt::Dense2Pattern; + break; + + case 14: + style = Qt::Dense3Pattern; + break; + + case 15: + style = Qt::Dense4Pattern; + break; + + case 16: + style = Qt::Dense5Pattern; + break; + + case 17: + style = Qt::Dense6Pattern; + break; + + case 18: + style = Qt::Dense7Pattern; + break; + + case 20: + useLineFill = true; + lineAngle = 0; + lineSpacing = 6; + lineWidth = 1.2; + break; + + case 21: + useLineFill = true; + lineAngle = 0; + lineSpacing = 4; + lineWidth = 0.8; + break; + + case 22: + useLineFill = true; + lineAngle = 0; + lineSpacing = 3.4; + lineWidth = 1.2; + break; + + case 23: + useLineFill = true; + lineAngle = 0; + lineSpacing = 3.0; + lineWidth = 1.0; + break; + + case 25: + useLineFill = true; + lineAngle = 90; + lineSpacing = 6; + lineWidth = 1.2; + break; + + case 26: + useLineFill = true; + lineAngle = 90; + lineSpacing = 4; + lineWidth = 0.8; + break; + + case 27: + useLineFill = true; + lineAngle = 90; + lineSpacing = 3.4; + lineWidth = 1.2; + break; + + case 28: + useLineFill = true; + lineAngle = 90; + lineSpacing = 3.0; + lineWidth = 1.0; + break; + + case 30: + useLineFill = true; + lineAngle = 45; + lineSpacing = 6; + lineWidth = 1.2; + break; + + case 31: + useLineFill = true; + lineAngle = 45; + lineSpacing = 4; + lineWidth = 0.8; + break; + + case 32: + useLineFill = true; + lineAngle = 45; + lineSpacing = 3.4; + lineWidth = 1.2; + break; + + case 33: + useLineFill = true; + lineAngle = 45; + lineSpacing = 3.0; + lineWidth = 1.0; + break; + + case 35: + useLineFill = true; + lineAngle = 135; + lineSpacing = 6; + lineWidth = 1.2; + break; + + case 36: + useLineFill = true; + lineAngle = 135; + lineSpacing = 4; + lineWidth = 0.8; + break; + + case 37: + useLineFill = true; + lineAngle = 135; + lineSpacing = 3.4; + lineWidth = 1.2; + break; + + case 38: + useLineFill = true; + lineAngle = 135; + lineSpacing = 3.0; + lineWidth = 1.0; + break; + + case 40: + useLineFill = true; + crossFill = true; + lineAngle = 0; + lineSpacing = 6; + lineWidth = 1.2; + break; + + case 41: + useLineFill = true; + crossFill = true; + lineAngle = 0; + lineSpacing = 4; + lineWidth = 0.8; + break; + + case 42: + useLineFill = true; + crossFill = true; + lineAngle = 0; + lineSpacing = 3.4; + lineWidth = 1.2; + break; + + case 43: + useLineFill = true; + crossFill = true; + lineAngle = 0; + lineSpacing = 3.0; + lineWidth = 1.0; + break; + + case 45: + useLineFill = true; + crossFill = true; + lineAngle = 45; + lineSpacing = 6; + lineWidth = 1.2; + break; + + case 46: + useLineFill = true; + crossFill = true; + lineAngle = 45; + lineSpacing = 4; + lineWidth = 0.8; + break; + + case 47: + useLineFill = true; + crossFill = true; + lineAngle = 45; + lineSpacing = 3.4; + lineWidth = 1.2; + break; + + default: + context.pushWarning( QObject::tr( "The brush style is not supported in QGIS" ) ); + return nullptr; + } + + QgsSymbolLayerList layers; + if ( backColor.isValid() && style != Qt::SolidPattern && ( useLineFill || style != Qt::NoBrush ) ) + { + std::unique_ptr< QgsSimpleFillSymbolLayer > backgroundFill = std::make_unique< QgsSimpleFillSymbolLayer >( backColor ); + backgroundFill->setLocked( true ); + backgroundFill->setStrokeStyle( Qt::NoPen ); + layers << backgroundFill.release(); + } + + if ( !useLineFill ) + { + std::unique_ptr< QgsSimpleFillSymbolLayer > foregroundFill = std::make_unique< QgsSimpleFillSymbolLayer >( foreColor ); + foregroundFill->setBrushStyle( style ); + foregroundFill->setStrokeStyle( Qt::NoPen ); + layers << foregroundFill.release(); + } + else + { + std::unique_ptr< QgsLinePatternFillSymbolLayer > lineFill = std::make_unique< QgsLinePatternFillSymbolLayer >(); + + std::unique_ptr< QgsSimpleLineSymbolLayer > simpleLine = std::make_unique< QgsSimpleLineSymbolLayer >( foreColor, lineWidth ); + simpleLine->setWidthUnit( QgsUnitTypes::RenderPoints ); + lineFill->setSubSymbol( new QgsLineSymbol( QgsSymbolLayerList() << simpleLine.release() ) ); + + lineFill->setDistance( lineSpacing ); + lineFill->setDistanceUnit( QgsUnitTypes::RenderPoints ); + lineFill->setLineAngle( lineAngle ); + + if ( crossFill ) + { + std::unique_ptr< QgsLinePatternFillSymbolLayer > lineFill2( lineFill->clone() ); + lineFill2->setLineAngle( lineFill->lineAngle() + 90 ); + layers << lineFill2.release(); + } + + layers << lineFill.release(); + } + return new QgsFillSymbol( layers ); +} diff --git a/src/core/symbology/qgsmapinfosymbolconverter.h b/src/core/symbology/qgsmapinfosymbolconverter.h index 6b3aa0b8122..fa17bb6bc1a 100644 --- a/src/core/symbology/qgsmapinfosymbolconverter.h +++ b/src/core/symbology/qgsmapinfosymbolconverter.h @@ -20,8 +20,10 @@ #include "qgis_sip.h" #include "qgsunittypes.h" #include +#include class QgsLineSymbol; +class QgsFillSymbol; /** * Context for a MapInfo symbol conversion operation. @@ -71,6 +73,13 @@ class CORE_EXPORT QgsMapInfoSymbolConverter */ static QgsLineSymbol *convertLineSymbol( int identifier, QgsMapInfoSymbolConversionContext &context, const QColor &foreColor, double size, QgsUnitTypes::RenderUnit sizeUnit, bool interleaved = false ) SIP_FACTORY; + /** + * Converts the MapInfo fill symbol with the specified \a identifier to a QgsFillSymbol. + * + * The caller takes ownership of the returned symbol. + */ + static QgsFillSymbol *convertFillSymbol( int identifier, QgsMapInfoSymbolConversionContext &context, const QColor &foreColor, const QColor &backColor = QColor() ) SIP_FACTORY; + }; #endif // QGSMAPINFOSYMBOLCONVERTER_H diff --git a/tests/src/core/testqgsogrutils.cpp b/tests/src/core/testqgsogrutils.cpp index 58f5f6a92de..cc802a4da00 100644 --- a/tests/src/core/testqgsogrutils.cpp +++ b/tests/src/core/testqgsogrutils.cpp @@ -29,6 +29,7 @@ #include "qgspoint.h" #include "qgsogrproxytextcodec.h" #include "qgslinesymbollayer.h" +#include "qgsfillsymbollayer.h" class TestQgsOgrUtils: public QObject { @@ -591,10 +592,49 @@ void TestQgsOgrUtils::convertStyleString() symbol = QgsOgrUtils::symbolFromStyleString( QStringLiteral( R"""(PEN(c:#FFFF007F,w:4.000000pt);BRUSH(fc:#00FF007F))""" ), QgsSymbol::Line ); QVERIFY( symbol ); QCOMPARE( symbol->symbolLayerCount(), 1 ); - QCOMPARE( dynamic_cast( symbol->symbolLayer( 0 ) )->color().name(), QStringLiteral( "#ffff00" ) ); - QCOMPARE( dynamic_cast( symbol->symbolLayer( 0 ) )->color().alpha(), 127 ); - QCOMPARE( dynamic_cast( symbol->symbolLayer( 0 ) )->width(), 4.0 ); - QCOMPARE( dynamic_cast( symbol->symbolLayer( 0 ) )->widthUnit(), QgsUnitTypes::RenderPoints ); + QCOMPARE( qgis::down_cast( symbol->symbolLayer( 0 ) )->color().name(), QStringLiteral( "#ffff00" ) ); + QCOMPARE( qgis::down_cast( symbol->symbolLayer( 0 ) )->color().alpha(), 127 ); + QCOMPARE( qgis::down_cast( symbol->symbolLayer( 0 ) )->width(), 4.0 ); + QCOMPARE( qgis::down_cast( symbol->symbolLayer( 0 ) )->widthUnit(), QgsUnitTypes::RenderPoints ); + + // brush + symbol = QgsOgrUtils::symbolFromStyleString( QStringLiteral( R"""(BRUSH(fc:#00FF007F))""" ), QgsSymbol::Fill ); + QVERIFY( symbol ); + QCOMPARE( symbol->symbolLayerCount(), 1 ); + QCOMPARE( qgis::down_cast( symbol->symbolLayer( 0 ) )->color().name(), QStringLiteral( "#00ff00" ) ); + QCOMPARE( qgis::down_cast( symbol->symbolLayer( 0 ) )->color().alpha(), 127 ); + QCOMPARE( qgis::down_cast( symbol->symbolLayer( 0 ) )->brushStyle(), Qt::SolidPattern ); + QCOMPARE( qgis::down_cast( symbol->symbolLayer( 0 ) )->strokeStyle(), Qt::NoPen ); + + symbol = QgsOgrUtils::symbolFromStyleString( QStringLiteral( R"""(BRUSH(fc:#00FF007F,bc:#00000087,id:ogr-brush-6))""" ), QgsSymbol::Fill ); + QVERIFY( symbol ); + QCOMPARE( symbol->symbolLayerCount(), 2 ); + QCOMPARE( qgis::down_cast( symbol->symbolLayer( 0 ) )->color().name(), QStringLiteral( "#000000" ) ); + QCOMPARE( qgis::down_cast( symbol->symbolLayer( 0 ) )->color().alpha(), 135 ); + QCOMPARE( qgis::down_cast( symbol->symbolLayer( 0 ) )->brushStyle(), Qt::SolidPattern ); + QCOMPARE( qgis::down_cast( symbol->symbolLayer( 0 ) )->strokeStyle(), Qt::NoPen ); + QCOMPARE( qgis::down_cast( symbol->symbolLayer( 1 ) )->color().name(), QStringLiteral( "#00ff00" ) ); + QCOMPARE( qgis::down_cast( symbol->symbolLayer( 1 ) )->color().alpha(), 127 ); + QCOMPARE( qgis::down_cast( symbol->symbolLayer( 1 ) )->brushStyle(), Qt::CrossPattern ); + QCOMPARE( qgis::down_cast( symbol->symbolLayer( 1 ) )->strokeStyle(), Qt::NoPen ); + + // brush with pen + symbol = QgsOgrUtils::symbolFromStyleString( QStringLiteral( R"""(PEN(c:#FFFF007F,w:4.000000pt);BRUSH(fc:#00FF007F))""" ), QgsSymbol::Fill ); + QVERIFY( symbol ); + QCOMPARE( symbol->symbolLayerCount(), 1 ); + QCOMPARE( qgis::down_cast( symbol->symbolLayer( 0 ) )->color().name(), QStringLiteral( "#00ff00" ) ); + QCOMPARE( qgis::down_cast( symbol->symbolLayer( 0 ) )->color().alpha(), 127 ); + QCOMPARE( qgis::down_cast( symbol->symbolLayer( 0 ) )->brushStyle(), Qt::SolidPattern ); + QCOMPARE( qgis::down_cast( symbol->symbolLayer( 0 ) )->strokeStyle(), Qt::SolidLine ); + QCOMPARE( qgis::down_cast( symbol->symbolLayer( 0 ) )->strokeColor().name(), QStringLiteral( "#ffff00" ) ); + + // no brush, but need fill symbol + symbol = QgsOgrUtils::symbolFromStyleString( QStringLiteral( R"""(PEN(c:#FFFF007F,w:4.000000pt))""" ), QgsSymbol::Fill ); + QVERIFY( symbol ); + QCOMPARE( symbol->symbolLayerCount(), 1 ); + QCOMPARE( qgis::down_cast( symbol->symbolLayer( 0 ) )->brushStyle(), Qt::NoBrush ); + QCOMPARE( qgis::down_cast( symbol->symbolLayer( 0 ) )->strokeStyle(), Qt::SolidLine ); + QCOMPARE( qgis::down_cast( symbol->symbolLayer( 0 ) )->strokeColor().name(), QStringLiteral( "#ffff00" ) ); } QGSTEST_MAIN( TestQgsOgrUtils ) diff --git a/tests/src/python/test_qgsembeddedsymbolrenderer.py b/tests/src/python/test_qgsembeddedsymbolrenderer.py index 532bfd9b863..c59b9adba45 100644 --- a/tests/src/python/test_qgsembeddedsymbolrenderer.py +++ b/tests/src/python/test_qgsembeddedsymbolrenderer.py @@ -174,6 +174,26 @@ class TestQgsEmbeddedSymbolRenderer(unittest.TestCase): renderchecker.setControlName('expected_embedded_mapinfo_lines') self.assertTrue(renderchecker.runTest('embedded_mapinfo_lines')) + def testMapFillLineSymbolConversion(self): + line_layer = QgsVectorLayer(TEST_DATA_DIR + '/mapinfo/fill_styles.TAB', 'Fills', 'ogr') + + renderer = QgsEmbeddedSymbolRenderer(defaultSymbol=QgsLineSymbol.createSimple({})) + line_layer.setRenderer(renderer) + + mapsettings = QgsMapSettings() + mapsettings.setOutputSize(QSize(2000, 4000)) + mapsettings.setOutputDpi(96) + mapsettings.setMagnificationFactor(2) + mapsettings.setExtent(line_layer.extent().buffered(0.1)) + + mapsettings.setLayers([line_layer]) + + renderchecker = QgsMultiRenderChecker() + renderchecker.setMapSettings(mapsettings) + renderchecker.setControlPathPrefix('embedded') + renderchecker.setControlName('expected_embedded_mapinfo_fills') + self.assertTrue(renderchecker.runTest('embedded_mapinfo_fills')) + if __name__ == '__main__': unittest.main() diff --git a/tests/testdata/control_images/embedded/expected_embedded_mapinfo_fills/expected_embedded_mapinfo_fills.png b/tests/testdata/control_images/embedded/expected_embedded_mapinfo_fills/expected_embedded_mapinfo_fills.png new file mode 100644 index 00000000000..c58eeb34e45 Binary files /dev/null and b/tests/testdata/control_images/embedded/expected_embedded_mapinfo_fills/expected_embedded_mapinfo_fills.png differ diff --git a/tests/testdata/mapinfo/fill_styles.DAT b/tests/testdata/mapinfo/fill_styles.DAT new file mode 100644 index 00000000000..1d99ab173e3 Binary files /dev/null and b/tests/testdata/mapinfo/fill_styles.DAT differ diff --git a/tests/testdata/mapinfo/fill_styles.ID b/tests/testdata/mapinfo/fill_styles.ID new file mode 100644 index 00000000000..e17e0d54f0b Binary files /dev/null and b/tests/testdata/mapinfo/fill_styles.ID differ diff --git a/tests/testdata/mapinfo/fill_styles.MAP b/tests/testdata/mapinfo/fill_styles.MAP new file mode 100644 index 00000000000..9e47db85f56 Binary files /dev/null and b/tests/testdata/mapinfo/fill_styles.MAP differ diff --git a/tests/testdata/mapinfo/fill_styles.TAB b/tests/testdata/mapinfo/fill_styles.TAB new file mode 100644 index 00000000000..9744b299a4e --- /dev/null +++ b/tests/testdata/mapinfo/fill_styles.TAB @@ -0,0 +1,8 @@ +!table +!version 450 +!charset WindowsLatin1 + +Definition Table + Type NATIVE Charset "WindowsLatin1" + Fields 1 + Field1 Char (10) ;