summaryrefslogtreecommitdiffstats
path: root/vcl/source/filter
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--vcl/source/filter/FilterConfigCache.cxx489
-rw-r--r--vcl/source/filter/FilterConfigCache.hxx100
-rw-r--r--vcl/source/filter/FilterConfigItem.cxx389
-rw-r--r--vcl/source/filter/GraphicFormatDetector.cxx859
-rw-r--r--vcl/source/filter/GraphicNativeMetadata.cxx59
-rw-r--r--vcl/source/filter/GraphicNativeTransform.cxx164
-rw-r--r--vcl/source/filter/bmp/BmpReader.cxx30
-rw-r--r--vcl/source/filter/bmp/BmpWriter.cxx40
-rw-r--r--vcl/source/filter/egif/egif.cxx549
-rw-r--r--vcl/source/filter/egif/giflzwc.cxx225
-rw-r--r--vcl/source/filter/egif/giflzwc.hxx55
-rw-r--r--vcl/source/filter/eps/eps.cxx2667
-rw-r--r--vcl/source/filter/etiff/etiff.cxx586
-rw-r--r--vcl/source/filter/graphicfilter.cxx2056
-rw-r--r--vcl/source/filter/graphicfilter2.cxx1229
-rw-r--r--vcl/source/filter/graphicfilter_internal.hxx32
-rw-r--r--vcl/source/filter/idxf/dxf2mtf.cxx902
-rw-r--r--vcl/source/filter/idxf/dxf2mtf.hxx119
-rw-r--r--vcl/source/filter/idxf/dxfblkrd.cxx125
-rw-r--r--vcl/source/filter/idxf/dxfblkrd.hxx83
-rw-r--r--vcl/source/filter/idxf/dxfentrd.cxx848
-rw-r--r--vcl/source/filter/idxf/dxfentrd.hxx538
-rw-r--r--vcl/source/filter/idxf/dxfgrprd.cxx213
-rw-r--r--vcl/source/filter/idxf/dxfgrprd.hxx115
-rw-r--r--vcl/source/filter/idxf/dxfreprd.cxx480
-rw-r--r--vcl/source/filter/idxf/dxfreprd.hxx129
-rw-r--r--vcl/source/filter/idxf/dxftblrd.cxx381
-rw-r--r--vcl/source/filter/idxf/dxftblrd.hxx175
-rw-r--r--vcl/source/filter/idxf/dxfvec.cxx232
-rw-r--r--vcl/source/filter/idxf/dxfvec.hxx218
-rw-r--r--vcl/source/filter/idxf/idxf.cxx43
-rw-r--r--vcl/source/filter/ieps/ieps.cxx825
-rw-r--r--vcl/source/filter/igif/decode.cxx214
-rw-r--r--vcl/source/filter/igif/decode.hxx66
-rw-r--r--vcl/source/filter/igif/gifread.cxx1002
-rw-r--r--vcl/source/filter/igif/gifread.hxx30
-rw-r--r--vcl/source/filter/imet/ios2met.cxx2883
-rw-r--r--vcl/source/filter/ipbm/ipbm.cxx541
-rw-r--r--vcl/source/filter/ipcd/ipcd.cxx352
-rw-r--r--vcl/source/filter/ipcx/ipcx.cxx411
-rw-r--r--vcl/source/filter/ipdf/pdfcompat.cxx114
-rw-r--r--vcl/source/filter/ipdf/pdfdocument.cxx3325
-rw-r--r--vcl/source/filter/ipdf/pdfread.cxx399
-rw-r--r--vcl/source/filter/ipict/ipict.cxx2041
-rw-r--r--vcl/source/filter/ipict/shape.cxx259
-rw-r--r--vcl/source/filter/ipict/shape.hxx57
-rw-r--r--vcl/source/filter/ipsd/ipsd.cxx776
-rw-r--r--vcl/source/filter/iras/iras.cxx414
-rw-r--r--vcl/source/filter/itga/itga.cxx790
-rw-r--r--vcl/source/filter/itiff/itiff.cxx294
-rw-r--r--vcl/source/filter/ixbm/xbmread.cxx396
-rw-r--r--vcl/source/filter/ixbm/xbmread.hxx29
-rw-r--r--vcl/source/filter/ixpm/rgbtable.hxx696
-rw-r--r--vcl/source/filter/ixpm/xpmread.cxx697
-rw-r--r--vcl/source/filter/jpeg/Exif.cxx288
-rw-r--r--vcl/source/filter/jpeg/Exif.hxx83
-rw-r--r--vcl/source/filter/jpeg/JpegReader.cxx331
-rw-r--r--vcl/source/filter/jpeg/JpegReader.hxx74
-rw-r--r--vcl/source/filter/jpeg/JpegTransform.cxx42
-rw-r--r--vcl/source/filter/jpeg/JpegTransform.hxx40
-rw-r--r--vcl/source/filter/jpeg/JpegWriter.cxx265
-rw-r--r--vcl/source/filter/jpeg/JpegWriter.hxx57
-rw-r--r--vcl/source/filter/jpeg/jinclude.h79
-rw-r--r--vcl/source/filter/jpeg/jpeg.cxx62
-rw-r--r--vcl/source/filter/jpeg/jpeg.h67
-rw-r--r--vcl/source/filter/jpeg/jpeg.hxx39
-rw-r--r--vcl/source/filter/jpeg/jpegc.cxx520
-rw-r--r--vcl/source/filter/jpeg/jpegcomp.h18
-rw-r--r--vcl/source/filter/jpeg/transupp.c1570
-rw-r--r--vcl/source/filter/jpeg/transupp.h212
-rw-r--r--vcl/source/filter/png/PngImageReader.cxx532
-rw-r--r--vcl/source/filter/png/png.hxx33
-rw-r--r--vcl/source/filter/png/pngwrite.cxx660
-rw-r--r--vcl/source/filter/svm/SvmConverter.cxx1290
-rw-r--r--vcl/source/filter/svm/SvmConverter.hxx92
-rw-r--r--vcl/source/filter/svm/SvmReader.cxx1438
-rw-r--r--vcl/source/filter/svm/SvmWriter.cxx1421
-rw-r--r--vcl/source/filter/webp/reader.cxx318
-rw-r--r--vcl/source/filter/webp/writer.cxx205
-rw-r--r--vcl/source/filter/wmf/emfwr.cxx1511
-rw-r--r--vcl/source/filter/wmf/emfwr.hxx114
-rw-r--r--vcl/source/filter/wmf/wmf.cxx134
-rw-r--r--vcl/source/filter/wmf/wmfexternal.cxx74
-rw-r--r--vcl/source/filter/wmf/wmfwr.cxx1904
-rw-r--r--vcl/source/filter/wmf/wmfwr.hxx205
85 files changed, 44419 insertions, 0 deletions
diff --git a/vcl/source/filter/FilterConfigCache.cxx b/vcl/source/filter/FilterConfigCache.cxx
new file mode 100644
index 000000000..9f3668ccc
--- /dev/null
+++ b/vcl/source/filter/FilterConfigCache.cxx
@@ -0,0 +1,489 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include "FilterConfigCache.hxx"
+
+#include <o3tl/safeint.hxx>
+#include <vcl/graphicfilter.hxx>
+#include <unotools/configmgr.hxx>
+#include <tools/svlibrary.h>
+#include <com/sun/star/uno/Any.h>
+#include <comphelper/processfactory.hxx>
+#include <comphelper/sequence.hxx>
+#include <com/sun/star/uno/Exception.hpp>
+#include <com/sun/star/lang/XMultiServiceFactory.hpp>
+#include <com/sun/star/beans/PropertyValue.hpp>
+#include <com/sun/star/beans/XPropertySet.hpp>
+#include <com/sun/star/configuration/theDefaultProvider.hpp>
+#include <com/sun/star/container/XNameAccess.hpp>
+
+using namespace ::com::sun::star::lang ; // XMultiServiceFactory
+using namespace ::com::sun::star::container ; // XNameAccess
+using namespace ::com::sun::star::uno ; // Reference
+using namespace ::com::sun::star::beans ; // PropertyValue
+using namespace ::com::sun::star::configuration ;
+
+const char* FilterConfigCache::FilterConfigCacheEntry::InternalPixelFilterNameList[] =
+{
+ IMP_BMP, IMP_GIF, IMP_PNG, IMP_JPEG, IMP_TIFF, IMP_WEBP,
+ IMP_XBM, IMP_XPM, IMP_TGA, IMP_PICT, IMP_MET, IMP_RAS,
+ IMP_PCX, IMP_MOV, IMP_PSD, IMP_PCD, IMP_PBM, IMP_DXF,
+ EXP_BMP, EXP_GIF, EXP_PNG, EXP_JPEG, EXP_TIFF, EXP_WEBP,
+ nullptr
+};
+
+void FilterConfigCache::FilterConfigCacheEntry::CreateFilterName( const OUString& rUserDataEntry )
+{
+ bIsPixelFormat = false;
+ sFilterName = rUserDataEntry;
+ const char** pPtr;
+ for ( pPtr = InternalPixelFilterNameList; *pPtr; pPtr++ )
+ {
+ if ( sFilterName.equalsIgnoreAsciiCaseAscii( *pPtr ) )
+ {
+ bIsPixelFormat = true;
+ }
+ }
+}
+
+OUString FilterConfigCache::FilterConfigCacheEntry::GetShortName()
+{
+ OUString aShortName;
+ if ( !lExtensionList.empty() )
+ {
+ aShortName = lExtensionList[ 0 ];
+ if ( aShortName.startsWith( "*." ) )
+ aShortName = aShortName.replaceAt( 0, 2, u"" );
+ }
+ return aShortName;
+}
+
+/** helper to open the configuration root of the underlying
+ config package
+
+ @param sPackage
+ specify, which config package should be opened.
+ Must be one of "types" or "filters"
+
+ @return A valid object if open was successful. The access on opened
+ data will be readonly. It returns NULL in case open failed.
+
+ @throws It let pass RuntimeExceptions only.
+ */
+static Reference< XInterface > openConfig(const char* sPackage)
+{
+ Reference< XComponentContext > xContext(
+ comphelper::getProcessComponentContext() );
+ Reference< XInterface > xCfg;
+ try
+ {
+ // get access to config API (not to file!)
+ Reference< XMultiServiceFactory > xConfigProvider = theDefaultProvider::get( xContext );
+
+ PropertyValue aParam ;
+
+ // define cfg path for open
+ aParam.Name = "nodepath";
+ if (rtl_str_compareIgnoreAsciiCase(sPackage, "types") == 0)
+ aParam.Value <<= OUString( "/org.openoffice.TypeDetection.Types/Types" );
+ if (rtl_str_compareIgnoreAsciiCase(sPackage, "filters") == 0)
+ aParam.Value <<= OUString( "/org.openoffice.TypeDetection.GraphicFilter/Filters" );
+ Sequence< Any > lParams{ Any(aParam) };
+
+ // get access to file
+ xCfg = xConfigProvider->createInstanceWithArguments("com.sun.star.configuration.ConfigurationAccess", lParams);
+ }
+ catch(const RuntimeException&)
+ { throw; }
+ catch(const Exception&)
+ { xCfg.clear(); }
+
+ return xCfg;
+}
+
+void FilterConfigCache::ImplInit()
+{
+ OUString const STYPE ( "Type" );
+ OUString const SUINAME ( "UIName" );
+ OUString const SFLAGS ( "Flags" );
+ OUString const SMEDIATYPE ( "MediaType" );
+ OUString const SEXTENSIONS ( "Extensions" );
+ OUString const SFORMATNAME ( "FormatName" );
+ OUString const SREALFILTERNAME ( "RealFilterName" );
+
+ // get access to config
+ Reference< XNameAccess > xTypeAccess ( openConfig("types" ), UNO_QUERY );
+ Reference< XNameAccess > xFilterAccess( openConfig("filters"), UNO_QUERY );
+
+ if ( !(xTypeAccess.is() && xFilterAccess.is()) )
+ return;
+
+ const Sequence< OUString > lAllFilter = xFilterAccess->getElementNames();
+
+ for ( const OUString& sInternalFilterName : lAllFilter )
+ {
+ Reference< XPropertySet > xFilterSet;
+ xFilterAccess->getByName( sInternalFilterName ) >>= xFilterSet;
+ if (!xFilterSet.is())
+ continue;
+
+ FilterConfigCacheEntry aEntry;
+
+ aEntry.sInternalFilterName = sInternalFilterName;
+ xFilterSet->getPropertyValue(STYPE) >>= aEntry.sType;
+ xFilterSet->getPropertyValue(SUINAME) >>= aEntry.sUIName;
+ xFilterSet->getPropertyValue(SREALFILTERNAME) >>= aEntry.sFilterType;
+ Sequence< OUString > lFlags;
+ xFilterSet->getPropertyValue(SFLAGS) >>= lFlags;
+ if (lFlags.getLength()!=1 || lFlags[0].isEmpty())
+ continue;
+ if (lFlags[0].equalsIgnoreAsciiCase("import"))
+ aEntry.nFlags = 1;
+ else if (lFlags[0].equalsIgnoreAsciiCase("export"))
+ aEntry.nFlags = 2;
+ else
+ aEntry.nFlags = 0;
+
+ OUString sFormatName;
+ xFilterSet->getPropertyValue(SFORMATNAME) >>= sFormatName;
+ aEntry.CreateFilterName( sFormatName );
+
+ Reference< XPropertySet > xTypeSet;
+ xTypeAccess->getByName( aEntry.sType ) >>= xTypeSet;
+ if (!xTypeSet.is())
+ continue;
+
+ xTypeSet->getPropertyValue(SMEDIATYPE) >>= aEntry.sMediaType;
+ css::uno::Sequence<OUString> tmp;
+ if (xTypeSet->getPropertyValue(SEXTENSIONS) >>= tmp)
+ aEntry.lExtensionList = comphelper::sequenceToContainer<std::vector<OUString>>(tmp);
+
+ // The first extension will be used
+ // to generate our internal FilterType ( BMP, WMF ... )
+ OUString aExtension( aEntry.GetShortName() );
+ if (aExtension.isEmpty())
+ continue;
+
+ if ( aEntry.nFlags & 1 )
+ aImport.push_back( aEntry );
+ if ( aEntry.nFlags & 2 )
+ aExport.push_back( aEntry );
+
+ // bFilterEntryCreated!?
+ if (!( aEntry.nFlags & 3 ))
+ continue; //? Entry was already inserted ... but following code will be suppressed?!
+ }
+};
+
+const char* FilterConfigCache::InternalFilterListForSvxLight[] =
+{
+ "bmp","1","SVBMP",
+ "bmp","2","SVBMP",
+ "dxf","1","SVDXF",
+ "eps","1","SVIEPS",
+ "eps","2","SVEEPS",
+ "gif","1","SVIGIF",
+ "gif","2","SVEGIF",
+ "jpg","1","SVIJPEG",
+ "jpg","2","SVEJPEG",
+ "mov","1","SVMOV",
+ "mov","2","SVMOV",
+ "met","1","SVMET",
+ "png","1","SVIPNG",
+ "png","2","SVEPNG",
+ "pct","1","SVPICT",
+ "pcd","1","SVPCD",
+ "psd","1","SVPSD",
+ "pcx","1","SVPCX",
+ "pbm","1","SVPBM",
+ "pgm","1","SVPBM",
+ "ppm","1","SVPBM",
+ "ras","1","SVRAS",
+ "svm","1","SVMETAFILE",
+ "svm","2","SVMETAFILE",
+ "tga","1","SVTGA",
+ "tif","1","SVTIFF",
+ "tif","2","SVTIFF",
+ "emf","1","SVEMF",
+ "emf","2","SVEMF",
+ "wmf","1","SVWMF",
+ "wmf","2","SVWMF",
+ "xbm","1","SVIXBM",
+ "xpm","1","SVIXPM",
+ "svg","1","SVISVG",
+ "svg","2","SVESVG",
+ "webp","1","SVIWEBP",
+ "webp","2","SVEWEBP",
+ nullptr
+};
+
+void FilterConfigCache::ImplInitSmart()
+{
+ const char** pPtr;
+ for ( pPtr = InternalFilterListForSvxLight; *pPtr; pPtr++ )
+ {
+ FilterConfigCacheEntry aEntry;
+
+ OUString sExtension( OUString::createFromAscii( *pPtr++ ) );
+
+ aEntry.lExtensionList.push_back(sExtension);
+
+ aEntry.sType = sExtension;
+ aEntry.sUIName = sExtension;
+
+ OString sFlags( *pPtr++ );
+ aEntry.nFlags = sFlags.toInt32();
+
+ OUString sUserData( OUString::createFromAscii( *pPtr ) );
+ aEntry.CreateFilterName( sUserData );
+
+ if ( aEntry.nFlags & 1 )
+ aImport.push_back( aEntry );
+ if ( aEntry.nFlags & 2 )
+ aExport.push_back( aEntry );
+ }
+}
+
+FilterConfigCache::FilterConfigCache(bool bConfig)
+{
+ if (bConfig)
+ bConfig = !utl::ConfigManager::IsFuzzing();
+ if (bConfig)
+ ImplInit();
+ else
+ ImplInitSmart();
+}
+
+FilterConfigCache::~FilterConfigCache()
+{
+}
+
+OUString FilterConfigCache::GetImportFilterName( sal_uInt16 nFormat )
+{
+ if( nFormat < aImport.size() )
+ return aImport[ nFormat ].sFilterName;
+ return OUString();
+}
+
+sal_uInt16 FilterConfigCache::GetImportFormatNumber( std::u16string_view rFormatName )
+{
+ sal_uInt16 nPos = 0;
+ for (auto const& elem : aImport)
+ {
+ if ( elem.sUIName.equalsIgnoreAsciiCase( rFormatName ) )
+ return nPos;
+ ++nPos;
+ }
+ return GRFILTER_FORMAT_NOTFOUND;
+}
+
+/// get the index of the filter that matches this extension
+sal_uInt16 FilterConfigCache::GetImportFormatNumberForExtension( std::u16string_view rExt )
+{
+ sal_uInt16 nPos = 0;
+ for (auto const& elem : aImport)
+ {
+ for ( OUString const & s : elem.lExtensionList )
+ {
+ if ( s.equalsIgnoreAsciiCase( rExt ) )
+ return nPos;
+ }
+ ++nPos;
+ }
+ return GRFILTER_FORMAT_NOTFOUND;
+}
+
+sal_uInt16 FilterConfigCache::GetImportFormatNumberForShortName( std::u16string_view rShortName )
+{
+ sal_uInt16 nPos = 0;
+ for (auto & elem : aImport)
+ {
+ if ( elem.GetShortName().equalsIgnoreAsciiCase( rShortName ) )
+ return nPos;
+ ++nPos;
+ }
+ return GRFILTER_FORMAT_NOTFOUND;
+}
+
+sal_uInt16 FilterConfigCache::GetImportFormatNumberForTypeName( std::u16string_view rType )
+{
+ sal_uInt16 nPos = 0;
+ for (auto const& elem : aImport)
+ {
+ if ( elem.sType.equalsIgnoreAsciiCase( rType ) )
+ return nPos;
+ ++nPos;
+ }
+ return GRFILTER_FORMAT_NOTFOUND;
+}
+
+OUString FilterConfigCache::GetImportFormatName( sal_uInt16 nFormat )
+{
+ if( nFormat < aImport.size() )
+ return aImport[ nFormat ].sUIName;
+ return OUString();
+}
+
+OUString FilterConfigCache::GetImportFormatMediaType( sal_uInt16 nFormat )
+{
+ if( nFormat < aImport.size() )
+ return aImport[ nFormat ].sMediaType;
+ return OUString();
+}
+
+OUString FilterConfigCache::GetImportFormatShortName( sal_uInt16 nFormat )
+{
+ if( nFormat < aImport.size() )
+ return aImport[ nFormat ].GetShortName();
+ return OUString();
+}
+
+OUString FilterConfigCache::GetImportFormatExtension( sal_uInt16 nFormat, sal_Int32 nEntry )
+{
+ if ( (nFormat < aImport.size()) && (o3tl::make_unsigned(nEntry) < aImport[ nFormat ].lExtensionList.size()) )
+ return aImport[ nFormat ].lExtensionList[ nEntry ];
+ return OUString();
+}
+
+OUString FilterConfigCache::GetImportFilterType( sal_uInt16 nFormat )
+{
+ if( nFormat < aImport.size() )
+ return aImport[ nFormat ].sType;
+ return OUString();
+}
+
+OUString FilterConfigCache::GetImportFilterTypeName( sal_uInt16 nFormat )
+{
+ if( nFormat < aImport.size() )
+ return aImport[ nFormat ].sFilterType;
+ return OUString();
+}
+
+OUString FilterConfigCache::GetImportWildcard(sal_uInt16 nFormat, sal_Int32 nEntry)
+{
+ OUString aWildcard( GetImportFormatExtension( nFormat, nEntry ) );
+ if ( !aWildcard.isEmpty() )
+ aWildcard = aWildcard.replaceAt( 0, 0, u"*." );
+ return aWildcard;
+}
+
+OUString FilterConfigCache::GetExportFilterName( sal_uInt16 nFormat )
+{
+ if( nFormat < aExport.size() )
+ return aExport[ nFormat ].sFilterName;
+ return OUString();
+}
+
+sal_uInt16 FilterConfigCache::GetExportFormatNumber(std::u16string_view rFormatName)
+{
+ sal_uInt16 nPos = 0;
+ for (auto const& elem : aExport)
+ {
+ if ( elem.sUIName.equalsIgnoreAsciiCase( rFormatName ) )
+ return nPos;
+ ++nPos;
+ }
+ return GRFILTER_FORMAT_NOTFOUND;
+}
+
+sal_uInt16 FilterConfigCache::GetExportFormatNumberForMediaType( std::u16string_view rMediaType )
+{
+ sal_uInt16 nPos = 0;
+ for (auto const& elem : aExport)
+ {
+ if ( elem.sMediaType.equalsIgnoreAsciiCase( rMediaType ) )
+ return nPos;
+ ++nPos;
+ }
+ return GRFILTER_FORMAT_NOTFOUND;
+}
+
+sal_uInt16 FilterConfigCache::GetExportFormatNumberForShortName( std::u16string_view rShortName )
+{
+ sal_uInt16 nPos = 0;
+ for (auto & elem : aExport)
+ {
+ if ( elem.GetShortName().equalsIgnoreAsciiCase( rShortName ) )
+ return nPos;
+ ++nPos;
+ }
+ return GRFILTER_FORMAT_NOTFOUND;
+}
+
+sal_uInt16 FilterConfigCache::GetExportFormatNumberForTypeName( std::u16string_view rType )
+{
+ sal_uInt16 nPos = 0;
+ for (auto const& elem : aExport)
+ {
+ if ( elem.sType.equalsIgnoreAsciiCase( rType ) )
+ return nPos;
+ ++nPos;
+ }
+ return GRFILTER_FORMAT_NOTFOUND;
+}
+
+OUString FilterConfigCache::GetExportFormatName( sal_uInt16 nFormat )
+{
+ if( nFormat < aExport.size() )
+ return aExport[ nFormat ].sUIName;
+ return OUString();
+}
+
+OUString FilterConfigCache::GetExportFormatMediaType( sal_uInt16 nFormat )
+{
+ if( nFormat < aExport.size() )
+ return aExport[ nFormat ].sMediaType;
+ return OUString();
+}
+
+OUString FilterConfigCache::GetExportFormatShortName( sal_uInt16 nFormat )
+{
+ if( nFormat < aExport.size() )
+ return aExport[ nFormat ].GetShortName();
+ return OUString();
+}
+
+OUString FilterConfigCache::GetExportFormatExtension( sal_uInt16 nFormat, sal_Int32 nEntry )
+{
+ if ( (nFormat < aExport.size()) && (o3tl::make_unsigned(nEntry) < aExport[ nFormat ].lExtensionList.size()) )
+ return aExport[ nFormat ].lExtensionList[ nEntry ];
+ return OUString();
+}
+
+OUString FilterConfigCache::GetExportInternalFilterName( sal_uInt16 nFormat )
+{
+ if( nFormat < aExport.size() )
+ return aExport[ nFormat ].sInternalFilterName;
+ return OUString();
+}
+
+OUString FilterConfigCache::GetExportWildcard( sal_uInt16 nFormat, sal_Int32 nEntry )
+{
+ OUString aWildcard( GetExportFormatExtension( nFormat, nEntry ) );
+ if ( !aWildcard.isEmpty() )
+ aWildcard = aWildcard.replaceAt( 0, 0, u"*." );
+ return aWildcard;
+}
+
+bool FilterConfigCache::IsExportPixelFormat( sal_uInt16 nFormat )
+{
+ return (nFormat < aExport.size()) && aExport[ nFormat ].bIsPixelFormat;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/vcl/source/filter/FilterConfigCache.hxx b/vcl/source/filter/FilterConfigCache.hxx
new file mode 100644
index 000000000..f398a58ff
--- /dev/null
+++ b/vcl/source/filter/FilterConfigCache.hxx
@@ -0,0 +1,100 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#ifndef INCLUDED_VCL_SOURCE_FILTER_FILTERCONFIGCACHE_HXX
+#define INCLUDED_VCL_SOURCE_FILTER_FILTERCONFIGCACHE_HXX
+
+#include <rtl/ustring.hxx>
+#include <vector>
+
+/** Cache to keep list of graphic filters + the filters themselves. */
+class FilterConfigCache
+{
+ struct FilterConfigCacheEntry
+ {
+ OUString sInternalFilterName;
+ OUString sType;
+ std::vector< OUString > lExtensionList;
+ OUString sUIName;
+
+ OUString sMediaType;
+ OUString sFilterType;
+
+ sal_Int32 nFlags;
+
+ // user data
+ OUString sFilterName;
+ bool bIsPixelFormat : 1;
+
+ void CreateFilterName( const OUString& rUserDataEntry );
+ OUString GetShortName( );
+
+ static const char* InternalPixelFilterNameList[];
+ };
+
+
+ std::vector< FilterConfigCacheEntry > aImport;
+ std::vector< FilterConfigCacheEntry > aExport;
+
+ static const char* InternalFilterListForSvxLight[];
+
+ void ImplInit();
+ void ImplInitSmart();
+
+public:
+
+ sal_uInt16 GetImportFormatCount() const
+ { return sal::static_int_cast< sal_uInt16 >(aImport.size()); };
+ sal_uInt16 GetImportFormatNumber( std::u16string_view rFormatName );
+ sal_uInt16 GetImportFormatNumberForShortName( std::u16string_view rShortName );
+ sal_uInt16 GetImportFormatNumberForTypeName( std::u16string_view rType );
+ sal_uInt16 GetImportFormatNumberForExtension( std::u16string_view rExt );
+ OUString GetImportFilterName( sal_uInt16 nFormat );
+ OUString GetImportFormatName( sal_uInt16 nFormat );
+ OUString GetImportFormatExtension( sal_uInt16 nFormat, sal_Int32 nEntry = 0);
+ OUString GetImportFormatMediaType( sal_uInt16 nFormat );
+ OUString GetImportFormatShortName( sal_uInt16 nFormat );
+ OUString GetImportWildcard( sal_uInt16 nFormat, sal_Int32 nEntry );
+ OUString GetImportFilterType( sal_uInt16 nFormat );
+ OUString GetImportFilterTypeName( sal_uInt16 nFormat );
+
+
+ sal_uInt16 GetExportFormatCount() const
+ { return sal::static_int_cast< sal_uInt16 >(aExport.size()); };
+ sal_uInt16 GetExportFormatNumber( std::u16string_view rFormatName );
+ sal_uInt16 GetExportFormatNumberForMediaType( std::u16string_view rMediaType );
+ sal_uInt16 GetExportFormatNumberForShortName( std::u16string_view rShortName );
+ sal_uInt16 GetExportFormatNumberForTypeName( std::u16string_view rType );
+ OUString GetExportFilterName( sal_uInt16 nFormat );
+ OUString GetExportFormatName( sal_uInt16 nFormat );
+ OUString GetExportFormatExtension( sal_uInt16 nFormat, sal_Int32 nEntry = 0 );
+ OUString GetExportFormatMediaType( sal_uInt16 nFormat );
+ OUString GetExportFormatShortName( sal_uInt16 nFormat );
+ OUString GetExportWildcard( sal_uInt16 nFormat, sal_Int32 nEntry );
+ OUString GetExportInternalFilterName( sal_uInt16 nFormat );
+
+ bool IsExportPixelFormat( sal_uInt16 nFormat );
+
+ explicit FilterConfigCache( bool bUseConfig );
+ ~FilterConfigCache();
+};
+
+#endif // INCLUDED_VCL_SOURCE_FILTER_FILTERCONFIGCACHE_HXX
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/vcl/source/filter/FilterConfigItem.cxx b/vcl/source/filter/FilterConfigItem.cxx
new file mode 100644
index 000000000..962d970bf
--- /dev/null
+++ b/vcl/source/filter/FilterConfigItem.cxx
@@ -0,0 +1,389 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <vcl/FilterConfigItem.hxx>
+
+#include <unotools/configmgr.hxx>
+#include <comphelper/processfactory.hxx>
+#include <comphelper/propertyvalue.hxx>
+#include <osl/diagnose.h>
+#include <com/sun/star/beans/PropertyValue.hpp>
+#include <com/sun/star/configuration/theDefaultProvider.hpp>
+#include <com/sun/star/lang/XMultiServiceFactory.hpp>
+#include <com/sun/star/util/XChangesBatch.hpp>
+#include <com/sun/star/beans/XPropertySetInfo.hpp>
+#include <com/sun/star/beans/XPropertySet.hpp>
+#include <com/sun/star/container/XHierarchicalNameAccess.hpp>
+#include <com/sun/star/awt/Size.hpp>
+#include <com/sun/star/task/XStatusIndicator.hpp>
+
+using namespace ::com::sun::star::lang ; // XMultiServiceFactory
+using namespace ::com::sun::star::beans ; // PropertyValue
+using namespace ::com::sun::star::uno ; // Reference
+using namespace ::com::sun::star::util ; // XChangesBatch
+using namespace ::com::sun::star::awt ; // Size
+using namespace ::com::sun::star::container ;
+using namespace ::com::sun::star::configuration;
+using namespace ::com::sun::star::task ; // XStatusIndicator
+
+static bool ImpIsTreeAvailable( Reference< XMultiServiceFactory > const & rXCfgProv, const OUString& rTree )
+{
+ bool bAvailable = !rTree.isEmpty();
+ if ( bAvailable )
+ {
+ sal_Int32 nIdx{0};
+ if ( rTree[0] == '/' )
+ ++nIdx;
+
+ // creation arguments: nodepath
+ PropertyValue aPathArgument = comphelper::makePropertyValue("nodepath",
+ rTree.getToken(0, '/', nIdx));
+ Sequence< Any > aArguments{ Any(aPathArgument) };
+
+ Reference< XInterface > xReadAccess;
+ try
+ {
+ xReadAccess = rXCfgProv->createInstanceWithArguments(
+ "com.sun.star.configuration.ConfigurationAccess",
+ aArguments );
+ }
+ catch (const css::uno::Exception&)
+ {
+ bAvailable = false;
+ }
+ if ( xReadAccess.is() )
+ {
+ const sal_Int32 nEnd {rTree.getLength()};
+ while (bAvailable && nIdx>=0 && nIdx<nEnd)
+ {
+ Reference< XHierarchicalNameAccess > xHierarchicalNameAccess
+ ( xReadAccess, UNO_QUERY );
+
+ if ( !xHierarchicalNameAccess.is() )
+ bAvailable = false;
+ else
+ {
+ const OUString aNode( rTree.getToken(0, '/', nIdx) );
+ if ( !xHierarchicalNameAccess->hasByHierarchicalName( aNode ) )
+ bAvailable = false;
+ else
+ {
+ Any a( xHierarchicalNameAccess->getByHierarchicalName( aNode ) );
+ bAvailable = (a >>= xReadAccess);
+ }
+ }
+ }
+ }
+ }
+ return bAvailable;
+}
+
+void FilterConfigItem::ImpInitTree( std::u16string_view rSubTree )
+{
+ bModified = false;
+
+ Reference< XComponentContext > xContext( comphelper::getProcessComponentContext() );
+
+ Reference< XMultiServiceFactory > xCfgProv = theDefaultProvider::get( xContext );
+
+ OUString sTree = OUString::Concat("/org.openoffice.") + rSubTree;
+ if ( !ImpIsTreeAvailable(xCfgProv, sTree) )
+ return;
+
+ // creation arguments: nodepath
+ PropertyValue aPathArgument;
+ aPathArgument.Name = "nodepath";
+ aPathArgument.Value <<= sTree;
+
+ Sequence< Any > aArguments{ Any(aPathArgument) };
+
+ try
+ {
+ xUpdatableView = xCfgProv->createInstanceWithArguments(
+ "com.sun.star.configuration.ConfigurationUpdateAccess",
+ aArguments );
+ if ( xUpdatableView.is() )
+ xPropSet.set( xUpdatableView, UNO_QUERY );
+ }
+ catch ( css::uno::Exception& )
+ {
+ OSL_FAIL( "FilterConfigItem::FilterConfigItem - Could not access configuration Key" );
+ }
+}
+
+FilterConfigItem::FilterConfigItem( std::u16string_view rSubTree )
+{
+ ImpInitTree( rSubTree );
+}
+
+FilterConfigItem::FilterConfigItem( css::uno::Sequence< css::beans::PropertyValue > const * pFilterData )
+ : bModified(false)
+{
+ if ( pFilterData )
+ aFilterData = *pFilterData;
+}
+
+FilterConfigItem::FilterConfigItem( std::u16string_view rSubTree,
+ css::uno::Sequence< css::beans::PropertyValue > const * pFilterData )
+{
+ ImpInitTree( rSubTree );
+
+ if ( pFilterData )
+ aFilterData = *pFilterData;
+};
+
+FilterConfigItem::~FilterConfigItem()
+{
+ WriteModifiedConfig();
+}
+
+void FilterConfigItem::WriteModifiedConfig()
+{
+ if ( !xUpdatableView.is() )
+ return;
+
+ if ( !(xPropSet.is() && bModified) )
+ return;
+
+ Reference< XChangesBatch > xUpdateControl( xUpdatableView, UNO_QUERY );
+ if ( xUpdateControl.is() )
+ {
+ try
+ {
+ xUpdateControl->commitChanges();
+ bModified = false;
+ }
+ catch ( css::uno::Exception& )
+ {
+ OSL_FAIL( "FilterConfigItem::FilterConfigItem - Could not update configuration data" );
+ }
+ }
+}
+
+bool FilterConfigItem::ImplGetPropertyValue( Any& rAny, const Reference< XPropertySet >& rXPropSet, const OUString& rString )
+{
+ bool bRetValue = true;
+
+ if ( rXPropSet.is() )
+ {
+ bRetValue = false;
+ try
+ {
+ Reference< XPropertySetInfo >
+ aXPropSetInfo( rXPropSet->getPropertySetInfo() );
+ if ( aXPropSetInfo.is() )
+ bRetValue = aXPropSetInfo->hasPropertyByName( rString );
+ }
+ catch( css::uno::Exception& )
+ {
+ }
+ if ( bRetValue )
+ {
+ try
+ {
+ rAny = rXPropSet->getPropertyValue( rString );
+ if ( !rAny.hasValue() )
+ bRetValue = false;
+ }
+ catch( css::uno::Exception& )
+ {
+ bRetValue = false;
+ }
+ }
+ }
+ else
+ bRetValue = false;
+ return bRetValue;
+}
+
+// if property is available it returns a pointer,
+// otherwise the result is null
+const PropertyValue* FilterConfigItem::GetPropertyValue( const Sequence< PropertyValue >& rPropSeq, const OUString& rName )
+{
+ auto pProp = std::find_if(rPropSeq.begin(), rPropSeq.end(),
+ [&rName](const PropertyValue& rProp) { return rProp.Name == rName; });
+ if (pProp != rPropSeq.end())
+ return pProp;
+ return nullptr;
+}
+
+/* if PropertySequence already includes a PropertyValue using the same name, the
+ corresponding PropertyValue is replaced, otherwise the given PropertyValue
+ will be appended */
+
+bool FilterConfigItem::WritePropertyValue( Sequence< PropertyValue >& rPropSeq, const PropertyValue& rPropValue )
+{
+ bool bRet = false;
+ if ( !rPropValue.Name.isEmpty() )
+ {
+ auto pProp = std::find_if(std::cbegin(rPropSeq), std::cend(rPropSeq),
+ [&rPropValue](const PropertyValue& rProp) { return rProp.Name == rPropValue.Name; });
+ sal_Int32 i = std::distance(std::cbegin(rPropSeq), pProp);
+ sal_Int32 nCount = rPropSeq.getLength();
+ if ( i == nCount )
+ rPropSeq.realloc( ++nCount );
+
+ rPropSeq.getArray()[ i ] = rPropValue;
+
+ bRet = true;
+ }
+ return bRet;
+}
+
+bool FilterConfigItem::ReadBool( const OUString& rKey, bool bDefault )
+{
+ Any aAny;
+ bool bRetValue = bDefault;
+ const PropertyValue* pPropVal = GetPropertyValue( aFilterData, rKey );
+ if ( pPropVal )
+ {
+ pPropVal->Value >>= bRetValue;
+ }
+ else if ( ImplGetPropertyValue( aAny, xPropSet, rKey ) )
+ {
+ aAny >>= bRetValue;
+ }
+ PropertyValue aBool;
+ aBool.Name = rKey;
+ aBool.Value <<= bRetValue;
+ WritePropertyValue( aFilterData, aBool );
+ return bRetValue;
+}
+
+sal_Int32 FilterConfigItem::ReadInt32( const OUString& rKey, sal_Int32 nDefault )
+{
+ Any aAny;
+ sal_Int32 nRetValue = nDefault;
+ const PropertyValue* pPropVal = GetPropertyValue( aFilterData, rKey );
+ if ( pPropVal )
+ {
+ pPropVal->Value >>= nRetValue;
+ }
+ else if ( ImplGetPropertyValue( aAny, xPropSet, rKey ) )
+ {
+ aAny >>= nRetValue;
+ }
+ PropertyValue aInt32;
+ aInt32.Name = rKey;
+ aInt32.Value <<= nRetValue;
+ WritePropertyValue( aFilterData, aInt32 );
+ return nRetValue;
+}
+
+OUString FilterConfigItem::ReadString( const OUString& rKey, const OUString& rDefault )
+{
+ Any aAny;
+ OUString aRetValue( rDefault );
+ const PropertyValue* pPropVal = GetPropertyValue( aFilterData, rKey );
+ if ( pPropVal )
+ {
+ pPropVal->Value >>= aRetValue;
+ }
+ else if ( ImplGetPropertyValue( aAny, xPropSet, rKey ) )
+ {
+ aAny >>= aRetValue;
+ }
+ PropertyValue aString;
+ aString.Name = rKey;
+ aString.Value <<= aRetValue;
+ WritePropertyValue( aFilterData, aString );
+ return aRetValue;
+}
+
+void FilterConfigItem::WriteBool( const OUString& rKey, bool bNewValue )
+{
+ PropertyValue aBool;
+ aBool.Name = rKey;
+ aBool.Value <<= bNewValue;
+ WritePropertyValue( aFilterData, aBool );
+
+ if ( !xPropSet.is() )
+ return;
+
+ Any aAny;
+ if ( !ImplGetPropertyValue( aAny, xPropSet, rKey ) )
+ return;
+
+ bool bOldValue(true);
+ if ( !(aAny >>= bOldValue) )
+ return;
+
+ if ( bOldValue != bNewValue )
+ {
+ try
+ {
+ xPropSet->setPropertyValue( rKey, Any(bNewValue) );
+ bModified = true;
+ }
+ catch ( css::uno::Exception& )
+ {
+ OSL_FAIL( "FilterConfigItem::WriteBool - could not set PropertyValue" );
+ }
+ }
+}
+
+void FilterConfigItem::WriteInt32( const OUString& rKey, sal_Int32 nNewValue )
+{
+ PropertyValue aInt32;
+ aInt32.Name = rKey;
+ aInt32.Value <<= nNewValue;
+ WritePropertyValue( aFilterData, aInt32 );
+
+ if ( !xPropSet.is() )
+ return;
+
+ Any aAny;
+
+ if ( !ImplGetPropertyValue( aAny, xPropSet, rKey ) )
+ return;
+
+ sal_Int32 nOldValue = 0;
+ if ( !(aAny >>= nOldValue) )
+ return;
+
+ if ( nOldValue != nNewValue )
+ {
+ try
+ {
+ xPropSet->setPropertyValue( rKey, Any(nNewValue) );
+ bModified = true;
+ }
+ catch ( css::uno::Exception& )
+ {
+ OSL_FAIL( "FilterConfigItem::WriteInt32 - could not set PropertyValue" );
+ }
+ }
+}
+
+
+Reference< XStatusIndicator > FilterConfigItem::GetStatusIndicator() const
+{
+ Reference< XStatusIndicator > xStatusIndicator;
+
+ auto pPropVal = std::find_if(aFilterData.begin(), aFilterData.end(),
+ [](const css::beans::PropertyValue& rPropVal) {
+ return rPropVal.Name == "StatusIndicator"; });
+ if (pPropVal != aFilterData.end())
+ {
+ pPropVal->Value >>= xStatusIndicator;
+ }
+ return xStatusIndicator;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/vcl/source/filter/GraphicFormatDetector.cxx b/vcl/source/filter/GraphicFormatDetector.cxx
new file mode 100644
index 000000000..6ef3d3015
--- /dev/null
+++ b/vcl/source/filter/GraphicFormatDetector.cxx
@@ -0,0 +1,859 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <sal/config.h>
+
+#include <algorithm>
+
+#include <graphic/GraphicFormatDetector.hxx>
+#include <graphic/DetectorTools.hxx>
+#include <tools/solar.h>
+#include <tools/zcodec.hxx>
+
+constexpr sal_uInt32 SVG_CHECK_SIZE = 2048;
+constexpr sal_uInt32 WMF_EMF_CHECK_SIZE = 44;
+
+namespace vcl
+{
+bool peekGraphicFormat(SvStream& rStream, OUString& rFormatExtension, bool bTest)
+{
+ vcl::GraphicFormatDetector aDetector(rStream, rFormatExtension);
+ if (!aDetector.detect())
+ return false;
+
+ // The following variable is used when bTest == true. It remains false
+ // if the format (rFormatExtension) has not yet been set.
+ bool bSomethingTested = false;
+
+ // Now the different formats are checked. The order *does* matter. e.g. a MET file
+ // could also go through the BMP test, however, a BMP file can hardly go through the MET test.
+ // So MET should be tested prior to BMP. However, theoretically a BMP file could conceivably
+ // go through the MET test. These problems are of course not only in MET and BMP.
+ // Therefore, in the case of a format check (bTest == true) we only test *exactly* this
+ // format. Everything else could have fatal consequences, for example if the user says it is
+ // a BMP file (and it is a BMP) file, and the file would go through the MET test ...
+
+ if (!bTest || rFormatExtension.startsWith("MET"))
+ {
+ bSomethingTested = true;
+ if (aDetector.checkMET())
+ {
+ rFormatExtension = aDetector.msDetectedFormat;
+ return true;
+ }
+ }
+
+ if (!bTest || rFormatExtension.startsWith("BMP"))
+ {
+ bSomethingTested = true;
+ if (aDetector.checkBMP())
+ {
+ rFormatExtension = aDetector.msDetectedFormat;
+ return true;
+ }
+ }
+
+ if (!bTest || rFormatExtension.startsWith("WMF") || rFormatExtension.startsWith("EMF")
+ || rFormatExtension.startsWith("WMZ") || rFormatExtension.startsWith("EMZ"))
+ {
+ bSomethingTested = true;
+ if (aDetector.checkWMForEMF())
+ {
+ rFormatExtension = aDetector.msDetectedFormat;
+ return true;
+ }
+ }
+
+ if (!bTest || rFormatExtension.startsWith("PCX"))
+ {
+ bSomethingTested = true;
+ if (aDetector.checkPCX())
+ {
+ rFormatExtension = aDetector.msDetectedFormat;
+ return true;
+ }
+ }
+
+ if (!bTest || rFormatExtension.startsWith("TIF"))
+ {
+ bSomethingTested = true;
+ if (aDetector.checkTIF())
+ {
+ rFormatExtension = aDetector.msDetectedFormat;
+ return true;
+ }
+ }
+
+ if (!bTest || rFormatExtension.startsWith("GIF"))
+ {
+ bSomethingTested = true;
+ if (aDetector.checkGIF())
+ {
+ rFormatExtension = aDetector.msDetectedFormat;
+ return true;
+ }
+ }
+
+ if (!bTest || rFormatExtension.startsWith("PNG"))
+ {
+ bSomethingTested = true;
+ if (aDetector.checkPNG())
+ {
+ rFormatExtension = aDetector.msDetectedFormat;
+ return true;
+ }
+ }
+
+ if (!bTest || rFormatExtension.startsWith("JPG"))
+ {
+ bSomethingTested = true;
+ if (aDetector.checkJPG())
+ {
+ rFormatExtension = aDetector.msDetectedFormat;
+ return true;
+ }
+ }
+
+ if (!bTest || rFormatExtension.startsWith("SVM"))
+ {
+ bSomethingTested = true;
+ if (aDetector.checkSVM())
+ {
+ rFormatExtension = aDetector.msDetectedFormat;
+ return true;
+ }
+ }
+
+ if (!bTest || rFormatExtension.startsWith("PCD"))
+ {
+ bSomethingTested = true;
+ if (aDetector.checkPCD())
+ {
+ rFormatExtension = aDetector.msDetectedFormat;
+ return true;
+ }
+ }
+
+ if (!bTest || rFormatExtension.startsWith("PSD"))
+ {
+ bSomethingTested = true;
+ if (aDetector.checkPSD())
+ {
+ rFormatExtension = aDetector.msDetectedFormat;
+ return true;
+ }
+ }
+
+ if (!bTest || rFormatExtension.startsWith("EPS"))
+ {
+ bSomethingTested = true;
+ if (aDetector.checkEPS())
+ {
+ rFormatExtension = aDetector.msDetectedFormat;
+ return true;
+ }
+ }
+
+ if (!bTest || rFormatExtension.startsWith("DXF"))
+ {
+ if (aDetector.checkDXF())
+ {
+ rFormatExtension = aDetector.msDetectedFormat;
+ return true;
+ }
+ }
+
+ if (!bTest || rFormatExtension.startsWith("PCT"))
+ {
+ bSomethingTested = true;
+ if (aDetector.checkPCT())
+ {
+ rFormatExtension = aDetector.msDetectedFormat;
+ return true;
+ }
+ }
+
+ if (!bTest || rFormatExtension.startsWith("PBM") || rFormatExtension.startsWith("PGM")
+ || rFormatExtension.startsWith("PPM"))
+ {
+ bSomethingTested = true;
+ if (aDetector.checkPBMorPGMorPPM())
+ {
+ rFormatExtension = aDetector.msDetectedFormat;
+ return true;
+ }
+ }
+
+ if (!bTest || rFormatExtension.startsWith("RAS"))
+ {
+ bSomethingTested = true;
+ if (aDetector.checkRAS())
+ {
+ rFormatExtension = aDetector.msDetectedFormat;
+ return true;
+ }
+ }
+
+ if (!bTest)
+ {
+ bSomethingTested = true;
+ if (aDetector.checkXPM())
+ {
+ rFormatExtension = aDetector.msDetectedFormat;
+ return true;
+ }
+ }
+ else if (rFormatExtension.startsWith("XPM"))
+ {
+ return true;
+ }
+
+ if (!bTest)
+ {
+ if (aDetector.checkXBM())
+ {
+ rFormatExtension = aDetector.msDetectedFormat;
+ return true;
+ }
+ }
+ else if (rFormatExtension.startsWith("XBM"))
+ {
+ return true;
+ }
+
+ if (!bTest)
+ {
+ if (aDetector.checkSVG())
+ {
+ rFormatExtension = aDetector.msDetectedFormat;
+ return true;
+ }
+ }
+ else if (rFormatExtension.startsWith("SVG"))
+ {
+ return true;
+ }
+
+ if (!bTest || rFormatExtension.startsWith("TGA"))
+ {
+ bSomethingTested = true;
+ if (aDetector.checkTGA())
+ {
+ rFormatExtension = aDetector.msDetectedFormat;
+ return true;
+ }
+ }
+
+ if (!bTest || rFormatExtension.startsWith("MOV"))
+ {
+ if (aDetector.checkMOV())
+ {
+ rFormatExtension = aDetector.msDetectedFormat;
+ return true;
+ }
+ }
+
+ if (!bTest || rFormatExtension.startsWith("PDF"))
+ {
+ if (aDetector.checkPDF())
+ {
+ rFormatExtension = aDetector.msDetectedFormat;
+ return true;
+ }
+ }
+
+ if (!bTest || rFormatExtension.startsWith("WEBP"))
+ {
+ bSomethingTested = true;
+ if (aDetector.checkWEBP())
+ {
+ rFormatExtension = aDetector.msDetectedFormat;
+ return true;
+ }
+ }
+
+ return bTest && !bSomethingTested;
+}
+
+namespace
+{
+bool isPCT(SvStream& rStream, sal_uLong nStreamPos, sal_uLong nStreamLen)
+{
+ sal_uInt8 sBuf[3];
+ // store number format
+ SvStreamEndian oldNumberFormat = rStream.GetEndian();
+ sal_uInt32 nOffset; // in MS documents the pict format is used without the first 512 bytes
+ for (nOffset = 0; (nOffset <= 512) && ((nStreamPos + nOffset + 14) <= nStreamLen);
+ nOffset += 512)
+ {
+ short y1, x1, y2, x2;
+ bool bdBoxOk = true;
+
+ rStream.Seek(nStreamPos + nOffset);
+ // size of the pict in version 1 pict ( 2bytes) : ignored
+ rStream.SeekRel(2);
+ // bounding box (bytes 2 -> 9)
+ rStream.SetEndian(SvStreamEndian::BIG);
+ rStream.ReadInt16(y1).ReadInt16(x1).ReadInt16(y2).ReadInt16(x2);
+ rStream.SetEndian(oldNumberFormat); // reset format
+
+ // read version op
+ rStream.ReadBytes(sBuf, 3);
+
+ if (!rStream.good())
+ break;
+
+ if (x1 > x2 || y1 > y2 || // bad bdbox
+ (x1 == x2 && y1 == y2) || // 1 pixel picture
+ x2 - x1 > 2048 || y2 - y1 > 2048) // picture abnormally big
+ bdBoxOk = false;
+
+ // see http://developer.apple.com/legacy/mac/library/documentation/mac/pdf/Imaging_With_QuickDraw/Appendix_A.pdf
+ // normal version 2 - page A23 and A24
+ if (sBuf[0] == 0x00 && sBuf[1] == 0x11 && sBuf[2] == 0x02)
+ return true;
+ // normal version 1 - page A25
+ else if (sBuf[0] == 0x11 && sBuf[1] == 0x01 && bdBoxOk)
+ return true;
+ }
+ return false;
+}
+
+} // end anonymous namespace
+
+GraphicFormatDetector::GraphicFormatDetector(SvStream& rStream, OUString const& rFormatExtension)
+ : mrStream(rStream)
+ , maExtension(rFormatExtension)
+ , mnFirstLong(0)
+ , mnSecondLong(0)
+ , mnStreamPosition(0)
+ , mnStreamLength(0)
+{
+}
+
+bool GraphicFormatDetector::detect()
+{
+ maFirstBytes.clear();
+ maFirstBytes.resize(256, 0);
+
+ mnFirstLong = 0;
+ mnSecondLong = 0;
+
+ mnStreamPosition = mrStream.Tell();
+ mnStreamLength = mrStream.remainingSize();
+
+ if (!mnStreamLength)
+ {
+ SvLockBytes* pLockBytes = mrStream.GetLockBytes();
+ if (pLockBytes)
+ pLockBytes->SetSynchronMode();
+ mnStreamLength = mrStream.remainingSize();
+ }
+
+ if (mnStreamLength == 0)
+ {
+ return false; // this prevents at least a STL assertion
+ }
+ else if (mnStreamLength >= maFirstBytes.size())
+ {
+ // load first 256 bytes into a buffer
+ sal_uInt64 nRead = mrStream.ReadBytes(maFirstBytes.data(), maFirstBytes.size());
+ if (nRead < maFirstBytes.size())
+ mnStreamLength = nRead;
+ }
+ else
+ {
+ mnStreamLength = mrStream.ReadBytes(maFirstBytes.data(), mnStreamLength);
+ }
+
+ if (mrStream.GetError())
+ return false;
+
+ for (int i = 0; i < 4; ++i)
+ {
+ mnFirstLong = (mnFirstLong << 8) | sal_uInt32(maFirstBytes[i]);
+ mnSecondLong = (mnSecondLong << 8) | sal_uInt32(maFirstBytes[i + 4]);
+ }
+ return true;
+}
+
+bool GraphicFormatDetector::checkMET()
+{
+ if (maFirstBytes[2] != 0xd3)
+ return false;
+ mrStream.SetEndian(SvStreamEndian::BIG);
+ mrStream.Seek(mnStreamPosition);
+ sal_uInt16 nFieldSize;
+ sal_uInt8 nMagic;
+
+ mrStream.ReadUInt16(nFieldSize).ReadUChar(nMagic);
+ for (int i = 0; i < 3; i++)
+ {
+ if (nFieldSize < 6)
+ return false;
+ if (mnStreamLength < mrStream.Tell() + nFieldSize)
+ return false;
+ mrStream.SeekRel(nFieldSize - 3);
+ mrStream.ReadUInt16(nFieldSize).ReadUChar(nMagic);
+ if (nMagic != 0xd3)
+ return false;
+ }
+ mrStream.SetEndian(SvStreamEndian::LITTLE);
+
+ if (mrStream.GetError())
+ return false;
+
+ msDetectedFormat = "MET";
+ return true;
+}
+
+bool GraphicFormatDetector::checkBMP()
+{
+ sal_uInt8 nOffset;
+
+ // We're possibly also able to read an OS/2 bitmap array
+ // ('BA'), therefore we must adjust the offset to discover the
+ // first bitmap in the array
+ if (maFirstBytes[0] == 0x42 && maFirstBytes[1] == 0x41)
+ nOffset = 14;
+ else
+ nOffset = 0;
+
+ // Now we initially test on 'BM'
+ if (maFirstBytes[0 + nOffset] == 0x42 && maFirstBytes[1 + nOffset] == 0x4d)
+ {
+ // OS/2 can set the Reserved flags to a value other than 0
+ // (which they really should not do...);
+ // In this case we test the size of the BmpInfoHeaders
+ if ((maFirstBytes[6 + nOffset] == 0x00 && maFirstBytes[7 + nOffset] == 0x00
+ && maFirstBytes[8 + nOffset] == 0x00 && maFirstBytes[9 + nOffset] == 0x00)
+ || maFirstBytes[14 + nOffset] == 0x28 || maFirstBytes[14 + nOffset] == 0x0c)
+ {
+ msDetectedFormat = "BMP";
+ return true;
+ }
+ }
+ return false;
+}
+
+bool GraphicFormatDetector::checkWMForEMF()
+{
+ sal_uInt64 nCheckSize = std::min<sal_uInt64>(mnStreamLength, 256);
+ sal_uInt8 sExtendedOrDecompressedFirstBytes[WMF_EMF_CHECK_SIZE];
+ sal_uInt64 nDecompressedSize = nCheckSize;
+ // check if it is gzipped -> wmz/emz
+ sal_uInt8* pCheckArray = checkAndUncompressBuffer(sExtendedOrDecompressedFirstBytes,
+ WMF_EMF_CHECK_SIZE, nDecompressedSize);
+ if (mnFirstLong == 0xd7cdc69a || mnFirstLong == 0x01000900)
+ {
+ msDetectedFormat = "WMF";
+ return true;
+ }
+ else if (mnFirstLong == 0x01000000 && pCheckArray[40] == 0x20 && pCheckArray[41] == 0x45
+ && pCheckArray[42] == 0x4d && pCheckArray[43] == 0x46)
+ {
+ msDetectedFormat = "EMF";
+ return true;
+ }
+ return false;
+}
+
+bool GraphicFormatDetector::checkPCX()
+{
+ if (maFirstBytes[0] != 0x0a)
+ return false;
+
+ sal_uInt8 nVersion = maFirstBytes[1];
+ sal_uInt8 nEncoding = maFirstBytes[2];
+ if ((nVersion == 0 || nVersion == 2 || nVersion == 3 || nVersion == 5) && nEncoding <= 1)
+ {
+ msDetectedFormat = "PCX";
+ return true;
+ }
+
+ return false;
+}
+
+bool GraphicFormatDetector::checkTIF()
+{
+ if (mnFirstLong == 0x49492a00 || mnFirstLong == 0x4d4d002a)
+ {
+ msDetectedFormat = "TIF";
+ return true;
+ }
+ return false;
+}
+
+bool GraphicFormatDetector::checkGIF()
+{
+ if (mnFirstLong == 0x47494638 && (maFirstBytes[4] == 0x37 || maFirstBytes[4] == 0x39)
+ && maFirstBytes[5] == 0x61)
+ {
+ msDetectedFormat = "GIF";
+ return true;
+ }
+ return false;
+}
+
+bool GraphicFormatDetector::checkPNG()
+{
+ if (mnFirstLong == 0x89504e47 && mnSecondLong == 0x0d0a1a0a)
+ {
+ msDetectedFormat = "PNG";
+ return true;
+ }
+ return false;
+}
+
+bool GraphicFormatDetector::checkJPG()
+{
+ if ((mnFirstLong == 0xffd8ffe0 && maFirstBytes[6] == 0x4a && maFirstBytes[7] == 0x46
+ && maFirstBytes[8] == 0x49 && maFirstBytes[9] == 0x46)
+ || (mnFirstLong == 0xffd8fffe) || (0xffd8ff00 == (mnFirstLong & 0xffffff00)))
+ {
+ msDetectedFormat = "JPG";
+ return true;
+ }
+ return false;
+}
+
+bool GraphicFormatDetector::checkSVM()
+{
+ if (mnFirstLong == 0x53564744 && maFirstBytes[4] == 0x49)
+ {
+ msDetectedFormat = "SVM";
+ return true;
+ }
+ else if (maFirstBytes[0] == 0x56 && maFirstBytes[1] == 0x43 && maFirstBytes[2] == 0x4C
+ && maFirstBytes[3] == 0x4D && maFirstBytes[4] == 0x54 && maFirstBytes[5] == 0x46)
+ {
+ msDetectedFormat = "SVM";
+ return true;
+ }
+ return false;
+}
+
+bool GraphicFormatDetector::checkPCD()
+{
+ if (mnStreamLength < 2055)
+ return false;
+ char sBuffer[8];
+ mrStream.Seek(mnStreamPosition + 2048);
+ sBuffer[mrStream.ReadBytes(sBuffer, 7)] = 0;
+
+ if (strncmp(sBuffer, "PCD_IPI", 7) == 0)
+ {
+ msDetectedFormat = "PCD";
+ return true;
+ }
+ return false;
+}
+
+bool GraphicFormatDetector::checkPSD()
+{
+ if ((mnFirstLong == 0x38425053) && ((mnSecondLong >> 16) == 1))
+ {
+ msDetectedFormat = "PSD";
+ return true;
+ }
+ return false;
+}
+
+bool GraphicFormatDetector::checkEPS()
+{
+ const char* pFirstBytesAsCharArray = reinterpret_cast<char*>(maFirstBytes.data());
+
+ if (mnFirstLong == 0xC5D0D3C6)
+ {
+ msDetectedFormat = "EPS";
+ return true;
+ }
+ else if (checkArrayForMatchingStrings(pFirstBytesAsCharArray, 30, { "%!PS-Adobe", " EPS" }))
+ {
+ msDetectedFormat = "EPS";
+ return true;
+ }
+
+ return false;
+}
+
+bool GraphicFormatDetector::checkDXF()
+{
+ if (strncmp(reinterpret_cast<char*>(maFirstBytes.data()), "AutoCAD Binary DXF", 18) == 0)
+ {
+ msDetectedFormat = "DXF";
+ return true;
+ }
+
+ // ASCII DXF File Format
+ int i = 0;
+ while (i < 256 && maFirstBytes[i] <= 32)
+ {
+ ++i;
+ }
+
+ if (i < 256 && maFirstBytes[i] == '0')
+ {
+ ++i;
+
+ // only now do we have sufficient data to make a judgement
+ // based on a '0' + 'SECTION' == DXF argument
+
+ while (i < 256 && maFirstBytes[i] <= 32)
+ {
+ ++i;
+ }
+
+ if (i + 7 < 256
+ && (strncmp(reinterpret_cast<char*>(maFirstBytes.data() + i), "SECTION", 7) == 0))
+ {
+ msDetectedFormat = "DXF";
+ return true;
+ }
+ }
+ return false;
+}
+
+bool GraphicFormatDetector::checkPCT()
+{
+ if (isPCT(mrStream, mnStreamPosition, mnStreamLength))
+ {
+ msDetectedFormat = "PCT";
+ return true;
+ }
+ return false;
+}
+
+bool GraphicFormatDetector::checkPBMorPGMorPPM()
+{
+ if (maFirstBytes[0] == 'P')
+ {
+ switch (maFirstBytes[1])
+ {
+ case '1':
+ case '4':
+ msDetectedFormat = "PBM";
+ return true;
+
+ case '2':
+ case '5':
+ msDetectedFormat = "PGM";
+ return true;
+
+ case '3':
+ case '6':
+ msDetectedFormat = "PPM";
+ return true;
+ }
+ }
+ return false;
+}
+
+bool GraphicFormatDetector::checkRAS()
+{
+ if (mnFirstLong == 0x59a66a95)
+ {
+ msDetectedFormat = "RAS";
+ return true;
+ }
+ return false;
+}
+
+bool GraphicFormatDetector::checkXPM()
+{
+ const char* pFirstBytesAsCharArray = reinterpret_cast<char*>(maFirstBytes.data());
+ if (matchArrayWithString(pFirstBytesAsCharArray, 256, "/* XPM */"))
+ {
+ msDetectedFormat = "XPM";
+ return true;
+ }
+ return false;
+}
+
+bool GraphicFormatDetector::checkXBM()
+{
+ sal_uInt64 nSize = std::min<sal_uInt64>(mnStreamLength, 2048);
+ std::unique_ptr<sal_uInt8[]> pBuffer(new sal_uInt8[nSize]);
+
+ mrStream.Seek(mnStreamPosition);
+ nSize = mrStream.ReadBytes(pBuffer.get(), nSize);
+
+ const char* pBufferAsCharArray = reinterpret_cast<char*>(pBuffer.get());
+
+ if (checkArrayForMatchingStrings(pBufferAsCharArray, nSize, { "#define", "_width" }))
+ {
+ msDetectedFormat = "XBM";
+ return true;
+ }
+ return false;
+}
+
+bool GraphicFormatDetector::checkSVG()
+{
+ sal_uInt64 nCheckSize = std::min<sal_uInt64>(mnStreamLength, 256);
+ sal_uInt8 sExtendedOrDecompressedFirstBytes[SVG_CHECK_SIZE];
+ sal_uInt64 nDecompressedSize = nCheckSize;
+ // check if it is gzipped -> svgz
+ sal_uInt8* pCheckArray = checkAndUncompressBuffer(sExtendedOrDecompressedFirstBytes,
+ SVG_CHECK_SIZE, nDecompressedSize);
+ nCheckSize = std::min<sal_uInt64>(nDecompressedSize, 256);
+ bool bIsSvg(false);
+ bool bIsGZip = (nDecompressedSize > 0);
+ const char* pCheckArrayAsCharArray = reinterpret_cast<char*>(pCheckArray);
+ // check for XML
+ // #119176# SVG files which have no xml header at all have shown up this is optional
+ // check for "xml" then "version" then "DOCTYPE" and "svg" tags
+ if (checkArrayForMatchingStrings(pCheckArrayAsCharArray, nCheckSize,
+ { "<?xml", "version", "DOCTYPE", "svg" }))
+ {
+ bIsSvg = true;
+ }
+
+ // check for svg element in 1st 256 bytes
+ // search for '<svg'
+ if (!bIsSvg && checkArrayForMatchingStrings(pCheckArrayAsCharArray, nCheckSize, { "<svg" }))
+ {
+ bIsSvg = true;
+ }
+
+ // extended search for svg element
+ if (!bIsSvg)
+ {
+ // it's a xml, look for '<svg' in full file. Should not happen too
+ // often since the tests above will handle most cases, but can happen
+ // with Svg files containing big comment headers or Svg as the host
+ // language
+
+ pCheckArrayAsCharArray = reinterpret_cast<char*>(sExtendedOrDecompressedFirstBytes);
+
+ if (bIsGZip)
+ {
+ nCheckSize = std::min<sal_uInt64>(nDecompressedSize, 2048);
+ }
+ else
+ {
+ nCheckSize = std::min<sal_uInt64>(mnStreamLength, 2048);
+ mrStream.Seek(mnStreamPosition);
+ nCheckSize = mrStream.ReadBytes(sExtendedOrDecompressedFirstBytes, nCheckSize);
+ }
+
+ // search for '<svg'
+ if (checkArrayForMatchingStrings(pCheckArrayAsCharArray, nCheckSize, { "<svg" }))
+ {
+ bIsSvg = true;
+ }
+ }
+
+ if (bIsSvg)
+ {
+ msDetectedFormat = "SVG";
+ return true;
+ }
+ return false;
+}
+
+bool GraphicFormatDetector::checkTGA()
+{
+ // Check TGA ver.2 footer bytes
+ if (mnStreamLength > 18)
+ {
+ char sFooterBytes[18];
+
+ mrStream.Seek(STREAM_SEEK_TO_END);
+ mrStream.SeekRel(-18);
+ if (mrStream.ReadBytes(sFooterBytes, 18) == 18
+ && memcmp(sFooterBytes, "TRUEVISION-XFILE.", SAL_N_ELEMENTS(sFooterBytes)) == 0)
+ {
+ msDetectedFormat = "TGA";
+ return true;
+ }
+ }
+
+ // Fallback to file extension check
+ if (maExtension.startsWith("TGA"))
+ {
+ msDetectedFormat = "TGA";
+ return true;
+ }
+ return false;
+}
+
+bool GraphicFormatDetector::checkMOV()
+{
+ if ((maFirstBytes[4] == 'f' && maFirstBytes[5] == 't' && maFirstBytes[6] == 'y'
+ && maFirstBytes[7] == 'p' && maFirstBytes[8] == 'q' && maFirstBytes[9] == 't')
+ || (maFirstBytes[4] == 'm' && maFirstBytes[5] == 'o' && maFirstBytes[6] == 'o'
+ && maFirstBytes[7] == 'v' && maFirstBytes[11] == 'l' && maFirstBytes[12] == 'm'))
+ {
+ msDetectedFormat = "MOV";
+ return true;
+ }
+ return false;
+}
+
+bool GraphicFormatDetector::checkPDF()
+{
+ if (maFirstBytes[0] == '%' && maFirstBytes[1] == 'P' && maFirstBytes[2] == 'D'
+ && maFirstBytes[3] == 'F' && maFirstBytes[4] == '-')
+ {
+ msDetectedFormat = "PDF";
+ return true;
+ }
+ return false;
+}
+
+bool GraphicFormatDetector::checkWEBP()
+{
+ if (maFirstBytes[0] == 'R' && maFirstBytes[1] == 'I' && maFirstBytes[2] == 'F'
+ && maFirstBytes[3] == 'F' && maFirstBytes[8] == 'W' && maFirstBytes[9] == 'E'
+ && maFirstBytes[10] == 'B' && maFirstBytes[11] == 'P')
+ {
+ msDetectedFormat = "WEBP";
+ return true;
+ }
+ return false;
+}
+
+sal_uInt8* GraphicFormatDetector::checkAndUncompressBuffer(sal_uInt8* aUncompressedBuffer,
+ sal_uInt32 nSize, sal_uInt64& nRetSize)
+{
+ if (ZCodec::IsZCompressed(mrStream))
+ {
+ ZCodec aCodec;
+ mrStream.Seek(mnStreamPosition);
+ aCodec.BeginCompression(ZCODEC_DEFAULT_COMPRESSION, /*gzLib*/ true);
+ auto nDecompressedOut = aCodec.Read(mrStream, aUncompressedBuffer, nSize);
+ // ZCodec::Decompress returns -1 on failure
+ nRetSize = nDecompressedOut < 0 ? 0 : nDecompressedOut;
+ aCodec.EndCompression();
+ // Recalculate first/second long
+ for (int i = 0; i < 4; ++i)
+ {
+ mnFirstLong = (mnFirstLong << 8) | sal_uInt32(aUncompressedBuffer[i]);
+ mnSecondLong = (mnSecondLong << 8) | sal_uInt32(aUncompressedBuffer[i + 4]);
+ }
+ return aUncompressedBuffer;
+ }
+ nRetSize = 0;
+ return maFirstBytes.data();
+}
+
+} // vcl namespace
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/vcl/source/filter/GraphicNativeMetadata.cxx b/vcl/source/filter/GraphicNativeMetadata.cxx
new file mode 100644
index 000000000..5b11fcc78
--- /dev/null
+++ b/vcl/source/filter/GraphicNativeMetadata.cxx
@@ -0,0 +1,59 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <vcl/GraphicNativeMetadata.hxx>
+#include <vcl/gfxlink.hxx>
+#include "jpeg/Exif.hxx"
+#include <memory>
+
+GraphicNativeMetadata::GraphicNativeMetadata()
+ : mRotation(0)
+{
+}
+
+bool GraphicNativeMetadata::read(Graphic const& rGraphic)
+{
+ GfxLink aLink = rGraphic.GetGfxLink();
+ if (aLink.GetType() != GfxLinkType::NativeJpg)
+ return false;
+
+ sal_uInt32 aDataSize = aLink.GetDataSize();
+ if (!aDataSize)
+ return false;
+
+ std::unique_ptr<sal_uInt8[]> aBuffer(new sal_uInt8[aDataSize]);
+
+ memcpy(aBuffer.get(), aLink.GetData(), aDataSize);
+ SvMemoryStream aMemoryStream(aBuffer.get(), aDataSize, StreamMode::READ);
+
+ read(aMemoryStream);
+
+ return true;
+}
+
+bool GraphicNativeMetadata::read(SvStream& rStream)
+{
+ Exif aExif;
+ aExif.read(rStream);
+ mRotation = aExif.getRotation();
+
+ return true;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/vcl/source/filter/GraphicNativeTransform.cxx b/vcl/source/filter/GraphicNativeTransform.cxx
new file mode 100644
index 000000000..792dd6a93
--- /dev/null
+++ b/vcl/source/filter/GraphicNativeTransform.cxx
@@ -0,0 +1,164 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <vcl/GraphicNativeTransform.hxx>
+
+#include <vcl/gfxlink.hxx>
+#include <vcl/graphicfilter.hxx>
+#include <com/sun/star/beans/PropertyValue.hpp>
+#include <com/sun/star/uno/Sequence.hxx>
+#include <tools/stream.hxx>
+#include <comphelper/propertyvalue.hxx>
+
+#include "jpeg/Exif.hxx"
+#include "jpeg/JpegTransform.hxx"
+
+GraphicNativeTransform::GraphicNativeTransform(Graphic& rGraphic)
+ : mrGraphic(rGraphic)
+{
+}
+
+void GraphicNativeTransform::rotate(Degree10 aInputRotation)
+{
+ // Rotation can be between 0 and 3600
+ Degree10 aRotation = aInputRotation % 3600_deg10;
+
+ if (aRotation == 0_deg10)
+ {
+ return; // No rotation is needed
+ }
+ else if (aRotation != 900_deg10 && aRotation != 1800_deg10 && aRotation != 2700_deg10)
+ {
+ return;
+ }
+
+ GfxLink aLink = mrGraphic.GetGfxLink();
+ if (aLink.GetType() == GfxLinkType::NativeJpg)
+ {
+ rotateJPEG(aRotation);
+ }
+ else if (aLink.GetType() == GfxLinkType::NativePng)
+ {
+ rotateGeneric(aRotation, u"png");
+ }
+ else if (aLink.GetType() == GfxLinkType::NativeGif)
+ {
+ rotateGeneric(aRotation, u"gif");
+ }
+ else if (aLink.GetType() == GfxLinkType::NONE)
+ {
+ rotateBitmapOnly(aRotation);
+ }
+}
+
+bool GraphicNativeTransform::rotateBitmapOnly(Degree10 aRotation)
+{
+ if (mrGraphic.IsAnimated())
+ {
+ return false;
+ }
+
+ BitmapEx aBitmap = mrGraphic.GetBitmapEx();
+ aBitmap.Rotate(aRotation, COL_BLACK);
+ mrGraphic = aBitmap;
+
+ return true;
+}
+
+bool GraphicNativeTransform::rotateGeneric(Degree10 aRotation, std::u16string_view aType)
+{
+ // Can't rotate animations yet
+ if (mrGraphic.IsAnimated())
+ {
+ return false;
+ }
+
+ SvMemoryStream aStream;
+
+ GraphicFilter& rFilter = GraphicFilter::GetGraphicFilter();
+
+ css::uno::Sequence<css::beans::PropertyValue> aFilterData{
+ comphelper::makePropertyValue("Interlaced", sal_Int32(0)),
+ comphelper::makePropertyValue("Compression", sal_Int32(9)),
+ comphelper::makePropertyValue("Quality", sal_Int32(90))
+ };
+
+ sal_uInt16 nFilterFormat = rFilter.GetExportFormatNumberForShortName(aType);
+
+ BitmapEx aBitmap = mrGraphic.GetBitmapEx();
+ aBitmap.Rotate(aRotation, COL_BLACK);
+ rFilter.ExportGraphic(aBitmap, u"none", aStream, nFilterFormat, &aFilterData);
+
+ aStream.Seek(STREAM_SEEK_TO_BEGIN);
+
+ Graphic aGraphic;
+ rFilter.ImportGraphic(aGraphic, u"import", aStream);
+
+ mrGraphic = aGraphic;
+ return true;
+}
+
+void GraphicNativeTransform::rotateJPEG(Degree10 aRotation)
+{
+ BitmapEx aBitmap = mrGraphic.GetBitmapEx();
+
+ if (aBitmap.GetSizePixel().Width() % 16 != 0 || aBitmap.GetSizePixel().Height() % 16 != 0)
+ {
+ rotateGeneric(aRotation, u"png");
+ }
+ else
+ {
+ GfxLink aLink = mrGraphic.GetGfxLink();
+
+ SvMemoryStream aSourceStream;
+ aSourceStream.WriteBytes(aLink.GetData(), aLink.GetDataSize());
+ aSourceStream.Seek(STREAM_SEEK_TO_BEGIN);
+
+ exif::Orientation aOrientation = exif::TOP_LEFT;
+
+ Exif exif;
+ if (exif.read(aSourceStream))
+ {
+ aOrientation = exif.getOrientation();
+ }
+
+ SvMemoryStream aTargetStream;
+ JpegTransform transform(aSourceStream, aTargetStream);
+ transform.setRotate(aRotation);
+ transform.perform();
+
+ aTargetStream.Seek(STREAM_SEEK_TO_BEGIN);
+
+ // Reset orientation in exif if needed
+ if (exif.hasExif() && aOrientation != exif::TOP_LEFT)
+ {
+ exif.setOrientation(exif::TOP_LEFT);
+ exif.write(aTargetStream);
+ }
+
+ aTargetStream.Seek(STREAM_SEEK_TO_BEGIN);
+
+ Graphic aGraphic;
+ GraphicFilter& rFilter = GraphicFilter::GetGraphicFilter();
+ rFilter.ImportGraphic(aGraphic, u"import", aTargetStream);
+ mrGraphic = aGraphic;
+ }
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/vcl/source/filter/bmp/BmpReader.cxx b/vcl/source/filter/bmp/BmpReader.cxx
new file mode 100644
index 000000000..3d2b6a463
--- /dev/null
+++ b/vcl/source/filter/bmp/BmpReader.cxx
@@ -0,0 +1,30 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <filter/BmpReader.hxx>
+#include <vcl/TypeSerializer.hxx>
+
+bool BmpReader(SvStream& rStream, Graphic& rGraphic)
+{
+ TypeSerializer aSerializer(rStream);
+ aSerializer.readGraphic(rGraphic);
+ return !rStream.GetError();
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/vcl/source/filter/bmp/BmpWriter.cxx b/vcl/source/filter/bmp/BmpWriter.cxx
new file mode 100644
index 000000000..a5fcedec2
--- /dev/null
+++ b/vcl/source/filter/bmp/BmpWriter.cxx
@@ -0,0 +1,40 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <filter/BmpWriter.hxx>
+#include <vcl/dibtools.hxx>
+
+bool BmpWriter(SvStream& rStream, const Graphic& rGraphic, FilterConfigItem* pFilterConfigItem)
+{
+ BitmapEx aBitmap = rGraphic.GetBitmapEx();
+ sal_Int32 nColor = pFilterConfigItem->ReadInt32("Color", 0);
+
+ auto nColorRes = static_cast<BmpConversion>(nColor);
+ if (nColorRes != BmpConversion::NNONE && nColorRes <= BmpConversion::N24Bit)
+ {
+ if (!aBitmap.Convert(nColorRes))
+ aBitmap = rGraphic.GetBitmapEx();
+ }
+ bool bRleCoding = pFilterConfigItem->ReadBool("RLE_Coding", true);
+ WriteDIB(aBitmap, rStream, bRleCoding);
+
+ return rStream.good();
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/vcl/source/filter/egif/egif.cxx b/vcl/source/filter/egif/egif.cxx
new file mode 100644
index 000000000..699b7f05d
--- /dev/null
+++ b/vcl/source/filter/egif/egif.cxx
@@ -0,0 +1,549 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+
+#include <tools/stream.hxx>
+#include <tools/debug.hxx>
+#include <vcl/BitmapReadAccess.hxx>
+#include <vcl/graph.hxx>
+#include <vcl/outdev.hxx>
+#include <vcl/FilterConfigItem.hxx>
+#include <com/sun/star/task/XStatusIndicator.hpp>
+#include "giflzwc.hxx"
+#include <memory>
+#include <filter/GifWriter.hxx>
+
+namespace {
+
+class GIFWriter
+{
+ Bitmap aAccBmp;
+ SvStream& m_rGIF;
+ BitmapReadAccess* m_pAcc;
+ sal_uInt32 nMinPercent;
+ sal_uInt32 nMaxPercent;
+ sal_uInt32 nLastPercent;
+ tools::Long nActX;
+ tools::Long nActY;
+ sal_Int32 nInterlaced;
+ bool bStatus;
+ bool bTransparent;
+
+ void MayCallback(sal_uInt32 nPercent);
+ void WriteSignature( bool bGIF89a );
+ void WriteGlobalHeader( const Size& rSize );
+ void WriteLoopExtension( const Animation& rAnimation );
+ void WriteLogSizeExtension( const Size& rSize100 );
+ void WriteImageExtension( tools::Long nTimer, Disposal eDisposal );
+ void WriteLocalHeader();
+ void WritePalette();
+ void WriteAccess();
+ void WriteTerminator();
+
+ bool CreateAccess( const BitmapEx& rBmpEx );
+ void DestroyAccess();
+
+ void WriteAnimation( const Animation& rAnimation );
+ void WriteBitmapEx( const BitmapEx& rBmpEx, const Point& rPoint, bool bExtended,
+ tools::Long nTimer = 0, Disposal eDisposal = Disposal::Not );
+
+ css::uno::Reference< css::task::XStatusIndicator > xStatusIndicator;
+
+public:
+
+ explicit GIFWriter(SvStream &rStream);
+
+ bool WriteGIF( const Graphic& rGraphic, FilterConfigItem* pConfigItem );
+};
+
+}
+
+GIFWriter::GIFWriter(SvStream &rStream)
+ : m_rGIF(rStream)
+ , m_pAcc(nullptr)
+ , nMinPercent(0)
+ , nMaxPercent(0)
+ , nLastPercent(0)
+ , nActX(0)
+ , nActY(0)
+ , nInterlaced(0)
+ , bStatus(false)
+ , bTransparent(false)
+{
+}
+
+
+bool GIFWriter::WriteGIF(const Graphic& rGraphic, FilterConfigItem* pFilterConfigItem)
+{
+ if ( pFilterConfigItem )
+ {
+ xStatusIndicator = pFilterConfigItem->GetStatusIndicator();
+ if ( xStatusIndicator.is() )
+ {
+ xStatusIndicator->start( OUString(), 100 );
+ }
+ }
+
+ Size aSize100;
+ const MapMode aMap( rGraphic.GetPrefMapMode() );
+ bool bLogSize = ( aMap.GetMapUnit() != MapUnit::MapPixel );
+
+ if( bLogSize )
+ aSize100 = OutputDevice::LogicToLogic(rGraphic.GetPrefSize(), aMap, MapMode(MapUnit::Map100thMM));
+
+ bStatus = true;
+ nLastPercent = 0;
+ nInterlaced = 0;
+ m_pAcc = nullptr;
+
+ if ( pFilterConfigItem )
+ nInterlaced = pFilterConfigItem->ReadInt32( "Interlaced", 0 );
+
+ m_rGIF.SetEndian( SvStreamEndian::LITTLE );
+
+ if( rGraphic.IsAnimated() )
+ {
+ const Animation& rAnimation = rGraphic.GetAnimation();
+
+ WriteSignature( true );
+
+ if ( bStatus )
+ {
+ WriteGlobalHeader( rAnimation.GetDisplaySizePixel() );
+
+ if( bStatus )
+ {
+ WriteLoopExtension( rAnimation );
+
+ if( bStatus )
+ WriteAnimation( rAnimation );
+ }
+ }
+ }
+ else
+ {
+ const bool bGrafTrans = rGraphic.IsTransparent();
+
+ BitmapEx aBmpEx = rGraphic.GetBitmapEx();
+
+ nMinPercent = 0;
+ nMaxPercent = 100;
+
+ WriteSignature( bGrafTrans || bLogSize );
+
+ if( bStatus )
+ {
+ WriteGlobalHeader( aBmpEx.GetSizePixel() );
+
+ if( bStatus )
+ WriteBitmapEx( aBmpEx, Point(), bGrafTrans );
+ }
+ }
+
+ if( bStatus )
+ {
+ if( bLogSize )
+ WriteLogSizeExtension( aSize100 );
+
+ WriteTerminator();
+ }
+
+ if ( xStatusIndicator.is() )
+ xStatusIndicator->end();
+
+ return bStatus;
+}
+
+
+void GIFWriter::WriteBitmapEx( const BitmapEx& rBmpEx, const Point& rPoint,
+ bool bExtended, tools::Long nTimer, Disposal eDisposal )
+{
+ if( !CreateAccess( rBmpEx ) )
+ return;
+
+ nActX = rPoint.X();
+ nActY = rPoint.Y();
+
+ if( bExtended )
+ WriteImageExtension( nTimer, eDisposal );
+
+ if( bStatus )
+ {
+ WriteLocalHeader();
+
+ if( bStatus )
+ {
+ WritePalette();
+
+ if( bStatus )
+ WriteAccess();
+ }
+ }
+
+ DestroyAccess();
+}
+
+
+void GIFWriter::WriteAnimation( const Animation& rAnimation )
+{
+ const sal_uInt16 nCount = rAnimation.Count();
+
+ if( !nCount )
+ return;
+
+ const double fStep = 100. / nCount;
+
+ nMinPercent = 0;
+ nMaxPercent = static_cast<sal_uInt32>(fStep);
+
+ for( sal_uInt16 i = 0; i < nCount; i++ )
+ {
+ const AnimationBitmap& rAnimationBitmap = rAnimation.Get( i );
+
+ WriteBitmapEx(rAnimationBitmap.maBitmapEx, rAnimationBitmap.maPositionPixel, true,
+ rAnimationBitmap.mnWait, rAnimationBitmap.meDisposal );
+ nMinPercent = nMaxPercent;
+ nMaxPercent = static_cast<sal_uInt32>(nMaxPercent + fStep);
+ }
+}
+
+
+void GIFWriter::MayCallback(sal_uInt32 nPercent)
+{
+ if ( xStatusIndicator.is() )
+ {
+ if( nPercent >= nLastPercent + 3 )
+ {
+ nLastPercent = nPercent;
+ if ( nPercent <= 100 )
+ xStatusIndicator->setValue( nPercent );
+ }
+ }
+}
+
+
+bool GIFWriter::CreateAccess( const BitmapEx& rBmpEx )
+{
+ if( bStatus )
+ {
+ Bitmap aMask( rBmpEx.GetAlpha() );
+
+ aAccBmp = rBmpEx.GetBitmap();
+ bTransparent = false;
+
+ if( !aMask.IsEmpty() )
+ {
+ if( aAccBmp.Convert( BmpConversion::N8BitTrans ) )
+ {
+ aMask.Convert( BmpConversion::N1BitThreshold );
+ aAccBmp.Replace( aMask, BMP_COL_TRANS );
+ bTransparent = true;
+ }
+ else
+ aAccBmp.Convert( BmpConversion::N8BitColors );
+ }
+ else
+ aAccBmp.Convert( BmpConversion::N8BitColors );
+
+ m_pAcc = aAccBmp.AcquireReadAccess();
+
+ if( !m_pAcc )
+ bStatus = false;
+ }
+
+ return bStatus;
+}
+
+
+void GIFWriter::DestroyAccess()
+{
+ Bitmap::ReleaseAccess( m_pAcc );
+ m_pAcc = nullptr;
+}
+
+
+void GIFWriter::WriteSignature( bool bGIF89a )
+{
+ if( bStatus )
+ {
+ m_rGIF.WriteBytes(bGIF89a ? "GIF89a" : "GIF87a" , 6);
+
+ if( m_rGIF.GetError() )
+ bStatus = false;
+ }
+}
+
+
+void GIFWriter::WriteGlobalHeader( const Size& rSize )
+{
+ if( !bStatus )
+ return;
+
+ // 256 colors
+ const sal_uInt16 nWidth = static_cast<sal_uInt16>(rSize.Width());
+ const sal_uInt16 nHeight = static_cast<sal_uInt16>(rSize.Height());
+ const sal_uInt8 cFlags = 128 | ( 7 << 4 );
+
+ // write values
+ m_rGIF.WriteUInt16( nWidth );
+ m_rGIF.WriteUInt16( nHeight );
+ m_rGIF.WriteUChar( cFlags );
+ m_rGIF.WriteUChar( 0x00 );
+ m_rGIF.WriteUChar( 0x00 );
+
+ // write dummy palette with two entries (black/white);
+ // we do this only because of a bug in Photoshop, since those can't
+ // read pictures without a global color palette
+ m_rGIF.WriteUInt16( 0 );
+ m_rGIF.WriteUInt16( 255 );
+ m_rGIF.WriteUInt16( 65535 );
+
+ if( m_rGIF.GetError() )
+ bStatus = false;
+}
+
+
+void GIFWriter::WriteLoopExtension( const Animation& rAnimation )
+{
+ DBG_ASSERT( rAnimation.Count() > 0, "Animation has no bitmaps!" );
+
+ sal_uInt16 nLoopCount = static_cast<sal_uInt16>(rAnimation.GetLoopCount());
+
+ // if only one run should take place
+ // the LoopExtension won't be written
+ // The default in this case is a single run
+ if( nLoopCount == 1 )
+ return;
+
+ // Netscape interprets the LoopCount
+ // as the sole number of _repetitions_
+ if( nLoopCount )
+ nLoopCount--;
+
+ const sal_uInt8 cLoByte = static_cast<sal_uInt8>(nLoopCount);
+ const sal_uInt8 cHiByte = static_cast<sal_uInt8>( nLoopCount >> 8 );
+
+ m_rGIF.WriteUChar( 0x21 );
+ m_rGIF.WriteUChar( 0xff );
+ m_rGIF.WriteUChar( 0x0b );
+ m_rGIF.WriteBytes( "NETSCAPE2.0", 11 );
+ m_rGIF.WriteUChar( 0x03 );
+ m_rGIF.WriteUChar( 0x01 );
+ m_rGIF.WriteUChar( cLoByte );
+ m_rGIF.WriteUChar( cHiByte );
+ m_rGIF.WriteUChar( 0x00 );
+}
+
+
+void GIFWriter::WriteLogSizeExtension( const Size& rSize100 )
+{
+ // writer PrefSize in 100th-mm as ApplicationExtension
+ if( rSize100.Width() && rSize100.Height() )
+ {
+ m_rGIF.WriteUChar( 0x21 );
+ m_rGIF.WriteUChar( 0xff );
+ m_rGIF.WriteUChar( 0x0b );
+ m_rGIF.WriteBytes( "STARDIV 5.0", 11 );
+ m_rGIF.WriteUChar( 0x09 );
+ m_rGIF.WriteUChar( 0x01 );
+ m_rGIF.WriteUInt32( rSize100.Width() );
+ m_rGIF.WriteUInt32( rSize100.Height() );
+ m_rGIF.WriteUChar( 0x00 );
+ }
+}
+
+
+void GIFWriter::WriteImageExtension( tools::Long nTimer, Disposal eDisposal )
+{
+ if( !bStatus )
+ return;
+
+ const sal_uInt16 nDelay = static_cast<sal_uInt16>(nTimer);
+ sal_uInt8 cFlags = 0;
+
+ // set Transparency-Flag
+ if( bTransparent )
+ cFlags |= 1;
+
+ // set Disposal-value
+ if( eDisposal == Disposal::Back )
+ cFlags |= ( 2 << 2 );
+ else if( eDisposal == Disposal::Previous )
+ cFlags |= ( 3 << 2 );
+
+ m_rGIF.WriteUChar( 0x21 );
+ m_rGIF.WriteUChar( 0xf9 );
+ m_rGIF.WriteUChar( 0x04 );
+ m_rGIF.WriteUChar( cFlags );
+ m_rGIF.WriteUInt16( nDelay );
+ m_rGIF.WriteUChar( m_pAcc->GetBestPaletteIndex( BMP_COL_TRANS ) );
+ m_rGIF.WriteUChar( 0x00 );
+
+ if( m_rGIF.GetError() )
+ bStatus = false;
+}
+
+
+void GIFWriter::WriteLocalHeader()
+{
+ if( !bStatus )
+ return;
+
+ const sal_uInt16 nPosX = static_cast<sal_uInt16>(nActX);
+ const sal_uInt16 nPosY = static_cast<sal_uInt16>(nActY);
+ const sal_uInt16 nWidth = static_cast<sal_uInt16>(m_pAcc->Width());
+ const sal_uInt16 nHeight = static_cast<sal_uInt16>(m_pAcc->Height());
+ sal_uInt8 cFlags = static_cast<sal_uInt8>( m_pAcc->GetBitCount() - 1 );
+
+ // set Interlaced-Flag
+ if( nInterlaced )
+ cFlags |= 0x40;
+
+ // set Flag for the local color palette
+ cFlags |= 0x80;
+
+ m_rGIF.WriteUChar( 0x2c );
+ m_rGIF.WriteUInt16( nPosX );
+ m_rGIF.WriteUInt16( nPosY );
+ m_rGIF.WriteUInt16( nWidth );
+ m_rGIF.WriteUInt16( nHeight );
+ m_rGIF.WriteUChar( cFlags );
+
+ if( m_rGIF.GetError() )
+ bStatus = false;
+}
+
+
+void GIFWriter::WritePalette()
+{
+ if( !(bStatus && m_pAcc->HasPalette()) )
+ return;
+
+ const sal_uInt16 nCount = m_pAcc->GetPaletteEntryCount();
+ const sal_uInt16 nMaxCount = ( 1 << m_pAcc->GetBitCount() );
+
+ for ( sal_uInt16 i = 0; i < nCount; i++ )
+ {
+ const BitmapColor& rColor = m_pAcc->GetPaletteColor( i );
+
+ m_rGIF.WriteUChar( rColor.GetRed() );
+ m_rGIF.WriteUChar( rColor.GetGreen() );
+ m_rGIF.WriteUChar( rColor.GetBlue() );
+ }
+
+ // fill up the rest with 0
+ if( nCount < nMaxCount )
+ m_rGIF.SeekRel( ( nMaxCount - nCount ) * 3 );
+
+ if( m_rGIF.GetError() )
+ bStatus = false;
+}
+
+
+void GIFWriter::WriteAccess()
+{
+ GIFLZWCompressor aCompressor;
+ const tools::Long nWidth = m_pAcc->Width();
+ const tools::Long nHeight = m_pAcc->Height();
+ std::unique_ptr<sal_uInt8[]> pBuffer;
+ bool bNative = m_pAcc->GetScanlineFormat() == ScanlineFormat::N8BitPal;
+
+ if( !bNative )
+ pBuffer.reset(new sal_uInt8[ nWidth ]);
+
+ if( !(bStatus && ( 8 == m_pAcc->GetBitCount() ) && m_pAcc->HasPalette()) )
+ return;
+
+ aCompressor.StartCompression( m_rGIF, m_pAcc->GetBitCount() );
+
+ tools::Long nY, nT;
+
+ for( tools::Long i = 0; i < nHeight; ++i )
+ {
+ if( nInterlaced )
+ {
+ nY = i << 3;
+
+ if( nY >= nHeight )
+ {
+ nT = i - ( ( nHeight + 7 ) >> 3 );
+ nY= ( nT << 3 ) + 4;
+
+ if( nY >= nHeight )
+ {
+ nT -= ( nHeight + 3 ) >> 3;
+ nY = ( nT << 2 ) + 2;
+
+ if ( nY >= nHeight )
+ {
+ nT -= ( ( nHeight + 1 ) >> 2 );
+ nY = ( nT << 1 ) + 1;
+ }
+ }
+ }
+ }
+ else
+ nY = i;
+
+ if( bNative )
+ aCompressor.Compress( m_pAcc->GetScanline( nY ), nWidth );
+ else
+ {
+ Scanline pScanline = m_pAcc->GetScanline( nY );
+ for( tools::Long nX = 0; nX < nWidth; nX++ )
+ pBuffer[ nX ] = m_pAcc->GetIndexFromData( pScanline, nX );
+
+ aCompressor.Compress( pBuffer.get(), nWidth );
+ }
+
+ if ( m_rGIF.GetError() )
+ bStatus = false;
+
+ MayCallback( nMinPercent + ( nMaxPercent - nMinPercent ) * i / nHeight );
+
+ if( !bStatus )
+ break;
+ }
+
+ aCompressor.EndCompression();
+
+ if ( m_rGIF.GetError() )
+ bStatus = false;
+}
+
+
+void GIFWriter::WriteTerminator()
+{
+ if( bStatus )
+ {
+ m_rGIF.WriteUChar( 0x3b );
+
+ if( m_rGIF.GetError() )
+ bStatus = false;
+ }
+}
+
+
+bool ExportGifGraphic(SvStream& rStream, const Graphic& rGraphic, FilterConfigItem* pConfigItem)
+{
+ GIFWriter aWriter(rStream);
+ return aWriter.WriteGIF(rGraphic, pConfigItem);
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/vcl/source/filter/egif/giflzwc.cxx b/vcl/source/filter/egif/giflzwc.cxx
new file mode 100644
index 000000000..41c65d2da
--- /dev/null
+++ b/vcl/source/filter/egif/giflzwc.cxx
@@ -0,0 +1,225 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+
+#include <tools/stream.hxx>
+#include "giflzwc.hxx"
+#include <array>
+
+
+class GIFImageDataOutputStream
+{
+private:
+
+ void FlushBlockBuf();
+ inline void FlushBitsBufsFullBytes();
+
+ SvStream& rStream;
+ std::array<sal_uInt8, 255>
+ pBlockBuf;
+ sal_uInt8 nBlockBufSize;
+ sal_uInt32 nBitsBuf;
+ sal_uInt16 nBitsBufSize;
+
+public:
+
+ GIFImageDataOutputStream( SvStream & rGIF, sal_uInt8 nLZWDataSize );
+ ~GIFImageDataOutputStream();
+
+ inline void WriteBits( sal_uInt16 nCode, sal_uInt16 nCodeLen );
+};
+
+
+inline void GIFImageDataOutputStream::FlushBitsBufsFullBytes()
+{
+ while (nBitsBufSize>=8)
+ {
+ if( nBlockBufSize==255 )
+ FlushBlockBuf();
+
+ pBlockBuf[nBlockBufSize++] = static_cast<sal_uInt8>(nBitsBuf);
+ nBitsBuf >>= 8;
+ nBitsBufSize -= 8;
+ }
+}
+
+
+inline void GIFImageDataOutputStream::WriteBits( sal_uInt16 nCode, sal_uInt16 nCodeLen )
+{
+ if( nBitsBufSize+nCodeLen>32 )
+ FlushBitsBufsFullBytes();
+
+ nBitsBuf |= static_cast<sal_uInt32>(nCode) << nBitsBufSize;
+ nBitsBufSize = nBitsBufSize + nCodeLen;
+}
+
+
+GIFImageDataOutputStream::GIFImageDataOutputStream( SvStream & rGIF, sal_uInt8 nLZWDataSize ) :
+ rStream(rGIF), nBlockBufSize(0), nBitsBuf(0), nBitsBufSize(0)
+{
+ rStream.WriteUChar( nLZWDataSize );
+}
+
+
+GIFImageDataOutputStream::~GIFImageDataOutputStream()
+{
+ WriteBits(0,7);
+ FlushBitsBufsFullBytes();
+ FlushBlockBuf();
+ rStream.WriteUChar( 0 );
+}
+
+
+void GIFImageDataOutputStream::FlushBlockBuf()
+{
+ if( nBlockBufSize )
+ {
+ rStream.WriteUChar( nBlockBufSize );
+ rStream.WriteBytes(pBlockBuf.data(), nBlockBufSize);
+ nBlockBufSize = 0;
+ }
+}
+
+
+struct GIFLZWCTreeNode
+{
+
+ GIFLZWCTreeNode* pBrother; // next node which has the same father
+ GIFLZWCTreeNode* pFirstChild; // first
+ sal_uInt16 nCode; // the code for the string of pixel values which comes about
+ sal_uInt16 nValue; // the pixel value
+};
+
+
+GIFLZWCompressor::GIFLZWCompressor()
+ : pPrefix(nullptr), nDataSize(0), nClearCode(0),
+ nEOICode(0), nTableSize(0), nCodeSize(0)
+{
+}
+
+
+GIFLZWCompressor::~GIFLZWCompressor()
+{
+ if (pIDOS!=nullptr) EndCompression();
+}
+
+
+void GIFLZWCompressor::StartCompression( SvStream& rGIF, sal_uInt16 nPixelSize )
+{
+ if( pIDOS )
+ return;
+
+ sal_uInt16 i;
+
+ nDataSize = nPixelSize;
+
+ if( nDataSize < 2 )
+ nDataSize=2;
+
+ nClearCode=1<<nDataSize;
+ nEOICode=nClearCode+1;
+ nTableSize=nEOICode+1;
+ nCodeSize=nDataSize+1;
+
+ pIDOS.reset(new GIFImageDataOutputStream(rGIF,static_cast<sal_uInt8>(nDataSize)));
+ pTable.reset(new GIFLZWCTreeNode[4096]);
+
+ for (i=0; i<4096; i++)
+ {
+ pTable[i].pBrother = pTable[i].pFirstChild = nullptr;
+ pTable[i].nCode = i;
+ pTable[i].nValue = static_cast<sal_uInt8>( i );
+ }
+
+ pPrefix = nullptr;
+ pIDOS->WriteBits( nClearCode,nCodeSize );
+}
+
+void GIFLZWCompressor::Compress(sal_uInt8* pSrc, sal_uInt32 nSize)
+{
+ if( !pIDOS )
+ return;
+
+ GIFLZWCTreeNode* p;
+ sal_uInt16 i;
+ sal_uInt8 nV;
+
+ if( !pPrefix && nSize )
+ {
+ pPrefix=&pTable[*pSrc++];
+ nSize--;
+ }
+
+ while( nSize )
+ {
+ nSize--;
+ nV=*pSrc++;
+ for( p=pPrefix->pFirstChild; p!=nullptr; p=p->pBrother )
+ {
+ if (p->nValue==nV)
+ break;
+ }
+
+ if( p)
+ pPrefix=p;
+ else
+ {
+ pIDOS->WriteBits(pPrefix->nCode,nCodeSize);
+
+ if (nTableSize==4096)
+ {
+ pIDOS->WriteBits(nClearCode,nCodeSize);
+
+ for (i=0; i<nClearCode; i++)
+ pTable[i].pFirstChild=nullptr;
+
+ nCodeSize=nDataSize+1;
+ nTableSize=nEOICode+1;
+ }
+ else
+ {
+ if(nTableSize==static_cast<sal_uInt16>(1<<nCodeSize))
+ nCodeSize++;
+
+ p=&pTable[nTableSize++];
+ p->pBrother=pPrefix->pFirstChild;
+ pPrefix->pFirstChild=p;
+ p->nValue=nV;
+ p->pFirstChild=nullptr;
+ }
+
+ pPrefix=&pTable[nV];
+ }
+ }
+}
+
+void GIFLZWCompressor::EndCompression()
+{
+ if( pIDOS )
+ {
+ if( pPrefix )
+ pIDOS->WriteBits(pPrefix->nCode,nCodeSize);
+
+ pIDOS->WriteBits( nEOICode,nCodeSize );
+ pTable.reset();
+ pIDOS.reset();
+ }
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/vcl/source/filter/egif/giflzwc.hxx b/vcl/source/filter/egif/giflzwc.hxx
new file mode 100644
index 000000000..057710c85
--- /dev/null
+++ b/vcl/source/filter/egif/giflzwc.hxx
@@ -0,0 +1,55 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#ifndef INCLUDED_FILTER_SOURCE_GRAPHICFILTER_EGIF_GIFLZWC_HXX
+#define INCLUDED_FILTER_SOURCE_GRAPHICFILTER_EGIF_GIFLZWC_HXX
+
+#include <vcl/mapmod.hxx>
+
+
+class GIFImageDataOutputStream;
+struct GIFLZWCTreeNode;
+
+
+class GIFLZWCompressor
+{
+private:
+
+ std::unique_ptr<GIFImageDataOutputStream> pIDOS;
+ std::unique_ptr<GIFLZWCTreeNode[]> pTable;
+ GIFLZWCTreeNode* pPrefix;
+ sal_uInt16 nDataSize;
+ sal_uInt16 nClearCode;
+ sal_uInt16 nEOICode;
+ sal_uInt16 nTableSize;
+ sal_uInt16 nCodeSize;
+
+public:
+
+ GIFLZWCompressor();
+ ~GIFLZWCompressor();
+
+ void StartCompression( SvStream& rGIF, sal_uInt16 nPixelSize );
+ void Compress(sal_uInt8* pSrc, sal_uInt32 nSize);
+ void EndCompression();
+};
+
+#endif // INCLUDED_FILTER_SOURCE_GRAPHICFILTER_EGIF_GIFLZWC_HXX
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/vcl/source/filter/eps/eps.cxx b/vcl/source/filter/eps/eps.cxx
new file mode 100644
index 000000000..bb25bdccb
--- /dev/null
+++ b/vcl/source/filter/eps/eps.cxx
@@ -0,0 +1,2667 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <filter/EpsWriter.hxx>
+#include <tools/stream.hxx>
+#include <tools/poly.hxx>
+#include <tools/fract.hxx>
+#include <tools/helpers.hxx>
+#include <unotools/resmgr.hxx>
+#include <vcl/svapp.hxx>
+#include <vcl/metaact.hxx>
+#include <vcl/graph.hxx>
+#include <vcl/BitmapReadAccess.hxx>
+#include <vcl/region.hxx>
+#include <vcl/font.hxx>
+#include <vcl/virdev.hxx>
+#include <vcl/cvtgrf.hxx>
+#include <vcl/gradient.hxx>
+#include <unotools/configmgr.hxx>
+#include <vcl/FilterConfigItem.hxx>
+#include <vcl/graphictools.hxx>
+#include <vcl/weld.hxx>
+#include <strings.hrc>
+#include <osl/diagnose.h>
+#include <com/sun/star/task/XStatusIndicator.hpp>
+#include <officecfg/Office/Common.hxx>
+
+#include <cstdlib>
+#include <memory>
+
+using namespace ::com::sun::star::uno;
+
+#define POSTSCRIPT_BOUNDINGSEARCH 0x1000 // we only try to get the BoundingBox
+ // in the first 4096 bytes
+
+#define EPS_PREVIEW_TIFF 1
+#define EPS_PREVIEW_EPSI 2
+
+#define PS_LINESIZE 70 // maximum number of characters a line in the output
+
+// -----------------------------field-types------------------------------
+
+namespace {
+
+struct StackMember
+{
+ struct StackMember * pSucc;
+ Color aGlobalCol;
+ bool bLineCol;
+ Color aLineCol;
+ bool bFillCol;
+ Color aFillCol;
+ Color aTextCol;
+ bool bTextFillCol;
+ Color aTextFillCol;
+ Color aBackgroundCol;
+ vcl::Font aFont;
+ TextAlign eTextAlign;
+
+ double fLineWidth;
+ double fMiterLimit;
+ SvtGraphicStroke::CapType eLineCap;
+ SvtGraphicStroke::JoinType eJoinType;
+ SvtGraphicStroke::DashArray aDashArray;
+};
+
+struct PSLZWCTreeNode
+{
+
+ PSLZWCTreeNode* pBrother; // next node who has the same father
+ PSLZWCTreeNode* pFirstChild; // first son
+ sal_uInt16 nCode; // The code for the string of pixel values, which arises if... <missing comment>
+ sal_uInt16 nValue; // the pixel value
+};
+
+enum NMode {PS_NONE = 0x00, PS_SPACE = 0x01, PS_RET = 0x02, PS_WRAP = 0x04}; // formatting mode: action which is inserted behind the output
+inline NMode operator|(NMode a, NMode b)
+{
+ return static_cast<NMode>(static_cast<sal_uInt8>(a) | static_cast<sal_uInt8>(b));
+}
+
+class PSWriter
+{
+private:
+ bool mbStatus;
+ bool mbLevelWarning; // if there any embedded eps file which was not exported
+ sal_uInt32 mnLatestPush; // offset to streamposition, where last push was done
+
+ tools::Long mnLevel; // dialog options
+ bool mbGrayScale;
+ bool mbCompression;
+ sal_Int32 mnPreview;
+ sal_Int32 mnTextMode;
+
+ SvStream* mpPS;
+ const GDIMetaFile* pMTF;
+ std::unique_ptr<GDIMetaFile>
+ pAMTF; // only created if Graphics is not a Metafile
+ ScopedVclPtrInstance<VirtualDevice>
+ pVDev;
+
+ double nBoundingX2; // this represents the bounding box
+ double nBoundingY2;
+
+ StackMember* pGDIStack;
+ sal_uInt32 mnCursorPos; // current cursor position in output
+ Color aColor; // current color which is used for output
+ bool bLineColor;
+ Color aLineColor; // current GDIMetafile color settings
+ bool bFillColor;
+ Color aFillColor;
+ Color aTextColor;
+ bool bTextFillColor;
+ Color aTextFillColor;
+ Color aBackgroundColor;
+ TextAlign eTextAlign;
+
+ double fLineWidth;
+ double fMiterLimit;
+ SvtGraphicStroke::CapType eLineCap;
+ SvtGraphicStroke::JoinType eJoinType;
+ SvtGraphicStroke::DashArray aDashArray;
+
+ vcl::Font maFont;
+ vcl::Font maLastFont;
+
+ std::unique_ptr<PSLZWCTreeNode[]> pTable; // LZW compression data
+ PSLZWCTreeNode* pPrefix; // the compression is as same as the TIFF compression
+ sal_uInt16 nDataSize;
+ sal_uInt16 nClearCode;
+ sal_uInt16 nEOICode;
+ sal_uInt16 nTableSize;
+ sal_uInt16 nCodeSize;
+ sal_uInt32 nOffset;
+ sal_uInt32 dwShift;
+
+ css::uno::Reference< css::task::XStatusIndicator > xStatusIndicator;
+
+ void ImplWriteProlog( const Graphic* pPreviewEPSI );
+ void ImplWriteEpilog();
+ void ImplWriteActions( const GDIMetaFile& rMtf, VirtualDevice& rVDev );
+
+ // this method makes LF's, space inserting and word wrapping as used in all nMode
+ // parameters
+ inline void ImplExecMode( NMode nMode );
+
+ // writes char[] + LF to stream
+ inline void ImplWriteLine( const char*, NMode nMode = PS_RET );
+
+ // writes ( nNumb / 10^nCount ) in ASCII format to stream
+ void ImplWriteF( sal_Int32 nNumb, sal_uInt8 nCount = 3, NMode nMode = PS_SPACE );
+
+ // writes a double in ASCII format to stream
+ void ImplWriteDouble( double );
+
+ // writes a long in ASCII format to stream
+ void ImplWriteLong( sal_Int32 nNumb, NMode nMode = PS_SPACE );
+
+ // writes a byte in ASCII format to stream
+ void ImplWriteByte( sal_uInt8 nNumb, NMode nMode = PS_SPACE );
+
+ // writes a byte in ASCII (hex) format to stream
+ void ImplWriteHexByte( sal_uInt8 nNumb, NMode nMode = PS_WRAP );
+
+ // writes nNumb as number from 0.000 till 1.000 in ASCII format to stream
+ void ImplWriteB1( sal_uInt8 nNumb );
+
+ inline void ImplWritePoint( const Point& );
+ void ImplMoveTo( const Point& );
+ void ImplLineTo( const Point&, NMode nMode = PS_SPACE );
+ void ImplCurveTo( const Point& rP1, const Point& rP2, const Point& rP3, NMode nMode );
+ void ImplTranslate( const double& fX, const double& fY );
+ void ImplScale( const double& fX, const double& fY );
+
+ void ImplAddPath( const tools::Polygon & rPolygon );
+ void ImplWriteLineInfo( double fLineWidth, double fMiterLimit, SvtGraphicStroke::CapType eLineCap,
+ SvtGraphicStroke::JoinType eJoinType, SvtGraphicStroke::DashArray && rDashArray );
+ void ImplWriteLineInfo( const LineInfo& rLineInfo );
+ void ImplRect( const tools::Rectangle & rRectangle );
+ void ImplRectFill ( const tools::Rectangle & rRectangle );
+ void ImplWriteGradient( const tools::PolyPolygon& rPolyPoly, const Gradient& rGradient, VirtualDevice& rVDev );
+ void ImplIntersect( const tools::PolyPolygon& rPolyPoly );
+ void ImplPolyPoly( const tools::PolyPolygon & rPolyPolygon, bool bTextOutline = false );
+ void ImplPolyLine( const tools::Polygon & rPolygon );
+
+ void ImplSetClipRegion( vcl::Region const & rRegion );
+ void ImplBmp( Bitmap const *, Bitmap const *, const Point &, double nWidth, double nHeight );
+ void ImplText( const OUString& rUniString, const Point& rPos, o3tl::span<const sal_Int32> pDXArry, sal_Int32 nWidth, VirtualDevice const & rVDev );
+ void ImplSetAttrForText( const Point & rPoint );
+ void ImplWriteCharacter( char );
+ void ImplWriteString( const OString&, VirtualDevice const & rVDev, o3tl::span<const sal_Int32> pDXArry, bool bStretch );
+ void ImplDefineFont( const char*, const char* );
+
+ void ImplClosePathDraw();
+ void ImplPathDraw();
+
+ inline void ImplWriteLineColor( NMode nMode );
+ inline void ImplWriteFillColor( NMode nMode );
+ inline void ImplWriteTextColor( NMode nMode );
+ void ImplWriteColor( NMode nMode );
+
+ static double ImplGetScaling( const MapMode& );
+ void ImplGetMapMode( const MapMode& );
+ static bool ImplGetBoundingBox( double* nNumb, sal_uInt8* pSource, sal_uInt32 nSize );
+ static sal_uInt8* ImplSearchEntry( sal_uInt8* pSource, sal_uInt8 const * pDest, sal_uInt32 nComp, sal_uInt32 nSize );
+ // LZW methods
+ void StartCompression();
+ void Compress( sal_uInt8 nSrc );
+ void EndCompression();
+ inline void WriteBits( sal_uInt16 nCode, sal_uInt16 nCodeLen );
+
+public:
+ bool WritePS( const Graphic& rGraphic, SvStream& rTargetStream, FilterConfigItem* );
+ PSWriter();
+};
+
+}
+
+//========================== methods from PSWriter ==========================
+
+
+PSWriter::PSWriter()
+ : mbStatus(false)
+ , mbLevelWarning(false)
+ , mnLatestPush(0)
+ , mnLevel(0)
+ , mbGrayScale(false)
+ , mbCompression(false)
+ , mnPreview(0)
+ , mnTextMode(0)
+ , mpPS(nullptr)
+ , pMTF(nullptr)
+ , nBoundingX2(0)
+ , nBoundingY2(0)
+ , pGDIStack(nullptr)
+ , mnCursorPos(0)
+ , bLineColor(false)
+ , bFillColor(false)
+ , bTextFillColor(false)
+ , eTextAlign()
+ , fLineWidth(0)
+ , fMiterLimit(0)
+ , eLineCap()
+ , eJoinType()
+ , pPrefix(nullptr)
+ , nDataSize(0)
+ , nClearCode(0)
+ , nEOICode(0)
+ , nTableSize(0)
+ , nCodeSize(0)
+ , nOffset(0)
+ , dwShift(0)
+{
+}
+
+bool PSWriter::WritePS( const Graphic& rGraphic, SvStream& rTargetStream, FilterConfigItem* pFilterConfigItem )
+{
+ sal_uInt32 nStreamPosition = 0, nPSPosition = 0; // -Wall warning, unset, check
+
+ mbStatus = true;
+ mnPreview = 0;
+ mbLevelWarning = false;
+ mnLatestPush = 0xEFFFFFFE;
+
+ if ( pFilterConfigItem )
+ {
+ xStatusIndicator = pFilterConfigItem->GetStatusIndicator();
+ if ( xStatusIndicator.is() )
+ {
+ xStatusIndicator->start( OUString(), 100 );
+ }
+ }
+
+ mpPS = &rTargetStream;
+ mpPS->SetEndian( SvStreamEndian::LITTLE );
+
+ // default values for the dialog options
+ mnLevel = 2;
+ mbGrayScale = false;
+#ifdef UNX // don't compress by default on unix as ghostscript is unable to read LZW compressed eps
+ mbCompression = false;
+#else
+ mbCompression = true;
+#endif
+ mnTextMode = 0; // default0 : export glyph outlines
+
+ // try to get the dialog selection
+ if ( pFilterConfigItem )
+ {
+#ifdef UNX // don't put binary tiff preview ahead of postscript code by default on unix as ghostscript is unable to read it
+ mnPreview = pFilterConfigItem->ReadInt32( "Preview", 0 );
+#else
+ mnPreview = pFilterConfigItem->ReadInt32( "Preview", 1 );
+#endif
+ mnLevel = pFilterConfigItem->ReadInt32( "Version", 2 );
+ if ( mnLevel != 1 )
+ mnLevel = 2;
+ mbGrayScale = pFilterConfigItem->ReadInt32( "ColorFormat", 1 ) == 2;
+#ifdef UNX // don't compress by default on unix as ghostscript is unable to read LZW compressed eps
+ mbCompression = pFilterConfigItem->ReadInt32( "CompressionMode", 0 ) != 0;
+#else
+ mbCompression = pFilterConfigItem->ReadInt32( "CompressionMode", 1 ) == 1;
+#endif
+ mnTextMode = pFilterConfigItem->ReadInt32( "TextMode", 0 );
+ if ( mnTextMode > 2 )
+ mnTextMode = 0;
+ }
+
+ // compression is not available for Level 1
+ if ( mnLevel == 1 )
+ {
+ mbGrayScale = true;
+ mbCompression = false;
+ }
+
+ if ( mnPreview & EPS_PREVIEW_TIFF )
+ {
+ rTargetStream.WriteUInt32( 0xC6D3D0C5 );
+ nStreamPosition = rTargetStream.Tell();
+ rTargetStream.WriteUInt32( 0 ).WriteUInt32( 0 ).WriteUInt32( 0 ).WriteUInt32( 0 )
+ .WriteUInt32( nStreamPosition + 26 ).WriteUInt32( 0 ).WriteUInt16( 0xffff );
+
+ ErrCode nErrCode;
+ if ( mbGrayScale )
+ {
+ BitmapEx aTempBitmapEx( rGraphic.GetBitmapEx() );
+ aTempBitmapEx.Convert( BmpConversion::N8BitGreys );
+ nErrCode = GraphicConverter::Export( rTargetStream, aTempBitmapEx, ConvertDataFormat::TIF ) ;
+ }
+ else
+ nErrCode = GraphicConverter::Export( rTargetStream, rGraphic, ConvertDataFormat::TIF ) ;
+
+ if ( nErrCode == ERRCODE_NONE )
+ {
+ nPSPosition = rTargetStream.TellEnd();
+ rTargetStream.Seek( nStreamPosition + 20 );
+ rTargetStream.WriteUInt32( nPSPosition - 30 ); // size of tiff gfx
+ rTargetStream.Seek( nPSPosition );
+ }
+ else
+ {
+ mnPreview &=~ EPS_PREVIEW_TIFF;
+ rTargetStream.Seek( nStreamPosition - 4 );
+ }
+ }
+
+ // global default value setting
+ StackMember* pGS;
+
+ if (rGraphic.GetType() == GraphicType::GdiMetafile)
+ pMTF = &rGraphic.GetGDIMetaFile();
+ else if (rGraphic.GetGDIMetaFile().GetActionSize())
+ {
+ pAMTF.reset( new GDIMetaFile( rGraphic.GetGDIMetaFile() ) );
+ pMTF = pAMTF.get();
+ }
+ else
+ {
+ BitmapEx aBmp( rGraphic.GetBitmapEx() );
+ pAMTF.reset( new GDIMetaFile );
+ ScopedVclPtrInstance< VirtualDevice > pTmpVDev;
+ pAMTF->Record( pTmpVDev );
+ pTmpVDev->DrawBitmapEx( Point(), aBmp );
+ pAMTF->Stop();
+ pAMTF->SetPrefSize( aBmp.GetSizePixel() );
+ pMTF = pAMTF.get();
+ }
+ pVDev->SetMapMode( pMTF->GetPrefMapMode() );
+ nBoundingX2 = pMTF->GetPrefSize().Width();
+ nBoundingY2 = pMTF->GetPrefSize().Height();
+
+ pGDIStack = nullptr;
+ aColor = COL_TRANSPARENT;
+ bLineColor = true;
+ aLineColor = COL_BLACK;
+ bFillColor = true;
+ aFillColor = COL_WHITE;
+ bTextFillColor = true;
+ aTextFillColor = COL_BLACK;
+ fLineWidth = 1;
+ fMiterLimit = 15; // use same limit as most graphic systems and basegfx
+ eLineCap = SvtGraphicStroke::capButt;
+ eJoinType = SvtGraphicStroke::joinMiter;
+ aBackgroundColor = COL_WHITE;
+ eTextAlign = ALIGN_BASELINE;
+
+ if( pMTF->GetActionSize() )
+ {
+ ImplWriteProlog( ( mnPreview & EPS_PREVIEW_EPSI ) ? &rGraphic : nullptr );
+ mnCursorPos = 0;
+ ImplWriteActions( *pMTF, *pVDev );
+ ImplWriteEpilog();
+ if ( mnPreview & EPS_PREVIEW_TIFF )
+ {
+ sal_uInt32 nPosition = rTargetStream.Tell();
+ rTargetStream.Seek( nStreamPosition );
+ rTargetStream.WriteUInt32( nPSPosition );
+ rTargetStream.WriteUInt32( nPosition - nPSPosition );
+ rTargetStream.Seek( nPosition );
+ }
+ while( pGDIStack )
+ {
+ pGS=pGDIStack;
+ pGDIStack=pGS->pSucc;
+ delete pGS;
+ }
+ }
+ else
+ mbStatus = false;
+
+ if ( mbStatus && mbLevelWarning && pFilterConfigItem )
+ {
+ std::locale loc = Translate::Create("flt");
+ std::unique_ptr<weld::MessageDialog> xInfoBox(Application::CreateMessageDialog(nullptr,
+ VclMessageType::Info, VclButtonsType::Ok,
+ Translate::get(KEY_VERSION_CHECK, loc)));
+ xInfoBox->run();
+ }
+
+ if ( xStatusIndicator.is() )
+ xStatusIndicator->end();
+
+ return mbStatus;
+}
+
+void PSWriter::ImplWriteProlog( const Graphic* pPreview )
+{
+ ImplWriteLine( "%!PS-Adobe-3.0 EPSF-3.0 " );
+ mpPS->WriteCharPtr( "%%BoundingBox: " ); // BoundingBox
+ ImplWriteLong( 0 );
+ ImplWriteLong( 0 );
+ Size aSizePoint = OutputDevice::LogicToLogic( pMTF->GetPrefSize(),
+ pMTF->GetPrefMapMode(), MapMode(MapUnit::MapPoint));
+ ImplWriteLong( aSizePoint.Width() );
+ ImplWriteLong( aSizePoint.Height() ,PS_RET );
+ ImplWriteLine( "%%Pages: 0" );
+ OUString aCreator;
+ OUString aCreatorOverride = officecfg::Office::Common::Save::Document::GeneratorOverride::get();
+ if( !aCreatorOverride.isEmpty())
+ aCreator = aCreatorOverride;
+ else
+ aCreator = "%%Creator: " + utl::ConfigManager::getProductName() + " " +
+ utl::ConfigManager::getProductVersion();
+ ImplWriteLine( OUStringToOString( aCreator, RTL_TEXTENCODING_UTF8 ).getStr() );
+ ImplWriteLine( "%%Title: none" );
+ ImplWriteLine( "%%CreationDate: none" );
+
+// defaults
+
+ mpPS->WriteCharPtr( "%%LanguageLevel: " ); // Language level
+ ImplWriteLong( mnLevel, PS_RET );
+ if ( !mbGrayScale && mnLevel == 1 )
+ ImplWriteLine( "%%Extensions: CMYK" ); // CMYK extension is to set in color mode in level 1
+ ImplWriteLine( "%%EndComments" );
+ if ( pPreview && aSizePoint.Width() && aSizePoint.Height() )
+ {
+ Size aSizeBitmap( ( aSizePoint.Width() + 7 ) & ~7, aSizePoint.Height() );
+ Bitmap aTmpBitmap( pPreview->GetBitmapEx().GetBitmap() );
+ aTmpBitmap.Scale( aSizeBitmap, BmpScaleFlag::BestQuality );
+ aTmpBitmap.Convert( BmpConversion::N1BitThreshold );
+ BitmapReadAccess* pAcc = aTmpBitmap.AcquireReadAccess();
+ if ( pAcc )
+ {
+ mpPS->WriteCharPtr( "%%BeginPreview: " ); // BoundingBox
+ ImplWriteLong( aSizeBitmap.Width() );
+ ImplWriteLong( aSizeBitmap.Height() );
+ mpPS->WriteCharPtr( "1 " );
+ sal_Int32 nLines = aSizeBitmap.Width() / 312;
+ if ( ( nLines * 312 ) != aSizeBitmap.Width() )
+ nLines++;
+ nLines *= aSizeBitmap.Height();
+ ImplWriteLong( nLines );
+ sal_Int32 nCount2, nCount = 4;
+ const BitmapColor aBlack( pAcc->GetBestMatchingColor( COL_BLACK ) );
+ for ( tools::Long nY = 0; nY < aSizeBitmap.Height(); nY++ )
+ {
+ nCount2 = 0;
+ char nVal = 0;
+ Scanline pScanline = pAcc->GetScanline( nY );
+ for ( tools::Long nX = 0; nX < aSizeBitmap.Width(); nX++ )
+ {
+ if ( !nCount2 )
+ {
+ ImplExecMode( PS_RET );
+ mpPS->WriteCharPtr( "%" );
+ nCount2 = 312;
+ }
+ nVal <<= 1;
+ if ( pAcc->GetPixelFromData( pScanline, nX ) == aBlack )
+ nVal |= 1;
+ if ( ! ( --nCount ) )
+ {
+ if ( nVal > 9 )
+ nVal += 'A' - 10;
+ else
+ nVal += '0';
+ mpPS->WriteChar( nVal );
+ nVal = 0;
+ nCount += 4;
+ }
+ nCount2--;
+ }
+ }
+ Bitmap::ReleaseAccess( pAcc );
+ ImplExecMode( PS_RET );
+ ImplWriteLine( "%%EndPreview" );
+ }
+ }
+ ImplWriteLine( "%%BeginProlog" );
+ ImplWriteLine( "%%BeginResource: procset SDRes-Prolog 1.0 0" );
+
+// BEGIN EPSF
+ ImplWriteLine( "/b4_inc_state save def\n/dict_count countdictstack def\n/op_count count 1 sub def\nuserdict begin" );
+ ImplWriteLine( "0 setgray 0 setlinecap 1 setlinewidth 0 setlinejoin 10 setmiterlimit[] 0 setdash newpath" );
+ ImplWriteLine( "/languagelevel where {pop languagelevel 1 ne {false setstrokeadjust false setoverprint} if} if" );
+
+ ImplWriteLine( "/bdef {bind def} bind def" ); // the new operator bdef is created
+ if ( mbGrayScale )
+ ImplWriteLine( "/c {setgray} bdef" );
+ else
+ ImplWriteLine( "/c {setrgbcolor} bdef" );
+ ImplWriteLine( "/l {neg lineto} bdef" );
+ ImplWriteLine( "/rl {neg rlineto} bdef" );
+ ImplWriteLine( "/lc {setlinecap} bdef" );
+ ImplWriteLine( "/lj {setlinejoin} bdef" );
+ ImplWriteLine( "/lw {setlinewidth} bdef" );
+ ImplWriteLine( "/ml {setmiterlimit} bdef" );
+ ImplWriteLine( "/ld {setdash} bdef" );
+ ImplWriteLine( "/m {neg moveto} bdef" );
+ ImplWriteLine( "/ct {6 2 roll neg 6 2 roll neg 6 2 roll neg curveto} bdef" );
+ ImplWriteLine( "/r {rotate} bdef" );
+ ImplWriteLine( "/t {neg translate} bdef" );
+ ImplWriteLine( "/s {scale} bdef" );
+ ImplWriteLine( "/sw {show} bdef" );
+ ImplWriteLine( "/gs {gsave} bdef" );
+ ImplWriteLine( "/gr {grestore} bdef" );
+
+ ImplWriteLine( "/f {findfont dup length dict begin" ); // Setfont
+ ImplWriteLine( "{1 index /FID ne {def} {pop pop} ifelse} forall /Encoding ISOLatin1Encoding def" );
+ ImplWriteLine( "currentdict end /NFont exch definefont pop /NFont findfont} bdef" );
+
+ ImplWriteLine( "/p {closepath} bdef" );
+ ImplWriteLine( "/sf {scalefont setfont} bdef" );
+
+ ImplWriteLine( "/ef {eofill}bdef" ); // close path and fill
+ ImplWriteLine( "/pc {closepath stroke}bdef" ); // close path and draw
+ ImplWriteLine( "/ps {stroke}bdef" ); // draw current path
+ ImplWriteLine( "/pum {matrix currentmatrix}bdef" ); // pushes the current matrix
+ ImplWriteLine( "/pom {setmatrix}bdef" ); // pops the matrix
+ ImplWriteLine( "/bs {/aString exch def /nXOfs exch def /nWidth exch def currentpoint nXOfs 0 rmoveto pum nWidth aString stringwidth pop div 1 scale aString show pom moveto} bdef" );
+ ImplWriteLine( "%%EndResource" );
+ ImplWriteLine( "%%EndProlog" );
+ ImplWriteLine( "%%BeginSetup" );
+ ImplWriteLine( "%%EndSetup" );
+ ImplWriteLine( "%%Page: 1 1" );
+ ImplWriteLine( "%%BeginPageSetup" );
+ ImplWriteLine( "%%EndPageSetup" );
+ ImplWriteLine( "pum" );
+ ImplScale( static_cast<double>(aSizePoint.Width()) / static_cast<double>(pMTF->GetPrefSize().Width()), static_cast<double>(aSizePoint.Height()) / static_cast<double>(pMTF->GetPrefSize().Height()) );
+ ImplWriteDouble( 0 );
+ ImplWriteDouble( -pMTF->GetPrefSize().Height() );
+ ImplWriteLine( "t" );
+ ImplWriteLine( "/tm matrix currentmatrix def" );
+}
+
+void PSWriter::ImplWriteEpilog()
+{
+ ImplTranslate( 0, nBoundingY2 );
+ ImplWriteLine( "pom" );
+ ImplWriteLine( "count op_count sub {pop} repeat countdictstack dict_count sub {end} repeat b4_inc_state restore" );
+
+ ImplWriteLine( "%%PageTrailer" );
+ ImplWriteLine( "%%Trailer" );
+
+ ImplWriteLine( "%%EOF" );
+}
+
+void PSWriter::ImplWriteActions( const GDIMetaFile& rMtf, VirtualDevice& rVDev )
+{
+ tools::PolyPolygon aFillPath;
+
+ for( size_t nCurAction = 0, nCount = rMtf.GetActionSize(); nCurAction < nCount; nCurAction++ )
+ {
+ MetaAction* pMA = rMtf.GetAction( nCurAction );
+
+ switch( pMA->GetType() )
+ {
+ case MetaActionType::NONE :
+ break;
+
+ case MetaActionType::PIXEL :
+ {
+ Color aOldLineColor( aLineColor );
+ aLineColor = static_cast<const MetaPixelAction*>(pMA)->GetColor();
+ ImplWriteLineColor( PS_SPACE );
+ ImplMoveTo( static_cast<const MetaPixelAction*>(pMA)->GetPoint() );
+ ImplLineTo( static_cast<const MetaPixelAction*>(pMA)->GetPoint() );
+ ImplPathDraw();
+ aLineColor = aOldLineColor;
+ }
+ break;
+
+ case MetaActionType::POINT :
+ {
+ ImplWriteLineColor( PS_SPACE );
+ ImplMoveTo( static_cast<const MetaPointAction*>(pMA)->GetPoint() );
+ ImplLineTo( static_cast<const MetaPointAction*>(pMA)->GetPoint() );
+ ImplPathDraw();
+ }
+ break;
+
+ case MetaActionType::LINE :
+ {
+ const LineInfo& rLineInfo = static_cast<const MetaLineAction*>(pMA)->GetLineInfo();
+ ImplWriteLineInfo( rLineInfo );
+ if ( bLineColor )
+ {
+ ImplWriteLineColor( PS_SPACE );
+ ImplMoveTo( static_cast<const MetaLineAction*>(pMA)->GetStartPoint() );
+ ImplLineTo( static_cast<const MetaLineAction*>(pMA )->GetEndPoint() );
+ ImplPathDraw();
+ }
+ }
+ break;
+
+ case MetaActionType::RECT :
+ {
+ ImplRect( static_cast<const MetaRectAction*>(pMA)->GetRect() );
+ }
+ break;
+
+ case MetaActionType::ROUNDRECT :
+ ImplRect( static_cast<const MetaRoundRectAction*>(pMA)->GetRect() );
+ break;
+
+ case MetaActionType::ELLIPSE :
+ {
+ tools::Rectangle aRect = static_cast<const MetaEllipseAction*>(pMA)->GetRect();
+ Point aCenter = aRect.Center();
+ tools::Polygon aPoly( aCenter, aRect.GetWidth() / 2, aRect.GetHeight() / 2 );
+ tools::PolyPolygon aPolyPoly( aPoly );
+ ImplPolyPoly( aPolyPoly );
+ }
+ break;
+
+ case MetaActionType::ARC :
+ {
+ tools::Polygon aPoly( static_cast<const MetaArcAction*>(pMA)->GetRect(), static_cast<const MetaArcAction*>(pMA)->GetStartPoint(),
+ static_cast<const MetaArcAction*>(pMA)->GetEndPoint(), PolyStyle::Arc );
+ tools::PolyPolygon aPolyPoly( aPoly );
+ ImplPolyPoly( aPolyPoly );
+ }
+ break;
+
+ case MetaActionType::PIE :
+ {
+ tools::Polygon aPoly( static_cast<const MetaPieAction*>(pMA)->GetRect(), static_cast<const MetaPieAction*>(pMA)->GetStartPoint(),
+ static_cast<const MetaPieAction*>(pMA)->GetEndPoint(), PolyStyle::Pie );
+ tools::PolyPolygon aPolyPoly( aPoly );
+ ImplPolyPoly( aPolyPoly );
+ }
+ break;
+
+ case MetaActionType::CHORD :
+ {
+ tools::Polygon aPoly( static_cast<const MetaChordAction*>(pMA)->GetRect(), static_cast<const MetaChordAction*>(pMA)->GetStartPoint(),
+ static_cast<const MetaChordAction*>(pMA)->GetEndPoint(), PolyStyle::Chord );
+ tools::PolyPolygon aPolyPoly( aPoly );
+ ImplPolyPoly( aPolyPoly );
+ }
+ break;
+
+ case MetaActionType::POLYLINE :
+ {
+ tools::Polygon aPoly( static_cast<const MetaPolyLineAction*>(pMA)->GetPolygon() );
+ const LineInfo& rLineInfo = static_cast<const MetaPolyLineAction*>(pMA)->GetLineInfo();
+ ImplWriteLineInfo( rLineInfo );
+
+ if(basegfx::B2DLineJoin::NONE == rLineInfo.GetLineJoin()
+ && rLineInfo.GetWidth() > 1)
+ {
+ // emulate B2DLineJoin::NONE by creating single edges
+ const sal_uInt16 nPoints(aPoly.GetSize());
+ const bool bCurve(aPoly.HasFlags());
+
+ for(sal_uInt16 a(0); a + 1 < nPoints; a++)
+ {
+ if(bCurve
+ && PolyFlags::Normal != aPoly.GetFlags(a + 1)
+ && a + 2 < nPoints
+ && PolyFlags::Normal != aPoly.GetFlags(a + 2)
+ && a + 3 < nPoints)
+ {
+ const tools::Polygon aSnippet(4,
+ aPoly.GetConstPointAry() + a,
+ aPoly.GetConstFlagAry() + a);
+ ImplPolyLine(aSnippet);
+ a += 2;
+ }
+ else
+ {
+ const tools::Polygon aSnippet(2,
+ aPoly.GetConstPointAry() + a);
+ ImplPolyLine(aSnippet);
+ }
+ }
+ }
+ else
+ {
+ ImplPolyLine( aPoly );
+ }
+ }
+ break;
+
+ case MetaActionType::POLYGON :
+ {
+ tools::PolyPolygon aPolyPoly( static_cast<const MetaPolygonAction*>(pMA)->GetPolygon() );
+ ImplPolyPoly( aPolyPoly );
+ }
+ break;
+
+ case MetaActionType::POLYPOLYGON :
+ {
+ ImplPolyPoly( static_cast<const MetaPolyPolygonAction*>(pMA)->GetPolyPolygon() );
+ }
+ break;
+
+ case MetaActionType::TEXT:
+ {
+ const MetaTextAction * pA = static_cast<const MetaTextAction*>(pMA);
+
+ OUString aUniStr = pA->GetText().copy( pA->GetIndex(), pA->GetLen() );
+ Point aPoint( pA->GetPoint() );
+
+ ImplText( aUniStr, aPoint, {}, 0, rVDev );
+ }
+ break;
+
+ case MetaActionType::TEXTRECT:
+ {
+ OSL_FAIL( "Unsupported action: TextRect...Action!" );
+ }
+ break;
+
+ case MetaActionType::STRETCHTEXT :
+ {
+ const MetaStretchTextAction* pA = static_cast<const MetaStretchTextAction*>(pMA);
+ OUString aUniStr = pA->GetText().copy( pA->GetIndex(), pA->GetLen() );
+ Point aPoint( pA->GetPoint() );
+
+ ImplText( aUniStr, aPoint, {}, pA->GetWidth(), rVDev );
+ }
+ break;
+
+ case MetaActionType::TEXTARRAY:
+ {
+ const MetaTextArrayAction* pA = static_cast<const MetaTextArrayAction*>(pMA);
+ OUString aUniStr = pA->GetText().copy( pA->GetIndex(), pA->GetLen() );
+ Point aPoint( pA->GetPoint() );
+
+ ImplText( aUniStr, aPoint, pA->GetDXArray(), 0, rVDev );
+ }
+ break;
+
+ case MetaActionType::BMP :
+ {
+ Bitmap aBitmap = static_cast<const MetaBmpAction*>(pMA)->GetBitmap();
+ if ( mbGrayScale )
+ aBitmap.Convert( BmpConversion::N8BitGreys );
+ Point aPoint = static_cast<const MetaBmpAction*>(pMA)->GetPoint();
+ Size aSize( rVDev.PixelToLogic( aBitmap.GetSizePixel() ) );
+ ImplBmp( &aBitmap, nullptr, aPoint, aSize.Width(), aSize.Height() );
+ }
+ break;
+
+ case MetaActionType::BMPSCALE :
+ {
+ Bitmap aBitmap = static_cast<const MetaBmpScaleAction*>(pMA)->GetBitmap();
+ if ( mbGrayScale )
+ aBitmap.Convert( BmpConversion::N8BitGreys );
+ Point aPoint = static_cast<const MetaBmpScaleAction*>(pMA)->GetPoint();
+ Size aSize = static_cast<const MetaBmpScaleAction*>(pMA)->GetSize();
+ ImplBmp( &aBitmap, nullptr, aPoint, aSize.Width(), aSize.Height() );
+ }
+ break;
+
+ case MetaActionType::BMPSCALEPART :
+ {
+ Bitmap aBitmap( static_cast<const MetaBmpScalePartAction*>(pMA)->GetBitmap() );
+ aBitmap.Crop( tools::Rectangle( static_cast<const MetaBmpScalePartAction*>(pMA)->GetSrcPoint(),
+ static_cast<const MetaBmpScalePartAction*>(pMA)->GetSrcSize() ) );
+ if ( mbGrayScale )
+ aBitmap.Convert( BmpConversion::N8BitGreys );
+ Point aPoint = static_cast<const MetaBmpScalePartAction*>(pMA)->GetDestPoint();
+ Size aSize = static_cast<const MetaBmpScalePartAction*>(pMA)->GetDestSize();
+ ImplBmp( &aBitmap, nullptr, aPoint, aSize.Width(), aSize.Height() );
+ }
+ break;
+
+ case MetaActionType::BMPEX :
+ {
+ BitmapEx aBitmapEx( static_cast<MetaBmpExAction*>(pMA)->GetBitmapEx() );
+ Bitmap aBitmap( aBitmapEx.GetBitmap() );
+ if ( mbGrayScale )
+ aBitmap.Convert( BmpConversion::N8BitGreys );
+ Bitmap aMask( aBitmapEx.GetAlpha() );
+ Point aPoint( static_cast<const MetaBmpExAction*>(pMA)->GetPoint() );
+ Size aSize( rVDev.PixelToLogic( aBitmap.GetSizePixel() ) );
+ ImplBmp( &aBitmap, &aMask, aPoint, aSize.Width(), aSize.Height() );
+ }
+ break;
+
+ case MetaActionType::BMPEXSCALE :
+ {
+ BitmapEx aBitmapEx( static_cast<MetaBmpExScaleAction*>(pMA)->GetBitmapEx() );
+ Bitmap aBitmap( aBitmapEx.GetBitmap() );
+ if ( mbGrayScale )
+ aBitmap.Convert( BmpConversion::N8BitGreys );
+ Bitmap aMask( aBitmapEx.GetAlpha() );
+ Point aPoint = static_cast<const MetaBmpExScaleAction*>(pMA)->GetPoint();
+ Size aSize( static_cast<const MetaBmpExScaleAction*>(pMA)->GetSize() );
+ ImplBmp( &aBitmap, &aMask, aPoint, aSize.Width(), aSize.Height() );
+ }
+ break;
+
+ case MetaActionType::BMPEXSCALEPART :
+ {
+ BitmapEx aBitmapEx( static_cast<const MetaBmpExScalePartAction*>(pMA)->GetBitmapEx() );
+ aBitmapEx.Crop( tools::Rectangle( static_cast<const MetaBmpExScalePartAction*>(pMA)->GetSrcPoint(),
+ static_cast<const MetaBmpExScalePartAction*>(pMA)->GetSrcSize() ) );
+ Bitmap aBitmap( aBitmapEx.GetBitmap() );
+ if ( mbGrayScale )
+ aBitmap.Convert( BmpConversion::N8BitGreys );
+ Bitmap aMask( aBitmapEx.GetAlpha() );
+ Point aPoint = static_cast<const MetaBmpExScalePartAction*>(pMA)->GetDestPoint();
+ Size aSize = static_cast<const MetaBmpExScalePartAction*>(pMA)->GetDestSize();
+ ImplBmp( &aBitmap, &aMask, aPoint, aSize.Width(), aSize.Height() );
+ }
+ break;
+
+ // Unsupported Actions
+ case MetaActionType::MASK:
+ case MetaActionType::MASKSCALE:
+ case MetaActionType::MASKSCALEPART:
+ {
+ OSL_FAIL( "Unsupported action: MetaMask...Action!" );
+ }
+ break;
+
+ case MetaActionType::GRADIENT :
+ {
+ tools::PolyPolygon aPolyPoly( static_cast<const MetaGradientAction*>(pMA)->GetRect() );
+ ImplWriteGradient( aPolyPoly, static_cast<const MetaGradientAction*>(pMA)->GetGradient(), rVDev );
+ }
+ break;
+
+ case MetaActionType::GRADIENTEX :
+ {
+ tools::PolyPolygon aPolyPoly( static_cast<const MetaGradientExAction*>(pMA)->GetPolyPolygon() );
+ ImplWriteGradient( aPolyPoly, static_cast<const MetaGradientExAction*>(pMA)->GetGradient(), rVDev );
+ }
+ break;
+
+ case MetaActionType::HATCH :
+ {
+ ScopedVclPtrInstance< VirtualDevice > l_pVirDev;
+ GDIMetaFile aTmpMtf;
+
+ l_pVirDev->SetMapMode( rVDev.GetMapMode() );
+ l_pVirDev->AddHatchActions( static_cast<const MetaHatchAction*>(pMA)->GetPolyPolygon(),
+ static_cast<const MetaHatchAction*>(pMA)->GetHatch(), aTmpMtf );
+ ImplWriteActions( aTmpMtf, rVDev );
+ }
+ break;
+
+ case MetaActionType::WALLPAPER :
+ {
+ const MetaWallpaperAction* pA = static_cast<const MetaWallpaperAction*>(pMA);
+ tools::Rectangle aRect = pA->GetRect();
+ const Wallpaper& aWallpaper = pA->GetWallpaper();
+
+ if ( aWallpaper.IsBitmap() )
+ {
+ BitmapEx aBitmapEx = aWallpaper.GetBitmap();
+ Bitmap aBitmap( aBitmapEx.GetBitmap() );
+ if ( aBitmapEx.IsAlpha() )
+ {
+ if ( aWallpaper.IsGradient() )
+ {
+
+ // gradient action
+
+ }
+ Bitmap aMask( aBitmapEx.GetAlpha() );
+ ImplBmp( &aBitmap, &aMask, Point( aRect.Left(), aRect.Top() ), aRect.GetWidth(), aRect.GetHeight() );
+ }
+ else
+ ImplBmp( &aBitmap, nullptr, Point( aRect.Left(), aRect.Top() ), aRect.GetWidth(), aRect.GetHeight() );
+
+ // wallpaper Style
+
+ }
+ else if ( aWallpaper.IsGradient() )
+ {
+
+ // gradient action
+
+ }
+ else
+ {
+ aColor = aWallpaper.GetColor();
+ ImplRectFill( aRect );
+ }
+ }
+ break;
+
+ case MetaActionType::ISECTRECTCLIPREGION:
+ {
+ const MetaISectRectClipRegionAction* pA = static_cast<const MetaISectRectClipRegionAction*>(pMA);
+ vcl::Region aRegion( pA->GetRect() );
+ ImplSetClipRegion( aRegion );
+ }
+ break;
+
+ case MetaActionType::CLIPREGION:
+ {
+ const MetaClipRegionAction* pA = static_cast<const MetaClipRegionAction*>(pMA);
+ const vcl::Region& aRegion( pA->GetRegion() );
+ ImplSetClipRegion( aRegion );
+ }
+ break;
+
+ case MetaActionType::ISECTREGIONCLIPREGION:
+ {
+ const MetaISectRegionClipRegionAction* pA = static_cast<const MetaISectRegionClipRegionAction*>(pMA);
+ const vcl::Region& aRegion( pA->GetRegion() );
+ ImplSetClipRegion( aRegion );
+ }
+ break;
+
+ case MetaActionType::MOVECLIPREGION:
+ {
+ // TODO: Implement!
+ }
+ break;
+
+ case MetaActionType::LINECOLOR :
+ {
+ if ( static_cast<const MetaLineColorAction*>(pMA)->IsSetting() )
+ {
+ bLineColor = true;
+ aLineColor = static_cast<const MetaLineColorAction*>(pMA)->GetColor();
+ }
+ else
+ bLineColor = false;
+ }
+ break;
+
+ case MetaActionType::FILLCOLOR :
+ {
+ if ( static_cast<const MetaFillColorAction*>(pMA)->IsSetting() )
+ {
+ bFillColor = true;
+ aFillColor = static_cast<const MetaFillColorAction*>(pMA)->GetColor();
+ }
+ else
+ bFillColor = false;
+ }
+ break;
+
+ case MetaActionType::TEXTCOLOR :
+ {
+ aTextColor = static_cast<const MetaTextColorAction*>(pMA)->GetColor();
+ }
+ break;
+
+ case MetaActionType::TEXTFILLCOLOR :
+ {
+ if ( static_cast<const MetaTextFillColorAction*>(pMA)->IsSetting() )
+ {
+ bTextFillColor = true;
+ aTextFillColor = static_cast<const MetaTextFillColorAction*>(pMA)->GetColor();
+ }
+ else
+ bTextFillColor = false;
+ }
+ break;
+
+ case MetaActionType::TEXTALIGN :
+ {
+ eTextAlign = static_cast<const MetaTextAlignAction*>(pMA)->GetTextAlign();
+ }
+ break;
+
+ case MetaActionType::MAPMODE :
+ {
+ pMA->Execute( &rVDev );
+ ImplGetMapMode( rVDev.GetMapMode() );
+ }
+ break;
+
+ case MetaActionType::FONT :
+ {
+ maFont = static_cast<const MetaFontAction*>(pMA)->GetFont();
+ rVDev.SetFont( maFont );
+ }
+ break;
+
+ case MetaActionType::PUSH :
+ {
+ rVDev.Push(static_cast<const MetaPushAction*>(pMA)->GetFlags() );
+ StackMember* pGS = new StackMember;
+ pGS->pSucc = pGDIStack;
+ pGDIStack = pGS;
+ pGS->aDashArray = aDashArray;
+ pGS->eJoinType = eJoinType;
+ pGS->eLineCap = eLineCap;
+ pGS->fLineWidth = fLineWidth;
+ pGS->fMiterLimit = fMiterLimit;
+ pGS->eTextAlign = eTextAlign;
+ pGS->aGlobalCol = aColor;
+ pGS->bLineCol = bLineColor;
+ pGS->aLineCol = aLineColor;
+ pGS->bFillCol = bFillColor;
+ pGS->aFillCol = aFillColor;
+ pGS->aTextCol = aTextColor;
+ pGS->bTextFillCol = bTextFillColor;
+ pGS->aTextFillCol = aTextFillColor;
+ pGS->aBackgroundCol = aBackgroundColor;
+ pGS->aFont = maFont;
+ mnLatestPush = mpPS->Tell();
+ ImplWriteLine( "gs" );
+ }
+ break;
+
+ case MetaActionType::POP :
+ {
+ rVDev.Pop();
+ if( pGDIStack )
+ {
+ StackMember* pGS = pGDIStack;
+ pGDIStack = pGS->pSucc;
+ aDashArray = pGS->aDashArray;
+ eJoinType = pGS->eJoinType;
+ eLineCap = pGS->eLineCap;
+ fLineWidth = pGS->fLineWidth;
+ fMiterLimit = pGS->fMiterLimit;
+ eTextAlign = pGS->eTextAlign;
+ aColor = pGS->aGlobalCol;
+ bLineColor = pGS->bLineCol;
+ aLineColor = pGS->aLineCol;
+ bFillColor = pGS->bFillCol;
+ aFillColor = pGS->aFillCol;
+ aTextColor = pGS->aTextCol;
+ bTextFillColor = pGS->bTextFillCol;
+ aTextFillColor = pGS->aTextFillCol;
+ aBackgroundColor = pGS->aBackgroundCol;
+ maFont = pGS->aFont;
+ maLastFont = vcl::Font(); // set maLastFont != maFont -> so that
+ delete pGS;
+ sal_uInt32 nCurrentPos = mpPS->Tell();
+ if ( nCurrentPos - 3 == mnLatestPush )
+ {
+ mpPS->Seek( mnLatestPush );
+ ImplWriteLine( " " );
+ mpPS->Seek( mnLatestPush );
+ }
+ else
+ ImplWriteLine( "gr" );
+ }
+ }
+ break;
+
+ case MetaActionType::EPS :
+ {
+ GfxLink aGfxLink = static_cast<const MetaEPSAction*>(pMA)->GetLink();
+ const GDIMetaFile aSubstitute( static_cast<const MetaEPSAction*>(pMA)->GetSubstitute() );
+
+ bool bLevelConflict = false;
+ sal_uInt8* pSource = const_cast<sal_uInt8*>(aGfxLink.GetData());
+ sal_uInt32 nSize = aGfxLink.GetDataSize();
+ sal_uInt32 nParseThis = POSTSCRIPT_BOUNDINGSEARCH;
+ if ( nSize < 64 ) // assuming eps is larger than 64 bytes
+ pSource = nullptr;
+ if ( nParseThis > nSize )
+ nParseThis = nSize;
+
+ if ( pSource && ( mnLevel == 1 ) )
+ {
+ sal_uInt8* pFound = ImplSearchEntry( pSource, reinterpret_cast<sal_uInt8 const *>("%%LanguageLevel:"), nParseThis - 10, 16 );
+ if ( pFound )
+ {
+ sal_uInt8 k, i = 10;
+ pFound += 16;
+ while ( --i )
+ {
+ k = *pFound++;
+ if ( ( k > '0' ) && ( k <= '9' ) )
+ {
+ if ( k != '1' )
+ {
+ bLevelConflict = true;
+ mbLevelWarning = true;
+ }
+ break;
+ }
+ }
+ }
+ }
+ if ( !bLevelConflict )
+ {
+ double nBoundingBox[4];
+ if ( pSource && ImplGetBoundingBox( nBoundingBox, pSource, nParseThis ) )
+ {
+ Point aPoint = static_cast<const MetaEPSAction*>(pMA)->GetPoint();
+ Size aSize = static_cast<const MetaEPSAction*>(pMA)->GetSize();
+
+ MapMode aMapMode( aSubstitute.GetPrefMapMode() );
+ Size aOutSize( OutputDevice::LogicToLogic( aSize, rVDev.GetMapMode(), aMapMode ) );
+ Point aOrigin( OutputDevice::LogicToLogic( aPoint, rVDev.GetMapMode(), aMapMode ) );
+ aOrigin.AdjustY(aOutSize.Height() );
+ aMapMode.SetOrigin( aOrigin );
+ aMapMode.SetScaleX( Fraction(aOutSize.Width() / ( nBoundingBox[ 2 ] - nBoundingBox[ 0 ] )) );
+ aMapMode.SetScaleY( Fraction(aOutSize.Height() / ( nBoundingBox[ 3 ] - nBoundingBox[ 1 ] )) );
+ ImplWriteLine( "gs" );
+ ImplGetMapMode( aMapMode );
+ ImplWriteLine( "%%BeginDocument:" );
+ mpPS->WriteBytes(pSource, aGfxLink.GetDataSize());
+ ImplWriteLine( "%%EndDocument\ngr" );
+ }
+ }
+ }
+ break;
+
+ case MetaActionType::Transparent:
+ {
+ // TODO: implement!
+ }
+ break;
+
+ case MetaActionType::RASTEROP:
+ {
+ pMA->Execute( &rVDev );
+ }
+ break;
+
+ case MetaActionType::FLOATTRANSPARENT:
+ {
+ const MetaFloatTransparentAction* pA = static_cast<const MetaFloatTransparentAction*>(pMA);
+
+ GDIMetaFile aTmpMtf( pA->GetGDIMetaFile() );
+ Point aSrcPt( aTmpMtf.GetPrefMapMode().GetOrigin() );
+ const Size aSrcSize( aTmpMtf.GetPrefSize() );
+ const Point aDestPt( pA->GetPoint() );
+ const Size aDestSize( pA->GetSize() );
+ const double fScaleX = aSrcSize.Width() ? static_cast<double>(aDestSize.Width()) / aSrcSize.Width() : 1.0;
+ const double fScaleY = aSrcSize.Height() ? static_cast<double>(aDestSize.Height()) / aSrcSize.Height() : 1.0;
+ tools::Long nMoveX, nMoveY;
+
+ if( fScaleX != 1.0 || fScaleY != 1.0 )
+ {
+ aTmpMtf.Scale( fScaleX, fScaleY );
+ aSrcPt.setX( FRound( aSrcPt.X() * fScaleX ) );
+ aSrcPt.setY( FRound( aSrcPt.Y() * fScaleY ) );
+ }
+
+ nMoveX = aDestPt.X() - aSrcPt.X();
+ nMoveY = aDestPt.Y() - aSrcPt.Y();
+
+ if( nMoveX || nMoveY )
+ aTmpMtf.Move( nMoveX, nMoveY );
+
+ ImplWriteActions( aTmpMtf, rVDev );
+ }
+ break;
+
+ case MetaActionType::COMMENT:
+ {
+ const MetaCommentAction* pA = static_cast<const MetaCommentAction*>(pMA);
+ if ( pA->GetComment().equalsIgnoreAsciiCase("XGRAD_SEQ_BEGIN") )
+ {
+ const MetaGradientExAction* pGradAction = nullptr;
+ while( ++nCurAction < nCount )
+ {
+ MetaAction* pAction = rMtf.GetAction( nCurAction );
+ if( pAction->GetType() == MetaActionType::GRADIENTEX )
+ pGradAction = static_cast<const MetaGradientExAction*>(pAction);
+ else if( ( pAction->GetType() == MetaActionType::COMMENT ) &&
+ ( static_cast<const MetaCommentAction*>(pAction)->GetComment().equalsIgnoreAsciiCase("XGRAD_SEQ_END") ) )
+ {
+ break;
+ }
+ }
+
+ if( pGradAction )
+ ImplWriteGradient( pGradAction->GetPolyPolygon(), pGradAction->GetGradient(), rVDev );
+ }
+ else if ( pA->GetComment() == "XPATHFILL_SEQ_END" )
+ {
+ if ( aFillPath.Count() )
+ {
+ aFillPath = tools::PolyPolygon();
+ ImplWriteLine( "gr" );
+ }
+ }
+ else
+ {
+ const sal_uInt8* pData = pA->GetData();
+ if ( pData )
+ {
+ SvMemoryStream aMemStm( const_cast<sal_uInt8 *>(pData), pA->GetDataSize(), StreamMode::READ );
+ bool bSkipSequence = false;
+ OString sSeqEnd;
+
+ if( pA->GetComment() == "XPATHSTROKE_SEQ_BEGIN" )
+ {
+ sSeqEnd = "XPATHSTROKE_SEQ_END";
+ SvtGraphicStroke aStroke;
+ ReadSvtGraphicStroke( aMemStm, aStroke );
+
+ tools::Polygon aPath;
+ aStroke.getPath( aPath );
+
+ tools::PolyPolygon aStartArrow;
+ tools::PolyPolygon aEndArrow;
+ double fStrokeWidth( aStroke.getStrokeWidth() );
+ SvtGraphicStroke::JoinType eJT( aStroke.getJoinType() );
+ SvtGraphicStroke::DashArray l_aDashArray;
+
+ aStroke.getStartArrow( aStartArrow );
+ aStroke.getEndArrow( aEndArrow );
+ aStroke.getDashArray( l_aDashArray );
+
+ bSkipSequence = true;
+ if ( l_aDashArray.size() > 11 ) // ps dasharray limit is 11
+ bSkipSequence = false;
+ if ( aStartArrow.Count() || aEndArrow.Count() )
+ bSkipSequence = false;
+ if ( static_cast<sal_uInt32>(eJT) > 2 )
+ bSkipSequence = false;
+ if ( !l_aDashArray.empty() && ( fStrokeWidth != 0.0 ) )
+ bSkipSequence = false;
+ if ( bSkipSequence )
+ {
+ ImplWriteLineInfo( fStrokeWidth, aStroke.getMiterLimit(),
+ aStroke.getCapType(), eJT, std::move(l_aDashArray) );
+ ImplPolyLine( aPath );
+ }
+ }
+ else if (pA->GetComment() == "XPATHFILL_SEQ_BEGIN")
+ {
+ sSeqEnd = "XPATHFILL_SEQ_END";
+ SvtGraphicFill aFill;
+ ReadSvtGraphicFill( aMemStm, aFill );
+ switch( aFill.getFillType() )
+ {
+ case SvtGraphicFill::fillSolid :
+ {
+ bSkipSequence = true;
+ tools::PolyPolygon aPolyPoly;
+ aFill.getPath( aPolyPoly );
+ sal_uInt16 i, nPolyCount = aPolyPoly.Count();
+ if ( nPolyCount )
+ {
+ aFillColor = aFill.getFillColor();
+ ImplWriteFillColor( PS_SPACE );
+ for ( i = 0; i < nPolyCount; )
+ {
+ ImplAddPath( aPolyPoly.GetObject( i ) );
+ if ( ++i < nPolyCount )
+ {
+ mpPS->WriteCharPtr( "p" );
+ mnCursorPos += 2;
+ ImplExecMode( PS_RET );
+ }
+ }
+ mpPS->WriteCharPtr( "p ef" );
+ mnCursorPos += 4;
+ ImplExecMode( PS_RET );
+ }
+ }
+ break;
+
+ case SvtGraphicFill::fillTexture :
+ {
+ aFill.getPath( aFillPath );
+
+ /* normally an object filling is consisting of three MetaActions:
+ MetaBitmapAction using RasterOp xor,
+ MetaPolyPolygonAction using RasterOp rop_0
+ MetaBitmapAction using RasterOp xor
+
+ Because RasterOps cannot been used in Postscript, we have to
+ replace these actions. The MetaComment "XPATHFILL_SEQ_BEGIN" is
+ providing the clippath of the object. The following loop is
+ trying to find the bitmap that is matching the clippath, so that
+ only one bitmap is exported, otherwise if the bitmap is not
+ locatable, all metaactions are played normally.
+ */
+ sal_uInt32 nCommentStartAction = nCurAction;
+ sal_uInt32 nBitmapCount = 0;
+ sal_uInt32 nBitmapAction = 0;
+
+ bool bOk = true;
+ while( bOk && ( ++nCurAction < nCount ) )
+ {
+ MetaAction* pAction = rMtf.GetAction( nCurAction );
+ switch( pAction->GetType() )
+ {
+ case MetaActionType::BMPSCALE :
+ case MetaActionType::BMPSCALEPART :
+ case MetaActionType::BMPEXSCALE :
+ case MetaActionType::BMPEXSCALEPART :
+ {
+ nBitmapCount++;
+ nBitmapAction = nCurAction;
+ }
+ break;
+ case MetaActionType::COMMENT :
+ {
+ if (static_cast<const MetaCommentAction*>(pAction)->GetComment() == "XPATHFILL_SEQ_END")
+ bOk = false;
+ }
+ break;
+ default: break;
+ }
+ }
+ if( nBitmapCount == 2 )
+ {
+ ImplWriteLine( "gs" );
+ ImplIntersect( aFillPath );
+ GDIMetaFile aTempMtf;
+ aTempMtf.AddAction( rMtf.GetAction( nBitmapAction )->Clone() );
+ ImplWriteActions( aTempMtf, rVDev );
+ ImplWriteLine( "gr" );
+ aFillPath = tools::PolyPolygon();
+ }
+ else
+ nCurAction = nCommentStartAction + 1;
+ }
+ break;
+
+ case SvtGraphicFill::fillGradient :
+ aFill.getPath( aFillPath );
+ break;
+
+ case SvtGraphicFill::fillHatch :
+ break;
+ }
+ if ( aFillPath.Count() )
+ {
+ ImplWriteLine( "gs" );
+ ImplIntersect( aFillPath );
+ }
+ }
+ if ( bSkipSequence )
+ {
+ while( ++nCurAction < nCount )
+ {
+ pMA = rMtf.GetAction( nCurAction );
+ if ( pMA->GetType() == MetaActionType::COMMENT )
+ {
+ OString sComment( static_cast<MetaCommentAction*>(pMA)->GetComment() );
+ if ( sComment == sSeqEnd )
+ break;
+ }
+ }
+ }
+ }
+ }
+ }
+ break;
+ default: break;
+ }
+ }
+}
+
+inline void PSWriter::ImplWritePoint( const Point& rPoint )
+{
+ ImplWriteDouble( rPoint.X() );
+ ImplWriteDouble( rPoint.Y() );
+}
+
+void PSWriter::ImplMoveTo( const Point& rPoint )
+{
+ ImplWritePoint( rPoint );
+ ImplWriteByte( 'm' );
+ ImplExecMode( PS_SPACE );
+}
+
+void PSWriter::ImplLineTo( const Point& rPoint, NMode nMode )
+{
+ ImplWritePoint( rPoint );
+ ImplWriteByte( 'l' );
+ ImplExecMode( nMode );
+}
+
+void PSWriter::ImplCurveTo( const Point& rP1, const Point& rP2, const Point& rP3, NMode nMode )
+{
+ ImplWritePoint( rP1 );
+ ImplWritePoint( rP2 );
+ ImplWritePoint( rP3 );
+ mpPS->WriteCharPtr( "ct " );
+ ImplExecMode( nMode );
+}
+
+void PSWriter::ImplTranslate( const double& fX, const double& fY )
+{
+ ImplWriteDouble( fX );
+ ImplWriteDouble( fY );
+ ImplWriteByte( 't' );
+ ImplExecMode( PS_RET );
+}
+
+void PSWriter::ImplScale( const double& fX, const double& fY )
+{
+ ImplWriteDouble( fX );
+ ImplWriteDouble( fY );
+ ImplWriteByte( 's' );
+ ImplExecMode( PS_RET );
+}
+
+void PSWriter::ImplRect( const tools::Rectangle & rRect )
+{
+ if ( bFillColor )
+ ImplRectFill( rRect );
+ if ( bLineColor )
+ {
+ double nWidth = rRect.GetWidth();
+ double nHeight = rRect.GetHeight();
+
+ ImplWriteLineColor( PS_SPACE );
+ ImplMoveTo( rRect.TopLeft() );
+ ImplWriteDouble( nWidth );
+ mpPS->WriteCharPtr( "0 rl 0 " );
+ ImplWriteDouble( nHeight );
+ mpPS->WriteCharPtr( "rl " );
+ ImplWriteDouble( nWidth );
+ mpPS->WriteCharPtr( "neg 0 rl " );
+ ImplClosePathDraw();
+ }
+ mpPS->WriteUChar( 10 );
+ mnCursorPos = 0;
+}
+
+void PSWriter::ImplRectFill( const tools::Rectangle & rRect )
+{
+ double nWidth = rRect.GetWidth();
+ double nHeight = rRect.GetHeight();
+
+ ImplWriteFillColor( PS_SPACE );
+ ImplMoveTo( rRect.TopLeft() );
+ ImplWriteDouble( nWidth );
+ mpPS->WriteCharPtr( "0 rl 0 " );
+ ImplWriteDouble( nHeight );
+ mpPS->WriteCharPtr( "rl " );
+ ImplWriteDouble( nWidth );
+ mpPS->WriteCharPtr( "neg 0 rl ef " );
+ mpPS->WriteCharPtr( "p ef" );
+ mnCursorPos += 2;
+ ImplExecMode( PS_RET );
+}
+
+void PSWriter::ImplAddPath( const tools::Polygon & rPolygon )
+{
+ sal_uInt16 nPointCount = rPolygon.GetSize();
+ if ( nPointCount <= 1 )
+ return;
+
+ sal_uInt16 i = 1;
+ ImplMoveTo( rPolygon.GetPoint( 0 ) );
+ while ( i < nPointCount )
+ {
+ if ( ( rPolygon.GetFlags( i ) == PolyFlags::Control )
+ && ( ( i + 2 ) < nPointCount )
+ && ( rPolygon.GetFlags( i + 1 ) == PolyFlags::Control )
+ && ( rPolygon.GetFlags( i + 2 ) != PolyFlags::Control ) )
+ {
+ ImplCurveTo( rPolygon[ i ], rPolygon[ i + 1 ], rPolygon[ i + 2 ], PS_WRAP );
+ i += 3;
+ }
+ else
+ ImplLineTo( rPolygon.GetPoint( i++ ), PS_SPACE | PS_WRAP );
+ }
+}
+
+void PSWriter::ImplIntersect( const tools::PolyPolygon& rPolyPoly )
+{
+ sal_uInt16 i, nPolyCount = rPolyPoly.Count();
+ for ( i = 0; i < nPolyCount; )
+ {
+ ImplAddPath( rPolyPoly.GetObject( i ) );
+ if ( ++i < nPolyCount )
+ {
+ mpPS->WriteCharPtr( "p" );
+ mnCursorPos += 2;
+ ImplExecMode( PS_RET );
+ }
+ }
+ ImplWriteLine( "eoclip newpath" );
+}
+
+void PSWriter::ImplWriteGradient( const tools::PolyPolygon& rPolyPoly, const Gradient& rGradient, VirtualDevice& rVDev )
+{
+ ScopedVclPtrInstance< VirtualDevice > l_pVDev;
+ GDIMetaFile aTmpMtf;
+ l_pVDev->SetMapMode( rVDev.GetMapMode() );
+ Gradient aGradient(rGradient);
+ aGradient.AddGradientActions( rPolyPoly.GetBoundRect(), aTmpMtf );
+ ImplWriteActions( aTmpMtf, rVDev );
+}
+
+void PSWriter::ImplPolyPoly( const tools::PolyPolygon & rPolyPoly, bool bTextOutline )
+{
+ sal_uInt16 i, nPolyCount = rPolyPoly.Count();
+ if ( !nPolyCount )
+ return;
+
+ if ( bFillColor || bTextOutline )
+ {
+ if ( bTextOutline )
+ ImplWriteTextColor( PS_SPACE );
+ else
+ ImplWriteFillColor( PS_SPACE );
+ for ( i = 0; i < nPolyCount; )
+ {
+ ImplAddPath( rPolyPoly.GetObject( i ) );
+ if ( ++i < nPolyCount )
+ {
+ mpPS->WriteCharPtr( "p" );
+ mnCursorPos += 2;
+ ImplExecMode( PS_RET );
+ }
+ }
+ mpPS->WriteCharPtr( "p ef" );
+ mnCursorPos += 4;
+ ImplExecMode( PS_RET );
+ }
+ if ( bLineColor )
+ {
+ ImplWriteLineColor( PS_SPACE );
+ for ( i = 0; i < nPolyCount; i++ )
+ ImplAddPath( rPolyPoly.GetObject( i ) );
+ ImplClosePathDraw();
+ }
+}
+
+void PSWriter::ImplPolyLine( const tools::Polygon & rPoly )
+{
+ if ( !bLineColor )
+ return;
+
+ ImplWriteLineColor( PS_SPACE );
+ sal_uInt16 i, nPointCount = rPoly.GetSize();
+ if ( !nPointCount )
+ return;
+
+ if ( nPointCount > 1 )
+ {
+ ImplMoveTo( rPoly.GetPoint( 0 ) );
+ i = 1;
+ while ( i < nPointCount )
+ {
+ if ( ( rPoly.GetFlags( i ) == PolyFlags::Control )
+ && ( ( i + 2 ) < nPointCount )
+ && ( rPoly.GetFlags( i + 1 ) == PolyFlags::Control )
+ && ( rPoly.GetFlags( i + 2 ) != PolyFlags::Control ) )
+ {
+ ImplCurveTo( rPoly[ i ], rPoly[ i + 1 ], rPoly[ i + 2 ], PS_WRAP );
+ i += 3;
+ }
+ else
+ ImplLineTo( rPoly.GetPoint( i++ ), PS_SPACE | PS_WRAP );
+ }
+ }
+
+ // #104645# explicitly close path if polygon is closed
+ if( rPoly[ 0 ] == rPoly[ nPointCount-1 ] )
+ ImplClosePathDraw();
+ else
+ ImplPathDraw();
+}
+
+void PSWriter::ImplSetClipRegion( vcl::Region const & rClipRegion )
+{
+ if ( rClipRegion.IsEmpty() )
+ return;
+
+ RectangleVector aRectangles;
+ rClipRegion.GetRegionRectangles(aRectangles);
+
+ for (auto const& rectangle : aRectangles)
+ {
+ double nX1(rectangle.Left());
+ double nY1(rectangle.Top());
+ double nX2(rectangle.Right());
+ double nY2(rectangle.Bottom());
+
+ ImplWriteDouble( nX1 );
+ ImplWriteDouble( nY1 );
+ ImplWriteByte( 'm' );
+ ImplWriteDouble( nX2 );
+ ImplWriteDouble( nY1 );
+ ImplWriteByte( 'l' );
+ ImplWriteDouble( nX2 );
+ ImplWriteDouble( nY2 );
+ ImplWriteByte( 'l' );
+ ImplWriteDouble( nX1 );
+ ImplWriteDouble( nY2 );
+ ImplWriteByte( 'l' );
+ ImplWriteDouble( nX1 );
+ ImplWriteDouble( nY1 );
+ ImplWriteByte( 'l', PS_SPACE | PS_WRAP );
+ }
+
+ ImplWriteLine( "eoclip newpath" );
+}
+
+// possible gfx formats:
+//
+// level 1: grayscale 8 bit
+// color 24 bit
+//
+// level 2: grayscale 8 bit
+// color 1(pal), 4(pal), 8(pal), 24 Bit
+//
+
+void PSWriter::ImplBmp( Bitmap const * pBitmap, Bitmap const * pMaskBitmap, const Point & rPoint, double nXWidth, double nYHeightOrg )
+{
+ if ( !pBitmap )
+ return;
+
+ sal_Int32 nHeightOrg = pBitmap->GetSizePixel().Height();
+ sal_Int32 nHeightLeft = nHeightOrg;
+ tools::Long nWidth = pBitmap->GetSizePixel().Width();
+ Point aSourcePos( rPoint );
+
+ while ( nHeightLeft )
+ {
+ Bitmap aTileBitmap( *pBitmap );
+ tools::Long nHeight = nHeightLeft;
+ double nYHeight = nYHeightOrg;
+
+ bool bDoTrans = false;
+
+ tools::Rectangle aRect;
+ vcl::Region aRegion;
+
+ if ( pMaskBitmap )
+ {
+ bDoTrans = true;
+ while (true)
+ {
+ if ( mnLevel == 1 && nHeight > 10 )
+ nHeight = 8;
+ aRect = tools::Rectangle( Point( 0, nHeightOrg - nHeightLeft ), Size( nWidth, nHeight ) );
+ aRegion = pMaskBitmap->CreateRegion( COL_BLACK, aRect );
+
+ if( mnLevel == 1 )
+ {
+ RectangleVector aRectangleVector;
+ aRegion.GetRegionRectangles(aRectangleVector);
+
+ if ( aRectangleVector.size() * 5 > 1000 )
+ {
+ nHeight >>= 1;
+ if ( nHeight < 2 )
+ return;
+ continue;
+ }
+ }
+ break;
+ }
+ }
+ if ( nHeight != nHeightOrg )
+ {
+ nYHeight = nYHeightOrg * nHeight / nHeightOrg;
+ aTileBitmap.Crop( tools::Rectangle( Point( 0, nHeightOrg - nHeightLeft ), Size( nWidth, nHeight ) ) );
+ }
+ if ( bDoTrans )
+ {
+ ImplWriteLine( "gs\npum" );
+ ImplTranslate( aSourcePos.X(), aSourcePos.Y() );
+ ImplScale( nXWidth / nWidth, nYHeight / nHeight );
+
+ RectangleVector aRectangles;
+ aRegion.GetRegionRectangles(aRectangles);
+ const tools::Long nMoveVertical(nHeightLeft - nHeightOrg);
+
+ for (auto & rectangle : aRectangles)
+ {
+ rectangle.Move(0, nMoveVertical);
+
+ ImplWriteLong( rectangle.Left() );
+ ImplWriteLong( rectangle.Top() );
+ ImplWriteByte( 'm' );
+ ImplWriteLong( rectangle.Right() + 1 );
+ ImplWriteLong( rectangle.Top() );
+ ImplWriteByte( 'l' );
+ ImplWriteLong( rectangle.Right() + 1 );
+ ImplWriteLong( rectangle.Bottom() + 1 );
+ ImplWriteByte( 'l' );
+ ImplWriteLong( rectangle.Left() );
+ ImplWriteLong( rectangle.Bottom() + 1 );
+ ImplWriteByte( 'l' );
+ ImplWriteByte( 'p', PS_SPACE | PS_WRAP );
+ }
+
+ ImplWriteLine( "eoclip newpath" );
+ ImplWriteLine( "pom" );
+ }
+ BitmapReadAccess* pAcc = aTileBitmap.AcquireReadAccess();
+
+ if (!bDoTrans )
+ ImplWriteLine( "pum" );
+
+ ImplTranslate( aSourcePos.X(), aSourcePos.Y() + nYHeight );
+ ImplScale( nXWidth, nYHeight );
+ if ( mnLevel == 1 ) // level 1 is always grayscale !!!
+ {
+ ImplWriteLong( nWidth );
+ ImplWriteLong( nHeight );
+ mpPS->WriteCharPtr( "8 [" );
+ ImplWriteLong( nWidth );
+ mpPS->WriteCharPtr( "0 0 " );
+ ImplWriteLong( -nHeight );
+ ImplWriteLong( 0 );
+ ImplWriteLong( nHeight );
+ ImplWriteLine( "]" );
+ mpPS->WriteCharPtr( "{currentfile " );
+ ImplWriteLong( nWidth );
+ ImplWriteLine( "string readhexstring pop}" );
+ ImplWriteLine( "image" );
+ for ( tools::Long y = 0; y < nHeight; y++ )
+ {
+ Scanline pScanlineRead = pAcc->GetScanline( y );
+ for ( tools::Long x = 0; x < nWidth; x++ )
+ {
+ ImplWriteHexByte( pAcc->GetIndexFromData( pScanlineRead, x ) );
+ }
+ }
+ mpPS->WriteUChar( 10 );
+ }
+ else // Level 2
+ {
+ if ( mbGrayScale )
+ {
+ ImplWriteLine( "/DeviceGray setcolorspace" );
+ ImplWriteLine( "<<" );
+ ImplWriteLine( "/ImageType 1" );
+ mpPS->WriteCharPtr( "/Width " );
+ ImplWriteLong( nWidth, PS_RET );
+ mpPS->WriteCharPtr( "/Height " );
+ ImplWriteLong( nHeight, PS_RET );
+ ImplWriteLine( "/BitsPerComponent 8" );
+ ImplWriteLine( "/Decode[0 1]" );
+ mpPS->WriteCharPtr( "/ImageMatrix[" );
+ ImplWriteLong( nWidth );
+ mpPS->WriteCharPtr( "0 0 " );
+ ImplWriteLong( -nHeight );
+ ImplWriteLong( 0 );
+ ImplWriteLong( nHeight, PS_NONE );
+ ImplWriteByte( ']', PS_RET );
+ ImplWriteLine( "/DataSource currentfile" );
+ ImplWriteLine( "/ASCIIHexDecode filter" );
+ if ( mbCompression )
+ ImplWriteLine( "/LZWDecode filter" );
+ ImplWriteLine( ">>" );
+ ImplWriteLine( "image" );
+ if ( mbCompression )
+ {
+ StartCompression();
+ for ( tools::Long y = 0; y < nHeight; y++ )
+ {
+ Scanline pScanlineRead = pAcc->GetScanline( y );
+ for ( tools::Long x = 0; x < nWidth; x++ )
+ {
+ Compress( pAcc->GetIndexFromData( pScanlineRead, x ) );
+ }
+ }
+ EndCompression();
+ }
+ else
+ {
+ for ( tools::Long y = 0; y < nHeight; y++ )
+ {
+ Scanline pScanlineRead = pAcc->GetScanline( y );
+ for ( tools::Long x = 0; x < nWidth; x++ )
+ {
+ ImplWriteHexByte( pAcc->GetIndexFromData( pScanlineRead, x ) );
+ }
+ }
+ }
+ }
+ else
+ {
+ // have we to write a palette ?
+
+ if ( pAcc->HasPalette() )
+ {
+ ImplWriteLine( "[/Indexed /DeviceRGB " );
+ ImplWriteLong( pAcc->GetPaletteEntryCount() - 1, PS_RET );
+ ImplWriteByte( '<', PS_NONE );
+ for ( sal_uInt16 i = 0; i < pAcc->GetPaletteEntryCount(); i++ )
+ {
+ BitmapColor aBitmapColor = pAcc->GetPaletteColor( i );
+ ImplWriteHexByte( aBitmapColor.GetRed(), PS_NONE );
+ ImplWriteHexByte( aBitmapColor.GetGreen(), PS_NONE );
+ ImplWriteHexByte( aBitmapColor.GetBlue(), PS_SPACE | PS_WRAP );
+ }
+ ImplWriteByte( '>', PS_RET );
+
+ ImplWriteLine( "] setcolorspace" );
+ ImplWriteLine( "<<" );
+ ImplWriteLine( "/ImageType 1" );
+ mpPS->WriteCharPtr( "/Width " );
+ ImplWriteLong( nWidth, PS_RET );
+ mpPS->WriteCharPtr( "/Height " );
+ ImplWriteLong( nHeight, PS_RET );
+ ImplWriteLine( "/BitsPerComponent 8" );
+ ImplWriteLine( "/Decode[0 255]" );
+ mpPS->WriteCharPtr( "/ImageMatrix[" );
+ ImplWriteLong( nWidth );
+ mpPS->WriteCharPtr( "0 0 " );
+ ImplWriteLong( -nHeight );
+ ImplWriteLong( 0);
+ ImplWriteLong( nHeight, PS_NONE );
+ ImplWriteByte( ']', PS_RET );
+ ImplWriteLine( "/DataSource currentfile" );
+ ImplWriteLine( "/ASCIIHexDecode filter" );
+ if ( mbCompression )
+ ImplWriteLine( "/LZWDecode filter" );
+ ImplWriteLine( ">>" );
+ ImplWriteLine( "image" );
+ if ( mbCompression )
+ {
+ StartCompression();
+ for ( tools::Long y = 0; y < nHeight; y++ )
+ {
+ Scanline pScanlineRead = pAcc->GetScanline( y );
+ for ( tools::Long x = 0; x < nWidth; x++ )
+ {
+ Compress( pAcc->GetIndexFromData( pScanlineRead, x ) );
+ }
+ }
+ EndCompression();
+ }
+ else
+ {
+ for ( tools::Long y = 0; y < nHeight; y++ )
+ {
+ Scanline pScanlineRead = pAcc->GetScanline( y );
+ for ( tools::Long x = 0; x < nWidth; x++ )
+ {
+ ImplWriteHexByte( pAcc->GetIndexFromData( pScanlineRead, x ) );
+ }
+ }
+ }
+ }
+ else // 24 bit color
+ {
+ ImplWriteLine( "/DeviceRGB setcolorspace" );
+ ImplWriteLine( "<<" );
+ ImplWriteLine( "/ImageType 1" );
+ mpPS->WriteCharPtr( "/Width " );
+ ImplWriteLong( nWidth, PS_RET );
+ mpPS->WriteCharPtr( "/Height " );
+ ImplWriteLong( nHeight, PS_RET );
+ ImplWriteLine( "/BitsPerComponent 8" );
+ ImplWriteLine( "/Decode[0 1 0 1 0 1]" );
+ mpPS->WriteCharPtr( "/ImageMatrix[" );
+ ImplWriteLong( nWidth );
+ mpPS->WriteCharPtr( "0 0 " );
+ ImplWriteLong( -nHeight );
+ ImplWriteLong( 0 );
+ ImplWriteLong( nHeight, PS_NONE );
+ ImplWriteByte( ']', PS_RET );
+ ImplWriteLine( "/DataSource currentfile" );
+ ImplWriteLine( "/ASCIIHexDecode filter" );
+ if ( mbCompression )
+ ImplWriteLine( "/LZWDecode filter" );
+ ImplWriteLine( ">>" );
+ ImplWriteLine( "image" );
+ if ( mbCompression )
+ {
+ StartCompression();
+ for ( tools::Long y = 0; y < nHeight; y++ )
+ {
+ Scanline pScanlineRead = pAcc->GetScanline( y );
+ for ( tools::Long x = 0; x < nWidth; x++ )
+ {
+ const BitmapColor aBitmapColor( pAcc->GetPixelFromData( pScanlineRead, x ) );
+ Compress( aBitmapColor.GetRed() );
+ Compress( aBitmapColor.GetGreen() );
+ Compress( aBitmapColor.GetBlue() );
+ }
+ }
+ EndCompression();
+ }
+ else
+ {
+ for ( tools::Long y = 0; y < nHeight; y++ )
+ {
+ Scanline pScanline = pAcc->GetScanline( y );
+ for ( tools::Long x = 0; x < nWidth; x++ )
+ {
+ const BitmapColor aBitmapColor( pAcc->GetPixelFromData( pScanline, x ) );
+ ImplWriteHexByte( aBitmapColor.GetRed() );
+ ImplWriteHexByte( aBitmapColor.GetGreen() );
+ ImplWriteHexByte( aBitmapColor.GetBlue() );
+ }
+ }
+ }
+ }
+ }
+ ImplWriteLine( ">" ); // in Level 2 the dictionary needs to be closed (eod)
+ }
+ if ( bDoTrans )
+ ImplWriteLine( "gr" );
+ else
+ ImplWriteLine( "pom" );
+
+ Bitmap::ReleaseAccess( pAcc );
+ nHeightLeft -= nHeight;
+ if ( nHeightLeft )
+ {
+ nHeightLeft++;
+ aSourcePos.setY( static_cast<tools::Long>( rPoint.Y() + ( nYHeightOrg * ( nHeightOrg - nHeightLeft ) ) / nHeightOrg ) );
+ }
+ }
+}
+
+void PSWriter::ImplWriteCharacter( char nChar )
+{
+ switch( nChar )
+ {
+ case '(' :
+ case ')' :
+ case '\\' :
+ ImplWriteByte( sal_uInt8('\\'), PS_NONE );
+ }
+ ImplWriteByte( static_cast<sal_uInt8>(nChar), PS_NONE );
+}
+
+void PSWriter::ImplWriteString( const OString& rString, VirtualDevice const & rVDev, o3tl::span<const sal_Int32> pDXArry, bool bStretch )
+{
+ sal_Int32 nLen = rString.getLength();
+ if ( !nLen )
+ return;
+
+ if ( !pDXArry.empty() )
+ {
+ double nx = 0;
+
+ for (sal_Int32 i = 0; i < nLen; ++i)
+ {
+ if ( i > 0 )
+ nx = pDXArry[ i - 1 ];
+ ImplWriteDouble( bStretch ? nx : rVDev.GetTextWidth( OUString(rString[i]) ) );
+ ImplWriteDouble( nx );
+ ImplWriteLine( "(", PS_NONE );
+ ImplWriteCharacter( rString[i] );
+ ImplWriteLine( ") bs" );
+ }
+ }
+ else
+ {
+ ImplWriteByte( '(', PS_NONE );
+ for (sal_Int32 i = 0; i < nLen; ++i)
+ ImplWriteCharacter( rString[i] );
+ ImplWriteLine( ") sw" );
+ }
+}
+
+void PSWriter::ImplText( const OUString& rUniString, const Point& rPos, o3tl::span<const sal_Int32> pDXArry, sal_Int32 nWidth, VirtualDevice const & rVDev )
+{
+ if ( rUniString.isEmpty() )
+ return;
+ if ( mnTextMode == 0 ) // using glyph outlines
+ {
+ vcl::Font aNotRotatedFont( maFont );
+ aNotRotatedFont.SetOrientation( 0_deg10 );
+
+ ScopedVclPtrInstance< VirtualDevice > pVirDev(DeviceFormat::DEFAULT);
+ pVirDev->SetMapMode( rVDev.GetMapMode() );
+ pVirDev->SetFont( aNotRotatedFont );
+ pVirDev->SetTextAlign( eTextAlign );
+
+ Degree10 nRotation = maFont.GetOrientation();
+ tools::Polygon aPolyDummy( 1 );
+
+ Point aPos( rPos );
+ if ( nRotation )
+ {
+ aPolyDummy.SetPoint( aPos, 0 );
+ aPolyDummy.Rotate( rPos, nRotation );
+ aPos = aPolyDummy.GetPoint( 0 );
+ }
+ bool bOldLineColor = bLineColor;
+ bLineColor = false;
+ std::vector<tools::PolyPolygon> aPolyPolyVec;
+ if ( pVirDev->GetTextOutlines( aPolyPolyVec, rUniString, 0, 0, -1, nWidth, pDXArry ) )
+ {
+ // always adjust text position to match baseline alignment
+ ImplWriteLine( "pum" );
+ ImplWriteDouble( aPos.X() );
+ ImplWriteDouble( aPos.Y() );
+ ImplWriteLine( "t" );
+ if ( nRotation )
+ {
+ ImplWriteF( nRotation.get(), 1 );
+ mpPS->WriteCharPtr( "r " );
+ }
+ for (auto const& elem : aPolyPolyVec)
+ ImplPolyPoly( elem, true );
+ ImplWriteLine( "pom" );
+ }
+ bLineColor = bOldLineColor;
+ }
+ else if ( ( mnTextMode == 1 ) || ( mnTextMode == 2 ) ) // normal text output
+ {
+ if ( mnTextMode == 2 ) // forcing output one complete text packet, by
+ pDXArry = {}; // ignoring the kerning array
+ ImplSetAttrForText( rPos );
+ OString aStr(OUStringToOString(rUniString,
+ maFont.GetCharSet()));
+ ImplWriteString( aStr, rVDev, pDXArry, nWidth != 0 );
+ if ( maFont.GetOrientation() )
+ ImplWriteLine( "gr" );
+ }
+}
+
+void PSWriter::ImplSetAttrForText( const Point& rPoint )
+{
+ Point aPoint( rPoint );
+
+ Degree10 nRotation = maFont.GetOrientation();
+ ImplWriteTextColor(PS_RET);
+
+ Size aSize = maFont.GetFontSize();
+
+ if ( maLastFont != maFont )
+ {
+ if ( maFont.GetPitch() == PITCH_FIXED ) // a little bit font selection
+ ImplDefineFont( "Courier", "Oblique" );
+ else if ( maFont.GetCharSet() == RTL_TEXTENCODING_SYMBOL )
+ ImplWriteLine( "/Symbol findfont" );
+ else if ( maFont.GetFamilyType() == FAMILY_SWISS )
+ ImplDefineFont( "Helvetica", "Oblique" );
+ else
+ ImplDefineFont( "Times", "Italic" );
+
+ maLastFont = maFont;
+ aSize = maFont.GetFontSize();
+ ImplWriteDouble( aSize.Height() );
+ mpPS->WriteCharPtr( "sf " );
+ }
+ if ( eTextAlign != ALIGN_BASELINE )
+ { // PostScript does not know about FontAlignment
+ if ( eTextAlign == ALIGN_TOP ) // -> so I assume that
+ aPoint.AdjustY( aSize.Height() * 4 / 5 ); // the area under the baseline
+ else if ( eTextAlign == ALIGN_BOTTOM ) // is about 20% of the font size
+ aPoint.AdjustY( -( aSize.Height() / 5 ) );
+ }
+ ImplMoveTo( aPoint );
+ if ( nRotation )
+ {
+ mpPS->WriteCharPtr( "gs " );
+ ImplWriteF( nRotation.get(), 1 );
+ mpPS->WriteCharPtr( "r " );
+ }
+}
+
+void PSWriter::ImplDefineFont( const char* pOriginalName, const char* pItalic )
+{
+ mpPS->WriteUChar( '/' ); //convert the font pOriginalName using ISOLatin1Encoding
+ mpPS->WriteCharPtr( pOriginalName );
+ switch ( maFont.GetWeight() )
+ {
+ case WEIGHT_SEMIBOLD :
+ case WEIGHT_BOLD :
+ case WEIGHT_ULTRABOLD :
+ case WEIGHT_BLACK :
+ mpPS->WriteCharPtr( "-Bold" );
+ if ( maFont.GetItalic() != ITALIC_NONE )
+ mpPS->WriteCharPtr( pItalic );
+ break;
+ default:
+ if ( maFont.GetItalic() != ITALIC_NONE )
+ mpPS->WriteCharPtr( pItalic );
+ break;
+ }
+ ImplWriteLine( " f" );
+}
+
+void PSWriter::ImplClosePathDraw()
+{
+ mpPS->WriteCharPtr( "pc" );
+ mnCursorPos += 2;
+ ImplExecMode( PS_RET );
+}
+
+void PSWriter::ImplPathDraw()
+{
+ mpPS->WriteCharPtr( "ps" );
+ mnCursorPos += 2;
+ ImplExecMode( PS_RET );
+}
+
+
+inline void PSWriter::ImplWriteLineColor( NMode nMode )
+{
+ if ( aColor != aLineColor )
+ {
+ aColor = aLineColor;
+ ImplWriteColor( nMode );
+ }
+}
+
+inline void PSWriter::ImplWriteFillColor( NMode nMode )
+{
+ if ( aColor != aFillColor )
+ {
+ aColor = aFillColor;
+ ImplWriteColor( nMode );
+ }
+}
+
+inline void PSWriter::ImplWriteTextColor( NMode nMode )
+{
+ if ( aColor != aTextColor )
+ {
+ aColor = aTextColor;
+ ImplWriteColor( nMode );
+ }
+}
+
+void PSWriter::ImplWriteColor( NMode nMode )
+{
+ if ( mbGrayScale )
+ {
+ // writes the Color (grayscale) as a Number from 0.000 up to 1.000
+
+ ImplWriteF( 1000 * ( aColor.GetRed() * 77 + aColor.GetGreen() * 151 +
+ aColor.GetBlue() * 28 + 1 ) / 65536, 3, nMode );
+ }
+ else
+ {
+ ImplWriteB1 ( aColor.GetRed() );
+ ImplWriteB1 ( aColor.GetGreen() );
+ ImplWriteB1 ( aColor.GetBlue() );
+ }
+ mpPS->WriteCharPtr( "c" ); // ( c is defined as setrgbcolor or setgray )
+ ImplExecMode( nMode );
+}
+
+void PSWriter::ImplGetMapMode( const MapMode& rMapMode )
+{
+ ImplWriteLine( "tm setmatrix" );
+ double fMul = ImplGetScaling(rMapMode);
+ double fScaleX = static_cast<double>(rMapMode.GetScaleX()) * fMul;
+ double fScaleY = static_cast<double>(rMapMode.GetScaleY()) * fMul;
+ ImplTranslate( rMapMode.GetOrigin().X() * fScaleX, rMapMode.GetOrigin().Y() * fScaleY );
+ ImplScale( fScaleX, fScaleY );
+}
+
+inline void PSWriter::ImplExecMode( NMode nMode )
+{
+ if ( nMode & PS_WRAP )
+ {
+ if ( mnCursorPos >= PS_LINESIZE )
+ {
+ mnCursorPos = 0;
+ mpPS->WriteUChar( 0xa );
+ return;
+ }
+ }
+ if ( nMode & PS_SPACE )
+ {
+ mpPS->WriteUChar( 32 );
+ mnCursorPos++;
+ }
+ if ( nMode & PS_RET )
+ {
+ mpPS->WriteUChar( 0xa );
+ mnCursorPos = 0;
+ }
+}
+
+inline void PSWriter::ImplWriteLine( const char* pString, NMode nMode )
+{
+ sal_uInt32 i = 0;
+ while ( pString[ i ] )
+ {
+ mpPS->WriteUChar( pString[ i++ ] );
+ }
+ mnCursorPos += i;
+ ImplExecMode( nMode );
+}
+
+double PSWriter::ImplGetScaling( const MapMode& rMapMode )
+{
+ double nMul;
+ switch (rMapMode.GetMapUnit())
+ {
+ case MapUnit::MapPixel :
+ case MapUnit::MapSysFont :
+ case MapUnit::MapAppFont :
+
+ case MapUnit::Map100thMM :
+ nMul = 1;
+ break;
+ case MapUnit::Map10thMM :
+ nMul = 10;
+ break;
+ case MapUnit::MapMM :
+ nMul = 100;
+ break;
+ case MapUnit::MapCM :
+ nMul = 1000;
+ break;
+ case MapUnit::Map1000thInch :
+ nMul = 2.54;
+ break;
+ case MapUnit::Map100thInch :
+ nMul = 25.4;
+ break;
+ case MapUnit::Map10thInch :
+ nMul = 254;
+ break;
+ case MapUnit::MapInch :
+ nMul = 2540;
+ break;
+ case MapUnit::MapTwip :
+ nMul = 1.76388889;
+ break;
+ case MapUnit::MapPoint :
+ nMul = 35.27777778;
+ break;
+ default:
+ nMul = 1.0;
+ break;
+ }
+ return nMul;
+}
+
+
+void PSWriter::ImplWriteLineInfo( double fLWidth, double fMLimit,
+ SvtGraphicStroke::CapType eLCap,
+ SvtGraphicStroke::JoinType eJoin,
+ SvtGraphicStroke::DashArray && rLDash )
+{
+ if ( fLineWidth != fLWidth )
+ {
+ fLineWidth = fLWidth;
+ ImplWriteDouble( fLineWidth );
+ ImplWriteLine( "lw", PS_SPACE );
+ }
+ if ( eLineCap != eLCap )
+ {
+ eLineCap = eLCap;
+ ImplWriteLong( static_cast<sal_Int32>(eLineCap) );
+ ImplWriteLine( "lc", PS_SPACE );
+ }
+ if ( eJoinType != eJoin )
+ {
+ eJoinType = eJoin;
+ ImplWriteLong( static_cast<sal_Int32>(eJoinType) );
+ ImplWriteLine( "lj", PS_SPACE );
+ }
+ if ( eJoinType == SvtGraphicStroke::joinMiter )
+ {
+ if ( fMiterLimit != fMLimit )
+ {
+ fMiterLimit = fMLimit;
+ ImplWriteDouble( fMiterLimit );
+ ImplWriteLine( "ml", PS_SPACE );
+ }
+ }
+ if ( aDashArray != rLDash )
+ {
+ aDashArray = std::move(rLDash);
+ sal_uInt32 j, i = aDashArray.size();
+ ImplWriteLine( "[", PS_SPACE );
+ for ( j = 0; j < i; j++ )
+ ImplWriteDouble( aDashArray[ j ] );
+ ImplWriteLine( "] 0 ld" );
+ }
+}
+
+void PSWriter::ImplWriteLineInfo( const LineInfo& rLineInfo )
+{
+ std::vector< double > l_aDashArray;
+ if ( rLineInfo.GetStyle() == LineStyle::Dash )
+ l_aDashArray = rLineInfo.GetDotDashArray();
+ const double fLWidth(( ( rLineInfo.GetWidth() + 1 ) + ( rLineInfo.GetWidth() + 1 ) ) * 0.5);
+ SvtGraphicStroke::JoinType aJoinType(SvtGraphicStroke::joinMiter);
+ SvtGraphicStroke::CapType aCapType(SvtGraphicStroke::capButt);
+
+ switch(rLineInfo.GetLineJoin())
+ {
+ case basegfx::B2DLineJoin::NONE:
+ // do NOT use SvtGraphicStroke::joinNone here
+ // since it will be written as numerical value directly
+ // and is NOT a valid EPS value
+ break;
+ case basegfx::B2DLineJoin::Miter:
+ aJoinType = SvtGraphicStroke::joinMiter;
+ break;
+ case basegfx::B2DLineJoin::Bevel:
+ aJoinType = SvtGraphicStroke::joinBevel;
+ break;
+ case basegfx::B2DLineJoin::Round:
+ aJoinType = SvtGraphicStroke::joinRound;
+ break;
+ }
+ switch(rLineInfo.GetLineCap())
+ {
+ default: /* css::drawing::LineCap_BUTT */
+ {
+ aCapType = SvtGraphicStroke::capButt;
+ break;
+ }
+ case css::drawing::LineCap_ROUND:
+ {
+ aCapType = SvtGraphicStroke::capRound;
+ break;
+ }
+ case css::drawing::LineCap_SQUARE:
+ {
+ aCapType = SvtGraphicStroke::capSquare;
+ break;
+ }
+ }
+
+ ImplWriteLineInfo( fLWidth, fMiterLimit, aCapType, aJoinType, std::move(l_aDashArray) );
+}
+
+void PSWriter::ImplWriteLong(sal_Int32 nNumber, NMode nMode)
+{
+ const OString aNumber(OString::number(nNumber));
+ mnCursorPos += aNumber.getLength();
+ mpPS->WriteOString( aNumber );
+ ImplExecMode(nMode);
+}
+
+void PSWriter::ImplWriteDouble( double fNumber )
+{
+ sal_Int32 nPTemp = static_cast<sal_Int32>(fNumber);
+ sal_Int32 nATemp = std::abs( static_cast<sal_Int32>( ( fNumber - nPTemp ) * 100000 ) );
+
+ if ( !nPTemp && nATemp && ( fNumber < 0.0 ) )
+ mpPS->WriteChar( '-' );
+
+ const OString aNumber1(OString::number(nPTemp));
+ mpPS->WriteOString( aNumber1 );
+ mnCursorPos += aNumber1.getLength();
+
+ if ( nATemp )
+ {
+ int zCount = 0;
+ mpPS->WriteUChar( '.' );
+ mnCursorPos++;
+ const OString aNumber2(OString::number(nATemp));
+
+ sal_Int16 n, nLen = aNumber2.getLength();
+ if ( nLen < 8 )
+ {
+ mnCursorPos += 6 - nLen;
+ for ( n = 0; n < ( 5 - nLen ); n++ )
+ {
+ mpPS->WriteUChar( '0' );
+ }
+ }
+ mnCursorPos += nLen;
+ for ( n = 0; n < nLen; n++ )
+ {
+ mpPS->WriteChar( aNumber2[n] );
+ zCount--;
+ if ( aNumber2[n] != '0' )
+ zCount = 0;
+ }
+ if ( zCount )
+ mpPS->SeekRel( zCount );
+ }
+ ImplExecMode( PS_SPACE );
+}
+
+/// Writes the number to stream: nNumber / ( 10^nCount )
+void PSWriter::ImplWriteF( sal_Int32 nNumber, sal_uInt8 nCount, NMode nMode )
+{
+ if ( nNumber < 0 )
+ {
+ mpPS->WriteUChar( '-' );
+ nNumber = -nNumber;
+ mnCursorPos++;
+ }
+ const OString aScaleFactor(OString::number(nNumber));
+ sal_uInt32 nLen = aScaleFactor.getLength();
+ sal_Int32 const nStSize = (nCount + 1) - nLen;
+ static_assert(sizeof(nStSize) == sizeof((nCount + 1) - nLen)); // tdf#134667
+ if ( nStSize >= 1 )
+ {
+ mpPS->WriteUChar( '0' );
+ mnCursorPos++;
+ }
+ if ( nStSize >= 2 )
+ {
+ mpPS->WriteUChar( '.' );
+ for (sal_Int32 i = 1; i < nStSize; ++i)
+ {
+ mpPS->WriteUChar( '0' );
+ mnCursorPos++;
+ }
+ }
+ mnCursorPos += nLen;
+ for( sal_uInt32 n = 0; n < nLen; n++ )
+ {
+ if ( n == nLen - nCount )
+ {
+ mpPS->WriteUChar( '.' );
+ mnCursorPos++;
+ }
+ mpPS->WriteChar( aScaleFactor[n] );
+ }
+ ImplExecMode( nMode );
+}
+
+void PSWriter::ImplWriteByte( sal_uInt8 nNumb, NMode nMode )
+{
+ mpPS->WriteUChar( nNumb );
+ mnCursorPos++;
+ ImplExecMode( nMode );
+}
+
+void PSWriter::ImplWriteHexByte( sal_uInt8 nNumb, NMode nMode )
+{
+ if ( ( nNumb >> 4 ) > 9 )
+ mpPS->WriteUChar( ( nNumb >> 4 ) + 'A' - 10 );
+ else
+ mpPS->WriteUChar( ( nNumb >> 4 ) + '0' );
+
+ if ( ( nNumb & 0xf ) > 9 )
+ mpPS->WriteUChar( ( nNumb & 0xf ) + 'A' - 10 );
+ else
+ mpPS->WriteUChar( ( nNumb & 0xf ) + '0' );
+ mnCursorPos += 2;
+ ImplExecMode( nMode );
+}
+
+// writes the sal_uInt8 nNumb as a Number from 0.000 up to 1.000
+
+void PSWriter::ImplWriteB1( sal_uInt8 nNumb )
+{
+ ImplWriteF( 1000 * ( nNumb + 1 ) / 256 );
+}
+
+inline void PSWriter::WriteBits( sal_uInt16 nCode, sal_uInt16 nCodeLen )
+{
+ dwShift |= ( nCode << ( nOffset - nCodeLen ) );
+ nOffset -= nCodeLen;
+ while ( nOffset < 24 )
+ {
+ ImplWriteHexByte( static_cast<sal_uInt8>( dwShift >> 24 ) );
+ dwShift <<= 8;
+ nOffset += 8;
+ }
+ if ( nCode == 257 && nOffset != 32 )
+ ImplWriteHexByte( static_cast<sal_uInt8>( dwShift >> 24 ) );
+}
+
+void PSWriter::StartCompression()
+{
+ sal_uInt16 i;
+ nDataSize = 8;
+
+ nClearCode = 1 << nDataSize;
+ nEOICode = nClearCode + 1;
+ nTableSize = nEOICode + 1;
+ nCodeSize = nDataSize + 1;
+
+ nOffset = 32; // number of free unused in dwShift
+ dwShift = 0;
+
+ pTable.reset(new PSLZWCTreeNode[ 4096 ]);
+
+ for ( i = 0; i < 4096; i++ )
+ {
+ pTable[ i ].pBrother = pTable[ i ].pFirstChild = nullptr;
+ pTable[ i ].nCode = i;
+ pTable[ i ].nValue = static_cast<sal_uInt8>( i );
+ }
+ pPrefix = nullptr;
+ WriteBits( nClearCode, nCodeSize );
+}
+
+void PSWriter::Compress( sal_uInt8 nCompThis )
+{
+ PSLZWCTreeNode* p;
+ sal_uInt16 i;
+ sal_uInt8 nV;
+
+ if( !pPrefix )
+ {
+ pPrefix = pTable.get() + nCompThis;
+ }
+ else
+ {
+ nV = nCompThis;
+ for( p = pPrefix->pFirstChild; p != nullptr; p = p->pBrother )
+ {
+ if ( p->nValue == nV )
+ break;
+ }
+
+ if( p )
+ pPrefix = p;
+ else
+ {
+ WriteBits( pPrefix->nCode, nCodeSize );
+
+ if ( nTableSize == 409 )
+ {
+ WriteBits( nClearCode, nCodeSize );
+
+ for ( i = 0; i < nClearCode; i++ )
+ pTable[ i ].pFirstChild = nullptr;
+
+ nCodeSize = nDataSize + 1;
+ nTableSize = nEOICode + 1;
+ }
+ else
+ {
+ if( nTableSize == static_cast<sal_uInt16>( ( 1 << nCodeSize ) - 1 ) )
+ nCodeSize++;
+
+ p = pTable.get() + ( nTableSize++ );
+ p->pBrother = pPrefix->pFirstChild;
+ pPrefix->pFirstChild = p;
+ p->nValue = nV;
+ p->pFirstChild = nullptr;
+ }
+
+ pPrefix = pTable.get() + nV;
+ }
+ }
+}
+
+void PSWriter::EndCompression()
+{
+ if( pPrefix )
+ WriteBits( pPrefix->nCode, nCodeSize );
+
+ WriteBits( nEOICode, nCodeSize );
+ pTable.reset();
+}
+
+sal_uInt8* PSWriter::ImplSearchEntry( sal_uInt8* pSource, sal_uInt8 const * pDest, sal_uInt32 nComp, sal_uInt32 nSize )
+{
+ while ( nComp-- >= nSize )
+ {
+ sal_uInt64 i;
+ for ( i = 0; i < nSize; i++ )
+ {
+ if ( ( pSource[i]&~0x20 ) != ( pDest[i]&~0x20 ) )
+ break;
+ }
+ if ( i == nSize )
+ return pSource;
+ pSource++;
+ }
+ return nullptr;
+}
+
+bool PSWriter::ImplGetBoundingBox( double* nNumb, sal_uInt8* pSource, sal_uInt32 nSize )
+{
+ bool bRetValue = false;
+ sal_uInt32 nBytesRead;
+
+ if ( nSize < 256 ) // we assume that the file is greater than 256 bytes
+ return false;
+
+ if ( nSize < POSTSCRIPT_BOUNDINGSEARCH )
+ nBytesRead = nSize;
+ else
+ nBytesRead = POSTSCRIPT_BOUNDINGSEARCH;
+
+ sal_uInt8* pDest = ImplSearchEntry( pSource, reinterpret_cast<sal_uInt8 const *>("%%BoundingBox:"), nBytesRead, 14 );
+ if ( pDest )
+ {
+ int nSecurityCount = 100; // only 100 bytes following the bounding box will be checked
+ nNumb[0] = nNumb[1] = nNumb[2] = nNumb[3] = 0;
+ pDest += 14;
+ for ( int i = 0; ( i < 4 ) && nSecurityCount; i++ )
+ {
+ int nDivision = 1;
+ bool bDivision = false;
+ bool bNegative = false;
+ bool bValid = true;
+
+ while ( ( --nSecurityCount ) && ( ( *pDest == ' ' ) || ( *pDest == 0x9 ) ) )
+ pDest++;
+ sal_uInt8 nByte = *pDest;
+ while ( nSecurityCount && ( nByte != ' ' ) && ( nByte != 0x9 ) && ( nByte != 0xd ) && ( nByte != 0xa ) )
+ {
+ switch ( nByte )
+ {
+ case '.' :
+ if ( bDivision )
+ bValid = false;
+ else
+ bDivision = true;
+ break;
+ case '-' :
+ bNegative = true;
+ break;
+ default :
+ if ( ( nByte < '0' ) || ( nByte > '9' ) )
+ nSecurityCount = 1; // error parsing the bounding box values
+ else if ( bValid )
+ {
+ if ( bDivision )
+ nDivision*=10;
+ nNumb[i] *= 10;
+ nNumb[i] += nByte - '0';
+ }
+ break;
+ }
+ nSecurityCount--;
+ nByte = *(++pDest);
+ }
+ if ( bNegative )
+ nNumb[i] = -nNumb[i];
+ if ( bDivision && ( nDivision != 1 ) )
+ nNumb[i] /= nDivision;
+ }
+ if ( nSecurityCount)
+ bRetValue = true;
+ }
+ return bRetValue;
+}
+
+//================== GraphicExport - the exported function ===================
+
+bool ExportEpsGraphic(SvStream & rStream, const Graphic & rGraphic, FilterConfigItem* pFilterConfigItem)
+{
+ PSWriter aPSWriter;
+ return aPSWriter.WritePS(rGraphic, rStream, pFilterConfigItem);
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/vcl/source/filter/etiff/etiff.cxx b/vcl/source/filter/etiff/etiff.cxx
new file mode 100644
index 000000000..b34accab8
--- /dev/null
+++ b/vcl/source/filter/etiff/etiff.cxx
@@ -0,0 +1,586 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+
+#include <tools/stream.hxx>
+#include <vcl/graph.hxx>
+#include <vcl/outdev.hxx>
+#include <vcl/BitmapReadAccess.hxx>
+#include <vcl/FilterConfigItem.hxx>
+#include <com/sun/star/task/XStatusIndicator.hpp>
+#include <filter/TiffWriter.hxx>
+
+#define NewSubfileType 254
+#define ImageWidth 256
+#define ImageLength 257
+#define BitsPerSample 258
+#define Compression 259
+#define PhotometricInterpretation 262
+#define StripOffsets 273
+#define SamplesPerPixel 277
+#define RowsPerStrip 278
+#define StripByteCounts 279
+#define XResolution 282
+#define YResolution 283
+#define PlanarConfiguration 284
+#define ResolutionUnit 296
+#define ColorMap 320
+
+namespace {
+
+struct TIFFLZWCTreeNode
+{
+
+ TIFFLZWCTreeNode* pBrother; // next node with the same father
+ TIFFLZWCTreeNode* pFirstChild; // first son
+ sal_uInt16 nCode; // The code for the string of pixel values, which arises if... <missing comment>
+ sal_uInt16 nValue; // pixel value
+};
+
+
+class TIFFWriter
+{
+private:
+
+ SvStream& m_rOStm;
+ sal_uInt32 mnStreamOfs;
+
+ bool mbStatus;
+ BitmapReadAccess* mpAcc;
+
+ sal_uInt32 mnWidth, mnHeight, mnColors;
+ sal_uInt32 mnCurAllPictHeight;
+ sal_uInt32 mnSumOfAllPictHeight;
+ sal_uInt32 mnBitsPerPixel;
+ sal_uInt32 mnLastPercent;
+
+ sal_uInt32 mnLatestIfdPos;
+ sal_uInt16 mnTagCount; // number of tags already written
+ sal_uInt32 mnCurrentTagCountPos; // offset to the position where the current
+ // tag count is to insert
+
+ sal_uInt32 mnXResPos; // if != 0 this DWORDs stores the
+ sal_uInt32 mnYResPos; // actual streamposition of the
+ sal_uInt32 mnPalPos; // Tag Entry
+ sal_uInt32 mnBitmapPos;
+ sal_uInt32 mnStripByteCountPos;
+
+ std::unique_ptr<TIFFLZWCTreeNode[]> pTable;
+ TIFFLZWCTreeNode* pPrefix;
+ sal_uInt16 nDataSize;
+ sal_uInt16 nClearCode;
+ sal_uInt16 nEOICode;
+ sal_uInt16 nTableSize;
+ sal_uInt16 nCodeSize;
+ sal_uInt32 nOffset;
+ sal_uInt32 dwShift;
+
+ css::uno::Reference< css::task::XStatusIndicator > xStatusIndicator;
+
+ void ImplCallback( sal_uInt32 nPercent );
+ bool ImplWriteHeader( bool bMultiPage );
+ void ImplWritePalette();
+ void ImplWriteBody();
+ void ImplWriteTag( sal_uInt16 TagID, sal_uInt16 DataType, sal_uInt32 NumberOfItems, sal_uInt32 Value);
+ void ImplWriteResolution( sal_uInt64 nStreamPos, sal_uInt32 nResolutionUnit );
+ void StartCompression();
+ void Compress( sal_uInt8 nSrc );
+ void EndCompression();
+ inline void WriteBits( sal_uInt16 nCode, sal_uInt16 nCodeLen );
+
+public:
+
+ explicit TIFFWriter(SvStream &rStream);
+
+ bool WriteTIFF( const Graphic& rGraphic, FilterConfigItem const * pFilterConfigItem );
+};
+
+}
+
+TIFFWriter::TIFFWriter(SvStream &rStream)
+ : m_rOStm(rStream)
+ , mnStreamOfs(0)
+ , mbStatus(true)
+ , mpAcc(nullptr)
+ , mnWidth(0)
+ , mnHeight(0)
+ , mnColors(0)
+ , mnCurAllPictHeight(0)
+ , mnSumOfAllPictHeight(0)
+ , mnBitsPerPixel(0)
+ , mnLastPercent(0)
+ , mnLatestIfdPos(0)
+ , mnTagCount(0)
+ , mnCurrentTagCountPos(0)
+ , mnXResPos(0)
+ , mnYResPos(0)
+ , mnPalPos(0)
+ , mnBitmapPos(0)
+ , mnStripByteCountPos(0)
+ , pPrefix(nullptr)
+ , nDataSize(0)
+ , nClearCode(0)
+ , nEOICode(0)
+ , nTableSize(0)
+ , nCodeSize(0)
+ , nOffset(0)
+ , dwShift(0)
+{
+}
+
+
+bool TIFFWriter::WriteTIFF( const Graphic& rGraphic, FilterConfigItem const * pFilterConfigItem)
+{
+ if ( pFilterConfigItem )
+ {
+ xStatusIndicator = pFilterConfigItem->GetStatusIndicator();
+ if ( xStatusIndicator.is() )
+ {
+ xStatusIndicator->start( OUString(), 100 );
+ }
+ }
+
+ const SvStreamEndian nOldFormat = m_rOStm.GetEndian();
+ mnStreamOfs = m_rOStm.Tell();
+
+ // we will use the BIG Endian Mode
+ // TIFF header
+ m_rOStm.SetEndian( SvStreamEndian::BIG );
+ m_rOStm.WriteUInt32( 0x4d4d002a ); // TIFF identifier
+ mnLatestIfdPos = m_rOStm.Tell();
+ m_rOStm.WriteUInt32( 0 );
+
+ if( mbStatus )
+ {
+ Animation aAnimation = rGraphic.IsAnimated() ? rGraphic.GetAnimation() : Animation();
+ if (!rGraphic.IsAnimated())
+ aAnimation.Insert(AnimationBitmap(rGraphic.GetBitmapEx(), Point(), Size()));
+
+ for (size_t i = 0; i < aAnimation.Count(); ++i)
+ mnSumOfAllPictHeight += aAnimation.Get(i).maBitmapEx.GetSizePixel().Height();
+
+ for (size_t i = 0; mbStatus && i < aAnimation.Count(); ++i)
+ {
+ mnPalPos = 0;
+ const AnimationBitmap& rAnimationBitmap = aAnimation.Get( i );
+ Bitmap aBmp = rAnimationBitmap.maBitmapEx.GetBitmap();
+ mpAcc = aBmp.AcquireReadAccess();
+ if ( mpAcc )
+ {
+ mnBitsPerPixel = vcl::pixelFormatBitCount(aBmp.getPixelFormat());
+
+ // export code below only handles four discrete cases
+ mnBitsPerPixel =
+ mnBitsPerPixel <= 1 ? 1 : mnBitsPerPixel <= 4 ? 4 : mnBitsPerPixel <= 8 ? 8 : 24;
+
+ if ( ImplWriteHeader( aAnimation.Count() > 0 ) )
+ {
+ Size aDestMapSize( 300, 300 );
+ const MapMode& aMapMode( aBmp.GetPrefMapMode() );
+ if ( aMapMode.GetMapUnit() != MapUnit::MapPixel )
+ {
+ const Size aPrefSize( rGraphic.GetPrefSize() );
+ aDestMapSize = OutputDevice::LogicToLogic(aPrefSize, aMapMode, MapMode(MapUnit::MapInch));
+ }
+ ImplWriteResolution( mnXResPos, aDestMapSize.Width() );
+ ImplWriteResolution( mnYResPos, aDestMapSize.Height() );
+ if ( mnPalPos )
+ ImplWritePalette();
+ ImplWriteBody();
+ }
+ sal_uInt32 nCurPos = m_rOStm.Tell();
+ m_rOStm.Seek( mnCurrentTagCountPos );
+ m_rOStm.WriteUInt16( mnTagCount );
+ m_rOStm.Seek( nCurPos );
+
+ Bitmap::ReleaseAccess( mpAcc );
+ }
+ else
+ mbStatus = false;
+ }
+ }
+ m_rOStm.SetEndian( nOldFormat );
+
+ if ( xStatusIndicator.is() )
+ xStatusIndicator->end();
+
+ return mbStatus;
+}
+
+
+void TIFFWriter::ImplCallback( sal_uInt32 nPercent )
+{
+ if ( xStatusIndicator.is() )
+ {
+ if( nPercent >= mnLastPercent + 3 )
+ {
+ mnLastPercent = nPercent;
+ if ( nPercent <= 100 )
+ xStatusIndicator->setValue( nPercent );
+ }
+ }
+}
+
+
+bool TIFFWriter::ImplWriteHeader( bool bMultiPage )
+{
+ mnTagCount = 0;
+ mnWidth = mpAcc->Width();
+ mnHeight = mpAcc->Height();
+
+ if ( mnWidth && mnHeight && mnBitsPerPixel && mbStatus )
+ {
+ sal_uInt32 nCurrentPos = m_rOStm.Tell();
+ m_rOStm.Seek( mnLatestIfdPos );
+ m_rOStm.WriteUInt32( nCurrentPos - mnStreamOfs ); // offset to the IFD
+ m_rOStm.Seek( nCurrentPos );
+
+ // (OFS8) TIFF image file directory (IFD)
+ mnCurrentTagCountPos = m_rOStm.Tell();
+ m_rOStm.WriteUInt16( 0 ); // the number of tangents to insert later
+
+ sal_uInt32 nSubFileFlags = 0;
+ if ( bMultiPage )
+ nSubFileFlags |= 2;
+ ImplWriteTag( NewSubfileType, 4, 1, nSubFileFlags );
+ ImplWriteTag( ImageWidth, 4, 1, mnWidth );
+ ImplWriteTag( ImageLength, 4, 1, mnHeight);
+ ImplWriteTag( BitsPerSample, 3, 1, ( mnBitsPerPixel == 24 ) ? 8 : mnBitsPerPixel );
+ ImplWriteTag( Compression, 3, 1, 5 );
+ sal_uInt8 nTemp;
+ switch ( mnBitsPerPixel )
+ {
+ case 1 :
+ nTemp = 1;
+ break;
+ case 4 :
+ case 8 :
+ nTemp = 3;
+ break;
+ case 24:
+ nTemp = 2;
+ break;
+ default:
+ nTemp = 0; // -Wall set a default...
+ break;
+ }
+ ImplWriteTag( PhotometricInterpretation, 3, 1, nTemp );
+ mnBitmapPos = m_rOStm.Tell();
+ ImplWriteTag( StripOffsets, 4, 1, 0 );
+ ImplWriteTag( SamplesPerPixel, 3, 1, ( mnBitsPerPixel == 24 ) ? 3 : 1 );
+ ImplWriteTag( RowsPerStrip, 4, 1, mnHeight );
+ mnStripByteCountPos = m_rOStm.Tell();
+ ImplWriteTag( StripByteCounts, 4, 1, ( ( mnWidth * mnBitsPerPixel * mnHeight ) + 7 ) >> 3 );
+ mnXResPos = m_rOStm.Tell();
+ ImplWriteTag( XResolution, 5, 1, 0 );
+ mnYResPos = m_rOStm.Tell();
+ ImplWriteTag( YResolution, 5, 1, 0 );
+ if ( mnBitsPerPixel != 1 )
+ ImplWriteTag( PlanarConfiguration, 3, 1, 1 ); // ( RGB ORDER )
+ ImplWriteTag( ResolutionUnit, 3, 1, 2); // Resolution Unit is Inch
+ if ( ( mnBitsPerPixel == 4 ) || ( mnBitsPerPixel == 8 ) )
+ {
+ mnColors = mpAcc->GetPaletteEntryCount();
+ mnPalPos = m_rOStm.Tell();
+ ImplWriteTag( ColorMap, 3, 3 * mnColors, 0 );
+ }
+
+ // and last we write zero to close the num dir entries list
+ mnLatestIfdPos = m_rOStm.Tell();
+ m_rOStm.WriteUInt32( 0 ); // there are no more IFD
+ }
+ else
+ mbStatus = false;
+
+ return mbStatus;
+}
+
+
+void TIFFWriter::ImplWritePalette()
+{
+ sal_uInt64 nCurrentPos = m_rOStm.Tell();
+ m_rOStm.Seek( mnPalPos + 8 ); // the palette tag entry needs the offset
+ m_rOStm.WriteUInt32( nCurrentPos - mnStreamOfs ); // to the palette colors
+ m_rOStm.Seek( nCurrentPos );
+
+ for ( sal_uInt32 i = 0; i < mnColors; i++ )
+ {
+ const BitmapColor& rColor = mpAcc->GetPaletteColor( i );
+ m_rOStm.WriteUInt16( rColor.GetRed() << 8 );
+ }
+ for ( sal_uInt32 i = 0; i < mnColors; i++ )
+ {
+ const BitmapColor& rColor = mpAcc->GetPaletteColor( i );
+ m_rOStm.WriteUInt16( rColor.GetGreen() << 8 );
+ }
+ for ( sal_uInt32 i = 0; i < mnColors; i++ )
+ {
+ const BitmapColor& rColor = mpAcc->GetPaletteColor( i );
+ m_rOStm.WriteUInt16( rColor.GetBlue() << 8 );
+ }
+}
+
+
+void TIFFWriter::ImplWriteBody()
+{
+ sal_uInt8 nTemp = 0;
+ sal_uInt8 nShift;
+ sal_uInt32 j, x, y;
+
+ sal_uInt64 nGfxBegin = m_rOStm.Tell();
+ m_rOStm.Seek( mnBitmapPos + 8 ); // the strip offset tag entry needs the offset
+ m_rOStm.WriteUInt32( nGfxBegin - mnStreamOfs ); // to the bitmap data
+ m_rOStm.Seek( nGfxBegin );
+
+ StartCompression();
+
+ switch( mnBitsPerPixel )
+ {
+ case 24 :
+ {
+ for ( y = 0; y < mnHeight; y++, mnCurAllPictHeight++ )
+ {
+ ImplCallback( 100 * mnCurAllPictHeight / mnSumOfAllPictHeight );
+ Scanline pScanline = mpAcc->GetScanline( y );
+ for ( x = 0; x < mnWidth; x++ )
+ {
+ const BitmapColor& rColor = mpAcc->GetPixelFromData( pScanline, x );
+ Compress( rColor.GetRed() );
+ Compress( rColor.GetGreen() );
+ Compress( rColor.GetBlue() );
+ }
+ }
+ }
+ break;
+
+ case 8 :
+ {
+ for ( y = 0; y < mnHeight; y++, mnCurAllPictHeight++ )
+ {
+ ImplCallback( 100 * mnCurAllPictHeight / mnSumOfAllPictHeight );
+ Scanline pScanline = mpAcc->GetScanline( y );
+ for ( x = 0; x < mnWidth; x++ )
+ {
+ Compress( mpAcc->GetIndexFromData( pScanline, x ) );
+ }
+ }
+ }
+ break;
+
+ case 4 :
+ {
+ for ( nShift = 0, y = 0; y < mnHeight; y++, mnCurAllPictHeight++ )
+ {
+ ImplCallback( 100 * mnCurAllPictHeight / mnSumOfAllPictHeight );
+ Scanline pScanline = mpAcc->GetScanline( y );
+ for ( x = 0; x < mnWidth; x++, nShift++ )
+ {
+ if (!( nShift & 1 ))
+ nTemp = ( mpAcc->GetIndexFromData( pScanline, x ) << 4 );
+ else
+ Compress( static_cast<sal_uInt8>( nTemp | ( mpAcc->GetIndexFromData( pScanline, x ) & 0xf ) ) );
+ }
+ if ( nShift & 1 )
+ Compress( nTemp );
+ }
+ }
+ break;
+
+ case 1 :
+ {
+ j = 1;
+ for ( y = 0; y < mnHeight; y++, mnCurAllPictHeight++ )
+ {
+ ImplCallback( 100 * mnCurAllPictHeight / mnSumOfAllPictHeight );
+ Scanline pScanline = mpAcc->GetScanline( y );
+ for ( x = 0; x < mnWidth; x++)
+ {
+ j <<= 1;
+ j |= ( ( ~mpAcc->GetIndexFromData( pScanline, x ) ) & 1 );
+ if ( j & 0x100 )
+ {
+ Compress( static_cast<sal_uInt8>(j) );
+ j = 1;
+ }
+ }
+ if ( j != 1 )
+ {
+ Compress( static_cast<sal_uInt8>(j << ( ( ( x & 7) ^ 7 ) + 1 ) ) );
+ j = 1;
+ }
+ }
+ }
+ break;
+
+ default:
+ {
+ mbStatus = false;
+ }
+ break;
+ }
+
+ EndCompression();
+
+ if ( mnStripByteCountPos && mbStatus )
+ {
+ sal_uInt64 nGfxEnd = m_rOStm.Tell();
+ m_rOStm.Seek( mnStripByteCountPos + 8 );
+ m_rOStm.WriteUInt32( nGfxEnd - nGfxBegin ); // mnStripByteCountPos needs the size of the compression data
+ m_rOStm.Seek( nGfxEnd );
+ }
+}
+
+
+void TIFFWriter::ImplWriteResolution( sal_uInt64 nStreamPos, sal_uInt32 nResolutionUnit )
+{
+ sal_uInt64 nCurrentPos = m_rOStm.Tell();
+ m_rOStm.Seek( nStreamPos + 8 );
+ m_rOStm.WriteUInt32( nCurrentPos - mnStreamOfs );
+ m_rOStm.Seek( nCurrentPos );
+ m_rOStm.WriteUInt32( 1 );
+ m_rOStm.WriteUInt32( nResolutionUnit );
+}
+
+
+void TIFFWriter::ImplWriteTag( sal_uInt16 nTagID, sal_uInt16 nDataType, sal_uInt32 nNumberOfItems, sal_uInt32 nValue)
+{
+ mnTagCount++;
+
+ m_rOStm.WriteUInt16( nTagID );
+ m_rOStm.WriteUInt16( nDataType );
+ m_rOStm.WriteUInt32( nNumberOfItems );
+ if ( nDataType == 3 )
+ nValue <<=16; // in Big Endian Mode WORDS needed to be shifted to a DWORD
+ m_rOStm.WriteUInt32( nValue );
+}
+
+
+inline void TIFFWriter::WriteBits( sal_uInt16 nCode, sal_uInt16 nCodeLen )
+{
+ dwShift |= ( nCode << ( nOffset - nCodeLen ) );
+ nOffset -= nCodeLen;
+ while ( nOffset < 24 )
+ {
+ m_rOStm.WriteUChar( dwShift >> 24 );
+ dwShift <<= 8;
+ nOffset += 8;
+ }
+ if ( nCode == 257 && nOffset != 32 )
+ {
+ m_rOStm.WriteUChar( dwShift >> 24 );
+ }
+}
+
+
+void TIFFWriter::StartCompression()
+{
+ sal_uInt16 i;
+ nDataSize = 8;
+
+ nClearCode = 1 << nDataSize;
+ nEOICode = nClearCode + 1;
+ nTableSize = nEOICode + 1;
+ nCodeSize = nDataSize + 1;
+
+ nOffset = 32; // number of free bits in dwShift
+ dwShift = 0;
+
+ pTable.reset(new TIFFLZWCTreeNode[ 4096 ]);
+
+ for ( i = 0; i < 4096; i++)
+ {
+ pTable[ i ].pBrother = pTable[ i ].pFirstChild = nullptr;
+ pTable[ i ].nCode = i;
+ pTable[ i ].nValue = static_cast<sal_uInt8>( i );
+ }
+
+ pPrefix = nullptr;
+ WriteBits( nClearCode, nCodeSize );
+}
+
+
+void TIFFWriter::Compress( sal_uInt8 nCompThis )
+{
+ TIFFLZWCTreeNode* p;
+ sal_uInt16 i;
+ sal_uInt8 nV;
+
+ if( !pPrefix )
+ {
+ pPrefix = &pTable[nCompThis];
+ }
+ else
+ {
+ nV = nCompThis;
+ for( p = pPrefix->pFirstChild; p != nullptr; p = p->pBrother )
+ {
+ if ( p->nValue == nV )
+ break;
+ }
+
+ if( p )
+ pPrefix = p;
+ else
+ {
+ WriteBits( pPrefix->nCode, nCodeSize );
+
+ if ( nTableSize == 409 )
+ {
+ WriteBits( nClearCode, nCodeSize );
+
+ for ( i = 0; i < nClearCode; i++ )
+ pTable[ i ].pFirstChild = nullptr;
+
+ nCodeSize = nDataSize + 1;
+ nTableSize = nEOICode + 1;
+ }
+ else
+ {
+ if( nTableSize == static_cast<sal_uInt16>( ( 1 << nCodeSize ) - 1 ) )
+ nCodeSize++;
+
+ p = &pTable[ nTableSize++ ];
+ p->pBrother = pPrefix->pFirstChild;
+ pPrefix->pFirstChild = p;
+ p->nValue = nV;
+ p->pFirstChild = nullptr;
+ }
+
+ pPrefix = &pTable[nV];
+ }
+ }
+}
+
+
+void TIFFWriter::EndCompression()
+{
+ if( pPrefix )
+ WriteBits( pPrefix->nCode, nCodeSize );
+
+ WriteBits( nEOICode, nCodeSize );
+ pTable.reset();
+}
+
+bool ExportTiffGraphicImport(SvStream & rStream, const Graphic & rGraphic, const FilterConfigItem* pFilterConfigItem)
+{
+ TIFFWriter aWriter(rStream);
+ return aWriter.WriteTIFF( rGraphic, pFilterConfigItem );
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/vcl/source/filter/graphicfilter.cxx b/vcl/source/filter/graphicfilter.cxx
new file mode 100644
index 000000000..d208c7d81
--- /dev/null
+++ b/vcl/source/filter/graphicfilter.cxx
@@ -0,0 +1,2056 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <config_folders.h>
+
+#include <sal/log.hxx>
+#include <comphelper/processfactory.hxx>
+#include <comphelper/propertyvalue.hxx>
+#include <comphelper/threadpool.hxx>
+#include <cppuhelper/implbase.hxx>
+#include <tools/fract.hxx>
+#include <unotools/configmgr.hxx>
+#include <tools/stream.hxx>
+#include <tools/urlobj.hxx>
+#include <tools/zcodec.hxx>
+#include <fltcall.hxx>
+#include <vcl/salctype.hxx>
+#include <vcl/filter/PngImageReader.hxx>
+#include <vcl/filter/SvmWriter.hxx>
+#include <vcl/pngwrite.hxx>
+#include <vcl/vectorgraphicdata.hxx>
+#include <vcl/virdev.hxx>
+#include <impgraph.hxx>
+#include <vcl/svapp.hxx>
+#include <osl/file.hxx>
+#include <vcl/graphicfilter.hxx>
+#include <vcl/FilterConfigItem.hxx>
+#include <vcl/wmf.hxx>
+#include "igif/gifread.hxx"
+#include <vcl/pdfread.hxx>
+#include "jpeg/jpeg.hxx"
+#include "png/png.hxx"
+#include "ixbm/xbmread.hxx"
+#include <filter/XpmReader.hxx>
+#include <filter/TiffReader.hxx>
+#include <filter/TiffWriter.hxx>
+#include <filter/TgaReader.hxx>
+#include <filter/PictReader.hxx>
+#include <filter/MetReader.hxx>
+#include <filter/RasReader.hxx>
+#include <filter/PcxReader.hxx>
+#include <filter/EpsReader.hxx>
+#include <filter/EpsWriter.hxx>
+#include <filter/PsdReader.hxx>
+#include <filter/PcdReader.hxx>
+#include <filter/PbmReader.hxx>
+#include <filter/DxfReader.hxx>
+#include <filter/GifWriter.hxx>
+#include <filter/BmpReader.hxx>
+#include <filter/BmpWriter.hxx>
+#include <filter/WebpReader.hxx>
+#include <filter/WebpWriter.hxx>
+#include <osl/module.hxx>
+#include <com/sun/star/uno/Reference.h>
+#include <com/sun/star/awt/Size.hpp>
+#include <com/sun/star/uno/XInterface.hpp>
+#include <com/sun/star/io/XActiveDataSource.hpp>
+#include <com/sun/star/io/XOutputStream.hpp>
+#include <com/sun/star/svg/XSVGWriter.hpp>
+#include <com/sun/star/xml/sax/XDocumentHandler.hpp>
+#include <com/sun/star/xml/sax/Writer.hpp>
+#include <unotools/ucbstreamhelper.hxx>
+#include <rtl/bootstrap.hxx>
+#include <tools/svlibrary.h>
+#include <comphelper/string.hxx>
+#include <unotools/ucbhelper.hxx>
+#include <vector>
+#include <memory>
+#include <mutex>
+#include <string_view>
+#include <o3tl/string_view.hxx>
+#include <vcl/TypeSerializer.hxx>
+
+#include "FilterConfigCache.hxx"
+#include "graphicfilter_internal.hxx"
+
+#include <graphic/GraphicFormatDetector.hxx>
+#include <graphic/GraphicReader.hxx>
+
+// Support for GfxLinkType::NativeWebp is so far disabled,
+// as enabling it would write .webp images e.g. to .odt documents,
+// making those images unreadable for older readers. So for now
+// disable the support so that .webp images will be written out as .png,
+// and somewhen later enable the support unconditionally.
+static bool supportNativeWebp()
+{
+ const char* const testname = getenv("LO_TESTNAME");
+ if(testname == nullptr)
+ return false;
+ // Enable support only for those unittests that test it.
+ if( std::string_view("_anonymous_namespace___GraphicTest__testUnloadedGraphicLoading_") == testname
+ || std::string_view("VclFiltersTest__testExportImport_") == testname
+ || o3tl::starts_with(std::string_view(testname), "WebpFilterTest__"))
+ return true;
+ return false;
+}
+
+static std::vector< GraphicFilter* > gaFilterHdlList;
+
+static std::mutex& getListMutex()
+{
+ static std::mutex s_aListProtection;
+ return s_aListProtection;
+}
+
+namespace {
+
+class ImpFilterOutputStream : public ::cppu::WeakImplHelper< css::io::XOutputStream >
+{
+ SvStream& mrStm;
+
+ virtual void SAL_CALL writeBytes( const css::uno::Sequence< sal_Int8 >& rData ) override
+ { mrStm.WriteBytes(rData.getConstArray(), rData.getLength()); }
+ virtual void SAL_CALL flush() override
+ { mrStm.FlushBuffer(); }
+ virtual void SAL_CALL closeOutput() override {}
+
+public:
+
+ explicit ImpFilterOutputStream( SvStream& rStm ) : mrStm( rStm ) {}
+};
+
+}
+
+// Helper functions
+
+sal_uInt8* ImplSearchEntry( sal_uInt8* pSource, sal_uInt8 const * pDest, sal_uLong nComp, sal_uLong nSize )
+{
+ while ( nComp-- >= nSize )
+ {
+ sal_uLong i;
+ for ( i = 0; i < nSize; i++ )
+ {
+ if ( ( pSource[i]&~0x20 ) != ( pDest[i]&~0x20 ) )
+ break;
+ }
+ if ( i == nSize )
+ return pSource;
+ pSource++;
+ }
+ return nullptr;
+}
+
+static OUString ImpGetExtension( std::u16string_view rPath )
+{
+ OUString aExt;
+ INetURLObject aURL( rPath );
+ aExt = aURL.GetFileExtension().toAsciiUpperCase();
+ return aExt;
+}
+
+bool isPCT(SvStream& rStream, sal_uLong nStreamPos, sal_uLong nStreamLen)
+{
+ sal_uInt8 sBuf[3];
+ // store number format
+ SvStreamEndian oldNumberFormat = rStream.GetEndian();
+ sal_uInt32 nOffset; // in MS documents the pict format is used without the first 512 bytes
+ for ( nOffset = 0; ( nOffset <= 512 ) && ( ( nStreamPos + nOffset + 14 ) <= nStreamLen ); nOffset += 512 )
+ {
+ short y1,x1,y2,x2;
+ bool bdBoxOk = true;
+
+ rStream.Seek( nStreamPos + nOffset);
+ // size of the pict in version 1 pict ( 2bytes) : ignored
+ rStream.SeekRel(2);
+ // bounding box (bytes 2 -> 9)
+ rStream.SetEndian(SvStreamEndian::BIG);
+ rStream.ReadInt16( y1 ).ReadInt16( x1 ).ReadInt16( y2 ).ReadInt16( x2 );
+ rStream.SetEndian(oldNumberFormat); // reset format
+
+ if (x1 > x2 || y1 > y2 || // bad bdbox
+ (x1 == x2 && y1 == y2) || // 1 pixel picture
+ x2-x1 > 2048 || y2-y1 > 2048 ) // picture abnormally big
+ bdBoxOk = false;
+
+ // read version op
+ rStream.ReadBytes(sBuf, 3);
+ // see http://developer.apple.com/legacy/mac/library/documentation/mac/pdf/Imaging_With_QuickDraw/Appendix_A.pdf
+ // normal version 2 - page A23 and A24
+ if ( sBuf[ 0 ] == 0x00 && sBuf[ 1 ] == 0x11 && sBuf[ 2 ] == 0x02)
+ return true;
+ // normal version 1 - page A25
+ else if (sBuf[ 0 ] == 0x11 && sBuf[ 1 ] == 0x01 && bdBoxOk)
+ return true;
+ }
+ return false;
+}
+
+ErrCode GraphicFilter::ImpTestOrFindFormat( std::u16string_view rPath, SvStream& rStream, sal_uInt16& rFormat )
+{
+ // determine or check the filter/format by reading into it
+ if( rFormat == GRFILTER_FORMAT_DONTKNOW )
+ {
+ OUString aFormatExt;
+ if (vcl::peekGraphicFormat(rStream, aFormatExt, false))
+ {
+ rFormat = pConfig->GetImportFormatNumberForExtension( aFormatExt );
+ if( rFormat != GRFILTER_FORMAT_DONTKNOW )
+ return ERRCODE_NONE;
+ }
+ // determine filter by file extension
+ if( !rPath.empty() )
+ {
+ OUString aExt( ImpGetExtension( rPath ) );
+ rFormat = pConfig->GetImportFormatNumberForExtension( aExt );
+ if( rFormat != GRFILTER_FORMAT_DONTKNOW )
+ return ERRCODE_NONE;
+ }
+ return ERRCODE_GRFILTER_FORMATERROR;
+ }
+ else
+ {
+ OUString aTmpStr( pConfig->GetImportFormatExtension( rFormat ) );
+ aTmpStr = aTmpStr.toAsciiUpperCase();
+ if (!vcl::peekGraphicFormat(rStream, aTmpStr, true))
+ return ERRCODE_GRFILTER_FORMATERROR;
+ if ( pConfig->GetImportFormatExtension( rFormat ).equalsIgnoreAsciiCase( "pcd" ) )
+ {
+ sal_Int32 nBase = 2; // default Base0
+ if ( pConfig->GetImportFilterType( rFormat ).equalsIgnoreAsciiCase( "pcd_Photo_CD_Base4" ) )
+ nBase = 1;
+ else if ( pConfig->GetImportFilterType( rFormat ).equalsIgnoreAsciiCase( "pcd_Photo_CD_Base16" ) )
+ nBase = 0;
+ FilterConfigItem aFilterConfigItem( u"Office.Common/Filter/Graphic/Import/PCD" );
+ aFilterConfigItem.WriteInt32( "Resolution", nBase );
+ }
+ }
+
+ return ERRCODE_NONE;
+}
+
+static Graphic ImpGetScaledGraphic( const Graphic& rGraphic, FilterConfigItem& rConfigItem )
+{
+ Graphic aGraphic;
+
+ sal_Int32 nLogicalWidth = rConfigItem.ReadInt32( "LogicalWidth", 0 );
+ sal_Int32 nLogicalHeight = rConfigItem.ReadInt32( "LogicalHeight", 0 );
+
+ if ( rGraphic.GetType() != GraphicType::NONE )
+ {
+ sal_Int32 nMode = rConfigItem.ReadInt32( "ExportMode", -1 );
+
+ if ( nMode == -1 ) // the property is not there, this is possible, if the graphic filter
+ { // is called via UnoGraphicExporter and not from a graphic export Dialog
+ nMode = 0; // then we are defaulting this mode to 0
+ if ( nLogicalWidth || nLogicalHeight )
+ nMode = 2;
+ }
+
+ Size aOriginalSize;
+ Size aPrefSize( rGraphic.GetPrefSize() );
+ MapMode aPrefMapMode( rGraphic.GetPrefMapMode() );
+ if (aPrefMapMode.GetMapUnit() == MapUnit::MapPixel)
+ aOriginalSize = Application::GetDefaultDevice()->PixelToLogic(aPrefSize, MapMode(MapUnit::Map100thMM));
+ else
+ aOriginalSize = OutputDevice::LogicToLogic(aPrefSize, aPrefMapMode, MapMode(MapUnit::Map100thMM));
+ if ( !nLogicalWidth )
+ nLogicalWidth = aOriginalSize.Width();
+ if ( !nLogicalHeight )
+ nLogicalHeight = aOriginalSize.Height();
+ if( rGraphic.GetType() == GraphicType::Bitmap )
+ {
+
+ // Resolution is set
+ if( nMode == 1 )
+ {
+ BitmapEx aBitmap( rGraphic.GetBitmapEx() );
+ MapMode aMap( MapUnit::Map100thInch );
+
+ sal_Int32 nDPI = rConfigItem.ReadInt32( "Resolution", 75 );
+ Fraction aFrac( 1, std::clamp( nDPI, sal_Int32(75), sal_Int32(600) ) );
+
+ aMap.SetScaleX( aFrac );
+ aMap.SetScaleY( aFrac );
+
+ Size aOldSize = aBitmap.GetSizePixel();
+ aGraphic = rGraphic;
+ aGraphic.SetPrefMapMode( aMap );
+ aGraphic.SetPrefSize( Size( aOldSize.Width() * 100,
+ aOldSize.Height() * 100 ) );
+ }
+ // Size is set
+ else if( nMode == 2 )
+ {
+ aGraphic = rGraphic;
+ aGraphic.SetPrefMapMode( MapMode( MapUnit::Map100thMM ) );
+ aGraphic.SetPrefSize( Size( nLogicalWidth, nLogicalHeight ) );
+ }
+ else
+ aGraphic = rGraphic;
+
+ sal_Int32 nColors = rConfigItem.ReadInt32( "Color", 0 );
+ if ( nColors ) // graphic conversion necessary ?
+ {
+ BitmapEx aBmpEx( aGraphic.GetBitmapEx() );
+ aBmpEx.Convert( static_cast<BmpConversion>(nColors) ); // the entries in the xml section have the same meaning as
+ aGraphic = aBmpEx; // they have in the BmpConversion enum, so it should be
+ } // allowed to cast them
+ }
+ else
+ {
+ if( ( nMode == 1 ) || ( nMode == 2 ) )
+ {
+ GDIMetaFile aMtf( rGraphic.GetGDIMetaFile() );
+ Size aNewSize( OutputDevice::LogicToLogic(Size(nLogicalWidth, nLogicalHeight), MapMode(MapUnit::Map100thMM), aMtf.GetPrefMapMode()) );
+
+ if( aNewSize.Width() && aNewSize.Height() )
+ {
+ const Size aPreferredSize( aMtf.GetPrefSize() );
+ aMtf.Scale( Fraction( aNewSize.Width(), aPreferredSize.Width() ),
+ Fraction( aNewSize.Height(), aPreferredSize.Height() ) );
+ }
+ aGraphic = Graphic( aMtf );
+ }
+ else
+ aGraphic = rGraphic;
+ }
+
+ }
+ else
+ aGraphic = rGraphic;
+
+ return aGraphic;
+}
+
+GraphicFilter::GraphicFilter( bool bConfig )
+ : bUseConfig(bConfig)
+{
+ ImplInit();
+}
+
+GraphicFilter::~GraphicFilter()
+{
+ {
+ std::scoped_lock aGuard( getListMutex() );
+ auto it = std::find(gaFilterHdlList.begin(), gaFilterHdlList.end(), this);
+ if( it != gaFilterHdlList.end() )
+ gaFilterHdlList.erase( it );
+
+ if( gaFilterHdlList.empty() )
+ delete pConfig;
+ }
+
+ mxErrorEx.reset();
+}
+
+void GraphicFilter::ImplInit()
+{
+ {
+ std::scoped_lock aGuard( getListMutex() );
+
+ if ( gaFilterHdlList.empty() )
+ pConfig = new FilterConfigCache( bUseConfig );
+ else
+ pConfig = gaFilterHdlList.front()->pConfig;
+
+ gaFilterHdlList.push_back( this );
+ }
+
+ if( bUseConfig )
+ {
+ OUString url("$BRAND_BASE_DIR/" LIBO_LIB_FOLDER);
+ rtl::Bootstrap::expandMacros(url); //TODO: detect failure
+ osl::FileBase::getSystemPathFromFileURL(url, aFilterPath);
+ }
+
+ mxErrorEx = ERRCODE_NONE;
+}
+
+ErrCode GraphicFilter::ImplSetError( ErrCode nError, const SvStream* pStm )
+{
+ mxErrorEx = pStm ? pStm->GetError() : ERRCODE_NONE;
+ return nError;
+}
+
+sal_uInt16 GraphicFilter::GetImportFormatCount() const
+{
+ return pConfig->GetImportFormatCount();
+}
+
+sal_uInt16 GraphicFilter::GetImportFormatNumber( std::u16string_view rFormatName )
+{
+ return pConfig->GetImportFormatNumber( rFormatName );
+}
+
+sal_uInt16 GraphicFilter::GetImportFormatNumberForShortName( std::u16string_view rShortName )
+{
+ return pConfig->GetImportFormatNumberForShortName( rShortName );
+}
+
+sal_uInt16 GraphicFilter::GetImportFormatNumberForTypeName( std::u16string_view rType )
+{
+ return pConfig->GetImportFormatNumberForTypeName( rType );
+}
+
+OUString GraphicFilter::GetImportFormatName( sal_uInt16 nFormat )
+{
+ return pConfig->GetImportFormatName( nFormat );
+}
+
+OUString GraphicFilter::GetImportFormatTypeName( sal_uInt16 nFormat )
+{
+ return pConfig->GetImportFilterTypeName( nFormat );
+}
+
+#ifdef _WIN32
+OUString GraphicFilter::GetImportFormatMediaType( sal_uInt16 nFormat )
+{
+ return pConfig->GetImportFormatMediaType( nFormat );
+}
+#endif
+
+OUString GraphicFilter::GetImportFormatShortName( sal_uInt16 nFormat )
+{
+ return pConfig->GetImportFormatShortName( nFormat );
+}
+
+OUString GraphicFilter::GetImportWildcard( sal_uInt16 nFormat, sal_Int32 nEntry )
+{
+ return pConfig->GetImportWildcard( nFormat, nEntry );
+}
+
+sal_uInt16 GraphicFilter::GetExportFormatCount() const
+{
+ return pConfig->GetExportFormatCount();
+}
+
+sal_uInt16 GraphicFilter::GetExportFormatNumber( std::u16string_view rFormatName )
+{
+ return pConfig->GetExportFormatNumber( rFormatName );
+}
+
+sal_uInt16 GraphicFilter::GetExportFormatNumberForMediaType( std::u16string_view rMediaType )
+{
+ return pConfig->GetExportFormatNumberForMediaType( rMediaType );
+}
+
+sal_uInt16 GraphicFilter::GetExportFormatNumberForShortName( std::u16string_view rShortName )
+{
+ return pConfig->GetExportFormatNumberForShortName( rShortName );
+}
+
+OUString GraphicFilter::GetExportInternalFilterName( sal_uInt16 nFormat )
+{
+ return pConfig->GetExportInternalFilterName( nFormat );
+}
+
+sal_uInt16 GraphicFilter::GetExportFormatNumberForTypeName( std::u16string_view rType )
+{
+ return pConfig->GetExportFormatNumberForTypeName( rType );
+}
+
+OUString GraphicFilter::GetExportFormatName( sal_uInt16 nFormat )
+{
+ return pConfig->GetExportFormatName( nFormat );
+}
+
+OUString GraphicFilter::GetExportFormatMediaType( sal_uInt16 nFormat )
+{
+ return pConfig->GetExportFormatMediaType( nFormat );
+}
+
+OUString GraphicFilter::GetExportFormatShortName( sal_uInt16 nFormat )
+{
+ return pConfig->GetExportFormatShortName( nFormat );
+}
+
+OUString GraphicFilter::GetExportWildcard( sal_uInt16 nFormat )
+{
+ return pConfig->GetExportWildcard( nFormat, 0 );
+}
+
+bool GraphicFilter::IsExportPixelFormat( sal_uInt16 nFormat )
+{
+ return pConfig->IsExportPixelFormat( nFormat );
+}
+
+ErrCode GraphicFilter::CanImportGraphic( const INetURLObject& rPath,
+ sal_uInt16 nFormat, sal_uInt16* pDeterminedFormat )
+{
+ ErrCode nRetValue = ERRCODE_GRFILTER_FORMATERROR;
+ SAL_WARN_IF( rPath.GetProtocol() == INetProtocol::NotValid, "vcl.filter", "GraphicFilter::CanImportGraphic() : ProtType == INetProtocol::NotValid" );
+
+ OUString aMainUrl( rPath.GetMainURL( INetURLObject::DecodeMechanism::NONE ) );
+ std::unique_ptr<SvStream> xStream(::utl::UcbStreamHelper::CreateStream( aMainUrl, StreamMode::READ | StreamMode::SHARE_DENYNONE ));
+ if (xStream)
+ {
+ nRetValue = CanImportGraphic( aMainUrl, *xStream, nFormat, pDeterminedFormat );
+ }
+ return nRetValue;
+}
+
+ErrCode GraphicFilter::CanImportGraphic( std::u16string_view rMainUrl, SvStream& rIStream,
+ sal_uInt16 nFormat, sal_uInt16* pDeterminedFormat )
+{
+ sal_uInt64 nStreamPos = rIStream.Tell();
+ ErrCode nRes = ImpTestOrFindFormat( rMainUrl, rIStream, nFormat );
+
+ rIStream.Seek(nStreamPos);
+
+ if( nRes==ERRCODE_NONE && pDeterminedFormat!=nullptr )
+ *pDeterminedFormat = nFormat;
+
+ return ImplSetError( nRes, &rIStream );
+}
+
+//SJ: TODO, we need to create a GraphicImporter component
+ErrCode GraphicFilter::ImportGraphic( Graphic& rGraphic, const INetURLObject& rPath,
+ sal_uInt16 nFormat, sal_uInt16 * pDeterminedFormat, GraphicFilterImportFlags nImportFlags )
+{
+ ErrCode nRetValue = ERRCODE_GRFILTER_FORMATERROR;
+ SAL_WARN_IF( rPath.GetProtocol() == INetProtocol::NotValid, "vcl.filter", "GraphicFilter::ImportGraphic() : ProtType == INetProtocol::NotValid" );
+
+ OUString aMainUrl( rPath.GetMainURL( INetURLObject::DecodeMechanism::NONE ) );
+ std::unique_ptr<SvStream> xStream(::utl::UcbStreamHelper::CreateStream( aMainUrl, StreamMode::READ | StreamMode::SHARE_DENYNONE ));
+ if (xStream)
+ {
+ nRetValue = ImportGraphic( rGraphic, aMainUrl, *xStream, nFormat, pDeterminedFormat, nImportFlags );
+ }
+ return nRetValue;
+}
+
+namespace {
+
+/// Contains a stream and other associated data to import pixels into a
+/// Graphic.
+struct GraphicImportContext
+{
+ /// Pixel data is read from this stream.
+ std::unique_ptr<SvStream> m_pStream;
+ /// The Graphic the import filter gets.
+ std::shared_ptr<Graphic> m_pGraphic;
+ /// Write pixel data using this access.
+ std::unique_ptr<BitmapScopedWriteAccess> m_pAccess;
+ std::unique_ptr<AlphaScopedWriteAccess> m_pAlphaAccess;
+ // Need to have an AlphaMask instance to keep its lifetime.
+ AlphaMask mAlphaMask;
+ /// Signals if import finished correctly.
+ ErrCode m_nStatus = ERRCODE_GRFILTER_FILTERERROR;
+ /// Original graphic format.
+ GfxLinkType m_eLinkType = GfxLinkType::NONE;
+ /// Position of the stream before reading the data.
+ sal_uInt64 m_nStreamBegin = 0;
+ /// Flags for the import filter.
+ GraphicFilterImportFlags m_nImportFlags = GraphicFilterImportFlags::NONE;
+};
+
+/// Graphic import worker that gets executed on a thread.
+class GraphicImportTask : public comphelper::ThreadTask
+{
+ GraphicImportContext& m_rContext;
+public:
+ GraphicImportTask(const std::shared_ptr<comphelper::ThreadTaskTag>& pTag, GraphicImportContext& rContext);
+ void doWork() override;
+ /// Shared code between threaded and non-threaded version.
+ static void doImport(GraphicImportContext& rContext);
+};
+
+}
+
+GraphicImportTask::GraphicImportTask(const std::shared_ptr<comphelper::ThreadTaskTag>& pTag, GraphicImportContext& rContext)
+ : comphelper::ThreadTask(pTag),
+ m_rContext(rContext)
+{
+}
+
+void GraphicImportTask::doWork()
+{
+ GraphicImportTask::doImport(m_rContext);
+}
+
+void GraphicImportTask::doImport(GraphicImportContext& rContext)
+{
+ if(rContext.m_eLinkType == GfxLinkType::NativeJpg)
+ {
+ if (!ImportJPEG(*rContext.m_pStream, *rContext.m_pGraphic, rContext.m_nImportFlags | GraphicFilterImportFlags::UseExistingBitmap, rContext.m_pAccess.get()))
+ rContext.m_nStatus = ERRCODE_GRFILTER_FILTERERROR;
+ }
+ else if(rContext.m_eLinkType == GfxLinkType::NativePng)
+ {
+ if (!vcl::ImportPNG(*rContext.m_pStream, *rContext.m_pGraphic,
+ rContext.m_nImportFlags | GraphicFilterImportFlags::UseExistingBitmap,
+ rContext.m_pAccess.get(), rContext.m_pAlphaAccess.get()))
+ {
+ rContext.m_nStatus = ERRCODE_GRFILTER_FILTERERROR;
+ }
+ }
+}
+
+void GraphicFilter::ImportGraphics(std::vector< std::shared_ptr<Graphic> >& rGraphics, std::vector< std::unique_ptr<SvStream> > vStreams)
+{
+ static bool bThreads = !getenv("VCL_NO_THREAD_IMPORT");
+ std::vector<GraphicImportContext> aContexts;
+ aContexts.reserve(vStreams.size());
+ comphelper::ThreadPool& rSharedPool = comphelper::ThreadPool::getSharedOptimalPool();
+ std::shared_ptr<comphelper::ThreadTaskTag> pTag = comphelper::ThreadPool::createThreadTaskTag();
+
+ for (auto& pStream : vStreams)
+ {
+ aContexts.emplace_back();
+ GraphicImportContext& rContext = aContexts.back();
+
+ if (pStream)
+ {
+ rContext.m_pStream = std::move(pStream);
+ rContext.m_pGraphic = std::make_shared<Graphic>();
+ rContext.m_nStatus = ERRCODE_NONE;
+
+ // Detect the format.
+ ResetLastError();
+ rContext.m_nStreamBegin = rContext.m_pStream->Tell();
+ sal_uInt16 nFormat = GRFILTER_FORMAT_DONTKNOW;
+ rContext.m_nStatus = ImpTestOrFindFormat(u"", *rContext.m_pStream, nFormat);
+ rContext.m_pStream->Seek(rContext.m_nStreamBegin);
+
+ // Import the graphic.
+ if (rContext.m_nStatus == ERRCODE_NONE && !rContext.m_pStream->GetError())
+ {
+ OUString aFilterName = pConfig->GetImportFilterName(nFormat);
+
+ if (aFilterName.equalsIgnoreAsciiCase(IMP_JPEG))
+ {
+ rContext.m_eLinkType = GfxLinkType::NativeJpg;
+ rContext.m_nImportFlags = GraphicFilterImportFlags::SetLogsizeForJpeg;
+
+ if (ImportJPEG( *rContext.m_pStream, *rContext.m_pGraphic, rContext.m_nImportFlags | GraphicFilterImportFlags::OnlyCreateBitmap, nullptr))
+ {
+ Bitmap& rBitmap = const_cast<Bitmap&>(rContext.m_pGraphic->GetBitmapExRef().GetBitmap());
+ rContext.m_pAccess = std::make_unique<BitmapScopedWriteAccess>(rBitmap);
+ rContext.m_pStream->Seek(rContext.m_nStreamBegin);
+ if (bThreads)
+ rSharedPool.pushTask(std::make_unique<GraphicImportTask>(pTag, rContext));
+ else
+ GraphicImportTask::doImport(rContext);
+ }
+ else
+ rContext.m_nStatus = ERRCODE_GRFILTER_FILTERERROR;
+ }
+ else if (aFilterName.equalsIgnoreAsciiCase(IMP_PNG))
+ {
+ rContext.m_eLinkType = GfxLinkType::NativePng;
+
+ if (vcl::ImportPNG( *rContext.m_pStream, *rContext.m_pGraphic, rContext.m_nImportFlags | GraphicFilterImportFlags::OnlyCreateBitmap, nullptr, nullptr))
+ {
+ const BitmapEx& rBitmapEx = rContext.m_pGraphic->GetBitmapExRef();
+ Bitmap& rBitmap = const_cast<Bitmap&>(rBitmapEx.GetBitmap());
+ rContext.m_pAccess = std::make_unique<BitmapScopedWriteAccess>(rBitmap);
+ if(rBitmapEx.IsAlpha())
+ {
+ // The separate alpha bitmap causes a number of complications. Not only
+ // we need to have an extra bitmap access for it, but we also need
+ // to keep an AlphaMask instance in the context. This is because
+ // BitmapEx internally keeps Bitmap and not AlphaMask (because the Bitmap
+ // may be also a mask, not alpha). So BitmapEx::GetAlpha() returns
+ // a temporary, and direct access to the Bitmap wouldn't work
+ // with AlphaScopedBitmapAccess. *sigh*
+ rContext.mAlphaMask = rBitmapEx.GetAlpha();
+ rContext.m_pAlphaAccess = std::make_unique<AlphaScopedWriteAccess>(rContext.mAlphaMask);
+ }
+ rContext.m_pStream->Seek(rContext.m_nStreamBegin);
+ if (bThreads)
+ rSharedPool.pushTask(std::make_unique<GraphicImportTask>(pTag, rContext));
+ else
+ GraphicImportTask::doImport(rContext);
+ }
+ else
+ rContext.m_nStatus = ERRCODE_GRFILTER_FILTERERROR;
+ }
+ else
+ rContext.m_nStatus = ERRCODE_GRFILTER_FILTERERROR;
+ }
+ }
+ }
+
+ rSharedPool.waitUntilDone(pTag);
+
+ // Process data after import.
+ for (auto& rContext : aContexts)
+ {
+ if(rContext.m_pAlphaAccess) // Need to move the AlphaMask back to the BitmapEx.
+ *rContext.m_pGraphic = BitmapEx( rContext.m_pGraphic->GetBitmapExRef().GetBitmap(), rContext.mAlphaMask );
+ rContext.m_pAccess.reset();
+ rContext.m_pAlphaAccess.reset();
+
+ if (rContext.m_nStatus == ERRCODE_NONE && (rContext.m_eLinkType != GfxLinkType::NONE) && !rContext.m_pGraphic->GetReaderContext())
+ {
+ std::unique_ptr<sal_uInt8[]> pGraphicContent;
+
+ const sal_uInt64 nStreamEnd = rContext.m_pStream->Tell();
+ sal_Int32 nGraphicContentSize = nStreamEnd - rContext.m_nStreamBegin;
+
+ if (nGraphicContentSize > 0)
+ {
+ try
+ {
+ pGraphicContent.reset(new sal_uInt8[nGraphicContentSize]);
+ }
+ catch (const std::bad_alloc&)
+ {
+ rContext.m_nStatus = ERRCODE_GRFILTER_TOOBIG;
+ }
+
+ if (rContext.m_nStatus == ERRCODE_NONE)
+ {
+ rContext.m_pStream->Seek(rContext.m_nStreamBegin);
+ rContext.m_pStream->ReadBytes(pGraphicContent.get(), nGraphicContentSize);
+ }
+ }
+
+ if (rContext.m_nStatus == ERRCODE_NONE)
+ rContext.m_pGraphic->SetGfxLink(std::make_shared<GfxLink>(std::move(pGraphicContent), nGraphicContentSize, rContext.m_eLinkType));
+ }
+
+ if (rContext.m_nStatus != ERRCODE_NONE)
+ rContext.m_pGraphic = nullptr;
+
+ rGraphics.push_back(rContext.m_pGraphic);
+ }
+}
+
+void GraphicFilter::MakeGraphicsAvailableThreaded(std::vector<Graphic*>& graphics)
+{
+ // Graphic::makeAvailable() is not thread-safe. Only the jpeg and png loaders are, so here
+ // we process only jpeg and png images that also have their stream data, load new Graphic's
+ // from them and then update the passed objects using them.
+ std::vector< Graphic* > toLoad;
+ for(auto graphic : graphics)
+ {
+ // Need to use GetSharedGfxLink, to access the pointer without copying.
+ if(!graphic->isAvailable() && graphic->IsGfxLink()
+ && graphic->GetSharedGfxLink()->GetDataSize() != 0
+ && (graphic->GetSharedGfxLink()->GetType() == GfxLinkType::NativeJpg
+ || graphic->GetSharedGfxLink()->GetType() == GfxLinkType::NativePng))
+ {
+ // Graphic objects share internal ImpGraphic, do not process any of those twice.
+ const auto predicate = [graphic](Graphic* item) { return item->ImplGetImpGraphic() == graphic->ImplGetImpGraphic(); };
+ if( std::find_if(toLoad.begin(), toLoad.end(), predicate ) == toLoad.end())
+ toLoad.push_back( graphic );
+ }
+ }
+ if( toLoad.empty())
+ return;
+ std::vector< std::unique_ptr<SvStream>> streams;
+ for( auto graphic : toLoad )
+ {
+ streams.push_back( std::make_unique<SvMemoryStream>( const_cast<sal_uInt8*>(graphic->GetSharedGfxLink()->GetData()),
+ graphic->GetSharedGfxLink()->GetDataSize(), StreamMode::READ | StreamMode::WRITE));
+ }
+ std::vector< std::shared_ptr<Graphic>> loadedGraphics;
+ ImportGraphics(loadedGraphics, std::move(streams));
+ assert(loadedGraphics.size() == toLoad.size());
+ for( size_t i = 0; i < toLoad.size(); ++i )
+ {
+ if(loadedGraphics[ i ] != nullptr)
+ toLoad[ i ]->ImplGetImpGraphic()->updateFromLoadedGraphic(loadedGraphics[ i ]->ImplGetImpGraphic());
+ }
+}
+
+Graphic GraphicFilter::ImportUnloadedGraphic(SvStream& rIStream, sal_uInt64 sizeLimit,
+ const Size* pSizeHint)
+{
+ Graphic aGraphic;
+ sal_uInt16 nFormat = GRFILTER_FORMAT_DONTKNOW;
+ GfxLinkType eLinkType = GfxLinkType::NONE;
+
+ ResetLastError();
+
+ const sal_uInt64 nStreamBegin = rIStream.Tell();
+
+ rIStream.Seek(nStreamBegin);
+
+ ErrCode nStatus = ImpTestOrFindFormat(u"", rIStream, nFormat);
+
+ rIStream.Seek(nStreamBegin);
+ sal_uInt32 nStreamLength(rIStream.remainingSize());
+ if (sizeLimit && sizeLimit < nStreamLength)
+ nStreamLength = sizeLimit;
+
+ OUString aFilterName = pConfig->GetImportFilterName(nFormat);
+
+ std::unique_ptr<sal_uInt8[]> pGraphicContent;
+ sal_Int32 nGraphicContentSize = 0;
+
+ // read graphic
+ {
+ if (aFilterName.equalsIgnoreAsciiCase(IMP_GIF))
+ {
+ eLinkType = GfxLinkType::NativeGif;
+ }
+ else if (aFilterName.equalsIgnoreAsciiCase(IMP_PNG))
+ {
+ // check if this PNG contains a GIF chunk!
+ pGraphicContent = vcl::PngImageReader::getMicrosoftGifChunk(rIStream, &nGraphicContentSize);
+ if( pGraphicContent )
+ eLinkType = GfxLinkType::NativeGif;
+ else
+ eLinkType = GfxLinkType::NativePng;
+ }
+ else if (aFilterName.equalsIgnoreAsciiCase(IMP_JPEG))
+ {
+ eLinkType = GfxLinkType::NativeJpg;
+ }
+ else if (aFilterName.equalsIgnoreAsciiCase(IMP_SVG))
+ {
+ bool bOkay(false);
+
+ if (nStreamLength > 0)
+ {
+ std::vector<sal_uInt8> aTwoBytes(2);
+ rIStream.ReadBytes(aTwoBytes.data(), 2);
+ rIStream.Seek(nStreamBegin);
+
+ if (aTwoBytes[0] == 0x1F && aTwoBytes[1] == 0x8B)
+ {
+ SvMemoryStream aMemStream;
+ ZCodec aCodec;
+ tools::Long nMemoryLength;
+
+ aCodec.BeginCompression(ZCODEC_DEFAULT_COMPRESSION, /*gzLib*/true);
+ nMemoryLength = aCodec.Decompress(rIStream, aMemStream);
+ aCodec.EndCompression();
+
+ if (!rIStream.GetError() && nMemoryLength >= 0)
+ {
+ nGraphicContentSize = nMemoryLength;
+ pGraphicContent.reset(new sal_uInt8[nGraphicContentSize]);
+
+ aMemStream.Seek(STREAM_SEEK_TO_BEGIN);
+ aMemStream.ReadBytes(pGraphicContent.get(), nGraphicContentSize);
+
+ bOkay = true;
+ }
+ }
+ else
+ {
+ nGraphicContentSize = nStreamLength;
+ pGraphicContent.reset(new sal_uInt8[nGraphicContentSize]);
+ rIStream.ReadBytes(pGraphicContent.get(), nStreamLength);
+
+ bOkay = true;
+ }
+ }
+
+ if (bOkay)
+ {
+ eLinkType = GfxLinkType::NativeSvg;
+ }
+ else
+ {
+ nStatus = ERRCODE_GRFILTER_FILTERERROR;
+ }
+ }
+ else if (aFilterName.equalsIgnoreAsciiCase(IMP_BMP))
+ {
+ eLinkType = GfxLinkType::NativeBmp;
+ }
+ else if (aFilterName.equalsIgnoreAsciiCase(IMP_MOV))
+ {
+ eLinkType = GfxLinkType::NativeMov;
+ }
+ else if (aFilterName.equalsIgnoreAsciiCase(IMP_WMF) ||
+ aFilterName.equalsIgnoreAsciiCase(IMP_EMF))
+ {
+ nGraphicContentSize = nStreamLength;
+ pGraphicContent.reset(new sal_uInt8[nGraphicContentSize]);
+ rIStream.Seek(nStreamBegin);
+ if (ZCodec::IsZCompressed(rIStream))
+ {
+ ZCodec aCodec;
+ SvMemoryStream aMemStream;
+ tools::Long nMemoryLength;
+ aCodec.BeginCompression(ZCODEC_DEFAULT_COMPRESSION, /*gzLib*/true);
+ nMemoryLength = aCodec.Decompress(rIStream, aMemStream);
+ aCodec.EndCompression();
+
+ if (!rIStream.GetError() && nMemoryLength >= 0)
+ {
+ nGraphicContentSize = nMemoryLength;
+ pGraphicContent.reset(new sal_uInt8[nGraphicContentSize]);
+
+ aMemStream.Seek(STREAM_SEEK_TO_BEGIN);
+ aMemStream.ReadBytes(pGraphicContent.get(), nGraphicContentSize);
+ }
+ }
+ else
+ {
+ rIStream.ReadBytes(pGraphicContent.get(), nStreamLength);
+ }
+ if (!rIStream.GetError())
+ {
+ eLinkType = GfxLinkType::NativeWmf;
+ }
+ else
+ {
+ nStatus = ERRCODE_GRFILTER_FILTERERROR;
+ }
+ }
+ else if (aFilterName == IMP_PDF)
+ {
+ eLinkType = GfxLinkType::NativePdf;
+ }
+ else if (aFilterName == IMP_TIFF)
+ {
+ eLinkType = GfxLinkType::NativeTif;
+ }
+ else if (aFilterName == IMP_PICT)
+ {
+ eLinkType = GfxLinkType::NativePct;
+ }
+ else if (aFilterName == IMP_MET)
+ {
+ eLinkType = GfxLinkType::NativeMet;
+ }
+ else if (aFilterName.equalsIgnoreAsciiCase(IMP_WEBP))
+ {
+ if(supportNativeWebp())
+ eLinkType = GfxLinkType::NativeWebp;
+ else
+ nStatus = ERRCODE_GRFILTER_FILTERERROR;
+ }
+ else
+ {
+ nStatus = ERRCODE_GRFILTER_FILTERERROR;
+ }
+ }
+
+ if (nStatus == ERRCODE_NONE && eLinkType != GfxLinkType::NONE)
+ {
+ if (!pGraphicContent)
+ {
+ nGraphicContentSize = nStreamLength;
+
+ if (nGraphicContentSize > 0)
+ {
+ try
+ {
+ pGraphicContent.reset(new sal_uInt8[nGraphicContentSize]);
+ }
+ catch (const std::bad_alloc&)
+ {
+ nStatus = ERRCODE_GRFILTER_TOOBIG;
+ }
+
+ if (nStatus == ERRCODE_NONE)
+ {
+ rIStream.Seek(nStreamBegin);
+ nGraphicContentSize = rIStream.ReadBytes(pGraphicContent.get(), nGraphicContentSize);
+ }
+ }
+ }
+
+ if( nStatus == ERRCODE_NONE )
+ {
+ bool bAnimated = false;
+ Size aLogicSize;
+ if (eLinkType == GfxLinkType::NativeGif)
+ {
+ SvMemoryStream aMemoryStream(pGraphicContent.get(), nGraphicContentSize, StreamMode::READ);
+ bAnimated = IsGIFAnimated(aMemoryStream, aLogicSize);
+ if (!pSizeHint && aLogicSize.getWidth() && aLogicSize.getHeight())
+ {
+ pSizeHint = &aLogicSize;
+ }
+ }
+ aGraphic.SetGfxLink(std::make_shared<GfxLink>(std::move(pGraphicContent), nGraphicContentSize, eLinkType));
+ aGraphic.ImplGetImpGraphic()->setPrepared(bAnimated, pSizeHint);
+ }
+ }
+
+ // Set error code or try to set native buffer
+ if (nStatus != ERRCODE_NONE)
+ ImplSetError(nStatus, &rIStream);
+ if (nStatus != ERRCODE_NONE || eLinkType == GfxLinkType::NONE)
+ rIStream.Seek(nStreamBegin);
+
+ return aGraphic;
+}
+
+ErrCode GraphicFilter::readGIF(SvStream & rStream, Graphic & rGraphic, GfxLinkType & rLinkType)
+{
+ if (ImportGIF(rStream, rGraphic))
+ {
+ rLinkType = GfxLinkType::NativeGif;
+ return ERRCODE_NONE;
+ }
+ else
+ return ERRCODE_GRFILTER_FILTERERROR;
+}
+
+ErrCode GraphicFilter::readPNG(SvStream & rStream, Graphic & rGraphic, GfxLinkType & rLinkType, std::unique_ptr<sal_uInt8[]> & rpGraphicContent,
+ sal_Int32& rGraphicContentSize)
+{
+ ErrCode aReturnCode = ERRCODE_NONE;
+
+ // check if this PNG contains a GIF chunk!
+ rpGraphicContent = vcl::PngImageReader::getMicrosoftGifChunk(rStream, &rGraphicContentSize);
+ if( rpGraphicContent )
+ {
+ SvMemoryStream aIStrm(rpGraphicContent.get(), rGraphicContentSize, StreamMode::READ);
+ ImportGIF(aIStrm, rGraphic);
+ rLinkType = GfxLinkType::NativeGif;
+ return aReturnCode;
+ }
+
+ // PNG has no GIF chunk
+ vcl::PngImageReader aPNGReader(rStream);
+ BitmapEx aBitmapEx(aPNGReader.read());
+ if (!aBitmapEx.IsEmpty())
+ {
+ rGraphic = aBitmapEx;
+ rLinkType = GfxLinkType::NativePng;
+ }
+ else
+ aReturnCode = ERRCODE_GRFILTER_FILTERERROR;
+
+ return aReturnCode;
+}
+
+ErrCode GraphicFilter::readJPEG(SvStream & rStream, Graphic & rGraphic, GfxLinkType & rLinkType, GraphicFilterImportFlags nImportFlags)
+{
+ ErrCode aReturnCode = ERRCODE_NONE;
+
+ // set LOGSIZE flag always, if not explicitly disabled
+ // (see #90508 and #106763)
+ if (!(nImportFlags & GraphicFilterImportFlags::DontSetLogsizeForJpeg))
+ {
+ nImportFlags |= GraphicFilterImportFlags::SetLogsizeForJpeg;
+ }
+
+ sal_uInt64 nPosition = rStream.Tell();
+ if (!ImportJPEG(rStream, rGraphic, nImportFlags | GraphicFilterImportFlags::OnlyCreateBitmap, nullptr))
+ aReturnCode = ERRCODE_GRFILTER_FILTERERROR;
+ else
+ {
+ Bitmap& rBitmap = const_cast<Bitmap&>(rGraphic.GetBitmapExRef().GetBitmap());
+ BitmapScopedWriteAccess pWriteAccess(rBitmap);
+ rStream.Seek(nPosition);
+ if (!ImportJPEG(rStream, rGraphic, nImportFlags | GraphicFilterImportFlags::UseExistingBitmap, &pWriteAccess))
+ aReturnCode = ERRCODE_GRFILTER_FILTERERROR;
+ else
+ rLinkType = GfxLinkType::NativeJpg;
+ }
+
+ return aReturnCode;
+}
+
+ErrCode GraphicFilter::readSVG(SvStream & rStream, Graphic & rGraphic, GfxLinkType & rLinkType, std::unique_ptr<sal_uInt8[]> & rpGraphicContent,
+ sal_Int32& rGraphicContentSize)
+{
+ ErrCode aReturnCode = ERRCODE_NONE;
+
+ const sal_uInt64 nStreamPosition(rStream.Tell());
+ const sal_uInt64 nStreamLength(rStream.remainingSize());
+
+ bool bOkay(false);
+
+ if (nStreamLength > 0)
+ {
+ std::vector<sal_uInt8> aTwoBytes(2);
+ rStream.ReadBytes(aTwoBytes.data(), 2);
+ rStream.Seek(nStreamPosition);
+
+ if (aTwoBytes[0] == 0x1F && aTwoBytes[1] == 0x8B)
+ {
+ SvMemoryStream aMemStream;
+ ZCodec aCodec;
+ tools::Long nMemoryLength;
+
+ aCodec.BeginCompression(ZCODEC_DEFAULT_COMPRESSION, /*gzLib*/true);
+ nMemoryLength = aCodec.Decompress(rStream, aMemStream);
+ aCodec.EndCompression();
+
+ if (!rStream.GetError() && nMemoryLength >= 0)
+ {
+ VectorGraphicDataArray aNewData(nMemoryLength);
+ aMemStream.Seek(STREAM_SEEK_TO_BEGIN);
+ aMemStream.ReadBytes(aNewData.getArray(), nMemoryLength);
+
+ // Make a uncompressed copy for GfxLink
+ rGraphicContentSize = nMemoryLength;
+ rpGraphicContent.reset(new sal_uInt8[rGraphicContentSize]);
+ std::copy(std::cbegin(aNewData), std::cend(aNewData), rpGraphicContent.get());
+
+ if (!aMemStream.GetError())
+ {
+ BinaryDataContainer aDataContainer(reinterpret_cast<const sal_uInt8*>(aNewData.getConstArray()), aNewData.getLength());
+ auto aVectorGraphicDataPtr = std::make_shared<VectorGraphicData>(aDataContainer, VectorGraphicDataType::Svg);
+ rGraphic = Graphic(aVectorGraphicDataPtr);
+ bOkay = true;
+ }
+ }
+ }
+ else
+ {
+ VectorGraphicDataArray aNewData(nStreamLength);
+ rStream.ReadBytes(aNewData.getArray(), nStreamLength);
+
+ if (!rStream.GetError())
+ {
+ BinaryDataContainer aDataContainer(reinterpret_cast<const sal_uInt8*>(aNewData.getConstArray()), aNewData.getLength());
+ auto aVectorGraphicDataPtr = std::make_shared<VectorGraphicData>(aDataContainer, VectorGraphicDataType::Svg);
+ rGraphic = Graphic(aVectorGraphicDataPtr);
+ bOkay = true;
+ }
+ }
+ }
+
+ if (bOkay)
+ {
+ rLinkType = GfxLinkType::NativeSvg;
+ }
+ else
+ {
+ aReturnCode = ERRCODE_GRFILTER_FILTERERROR;
+ }
+
+ return aReturnCode;
+}
+
+ErrCode GraphicFilter::readXBM(SvStream & rStream, Graphic & rGraphic)
+{
+ if (ImportXBM(rStream, rGraphic))
+ return ERRCODE_NONE;
+ else
+ return ERRCODE_GRFILTER_FILTERERROR;
+}
+
+ErrCode GraphicFilter::readXPM(SvStream & rStream, Graphic & rGraphic)
+{
+ if (ImportXPM(rStream, rGraphic))
+ return ERRCODE_NONE;
+ else
+ return ERRCODE_GRFILTER_FILTERERROR;
+}
+
+ErrCode GraphicFilter::readWMF_EMF(SvStream & rStream, Graphic & rGraphic, GfxLinkType & rLinkType, VectorGraphicDataType eType)
+{
+ // use new UNO API service, do not directly import but create a
+ // Graphic that contains the original data and decomposes to
+ // primitives on demand
+ sal_uInt32 nStreamLength(rStream.remainingSize());
+ SvStream* aNewStream = &rStream;
+ ErrCode aReturnCode = ERRCODE_GRFILTER_FILTERERROR;
+ SvMemoryStream aMemStream;
+ if (ZCodec::IsZCompressed(rStream))
+ {
+ ZCodec aCodec;
+ aCodec.BeginCompression(ZCODEC_DEFAULT_COMPRESSION, /*gzLib*/true);
+ auto nDecompressLength = aCodec.Decompress(rStream, aMemStream);
+ aCodec.EndCompression();
+ aMemStream.Seek(STREAM_SEEK_TO_BEGIN);
+ if (nDecompressLength >= 0)
+ {
+ nStreamLength = nDecompressLength;
+ aNewStream = &aMemStream;
+ }
+ }
+ VectorGraphicDataArray aNewData(nStreamLength);
+ aNewStream->ReadBytes(aNewData.getArray(), nStreamLength);
+ if (!aNewStream->GetError())
+ {
+ const VectorGraphicDataType aDataType(eType);
+ BinaryDataContainer aDataContainer(reinterpret_cast<const sal_uInt8*>(aNewData.getConstArray()), aNewData.getLength());
+
+ auto aVectorGraphicDataPtr = std::make_shared<VectorGraphicData>(aDataContainer, aDataType);
+
+ rGraphic = Graphic(aVectorGraphicDataPtr);
+ rLinkType = GfxLinkType::NativeWmf;
+ aReturnCode = ERRCODE_NONE;
+ }
+
+ return aReturnCode;
+}
+
+ErrCode GraphicFilter::readWMF(SvStream & rStream, Graphic & rGraphic, GfxLinkType & rLinkType)
+{
+ return readWMF_EMF(rStream, rGraphic, rLinkType,VectorGraphicDataType::Wmf);
+}
+
+ErrCode GraphicFilter::readEMF(SvStream & rStream, Graphic & rGraphic, GfxLinkType & rLinkType)
+{
+ return readWMF_EMF(rStream, rGraphic, rLinkType, VectorGraphicDataType::Emf);
+}
+
+ErrCode GraphicFilter::readPDF(SvStream & rStream, Graphic & rGraphic, GfxLinkType & rLinkType)
+{
+ if (vcl::ImportPDF(rStream, rGraphic))
+ {
+ rLinkType = GfxLinkType::NativePdf;
+ return ERRCODE_NONE;
+ }
+ else
+ return ERRCODE_GRFILTER_FILTERERROR;
+}
+
+ErrCode GraphicFilter::readTIFF(SvStream & rStream, Graphic & rGraphic, GfxLinkType & rLinkType)
+{
+ if (ImportTiffGraphicImport(rStream, rGraphic))
+ {
+ rLinkType = GfxLinkType::NativeTif;
+ return ERRCODE_NONE;
+ }
+ else
+ return ERRCODE_GRFILTER_FILTERERROR;
+}
+
+ErrCode GraphicFilter::readWithTypeSerializer(SvStream & rStream, Graphic & rGraphic, GfxLinkType & rLinkType, std::u16string_view aFilterName)
+{
+ ErrCode aReturnCode = ERRCODE_GRFILTER_FILTERERROR;
+
+ // SV internal filters for import bitmaps and MetaFiles
+ TypeSerializer aSerializer(rStream);
+ aSerializer.readGraphic(rGraphic);
+
+ if (!rStream.GetError())
+ {
+ if (o3tl::equalsIgnoreAsciiCase(aFilterName, u"" IMP_MOV))
+ {
+ rGraphic.SetDefaultType();
+ rStream.Seek(STREAM_SEEK_TO_END);
+ rLinkType = GfxLinkType::NativeMov;
+ }
+ aReturnCode = ERRCODE_NONE;
+ }
+ return aReturnCode;
+}
+
+ErrCode GraphicFilter::readBMP(SvStream & rStream, Graphic & rGraphic, GfxLinkType & rLinkType)
+{
+ if (BmpReader(rStream, rGraphic))
+ {
+ rLinkType = GfxLinkType::NativeBmp;
+ return ERRCODE_NONE;
+ }
+ else
+ return ERRCODE_GRFILTER_FILTERERROR;
+}
+
+ErrCode GraphicFilter::readTGA(SvStream & rStream, Graphic & rGraphic)
+{
+ if (ImportTgaGraphic(rStream, rGraphic))
+ return ERRCODE_NONE;
+ else
+ return ERRCODE_GRFILTER_FILTERERROR;
+}
+
+ErrCode GraphicFilter::readPICT(SvStream & rStream, Graphic & rGraphic, GfxLinkType & rLinkType)
+{
+ if (ImportPictGraphic(rStream, rGraphic))
+ {
+ rLinkType = GfxLinkType::NativePct;
+ return ERRCODE_NONE;
+ }
+ else
+ return ERRCODE_GRFILTER_FILTERERROR;
+}
+
+ErrCode GraphicFilter::readMET(SvStream & rStream, Graphic & rGraphic, GfxLinkType & rLinkType)
+{
+ if (ImportMetGraphic(rStream, rGraphic))
+ {
+ rLinkType = GfxLinkType::NativeMet;
+ return ERRCODE_NONE;
+ }
+ else
+ return ERRCODE_GRFILTER_FILTERERROR;
+}
+
+ErrCode GraphicFilter::readRAS(SvStream & rStream, Graphic & rGraphic)
+{
+ if (ImportRasGraphic(rStream, rGraphic))
+ return ERRCODE_NONE;
+ else
+ return ERRCODE_GRFILTER_FILTERERROR;
+}
+
+ErrCode GraphicFilter::readPCX(SvStream & rStream, Graphic & rGraphic)
+{
+ if (ImportPcxGraphic(rStream, rGraphic))
+ return ERRCODE_NONE;
+ else
+ return ERRCODE_GRFILTER_FILTERERROR;
+}
+
+ErrCode GraphicFilter::readEPS(SvStream & rStream, Graphic & rGraphic)
+{
+ if (ImportEpsGraphic(rStream, rGraphic))
+ return ERRCODE_NONE;
+ else
+ return ERRCODE_GRFILTER_FILTERERROR;
+}
+
+ErrCode GraphicFilter::readPSD(SvStream & rStream, Graphic & rGraphic)
+{
+ if (ImportPsdGraphic(rStream, rGraphic))
+ return ERRCODE_NONE;
+ else
+ return ERRCODE_GRFILTER_FILTERERROR;
+}
+
+ErrCode GraphicFilter::readPCD(SvStream & rStream, Graphic & rGraphic)
+{
+ std::unique_ptr<FilterConfigItem> pFilterConfigItem;
+ if (!utl::ConfigManager::IsFuzzing())
+ {
+ OUString aFilterConfigPath( "Office.Common/Filter/Graphic/Import/PCD" );
+ pFilterConfigItem = std::make_unique<FilterConfigItem>(aFilterConfigPath);
+ }
+
+ if (ImportPcdGraphic(rStream, rGraphic, pFilterConfigItem.get()))
+ return ERRCODE_NONE;
+ else
+ return ERRCODE_GRFILTER_FILTERERROR;
+}
+
+ErrCode GraphicFilter::readPBM(SvStream & rStream, Graphic & rGraphic)
+{
+ if (ImportPbmGraphic(rStream, rGraphic))
+ return ERRCODE_NONE;
+ else
+ return ERRCODE_GRFILTER_FILTERERROR;
+}
+
+ErrCode GraphicFilter::readDXF(SvStream & rStream, Graphic & rGraphic)
+{
+ if (ImportDxfGraphic(rStream, rGraphic))
+ return ERRCODE_NONE;
+ else
+ return ERRCODE_GRFILTER_FILTERERROR;
+}
+
+ErrCode GraphicFilter::readWEBP(SvStream & rStream, Graphic & rGraphic, GfxLinkType & rLinkType)
+{
+ if (ImportWebpGraphic(rStream, rGraphic))
+ {
+ if(supportNativeWebp())
+ rLinkType = GfxLinkType::NativeWebp;
+ return ERRCODE_NONE;
+ }
+ else
+ return ERRCODE_GRFILTER_FILTERERROR;
+}
+
+ErrCode GraphicFilter::ImportGraphic(Graphic& rGraphic, std::u16string_view rPath, SvStream& rIStream,
+ sal_uInt16 nFormat, sal_uInt16* pDeterminedFormat, GraphicFilterImportFlags nImportFlags)
+{
+ OUString aFilterName;
+ sal_uInt64 nStreamBegin;
+ ErrCode nStatus;
+ GfxLinkType eLinkType = GfxLinkType::NONE;
+ const bool bLinkSet = rGraphic.IsGfxLink();
+
+ std::unique_ptr<sal_uInt8[]> pGraphicContent;
+ sal_Int32 nGraphicContentSize = 0;
+
+ ResetLastError();
+
+ std::shared_ptr<GraphicReader> pContext = rGraphic.GetReaderContext();
+ bool bDummyContext = rGraphic.IsDummyContext();
+ if( !pContext || bDummyContext )
+ {
+ if( bDummyContext )
+ {
+ rGraphic.SetDummyContext( false );
+ nStreamBegin = 0;
+ }
+ else
+ nStreamBegin = rIStream.Tell();
+
+ nStatus = ImpTestOrFindFormat( rPath, rIStream, nFormat );
+ // if pending, return ERRCODE_NONE in order to request more bytes
+ if( rIStream.GetError() == ERRCODE_IO_PENDING )
+ {
+ rGraphic.SetDummyContext(true);
+ rIStream.ResetError();
+ rIStream.Seek( nStreamBegin );
+ return ImplSetError( ERRCODE_NONE );
+ }
+
+ rIStream.Seek( nStreamBegin );
+
+ if( ( nStatus != ERRCODE_NONE ) || rIStream.GetError() )
+ return ImplSetError( ( nStatus != ERRCODE_NONE ) ? nStatus : ERRCODE_GRFILTER_OPENERROR, &rIStream );
+
+ if( pDeterminedFormat )
+ *pDeterminedFormat = nFormat;
+
+ aFilterName = pConfig->GetImportFilterName( nFormat );
+ }
+ else
+ {
+ aFilterName = pContext->GetUpperFilterName();
+
+ nStreamBegin = 0;
+ nStatus = ERRCODE_NONE;
+ }
+
+ // read graphic
+ {
+ if (aFilterName.equalsIgnoreAsciiCase(IMP_GIF))
+ {
+ nStatus = readGIF(rIStream, rGraphic, eLinkType);
+ }
+ else if (aFilterName.equalsIgnoreAsciiCase(IMP_PNG))
+ {
+ nStatus = readPNG(rIStream, rGraphic, eLinkType, pGraphicContent, nGraphicContentSize);
+ }
+ else if (aFilterName.equalsIgnoreAsciiCase(IMP_JPEG))
+ {
+ nStatus = readJPEG(rIStream, rGraphic, eLinkType, nImportFlags);
+ }
+ else if (aFilterName.equalsIgnoreAsciiCase(IMP_SVG))
+ {
+ nStatus = readSVG(rIStream, rGraphic, eLinkType, pGraphicContent, nGraphicContentSize);
+ }
+ else if( aFilterName.equalsIgnoreAsciiCase( IMP_XBM ) )
+ {
+ nStatus = readXBM(rIStream, rGraphic);
+ }
+ else if( aFilterName.equalsIgnoreAsciiCase( IMP_XPM ) )
+ {
+ nStatus = readXPM(rIStream, rGraphic);
+ }
+ else if (aFilterName.equalsIgnoreAsciiCase(IMP_BMP))
+ {
+ nStatus = readBMP(rIStream, rGraphic, eLinkType);
+ }
+ else if (aFilterName.equalsIgnoreAsciiCase(IMP_SVMETAFILE))
+ {
+ nStatus = readWithTypeSerializer(rIStream, rGraphic, eLinkType, aFilterName);
+ }
+ else if( aFilterName.equalsIgnoreAsciiCase(IMP_MOV))
+ {
+ nStatus = readWithTypeSerializer(rIStream, rGraphic, eLinkType, aFilterName);
+ }
+ else if (aFilterName.equalsIgnoreAsciiCase(IMP_WMF))
+ {
+ nStatus = readWMF(rIStream, rGraphic, eLinkType);
+ }
+ else if (aFilterName.equalsIgnoreAsciiCase(IMP_EMF))
+ {
+ nStatus = readEMF(rIStream, rGraphic, eLinkType);
+ }
+ else if (aFilterName.equalsIgnoreAsciiCase(IMP_PDF))
+ {
+ nStatus = readPDF(rIStream, rGraphic, eLinkType);
+ }
+ else if (aFilterName.equalsIgnoreAsciiCase(IMP_TIFF) )
+ {
+ nStatus = readTIFF(rIStream, rGraphic, eLinkType);
+ }
+ else if (aFilterName.equalsIgnoreAsciiCase(IMP_TGA) )
+ {
+ nStatus = readTGA(rIStream, rGraphic);
+ }
+ else if (aFilterName.equalsIgnoreAsciiCase(IMP_PICT))
+ {
+ nStatus = readPICT(rIStream, rGraphic, eLinkType);
+ }
+ else if (aFilterName.equalsIgnoreAsciiCase(IMP_MET))
+ {
+ nStatus = readMET(rIStream, rGraphic, eLinkType);
+ }
+ else if (aFilterName.equalsIgnoreAsciiCase(IMP_RAS))
+ {
+ nStatus = readRAS(rIStream, rGraphic);
+ }
+ else if (aFilterName.equalsIgnoreAsciiCase(IMP_PCX))
+ {
+ nStatus = readPCX(rIStream, rGraphic);
+ }
+ else if (aFilterName.equalsIgnoreAsciiCase(IMP_EPS))
+ {
+ nStatus = readEPS(rIStream, rGraphic);
+ }
+ else if (aFilterName.equalsIgnoreAsciiCase(IMP_PSD))
+ {
+ nStatus = readPSD(rIStream, rGraphic);
+ }
+ else if (aFilterName.equalsIgnoreAsciiCase(IMP_PCD))
+ {
+ nStatus = readPCD(rIStream, rGraphic);
+ }
+ else if (aFilterName.equalsIgnoreAsciiCase(IMP_PBM))
+ {
+ nStatus = readPBM(rIStream, rGraphic);
+ }
+ else if (aFilterName.equalsIgnoreAsciiCase(IMP_DXF))
+ {
+ nStatus = readDXF(rIStream, rGraphic);
+ }
+ else if (aFilterName.equalsIgnoreAsciiCase(IMP_WEBP))
+ {
+ nStatus = readWEBP(rIStream, rGraphic, eLinkType);
+ }
+ else
+ nStatus = ERRCODE_GRFILTER_FILTERERROR;
+ }
+
+ if( nStatus == ERRCODE_NONE && ( eLinkType != GfxLinkType::NONE ) && !rGraphic.GetReaderContext() && !bLinkSet )
+ {
+ if (!pGraphicContent)
+ {
+ const sal_uInt64 nStreamEnd = rIStream.Tell();
+ nGraphicContentSize = nStreamEnd - nStreamBegin;
+
+ if (nGraphicContentSize > 0)
+ {
+ try
+ {
+ pGraphicContent.reset(new sal_uInt8[nGraphicContentSize]);
+ }
+ catch (const std::bad_alloc&)
+ {
+ nStatus = ERRCODE_GRFILTER_TOOBIG;
+ }
+
+ if( nStatus == ERRCODE_NONE )
+ {
+ rIStream.Seek(nStreamBegin);
+ rIStream.ReadBytes(pGraphicContent.get(), nGraphicContentSize);
+ }
+ }
+ }
+ if( nStatus == ERRCODE_NONE )
+ {
+ rGraphic.SetGfxLink(std::make_shared<GfxLink>(std::move(pGraphicContent), nGraphicContentSize, eLinkType));
+ }
+ }
+
+ // Set error code or try to set native buffer
+ if( nStatus != ERRCODE_NONE )
+ {
+ ImplSetError( nStatus, &rIStream );
+ rIStream.Seek( nStreamBegin );
+ rGraphic.Clear();
+ }
+
+ return nStatus;
+}
+
+ErrCode GraphicFilter::ExportGraphic( const Graphic& rGraphic, const INetURLObject& rPath,
+ sal_uInt16 nFormat, const css::uno::Sequence< css::beans::PropertyValue >* pFilterData )
+{
+ SAL_INFO( "vcl.filter", "GraphicFilter::ExportGraphic() (thb)" );
+ ErrCode nRetValue = ERRCODE_GRFILTER_FORMATERROR;
+ SAL_WARN_IF( rPath.GetProtocol() == INetProtocol::NotValid, "vcl.filter", "GraphicFilter::ExportGraphic() : ProtType == INetProtocol::NotValid" );
+
+ OUString aMainUrl(rPath.GetMainURL(INetURLObject::DecodeMechanism::NONE));
+ bool bAlreadyExists = utl::UCBContentHelper::IsDocument(aMainUrl);
+
+ std::unique_ptr<SvStream> xStream(::utl::UcbStreamHelper::CreateStream( aMainUrl, StreamMode::WRITE | StreamMode::TRUNC ));
+ if (xStream)
+ {
+ nRetValue = ExportGraphic( rGraphic, aMainUrl, *xStream, nFormat, pFilterData );
+ xStream.reset();
+
+ if( ( ERRCODE_NONE != nRetValue ) && !bAlreadyExists )
+ utl::UCBContentHelper::Kill(aMainUrl);
+ }
+ return nRetValue;
+}
+
+ErrCode GraphicFilter::ExportGraphic( const Graphic& rGraphic, std::u16string_view rPath,
+ SvStream& rOStm, sal_uInt16 nFormat, const css::uno::Sequence< css::beans::PropertyValue >* pFilterData )
+{
+ SAL_INFO( "vcl.filter", "GraphicFilter::ExportGraphic() (thb)" );
+ sal_uInt16 nFormatCount = GetExportFormatCount();
+
+ ResetLastError();
+
+ if( nFormat == GRFILTER_FORMAT_DONTKNOW )
+ {
+ INetURLObject aURL( rPath );
+ OUString aExt( aURL.GetFileExtension().toAsciiUpperCase() );
+
+ for( sal_uInt16 i = 0; i < nFormatCount; i++ )
+ {
+ if ( pConfig->GetExportFormatExtension( i ).equalsIgnoreAsciiCase( aExt ) )
+ {
+ nFormat=i;
+ break;
+ }
+ }
+ }
+ if( nFormat >= nFormatCount )
+ return ImplSetError( ERRCODE_GRFILTER_FORMATERROR );
+
+ FilterConfigItem aConfigItem( pFilterData );
+ OUString aFilterName( pConfig->GetExportFilterName( nFormat ) );
+ ErrCode nStatus = ERRCODE_NONE;
+ GraphicType eType;
+ Graphic aGraphic = ImpGetScaledGraphic( rGraphic, aConfigItem );
+ eType = aGraphic.GetType();
+
+ if( pConfig->IsExportPixelFormat( nFormat ) )
+ {
+ if( eType != GraphicType::Bitmap )
+ {
+ Size aSizePixel;
+ sal_uLong nBitsPerPixel,nNeededMem,nMaxMem;
+ ScopedVclPtrInstance< VirtualDevice > aVirDev;
+
+ nMaxMem = 1024;
+ nMaxMem *= 1024; // In Bytes
+
+ // Calculate how big the image would normally be:
+ aSizePixel=aVirDev->LogicToPixel(aGraphic.GetPrefSize(),aGraphic.GetPrefMapMode());
+
+ // Calculate how much memory the image will take up
+ nBitsPerPixel=aVirDev->GetBitCount();
+ nNeededMem=(static_cast<sal_uLong>(aSizePixel.Width())*static_cast<sal_uLong>(aSizePixel.Height())*nBitsPerPixel+7)/8;
+
+ // is the image larger than available memory?
+ if (nMaxMem<nNeededMem)
+ {
+ double fFak=sqrt(static_cast<double>(nMaxMem)/static_cast<double>(nNeededMem));
+ aSizePixel.setWidth(static_cast<sal_uLong>(static_cast<double>(aSizePixel.Width())*fFak) );
+ aSizePixel.setHeight(static_cast<sal_uLong>(static_cast<double>(aSizePixel.Height())*fFak) );
+ }
+
+ aVirDev->SetMapMode(MapMode(MapUnit::MapPixel));
+ aVirDev->SetOutputSizePixel(aSizePixel);
+ Graphic aGraphic2=aGraphic;
+ aGraphic2.Draw(*aVirDev, Point(0, 0), aSizePixel); // this changes the MapMode
+ aVirDev->SetMapMode(MapMode(MapUnit::MapPixel));
+ aGraphic=Graphic(aVirDev->GetBitmapEx(Point(0,0),aSizePixel));
+ }
+ }
+ if( rOStm.GetError() )
+ nStatus = ERRCODE_GRFILTER_IOERROR;
+ if( ERRCODE_NONE == nStatus )
+ {
+ if( aFilterName.equalsIgnoreAsciiCase( EXP_BMP ) )
+ {
+ if (!BmpWriter(rOStm, aGraphic, &aConfigItem))
+ nStatus = ERRCODE_GRFILTER_FORMATERROR;
+ if (rOStm.GetError())
+ nStatus = ERRCODE_GRFILTER_IOERROR;
+ }
+ else if (aFilterName.equalsIgnoreAsciiCase(EXP_TIFF))
+ {
+ if (!ExportTiffGraphicImport(rOStm, aGraphic, &aConfigItem))
+ nStatus = ERRCODE_GRFILTER_FORMATERROR;
+
+ if( rOStm.GetError() )
+ nStatus = ERRCODE_GRFILTER_IOERROR;
+ }
+ else if (aFilterName.equalsIgnoreAsciiCase(EXP_GIF))
+ {
+ if (!ExportGifGraphic(rOStm, aGraphic, &aConfigItem))
+ nStatus = ERRCODE_GRFILTER_FORMATERROR;
+
+ if( rOStm.GetError() )
+ nStatus = ERRCODE_GRFILTER_IOERROR;
+ }
+ else if( aFilterName.equalsIgnoreAsciiCase( EXP_SVMETAFILE ) )
+ {
+ sal_Int32 nVersion = aConfigItem.ReadInt32( "Version", 0 ) ;
+ if ( nVersion )
+ rOStm.SetVersion( nVersion );
+
+ // #i119735# just use GetGDIMetaFile, it will create a buffered version of contained bitmap now automatically
+ GDIMetaFile aMTF(aGraphic.GetGDIMetaFile());
+
+ SvmWriter aWriter( rOStm );
+ aWriter.Write( aMTF );
+
+ if( rOStm.GetError() )
+ nStatus = ERRCODE_GRFILTER_IOERROR;
+ }
+ else if ( aFilterName.equalsIgnoreAsciiCase( EXP_WMF ) )
+ {
+ bool bDone(false);
+
+ // do we have a native Vector Graphic Data RenderGraphic, whose data can be written directly?
+ auto const & rVectorGraphicDataPtr(rGraphic.getVectorGraphicData());
+
+ bool bIsEMF = rGraphic.GetGfxLink().IsEMF();
+
+ // VectorGraphicDataType::Wmf means WMF or EMF, allow direct write in the WMF case
+ // only.
+ if (rVectorGraphicDataPtr
+ && rVectorGraphicDataPtr->getType() == VectorGraphicDataType::Wmf
+ && !rVectorGraphicDataPtr->getBinaryDataContainer().isEmpty()
+ && !bIsEMF)
+ {
+ auto & aDataContainer = rVectorGraphicDataPtr->getBinaryDataContainer();
+ rOStm.WriteBytes(aDataContainer.getData(), aDataContainer.getSize());
+
+ if (rOStm.GetError())
+ {
+ nStatus = ERRCODE_GRFILTER_IOERROR;
+ }
+ else
+ {
+ bDone = true;
+ }
+ }
+
+ if (!bDone)
+ {
+ // #i119735# just use GetGDIMetaFile, it will create a buffered version of contained bitmap now automatically
+ if (!ConvertGraphicToWMF(aGraphic, rOStm, &aConfigItem))
+ nStatus = ERRCODE_GRFILTER_FORMATERROR;
+
+ if (rOStm.GetError())
+ nStatus = ERRCODE_GRFILTER_IOERROR;
+ }
+ }
+ else if ( aFilterName.equalsIgnoreAsciiCase( EXP_EMF ) )
+ {
+ bool bDone(false);
+
+ // do we have a native Vector Graphic Data RenderGraphic, whose data can be written directly?
+ auto const & rVectorGraphicDataPtr(rGraphic.getVectorGraphicData());
+
+ if (rVectorGraphicDataPtr
+ && rVectorGraphicDataPtr->getType() == VectorGraphicDataType::Emf
+ && !rVectorGraphicDataPtr->getBinaryDataContainer().isEmpty())
+ {
+ auto & aDataContainer = rVectorGraphicDataPtr->getBinaryDataContainer();
+ rOStm.WriteBytes(aDataContainer.getData(), aDataContainer.getSize());
+
+ if (rOStm.GetError())
+ {
+ nStatus = ERRCODE_GRFILTER_IOERROR;
+ }
+ else
+ {
+ bDone = true;
+ }
+ }
+
+ if (!bDone)
+ {
+ // #i119735# just use GetGDIMetaFile, it will create a buffered version of contained bitmap now automatically
+ if (!ConvertGDIMetaFileToEMF(aGraphic.GetGDIMetaFile(), rOStm))
+ nStatus = ERRCODE_GRFILTER_FORMATERROR;
+
+ if (rOStm.GetError())
+ nStatus = ERRCODE_GRFILTER_IOERROR;
+ }
+ }
+ else if( aFilterName.equalsIgnoreAsciiCase( EXP_JPEG ) )
+ {
+ bool bExportedGrayJPEG = false;
+ if( !ExportJPEG( rOStm, aGraphic, pFilterData, &bExportedGrayJPEG ) )
+ nStatus = ERRCODE_GRFILTER_FORMATERROR;
+
+ if( rOStm.GetError() )
+ nStatus = ERRCODE_GRFILTER_IOERROR;
+ }
+ else if (aFilterName.equalsIgnoreAsciiCase(EXP_EPS))
+ {
+ if (!ExportEpsGraphic(rOStm, aGraphic, &aConfigItem))
+ nStatus = ERRCODE_GRFILTER_FORMATERROR;
+
+ if (rOStm.GetError())
+ nStatus = ERRCODE_GRFILTER_IOERROR;
+ }
+ else if ( aFilterName.equalsIgnoreAsciiCase( EXP_PNG ) )
+ {
+ vcl::PNGWriter aPNGWriter( aGraphic.GetBitmapEx(), pFilterData );
+ if ( pFilterData )
+ {
+ for ( const auto& rPropVal : *pFilterData )
+ {
+ if ( rPropVal.Name == "AdditionalChunks" )
+ {
+ css::uno::Sequence< css::beans::PropertyValue > aAdditionalChunkSequence;
+ if ( rPropVal.Value >>= aAdditionalChunkSequence )
+ {
+ for ( const auto& rAdditionalChunk : std::as_const(aAdditionalChunkSequence) )
+ {
+ if ( rAdditionalChunk.Name.getLength() == 4 )
+ {
+ sal_uInt32 nChunkType = 0;
+ for ( sal_Int32 k = 0; k < 4; k++ )
+ {
+ nChunkType <<= 8;
+ nChunkType |= static_cast<sal_uInt8>(rAdditionalChunk.Name[ k ]);
+ }
+ css::uno::Sequence< sal_Int8 > aByteSeq;
+ if ( rAdditionalChunk.Value >>= aByteSeq )
+ {
+ std::vector< vcl::PNGWriter::ChunkData >& rChunkData = aPNGWriter.GetChunks();
+ if ( !rChunkData.empty() )
+ {
+ sal_uInt32 nChunkLen = aByteSeq.getLength();
+
+ vcl::PNGWriter::ChunkData aChunkData;
+ aChunkData.nType = nChunkType;
+ if ( nChunkLen )
+ {
+ aChunkData.aData.resize( nChunkLen );
+ memcpy( aChunkData.aData.data(), aByteSeq.getConstArray(), nChunkLen );
+ }
+ std::vector< vcl::PNGWriter::ChunkData >::iterator aIter = rChunkData.end() - 1;
+ rChunkData.insert( aIter, aChunkData );
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ aPNGWriter.Write( rOStm );
+
+ if( rOStm.GetError() )
+ nStatus = ERRCODE_GRFILTER_IOERROR;
+ }
+ else if( aFilterName.equalsIgnoreAsciiCase( EXP_SVG ) )
+ {
+ bool bDone(false);
+
+ // do we have a native Vector Graphic Data RenderGraphic, whose data can be written directly?
+ auto const & rVectorGraphicDataPtr(rGraphic.getVectorGraphicData());
+
+ if (rVectorGraphicDataPtr
+ && rVectorGraphicDataPtr->getType() == VectorGraphicDataType::Svg
+ && !rVectorGraphicDataPtr->getBinaryDataContainer().isEmpty())
+ {
+ auto & aDataContainer = rVectorGraphicDataPtr->getBinaryDataContainer();
+ rOStm.WriteBytes(aDataContainer.getData(), aDataContainer.getSize());
+
+ if( rOStm.GetError() )
+ {
+ nStatus = ERRCODE_GRFILTER_IOERROR;
+ }
+ else
+ {
+ bDone = true;
+ }
+ }
+
+ if( !bDone )
+ {
+ // do the normal GDIMetaFile export instead
+ try
+ {
+ css::uno::Reference< css::uno::XComponentContext > xContext( ::comphelper::getProcessComponentContext() );
+
+ css::uno::Reference< css::xml::sax::XDocumentHandler > xSaxWriter(
+ css::xml::sax::Writer::create( xContext ), css::uno::UNO_QUERY_THROW);
+ css::uno::Sequence< css::uno::Any > aArguments{ css::uno::Any(
+ aConfigItem.GetFilterData()) };
+ css::uno::Reference< css::svg::XSVGWriter > xSVGWriter(
+ xContext->getServiceManager()->createInstanceWithArgumentsAndContext( "com.sun.star.svg.SVGWriter", aArguments, xContext),
+ css::uno::UNO_QUERY );
+ if( xSaxWriter.is() && xSVGWriter.is() )
+ {
+ css::uno::Reference< css::io::XActiveDataSource > xActiveDataSource(
+ xSaxWriter, css::uno::UNO_QUERY );
+
+ if( xActiveDataSource.is() )
+ {
+ const css::uno::Reference< css::uno::XInterface > xStmIf(
+ static_cast< ::cppu::OWeakObject* >( new ImpFilterOutputStream( rOStm ) ) );
+
+ SvMemoryStream aMemStm( 65535, 65535 );
+
+ // #i119735# just use GetGDIMetaFile, it will create a buffered version of contained bitmap now automatically
+ SvmWriter aWriter( aMemStm );
+ aWriter.Write( aGraphic.GetGDIMetaFile() );
+
+ xActiveDataSource->setOutputStream( css::uno::Reference< css::io::XOutputStream >(
+ xStmIf, css::uno::UNO_QUERY ) );
+ css::uno::Sequence< sal_Int8 > aMtfSeq( static_cast<sal_Int8 const *>(aMemStm.GetData()), aMemStm.Tell() );
+ xSVGWriter->write( xSaxWriter, aMtfSeq );
+ }
+ }
+ }
+ catch(const css::uno::Exception&)
+ {
+ nStatus = ERRCODE_GRFILTER_IOERROR;
+ }
+ }
+ }
+ else if (aFilterName.equalsIgnoreAsciiCase(EXP_WEBP))
+ {
+ if (!ExportWebpGraphic(rOStm, aGraphic, &aConfigItem))
+ nStatus = ERRCODE_GRFILTER_FORMATERROR;
+
+ if( rOStm.GetError() )
+ nStatus = ERRCODE_GRFILTER_IOERROR;
+ }
+ else
+ nStatus = ERRCODE_GRFILTER_FILTERERROR;
+ }
+ if( nStatus != ERRCODE_NONE )
+ {
+ ImplSetError( nStatus, &rOStm );
+ }
+ return nStatus;
+}
+
+
+void GraphicFilter::ResetLastError()
+{
+ mxErrorEx = ERRCODE_NONE;
+}
+
+Link<ConvertData&,bool> GraphicFilter::GetFilterCallback() const
+{
+ Link<ConvertData&,bool> aLink( LINK( const_cast<GraphicFilter*>(this), GraphicFilter, FilterCallback ) );
+ return aLink;
+}
+
+IMPL_LINK( GraphicFilter, FilterCallback, ConvertData&, rData, bool )
+{
+ bool bRet = false;
+
+ sal_uInt16 nFormat = GRFILTER_FORMAT_DONTKNOW;
+ OUString aShortName;
+ css::uno::Sequence< css::beans::PropertyValue > aFilterData;
+ switch( rData.mnFormat )
+ {
+ case ConvertDataFormat::BMP: aShortName = BMP_SHORTNAME; break;
+ case ConvertDataFormat::GIF: aShortName = GIF_SHORTNAME; break;
+ case ConvertDataFormat::JPG: aShortName = JPG_SHORTNAME; break;
+ case ConvertDataFormat::MET: aShortName = MET_SHORTNAME; break;
+ case ConvertDataFormat::PCT: aShortName = PCT_SHORTNAME; break;
+ case ConvertDataFormat::PNG: aShortName = PNG_SHORTNAME; break;
+ case ConvertDataFormat::SVM: aShortName = SVM_SHORTNAME; break;
+ case ConvertDataFormat::TIF: aShortName = TIF_SHORTNAME; break;
+ case ConvertDataFormat::WMF: aShortName = WMF_SHORTNAME; break;
+ case ConvertDataFormat::EMF: aShortName = EMF_SHORTNAME; break;
+ case ConvertDataFormat::SVG: aShortName = SVG_SHORTNAME; break;
+ case ConvertDataFormat::WEBP: aShortName = WEBP_SHORTNAME; break;
+
+ default:
+ break;
+ }
+ if( GraphicType::NONE == rData.maGraphic.GetType() || rData.maGraphic.GetReaderContext() ) // Import
+ {
+ // Import
+ nFormat = GetImportFormatNumberForShortName( aShortName );
+ bRet = ImportGraphic( rData.maGraphic, u"", rData.mrStm, nFormat ) == ERRCODE_NONE;
+ }
+ else if( !aShortName.isEmpty() )
+ {
+ // Export
+#if defined(IOS) || defined(ANDROID)
+ if (aShortName == PNG_SHORTNAME)
+ {
+ aFilterData.realloc(aFilterData.getLength() + 1);
+ auto pFilterData = aFilterData.getArray();
+ pFilterData[aFilterData.getLength() - 1].Name = "Compression";
+ // We "know" that this gets passed to zlib's deflateInit2_(). 1 means best speed.
+ pFilterData[aFilterData.getLength() - 1].Value <<= static_cast<sal_Int32>(1);
+ }
+#endif
+ nFormat = GetExportFormatNumberForShortName( aShortName );
+ bRet = ExportGraphic( rData.maGraphic, u"", rData.mrStm, nFormat, &aFilterData ) == ERRCODE_NONE;
+ }
+
+ return bRet;
+}
+
+namespace
+{
+ class StandardGraphicFilter
+ {
+ public:
+ StandardGraphicFilter()
+ {
+ m_aFilter.GetImportFormatCount();
+ }
+ GraphicFilter m_aFilter;
+ };
+}
+
+GraphicFilter& GraphicFilter::GetGraphicFilter()
+{
+ static StandardGraphicFilter gStandardFilter;
+ return gStandardFilter.m_aFilter;
+}
+
+ErrCode GraphicFilter::LoadGraphic( const OUString &rPath, const OUString &rFilterName,
+ Graphic& rGraphic, GraphicFilter* pFilter,
+ sal_uInt16* pDeterminedFormat )
+{
+ if ( !pFilter )
+ pFilter = &GetGraphicFilter();
+
+ const sal_uInt16 nFilter = !rFilterName.isEmpty() && pFilter->GetImportFormatCount()
+ ? pFilter->GetImportFormatNumber( rFilterName )
+ : GRFILTER_FORMAT_DONTKNOW;
+
+ INetURLObject aURL( rPath );
+ if ( aURL.HasError() )
+ {
+ aURL.SetSmartProtocol( INetProtocol::File );
+ aURL.SetSmartURL( rPath );
+ }
+
+ std::unique_ptr<SvStream> pStream;
+ if ( INetProtocol::File != aURL.GetProtocol() )
+ pStream = ::utl::UcbStreamHelper::CreateStream( rPath, StreamMode::READ );
+
+ ErrCode nRes = ERRCODE_NONE;
+ if ( !pStream )
+ nRes = pFilter->ImportGraphic( rGraphic, aURL, nFilter, pDeterminedFormat );
+ else
+ nRes = pFilter->ImportGraphic( rGraphic, rPath, *pStream, nFilter, pDeterminedFormat );
+
+#ifdef DBG_UTIL
+ OUString aReturnString;
+
+ if (nRes == ERRCODE_GRFILTER_OPENERROR)
+ aReturnString="open error";
+ else if (nRes == ERRCODE_GRFILTER_IOERROR)
+ aReturnString="IO error";
+ else if (nRes == ERRCODE_GRFILTER_FORMATERROR)
+ aReturnString="format error";
+ else if (nRes == ERRCODE_GRFILTER_VERSIONERROR)
+ aReturnString="version error";
+ else if (nRes == ERRCODE_GRFILTER_FILTERERROR)
+ aReturnString="filter error";
+ else if (nRes == ERRCODE_GRFILTER_TOOBIG)
+ aReturnString="graphic is too big";
+
+ SAL_INFO_IF( nRes, "vcl.filter", "Problem importing graphic " << rPath << ". Reason: " << aReturnString );
+#endif
+
+ return nRes;
+}
+
+ErrCode GraphicFilter::compressAsPNG(const Graphic& rGraphic, SvStream& rOutputStream)
+{
+ css::uno::Sequence< css::beans::PropertyValue > aFilterData{ comphelper::makePropertyValue(
+ "Compression", sal_uInt32(9)) };
+
+ sal_uInt16 nFilterFormat = GetExportFormatNumberForShortName(u"PNG");
+ return ExportGraphic(rGraphic, u"", rOutputStream, nFilterFormat, &aFilterData);
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/vcl/source/filter/graphicfilter2.cxx b/vcl/source/filter/graphicfilter2.cxx
new file mode 100644
index 000000000..c02e9d557
--- /dev/null
+++ b/vcl/source/filter/graphicfilter2.cxx
@@ -0,0 +1,1229 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <string.h>
+#include <tools/stream.hxx>
+#include <tools/fract.hxx>
+#include <tools/urlobj.hxx>
+#include <tools/zcodec.hxx>
+#include <vcl/TypeSerializer.hxx>
+#include <vcl/outdev.hxx>
+#include <vcl/graphicfilter.hxx>
+#include <unotools/ucbstreamhelper.hxx>
+#include <filter/WebpReader.hxx>
+#include "graphicfilter_internal.hxx"
+
+#define DATA_SIZE 640
+constexpr sal_uInt32 EMF_CHECK_SIZE = 44;
+constexpr sal_uInt32 EMR_HEADER = 0x00000001;
+constexpr sal_uInt32 ENHMETA_SIGNATURE = 0x464d4520;
+
+GraphicDescriptor::GraphicDescriptor( const INetURLObject& rPath ) :
+ pFileStm( ::utl::UcbStreamHelper::CreateStream( rPath.GetMainURL( INetURLObject::DecodeMechanism::NONE ), StreamMode::READ ).release() ),
+ aPathExt( rPath.GetFileExtension().toAsciiLowerCase() ),
+ bOwnStream( true )
+{
+ ImpConstruct();
+}
+
+GraphicDescriptor::GraphicDescriptor( SvStream& rInStream, const OUString* pPath) :
+ pFileStm ( &rInStream ),
+ bOwnStream ( false )
+{
+ ImpConstruct();
+
+ if ( pPath )
+ {
+ INetURLObject aURL( *pPath );
+ aPathExt = aURL.GetFileExtension().toAsciiLowerCase();
+ }
+}
+
+GraphicDescriptor::~GraphicDescriptor()
+{
+ if ( bOwnStream )
+ delete pFileStm;
+}
+
+bool GraphicDescriptor::Detect( bool bExtendedInfo )
+{
+ bool bRet = false;
+ if ( pFileStm && !pFileStm->GetError() )
+ {
+ SvStream& rStm = *pFileStm;
+ SvStreamEndian nOldFormat = rStm.GetEndian();
+
+ if ( ImpDetectGIF( rStm, bExtendedInfo ) ) bRet = true;
+ else if ( ImpDetectJPG( rStm, bExtendedInfo ) ) bRet = true;
+ else if ( ImpDetectBMP( rStm, bExtendedInfo ) ) bRet = true;
+ else if ( ImpDetectPNG( rStm, bExtendedInfo ) ) bRet = true;
+ else if ( ImpDetectTIF( rStm, bExtendedInfo ) ) bRet = true;
+ else if ( ImpDetectPCX( rStm ) ) bRet = true;
+ else if ( ImpDetectDXF( rStm, bExtendedInfo ) ) bRet = true;
+ else if ( ImpDetectMET( rStm, bExtendedInfo ) ) bRet = true;
+ else if ( ImpDetectSVM( rStm, bExtendedInfo ) ) bRet = true;
+ else if ( ImpDetectWMF( rStm, bExtendedInfo ) ) bRet = true;
+ else if ( ImpDetectEMF( rStm, bExtendedInfo ) ) bRet = true;
+ else if ( ImpDetectSVG( rStm, bExtendedInfo ) ) bRet = true;
+ else if ( ImpDetectPCT( rStm, bExtendedInfo ) ) bRet = true;
+ else if ( ImpDetectXBM( rStm, bExtendedInfo ) ) bRet = true;
+ else if ( ImpDetectXPM( rStm, bExtendedInfo ) ) bRet = true;
+ else if ( ImpDetectPBM( rStm, bExtendedInfo ) ) bRet = true;
+ else if ( ImpDetectPGM( rStm, bExtendedInfo ) ) bRet = true;
+ else if ( ImpDetectPPM( rStm, bExtendedInfo ) ) bRet = true;
+ else if ( ImpDetectRAS( rStm, bExtendedInfo ) ) bRet = true;
+ else if ( ImpDetectTGA( rStm, bExtendedInfo ) ) bRet = true;
+ else if ( ImpDetectPSD( rStm, bExtendedInfo ) ) bRet = true;
+ else if ( ImpDetectEPS( rStm, bExtendedInfo ) ) bRet = true;
+ else if ( ImpDetectPCD( rStm, bExtendedInfo ) ) bRet = true;
+ else if ( ImpDetectWEBP( rStm, bExtendedInfo ) ) bRet = true;
+
+ rStm.SetEndian( nOldFormat );
+ }
+ return bRet;
+}
+
+void GraphicDescriptor::ImpConstruct()
+{
+ nFormat = GraphicFileFormat::NOT;
+ nBitsPerPixel = 0;
+ nPlanes = 0;
+ mnNumberOfImageComponents = 0;
+ bIsTransparent = false;
+ bIsAlpha = false;
+}
+
+bool GraphicDescriptor::ImpDetectBMP( SvStream& rStm, bool bExtendedInfo )
+{
+ sal_uInt16 nTemp16 = 0;
+ bool bRet = false;
+ sal_Int32 nStmPos = rStm.Tell();
+
+ rStm.SetEndian( SvStreamEndian::LITTLE );
+ rStm.ReadUInt16( nTemp16 );
+
+ // OS/2-BitmapArray
+ if ( nTemp16 == 0x4142 )
+ {
+ rStm.SeekRel( 0x0c );
+ rStm.ReadUInt16( nTemp16 );
+ }
+
+ // Bitmap
+ if ( nTemp16 == 0x4d42 )
+ {
+ nFormat = GraphicFileFormat::BMP;
+ bRet = true;
+
+ if ( bExtendedInfo )
+ {
+ sal_uInt32 nTemp32;
+ sal_uInt32 nCompression;
+
+ // up to first info
+ rStm.SeekRel( 0x10 );
+
+ // Pixel width
+ rStm.ReadUInt32( nTemp32 );
+ aPixSize.setWidth( nTemp32 );
+
+ // Pixel height
+ rStm.ReadUInt32( nTemp32 );
+ aPixSize.setHeight( nTemp32 );
+
+ // Planes
+ rStm.ReadUInt16( nTemp16 );
+ nPlanes = nTemp16;
+
+ // BitCount
+ rStm.ReadUInt16( nTemp16 );
+ nBitsPerPixel = nTemp16;
+
+ // Compression
+ rStm.ReadUInt32( nTemp32 );
+ nCompression = nTemp32;
+
+ // logical width
+ rStm.SeekRel( 4 );
+ rStm.ReadUInt32( nTemp32 );
+ sal_uInt32 nXPelsPerMeter = 0;
+ if ( nTemp32 )
+ {
+ aLogSize.setWidth( ( aPixSize.Width() * 100000 ) / nTemp32 );
+ nXPelsPerMeter = nTemp32;
+ }
+
+ // logical height
+ rStm.ReadUInt32( nTemp32 );
+ sal_uInt32 nYPelsPerMeter = 0;
+ if ( nTemp32 )
+ {
+ aLogSize.setHeight( ( aPixSize.Height() * 100000 ) / nTemp32 );
+ nYPelsPerMeter = nTemp32;
+ }
+
+ // further validation, check for rational values
+ if ( ( nBitsPerPixel > 24 ) || ( nCompression > 3 ) )
+ {
+ nFormat = GraphicFileFormat::NOT;
+ bRet = false;
+ }
+
+ if (bRet && nXPelsPerMeter && nYPelsPerMeter)
+ {
+ maPreferredMapMode
+ = MapMode(MapUnit::MapMM, Point(), Fraction(1000, nXPelsPerMeter),
+ Fraction(1000, nYPelsPerMeter));
+
+ maPreferredLogSize = Size(aPixSize.getWidth(), aPixSize.getHeight());
+ }
+ }
+ }
+ rStm.Seek( nStmPos );
+ return bRet;
+}
+
+bool GraphicDescriptor::ImpDetectGIF( SvStream& rStm, bool bExtendedInfo )
+{
+ sal_uInt32 n32 = 0;
+ bool bRet = false;
+
+ sal_Int32 nStmPos = rStm.Tell();
+ rStm.SetEndian( SvStreamEndian::LITTLE );
+ rStm.ReadUInt32( n32 );
+
+ if ( n32 == 0x38464947 )
+ {
+ sal_uInt16 n16 = 0;
+ rStm.ReadUInt16( n16 );
+ if ( ( n16 == 0x6137 ) || ( n16 == 0x6139 ) )
+ {
+ nFormat = GraphicFileFormat::GIF;
+ bRet = true;
+
+ if ( bExtendedInfo )
+ {
+ sal_uInt16 nTemp16 = 0;
+ sal_uInt8 cByte = 0;
+
+ // Pixel width
+ rStm.ReadUInt16( nTemp16 );
+ aPixSize.setWidth( nTemp16 );
+
+ // Pixel height
+ rStm.ReadUInt16( nTemp16 );
+ aPixSize.setHeight( nTemp16 );
+
+ // Bits/Pixel
+ rStm.ReadUChar( cByte );
+ nBitsPerPixel = ( ( cByte & 112 ) >> 4 ) + 1;
+ }
+ }
+ }
+ rStm.Seek( nStmPos );
+ return bRet;
+}
+
+// returns the next jpeg marker, a return value of 0 represents an error
+static sal_uInt8 ImpDetectJPG_GetNextMarker( SvStream& rStm )
+{
+ sal_uInt8 nByte;
+ do
+ {
+ do
+ {
+ rStm.ReadUChar( nByte );
+ if (!rStm.good()) // as 0 is not allowed as marker,
+ return 0; // we can use it as errorcode
+ }
+ while ( nByte != 0xff );
+ do
+ {
+ rStm.ReadUChar( nByte );
+ if (!rStm.good())
+ return 0;
+ }
+ while( nByte == 0xff );
+ }
+ while( nByte == 0 ); // 0xff00 represents 0xff and not a marker,
+ // the marker detection has to be restarted.
+ return nByte;
+}
+
+bool GraphicDescriptor::ImpDetectJPG( SvStream& rStm, bool bExtendedInfo )
+{
+ sal_uInt32 nTemp32 = 0;
+ bool bRet = false;
+
+ sal_Int32 nStmPos = rStm.Tell();
+
+ rStm.SetEndian( SvStreamEndian::BIG );
+ rStm.ReadUInt32( nTemp32 );
+
+ // compare upper 24 bits
+ if( 0xffd8ff00 == ( nTemp32 & 0xffffff00 ) )
+ {
+ nFormat = GraphicFileFormat::JPG;
+ bRet = true;
+
+ if ( bExtendedInfo )
+ {
+ rStm.SeekRel( -2 );
+
+ ErrCode nError( rStm.GetError() );
+
+ bool bScanFailure = false;
+ bool bScanFinished = false;
+ MapMode aMap;
+
+ while (!bScanFailure && !bScanFinished && rStm.good())
+ {
+ sal_uInt8 nMarker = ImpDetectJPG_GetNextMarker( rStm );
+ switch( nMarker )
+ {
+ // fixed size marker, not having a two byte length parameter
+ case 0xd0 : // RST0
+ case 0xd1 :
+ case 0xd2 :
+ case 0xd3 :
+ case 0xd4 :
+ case 0xd5 :
+ case 0xd6 :
+ case 0xd7 : // RST7
+ case 0x01 : // TEM
+ break;
+
+ case 0xd8 : // SOI (has already been checked, there should not be a second one)
+ case 0x00 : // marker is invalid, we should stop now
+ bScanFailure = true;
+ break;
+
+ case 0xd9 : // EOI
+ bScanFinished = true;
+ break;
+
+ // per default we assume marker segments containing a length parameter
+ default :
+ {
+ sal_uInt16 nLength = 0;
+ rStm.ReadUInt16( nLength );
+
+ if ( nLength < 2 )
+ bScanFailure = true;
+ else
+ {
+ sal_uInt32 nNextMarkerPos = rStm.Tell() + nLength - 2;
+ switch( nMarker )
+ {
+ case 0xe0 : // APP0 Marker
+ {
+ if ( nLength == 16 )
+ {
+ sal_Int32 nIdentifier = 0;
+ rStm.ReadInt32( nIdentifier );
+ if ( nIdentifier == 0x4a464946 ) // JFIF Identifier
+ {
+ sal_uInt8 nStringTerminator = 0;
+ sal_uInt8 nMajorRevision = 0;
+ sal_uInt8 nMinorRevision = 0;
+ sal_uInt8 nUnits = 0;
+ sal_uInt16 nHorizontalResolution = 0;
+ sal_uInt16 nVerticalResolution = 0;
+ sal_uInt8 nHorzThumbnailPixelCount = 0;
+ sal_uInt8 nVertThumbnailPixelCount = 0;
+
+ rStm.ReadUChar( nStringTerminator )
+ .ReadUChar( nMajorRevision )
+ .ReadUChar( nMinorRevision )
+ .ReadUChar( nUnits )
+ .ReadUInt16( nHorizontalResolution )
+ .ReadUInt16( nVerticalResolution )
+ .ReadUChar( nHorzThumbnailPixelCount )
+ .ReadUChar( nVertThumbnailPixelCount );
+
+ // setting the logical size
+ if ( nUnits && nHorizontalResolution && nVerticalResolution )
+ {
+ aMap.SetMapUnit( nUnits == 1 ? MapUnit::MapInch : MapUnit::MapCM );
+ aMap.SetScaleX( Fraction( 1, nHorizontalResolution ) );
+ aMap.SetScaleY( Fraction( 1, nVerticalResolution ) );
+ aLogSize = OutputDevice::LogicToLogic( aPixSize, aMap, MapMode( MapUnit::Map100thMM ) );
+ }
+ }
+ }
+ }
+ break;
+
+ // Start of Frame Markers
+ case 0xc0 : // SOF0
+ case 0xc1 : // SOF1
+ case 0xc2 : // SOF2
+ case 0xc3 : // SOF3
+ case 0xc5 : // SOF5
+ case 0xc6 : // SOF6
+ case 0xc7 : // SOF7
+ case 0xc9 : // SOF9
+ case 0xca : // SOF10
+ case 0xcb : // SOF11
+ case 0xcd : // SOF13
+ case 0xce : // SOF14
+ case 0xcf : // SOF15
+ {
+ sal_uInt8 nSamplePrecision = 0;
+ sal_uInt16 nNumberOfLines = 0;
+ sal_uInt16 nSamplesPerLine = 0;
+ sal_uInt8 nNumberOfImageComponents = 0;
+ sal_uInt8 nComponentsIdentifier = 0;
+ sal_uInt8 nSamplingFactor = 0;
+ sal_uInt8 nQuantizationTableDestinationSelector = 0;
+ rStm.ReadUChar( nSamplePrecision )
+ .ReadUInt16( nNumberOfLines )
+ .ReadUInt16( nSamplesPerLine )
+ .ReadUChar( nNumberOfImageComponents )
+ .ReadUChar( nComponentsIdentifier )
+ .ReadUChar( nSamplingFactor )
+ .ReadUChar( nQuantizationTableDestinationSelector );
+ mnNumberOfImageComponents = nNumberOfImageComponents;
+
+ // nSamplingFactor (lower nibble: vertical,
+ // upper nibble: horizontal) is unused
+
+ aPixSize.setHeight( nNumberOfLines );
+ aPixSize.setWidth( nSamplesPerLine );
+ nBitsPerPixel = ( nNumberOfImageComponents == 3 ? 24 : nNumberOfImageComponents == 1 ? 8 : 0 );
+ nPlanes = 1;
+
+ if (aMap.GetMapUnit() != MapUnit::MapPixel)
+ // We already know the DPI, but the
+ // pixel size arrived later, so do the
+ // conversion again.
+ aLogSize = OutputDevice::LogicToLogic(
+ aPixSize, aMap, MapMode(MapUnit::Map100thMM));
+
+ bScanFinished = true;
+ }
+ break;
+ }
+ rStm.Seek( nNextMarkerPos );
+ }
+ }
+ break;
+ }
+ }
+ rStm.SetError( nError );
+ }
+ }
+ rStm.Seek( nStmPos );
+ return bRet;
+}
+
+bool GraphicDescriptor::ImpDetectPCD( SvStream& rStm, bool )
+{
+ bool bRet = false;
+
+ sal_Int32 nStmPos = rStm.Tell();
+ rStm.SetEndian( SvStreamEndian::LITTLE );
+
+ sal_uInt32 nTemp32 = 0;
+ sal_uInt16 nTemp16 = 0;
+ sal_uInt8 cByte = 0;
+
+ rStm.SeekRel( 2048 );
+ rStm.ReadUInt32( nTemp32 );
+ rStm.ReadUInt16( nTemp16 );
+ rStm.ReadUChar( cByte );
+
+ if ( ( nTemp32 == 0x5f444350 ) &&
+ ( nTemp16 == 0x5049 ) &&
+ ( cByte == 0x49 ) )
+ {
+ nFormat = GraphicFileFormat::PCD;
+ bRet = true;
+ }
+ rStm.Seek( nStmPos );
+ return bRet;
+}
+
+bool GraphicDescriptor::ImpDetectPCX( SvStream& rStm )
+{
+ // ! Because 0x0a can be interpreted as LF too ...
+ // we can't be sure that this special sign represent a PCX file only.
+ // Every Ascii file is possible here :-(
+ // We must detect the whole header.
+
+ bool bRet = false;
+ sal_uInt8 cByte = 0;
+
+ sal_Int32 nStmPos = rStm.Tell();
+ rStm.SetEndian( SvStreamEndian::LITTLE );
+ rStm.ReadUChar( cByte );
+
+ if ( cByte == 0x0a )
+ {
+ nFormat = GraphicFileFormat::PCX;
+
+ rStm.SeekRel( 1 );
+
+ // compression
+ rStm.ReadUChar( cByte );
+
+ bRet = (cByte==0 || cByte ==1);
+ if (bRet)
+ {
+ sal_uInt16 nTemp16;
+ sal_uInt16 nXmin;
+ sal_uInt16 nXmax;
+ sal_uInt16 nYmin;
+ sal_uInt16 nYmax;
+ sal_uInt16 nDPIx;
+ sal_uInt16 nDPIy;
+
+ // Bits/Pixel
+ rStm.ReadUChar( cByte );
+ nBitsPerPixel = cByte;
+
+ // image dimensions
+ rStm.ReadUInt16( nTemp16 );
+ nXmin = nTemp16;
+ rStm.ReadUInt16( nTemp16 );
+ nYmin = nTemp16;
+ rStm.ReadUInt16( nTemp16 );
+ nXmax = nTemp16;
+ rStm.ReadUInt16( nTemp16 );
+ nYmax = nTemp16;
+
+ aPixSize.setWidth( nXmax - nXmin + 1 );
+ aPixSize.setHeight( nYmax - nYmin + 1 );
+
+ // resolution
+ rStm.ReadUInt16( nTemp16 );
+ nDPIx = nTemp16;
+ rStm.ReadUInt16( nTemp16 );
+ nDPIy = nTemp16;
+
+ // set logical size
+ MapMode aMap( MapUnit::MapInch, Point(),
+ Fraction( 1, nDPIx ), Fraction( 1, nDPIy ) );
+ aLogSize = OutputDevice::LogicToLogic( aPixSize, aMap,
+ MapMode( MapUnit::Map100thMM ) );
+
+ // number of color planes
+ cByte = 5; // Illegal value in case of EOF.
+ rStm.SeekRel( 49 );
+ rStm.ReadUChar( cByte );
+ nPlanes = cByte;
+
+ bRet = (nPlanes<=4);
+ }
+ }
+
+ rStm.Seek( nStmPos );
+ return bRet;
+}
+
+bool GraphicDescriptor::ImpDetectPNG( SvStream& rStm, bool bExtendedInfo )
+{
+ sal_uInt32 nTemp32 = 0;
+ bool bRet = false;
+
+ sal_Int32 nStmPos = rStm.Tell();
+ rStm.SetEndian( SvStreamEndian::BIG );
+ rStm.ReadUInt32( nTemp32 );
+
+ if ( nTemp32 == 0x89504e47 )
+ {
+ rStm.ReadUInt32( nTemp32 );
+ if ( nTemp32 == 0x0d0a1a0a )
+ {
+ nFormat = GraphicFileFormat::PNG;
+ bRet = true;
+
+ if ( bExtendedInfo )
+ {
+ do {
+ sal_uInt8 cByte = 0;
+
+ // IHDR-Chunk
+ rStm.SeekRel( 8 );
+
+ // width
+ rStm.ReadUInt32( nTemp32 );
+ if (!rStm.good())
+ break;
+ aPixSize.setWidth( nTemp32 );
+
+ // height
+ rStm.ReadUInt32( nTemp32 );
+ if (!rStm.good())
+ break;
+ aPixSize.setHeight( nTemp32 );
+
+ // Bits/Pixel
+ rStm.ReadUChar( cByte );
+ if (!rStm.good())
+ break;
+ nBitsPerPixel = cByte;
+
+ // Colour type - check whether it supports alpha values
+ sal_uInt8 cColType = 0;
+ rStm.ReadUChar( cColType );
+ if (!rStm.good())
+ break;
+ bIsAlpha = bIsTransparent = ( cColType == 4 || cColType == 6 );
+
+ // Planes always 1;
+ // compression always
+ nPlanes = 1;
+
+ sal_uInt32 nLen32 = 0;
+ nTemp32 = 0;
+
+ rStm.SeekRel( 7 );
+
+ // read up to the start of the image
+ rStm.ReadUInt32( nLen32 );
+ rStm.ReadUInt32( nTemp32 );
+ while (rStm.good() && nTemp32 != 0x49444154)
+ {
+ if ( nTemp32 == 0x70485973 ) // physical pixel dimensions
+ {
+ sal_uLong nXRes;
+ sal_uLong nYRes;
+
+ // horizontal resolution
+ nTemp32 = 0;
+ rStm.ReadUInt32( nTemp32 );
+ nXRes = nTemp32;
+
+ // vertical resolution
+ nTemp32 = 0;
+ rStm.ReadUInt32( nTemp32 );
+ nYRes = nTemp32;
+
+ // unit
+ cByte = 0;
+ rStm.ReadUChar( cByte );
+
+ if ( cByte )
+ {
+ if ( nXRes )
+ aLogSize.setWidth( (aPixSize.Width() * 100000) / nXRes );
+
+ if ( nYRes )
+ aLogSize.setHeight( (aPixSize.Height() * 100000) / nYRes );
+ }
+
+ nLen32 -= 9;
+ }
+ else if ( nTemp32 == 0x74524e53 ) // transparency
+ {
+ bIsTransparent = true;
+ bIsAlpha = ( cColType != 0 && cColType != 2 );
+ }
+
+ // skip forward to next chunk
+ rStm.SeekRel( 4 + nLen32 );
+ rStm.ReadUInt32( nLen32 );
+ rStm.ReadUInt32( nTemp32 );
+ }
+ } while (false);
+ }
+ }
+ }
+ rStm.Seek( nStmPos );
+ return bRet;
+}
+
+bool GraphicDescriptor::ImpDetectTIF( SvStream& rStm, bool bExtendedInfo )
+{
+ bool bRet = false;
+ sal_uInt8 cByte1 = 0;
+ sal_uInt8 cByte2 = 1;
+
+ sal_Int32 nStmPos = rStm.Tell();
+ rStm.ReadUChar( cByte1 );
+ rStm.ReadUChar( cByte2 );
+ if ( cByte1 == cByte2 )
+ {
+ bool bDetectOk = false;
+
+ if ( cByte1 == 0x49 )
+ {
+ rStm.SetEndian( SvStreamEndian::LITTLE );
+ bDetectOk = true;
+ }
+ else if ( cByte1 == 0x4d )
+ {
+ rStm.SetEndian( SvStreamEndian::BIG );
+ bDetectOk = true;
+ }
+
+ if ( bDetectOk )
+ {
+ sal_uInt16 nTemp16 = 0;
+
+ rStm.ReadUInt16( nTemp16 );
+ if ( nTemp16 == 0x2a )
+ {
+ nFormat = GraphicFileFormat::TIF;
+ bRet = true;
+
+ if ( bExtendedInfo )
+ {
+ sal_uLong nCount;
+ sal_uLong nMax = DATA_SIZE - 48;
+ sal_uInt32 nTemp32 = 0;
+
+ // Offset of the first IFD
+ rStm.ReadUInt32( nTemp32 );
+ nCount = nTemp32 + 2;
+ rStm.SeekRel( nCount - 0x08 );
+
+ if ( nCount < nMax )
+ {
+ bool bOk = false;
+
+ // read tags till we find Tag256 ( Width )
+ // do not read more bytes than DATA_SIZE
+ rStm.ReadUInt16( nTemp16 );
+ while ( nTemp16 != 256 )
+ {
+ bOk = nCount < nMax;
+ if ( !bOk )
+ {
+ break;
+ }
+ rStm.SeekRel( 10 );
+ rStm.ReadUInt16( nTemp16 );
+ nCount += 12;
+ }
+
+ if ( bOk )
+ {
+ // width
+ rStm.ReadUInt16( nTemp16 );
+ rStm.SeekRel( 4 );
+ if ( nTemp16 == 3 )
+ {
+ rStm.ReadUInt16( nTemp16 );
+ aPixSize.setWidth( nTemp16 );
+ rStm.SeekRel( 2 );
+ }
+ else
+ {
+ rStm.ReadUInt32( nTemp32 );
+ aPixSize.setWidth( nTemp32 );
+ }
+
+ // height
+ rStm.SeekRel( 2 );
+ rStm.ReadUInt16( nTemp16 );
+ rStm.SeekRel( 4 );
+ if ( nTemp16 == 3 )
+ {
+ rStm.ReadUInt16( nTemp16 );
+ aPixSize.setHeight( nTemp16 );
+ rStm.SeekRel( 2 );
+ }
+ else
+ {
+ rStm.ReadUInt32( nTemp32 );
+ aPixSize.setHeight( nTemp32 );
+ }
+
+ // Bits/Pixel
+ rStm.ReadUInt16( nTemp16 );
+ if ( nTemp16 == 258 )
+ {
+ rStm.SeekRel( 6 );
+ rStm.ReadUInt16( nTemp16 );
+ nBitsPerPixel = nTemp16;
+ rStm.SeekRel( 2 );
+ }
+ else
+ rStm.SeekRel( -2 );
+
+ // compression
+ rStm.ReadUInt16( nTemp16 );
+ if ( nTemp16 == 259 )
+ {
+ rStm.SeekRel( 6 );
+ rStm.ReadUInt16( nTemp16 ); // compression
+ rStm.SeekRel( 2 );
+ }
+ else
+ rStm.SeekRel( -2 );
+ }
+ }
+ }
+ }
+ }
+ }
+ rStm.Seek( nStmPos );
+ return bRet;
+}
+
+bool GraphicDescriptor::ImpDetectXBM( SvStream&, bool )
+{
+ bool bRet = aPathExt.startsWith( "xbm" );
+ if (bRet)
+ nFormat = GraphicFileFormat::XBM;
+
+ return bRet;
+}
+
+bool GraphicDescriptor::ImpDetectXPM( SvStream&, bool )
+{
+ bool bRet = aPathExt.startsWith( "xpm" );
+ if (bRet)
+ nFormat = GraphicFileFormat::XPM;
+
+ return bRet;
+}
+
+bool GraphicDescriptor::ImpDetectPBM( SvStream& rStm, bool )
+{
+ bool bRet = false;
+
+ // check file extension first, as this trumps the 2 ID bytes
+ if ( aPathExt.startsWith( "pbm" ) )
+ bRet = true;
+ else
+ {
+ sal_Int32 nStmPos = rStm.Tell();
+ sal_uInt8 nFirst = 0, nSecond = 0;
+ rStm.ReadUChar( nFirst ).ReadUChar( nSecond );
+ if ( nFirst == 'P' && ( ( nSecond == '1' ) || ( nSecond == '4' ) ) )
+ bRet = true;
+ rStm.Seek( nStmPos );
+ }
+
+ if ( bRet )
+ nFormat = GraphicFileFormat::PBM;
+
+ return bRet;
+}
+
+bool GraphicDescriptor::ImpDetectPGM( SvStream& rStm, bool )
+{
+ bool bRet = false;
+
+ if ( aPathExt.startsWith( "pgm" ) )
+ bRet = true;
+ else
+ {
+ sal_uInt8 nFirst = 0, nSecond = 0;
+ sal_Int32 nStmPos = rStm.Tell();
+ rStm.ReadUChar( nFirst ).ReadUChar( nSecond );
+ if ( nFirst == 'P' && ( ( nSecond == '2' ) || ( nSecond == '5' ) ) )
+ bRet = true;
+ rStm.Seek( nStmPos );
+ }
+
+ if ( bRet )
+ nFormat = GraphicFileFormat::PGM;
+
+ return bRet;
+}
+
+bool GraphicDescriptor::ImpDetectPPM( SvStream& rStm, bool )
+{
+ bool bRet = false;
+
+ if ( aPathExt.startsWith( "ppm" ) )
+ bRet = true;
+ else
+ {
+ sal_uInt8 nFirst = 0, nSecond = 0;
+ sal_Int32 nStmPos = rStm.Tell();
+ rStm.ReadUChar( nFirst ).ReadUChar( nSecond );
+ if ( nFirst == 'P' && ( ( nSecond == '3' ) || ( nSecond == '6' ) ) )
+ bRet = true;
+ rStm.Seek( nStmPos );
+ }
+
+ if ( bRet )
+ nFormat = GraphicFileFormat::PPM;
+
+ return bRet;
+}
+
+bool GraphicDescriptor::ImpDetectRAS( SvStream& rStm, bool )
+{
+ sal_uInt32 nMagicNumber = 0;
+ bool bRet = false;
+ sal_Int32 nStmPos = rStm.Tell();
+ rStm.SetEndian( SvStreamEndian::BIG );
+ rStm.ReadUInt32( nMagicNumber );
+ if ( nMagicNumber == 0x59a66a95 )
+ {
+ nFormat = GraphicFileFormat::RAS;
+ bRet = true;
+ }
+ rStm.Seek( nStmPos );
+ return bRet;
+}
+
+bool GraphicDescriptor::ImpDetectTGA( SvStream&, bool )
+{
+ bool bRet = aPathExt.startsWith( "tga" );
+ if (bRet)
+ nFormat = GraphicFileFormat::TGA;
+
+ return bRet;
+}
+
+bool GraphicDescriptor::ImpDetectPSD( SvStream& rStm, bool bExtendedInfo )
+{
+ bool bRet = false;
+
+ sal_uInt32 nMagicNumber = 0;
+ sal_Int32 nStmPos = rStm.Tell();
+ rStm.SetEndian( SvStreamEndian::BIG );
+ rStm.ReadUInt32( nMagicNumber );
+ if ( nMagicNumber == 0x38425053 )
+ {
+ sal_uInt16 nVersion = 0;
+ rStm.ReadUInt16( nVersion );
+ if ( nVersion == 1 )
+ {
+ bRet = true;
+ if ( bExtendedInfo )
+ {
+ sal_uInt16 nChannels = 0;
+ sal_uInt32 nRows = 0;
+ sal_uInt32 nColumns = 0;
+ sal_uInt16 nDepth = 0;
+ sal_uInt16 nMode = 0;
+ rStm.SeekRel( 6 ); // Pad
+ rStm.ReadUInt16( nChannels ).ReadUInt32( nRows ).ReadUInt32( nColumns ).ReadUInt16( nDepth ).ReadUInt16( nMode );
+ if ( ( nDepth == 1 ) || ( nDepth == 8 ) || ( nDepth == 16 ) )
+ {
+ nBitsPerPixel = ( nDepth == 16 ) ? 8 : nDepth;
+ switch ( nChannels )
+ {
+ case 4 :
+ case 3 :
+ nBitsPerPixel = 24;
+ [[fallthrough]];
+ case 2 :
+ case 1 :
+ aPixSize.setWidth( nColumns );
+ aPixSize.setHeight( nRows );
+ break;
+ default:
+ bRet = false;
+ }
+ }
+ else
+ bRet = false;
+ }
+ }
+ }
+
+ if ( bRet )
+ nFormat = GraphicFileFormat::PSD;
+ rStm.Seek( nStmPos );
+ return bRet;
+}
+
+bool GraphicDescriptor::ImpDetectEPS( SvStream& rStm, bool )
+{
+ // check the EPS preview and the file extension
+ sal_uInt32 nFirstLong = 0;
+ sal_uInt8 nFirstBytes[20] = {};
+ bool bRet = false;
+
+ sal_Int32 nStmPos = rStm.Tell();
+ rStm.SetEndian( SvStreamEndian::BIG );
+ rStm.ReadUInt32( nFirstLong );
+ rStm.SeekRel( -4 );
+ rStm.ReadBytes( &nFirstBytes, 20 );
+
+ if ( ( nFirstLong == 0xC5D0D3C6 ) || aPathExt.startsWith( "eps" ) ||
+ ( ImplSearchEntry( nFirstBytes, reinterpret_cast<sal_uInt8 const *>("%!PS-Adobe"), 10, 10 )
+ && ImplSearchEntry( &nFirstBytes[15], reinterpret_cast<sal_uInt8 const *>("EPS"), 3, 3 ) ) )
+ {
+ nFormat = GraphicFileFormat::EPS;
+ bRet = true;
+ }
+ rStm.Seek( nStmPos );
+ return bRet;
+}
+
+bool GraphicDescriptor::ImpDetectDXF( SvStream&, bool )
+{
+ bool bRet = aPathExt.startsWith( "dxf" );
+ if (bRet)
+ nFormat = GraphicFileFormat::DXF;
+
+ return bRet;
+}
+
+bool GraphicDescriptor::ImpDetectMET( SvStream&, bool )
+{
+ bool bRet = aPathExt.startsWith( "met" );
+ if (bRet)
+ nFormat = GraphicFileFormat::MET;
+
+ return bRet;
+}
+
+bool GraphicDescriptor::ImpDetectPCT( SvStream& rStm, bool )
+{
+ bool bRet = aPathExt.startsWith( "pct" );
+ if (bRet)
+ nFormat = GraphicFileFormat::PCT;
+ else
+ {
+ sal_uInt64 const nStreamPos = rStm.Tell();
+ sal_uInt64 const nStreamLen = rStm.remainingSize();
+ if (isPCT(rStm, nStreamPos, nStreamLen))
+ {
+ bRet = true;
+ nFormat = GraphicFileFormat::PCT;
+ }
+ rStm.Seek(nStreamPos);
+ }
+
+ return bRet;
+}
+
+bool GraphicDescriptor::ImpDetectSVM( SvStream& rStm, bool bExtendedInfo )
+{
+ sal_uInt32 n32 = 0;
+ bool bRet = false;
+
+ sal_Int32 nStmPos = rStm.Tell();
+ rStm.SetEndian( SvStreamEndian::LITTLE );
+ rStm.ReadUInt32( n32 );
+ if ( n32 == 0x44475653 )
+ {
+ sal_uInt8 cByte = 0;
+ rStm.ReadUChar( cByte );
+ if ( cByte == 0x49 )
+ {
+ nFormat = GraphicFileFormat::SVM;
+ bRet = true;
+
+ if ( bExtendedInfo )
+ {
+ sal_uInt32 nTemp32;
+ sal_uInt16 nTemp16;
+
+ rStm.SeekRel( 0x04 );
+
+ // width
+ nTemp32 = 0;
+ rStm.ReadUInt32( nTemp32 );
+ aLogSize.setWidth( nTemp32 );
+
+ // height
+ nTemp32 = 0;
+ rStm.ReadUInt32( nTemp32 );
+ aLogSize.setHeight( nTemp32 );
+
+ // read MapUnit and determine PrefSize
+ nTemp16 = 0;
+ rStm.ReadUInt16( nTemp16 );
+ aLogSize = OutputDevice::LogicToLogic( aLogSize,
+ MapMode( static_cast<MapUnit>(nTemp16) ),
+ MapMode( MapUnit::Map100thMM ) );
+ }
+ }
+ }
+ else
+ {
+ rStm.SeekRel( -4 );
+ n32 = 0;
+ rStm.ReadUInt32( n32 );
+
+ if( n32 == 0x4D4C4356 )
+ {
+ sal_uInt16 nTmp16 = 0;
+
+ rStm.ReadUInt16( nTmp16 );
+
+ if( nTmp16 == 0x4654 )
+ {
+ nFormat = GraphicFileFormat::SVM;
+ bRet = true;
+
+ if( bExtendedInfo )
+ {
+ MapMode aMapMode;
+ rStm.SeekRel( 0x06 );
+ TypeSerializer aSerializer(rStm);
+ aSerializer.readMapMode(aMapMode);
+ aSerializer.readSize(aLogSize);
+ aLogSize = OutputDevice::LogicToLogic( aLogSize, aMapMode, MapMode( MapUnit::Map100thMM ) );
+ }
+ }
+ }
+ }
+ rStm.Seek( nStmPos );
+ return bRet;
+}
+
+bool GraphicDescriptor::ImpDetectWMF( SvStream&, bool )
+{
+ bool bRet = aPathExt.startsWith( "wmf" ) || aPathExt.startsWith( "wmz" );
+ if (bRet)
+ nFormat = GraphicFileFormat::WMF;
+
+ return bRet;
+}
+
+bool GraphicDescriptor::ImpDetectEMF(SvStream& rStm, bool bExtendedInfo)
+{
+ SvStream* aNewStream = &rStm;
+ SvMemoryStream aMemStream;
+ sal_uInt8 aUncompressedBuffer[EMF_CHECK_SIZE];
+ if (ZCodec::IsZCompressed(rStm))
+ {
+ ZCodec aCodec;
+ aCodec.BeginCompression(ZCODEC_DEFAULT_COMPRESSION, /*gzLib*/ true);
+ auto nDecompressLength = aCodec.Read(rStm, aUncompressedBuffer, EMF_CHECK_SIZE);
+ aCodec.EndCompression();
+ if (nDecompressLength != EMF_CHECK_SIZE)
+ return false;
+ aMemStream.SetBuffer(aUncompressedBuffer, EMF_CHECK_SIZE, EMF_CHECK_SIZE);
+ aNewStream = &aMemStream;
+ }
+
+ sal_uInt32 nRecordType = 0;
+ bool bRet = false;
+ sal_Int32 nStmPos = aNewStream->Tell();
+ aNewStream->SetEndian(SvStreamEndian::LITTLE);
+ aNewStream->ReadUInt32(nRecordType);
+ if (nRecordType == EMR_HEADER)
+ {
+ sal_Int32 nBoundLeft = 0, nBoundTop = 0, nBoundRight = 0, nBoundBottom = 0;
+ sal_Int32 nFrameLeft = 0, nFrameTop = 0, nFrameRight = 0, nFrameBottom = 0;
+ sal_uInt32 nSignature = 0;
+
+ aNewStream->SeekRel(4);
+ aNewStream->ReadInt32(nBoundLeft);
+ aNewStream->ReadInt32(nBoundTop);
+ aNewStream->ReadInt32(nBoundRight);
+ aNewStream->ReadInt32(nBoundBottom);
+ aNewStream->ReadInt32(nFrameLeft);
+ aNewStream->ReadInt32(nFrameTop);
+ aNewStream->ReadInt32(nFrameRight);
+ aNewStream->ReadInt32(nFrameBottom);
+ aNewStream->ReadUInt32(nSignature);
+
+ if (nSignature == ENHMETA_SIGNATURE)
+ {
+ nFormat = GraphicFileFormat::EMF;
+ bRet = true;
+
+ if (bExtendedInfo)
+ {
+ // size in pixels
+ aPixSize.setWidth(nBoundRight - nBoundLeft + 1);
+ aPixSize.setHeight(nBoundBottom - nBoundTop + 1);
+
+ // size in 0.01mm units
+ aLogSize.setWidth(nFrameRight - nFrameLeft + 1);
+ aLogSize.setHeight(nFrameBottom - nFrameTop + 1);
+ }
+ }
+ }
+
+ rStm.Seek(nStmPos);
+ return bRet;
+}
+
+bool GraphicDescriptor::ImpDetectSVG( SvStream& /*rStm*/, bool /*bExtendedInfo*/ )
+{
+ bool bRet = aPathExt.startsWith( "svg" );
+ if (bRet)
+ nFormat = GraphicFileFormat::SVG;
+
+ return bRet;
+}
+
+bool GraphicDescriptor::ImpDetectWEBP( SvStream& rStm, bool bExtendedInfo )
+{
+ sal_uInt32 nTemp32 = 0;
+ bool bRet = false;
+
+ sal_Int32 nStmPos = rStm.Tell();
+ rStm.SetEndian( SvStreamEndian::BIG );
+ rStm.ReadUInt32( nTemp32 );
+
+ if ( nTemp32 == 0x52494646 )
+ {
+ rStm.ReadUInt32( nTemp32 ); // skip
+ rStm.ReadUInt32( nTemp32 );
+ if ( nTemp32 == 0x57454250 )
+ {
+ nFormat = GraphicFileFormat::WEBP;
+ bRet = true;
+
+ if ( bExtendedInfo )
+ {
+ rStm.Seek(nStmPos);
+ ReadWebpInfo(rStm, aPixSize, nBitsPerPixel, bIsAlpha );
+ bIsTransparent = bIsAlpha;
+ }
+ }
+ }
+ rStm.Seek( nStmPos );
+ return bRet;
+}
+
+OUString GraphicDescriptor::GetImportFormatShortName( GraphicFileFormat nFormat )
+{
+ const char *pKeyName = nullptr;
+
+ switch( nFormat )
+ {
+ case GraphicFileFormat::BMP : pKeyName = "bmp"; break;
+ case GraphicFileFormat::GIF : pKeyName = "gif"; break;
+ case GraphicFileFormat::JPG : pKeyName = "jpg"; break;
+ case GraphicFileFormat::PCD : pKeyName = "pcd"; break;
+ case GraphicFileFormat::PCX : pKeyName = "pcx"; break;
+ case GraphicFileFormat::PNG : pKeyName = "png"; break;
+ case GraphicFileFormat::XBM : pKeyName = "xbm"; break;
+ case GraphicFileFormat::XPM : pKeyName = "xpm"; break;
+ case GraphicFileFormat::PBM : pKeyName = "pbm"; break;
+ case GraphicFileFormat::PGM : pKeyName = "pgm"; break;
+ case GraphicFileFormat::PPM : pKeyName = "ppm"; break;
+ case GraphicFileFormat::RAS : pKeyName = "ras"; break;
+ case GraphicFileFormat::TGA : pKeyName = "tga"; break;
+ case GraphicFileFormat::PSD : pKeyName = "psd"; break;
+ case GraphicFileFormat::EPS : pKeyName = "eps"; break;
+ case GraphicFileFormat::TIF : pKeyName = "tif"; break;
+ case GraphicFileFormat::DXF : pKeyName = "dxf"; break;
+ case GraphicFileFormat::MET : pKeyName = "met"; break;
+ case GraphicFileFormat::PCT : pKeyName = "pct"; break;
+ case GraphicFileFormat::SVM : pKeyName = "svm"; break;
+ case GraphicFileFormat::WMF : pKeyName = "wmf"; break;
+ case GraphicFileFormat::EMF : pKeyName = "emf"; break;
+ case GraphicFileFormat::SVG : pKeyName = "svg"; break;
+ case GraphicFileFormat::WEBP : pKeyName = "webp"; break;
+ default: assert(false);
+ }
+
+ return OUString::createFromAscii(pKeyName);
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/vcl/source/filter/graphicfilter_internal.hxx b/vcl/source/filter/graphicfilter_internal.hxx
new file mode 100644
index 000000000..87f328fce
--- /dev/null
+++ b/vcl/source/filter/graphicfilter_internal.hxx
@@ -0,0 +1,32 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#ifndef INCLUDED_VCL_SOURCE_FILTER_GRAPHICFILTER_INTERNAL_HXX
+#define INCLUDED_VCL_SOURCE_FILTER_GRAPHICFILTER_INTERNAL_HXX
+
+#include <tools/solar.h>
+#include <tools/stream.hxx>
+
+sal_uInt8* ImplSearchEntry(sal_uInt8*, sal_uInt8 const*, sal_uLong, sal_uLong);
+
+extern bool isPCT(SvStream& rStream, sal_uLong nStreamPos, sal_uLong nStreamLen);
+
+#endif // INCLUDED_VCL_SOURCE_FILTER_GRAPHICFILTER_INTERNAL_HXX
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/vcl/source/filter/idxf/dxf2mtf.cxx b/vcl/source/filter/idxf/dxf2mtf.cxx
new file mode 100644
index 000000000..2b26abffd
--- /dev/null
+++ b/vcl/source/filter/idxf/dxf2mtf.cxx
@@ -0,0 +1,902 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+
+#include <unotools/configmgr.hxx>
+#include <vcl/gdimtf.hxx>
+#include <vcl/metaact.hxx>
+#include <vcl/virdev.hxx>
+#include <tools/poly.hxx>
+#include "dxf2mtf.hxx"
+
+#include <math.h>
+
+
+sal_uInt64 DXF2GDIMetaFile::CountEntities(const DXFEntities & rEntities)
+{
+ const DXFBasicEntity * pBE;
+ sal_uInt64 nRes;
+
+ nRes=0;
+ for (pBE=rEntities.pFirst; pBE!=nullptr; pBE=pBE->pSucc) nRes++;
+ return nRes;
+}
+
+Color DXF2GDIMetaFile::ConvertColor(sal_uInt8 nColor) const
+{
+ return Color(
+ pDXF->aPalette.GetRed( nColor ),
+ pDXF->aPalette.GetGreen( nColor ),
+ pDXF->aPalette.GetBlue( nColor ) );
+}
+
+tools::Long DXF2GDIMetaFile::GetEntityColor(const DXFBasicEntity & rE) const
+{
+ tools::Long nColor;
+
+ nColor=rE.nColor;
+ if (nColor==256) {
+ if (rE.m_sLayer.getLength() < 2) {
+ nColor=nParentLayerColor;
+ } else {
+ const DXFLayer * pLayer=pDXF->aTables.SearchLayer(rE.m_sLayer);
+ if (pLayer!=nullptr) nColor=pLayer->nColor;
+ else nColor=nParentLayerColor;
+ }
+ }
+ else if (nColor==0) nColor=nBlockColor;
+ return nColor;
+}
+
+DXFLineInfo DXF2GDIMetaFile::LTypeToDXFLineInfo(std::string_view rLineType) const
+{
+ const DXFLType * pLT;
+ DXFLineInfo aDXFLineInfo;
+
+ pLT = pDXF->aTables.SearchLType(rLineType);
+ if (pLT==nullptr || pLT->nDashCount == 0) {
+ aDXFLineInfo.eStyle = LineStyle::Solid;
+ }
+ else {
+ aDXFLineInfo.eStyle = LineStyle::Dash;
+ for (tools::Long i=0; i < (pLT->nDashCount); i++) {
+ const double x = pLT->fDash[i] * pDXF->getGlobalLineTypeScale();
+ if ( x >= 0.0 ) {
+ if ( aDXFLineInfo.nDotCount == 0 ) {
+ aDXFLineInfo.nDotCount ++;
+ aDXFLineInfo.fDotLen = x;
+ }
+ else if ( aDXFLineInfo.fDotLen == x ) {
+ aDXFLineInfo.nDotCount ++;
+ }
+ else if ( aDXFLineInfo.nDashCount == 0 ) {
+ aDXFLineInfo.nDashCount ++;
+ aDXFLineInfo.fDashLen = x;
+ }
+ else if ( aDXFLineInfo.fDashLen == x ) {
+ aDXFLineInfo.nDashCount ++;
+ }
+ else {
+ // It is impossible to be converted.
+ }
+ }
+ else {
+ if ( aDXFLineInfo.fDistance == 0 ) {
+ aDXFLineInfo.fDistance = -1 * x;
+ }
+ else {
+ // It is impossible to be converted.
+ }
+ }
+
+ }
+ }
+
+ return aDXFLineInfo;
+}
+
+DXFLineInfo DXF2GDIMetaFile::GetEntityDXFLineInfo(const DXFBasicEntity & rE)
+{
+ DXFLineInfo aDXFLineInfo;
+
+ aDXFLineInfo.eStyle = LineStyle::Solid;
+ aDXFLineInfo.nDashCount = 0;
+ aDXFLineInfo.fDashLen = 0;
+ aDXFLineInfo.nDotCount = 0;
+ aDXFLineInfo.fDotLen = 0;
+ aDXFLineInfo.fDistance = 0;
+
+ if (rE.m_sLineType == "BYLAYER") {
+ if (rE.m_sLayer.getLength() < 2) {
+ aDXFLineInfo=aParentLayerDXFLineInfo;
+ } else {
+ const DXFLayer * pLayer=pDXF->aTables.SearchLayer(rE.m_sLayer);
+ if (pLayer!=nullptr) {
+ aDXFLineInfo = LTypeToDXFLineInfo(pLayer->m_sLineType);
+ }
+ else aDXFLineInfo=aParentLayerDXFLineInfo;
+ }
+ }
+ else if (rE.m_sLineType == "BYBLOCK") {
+ aDXFLineInfo=aBlockDXFLineInfo;
+ }
+ else {
+ aDXFLineInfo = LTypeToDXFLineInfo(rE.m_sLineType);
+ }
+ return aDXFLineInfo;
+}
+
+
+bool DXF2GDIMetaFile::SetLineAttribute(const DXFBasicEntity & rE)
+{
+ tools::Long nColor;
+ Color aColor;
+
+ nColor=GetEntityColor(rE);
+ if (nColor<0) return false;
+ aColor=ConvertColor(static_cast<sal_uInt8>(nColor));
+
+ if (aActLineColor!=aColor) {
+ aActLineColor = aColor;
+ pVirDev->SetLineColor( aActLineColor );
+ }
+
+ if (aActFillColor!=COL_TRANSPARENT) {
+ aActFillColor = COL_TRANSPARENT;
+ pVirDev->SetFillColor(aActFillColor);
+ }
+ return true;
+}
+
+
+bool DXF2GDIMetaFile::SetAreaAttribute(const DXFBasicEntity & rE)
+{
+ tools::Long nColor;
+ Color aColor;
+
+ nColor=GetEntityColor(rE);
+ if (nColor<0) return false;
+ aColor=ConvertColor(static_cast<sal_uInt8>(nColor));
+
+ if (aActLineColor!=aColor) {
+ aActLineColor = aColor;
+ pVirDev->SetLineColor( aActLineColor );
+ }
+
+ if ( aActFillColor == COL_TRANSPARENT || aActFillColor != aColor) {
+ aActFillColor = aColor;
+ pVirDev->SetFillColor( aActFillColor );
+ }
+ return true;
+}
+
+
+bool DXF2GDIMetaFile::SetFontAttribute(const DXFBasicEntity & rE, short nAngle, sal_uInt16 nHeight)
+{
+ tools::Long nColor;
+ Color aColor;
+ vcl::Font aFont;
+
+ nAngle=-nAngle;
+ while (nAngle>=3600) nAngle-=3600;
+ while (nAngle<0) nAngle+=3600;
+
+ nColor=GetEntityColor(rE);
+ if (nColor<0) return false;
+ aColor=ConvertColor(static_cast<sal_uInt8>(nColor));
+
+ aFont.SetColor(aColor);
+ aFont.SetTransparent(true);
+ aFont.SetFamily(FAMILY_SWISS);
+ aFont.SetFontSize(Size(0,nHeight));
+ aFont.SetAlignment(ALIGN_BASELINE);
+ aFont.SetOrientation(Degree10(nAngle));
+ if (aActFont!=aFont) {
+ aActFont=aFont;
+ pVirDev->SetFont(aActFont);
+ }
+
+ return true;
+}
+
+
+void DXF2GDIMetaFile::DrawLineEntity(const DXFLineEntity & rE, const DXFTransform & rTransform)
+{
+ if (!SetLineAttribute(rE))
+ return;
+
+ Point aP0,aP1;
+ rTransform.Transform(rE.aP0,aP0);
+ rTransform.Transform(rE.aP1,aP1);
+
+ DXFLineInfo aDXFLineInfo=GetEntityDXFLineInfo(rE);
+ LineInfo aLineInfo;
+ aLineInfo = rTransform.Transform(aDXFLineInfo);
+
+ pVirDev->DrawLine(aP0,aP1,aLineInfo);
+ if (rE.fThickness!=0) {
+ Point aP2,aP3;
+ rTransform.Transform(rE.aP0+DXFVector(0,0,rE.fThickness),aP2);
+ rTransform.Transform(rE.aP1+DXFVector(0,0,rE.fThickness),aP3);
+ DrawLine(aP2,aP3);
+ DrawLine(aP0,aP2);
+ DrawLine(aP1,aP3);
+ }
+}
+
+
+void DXF2GDIMetaFile::DrawPointEntity(const DXFPointEntity & rE, const DXFTransform & rTransform)
+{
+
+ if (SetLineAttribute(rE)) {
+ Point aP0;
+ rTransform.Transform(rE.aP0,aP0);
+ if (rE.fThickness==0) pVirDev->DrawPixel(aP0);
+ else {
+ Point aP1;
+ rTransform.Transform(rE.aP0+DXFVector(0,0,rE.fThickness),aP1);
+ DrawLine(aP0,aP1);
+ }
+ }
+}
+
+
+void DXF2GDIMetaFile::DrawCircleEntity(const DXFCircleEntity & rE, const DXFTransform & rTransform)
+{
+ double frx,fry;
+ sal_uInt16 nPoints,i;
+ DXFVector aC;
+
+ if (!SetLineAttribute(rE)) return;
+ rTransform.Transform(rE.aP0,aC);
+ if (rE.fThickness==0 && rTransform.TransCircleToEllipse(rE.fRadius,frx,fry)) {
+ pVirDev->DrawEllipse(
+ tools::Rectangle(static_cast<tools::Long>(aC.fx-frx+0.5),static_cast<tools::Long>(aC.fy-fry+0.5),
+ static_cast<tools::Long>(aC.fx+frx+0.5),static_cast<tools::Long>(aC.fy+fry+0.5)));
+ }
+ else {
+ double fAng;
+ nPoints=OptPointsPerCircle;
+ tools::Polygon aPoly(nPoints);
+ for (i=0; i<nPoints; i++) {
+ fAng=2*M_PI/static_cast<double>(nPoints-1)*static_cast<double>(i);
+ rTransform.Transform(
+ rE.aP0+DXFVector(rE.fRadius*cos(fAng),rE.fRadius*sin(fAng),0),
+ aPoly[i]
+ );
+ }
+ pVirDev->DrawPolyLine(aPoly);
+ if (rE.fThickness!=0) {
+ tools::Polygon aPoly2(nPoints);
+ for (i=0; i<nPoints; i++) {
+ fAng=2*M_PI/static_cast<double>(nPoints-1)*static_cast<double>(i);
+ rTransform.Transform(
+ rE.aP0+DXFVector(rE.fRadius*cos(fAng),rE.fRadius*sin(fAng),rE.fThickness),
+ aPoly2[i]
+ );
+
+ }
+ pVirDev->DrawPolyLine(aPoly2);
+ for (i=0; i<nPoints-1; i++) DrawLine(aPoly[i],aPoly2[i]);
+ }
+ }
+}
+
+void DXF2GDIMetaFile::DrawLine(const Point& rA, const Point& rB)
+{
+ if (utl::ConfigManager::IsFuzzing())
+ return;
+ GDIMetaFile* pMetaFile = pVirDev->GetConnectMetaFile();
+ assert(pMetaFile);
+ //use AddAction instead of OutputDevice::DrawLine so that we can explicitly share
+ //the aDefaultLineInfo between the MetaLineActions to reduce memory use
+ pMetaFile->AddAction(new MetaLineAction(rA, rB, aDefaultLineInfo));
+}
+
+void DXF2GDIMetaFile::DrawArcEntity(const DXFArcEntity & rE, const DXFTransform & rTransform)
+{
+ double frx,fry;
+ sal_uInt16 nPoints,i;
+ DXFVector aC;
+
+ if (!SetLineAttribute(rE)) return;
+ double fA1=rE.fStart;
+ double fdA=rE.fEnd-fA1;
+ fdA = fmod(fdA, 360.0);
+ if (fdA<=0) fdA+=360.0;
+ rTransform.Transform(rE.aP0,aC);
+ if (rE.fThickness==0 && fdA>5.0 && rTransform.TransCircleToEllipse(rE.fRadius,frx,fry)) {
+ DXFVector aVS(cos(basegfx::deg2rad(fA1)),sin(basegfx::deg2rad(fA1)),0.0);
+ aVS*=rE.fRadius;
+ aVS+=rE.aP0;
+ DXFVector aVE(cos(basegfx::deg2rad(fA1+fdA)),sin(basegfx::deg2rad(fA1+fdA)),0.0);
+ aVE*=rE.fRadius;
+ aVE+=rE.aP0;
+ Point aPS,aPE;
+ if (rTransform.Mirror()) {
+ rTransform.Transform(aVS,aPS);
+ rTransform.Transform(aVE,aPE);
+ }
+ else {
+ rTransform.Transform(aVS,aPE);
+ rTransform.Transform(aVE,aPS);
+ }
+ pVirDev->DrawArc(
+ tools::Rectangle(static_cast<tools::Long>(aC.fx-frx+0.5),static_cast<tools::Long>(aC.fy-fry+0.5),
+ static_cast<tools::Long>(aC.fx+frx+0.5),static_cast<tools::Long>(aC.fy+fry+0.5)),
+ aPS,aPE
+ );
+ }
+ else {
+ double fAng;
+ nPoints=static_cast<sal_uInt16>(fdA/360.0*static_cast<double>(OptPointsPerCircle)+0.5);
+ if (nPoints<2) nPoints=2;
+ tools::Polygon aPoly(nPoints);
+ for (i=0; i<nPoints; i++) {
+ fAng=basegfx::deg2rad( fA1 + fdA/static_cast<double>(nPoints-1)*static_cast<double>(i) );
+ rTransform.Transform(
+ rE.aP0+DXFVector(rE.fRadius*cos(fAng),rE.fRadius*sin(fAng),0),
+ aPoly[i]
+ );
+ }
+ pVirDev->DrawPolyLine(aPoly);
+ if (rE.fThickness!=0) {
+ tools::Polygon aPoly2(nPoints);
+ for (i=0; i<nPoints; i++) {
+ fAng=basegfx::deg2rad( fA1 + fdA/static_cast<double>(nPoints-1)*static_cast<double>(i) );
+ rTransform.Transform(
+ rE.aP0+DXFVector(rE.fRadius*cos(fAng),rE.fRadius*sin(fAng),rE.fThickness),
+ aPoly2[i]
+ );
+ }
+ pVirDev->DrawPolyLine(aPoly2);
+ for (i=0; i<nPoints; i++)
+ DrawLine(aPoly[i], aPoly2[i]);
+ }
+ }
+}
+
+void DXF2GDIMetaFile::DrawTraceEntity(const DXFTraceEntity & rE, const DXFTransform & rTransform)
+{
+ if (!SetLineAttribute(rE))
+ return;
+
+ tools::Polygon aPoly(4);
+ rTransform.Transform(rE.aP0,aPoly[0]);
+ rTransform.Transform(rE.aP1,aPoly[1]);
+ rTransform.Transform(rE.aP3,aPoly[2]);
+ rTransform.Transform(rE.aP2,aPoly[3]);
+ pVirDev->DrawPolygon(aPoly);
+ if (rE.fThickness!=0) {
+ sal_uInt16 i;
+ tools::Polygon aPoly2(4);
+ DXFVector aVAdd(0,0,rE.fThickness);
+ rTransform.Transform(rE.aP0+aVAdd,aPoly2[0]);
+ rTransform.Transform(rE.aP1+aVAdd,aPoly2[1]);
+ rTransform.Transform(rE.aP3+aVAdd,aPoly2[2]);
+ rTransform.Transform(rE.aP2+aVAdd,aPoly2[3]);
+ pVirDev->DrawPolygon(aPoly2);
+ for (i=0; i<4; i++) DrawLine(aPoly[i],aPoly2[i]);
+ }
+}
+
+
+void DXF2GDIMetaFile::DrawSolidEntity(const DXFSolidEntity & rE, const DXFTransform & rTransform)
+{
+ if (!SetAreaAttribute(rE))
+ return;
+
+ sal_uInt16 nN;
+ if (rE.aP2==rE.aP3) nN=3; else nN=4;
+ tools::Polygon aPoly(nN);
+ rTransform.Transform(rE.aP0,aPoly[0]);
+ rTransform.Transform(rE.aP1,aPoly[1]);
+ rTransform.Transform(rE.aP3,aPoly[2]);
+ if (nN>3) rTransform.Transform(rE.aP2,aPoly[3]);
+ pVirDev->DrawPolygon(aPoly);
+ if (rE.fThickness==0) return;
+
+ tools::Polygon aPoly2(nN);
+ DXFVector aVAdd(0,0,rE.fThickness);
+ rTransform.Transform(rE.aP0+aVAdd,aPoly2[0]);
+ rTransform.Transform(rE.aP1+aVAdd,aPoly2[1]);
+ rTransform.Transform(rE.aP3+aVAdd,aPoly2[2]);
+ if (nN>3) rTransform.Transform(rE.aP2+aVAdd,aPoly2[3]);
+ pVirDev->DrawPolygon(aPoly2);
+ if (SetLineAttribute(rE)) {
+ sal_uInt16 i;
+ for (i=0; i<nN; i++) DrawLine(aPoly[i],aPoly2[i]);
+ }
+}
+
+
+void DXF2GDIMetaFile::DrawTextEntity(const DXFTextEntity & rE, const DXFTransform & rTransform)
+{
+ DXFVector aV;
+ double fA;
+ sal_uInt16 nHeight;
+ short nAng;
+ DXFTransform aT( DXFTransform(rE.fXScale,rE.fHeight,1.0,rE.fRotAngle,rE.aP0), rTransform );
+ aT.TransDir(DXFVector(0,1,0),aV);
+ nHeight=static_cast<sal_uInt16>(aV.Abs()+0.5);
+ fA=aT.CalcRotAngle();
+ nAng=static_cast<short>(fA*10.0+0.5);
+ aT.TransDir(DXFVector(1,0,0),aV);
+ if ( SetFontAttribute( rE,nAng, nHeight ) )
+ {
+ OUString const aUString(pDXF->ToOUString(rE.m_sText));
+ Point aPt;
+ aT.Transform( DXFVector( 0, 0, 0 ), aPt );
+ pVirDev->DrawText( aPt, aUString );
+ }
+}
+
+
+void DXF2GDIMetaFile::DrawInsertEntity(const DXFInsertEntity & rE, const DXFTransform & rTransform)
+{
+ const DXFBlock * pB;
+ pB=pDXF->aBlocks.Search(rE.m_sName);
+ if (pB==nullptr)
+ return;
+
+ DXFTransform aDXFTransform1(1.0,1.0,1.0,DXFVector(0.0,0.0,0.0)-pB->aBasePoint);
+ DXFTransform aDXFTransform2(rE.fXScale,rE.fYScale,rE.fZScale,rE.fRotAngle,rE.aP0);
+ DXFTransform aT(
+ DXFTransform( aDXFTransform1, aDXFTransform2 ),
+ rTransform
+ );
+ tools::Long nSavedBlockColor, nSavedParentLayerColor;
+ DXFLineInfo aSavedBlockDXFLineInfo, aSavedParentLayerDXFLineInfo;
+ nSavedBlockColor=nBlockColor;
+ nSavedParentLayerColor=nParentLayerColor;
+ aSavedBlockDXFLineInfo=aBlockDXFLineInfo;
+ aSavedParentLayerDXFLineInfo=aParentLayerDXFLineInfo;
+ nBlockColor=GetEntityColor(rE);
+ aBlockDXFLineInfo=GetEntityDXFLineInfo(rE);
+ if (rE.m_sLayer.getLength() > 1) {
+ DXFLayer * pLayer=pDXF->aTables.SearchLayer(rE.m_sLayer);
+ if (pLayer!=nullptr) {
+ nParentLayerColor=pLayer->nColor;
+ aParentLayerDXFLineInfo = LTypeToDXFLineInfo(pLayer->m_sLineType);
+ }
+ }
+ DrawEntities(*pB,aT);
+ aBlockDXFLineInfo=aSavedBlockDXFLineInfo;
+ aParentLayerDXFLineInfo=aSavedParentLayerDXFLineInfo;
+ nBlockColor=nSavedBlockColor;
+ nParentLayerColor=nSavedParentLayerColor;
+}
+
+
+void DXF2GDIMetaFile::DrawAttribEntity(const DXFAttribEntity & rE, const DXFTransform & rTransform)
+{
+ if ((rE.nAttrFlags&1)!=0)
+ return;
+
+ DXFVector aV;
+ double fA;
+ sal_uInt16 nHeight;
+ short nAng;
+ DXFTransform aT( DXFTransform( rE.fXScale, rE.fHeight, 1.0, rE.fRotAngle, rE.aP0 ), rTransform );
+ aT.TransDir(DXFVector(0,1,0),aV);
+ nHeight=static_cast<sal_uInt16>(aV.Abs()+0.5);
+ fA=aT.CalcRotAngle();
+ nAng=static_cast<short>(fA*10.0+0.5);
+ aT.TransDir(DXFVector(1,0,0),aV);
+ if (SetFontAttribute(rE,nAng,nHeight))
+ {
+ OUString const aUString(pDXF->ToOUString(rE.m_sText));
+ Point aPt;
+ aT.Transform( DXFVector( 0, 0, 0 ), aPt );
+ pVirDev->DrawText( aPt, aUString );
+ }
+}
+
+
+void DXF2GDIMetaFile::DrawPolyLineEntity(const DXFPolyLineEntity & rE, const DXFTransform & rTransform)
+{
+ sal_uInt16 i,nPolySize;
+ const DXFBasicEntity * pBE;
+
+ nPolySize=0;
+ pBE=rE.pSucc;
+ while (pBE!=nullptr && pBE->eType==DXF_VERTEX) {
+ nPolySize++;
+ pBE=pBE->pSucc;
+ }
+ if (nPolySize<2)
+ return;
+ tools::Polygon aPoly(nPolySize);
+ pBE=rE.pSucc;
+ for (i=0; i<nPolySize; i++) {
+ rTransform.Transform(static_cast<const DXFVertexEntity*>(pBE)->aP0,aPoly[i]);
+ pBE=pBE->pSucc;
+ }
+
+ if (!SetLineAttribute(rE))
+ return;
+
+ if ((rE.nFlags&1)!=0) pVirDev->DrawPolygon(aPoly);
+ else pVirDev->DrawPolyLine(aPoly);
+ if (rE.fThickness==0)
+ return;
+
+ tools::Polygon aPoly2(nPolySize);
+ pBE=rE.pSucc;
+ for (i=0; i<nPolySize; i++) {
+ rTransform.Transform(
+ (static_cast<const DXFVertexEntity*>(pBE)->aP0)+DXFVector(0,0,rE.fThickness),
+ aPoly2[i]
+ );
+ pBE=pBE->pSucc;
+ }
+ if ((rE.nFlags&1)!=0) pVirDev->DrawPolygon(aPoly2);
+ else pVirDev->DrawPolyLine(aPoly2);
+ for (i=0; i<nPolySize; i++) DrawLine(aPoly[i],aPoly2[i]);
+}
+
+void DXF2GDIMetaFile::DrawLWPolyLineEntity(const DXFLWPolyLineEntity & rE, const DXFTransform & rTransform )
+{
+ sal_Int32 nPolySize = rE.aP.size();
+ if (!nPolySize)
+ return;
+
+ tools::Polygon aPoly( static_cast<sal_uInt16>(nPolySize));
+ for (sal_Int32 i = 0; i < nPolySize; ++i)
+ {
+ rTransform.Transform( rE.aP[ static_cast<sal_uInt16>(i) ], aPoly[ static_cast<sal_uInt16>(i) ] );
+ }
+ if ( SetLineAttribute( rE ) )
+ {
+ if ( ( rE.nFlags & 1 ) != 0 )
+ pVirDev->DrawPolygon( aPoly );
+ else
+ pVirDev->DrawPolyLine( aPoly );
+ }
+}
+
+void DXF2GDIMetaFile::DrawHatchEntity(const DXFHatchEntity & rE, const DXFTransform & rTransform )
+{
+ if (rE.aBoundaryPathData.empty())
+ return;
+
+ SetAreaAttribute( rE );
+ tools::PolyPolygon aPolyPoly;
+ for (const DXFBoundaryPathData& rPathData : rE.aBoundaryPathData)
+ {
+ std::vector< Point > aPtAry;
+ if ( rPathData.bIsPolyLine )
+ {
+ for (const auto& a : rPathData.aP)
+ {
+ Point aPt;
+ rTransform.Transform(a, aPt);
+ aPtAry.push_back( aPt );
+ }
+ }
+ else
+ {
+ for ( auto& rEdge : rPathData.aEdges )
+ {
+ const DXFEdgeType* pEdge = rEdge.get();
+ switch( pEdge->nEdgeType )
+ {
+ case 1 :
+ {
+ Point aPt;
+ rTransform.Transform( static_cast<const DXFEdgeTypeLine*>(pEdge)->aStartPoint, aPt );
+ aPtAry.push_back( aPt );
+ rTransform.Transform( static_cast<const DXFEdgeTypeLine*>(pEdge)->aEndPoint, aPt );
+ aPtAry.push_back( aPt );
+ }
+ break;
+ case 2 :
+ case 3 :
+ case 4 :
+ break;
+ }
+ }
+ }
+ sal_uInt16 i, nSize = static_cast<sal_uInt16>(aPtAry.size());
+ if ( nSize )
+ {
+ tools::Polygon aPoly( nSize );
+ for ( i = 0; i < nSize; i++ )
+ aPoly[ i ] = aPtAry[ i ];
+ aPolyPoly.Insert( aPoly );
+ }
+ }
+ if ( aPolyPoly.Count() )
+ pVirDev->DrawPolyPolygon( aPolyPoly );
+}
+
+void DXF2GDIMetaFile::Draw3DFaceEntity(const DXF3DFaceEntity & rE, const DXFTransform & rTransform)
+{
+ sal_uInt16 nN,i;
+ if (!SetLineAttribute(rE))
+ return;
+
+ if (rE.aP2==rE.aP3) nN=3; else nN=4;
+ tools::Polygon aPoly(nN);
+ rTransform.Transform(rE.aP0,aPoly[0]);
+ rTransform.Transform(rE.aP1,aPoly[1]);
+ rTransform.Transform(rE.aP2,aPoly[2]);
+ if (nN>3) rTransform.Transform(rE.aP3,aPoly[3]);
+ if ((rE.nIEFlags&0x0f)==0) pVirDev->DrawPolygon(aPoly);
+ else {
+ for (i=0; i<nN; i++) {
+ if ( (rE.nIEFlags & (static_cast<tools::Long>(1)<<i)) == 0 ) {
+ DrawLine(aPoly[i],aPoly[(i+1)%nN]);
+ }
+ }
+ }
+}
+
+void DXF2GDIMetaFile::DrawDimensionEntity(const DXFDimensionEntity & rE, const DXFTransform & rTransform)
+{
+ const DXFBlock * pB;
+ pB=pDXF->aBlocks.Search(rE.m_sPseudoBlock);
+ if (pB==nullptr)
+ return;
+
+ DXFTransform aT(
+ DXFTransform(1.0,1.0,1.0,DXFVector(0.0,0.0,0.0)-pB->aBasePoint),
+ rTransform
+ );
+ tools::Long nSavedBlockColor, nSavedParentLayerColor;
+ DXFLineInfo aSavedBlockDXFLineInfo, aSavedParentLayerDXFLineInfo;
+ nSavedBlockColor=nBlockColor;
+ nSavedParentLayerColor=nParentLayerColor;
+ aSavedBlockDXFLineInfo=aBlockDXFLineInfo;
+ aSavedParentLayerDXFLineInfo=aParentLayerDXFLineInfo;
+ nBlockColor=GetEntityColor(rE);
+ aBlockDXFLineInfo=GetEntityDXFLineInfo(rE);
+ if (rE.m_sLayer.getLength() > 1) {
+ DXFLayer * pLayer=pDXF->aTables.SearchLayer(rE.m_sLayer);
+ if (pLayer!=nullptr) {
+ nParentLayerColor=pLayer->nColor;
+ aParentLayerDXFLineInfo = LTypeToDXFLineInfo(pLayer->m_sLineType);
+ }
+ }
+ DrawEntities(*pB,aT);
+ aBlockDXFLineInfo=aSavedBlockDXFLineInfo;
+ aParentLayerDXFLineInfo=aSavedParentLayerDXFLineInfo;
+ nBlockColor=nSavedBlockColor;
+ nParentLayerColor=nSavedParentLayerColor;
+}
+
+
+void DXF2GDIMetaFile::DrawEntities(const DXFEntities & rEntities,
+ const DXFTransform & rTransform)
+{
+ if (rEntities.mbBeingDrawn)
+ return;
+ rEntities.mbBeingDrawn = true;
+
+ DXFTransform aET;
+ const DXFTransform * pT;
+
+ const DXFBasicEntity * pE=rEntities.pFirst;
+
+ while (pE!=nullptr && bStatus) {
+ if (pE->nSpace==0) {
+ if (pE->aExtrusion.fz==1.0) {
+ pT=&rTransform;
+ }
+ else {
+ aET=DXFTransform(DXFTransform(pE->aExtrusion),rTransform);
+ pT=&aET;
+ }
+ switch (pE->eType) {
+ case DXF_LINE:
+ DrawLineEntity(static_cast<const DXFLineEntity&>(*pE),*pT);
+ break;
+ case DXF_POINT:
+ DrawPointEntity(static_cast<const DXFPointEntity&>(*pE),*pT);
+ break;
+ case DXF_CIRCLE:
+ DrawCircleEntity(static_cast<const DXFCircleEntity&>(*pE),*pT);
+ break;
+ case DXF_ARC:
+ DrawArcEntity(static_cast<const DXFArcEntity&>(*pE),*pT);
+ break;
+ case DXF_TRACE:
+ DrawTraceEntity(static_cast<const DXFTraceEntity&>(*pE),*pT);
+ break;
+ case DXF_SOLID:
+ DrawSolidEntity(static_cast<const DXFSolidEntity&>(*pE),*pT);
+ break;
+ case DXF_TEXT:
+ DrawTextEntity(static_cast<const DXFTextEntity&>(*pE),*pT);
+ break;
+ case DXF_INSERT:
+ DrawInsertEntity(static_cast<const DXFInsertEntity&>(*pE),*pT);
+ break;
+ case DXF_ATTRIB:
+ DrawAttribEntity(static_cast<const DXFAttribEntity&>(*pE),*pT);
+ break;
+ case DXF_POLYLINE:
+ DrawPolyLineEntity(static_cast<const DXFPolyLineEntity&>(*pE),*pT);
+ break;
+ case DXF_LWPOLYLINE :
+ DrawLWPolyLineEntity(static_cast<const DXFLWPolyLineEntity&>(*pE), *pT);
+ break;
+ case DXF_HATCH :
+ DrawHatchEntity(static_cast<const DXFHatchEntity&>(*pE), *pT);
+ break;
+ case DXF_3DFACE:
+ Draw3DFaceEntity(static_cast<const DXF3DFaceEntity&>(*pE),*pT);
+ break;
+ case DXF_DIMENSION:
+ DrawDimensionEntity(static_cast<const DXFDimensionEntity&>(*pE),*pT);
+ break;
+ default:
+ break; // four other values not handled -Wall
+ }
+ }
+ pE=pE->pSucc;
+ }
+
+ rEntities.mbBeingDrawn = false;
+}
+
+
+DXF2GDIMetaFile::DXF2GDIMetaFile()
+ : pVirDev(nullptr)
+ , pDXF(nullptr)
+ , bStatus(false)
+ , OptPointsPerCircle(0)
+ , nMinPercent(0)
+ , nMaxPercent(0)
+ , nLastPercent(0)
+ , nMainEntitiesCount(0)
+ , nBlockColor(0)
+ , nParentLayerColor(0)
+{
+}
+
+
+DXF2GDIMetaFile::~DXF2GDIMetaFile()
+{
+}
+
+
+bool DXF2GDIMetaFile::Convert(const DXFRepresentation & rDXF, GDIMetaFile & rMTF, sal_uInt16 nminpercent, sal_uInt16 nmaxpercent)
+{
+ double fWidth,fHeight,fScale(0.0);
+ DXFTransform aTransform;
+ Size aPrefSize;
+ const DXFLayer * pLayer;
+ const DXFVPort * pVPort;
+
+ pVirDev = VclPtr<VirtualDevice>::Create();
+ pDXF = &rDXF;
+ bStatus = true;
+
+ OptPointsPerCircle=50;
+
+ nMinPercent=nminpercent;
+ nMaxPercent=nmaxpercent;
+ nLastPercent=nMinPercent;
+ nMainEntitiesCount=CountEntities(pDXF->aEntities);
+
+ nBlockColor=7;
+ aBlockDXFLineInfo.eStyle = LineStyle::Solid;
+ aBlockDXFLineInfo.nDashCount = 0;
+ aBlockDXFLineInfo.fDashLen = 0;
+ aBlockDXFLineInfo.nDotCount = 0;
+ aBlockDXFLineInfo.fDotLen = 0;
+ aBlockDXFLineInfo.fDistance = 0;
+
+ pLayer=pDXF->aTables.SearchLayer("0");
+ if (pLayer!=nullptr) {
+ nParentLayerColor=pLayer->nColor & 0xff;
+ aParentLayerDXFLineInfo = LTypeToDXFLineInfo(pLayer->m_sLineType);
+ }
+ else {
+ nParentLayerColor=7;
+ aParentLayerDXFLineInfo.eStyle = LineStyle::Solid;
+ aParentLayerDXFLineInfo.nDashCount = 0;
+ aParentLayerDXFLineInfo.fDashLen = 0;
+ aParentLayerDXFLineInfo.nDotCount = 0;
+ aParentLayerDXFLineInfo.fDotLen = 0;
+ aParentLayerDXFLineInfo.fDistance = 0;
+ }
+
+ pVirDev->EnableOutput(false);
+ if (!utl::ConfigManager::IsFuzzing()) // for fuzzing don't bother recording the drawing
+ rMTF.Record(pVirDev);
+
+ aActLineColor = pVirDev->GetLineColor();
+ aActFillColor = pVirDev->GetFillColor();
+ aActFont = pVirDev->GetFont();
+
+ pVPort=pDXF->aTables.SearchVPort("*ACTIVE");
+ if (pVPort!=nullptr) {
+ if (pVPort->aDirection.fx==0 && pVPort->aDirection.fy==0)
+ pVPort=nullptr;
+ }
+
+ if (pVPort==nullptr) {
+ if (pDXF->aBoundingBox.bEmpty)
+ bStatus=false;
+ else {
+ fWidth=pDXF->aBoundingBox.fMaxX-pDXF->aBoundingBox.fMinX;
+ fHeight=pDXF->aBoundingBox.fMaxY-pDXF->aBoundingBox.fMinY;
+ if (fWidth<=0 || fHeight<=0) {
+ bStatus=false;
+ }
+ else {
+ if (fWidth>fHeight)
+ fScale=10000.0/fWidth;
+ else
+ fScale=10000.0/fHeight;
+ aTransform=DXFTransform(fScale,-fScale,fScale,
+ DXFVector(-pDXF->aBoundingBox.fMinX*fScale,
+ pDXF->aBoundingBox.fMaxY*fScale,
+ -pDXF->aBoundingBox.fMinZ*fScale));
+ }
+ aPrefSize.setWidth(static_cast<tools::Long>(fWidth*fScale+1.5) );
+ aPrefSize.setHeight(static_cast<tools::Long>(fHeight*fScale+1.5) );
+ }
+ }
+ else {
+ fHeight=pVPort->fHeight;
+ fWidth=fHeight*pVPort->fAspectRatio;
+ if (fWidth<=0 || fHeight<=0) {
+ bStatus=false;
+ } else {
+ if (fWidth>fHeight)
+ fScale=10000.0/fWidth;
+ else
+ fScale=10000.0/fHeight;
+ aTransform=DXFTransform(
+ DXFTransform(pVPort->aDirection,pVPort->aTarget),
+ DXFTransform(
+ DXFTransform(1.0,-1.0,1.0,DXFVector(fWidth/2-pVPort->fCenterX,fHeight/2+pVPort->fCenterY,0)),
+ DXFTransform(fScale,fScale,fScale,DXFVector(0,0,0))
+ )
+ );
+ }
+ aPrefSize.setWidth(static_cast<tools::Long>(fWidth*fScale+1.5) );
+ aPrefSize.setHeight(static_cast<tools::Long>(fHeight*fScale+1.5) );
+ }
+
+ if (bStatus)
+ DrawEntities(pDXF->aEntities,aTransform);
+
+ rMTF.Stop();
+
+ if ( bStatus )
+ {
+ rMTF.SetPrefSize( aPrefSize );
+ // simply set map mode to 1/100-mm (1/10-mm) if the graphic
+ // does not get not too small (<0.5cm)
+ if( ( aPrefSize.Width() < 500 ) && ( aPrefSize.Height() < 500 ) )
+ rMTF.SetPrefMapMode( MapMode( MapUnit::Map10thMM ) );
+ else
+ rMTF.SetPrefMapMode( MapMode( MapUnit::Map100thMM ) );
+ }
+
+ pVirDev.disposeAndClear();
+ return bStatus;
+}
+
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/vcl/source/filter/idxf/dxf2mtf.hxx b/vcl/source/filter/idxf/dxf2mtf.hxx
new file mode 100644
index 000000000..d8a935b51
--- /dev/null
+++ b/vcl/source/filter/idxf/dxf2mtf.hxx
@@ -0,0 +1,119 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#ifndef INCLUDED_FILTER_SOURCE_GRAPHICFILTER_IDXF_DXF2MTF_HXX
+#define INCLUDED_FILTER_SOURCE_GRAPHICFILTER_IDXF_DXF2MTF_HXX
+
+#include <sal/config.h>
+
+#include <string_view>
+
+#include "dxfreprd.hxx"
+#include <vcl/font.hxx>
+#include <vcl/lineinfo.hxx>
+#include <vcl/vclptr.hxx>
+#include <vcl/virdev.hxx>
+
+class DXF2GDIMetaFile {
+private:
+
+ VclPtr<VirtualDevice> pVirDev;
+ const DXFRepresentation * pDXF;
+ bool bStatus;
+
+ sal_uInt16 OptPointsPerCircle;
+
+ sal_uInt16 nMinPercent;
+ sal_uInt16 nMaxPercent;
+ sal_uInt16 nLastPercent;
+ sal_uInt16 nMainEntitiesCount;
+
+ tools::Long nBlockColor;
+ DXFLineInfo aBlockDXFLineInfo;
+ tools::Long nParentLayerColor;
+ DXFLineInfo aParentLayerDXFLineInfo;
+ Color aActLineColor;
+ Color aActFillColor;
+ vcl::Font aActFont;
+ const LineInfo aDefaultLineInfo; // to share between lines to reduce memory
+
+ static sal_uInt64 CountEntities(const DXFEntities & rEntities);
+
+ Color ConvertColor(sal_uInt8 nColor) const;
+
+ tools::Long GetEntityColor(const DXFBasicEntity & rE) const;
+
+ DXFLineInfo LTypeToDXFLineInfo(std::string_view rLineType) const;
+
+ DXFLineInfo GetEntityDXFLineInfo(const DXFBasicEntity & rE);
+
+ bool SetLineAttribute(const DXFBasicEntity & rE);
+
+ bool SetAreaAttribute(const DXFBasicEntity & rE);
+
+ bool SetFontAttribute(const DXFBasicEntity & rE, short nAngle,
+ sal_uInt16 nHeight);
+
+ void DrawLineEntity(const DXFLineEntity & rE, const DXFTransform & rTransform);
+
+ void DrawPointEntity(const DXFPointEntity & rE, const DXFTransform & rTransform);
+
+ void DrawCircleEntity(const DXFCircleEntity & rE, const DXFTransform & rTransform);
+
+ void DrawArcEntity(const DXFArcEntity & rE, const DXFTransform & rTransform);
+
+ void DrawTraceEntity(const DXFTraceEntity & rE, const DXFTransform & rTransform);
+
+ void DrawSolidEntity(const DXFSolidEntity & rE, const DXFTransform & rTransform);
+
+ void DrawTextEntity(const DXFTextEntity & rE, const DXFTransform & rTransform);
+
+ void DrawInsertEntity(const DXFInsertEntity & rE, const DXFTransform & rTransform);
+
+ void DrawAttribEntity(const DXFAttribEntity & rE, const DXFTransform & rTransform);
+
+ void DrawPolyLineEntity(const DXFPolyLineEntity & rE, const DXFTransform & rTransform);
+
+ void Draw3DFaceEntity(const DXF3DFaceEntity & rE, const DXFTransform & rTransform);
+
+ void DrawDimensionEntity(const DXFDimensionEntity & rE, const DXFTransform & rTransform);
+
+ void DrawLWPolyLineEntity( const DXFLWPolyLineEntity & rE, const DXFTransform & rTransform );
+
+ void DrawHatchEntity( const DXFHatchEntity & rE, const DXFTransform & rTransform );
+
+ void DrawEntities(const DXFEntities & rEntities,
+ const DXFTransform & rTransform);
+
+ void DrawLine(const Point& rA, const Point& rB);
+
+public:
+
+ DXF2GDIMetaFile();
+ ~DXF2GDIMetaFile();
+
+ bool Convert( const DXFRepresentation & rDXF, GDIMetaFile & rMTF, sal_uInt16 nMinPercent, sal_uInt16 nMaxPercent);
+
+};
+
+
+#endif
+
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/vcl/source/filter/idxf/dxfblkrd.cxx b/vcl/source/filter/idxf/dxfblkrd.cxx
new file mode 100644
index 000000000..b5a96b93e
--- /dev/null
+++ b/vcl/source/filter/idxf/dxfblkrd.cxx
@@ -0,0 +1,125 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+
+#include "dxfblkrd.hxx"
+
+
+//---------------- DXFBlock --------------------------------------------------
+
+
+DXFBlock::DXFBlock()
+ : pSucc(nullptr)
+ , nFlags(0)
+{
+}
+
+
+DXFBlock::~DXFBlock()
+{
+}
+
+
+void DXFBlock::Read(DXFGroupReader & rDGR)
+{
+ m_sName = "";
+ m_sAlsoName = "";
+ aBasePoint.fx=0.0;
+ aBasePoint.fy=0.0;
+ aBasePoint.fz=0.0;
+ nFlags=0;
+ m_sXRef = "";
+
+ while (rDGR.Read()!=0)
+ {
+ switch (rDGR.GetG())
+ {
+ case 2: m_sName = rDGR.GetS(); break;
+ case 3: m_sAlsoName = rDGR.GetS(); break;
+ case 70: nFlags=rDGR.GetI(); break;
+ case 10: aBasePoint.fx=rDGR.GetF(); break;
+ case 20: aBasePoint.fy=rDGR.GetF(); break;
+ case 30: aBasePoint.fz=rDGR.GetF(); break;
+ case 1: m_sXRef = rDGR.GetS(); break;
+ }
+ }
+ DXFEntities::Read(rDGR);
+}
+
+
+//---------------- DXFBlocks -------------------------------------------------
+
+
+DXFBlocks::DXFBlocks()
+{
+ pFirst=nullptr;
+}
+
+
+DXFBlocks::~DXFBlocks()
+{
+ Clear();
+}
+
+
+void DXFBlocks::Read(DXFGroupReader & rDGR)
+{
+ DXFBlock * pB, * * ppSucc;
+
+ ppSucc=&pFirst;
+ while (*ppSucc!=nullptr) ppSucc=&((*ppSucc)->pSucc);
+
+ for (;;) {
+ while (rDGR.GetG()!=0) rDGR.Read();
+ if (rDGR.GetS() == "ENDSEC" ||
+ rDGR.GetS() == "EOF") break;
+ if (rDGR.GetS() == "BLOCK") {
+ pB=new DXFBlock;
+ pB->Read(rDGR);
+ *ppSucc=pB;
+ ppSucc=&(pB->pSucc);
+ }
+ else rDGR.Read();
+ }
+}
+
+
+DXFBlock * DXFBlocks::Search(std::string_view rName) const
+{
+ DXFBlock * pB;
+ for (pB=pFirst; pB!=nullptr; pB=pB->pSucc) {
+ if (rName == pB->m_sName) break;
+ }
+ return pB;
+}
+
+
+void DXFBlocks::Clear()
+{
+ DXFBlock * ptmp;
+
+ while (pFirst!=nullptr) {
+ ptmp=pFirst;
+ pFirst=ptmp->pSucc;
+ delete ptmp;
+ }
+}
+
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/vcl/source/filter/idxf/dxfblkrd.hxx b/vcl/source/filter/idxf/dxfblkrd.hxx
new file mode 100644
index 000000000..ca0a0e68a
--- /dev/null
+++ b/vcl/source/filter/idxf/dxfblkrd.hxx
@@ -0,0 +1,83 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#ifndef INCLUDED_FILTER_SOURCE_GRAPHICFILTER_IDXF_DXFBLKRD_HXX
+#define INCLUDED_FILTER_SOURCE_GRAPHICFILTER_IDXF_DXFBLKRD_HXX
+
+#include <sal/config.h>
+
+#include <string_view>
+
+#include "dxfentrd.hxx"
+
+
+//---------------- A Block (= Set of Entities) --------------------------
+
+
+class DXFBlock : public DXFEntities {
+
+public:
+
+ DXFBlock * pSucc;
+ // pointer to the next block in the list DXFBlocks::pFirst
+
+ // properties of blocks; commented with group codes:
+ OString m_sName; // 2
+ OString m_sAlsoName; // 3
+ tools::Long nFlags; // 70
+ DXFVector aBasePoint; // 10,20,30
+ OString m_sXRef; // 1
+
+ DXFBlock();
+ ~DXFBlock();
+
+ void Read(DXFGroupReader & rDGR);
+ // reads the block (including entities) from a dxf file
+ // by rGDR until an ENDBLK, ENDSEC or EOF.
+};
+
+
+//---------------- A set of blocks -----------------------------------
+
+
+class DXFBlocks {
+
+ DXFBlock * pFirst;
+ // list of blocks, READ ONLY!
+
+public:
+
+ DXFBlocks();
+ ~DXFBlocks();
+
+ void Read(DXFGroupReader & rDGR);
+ // reads all block per rDGR until an ENDSEC or EOF.
+
+ DXFBlock * Search(std::string_view rName) const;
+ // looks for a block with the name, return NULL if not successful
+
+ void Clear();
+ // deletes all blocks
+
+};
+
+#endif
+
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/vcl/source/filter/idxf/dxfentrd.cxx b/vcl/source/filter/idxf/dxfentrd.cxx
new file mode 100644
index 000000000..b4915c657
--- /dev/null
+++ b/vcl/source/filter/idxf/dxfentrd.cxx
@@ -0,0 +1,848 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <sal/config.h>
+
+#include <o3tl/safeint.hxx>
+
+#include "dxfentrd.hxx"
+
+//--------------------------DXFBasicEntity--------------------------------------
+
+DXFBasicEntity::DXFBasicEntity(DXFEntityType eThisType)
+ : m_sLayer("0")
+ , m_sLineType("BYLAYER")
+{
+ eType=eThisType;
+ pSucc=nullptr;
+ fThickness=0;
+ nColor=256;
+ nSpace=0;
+ aExtrusion.fx=0.0;
+ aExtrusion.fy=0.0;
+ aExtrusion.fz=1.0;
+}
+
+void DXFBasicEntity::Read(DXFGroupReader & rDGR)
+{
+ while (rDGR.Read()!=0) EvaluateGroup(rDGR);
+}
+
+void DXFBasicEntity::EvaluateGroup(DXFGroupReader & rDGR)
+{
+ switch (rDGR.GetG())
+ {
+ case 8: m_sLayer = rDGR.GetS(); break;
+ case 6: m_sLineType = rDGR.GetS(); break;
+ case 39: fThickness=rDGR.GetF(); break;
+ case 62: nColor=rDGR.GetI(); break;
+ case 67: nSpace=rDGR.GetI(); break;
+ case 210: aExtrusion.fx=rDGR.GetF(); break;
+ case 220: aExtrusion.fy=rDGR.GetF(); break;
+ case 230: aExtrusion.fz=rDGR.GetF(); break;
+ }
+}
+
+DXFBasicEntity::~DXFBasicEntity()
+{
+}
+
+//--------------------------DXFLineEntity---------------------------------------
+
+DXFLineEntity::DXFLineEntity() : DXFBasicEntity(DXF_LINE)
+{
+}
+
+void DXFLineEntity::EvaluateGroup(DXFGroupReader & rDGR)
+{
+ switch (rDGR.GetG()) {
+ case 10: aP0.fx=rDGR.GetF(); break;
+ case 20: aP0.fy=rDGR.GetF(); break;
+ case 30: aP0.fz=rDGR.GetF(); break;
+ case 11: aP1.fx=rDGR.GetF(); break;
+ case 21: aP1.fy=rDGR.GetF(); break;
+ case 31: aP1.fz=rDGR.GetF(); break;
+ default: DXFBasicEntity::EvaluateGroup(rDGR);
+ }
+}
+
+//--------------------------DXFPointEntity--------------------------------------
+
+DXFPointEntity::DXFPointEntity() : DXFBasicEntity(DXF_POINT)
+{
+}
+
+void DXFPointEntity::EvaluateGroup(DXFGroupReader & rDGR)
+{
+ switch (rDGR.GetG()) {
+ case 10: aP0.fx=rDGR.GetF(); break;
+ case 20: aP0.fy=rDGR.GetF(); break;
+ case 30: aP0.fz=rDGR.GetF(); break;
+ default: DXFBasicEntity::EvaluateGroup(rDGR);
+ }
+}
+
+//--------------------------DXFCircleEntity-------------------------------------
+
+DXFCircleEntity::DXFCircleEntity() : DXFBasicEntity(DXF_CIRCLE)
+{
+ fRadius=1.0;
+}
+
+void DXFCircleEntity::EvaluateGroup(DXFGroupReader & rDGR)
+{
+ switch (rDGR.GetG()) {
+ case 10: aP0.fx=rDGR.GetF(); break;
+ case 20: aP0.fy=rDGR.GetF(); break;
+ case 30: aP0.fz=rDGR.GetF(); break;
+ case 40: fRadius=rDGR.GetF(); break;
+ default: DXFBasicEntity::EvaluateGroup(rDGR);
+ }
+}
+
+//--------------------------DXFArcEntity----------------------------------------
+
+DXFArcEntity::DXFArcEntity() : DXFBasicEntity(DXF_ARC)
+{
+ fRadius=1.0;
+ fStart=0;
+ fEnd=360.0;
+}
+
+void DXFArcEntity::EvaluateGroup(DXFGroupReader & rDGR)
+{
+ switch (rDGR.GetG()) {
+ case 10: aP0.fx=rDGR.GetF(); break;
+ case 20: aP0.fy=rDGR.GetF(); break;
+ case 30: aP0.fz=rDGR.GetF(); break;
+ case 40: fRadius=rDGR.GetF(); break;
+ case 50: fStart=rDGR.GetF(); break;
+ case 51: fEnd=rDGR.GetF(); break;
+ default: DXFBasicEntity::EvaluateGroup(rDGR);
+ }
+}
+
+//--------------------------DXFTraceEntity--------------------------------------
+
+DXFTraceEntity::DXFTraceEntity() : DXFBasicEntity(DXF_TRACE)
+{
+}
+
+void DXFTraceEntity::EvaluateGroup(DXFGroupReader & rDGR)
+{
+ switch (rDGR.GetG()) {
+ case 10: aP0.fx=rDGR.GetF(); break;
+ case 20: aP0.fy=rDGR.GetF(); break;
+ case 30: aP0.fz=rDGR.GetF(); break;
+ case 11: aP1.fx=rDGR.GetF(); break;
+ case 21: aP1.fy=rDGR.GetF(); break;
+ case 31: aP1.fz=rDGR.GetF(); break;
+ case 12: aP2.fx=rDGR.GetF(); break;
+ case 22: aP2.fy=rDGR.GetF(); break;
+ case 32: aP2.fz=rDGR.GetF(); break;
+ case 13: aP3.fx=rDGR.GetF(); break;
+ case 23: aP3.fy=rDGR.GetF(); break;
+ case 33: aP3.fz=rDGR.GetF(); break;
+ default: DXFBasicEntity::EvaluateGroup(rDGR);
+ }
+}
+
+//--------------------------DXFSolidEntity--------------------------------------
+
+DXFSolidEntity::DXFSolidEntity() : DXFBasicEntity(DXF_SOLID)
+{
+}
+
+void DXFSolidEntity::EvaluateGroup(DXFGroupReader & rDGR)
+{
+ switch (rDGR.GetG()) {
+ case 10: aP0.fx=rDGR.GetF(); break;
+ case 20: aP0.fy=rDGR.GetF(); break;
+ case 30: aP0.fz=rDGR.GetF(); break;
+ case 11: aP1.fx=rDGR.GetF(); break;
+ case 21: aP1.fy=rDGR.GetF(); break;
+ case 31: aP1.fz=rDGR.GetF(); break;
+ case 12: aP2.fx=rDGR.GetF(); break;
+ case 22: aP2.fy=rDGR.GetF(); break;
+ case 32: aP2.fz=rDGR.GetF(); break;
+ case 13: aP3.fx=rDGR.GetF(); break;
+ case 23: aP3.fy=rDGR.GetF(); break;
+ case 33: aP3.fz=rDGR.GetF(); break;
+ default: DXFBasicEntity::EvaluateGroup(rDGR);
+ }
+}
+
+//--------------------------DXFTextEntity---------------------------------------
+
+DXFTextEntity::DXFTextEntity()
+ : DXFBasicEntity(DXF_TEXT)
+ , m_sStyle("STANDARD")
+{
+ fHeight=1.0;
+ fRotAngle=0.0;
+ fXScale=1.0;
+ fOblAngle=0.0;
+ nGenFlags=0;
+ nHorzJust=0;
+ nVertJust=0;
+}
+
+void DXFTextEntity::EvaluateGroup(DXFGroupReader & rDGR)
+{
+ switch (rDGR.GetG()) {
+ case 10: aP0.fx=rDGR.GetF(); break;
+ case 20: aP0.fy=rDGR.GetF(); break;
+ case 30: aP0.fz=rDGR.GetF(); break;
+ case 40: fHeight=rDGR.GetF(); break;
+ case 1: m_sText = rDGR.GetS(); break;
+ case 50: fRotAngle=rDGR.GetF(); break;
+ case 41: fXScale=rDGR.GetF(); break;
+ case 42: fOblAngle=rDGR.GetF(); break;
+ case 7: m_sStyle = rDGR.GetS(); break;
+ case 71: nGenFlags=rDGR.GetI(); break;
+ case 72: nHorzJust=rDGR.GetI(); break;
+ case 73: nVertJust=rDGR.GetI(); break;
+ case 11: aAlign.fx=rDGR.GetF(); break;
+ case 21: aAlign.fy=rDGR.GetF(); break;
+ case 31: aAlign.fz=rDGR.GetF(); break;
+ default: DXFBasicEntity::EvaluateGroup(rDGR);
+ }
+}
+
+//--------------------------DXFShapeEntity--------------------------------------
+
+DXFShapeEntity::DXFShapeEntity() : DXFBasicEntity(DXF_SHAPE)
+{
+ fSize=1.0;
+ fRotAngle=0;
+ fXScale=1.0;
+ fOblAngle=0;
+}
+
+void DXFShapeEntity::EvaluateGroup(DXFGroupReader & rDGR)
+{
+ switch (rDGR.GetG()) {
+ case 10: aP0.fx=rDGR.GetF(); break;
+ case 20: aP0.fy=rDGR.GetF(); break;
+ case 30: aP0.fz=rDGR.GetF(); break;
+ case 40: fSize=rDGR.GetF(); break;
+ case 2: m_sName = rDGR.GetS(); break;
+ case 50: fRotAngle=rDGR.GetF(); break;
+ case 41: fXScale=rDGR.GetF(); break;
+ case 51: fOblAngle=rDGR.GetF(); break;
+ default: DXFBasicEntity::EvaluateGroup(rDGR);
+ }
+}
+
+//--------------------------DXFInsertEntity-------------------------------------
+
+DXFInsertEntity::DXFInsertEntity() : DXFBasicEntity(DXF_INSERT)
+{
+ nAttrFlag=0;
+ fXScale=1.0;
+ fYScale=1.0;
+ fZScale=1.0;
+ fRotAngle=0.0;
+ nColCount=1;
+ nRowCount=1;
+ fColSpace=0.0;
+ fRowSpace=0.0;
+}
+
+void DXFInsertEntity::EvaluateGroup(DXFGroupReader & rDGR)
+{
+ switch (rDGR.GetG()) {
+ case 66: nAttrFlag=rDGR.GetI(); break;
+ case 2: m_sName = rDGR.GetS(); break;
+ case 10: aP0.fx=rDGR.GetF(); break;
+ case 20: aP0.fy=rDGR.GetF(); break;
+ case 30: aP0.fz=rDGR.GetF(); break;
+ case 41: fXScale=rDGR.GetF(); break;
+ case 42: fYScale=rDGR.GetF(); break;
+ case 43: fZScale=rDGR.GetF(); break;
+ case 50: fRotAngle=rDGR.GetF(); break;
+ case 70: nColCount=rDGR.GetI(); break;
+ case 71: nRowCount=rDGR.GetI(); break;
+ case 44: fColSpace=rDGR.GetF(); break;
+ case 45: fRowSpace=rDGR.GetF(); break;
+ default: DXFBasicEntity::EvaluateGroup(rDGR);
+ }
+}
+
+//--------------------------DXFAttDefEntity-------------------------------------
+
+DXFAttDefEntity::DXFAttDefEntity()
+ : DXFBasicEntity(DXF_ATTDEF)
+ , m_sStyle("STANDARD")
+{
+ fHeight=1.0;
+ nAttrFlags=0;
+ nFieldLen=0;
+ fRotAngle=0.0;
+ fXScale=1.0;
+ fOblAngle=0.0;
+ nGenFlags=0;
+ nHorzJust=0;
+ nVertJust=0;
+}
+
+void DXFAttDefEntity::EvaluateGroup(DXFGroupReader & rDGR)
+{
+ switch (rDGR.GetG()) {
+ case 10: aP0.fx=rDGR.GetF(); break;
+ case 20: aP0.fy=rDGR.GetF(); break;
+ case 30: aP0.fz=rDGR.GetF(); break;
+ case 40: fHeight=rDGR.GetF(); break;
+ case 1: m_sDefVal = rDGR.GetS(); break;
+ case 3: m_sPrompt = rDGR.GetS(); break;
+ case 2: m_sTagStr = rDGR.GetS(); break;
+ case 70: nAttrFlags=rDGR.GetI(); break;
+ case 73: nFieldLen=rDGR.GetI(); break;
+ case 50: fRotAngle=rDGR.GetF(); break;
+ case 41: fXScale=rDGR.GetF(); break;
+ case 51: fOblAngle=rDGR.GetF(); break;
+ case 7: m_sStyle = rDGR.GetS(); break;
+ case 71: nGenFlags=rDGR.GetI(); break;
+ case 72: nHorzJust=rDGR.GetI(); break;
+ case 74: nVertJust=rDGR.GetI(); break;
+ case 11: aAlign.fx=rDGR.GetF(); break;
+ case 21: aAlign.fy=rDGR.GetF(); break;
+ case 31: aAlign.fz=rDGR.GetF(); break;
+ default: DXFBasicEntity::EvaluateGroup(rDGR);
+ }
+}
+
+//--------------------------DXFAttribEntity-------------------------------------
+
+DXFAttribEntity::DXFAttribEntity()
+ : DXFBasicEntity(DXF_ATTRIB)
+ , m_sStyle("STANDARD")
+{
+ fHeight=1.0;
+ nAttrFlags=0;
+ nFieldLen=0;
+ fRotAngle=0.0;
+ fXScale=1.0;
+ fOblAngle=0.0;
+ nGenFlags=0;
+ nHorzJust=0;
+ nVertJust=0;
+}
+
+void DXFAttribEntity::EvaluateGroup(DXFGroupReader & rDGR)
+{
+ switch (rDGR.GetG()) {
+ case 10: aP0.fx=rDGR.GetF(); break;
+ case 20: aP0.fy=rDGR.GetF(); break;
+ case 30: aP0.fz=rDGR.GetF(); break;
+ case 40: fHeight=rDGR.GetF(); break;
+ case 1: m_sText = rDGR.GetS(); break;
+ case 2: m_sTagStr = rDGR.GetS(); break;
+ case 70: nAttrFlags=rDGR.GetI(); break;
+ case 73: nFieldLen=rDGR.GetI(); break;
+ case 50: fRotAngle=rDGR.GetF(); break;
+ case 41: fXScale=rDGR.GetF(); break;
+ case 51: fOblAngle=rDGR.GetF(); break;
+ case 7: m_sStyle = rDGR.GetS(); break;
+ case 71: nGenFlags=rDGR.GetI(); break;
+ case 72: nHorzJust=rDGR.GetI(); break;
+ case 74: nVertJust=rDGR.GetI(); break;
+ case 11: aAlign.fx=rDGR.GetF(); break;
+ case 21: aAlign.fy=rDGR.GetF(); break;
+ case 31: aAlign.fz=rDGR.GetF(); break;
+ default: DXFBasicEntity::EvaluateGroup(rDGR);
+ }
+}
+
+//--------------------------DXFPolyLine-----------------------------------------
+
+DXFPolyLineEntity::DXFPolyLineEntity() : DXFBasicEntity(DXF_POLYLINE)
+{
+ nFlags=0;
+ fSWidth=0.0;
+ fEWidth=0.0;
+ nMeshMCount=0;
+ nMeshNCount=0;
+ nMDensity=0;
+ nNDensity=0;
+ nCSSType=0;
+}
+
+void DXFPolyLineEntity::EvaluateGroup(DXFGroupReader & rDGR)
+{
+ switch (rDGR.GetG()) {
+ case 70: nFlags=rDGR.GetI(); break;
+ case 40: fSWidth=rDGR.GetF(); break;
+ case 41: fEWidth=rDGR.GetF(); break;
+ case 71: nMeshMCount=rDGR.GetI(); break;
+ case 72: nMeshNCount=rDGR.GetI(); break;
+ case 73: nMDensity=rDGR.GetI(); break;
+ case 74: nNDensity=rDGR.GetI(); break;
+ case 75: nCSSType=rDGR.GetI(); break;
+ default: DXFBasicEntity::EvaluateGroup(rDGR);
+ }
+}
+
+//--------------------------DXFLWPolyLine---------------------------------------
+
+DXFLWPolyLineEntity::DXFLWPolyLineEntity() :
+ DXFBasicEntity( DXF_LWPOLYLINE ),
+ nIndex( 0 ),
+ nCount( 0 ),
+ nFlags( 0 ),
+ fConstantWidth( 0.0 ),
+ fStartWidth( 0.0 ),
+ fEndWidth( 0.0 )
+{
+}
+
+void DXFLWPolyLineEntity::EvaluateGroup( DXFGroupReader & rDGR )
+{
+ switch ( rDGR.GetG() )
+ {
+ case 90 :
+ {
+ nCount = rDGR.GetI();
+ // limit alloc to max reasonable size based on remaining data in stream
+ if (nCount > 0 && o3tl::make_unsigned(nCount) <= rDGR.remainingSize())
+ aP.reserve(nCount);
+ else
+ nCount = 0;
+ }
+ break;
+ case 70: nFlags = rDGR.GetI(); break;
+ case 43: fConstantWidth = rDGR.GetF(); break;
+ case 40: fStartWidth = rDGR.GetF(); break;
+ case 41: fEndWidth = rDGR.GetF(); break;
+ case 10:
+ {
+ if (nIndex < nCount)
+ {
+ aP.resize(nIndex+1);
+ aP[nIndex].fx = rDGR.GetF();
+ }
+ }
+ break;
+ case 20:
+ {
+ if (nIndex < nCount)
+ {
+ aP.resize(nIndex+1);
+ aP[nIndex].fy = rDGR.GetF();
+ ++nIndex;
+ }
+ }
+ break;
+ default: DXFBasicEntity::EvaluateGroup(rDGR);
+ }
+}
+
+//--------------------------DXFHatchEntity-------------------------------------
+
+DXFEdgeTypeLine::DXFEdgeTypeLine() :
+ DXFEdgeType( 1 )
+{
+
+}
+
+bool DXFEdgeTypeLine::EvaluateGroup( DXFGroupReader & rDGR )
+{
+ bool bExecutingGroupCode = true;
+ switch ( rDGR.GetG() )
+ {
+ case 10 : aStartPoint.fx = rDGR.GetF(); break;
+ case 20 : aStartPoint.fy = rDGR.GetF(); break;
+ case 11 : aEndPoint.fx = rDGR.GetF(); break;
+ case 21 : aEndPoint.fy = rDGR.GetF(); break;
+ default : bExecutingGroupCode = false; break;
+ }
+ return bExecutingGroupCode;
+}
+
+DXFEdgeTypeCircularArc::DXFEdgeTypeCircularArc() :
+ DXFEdgeType( 2 ),
+ fRadius( 0.0 ),
+ fStartAngle( 0.0 ),
+ fEndAngle( 0.0 ),
+ nIsCounterClockwiseFlag( 0 )
+{
+}
+
+bool DXFEdgeTypeCircularArc::EvaluateGroup( DXFGroupReader & rDGR )
+{
+ bool bExecutingGroupCode = true;
+ switch ( rDGR.GetG() )
+ {
+ case 10 : aCenter.fx = rDGR.GetF(); break;
+ case 20 : aCenter.fy = rDGR.GetF(); break;
+ case 40 : fRadius = rDGR.GetF(); break;
+ case 50 : fStartAngle = rDGR.GetF(); break;
+ case 51 : fEndAngle = rDGR.GetF(); break;
+ case 73 : nIsCounterClockwiseFlag = rDGR.GetI(); break;
+ default : bExecutingGroupCode = false; break;
+ }
+ return bExecutingGroupCode;
+}
+
+DXFEdgeTypeEllipticalArc::DXFEdgeTypeEllipticalArc() :
+ DXFEdgeType( 3 ),
+ fLength( 0.0 ),
+ fStartAngle( 0.0 ),
+ fEndAngle( 0.0 ),
+ nIsCounterClockwiseFlag( 0 )
+{
+}
+
+bool DXFEdgeTypeEllipticalArc::EvaluateGroup( DXFGroupReader & rDGR )
+{
+ bool bExecutingGroupCode = true;
+ switch( rDGR.GetG() )
+ {
+ case 10 : aCenter.fx = rDGR.GetF(); break;
+ case 20 : aCenter.fy = rDGR.GetF(); break;
+ case 11 : aEndPoint.fx = rDGR.GetF(); break;
+ case 21 : aEndPoint.fy = rDGR.GetF(); break;
+ case 40 : fLength = rDGR.GetF(); break;
+ case 50 : fStartAngle = rDGR.GetF(); break;
+ case 51 : fEndAngle = rDGR.GetF(); break;
+ case 73 : nIsCounterClockwiseFlag = rDGR.GetI(); break;
+ default : bExecutingGroupCode = false; break;
+ }
+ return bExecutingGroupCode;
+}
+
+DXFEdgeTypeSpline::DXFEdgeTypeSpline() :
+ DXFEdgeType( 4 ),
+ nDegree( 0 ),
+ nRational( 0 ),
+ nPeriodic( 0 ),
+ nKnotCount( 0 ),
+ nControlCount( 0 )
+{
+}
+
+bool DXFEdgeTypeSpline::EvaluateGroup( DXFGroupReader & rDGR )
+{
+ bool bExecutingGroupCode = true;
+ switch ( rDGR.GetG() )
+ {
+ case 94 : nDegree = rDGR.GetI(); break;
+ case 73 : nRational = rDGR.GetI(); break;
+ case 74 : nPeriodic = rDGR.GetI(); break;
+ case 95 : nKnotCount = rDGR.GetI(); break;
+ case 96 : nControlCount = rDGR.GetI(); break;
+ default : bExecutingGroupCode = false; break;
+ }
+ return bExecutingGroupCode;
+}
+
+DXFBoundaryPathData::DXFBoundaryPathData() :
+ nPointCount( 0 ),
+ nFlags( 0 ),
+ nHasBulgeFlag( 0 ),
+ nIsClosedFlag( 0 ),
+ fBulge( 0.0 ),
+ nSourceBoundaryObjects( 0 ),
+ nEdgeCount( 0 ),
+ bIsPolyLine( true ),
+ nPointIndex( 0 )
+{
+}
+
+bool DXFBoundaryPathData::EvaluateGroup( DXFGroupReader & rDGR )
+{
+ bool bExecutingGroupCode = true;
+ if ( bIsPolyLine )
+ {
+ switch( rDGR.GetG() )
+ {
+ case 92 :
+ {
+ nFlags = rDGR.GetI();
+ if ( ( nFlags & 2 ) == 0 )
+ bIsPolyLine = false;
+ }
+ break;
+ case 93 :
+ {
+ nPointCount = rDGR.GetI();
+ // limit alloc to max reasonable size based on remaining data in stream
+ if (nPointCount > 0 && o3tl::make_unsigned(nPointCount) <= rDGR.remainingSize())
+ aP.reserve(nPointCount);
+ else
+ nPointCount = 0;
+ }
+ break;
+ case 72 : nHasBulgeFlag = rDGR.GetI(); break;
+ case 73 : nIsClosedFlag = rDGR.GetI(); break;
+ case 97 : nSourceBoundaryObjects = rDGR.GetI(); break;
+ case 42 : fBulge = rDGR.GetF(); break;
+ case 10:
+ {
+ if (nPointIndex < nPointCount)
+ {
+ aP.resize(nPointIndex+1);
+ aP[nPointIndex].fx = rDGR.GetF();
+ }
+ }
+ break;
+ case 20:
+ {
+ if (nPointIndex < nPointCount)
+ {
+ aP.resize(nPointIndex+1);
+ aP[nPointIndex].fy = rDGR.GetF();
+ ++nPointIndex;
+ }
+ }
+ break;
+
+ default : bExecutingGroupCode = false; break;
+ }
+ }
+ else
+ {
+ if ( rDGR.GetG() == 93 )
+ nEdgeCount = rDGR.GetI();
+ else if ( rDGR.GetG() == 72 )
+ {
+ sal_Int32 nEdgeType = rDGR.GetI();
+ switch( nEdgeType )
+ {
+ case 1 : aEdges.emplace_back( new DXFEdgeTypeLine() ); break;
+ case 2 : aEdges.emplace_back( new DXFEdgeTypeCircularArc() ); break;
+ case 3 : aEdges.emplace_back( new DXFEdgeTypeEllipticalArc() ); break;
+ case 4 : aEdges.emplace_back( new DXFEdgeTypeSpline() ); break;
+ }
+ }
+ else if ( !aEdges.empty() )
+ aEdges.back()->EvaluateGroup( rDGR );
+ else
+ bExecutingGroupCode = false;
+ }
+ return bExecutingGroupCode;
+}
+
+DXFHatchEntity::DXFHatchEntity() :
+ DXFBasicEntity( DXF_HATCH ),
+ bIsInBoundaryPathContext( false ),
+ nCurrentBoundaryPathIndex( -1 ),
+ nFlags( 0 ),
+ nAssociativityFlag( 0 ),
+ nMaxBoundaryPathCount( 0 ),
+ nHatchStyle( 0 ),
+ nHatchPatternType( 0 ),
+ fHatchPatternAngle( 0.0 ),
+ fHatchPatternScale( 1.0 ),
+ nHatchDoubleFlag( 0 ),
+ nHatchPatternDefinitionLines( 0 ),
+ fPixelSize( 1.0 ),
+ nNumberOfSeedPoints( 0 )
+{
+}
+
+void DXFHatchEntity::EvaluateGroup( DXFGroupReader & rDGR )
+{
+ switch ( rDGR.GetG() )
+ {
+// case 10 : aElevationPoint.fx = rDGR.GetF(); break;
+// case 20 : aElevationPoint.fy = rDGR.GetF(); break;
+// case 30 : aElevationPoint.fz = rDGR.GetF(); break;
+ case 70 : nFlags = rDGR.GetI(); break;
+ case 71 : nAssociativityFlag = rDGR.GetI(); break;
+ case 91 :
+ {
+ bIsInBoundaryPathContext = true;
+ nMaxBoundaryPathCount = rDGR.GetI();
+ // limit alloc to max reasonable size based on remaining data in stream
+ if (nMaxBoundaryPathCount > 0 && o3tl::make_unsigned(nMaxBoundaryPathCount) <= rDGR.remainingSize())
+ aBoundaryPathData.reserve(nMaxBoundaryPathCount);
+ else
+ nMaxBoundaryPathCount = 0;
+ }
+ break;
+ case 75 :
+ {
+ nHatchStyle = rDGR.GetI();
+ bIsInBoundaryPathContext = false;
+ }
+ break;
+ case 76 : nHatchPatternType = rDGR.GetI(); break;
+ case 52 : fHatchPatternAngle = rDGR.GetF(); break;
+ case 41 : fHatchPatternScale = rDGR.GetF(); break;
+ case 77 : nHatchDoubleFlag = rDGR.GetI(); break;
+ case 78 : nHatchPatternDefinitionLines = rDGR.GetI(); break;
+ case 47 : fPixelSize = rDGR.GetF(); break;
+ case 98 : nNumberOfSeedPoints = rDGR.GetI(); break;
+
+ case 92:
+ nCurrentBoundaryPathIndex++;
+ [[fallthrough]];
+ default:
+ {
+ bool bExecutingGroupCode = false;
+ if ( bIsInBoundaryPathContext )
+ {
+ if (nCurrentBoundaryPathIndex >= 0 && nCurrentBoundaryPathIndex < nMaxBoundaryPathCount)
+ {
+ aBoundaryPathData.resize(nCurrentBoundaryPathIndex + 1);
+ bExecutingGroupCode = aBoundaryPathData.back().EvaluateGroup(rDGR);
+ }
+ }
+ if ( !bExecutingGroupCode )
+ DXFBasicEntity::EvaluateGroup(rDGR);
+ }
+ break;
+ }
+}
+
+//--------------------------DXFVertexEntity-------------------------------------
+
+DXFVertexEntity::DXFVertexEntity() : DXFBasicEntity(DXF_VERTEX)
+{
+ fSWidth=-1.0;
+ fEWidth=-1.0;
+ fBulge=0.0;
+ nFlags=0;
+ fCFTDir=0.0;
+
+}
+
+void DXFVertexEntity::EvaluateGroup(DXFGroupReader & rDGR)
+{
+ switch (rDGR.GetG()) {
+ case 10: aP0.fx=rDGR.GetF(); break;
+ case 20: aP0.fy=rDGR.GetF(); break;
+ case 30: aP0.fz=rDGR.GetF(); break;
+ case 40: fSWidth=rDGR.GetF(); break;
+ case 41: fEWidth=rDGR.GetF(); break;
+ case 42: fBulge=rDGR.GetF(); break;
+ case 70: nFlags=rDGR.GetI(); break;
+ case 50: fCFTDir=rDGR.GetF(); break;
+ default: DXFBasicEntity::EvaluateGroup(rDGR);
+ }
+}
+
+//--------------------------DXFSeqEndEntity-------------------------------------
+
+DXFSeqEndEntity::DXFSeqEndEntity() : DXFBasicEntity(DXF_SEQEND)
+{
+}
+
+//--------------------------DXF3DFace-------------------------------------------
+
+DXF3DFaceEntity::DXF3DFaceEntity() : DXFBasicEntity(DXF_3DFACE)
+{
+ nIEFlags=0;
+}
+
+void DXF3DFaceEntity::EvaluateGroup(DXFGroupReader & rDGR)
+{
+ switch (rDGR.GetG()) {
+ case 10: aP0.fx=rDGR.GetF(); break;
+ case 20: aP0.fy=rDGR.GetF(); break;
+ case 30: aP0.fz=rDGR.GetF(); break;
+ case 11: aP1.fx=rDGR.GetF(); break;
+ case 21: aP1.fy=rDGR.GetF(); break;
+ case 31: aP1.fz=rDGR.GetF(); break;
+ case 12: aP2.fx=rDGR.GetF(); break;
+ case 22: aP2.fy=rDGR.GetF(); break;
+ case 32: aP2.fz=rDGR.GetF(); break;
+ case 13: aP3.fx=rDGR.GetF(); break;
+ case 23: aP3.fy=rDGR.GetF(); break;
+ case 33: aP3.fz=rDGR.GetF(); break;
+ case 70: nIEFlags=rDGR.GetI(); break;
+ default: DXFBasicEntity::EvaluateGroup(rDGR);
+ }
+}
+
+
+//--------------------------DXFDimensionEntity----------------------------------
+
+DXFDimensionEntity::DXFDimensionEntity() : DXFBasicEntity(DXF_DIMENSION)
+{
+}
+
+void DXFDimensionEntity::EvaluateGroup(DXFGroupReader & rDGR)
+{
+ switch (rDGR.GetG()) {
+ case 2: m_sPseudoBlock = rDGR.GetS(); break;
+ default: DXFBasicEntity::EvaluateGroup(rDGR);
+ }
+}
+
+//---------------------------- DXFEntities --------------------------------------
+
+void DXFEntities::Read(DXFGroupReader & rDGR)
+{
+ DXFBasicEntity * pE, * * ppSucc;
+
+ ppSucc=&pFirst;
+ while (*ppSucc!=nullptr) ppSucc=&((*ppSucc)->pSucc);
+
+ while (rDGR.GetG()!=0) rDGR.Read();
+
+ while (rDGR.GetS()!="ENDBLK" &&
+ rDGR.GetS()!="ENDSEC" &&
+ rDGR.GetS()!="EOF" )
+ {
+
+ if (rDGR.GetS() == "LINE" ) pE=new DXFLineEntity;
+ else if (rDGR.GetS() == "POINT" ) pE=new DXFPointEntity;
+ else if (rDGR.GetS() == "CIRCLE" ) pE=new DXFCircleEntity;
+ else if (rDGR.GetS() == "ARC" ) pE=new DXFArcEntity;
+ else if (rDGR.GetS() == "TRACE" ) pE=new DXFTraceEntity;
+ else if (rDGR.GetS() == "SOLID" ) pE=new DXFSolidEntity;
+ else if (rDGR.GetS() == "TEXT" ) pE=new DXFTextEntity;
+ else if (rDGR.GetS() == "SHAPE" ) pE=new DXFShapeEntity;
+ else if (rDGR.GetS() == "INSERT" ) pE=new DXFInsertEntity;
+ else if (rDGR.GetS() == "ATTDEF" ) pE=new DXFAttDefEntity;
+ else if (rDGR.GetS() == "ATTRIB" ) pE=new DXFAttribEntity;
+ else if (rDGR.GetS() == "POLYLINE" ) pE=new DXFPolyLineEntity;
+ else if (rDGR.GetS() == "LWPOLYLINE") pE=new DXFLWPolyLineEntity;
+ else if (rDGR.GetS() == "VERTEX" ) pE=new DXFVertexEntity;
+ else if (rDGR.GetS() == "SEQEND" ) pE=new DXFSeqEndEntity;
+ else if (rDGR.GetS() == "3DFACE" ) pE=new DXF3DFaceEntity;
+ else if (rDGR.GetS() == "DIMENSION" ) pE=new DXFDimensionEntity;
+ else if (rDGR.GetS() == "HATCH" ) pE=new DXFHatchEntity;
+ else
+ {
+ do {
+ rDGR.Read();
+ } while (rDGR.GetG()!=0);
+ continue;
+ }
+ *ppSucc=pE;
+ ppSucc=&(pE->pSucc);
+ pE->Read(rDGR);
+ }
+}
+
+void DXFEntities::Clear()
+{
+ DXFBasicEntity * ptmp;
+
+ while (pFirst!=nullptr) {
+ ptmp=pFirst;
+ pFirst=ptmp->pSucc;
+ delete ptmp;
+ }
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/vcl/source/filter/idxf/dxfentrd.hxx b/vcl/source/filter/idxf/dxfentrd.hxx
new file mode 100644
index 000000000..85dbf7dd0
--- /dev/null
+++ b/vcl/source/filter/idxf/dxfentrd.hxx
@@ -0,0 +1,538 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#ifndef INCLUDED_FILTER_SOURCE_GRAPHICFILTER_IDXF_DXFENTRD_HXX
+#define INCLUDED_FILTER_SOURCE_GRAPHICFILTER_IDXF_DXFENTRD_HXX
+
+#include "dxfgrprd.hxx"
+#include "dxfvec.hxx"
+#include <tools/long.hxx>
+
+#include <memory>
+#include <vector>
+
+enum DXFEntityType {
+ DXF_LINE,
+ DXF_POINT,
+ DXF_CIRCLE,
+ DXF_ARC,
+ DXF_TRACE,
+ DXF_SOLID,
+ DXF_TEXT,
+ DXF_SHAPE,
+ DXF_INSERT,
+ DXF_ATTDEF,
+ DXF_ATTRIB,
+ DXF_POLYLINE,
+ DXF_VERTEX,
+ DXF_SEQEND,
+ DXF_3DFACE,
+ DXF_DIMENSION,
+ DXF_LWPOLYLINE,
+ DXF_HATCH
+};
+
+// base class of an entity
+
+class DXFBasicEntity {
+
+public:
+
+ DXFBasicEntity * pSucc;
+ // pointer to next entity (in the list of DXFEntities.pFirst)
+
+ DXFEntityType eType;
+ // entity kind (line or circle or what)
+
+ // properties that all entities have, each
+ // commented with group codes:
+ OString m_sLayer; // 8
+ OString m_sLineType; // 6
+ double fThickness; // 39
+ tools::Long nColor; // 62
+ tools::Long nSpace; // 67
+ DXFVector aExtrusion; // 210,220,230
+
+protected:
+
+ DXFBasicEntity(DXFEntityType eThisType);
+ // always initialize the constructors of entities with default values
+
+public:
+
+ virtual ~DXFBasicEntity();
+ void Read(DXFGroupReader & rDGR);
+ // Reads a parameter till the next 0-group
+
+protected:
+
+ virtual void EvaluateGroup(DXFGroupReader & rDGR);
+ // This method will be called by Read() for every parameter (respectively
+ // for every group).
+ // As far as the group code of the entity is known, the corresponding
+ // parameter is fetched.
+
+};
+
+
+// the different kinds of entities
+
+class DXFLineEntity : public DXFBasicEntity {
+
+public:
+
+ DXFVector aP0; // 10,20,30
+ DXFVector aP1; // 11,21,31
+
+ DXFLineEntity();
+
+protected:
+
+ virtual void EvaluateGroup(DXFGroupReader & rDGR) override;
+};
+
+class DXFPointEntity : public DXFBasicEntity {
+
+public:
+
+ DXFVector aP0; // 10,20,30
+
+ DXFPointEntity();
+
+protected:
+
+ virtual void EvaluateGroup(DXFGroupReader & rDGR) override;
+};
+
+class DXFCircleEntity : public DXFBasicEntity {
+
+public:
+
+ DXFVector aP0; // 10,20,30
+ double fRadius; // 40
+
+ DXFCircleEntity();
+
+protected:
+
+ virtual void EvaluateGroup(DXFGroupReader & rDGR) override;
+};
+
+class DXFArcEntity : public DXFBasicEntity {
+
+public:
+
+ DXFVector aP0; // 10,20,30
+ double fRadius; // 40
+ double fStart; // 50
+ double fEnd; // 51
+
+ DXFArcEntity();
+
+protected:
+
+ virtual void EvaluateGroup(DXFGroupReader & rDGR) override;
+};
+
+class DXFTraceEntity : public DXFBasicEntity {
+
+public:
+
+ DXFVector aP0; // 10,20,30
+ DXFVector aP1; // 11,21,31
+ DXFVector aP2; // 12,22,32
+ DXFVector aP3; // 13,23,33
+
+ DXFTraceEntity();
+
+protected:
+
+ virtual void EvaluateGroup(DXFGroupReader & rDGR) override;
+};
+
+class DXFSolidEntity : public DXFBasicEntity {
+
+public:
+
+ DXFVector aP0; // 10,20,30
+ DXFVector aP1; // 11,21,31
+ DXFVector aP2; // 12,22,32
+ DXFVector aP3; // 13,23,33
+
+ DXFSolidEntity();
+
+protected:
+
+ virtual void EvaluateGroup(DXFGroupReader & rDGR) override;
+};
+
+class DXFTextEntity : public DXFBasicEntity {
+
+public:
+
+ DXFVector aP0; // 10,20,30
+ double fHeight; // 40
+ OString m_sText; // 1
+ double fRotAngle; // 50
+ double fXScale; // 41
+ double fOblAngle; // 42
+ OString m_sStyle; // 7
+ tools::Long nGenFlags; // 71
+ tools::Long nHorzJust; // 72
+ tools::Long nVertJust; // 73
+ DXFVector aAlign; // 11,21,31
+
+ DXFTextEntity();
+
+protected:
+
+ virtual void EvaluateGroup(DXFGroupReader & rDGR) override;
+};
+
+class DXFShapeEntity : public DXFBasicEntity {
+
+ DXFVector aP0; // 10,20,30
+ double fSize; // 40
+ OString m_sName; // 2
+ double fRotAngle; // 50
+ double fXScale; // 41
+ double fOblAngle; // 51
+
+public:
+
+ DXFShapeEntity();
+
+protected:
+
+ virtual void EvaluateGroup(DXFGroupReader & rDGR) override;
+};
+
+class DXFInsertEntity : public DXFBasicEntity {
+
+public:
+
+ tools::Long nAttrFlag; // 66
+ OString m_sName; // 2
+ DXFVector aP0; // 10,20,30
+ double fXScale; // 41
+ double fYScale; // 42
+ double fZScale; // 43
+ double fRotAngle; // 50
+ tools::Long nColCount; // 70
+ tools::Long nRowCount; // 71
+ double fColSpace; // 44
+ double fRowSpace; // 45
+
+ DXFInsertEntity();
+
+protected:
+
+ virtual void EvaluateGroup(DXFGroupReader & rDGR) override;
+};
+
+class DXFAttDefEntity : public DXFBasicEntity {
+
+ DXFVector aP0; // 10,20,30
+ double fHeight; // 40
+ OString m_sDefVal; // 1
+ OString m_sPrompt; // 3
+ OString m_sTagStr; // 2
+ tools::Long nAttrFlags; // 70
+ tools::Long nFieldLen; // 73
+ double fRotAngle; // 50
+ double fXScale; // 41
+ double fOblAngle; // 51
+ OString m_sStyle; // 7
+ tools::Long nGenFlags; // 71
+ tools::Long nHorzJust; // 72
+ tools::Long nVertJust; // 74
+ DXFVector aAlign; // 11,21,31
+
+public:
+
+ DXFAttDefEntity();
+
+protected:
+
+ virtual void EvaluateGroup(DXFGroupReader & rDGR) override;
+};
+
+class DXFAttribEntity : public DXFBasicEntity {
+
+public:
+
+ DXFVector aP0; // 10,20,30
+ double fHeight; // 40
+ OString m_sText; // 1
+ OString m_sTagStr; // 2
+ tools::Long nAttrFlags; // 70
+ tools::Long nFieldLen; // 73
+ double fRotAngle; // 50
+ double fXScale; // 41
+ double fOblAngle; // 51
+ OString m_sStyle; // 7
+ tools::Long nGenFlags; // 71
+ tools::Long nHorzJust; // 72
+ tools::Long nVertJust; // 74
+ DXFVector aAlign; // 11,21,31
+
+ DXFAttribEntity();
+
+protected:
+
+ virtual void EvaluateGroup(DXFGroupReader & rDGR) override;
+};
+
+class DXFPolyLineEntity : public DXFBasicEntity {
+
+public:
+
+ tools::Long nFlags; // 70
+ double fSWidth; // 40
+ double fEWidth; // 41
+ tools::Long nMeshMCount; // 71
+ tools::Long nMeshNCount; // 72
+ tools::Long nMDensity; // 73
+ tools::Long nNDensity; // 74
+ tools::Long nCSSType; // 75
+
+ DXFPolyLineEntity();
+
+protected:
+
+ virtual void EvaluateGroup(DXFGroupReader & rDGR) override;
+};
+
+class DXFLWPolyLineEntity : public DXFBasicEntity
+{
+ sal_Int32 nIndex;
+ sal_Int32 nCount; // 90
+
+ public:
+
+ sal_Int32 nFlags; // 70 1 = closed, 128 = plinegen
+ double fConstantWidth; // 43 (optional - default: 0, not used if fStartWidth and/or fEndWidth is used)
+ double fStartWidth; // 40
+ double fEndWidth; // 41
+
+ std::vector<DXFVector> aP;
+
+ DXFLWPolyLineEntity();
+
+ protected:
+
+ virtual void EvaluateGroup( DXFGroupReader & rDGR ) override;
+
+};
+
+struct DXFEdgeType
+{
+ sal_Int32 nEdgeType;
+
+ virtual ~DXFEdgeType(){};
+ virtual bool EvaluateGroup( DXFGroupReader & /*rDGR*/ ){ return true; };
+
+ protected:
+
+ DXFEdgeType( sal_Int32 EdgeType ):nEdgeType(EdgeType){};
+};
+
+struct DXFEdgeTypeLine : public DXFEdgeType
+{
+ DXFVector aStartPoint; // 10,20
+ DXFVector aEndPoint; // 11,21
+ DXFEdgeTypeLine();
+ virtual bool EvaluateGroup( DXFGroupReader & rDGR ) override;
+};
+
+struct DXFEdgeTypeCircularArc : public DXFEdgeType
+{
+ DXFVector aCenter; // 10,20
+ double fRadius; // 40
+ double fStartAngle; // 50
+ double fEndAngle; // 51
+ sal_Int32 nIsCounterClockwiseFlag; // 73
+ DXFEdgeTypeCircularArc();
+ virtual bool EvaluateGroup( DXFGroupReader & rDGR ) override;
+};
+
+struct DXFEdgeTypeEllipticalArc : public DXFEdgeType
+{
+ DXFVector aCenter; // 10,20
+ DXFVector aEndPoint; // 11,21
+ double fLength; // 40
+ double fStartAngle; // 50
+ double fEndAngle; // 51
+ sal_Int32 nIsCounterClockwiseFlag; // 73
+
+ DXFEdgeTypeEllipticalArc();
+ virtual bool EvaluateGroup( DXFGroupReader & rDGR ) override;
+};
+
+struct DXFEdgeTypeSpline : public DXFEdgeType
+{
+ sal_Int32 nDegree; // 94
+ sal_Int32 nRational; // 73
+ sal_Int32 nPeriodic; // 74
+ sal_Int32 nKnotCount; // 75
+ sal_Int32 nControlCount; // 76
+
+ DXFEdgeTypeSpline();
+ virtual bool EvaluateGroup( DXFGroupReader & rDGR ) override;
+};
+
+struct DXFBoundaryPathData
+{
+private:
+ sal_Int32 nPointCount; // 93
+public:
+ sal_Int32 nFlags; // 92
+ sal_Int32 nHasBulgeFlag; // 72
+ sal_Int32 nIsClosedFlag; // 73
+ double fBulge; // 42
+ sal_Int32 nSourceBoundaryObjects; // 97
+ sal_Int32 nEdgeCount; // 93
+
+ bool bIsPolyLine;
+ sal_Int32 nPointIndex;
+
+ std::vector<DXFVector> aP;
+ std::vector<std::unique_ptr<DXFEdgeType>> aEdges;
+
+ DXFBoundaryPathData();
+
+ bool EvaluateGroup( DXFGroupReader & rDGR );
+};
+
+class DXFHatchEntity : public DXFBasicEntity
+{
+ bool bIsInBoundaryPathContext;
+ sal_Int32 nCurrentBoundaryPathIndex;
+
+ public:
+
+ sal_Int32 nFlags; // 70 (solid fill = 1, pattern fill = 0)
+ sal_Int32 nAssociativityFlag; // 71 (associative = 1, non-associative = 0)
+ sal_Int32 nMaxBoundaryPathCount; // 91
+ sal_Int32 nHatchStyle; // 75 (odd parity = 0, outmost area = 1, entire area = 2 )
+ sal_Int32 nHatchPatternType; // 76 (user defined = 0, predefined = 1, custom = 2)
+ double fHatchPatternAngle; // 52 (pattern fill only)
+ double fHatchPatternScale; // 41 (pattern fill only:scale or spacing)
+ sal_Int32 nHatchDoubleFlag; // 77 (pattern fill only:double = 1, not double = 0)
+ sal_Int32 nHatchPatternDefinitionLines; // 78
+ double fPixelSize; // 47
+ sal_Int32 nNumberOfSeedPoints; // 98
+
+ std::vector<DXFBoundaryPathData> aBoundaryPathData;
+
+ DXFHatchEntity();
+
+ protected:
+
+ virtual void EvaluateGroup( DXFGroupReader & rDGR ) override;
+};
+
+class DXFVertexEntity : public DXFBasicEntity {
+
+public:
+
+ DXFVector aP0; // 10,20,30
+ double fSWidth; // 40 (if <0.0, then one has DXFPolyLine::fSWidth)
+ double fEWidth; // 41 (if <0.0, then one has DXFPolyLine::fEWidth)
+ double fBulge; // 42
+ tools::Long nFlags; // 70
+ double fCFTDir; // 50
+
+ DXFVertexEntity();
+
+protected:
+
+ virtual void EvaluateGroup(DXFGroupReader & rDGR) override;
+};
+
+class DXFSeqEndEntity : public DXFBasicEntity {
+
+public:
+
+ DXFSeqEndEntity();
+};
+
+class DXF3DFaceEntity : public DXFBasicEntity {
+
+public:
+
+ DXFVector aP0; // 10,20,30
+ DXFVector aP1; // 11,21,31
+ DXFVector aP2; // 12,22,32
+ DXFVector aP3; // 13,23,33
+ tools::Long nIEFlags; // 70
+
+ DXF3DFaceEntity();
+
+protected:
+
+ virtual void EvaluateGroup(DXFGroupReader & rDGR) override;
+};
+
+class DXFDimensionEntity : public DXFBasicEntity {
+
+public:
+
+ OString m_sPseudoBlock; // 2
+
+ DXFDimensionEntity();
+
+protected:
+
+ virtual void EvaluateGroup(DXFGroupReader & rDGR) override;
+};
+
+
+// read and represent the set of entities
+class DXFEntities {
+
+public:
+
+ DXFEntities()
+ : pFirst(nullptr)
+ , mbBeingDrawn(false)
+ {
+ }
+
+ ~DXFEntities()
+ {
+ Clear();
+ }
+
+ DXFBasicEntity * pFirst; // list of entities, READ ONLY!
+ mutable bool mbBeingDrawn; // guard for loop in entity parsing
+
+ void Read(DXFGroupReader & rDGR);
+ // read entities by rGDR of a DXF file until a
+ // ENDBLK, ENDSEC or EOF (of group 0).
+ // (all unknown thing will be skipped)
+
+ void Clear();
+ // deletes all entities
+};
+
+#endif
+
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/vcl/source/filter/idxf/dxfgrprd.cxx b/vcl/source/filter/idxf/dxfgrprd.cxx
new file mode 100644
index 000000000..48cdb3660
--- /dev/null
+++ b/vcl/source/filter/idxf/dxfgrprd.cxx
@@ -0,0 +1,213 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <stdlib.h>
+#include <rtl/strbuf.hxx>
+#include <tools/stream.hxx>
+#include <o3tl/string_view.hxx>
+#include "dxfgrprd.hxx"
+
+// we use an own ReadLine function, because Stream::ReadLine stops if
+// a 0-sign occurs; this function converts 0-signs to blanks and reads
+// a complete line until a cr/lf is found
+
+static OString DXFReadLine(SvStream& rIStm)
+{
+ char buf[256 + 1];
+ bool bEnd = false;
+ sal_uInt64 nOldFilePos = rIStm.Tell();
+ char c = 0;
+
+ OStringBuffer aBuf(512);
+
+ while( !bEnd && !rIStm.GetError() ) // !!! do not check for EOF
+ // !!! because we read blockwise
+ {
+ sal_uInt16 nLen = static_cast<sal_uInt16>(rIStm.ReadBytes(buf, sizeof(buf)-1));
+ if( !nLen )
+ {
+ if( aBuf.isEmpty() )
+ return OString();
+ else
+ break;
+ }
+
+ for( sal_uInt16 n = 0; n < nLen ; n++ )
+ {
+ c = buf[n];
+ if( c != '\n' && c != '\r' )
+ {
+ if( !c )
+ c = ' ';
+ aBuf.append(c);
+ }
+ else
+ {
+ bEnd = true;
+ break;
+ }
+ }
+ }
+
+ if( !bEnd && !rIStm.GetError() && !aBuf.isEmpty() )
+ bEnd = true;
+
+ nOldFilePos += aBuf.getLength();
+ if( rIStm.Tell() > nOldFilePos )
+ nOldFilePos++;
+ rIStm.Seek( nOldFilePos ); // seek because of BlockRead above!
+
+ if( bEnd && (c=='\r' || c=='\n')) // special treatment of DOS files
+ {
+ char cTemp(0);
+ rIStm.ReadBytes(&cTemp, 1);
+ if( cTemp == c || (cTemp != '\n' && cTemp != '\r') )
+ rIStm.Seek( nOldFilePos );
+ }
+
+ return aBuf.makeStringAndClear();
+}
+
+static void DXFSkipLine(SvStream& rIStm)
+{
+ while (rIStm.good())
+ {
+ char buf[256 + 1];
+ sal_uInt16 nLen = static_cast<sal_uInt16>(rIStm.ReadBytes(buf, sizeof(buf) - 1));
+ for (sal_uInt16 n = 0; n < nLen; n++)
+ {
+ char c = buf[n];
+ if ((c == '\n') || (c == '\r'))
+ {
+ rIStm.SeekRel(n-nLen+1); // return stream to next to current position
+ char c1 = 0;
+ rIStm.ReadBytes(&c1, 1);
+ if (c1 == c || (c1 != '\n' && c1!= '\r'))
+ rIStm.SeekRel(-1);
+ return;
+ }
+ }
+ }
+}
+
+DXFGroupReader::DXFGroupReader(SvStream & rIStream)
+ : rIS(rIStream)
+ , bStatus(true)
+ , nLastG(0)
+ , I(0)
+{
+ rIS.Seek(0);
+}
+
+sal_uInt16 DXFGroupReader::Read()
+{
+ sal_uInt16 nG = 0;
+ if ( bStatus )
+ {
+ nG = static_cast<sal_uInt16>(ReadI());
+ if ( bStatus )
+ {
+ if (nG< 10) ReadS();
+ else if (nG< 60) F = ReadF();
+ else if (nG< 80) I = ReadI();
+ else if (nG< 90) DXFSkipLine(rIS);
+ else if (nG< 99) I = ReadI();
+ else if (nG==100) ReadS();
+ else if (nG==102) ReadS();
+ else if (nG==105) DXFSkipLine(rIS);
+ else if (nG< 140) DXFSkipLine(rIS);
+ else if (nG< 148) F = ReadF();
+ else if (nG< 170) DXFSkipLine(rIS);
+ else if (nG< 176) I = ReadI();
+ else if (nG< 180) DXFSkipLine(rIS); // ReadI();
+ else if (nG< 210) DXFSkipLine(rIS);
+ else if (nG< 240) F = ReadF();
+ else if (nG<=369) DXFSkipLine(rIS);
+ else if (nG< 999) DXFSkipLine(rIS);
+ else if (nG<1010) ReadS();
+ else if (nG<1060) F = ReadF();
+ else if (nG<1072) I = ReadI();
+ else bStatus = false;
+ }
+ }
+ if ( !bStatus )
+ {
+ nG = 0;
+ S = "EOF";
+ }
+ nLastG = nG;
+ return nG;
+}
+
+tools::Long DXFGroupReader::ReadI()
+{
+ OString s = DXFReadLine(rIS);
+ char *p=s.pData->buffer;
+ const char *end = s.pData->buffer + s.pData->length;
+
+ while((p != end) && (*p==0x20)) p++;
+
+ if ((p == end) || ((*p<'0' || *p>'9') && *p!='-')) {
+ bStatus=false;
+ return 0;
+ }
+
+ OStringBuffer aNumber;
+ if (*p == '-') {
+ aNumber.append(*p++);
+ }
+
+ while ((p != end) && *p >= '0' && *p <= '9') {
+ aNumber.append(*p++);
+ }
+
+ while ((p != end) && (*p==0x20)) p++;
+ if (p != end) {
+ bStatus=false;
+ return 0;
+ }
+
+ return o3tl::toInt32(aNumber);
+}
+
+double DXFGroupReader::ReadF()
+{
+ OString s = DXFReadLine(rIS);
+ char *p = s.pData->buffer;
+ const char *end = s.pData->buffer + s.pData->length;
+
+ while((p != end) && (*p==0x20)) p++;
+ if ((p == end) || ((*p<'0' || *p>'9') && *p!='.' && *p!='-')) {
+ bStatus=false;
+ return 0.0;
+ }
+ return atof(p);
+}
+
+void DXFGroupReader::ReadS()
+{
+ S = DXFReadLine(rIS);
+}
+
+sal_uInt64 DXFGroupReader::remainingSize() const
+{
+ return rIS.remainingSize();
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/vcl/source/filter/idxf/dxfgrprd.hxx b/vcl/source/filter/idxf/dxfgrprd.hxx
new file mode 100644
index 000000000..4d20ae2bf
--- /dev/null
+++ b/vcl/source/filter/idxf/dxfgrprd.hxx
@@ -0,0 +1,115 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#ifndef INCLUDED_FILTER_SOURCE_GRAPHICFILTER_IDXF_DXFGRPRD_HXX
+#define INCLUDED_FILTER_SOURCE_GRAPHICFILTER_IDXF_DXFGRPRD_HXX
+
+#include <rtl/string.hxx>
+#include <sal/types.h>
+#include <tools/long.hxx>
+
+class SvStream;
+
+class DXFGroupReader
+{
+public:
+ explicit DXFGroupReader( SvStream & rIStream );
+
+ bool GetStatus() const;
+
+ void SetError();
+
+ sal_uInt16 Read();
+ // Reads next group and returns the group code.
+ // In case of an error GetStatus() returns sal_False, group code will be set
+ // to 0 and SetS(0,"EOF") will be executed.
+ bool Read(sal_uInt16 nExpectedG) { return Read() == nExpectedG; }
+
+ sal_uInt16 GetG() const;
+ // Return the last group code (the one the last Read() did return).
+
+ tools::Long GetI() const;
+ // Returns the integer value of the group which was read earlier with Read().
+ // This read must have returned a group code for datatype Integer.
+ // If not 0 is returned
+
+ double GetF() const;
+ // Returns the floating point value of the group which was read earlier with Read().
+ // This read must have returned a group code for datatype Floatingpoint.
+ // If not 0 is returned
+
+ const OString& GetS() const;
+ // Returns the string of the group which was read earlier with Read().
+ // This read must have returned a group code for datatype String.
+ // If not NULL is returned
+
+ sal_uInt64 remainingSize() const;
+private:
+
+ tools::Long ReadI();
+ double ReadF();
+ void ReadS();
+
+ SvStream & rIS;
+ bool bStatus;
+ sal_uInt16 nLastG;
+
+ OString S;
+ union {
+ double F;
+ tools::Long I;
+ };
+};
+
+
+inline bool DXFGroupReader::GetStatus() const
+{
+ return bStatus;
+}
+
+
+inline void DXFGroupReader::SetError()
+{
+ bStatus=false;
+}
+
+inline sal_uInt16 DXFGroupReader::GetG() const
+{
+ return nLastG;
+}
+
+inline tools::Long DXFGroupReader::GetI() const
+{
+ return I;
+}
+
+inline double DXFGroupReader::GetF() const
+{
+ return F;
+}
+
+inline const OString& DXFGroupReader::GetS() const
+{
+ return S;
+}
+
+#endif
+
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/vcl/source/filter/idxf/dxfreprd.cxx b/vcl/source/filter/idxf/dxfreprd.cxx
new file mode 100644
index 000000000..7124e296e
--- /dev/null
+++ b/vcl/source/filter/idxf/dxfreprd.cxx
@@ -0,0 +1,480 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <sal/config.h>
+
+#include <string_view>
+
+#include "dxfreprd.hxx"
+#include <osl/nlsupport.h>
+#include <unotools/defaultencoding.hxx>
+#include <unotools/wincodepage.hxx>
+
+//------------------DXFBoundingBox--------------------------------------------
+
+
+void DXFBoundingBox::Union(const DXFVector & rVector)
+{
+ if (bEmpty) {
+ fMinX=rVector.fx;
+ fMinY=rVector.fy;
+ fMinZ=rVector.fz;
+ fMaxX=rVector.fx;
+ fMaxY=rVector.fy;
+ fMaxZ=rVector.fz;
+ bEmpty=false;
+ }
+ else {
+ if (fMinX>rVector.fx) fMinX=rVector.fx;
+ if (fMinY>rVector.fy) fMinY=rVector.fy;
+ if (fMinZ>rVector.fz) fMinZ=rVector.fz;
+ if (fMaxX<rVector.fx) fMaxX=rVector.fx;
+ if (fMaxY<rVector.fy) fMaxY=rVector.fy;
+ if (fMaxZ<rVector.fz) fMaxZ=rVector.fz;
+ }
+}
+
+
+//------------------DXFPalette------------------------------------------------
+
+
+DXFPalette::DXFPalette()
+{
+ short i,j,nHue,nNSat,nVal,nC[3],nmax,nmed,nmin;
+ sal_uInt8 nV;
+
+ // colors 0 - 9 (normal colors)
+ SetColor(0, 0x00, 0x00, 0x00); // actually never being used
+ SetColor(1, 0xff, 0x00, 0x00);
+ SetColor(2, 0xff, 0xff, 0x00);
+ SetColor(3, 0x00, 0xff, 0x00);
+ SetColor(4, 0x00, 0xff, 0xff);
+ SetColor(5, 0x00, 0x00, 0xff);
+ SetColor(6, 0xff, 0x00, 0xff);
+ SetColor(7, 0x0f, 0x0f, 0x0f); // actually white???
+ SetColor(8, 0x80, 0x80, 0x80);
+ SetColor(9, 0xc0, 0xc0, 0xc0);
+
+ // colors 10 - 249
+ // (Universal-Palette: 24 hues * 5 lightnesses * 2 saturations )
+ i=10;
+ for (nHue=0; nHue<24; nHue++) {
+ for (nVal=5; nVal>=1; nVal--) {
+ for (nNSat=0; nNSat<2; nNSat++) {
+ nmax=((nHue+3)>>3)%3;
+ j=nHue-(nmax<<3); if (j>4) j=j-24;
+ if (j>=0) {
+ nmed=(nmax+1)%3;
+ nmin=(nmax+2)%3;
+ }
+ else {
+ nmed=(nmax+2)%3;
+ nmin=(nmax+1)%3;
+ j=-j;
+ }
+ nC[nmin]=0;
+ nC[nmed]=255*j/4;
+ nC[nmax]=255;
+ if (nNSat!=0) {
+ for (j=0; j<3; j++) nC[j]=(nC[j]>>1)+128;
+ }
+ for (j=0; j<3; j++) nC[j]=nC[j]*nVal/5;
+ SetColor(static_cast<sal_uInt8>(i++),static_cast<sal_uInt8>(nC[0]),static_cast<sal_uInt8>(nC[1]),static_cast<sal_uInt8>(nC[2]));
+ }
+ }
+ }
+
+ // Farben 250 - 255 (shades of gray)
+ for (i=0; i<6; i++) {
+ nV=static_cast<sal_uInt8>(i*38+65);
+ SetColor(static_cast<sal_uInt8>(250+i),nV,nV,nV);
+ }
+}
+
+
+void DXFPalette::SetColor(sal_uInt8 nIndex, sal_uInt8 nRed, sal_uInt8 nGreen, sal_uInt8 nBlue)
+{
+ pRed[nIndex]=nRed;
+ pGreen[nIndex]=nGreen;
+ pBlue[nIndex]=nBlue;
+}
+
+
+//------------------DXFRepresentation-----------------------------------------
+
+
+DXFRepresentation::DXFRepresentation()
+ : mEnc(RTL_TEXTENCODING_DONTKNOW)
+ , mbInCalc(false)
+{
+ setGlobalLineTypeScale(1.0);
+}
+
+DXFRepresentation::~DXFRepresentation()
+{
+}
+
+rtl_TextEncoding DXFRepresentation::getTextEncoding() const
+{
+ return (isTextEncodingSet()) ?
+ mEnc :
+ osl_getTextEncodingFromLocale(nullptr); // Use default encoding if none specified
+}
+
+bool DXFRepresentation::Read( SvStream & rIStream )
+{
+ bool bRes;
+
+ aTables.Clear();
+ aBlocks.Clear();
+ aEntities.Clear();
+
+ DXFGroupReader DGR( rIStream );
+
+ DGR.Read();
+ while (DGR.GetG()!=0 || (DGR.GetS() != "EOF")) {
+ if (DGR.GetG()==0 && DGR.GetS() == "SECTION") {
+ if (DGR.Read()!=2) {
+ DGR.SetError();
+ break;
+ }
+ if (DGR.GetS() == "HEADER") ReadHeader(DGR);
+ else if (DGR.GetS() == "TABLES") aTables.Read(DGR);
+ else if (DGR.GetS() == "BLOCKS") aBlocks.Read(DGR);
+ else if (DGR.GetS() == "ENTITIES") aEntities.Read(DGR);
+ else DGR.Read();
+ }
+ else DGR.Read();
+ }
+
+ bRes=DGR.GetStatus();
+
+ if (bRes && aBoundingBox.bEmpty)
+ CalcBoundingBox(aEntities,aBoundingBox);
+
+ return bRes;
+}
+
+void DXFRepresentation::ReadHeader(DXFGroupReader & rDGR)
+{
+ while (rDGR.GetG()!=0 || (rDGR.GetS() != "EOF" && rDGR.GetS() != "ENDSEC") )
+ {
+ if (rDGR.GetG()==9) {
+ if (rDGR.GetS() == "$EXTMIN" ||
+ rDGR.GetS() == "$EXTMAX")
+ {
+ DXFVector aVector;
+ while (rDGR.Read()!=9 && rDGR.GetG()!=0) {
+ switch (rDGR.GetG()) {
+ case 10: aVector.fx = rDGR.GetF(); break;
+ case 20: aVector.fy = rDGR.GetF(); break;
+ case 30: aVector.fz = rDGR.GetF(); break;
+ }
+ }
+ aBoundingBox.Union(aVector);
+ }
+ else if (rDGR.GetS() == "$ACADVER")
+ {
+ if (!rDGR.Read(1))
+ continue;
+ // Versions of AutoCAD up to Release 12 (inclusive, AC1009)
+ // were DOS software and used OEM encoding for storing strings.
+ // Release 13 (AC1012) had both DOS and Windows variants.
+ // Its Windows variant, and later releases used ANSI encodings for
+ // strings (up to version 2006, which was the last one to do so).
+ // Later versions (2007+, AC1021+) use UTF-8 for that.
+ // Other (non-Autodesk) implementations may have used different
+ // encodings for storing to corresponding formats, but there's
+ // no way to know that.
+ // See http://autodesk.blogs.com/between_the_lines/autocad-release-history.html
+ if ((rDGR.GetS() <= std::string_view("AC1009")) || (rDGR.GetS() == "AC2.22") || (rDGR.GetS() == "AC2.21") || (rDGR.GetS() == "AC2.10") ||
+ (rDGR.GetS() == "AC1.50") || (rDGR.GetS() == "AC1.40") || (rDGR.GetS() == "AC1.2") || (rDGR.GetS() == "MC0.0"))
+ {
+ // Set OEM encoding for old DOS formats
+ // only if the encoding is not set yet
+ // e.g. by previous $DWGCODEPAGE
+ if (!isTextEncodingSet())
+ setTextEncoding(utl_getWinTextEncodingFromLangStr(
+ utl_getLocaleForGlobalDefaultEncoding(), true));
+ }
+ else if (rDGR.GetS() >= std::string_view("AC1021"))
+ setTextEncoding(RTL_TEXTENCODING_UTF8);
+ else
+ {
+ // Set ANSI encoding for old Windows formats
+ // only if the encoding is not set yet
+ // e.g. by previous $DWGCODEPAGE
+ if (!isTextEncodingSet())
+ setTextEncoding(utl_getWinTextEncodingFromLangStr(
+ utl_getLocaleForGlobalDefaultEncoding()));
+ }
+ }
+ else if (rDGR.GetS() == "$DWGCODEPAGE")
+ {
+ if (!rDGR.Read(3))
+ continue;
+
+ // If we already use UTF8, then don't update encoding anymore
+ if (mEnc == RTL_TEXTENCODING_UTF8)
+ continue;
+ // FIXME: we really need a whole table of
+ // $DWGCODEPAGE to encodings mappings
+ else if ( (rDGR.GetS().equalsIgnoreAsciiCase("ANSI_932")) ||
+ (rDGR.GetS().equalsIgnoreAsciiCase("DOS932")) )
+ {
+ setTextEncoding(RTL_TEXTENCODING_MS_932);
+ }
+ else if (rDGR.GetS().equalsIgnoreAsciiCase("ANSI_936"))
+ {
+ setTextEncoding(RTL_TEXTENCODING_MS_936);
+ }
+ else if (rDGR.GetS().equalsIgnoreAsciiCase("ANSI_949"))
+ {
+ setTextEncoding(RTL_TEXTENCODING_MS_949);
+ }
+ else if (rDGR.GetS().equalsIgnoreAsciiCase("ANSI_950"))
+ {
+ setTextEncoding(RTL_TEXTENCODING_MS_950);
+ }
+ else if (rDGR.GetS().equalsIgnoreAsciiCase("ANSI_1251"))
+ {
+ setTextEncoding(RTL_TEXTENCODING_MS_1251);
+ }
+ }
+ else if (rDGR.GetS() == "$LTSCALE")
+ {
+ if (!rDGR.Read(40))
+ continue;
+ setGlobalLineTypeScale(getGlobalLineTypeScale() * rDGR.GetF());
+ }
+ else rDGR.Read();
+ }
+ else rDGR.Read();
+ }
+}
+
+void DXFRepresentation::CalcBoundingBox(const DXFEntities & rEntities,
+ DXFBoundingBox & rBox)
+{
+ if (mbInCalc)
+ return;
+ mbInCalc = true;
+
+ DXFBasicEntity * pBE=rEntities.pFirst;
+ while (pBE!=nullptr) {
+ switch (pBE->eType) {
+ case DXF_LINE: {
+ const DXFLineEntity * pE = static_cast<const DXFLineEntity*>(pBE);
+ rBox.Union(pE->aP0);
+ rBox.Union(pE->aP1);
+ break;
+ }
+ case DXF_POINT: {
+ const DXFPointEntity * pE = static_cast<const DXFPointEntity*>(pBE);
+ rBox.Union(pE->aP0);
+ break;
+ }
+ case DXF_CIRCLE: {
+ const DXFCircleEntity * pE = static_cast<const DXFCircleEntity*>(pBE);
+ DXFVector aP;
+ aP=pE->aP0;
+ aP.fx-=pE->fRadius;
+ aP.fy-=pE->fRadius;
+ rBox.Union(aP);
+ aP=pE->aP0;
+ aP.fx+=pE->fRadius;
+ aP.fy+=pE->fRadius;
+ rBox.Union(aP);
+ break;
+ }
+ case DXF_ARC: {
+ const DXFArcEntity * pE = static_cast<const DXFArcEntity*>(pBE);
+ DXFVector aP;
+ aP=pE->aP0;
+ aP.fx-=pE->fRadius;
+ aP.fy-=pE->fRadius;
+ rBox.Union(aP);
+ aP=pE->aP0;
+ aP.fx+=pE->fRadius;
+ aP.fy+=pE->fRadius;
+ rBox.Union(aP);
+ break;
+ }
+ case DXF_TRACE: {
+ const DXFTraceEntity * pE = static_cast<const DXFTraceEntity*>(pBE);
+ rBox.Union(pE->aP0);
+ rBox.Union(pE->aP1);
+ rBox.Union(pE->aP2);
+ rBox.Union(pE->aP3);
+ break;
+ }
+ case DXF_SOLID: {
+ const DXFSolidEntity * pE = static_cast<const DXFSolidEntity*>(pBE);
+ rBox.Union(pE->aP0);
+ rBox.Union(pE->aP1);
+ rBox.Union(pE->aP2);
+ rBox.Union(pE->aP3);
+ break;
+ }
+ case DXF_TEXT: {
+ //const DXFTextEntity * pE = (DXFTextEntity*)pBE;
+ //???
+ break;
+ }
+ case DXF_SHAPE: {
+ //const DXFShapeEntity * pE = (DXFShapeEntity*)pBE;
+ //???
+ break;
+ }
+ case DXF_INSERT: {
+ const DXFInsertEntity * pE = static_cast<const DXFInsertEntity*>(pBE);
+ DXFBlock * pB;
+ DXFBoundingBox aBox;
+ DXFVector aP;
+ pB=aBlocks.Search(pE->m_sName);
+ if (pB==nullptr) break;
+ CalcBoundingBox(*pB,aBox);
+ if (aBox.bEmpty) break;
+ aP.fx=(aBox.fMinX-pB->aBasePoint.fx)*pE->fXScale+pE->aP0.fx;
+ aP.fy=(aBox.fMinY-pB->aBasePoint.fy)*pE->fYScale+pE->aP0.fy;
+ aP.fz=(aBox.fMinZ-pB->aBasePoint.fz)*pE->fZScale+pE->aP0.fz;
+ rBox.Union(aP);
+ aP.fx=(aBox.fMaxX-pB->aBasePoint.fx)*pE->fXScale+pE->aP0.fx;
+ aP.fy=(aBox.fMaxY-pB->aBasePoint.fy)*pE->fYScale+pE->aP0.fy;
+ aP.fz=(aBox.fMaxZ-pB->aBasePoint.fz)*pE->fZScale+pE->aP0.fz;
+ rBox.Union(aP);
+ break;
+ }
+ case DXF_ATTDEF: {
+ //const DXFAttDefEntity * pE = (DXFAttDefEntity*)pBE;
+ //???
+ break;
+ }
+ case DXF_ATTRIB: {
+ //const DXFAttribEntity * pE = (DXFAttribEntity*)pBE;
+ //???
+ break;
+ }
+ case DXF_VERTEX: {
+ const DXFVertexEntity * pE = static_cast<const DXFVertexEntity*>(pBE);
+ rBox.Union(pE->aP0);
+ break;
+ }
+ case DXF_3DFACE: {
+ const DXF3DFaceEntity * pE = static_cast<const DXF3DFaceEntity*>(pBE);
+ rBox.Union(pE->aP0);
+ rBox.Union(pE->aP1);
+ rBox.Union(pE->aP2);
+ rBox.Union(pE->aP3);
+ break;
+ }
+ case DXF_DIMENSION: {
+ const DXFDimensionEntity * pE = static_cast<const DXFDimensionEntity*>(pBE);
+ DXFBlock * pB;
+ DXFBoundingBox aBox;
+ DXFVector aP;
+ pB = aBlocks.Search(pE->m_sPseudoBlock);
+ if (pB==nullptr) break;
+ CalcBoundingBox(*pB,aBox);
+ if (aBox.bEmpty) break;
+ aP.fx=aBox.fMinX-pB->aBasePoint.fx;
+ aP.fy=aBox.fMinY-pB->aBasePoint.fy;
+ aP.fz=aBox.fMinZ-pB->aBasePoint.fz;
+ rBox.Union(aP);
+ aP.fx=aBox.fMaxX-pB->aBasePoint.fx;
+ aP.fy=aBox.fMaxY-pB->aBasePoint.fy;
+ aP.fz=aBox.fMaxZ-pB->aBasePoint.fz;
+ rBox.Union(aP);
+ break;
+ }
+ case DXF_POLYLINE: {
+ //const DXFAttribEntity * pE = (DXFAttribEntity*)pBE;
+ //???
+ break;
+ }
+ case DXF_SEQEND: {
+ //const DXFAttribEntity * pE = (DXFAttribEntity*)pBE;
+ //???
+ break;
+ }
+ case DXF_HATCH :
+ break;
+ case DXF_LWPOLYLINE :
+ break;
+ }
+ pBE=pBE->pSucc;
+ }
+ mbInCalc = false;
+}
+
+namespace {
+ bool lcl_isDec(sal_Unicode ch)
+ {
+ return ch >= L'0' && ch <= L'9';
+ }
+ bool lcl_isHex(sal_Unicode ch)
+ {
+ return lcl_isDec(ch) || (ch >= L'A' && ch <= L'F') || (ch >= L'a' && ch <= L'f');
+ }
+}
+
+OUString DXFRepresentation::ToOUString(std::string_view s) const
+{
+ OUString result = OStringToOUString(s, getTextEncoding(),
+ RTL_TEXTTOUNICODE_FLAGS_UNDEFINED_ERROR
+ | RTL_TEXTTOUNICODE_FLAGS_MBUNDEFINED_ERROR
+ | RTL_TEXTTOUNICODE_FLAGS_INVALID_ERROR);
+ result = result.replaceAll("%%o", "") // Overscore - simply remove
+ .replaceAll("%%u", "") // Underscore - simply remove
+ .replaceAll("%%d", u"\u00B0") // Degrees symbol (°)
+ .replaceAll("%%p", u"\u00B1") // Tolerance symbol (±)
+ .replaceAll("%%c", u"\u2205") // Diameter symbol
+ .replaceAll("%%%", "%"); // Percent symbol
+
+ sal_Int32 pos = result.indexOf("%%"); // %%nnn, where nnn - 3-digit decimal ASCII code
+ while (pos != -1 && pos <= result.getLength() - 5) {
+ OUString asciiNum = result.copy(pos + 2, 3);
+ if (lcl_isDec(asciiNum[0]) &&
+ lcl_isDec(asciiNum[1]) &&
+ lcl_isDec(asciiNum[2]))
+ {
+ char ch = static_cast<char>(asciiNum.toUInt32());
+ OUString codePt(&ch, 1, mEnc);
+ result = result.replaceAll(result.subView(pos, 5), codePt, pos);
+ }
+ pos = result.indexOf("%%", pos + 1);
+ }
+
+ pos = result.indexOf("\\U+"); // \U+XXXX, where XXXX - 4-digit hex unicode
+ while (pos != -1 && pos <= result.getLength() - 7) {
+ OUString codePtNum = result.copy(pos + 3, 4);
+ if (lcl_isHex(codePtNum[0]) &&
+ lcl_isHex(codePtNum[1]) &&
+ lcl_isHex(codePtNum[2]) &&
+ lcl_isHex(codePtNum[3]))
+ {
+ OUString codePt(static_cast<sal_Unicode>(codePtNum.toUInt32(16)));
+ result = result.replaceAll(result.subView(pos, 7), codePt, pos);
+ }
+ pos = result.indexOf("\\U+", pos + 1);
+ }
+ return result;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/vcl/source/filter/idxf/dxfreprd.hxx b/vcl/source/filter/idxf/dxfreprd.hxx
new file mode 100644
index 000000000..e9c6bc653
--- /dev/null
+++ b/vcl/source/filter/idxf/dxfreprd.hxx
@@ -0,0 +1,129 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#ifndef INCLUDED_FILTER_SOURCE_GRAPHICFILTER_IDXF_DXFREPRD_HXX
+#define INCLUDED_FILTER_SOURCE_GRAPHICFILTER_IDXF_DXFREPRD_HXX
+
+#include "dxfblkrd.hxx"
+#include "dxftblrd.hxx"
+#include <array>
+#include <string_view>
+
+//--------------------Other stuff---------------------------------------------
+
+
+//-------------------A 3D-Min/Max-Box-----------------------------------------
+
+class DXFBoundingBox {
+public:
+ bool bEmpty;
+ double fMinX;
+ double fMinY;
+ double fMinZ;
+ double fMaxX;
+ double fMaxY;
+ double fMaxZ;
+
+ DXFBoundingBox():bEmpty(true), fMinX(0.0), fMinY(0.0), fMinZ(0.0), fMaxX(0.0), fMaxY(0.0), fMaxZ(0.0) {}
+ void Union(const DXFVector & rVector);
+};
+
+
+//-------------------The (constant) palette for DXF-------------------------
+
+class DXFPalette {
+
+public:
+
+ DXFPalette();
+
+ sal_uInt8 GetRed(sal_uInt8 nIndex) const;
+ sal_uInt8 GetGreen(sal_uInt8 nIndex) const;
+ sal_uInt8 GetBlue(sal_uInt8 nIndex) const;
+
+private:
+ std::array<sal_uInt8, 256> pRed;
+ std::array<sal_uInt8, 256> pGreen;
+ std::array<sal_uInt8, 256> pBlue;
+ void SetColor(sal_uInt8 nIndex, sal_uInt8 nRed, sal_uInt8 nGreen, sal_uInt8 nBlue);
+};
+
+
+//-----------------read and represent DXF file--------------------------------
+
+
+class DXFRepresentation {
+
+public:
+
+ DXFPalette aPalette;
+ // The always equal DXF color palette
+
+ DXFBoundingBox aBoundingBox;
+ // is equal to the AutoCAD variables EXTMIN, EXTMAX if those exist
+ // within the DXF file. Otherwise the BoundingBox gets calculated (in Read())
+
+ DXFTables aTables;
+ // the tables of the DXF file
+
+ DXFBlocks aBlocks;
+ // the blocks of the DXF file
+
+ DXFEntities aEntities;
+ // the entities (from the Entities-Section) of the DXF file
+
+ rtl_TextEncoding mEnc; // $DWGCODEPAGE
+
+ double mfGlobalLineTypeScale; // $LTSCALE
+
+ bool mbInCalc; // guard for self-recursive bounding box calc
+
+ DXFRepresentation();
+ ~DXFRepresentation();
+
+ rtl_TextEncoding getTextEncoding() const;
+ void setTextEncoding(rtl_TextEncoding aEnc) { mEnc = aEnc; }
+ OUString ToOUString(std::string_view s) const;
+
+ double getGlobalLineTypeScale() const { return mfGlobalLineTypeScale; }
+ void setGlobalLineTypeScale(double fGlobalLineTypeScale) { mfGlobalLineTypeScale = fGlobalLineTypeScale; }
+
+ bool Read( SvStream & rIStream );
+ // Reads complete DXF file.
+
+private:
+ void ReadHeader(DXFGroupReader & rDGR);
+ void CalcBoundingBox(const DXFEntities & rEntities,
+ DXFBoundingBox & rBox);
+
+ bool isTextEncodingSet() const { return mEnc != RTL_TEXTENCODING_DONTKNOW; }
+};
+
+
+//-------------------inlines--------------------------------------------------
+
+
+inline sal_uInt8 DXFPalette::GetRed(sal_uInt8 nIndex) const { return pRed[nIndex]; }
+inline sal_uInt8 DXFPalette::GetGreen(sal_uInt8 nIndex) const { return pGreen[nIndex]; }
+inline sal_uInt8 DXFPalette::GetBlue(sal_uInt8 nIndex) const { return pBlue[nIndex]; }
+
+#endif
+
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/vcl/source/filter/idxf/dxftblrd.cxx b/vcl/source/filter/idxf/dxftblrd.cxx
new file mode 100644
index 000000000..7e20e6c69
--- /dev/null
+++ b/vcl/source/filter/idxf/dxftblrd.cxx
@@ -0,0 +1,381 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+
+#include "dxftblrd.hxx"
+
+//----------------------------------DXFLType-----------------------------------
+
+DXFLType::DXFLType()
+ : pSucc(nullptr)
+ , nFlags(0)
+ , nDashCount(0)
+ , fPatternLength(0.0)
+ , fDash{0.0}
+{
+}
+
+void DXFLType::Read(DXFGroupReader & rDGR)
+{
+ tools::Long nDashIndex=-1;
+
+ while (rDGR.Read()!=0)
+ {
+ switch (rDGR.GetG())
+ {
+ case 2:
+ m_sName = rDGR.GetS();
+ break;
+ case 3:
+ m_sDescription = rDGR.GetS();
+ break;
+ case 70:
+ nFlags=rDGR.GetI();
+ break;
+ case 73:
+ if (nDashIndex!=-1)
+ {
+ rDGR.SetError();
+ return;
+ }
+ nDashCount=rDGR.GetI();
+ if (nDashCount>DXF_MAX_DASH_COUNT)
+ {
+ nDashCount=DXF_MAX_DASH_COUNT;
+ }
+ nDashIndex=0;
+ break;
+ case 40: fPatternLength=rDGR.GetF(); break;
+ case 49:
+ if (nDashCount==-1)
+ {
+ rDGR.SetError();
+ return;
+ }
+ if (nDashIndex < nDashCount)
+ {
+ if (nDashIndex < 0)
+ {
+ rDGR.SetError();
+ return;
+ }
+ fDash[nDashIndex++] = rDGR.GetF();
+ }
+ break;
+ }
+ }
+}
+
+//----------------------------------DXFLayer-----------------------------------
+
+DXFLayer::DXFLayer()
+{
+ pSucc=nullptr;
+ nFlags=0;
+ nColor=-1;
+}
+
+void DXFLayer::Read(DXFGroupReader & rDGR)
+{
+ while (rDGR.Read()!=0)
+ {
+ switch(rDGR.GetG())
+ {
+ case 2:
+ m_sName = rDGR.GetS();
+ break;
+ case 6:
+ m_sLineType = rDGR.GetS();
+ break;
+ case 70:
+ nFlags=rDGR.GetI();
+ break;
+ case 62:
+ nColor=rDGR.GetI();
+ break;
+ }
+ }
+}
+
+//----------------------------------DXFStyle-----------------------------------
+
+DXFStyle::DXFStyle()
+{
+ pSucc=nullptr;
+ nFlags=0;
+ fHeight=0.0;
+ fWidthFak=1.0;
+ fOblAngle=0.0;
+ nTextGenFlags=0;
+ fLastHeightUsed=0.0;
+}
+
+void DXFStyle::Read(DXFGroupReader & rDGR)
+{
+ while (rDGR.Read()!=0)
+ {
+ switch(rDGR.GetG())
+ {
+ case 2:
+ m_sName = rDGR.GetS();
+ break;
+ case 3:
+ m_sPrimFontFile = rDGR.GetS();
+ break;
+ case 4:
+ m_sBigFontFile = rDGR.GetS();
+ break;
+ case 70:
+ nFlags=rDGR.GetI();
+ break;
+ case 40:
+ fHeight=rDGR.GetF();
+ break;
+ case 41:
+ fWidthFak=rDGR.GetF();
+ break;
+ case 42:
+ fLastHeightUsed=rDGR.GetF();
+ break;
+ case 50:
+ fOblAngle=rDGR.GetF();
+ break;
+ case 71:
+ nTextGenFlags=rDGR.GetI();
+ break;
+ }
+ }
+}
+
+//----------------------------------DXFVPort-----------------------------------
+
+DXFVPort::DXFVPort()
+ : pSucc(nullptr)
+ , nFlags(0)
+ , fMinX(0.0)
+ , fMinY(0.0)
+ , fMaxX(0.0)
+ , fMaxY(0.0)
+ , fCenterX(0.0)
+ , fCenterY(0.0)
+ , fSnapBaseX(0.0)
+ , fSnapBaseY(0.0)
+ , fSnapSpacingX(0.0)
+ , fSnapSpacingY(0.0)
+ , fGridX(0.0)
+ , fGridY(0.0)
+ , aDirection(DXFVector(0.0, 0.0, 1.0))
+ , fHeight(0.0)
+ , fAspectRatio(0.0)
+ , fLensLength(0.0)
+ , fFrontClipPlane(0.0)
+ , fBackClipPlane(0.0)
+ , fTwistAngle(0.0)
+ , nStatus(0)
+ , nID(0)
+ , nMode(0)
+ , nCircleZoomPercent(0)
+ , nFastZoom(0)
+ , nUCSICON(0)
+ , nSnap(0)
+ , nGrid(0)
+ , nSnapStyle(0)
+ , nSnapIsopair(0)
+{
+}
+
+void DXFVPort::Read(DXFGroupReader & rDGR)
+{
+ while (rDGR.Read()!=0)
+ {
+ switch(rDGR.GetG())
+ {
+ case 2:
+ m_sName = rDGR.GetS();
+ break;
+ case 10: fMinX=rDGR.GetF(); break;
+ case 11: fMaxX=rDGR.GetF(); break;
+ case 12: fCenterX=rDGR.GetF(); break;
+ case 13: fSnapBaseX=rDGR.GetF(); break;
+ case 14: fSnapSpacingX=rDGR.GetF(); break;
+ case 15: fGridX=rDGR.GetF(); break;
+ case 16: aDirection.fx=rDGR.GetF(); break;
+ case 17: aTarget.fx=rDGR.GetF(); break;
+ case 20: fMinY=rDGR.GetF(); break;
+ case 21: fMaxY=rDGR.GetF(); break;
+ case 22: fCenterY=rDGR.GetF(); break;
+ case 23: fSnapBaseY=rDGR.GetF(); break;
+ case 24: fSnapSpacingY=rDGR.GetF(); break;
+ case 25: fGridY=rDGR.GetF(); break;
+ case 26: aDirection.fy=rDGR.GetF(); break;
+ case 27: aTarget.fy=rDGR.GetF(); break;
+ case 36: aDirection.fz=rDGR.GetF(); break;
+ case 37: aTarget.fz=rDGR.GetF(); break;
+ case 40: fHeight=rDGR.GetF(); break;
+ case 41: fAspectRatio=rDGR.GetF(); break;
+ case 42: fLensLength=rDGR.GetF(); break;
+ case 43: fFrontClipPlane=rDGR.GetF(); break;
+ case 44: fBackClipPlane=rDGR.GetF(); break;
+ case 51: fTwistAngle=rDGR.GetF(); break;
+ case 68: nStatus=rDGR.GetI(); break;
+ case 69: nID=rDGR.GetI(); break;
+ case 70: nFlags=rDGR.GetI(); break;
+ case 71: nMode=rDGR.GetI(); break;
+ case 72: nCircleZoomPercent=rDGR.GetI(); break;
+ case 73: nFastZoom=rDGR.GetI(); break;
+ case 74: nUCSICON=rDGR.GetI(); break;
+ case 75: nSnap=rDGR.GetI(); break;
+ case 76: nGrid=rDGR.GetI(); break;
+ case 77: nSnapStyle=rDGR.GetI(); break;
+ case 78: nSnapIsopair=rDGR.GetI(); break;
+ }
+ }
+}
+
+//----------------------------------DXFTables----------------------------------
+
+
+DXFTables::DXFTables()
+{
+ pLTypes=nullptr;
+ pLayers=nullptr;
+ pStyles=nullptr;
+ pVPorts=nullptr;
+}
+
+
+DXFTables::~DXFTables()
+{
+ Clear();
+}
+
+
+void DXFTables::Read(DXFGroupReader & rDGR)
+{
+ DXFLType * * ppLT, * pLT;
+ DXFLayer * * ppLa, * pLa;
+ DXFStyle * * ppSt, * pSt;
+ DXFVPort * * ppVP, * pVP;
+
+ ppLT=&pLTypes;
+ while(*ppLT!=nullptr) ppLT=&((*ppLT)->pSucc);
+
+ ppLa=&pLayers;
+ while(*ppLa!=nullptr) ppLa=&((*ppLa)->pSucc);
+
+ ppSt=&pStyles;
+ while(*ppSt!=nullptr) ppSt=&((*ppSt)->pSucc);
+
+ ppVP=&pVPorts;
+ while(*ppVP!=nullptr) ppVP=&((*ppVP)->pSucc);
+
+ for (;;) {
+ while (rDGR.GetG()!=0) rDGR.Read();
+ if (rDGR.GetS() == "EOF" ||
+ rDGR.GetS() == "ENDSEC") break;
+ else if (rDGR.GetS() == "LTYPE") {
+ pLT=new DXFLType;
+ pLT->Read(rDGR);
+ *ppLT=pLT;
+ ppLT=&(pLT->pSucc);
+ }
+ else if (rDGR.GetS() == "LAYER") {
+ pLa=new DXFLayer;
+ pLa->Read(rDGR);
+ *ppLa=pLa;
+ ppLa=&(pLa->pSucc);
+ }
+ else if (rDGR.GetS() == "STYLE") {
+ pSt=new DXFStyle;
+ pSt->Read(rDGR);
+ *ppSt=pSt;
+ ppSt=&(pSt->pSucc);
+ }
+ else if (rDGR.GetS() == "VPORT") {
+ pVP=new DXFVPort;
+ pVP->Read(rDGR);
+ *ppVP=pVP;
+ ppVP=&(pVP->pSucc);
+ }
+ else rDGR.Read();
+ }
+}
+
+
+void DXFTables::Clear()
+{
+ DXFLType * pLT;
+ DXFLayer * pLa;
+ DXFStyle * pSt;
+ DXFVPort * pVP;
+
+ while (pStyles!=nullptr) {
+ pSt=pStyles;
+ pStyles=pSt->pSucc;
+ delete pSt;
+ }
+ while (pLayers!=nullptr) {
+ pLa=pLayers;
+ pLayers=pLa->pSucc;
+ delete pLa;
+ }
+ while (pLTypes!=nullptr) {
+ pLT=pLTypes;
+ pLTypes=pLT->pSucc;
+ delete pLT;
+ }
+ while (pVPorts!=nullptr) {
+ pVP=pVPorts;
+ pVPorts=pVP->pSucc;
+ delete pVP;
+ }
+}
+
+
+DXFLType * DXFTables::SearchLType(std::string_view rName) const
+{
+ DXFLType * p;
+ for (p=pLTypes; p!=nullptr; p=p->pSucc) {
+ if (rName == p->m_sName) break;
+ }
+ return p;
+}
+
+
+DXFLayer * DXFTables::SearchLayer(std::string_view rName) const
+{
+ DXFLayer * p;
+ for (p=pLayers; p!=nullptr; p=p->pSucc) {
+ if (rName == p->m_sName) break;
+ }
+ return p;
+}
+
+
+DXFVPort * DXFTables::SearchVPort(std::string_view rName) const
+{
+ DXFVPort * p;
+ for (p=pVPorts; p!=nullptr; p=p->pSucc) {
+ if (rName == p->m_sName) break;
+ }
+ return p;
+}
+
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/vcl/source/filter/idxf/dxftblrd.hxx b/vcl/source/filter/idxf/dxftblrd.hxx
new file mode 100644
index 000000000..f60c0461e
--- /dev/null
+++ b/vcl/source/filter/idxf/dxftblrd.hxx
@@ -0,0 +1,175 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#ifndef INCLUDED_FILTER_SOURCE_GRAPHICFILTER_IDXF_DXFTBLRD_HXX
+#define INCLUDED_FILTER_SOURCE_GRAPHICFILTER_IDXF_DXFTBLRD_HXX
+
+#include <sal/config.h>
+
+#include <string_view>
+
+#include "dxfgrprd.hxx"
+#include "dxfvec.hxx"
+
+
+//------------------- Line Type ----------------------------------------------
+
+
+#define DXF_MAX_DASH_COUNT 32
+
+class DXFLType {
+
+public:
+
+ DXFLType * pSucc;
+
+ OString m_sName; // 2
+ tools::Long nFlags; // 70
+ OString m_sDescription; // 3
+ tools::Long nDashCount; // 73
+ double fPatternLength; // 40
+ double fDash[DXF_MAX_DASH_COUNT]; // 49,49,...
+
+ DXFLType();
+ void Read(DXFGroupReader & rDGR);
+};
+
+
+//------------------ Layer ---------------------------------------------------
+
+
+class DXFLayer {
+
+public:
+
+ DXFLayer * pSucc;
+
+ OString m_sName; // 2
+ tools::Long nFlags; // 70
+ tools::Long nColor; // 62
+ OString m_sLineType; // 6
+
+ DXFLayer();
+ void Read(DXFGroupReader & rDGR);
+};
+
+
+//------------------ Style ---------------------------------------------------
+
+
+class DXFStyle {
+
+public:
+
+ DXFStyle * pSucc;
+
+ OString m_sName; // 2
+ tools::Long nFlags; // 70
+ double fHeight; // 40
+ double fWidthFak; // 41
+ double fOblAngle; // 50
+ tools::Long nTextGenFlags; // 71
+ double fLastHeightUsed; // 42
+ OString m_sPrimFontFile; // 3
+ OString m_sBigFontFile; // 4
+
+ DXFStyle();
+ void Read(DXFGroupReader & rDGR);
+};
+
+
+//------------------ VPort ---------------------------------------------------
+
+
+class DXFVPort {
+
+public:
+
+ DXFVPort * pSucc;
+
+ OString m_sName; // 2
+ tools::Long nFlags; // 70
+ double fMinX; // 10
+ double fMinY; // 20
+ double fMaxX; // 11
+ double fMaxY; // 21
+ double fCenterX; // 12
+ double fCenterY; // 22
+ double fSnapBaseX; // 13
+ double fSnapBaseY; // 23
+ double fSnapSpacingX; // 14
+ double fSnapSpacingY; // 24
+ double fGridX; // 15
+ double fGridY; // 25
+ DXFVector aDirection; // 16,26,36
+ DXFVector aTarget; // 17,27,37
+ double fHeight; // 40
+ double fAspectRatio; // 41
+ double fLensLength; // 42
+ double fFrontClipPlane; // 43
+ double fBackClipPlane; // 44
+ double fTwistAngle; // 51
+ tools::Long nStatus; // 68
+ tools::Long nID; // 69
+ tools::Long nMode; // 71
+ tools::Long nCircleZoomPercent; // 72
+ tools::Long nFastZoom; // 73
+ tools::Long nUCSICON; // 74
+ tools::Long nSnap; // 75
+ tools::Long nGrid; // 76
+ tools::Long nSnapStyle; // 77
+ tools::Long nSnapIsopair; // 78
+
+ DXFVPort();
+ void Read(DXFGroupReader & rDGR);
+};
+
+
+//------------------ Tables --------------------------------------------------
+
+
+class DXFTables {
+
+ DXFLType * pLTypes; // list of line types
+ DXFLayer * pLayers; // list of layers
+ DXFStyle * pStyles; // list of styles
+ DXFVPort * pVPorts; // list of viewports
+
+public:
+
+ DXFTables();
+ ~DXFTables();
+
+ void Read(DXFGroupReader & rDGR);
+ // Reads the table until an ENDSEC or EOF
+ // (Unknown things/tables will be skipped)
+
+ void Clear();
+
+ // look for table entries:
+ DXFLType * SearchLType(std::string_view rName) const;
+ DXFLayer * SearchLayer(std::string_view rName) const;
+ DXFVPort * SearchVPort(std::string_view rName) const;
+
+};
+
+#endif
+
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/vcl/source/filter/idxf/dxfvec.cxx b/vcl/source/filter/idxf/dxfvec.cxx
new file mode 100644
index 000000000..dc5f39835
--- /dev/null
+++ b/vcl/source/filter/idxf/dxfvec.cxx
@@ -0,0 +1,232 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+
+#include <math.h>
+#include "dxfvec.hxx"
+#include <tools/gen.hxx>
+
+
+//---------------------------- DXFVector ---------------------------------------
+
+
+double DXFVector::Abs() const
+{
+ return sqrt(SProd(*this));
+}
+
+
+DXFVector DXFVector::Unit() const
+{
+ double flen;
+
+ flen=Abs();
+ if (flen!=0) return (*this)*(1.0/flen);
+ else return DXFVector(1.0,0.0,0.0);
+}
+
+
+//---------------------------- DXFTransform ------------------------------------
+
+
+DXFTransform::DXFTransform() :
+ aMX(1.0, 0.0, 0.0),
+ aMY(0.0, 1.0, 0.0),
+ aMZ(0.0, 0.0, 1.0),
+ aMP(0.0, 0.0, 0.0)
+{
+}
+
+
+DXFTransform::DXFTransform(double fScaleX, double fScaleY, double fScaleZ,
+ const DXFVector & rShift) :
+ aMX(fScaleX, 0.0, 0.0),
+ aMY(0.0, fScaleY, 0.0),
+ aMZ(0.0, 0.0, fScaleZ),
+ aMP(rShift)
+{
+}
+
+
+DXFTransform::DXFTransform(double fScaleX, double fScaleY, double fScaleZ,
+ double fRotAngle,
+ const DXFVector & rShift) :
+ aMX(0.0, 0.0, 0.0),
+ aMY(0.0, 0.0, 0.0),
+ aMZ(0.0, 0.0, fScaleZ),
+ aMP(rShift)
+{
+ aMX.fx=cos(basegfx::deg2rad(fRotAngle));
+ aMX.fy=sin(basegfx::deg2rad(fRotAngle));
+ aMY.fx=-aMX.fy;
+ aMY.fy=aMX.fx;
+ aMX*=fScaleX;
+ aMY*=fScaleY;
+}
+
+
+DXFTransform::DXFTransform(const DXFVector & rExtrusion) :
+ aMP(0.0, 0.0, 0.0)
+{
+ // 'Arbitrary Axis Algorithm' (cf. DXF documentation by Autodesk)
+ if ( fabs(rExtrusion.fx) < 1.0/64.0 && fabs(rExtrusion.fy) < 1.0/64.0) {
+ aMX = DXFVector(0.0, 1.0, 0.0) * rExtrusion;
+ }
+ else {
+ aMX = DXFVector(0.0, 0.0, 1.0) * rExtrusion;
+ }
+ aMX=aMX.Unit();
+ aMY=(rExtrusion*aMX).Unit();
+ aMZ=rExtrusion.Unit();
+}
+
+
+DXFTransform::DXFTransform(const DXFVector & rViewDir, const DXFVector & rViewTarget)
+{
+ DXFVector aV;
+
+ aV=rViewDir.Unit();
+ aMX.fz=aV.fx;
+ aMY.fz=aV.fy;
+ aMZ.fz=aV.fz;
+
+ aMZ.fx=0;
+ if (aV.fx==0) aMY.fx=0; else aMY.fx=sqrt(1/(1+aV.fy*aV.fy/(aV.fx*aV.fx)));
+ aMX.fx=sqrt(1-aMY.fx*aMY.fx);
+ if (aV.fx*aV.fy*aMY.fx>0) aMX.fx=-aMX.fx;
+
+ aV=aV*DXFVector(aMX.fx,aMY.fx,aMZ.fx);
+ aMX.fy=aV.fx;
+ aMY.fy=aV.fy;
+ aMZ.fy=aV.fz;
+
+ if (aMZ.fy<0) {
+ aMX.fy=-aMX.fy;
+ aMY.fy=-aMY.fy;
+ aMZ.fy=-aMZ.fy;
+ aMX.fx=-aMX.fx;
+ aMY.fx=-aMY.fx;
+ }
+
+ aV=DXFVector(0,0,0)-rViewTarget;
+ aMP.fx = aV.fx * aMX.fx + aV.fy * aMY.fx + aV.fz * aMZ.fx;
+ aMP.fy = aV.fx * aMX.fy + aV.fy * aMY.fy + aV.fz * aMZ.fy;
+ aMP.fz = aV.fx * aMX.fz + aV.fy * aMY.fz + aV.fz * aMZ.fz;
+}
+
+
+DXFTransform::DXFTransform(const DXFTransform & rT1, const DXFTransform & rT2)
+{
+ rT2.TransDir(rT1.aMX,aMX);
+ rT2.TransDir(rT1.aMY,aMY);
+ rT2.TransDir(rT1.aMZ,aMZ);
+ rT2.Transform(rT1.aMP,aMP);
+}
+
+
+void DXFTransform::Transform(const DXFVector & rSrc, DXFVector & rTgt) const
+{
+ rTgt.fx = rSrc.fx * aMX.fx + rSrc.fy * aMY.fx + rSrc.fz * aMZ.fx + aMP.fx;
+ rTgt.fy = rSrc.fx * aMX.fy + rSrc.fy * aMY.fy + rSrc.fz * aMZ.fy + aMP.fy;
+ rTgt.fz = rSrc.fx * aMX.fz + rSrc.fy * aMY.fz + rSrc.fz * aMZ.fz + aMP.fz;
+}
+
+
+void DXFTransform::Transform(const DXFVector & rSrc, Point & rTgt) const
+{
+ rTgt.setX(static_cast<tools::Long>( rSrc.fx * aMX.fx + rSrc.fy * aMY.fx + rSrc.fz * aMZ.fx + aMP.fx + 0.5 ) );
+ rTgt.setY(static_cast<tools::Long>( rSrc.fx * aMX.fy + rSrc.fy * aMY.fy + rSrc.fz * aMZ.fy + aMP.fy + 0.5 ) );
+}
+
+
+void DXFTransform::TransDir(const DXFVector & rSrc, DXFVector & rTgt) const
+{
+ rTgt.fx = rSrc.fx * aMX.fx + rSrc.fy * aMY.fx + rSrc.fz * aMZ.fx;
+ rTgt.fy = rSrc.fx * aMX.fy + rSrc.fy * aMY.fy + rSrc.fz * aMZ.fy;
+ rTgt.fz = rSrc.fx * aMX.fz + rSrc.fy * aMY.fz + rSrc.fz * aMZ.fz;
+}
+
+
+bool DXFTransform::TransCircleToEllipse(double fRadius, double & rEx, double & rEy) const
+{
+ double fMXAbs=aMX.Abs();
+ double fMYAbs=aMY.Abs();
+ double fNearNull=(fMXAbs+fMYAbs)*0.001;
+
+ if (fabs(aMX.fy)<=fNearNull && fabs(aMX.fz)<=fNearNull &&
+ fabs(aMY.fx)<=fNearNull && fabs(aMY.fz)<=fNearNull)
+ {
+ rEx=fabs(aMX.fx*fRadius);
+ rEy=fabs(aMY.fy*fRadius);
+ return true;
+ }
+ else if (fabs(aMX.fx)<=fNearNull && fabs(aMX.fz)<=fNearNull &&
+ fabs(aMY.fy)<=fNearNull && fabs(aMY.fz)<=fNearNull)
+ {
+ rEx=fabs(aMY.fx*fRadius);
+ rEy=fabs(aMX.fy*fRadius);
+ return true;
+ }
+ else if (fabs(fMXAbs-fMYAbs)<=fNearNull &&
+ fabs(aMX.fz)<=fNearNull && fabs(aMY.fz)<=fNearNull)
+ {
+ rEx=rEy=fabs(((fMXAbs+fMYAbs)/2)*fRadius);
+ return true;
+ }
+ else return false;
+}
+
+LineInfo DXFTransform::Transform(const DXFLineInfo& aDXFLineInfo) const
+{
+ double fex,fey,scale;
+
+ fex=sqrt(aMX.fx*aMX.fx + aMX.fy*aMX.fy);
+ fey=sqrt(aMY.fx*aMY.fx + aMY.fy*aMY.fy);
+ scale = (fex+fey)/2.0;
+
+ LineInfo aLineInfo;
+
+ aLineInfo.SetStyle( aDXFLineInfo.eStyle );
+ aLineInfo.SetWidth( 0 );
+ aLineInfo.SetDashCount( static_cast< sal_uInt16 >( aDXFLineInfo.nDashCount ) );
+ aLineInfo.SetDashLen( aDXFLineInfo.fDashLen * scale );
+ aLineInfo.SetDotCount( static_cast< sal_uInt16 >( aDXFLineInfo.nDotCount ) );
+ aLineInfo.SetDotLen( aDXFLineInfo.fDotLen * scale );
+ aLineInfo.SetDistance( aDXFLineInfo.fDistance * scale );
+
+ if ( aLineInfo.GetDashCount() > 0 && aLineInfo.GetDashLen() == 0 )
+ aLineInfo.SetDashLen(1);
+
+ if ( aLineInfo.GetDotCount() > 0 && aLineInfo.GetDotLen() == 0 )
+ aLineInfo.SetDotLen(1);
+
+ return aLineInfo;
+}
+
+double DXFTransform::CalcRotAngle() const
+{
+ return basegfx::rad2deg(atan2(aMX.fy,aMX.fx));
+}
+
+bool DXFTransform::Mirror() const
+{
+ return aMZ.SProd(aMX*aMY)<0;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/vcl/source/filter/idxf/dxfvec.hxx b/vcl/source/filter/idxf/dxfvec.hxx
new file mode 100644
index 000000000..59b6babc2
--- /dev/null
+++ b/vcl/source/filter/idxf/dxfvec.hxx
@@ -0,0 +1,218 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#ifndef INCLUDED_FILTER_SOURCE_GRAPHICFILTER_IDXF_DXFVEC_HXX
+#define INCLUDED_FILTER_SOURCE_GRAPHICFILTER_IDXF_DXFVEC_HXX
+
+#include <sal/types.h>
+#include <vcl/lineinfo.hxx>
+
+class Point;
+
+class DXFLineInfo {
+public:
+ LineStyle eStyle;
+ sal_Int32 nDashCount;
+ double fDashLen;
+ sal_Int32 nDotCount;
+ double fDotLen;
+ double fDistance;
+
+ DXFLineInfo() :
+ eStyle(LineStyle::Solid),
+ nDashCount(0),
+ fDashLen(0),
+ nDotCount(0),
+ fDotLen(0),
+ fDistance(0) {}
+};
+
+
+//---------------------------- DXFVector ---------------------------------------
+
+// common 3D vector with doubles
+
+class DXFVector {
+
+public:
+
+ double fx,fy,fz; // public ! - why not?
+
+ inline DXFVector(double fX=0.0, double fY=0.0, double fZ=0.0);
+
+ // summation/subtraktion:
+ DXFVector & operator += (const DXFVector & rV);
+ DXFVector operator + (const DXFVector & rV) const;
+ DXFVector operator - (const DXFVector & rV) const;
+
+ // vector product
+ DXFVector operator * (const DXFVector & rV) const;
+
+ // scalar product:
+ double SProd(const DXFVector & rV) const;
+
+ // multiplication with scalar:
+ DXFVector & operator *= (double fs);
+ DXFVector operator * (double fs) const;
+
+ // length:
+ double Abs() const;
+
+ // vector with same direction and a length of 1:
+ DXFVector Unit() const;
+
+ // equivalence or net:
+ bool operator == (const DXFVector & rV) const;
+};
+
+
+//---------------------------- DXFTransform ------------------------------------
+
+// a transformation matrice specialized for our problem
+
+class DXFTransform {
+
+public:
+
+ DXFTransform();
+ // destination coordinate = source coordinate
+
+ DXFTransform(double fScaleX, double fScaleY, double fScaleZ,
+ const DXFVector & rShift);
+ // dest coordinate = translate(scale(source coordinate))
+
+ DXFTransform(double fScaleX, double fScaleY, double fScaleZ,
+ double fRotAngle,
+ const DXFVector & rShift);
+ // dest coordinate = translate(rotate(scale(source coordinate)))
+ // rotation around z-axis, fRotAngle in degrees.
+
+ DXFTransform(const DXFVector & rExtrusion);
+ // Transformation "ECS->WCS" via "Entity Extrusion Direction"
+ // ant the "Arbitrary Axis Algorithm"
+ // (See DXF-Docu from AutoDesk)
+
+ DXFTransform(const DXFVector & rViewDir, const DXFVector & rViewTarget);
+ // Transformation object space->picture space on the basis of direction
+ // destination point of a viewport
+ // (See DXF-Docu from AutoDesk: VPORT)
+
+ DXFTransform(const DXFTransform & rT1, const DXFTransform & rT2);
+ // destination coordinate = rT2(rT1(source coordinate))
+
+
+ void Transform(const DXFVector & rSrc, DXFVector & rTgt) const;
+ // Transformation from DXFVector to DXFVector
+
+ void Transform(const DXFVector & rSrc, Point & rTgt) const;
+ // Transformation from DXFVector to SvPoint
+
+ void TransDir(const DXFVector & rSrc, DXFVector & rTgt) const;
+ // Transformation of a relative vector (so no translation)
+
+ bool TransCircleToEllipse(double fRadius, double & rEx, double & rEy) const;
+ // Attempt to transform a circle (in xy plane) so that it results
+ // in an aligned ellipse. If the does not work because an ellipse of
+ // arbitrary position would be created, sal_False is returned.
+ // (The center point will not be transformed, use Transform(..))
+
+ double CalcRotAngle() const;
+ // Calculates the rotation angle around z-axis (in degrees)
+
+ bool Mirror() const;
+ // Returns sal_True, if the matrice represents a left-handed coordinate system
+
+ LineInfo Transform(const DXFLineInfo& aDXFLineInfo) const;
+ // Transform to LineInfo
+
+private:
+ DXFVector aMX;
+ DXFVector aMY;
+ DXFVector aMZ;
+ DXFVector aMP;
+};
+
+
+//------------------------------- inlines --------------------------------------
+
+
+inline DXFVector::DXFVector(double fX, double fY, double fZ)
+{
+ fx=fX; fy=fY; fz=fZ;
+}
+
+
+inline DXFVector & DXFVector::operator += (const DXFVector & rV)
+{
+ fx+=rV.fx; fy+=rV.fy; fz+=rV.fz;
+ return *this;
+}
+
+
+inline DXFVector DXFVector::operator + (const DXFVector & rV) const
+{
+ return DXFVector(fx+rV.fx, fy+rV.fy, fz+rV.fz);
+}
+
+
+inline DXFVector DXFVector::operator - (const DXFVector & rV) const
+{
+ return DXFVector(fx-rV.fx, fy-rV.fy, fz-rV.fz);
+}
+
+
+inline DXFVector DXFVector::operator * (const DXFVector & rV) const
+{
+ return DXFVector(
+ fy * rV.fz - fz * rV.fy,
+ fz * rV.fx - fx * rV.fz,
+ fx * rV.fy - fy * rV.fx
+ );
+}
+
+
+inline double DXFVector::SProd(const DXFVector & rV) const
+{
+ return fx*rV.fx + fy*rV.fy + fz*rV.fz;
+}
+
+
+inline DXFVector & DXFVector::operator *= (double fs)
+{
+ fx*=fs; fy*=fs; fz*=fs;
+ return *this;
+}
+
+
+inline DXFVector DXFVector::operator * (double fs) const
+{
+ return DXFVector(fx*fs,fy*fs,fz*fs);
+}
+
+
+inline bool DXFVector::operator == (const DXFVector & rV) const
+{
+ if (fx==rV.fx && fy==rV.fy && fz==rV.fz) return true;
+ else return false;
+}
+
+
+#endif
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/vcl/source/filter/idxf/idxf.cxx b/vcl/source/filter/idxf/idxf.cxx
new file mode 100644
index 000000000..26d42b10c
--- /dev/null
+++ b/vcl/source/filter/idxf/idxf.cxx
@@ -0,0 +1,43 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+
+#include <filter/DxfReader.hxx>
+#include <vcl/gdimtf.hxx>
+#include <vcl/graph.hxx>
+#include "dxf2mtf.hxx"
+
+//================== GraphicImport - the exported function ================
+
+bool ImportDxfGraphic(SvStream & rStream, Graphic & rGraphic)
+{
+ DXFRepresentation aDXF;
+ DXF2GDIMetaFile aConverter;
+ GDIMetaFile aMTF;
+
+ if ( !aDXF.Read( rStream ) )
+ return false;
+ if ( !aConverter.Convert( aDXF, aMTF, 60, 100 ) )
+ return false;
+ rGraphic = Graphic(aMTF);
+
+ return true;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/vcl/source/filter/ieps/ieps.cxx b/vcl/source/filter/ieps/ieps.cxx
new file mode 100644
index 000000000..9c223d6cd
--- /dev/null
+++ b/vcl/source/filter/ieps/ieps.cxx
@@ -0,0 +1,825 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+
+#include <filter/EpsReader.hxx>
+#include <vcl/svapp.hxx>
+#include <vcl/gdimtf.hxx>
+#include <vcl/graph.hxx>
+#include <vcl/metaact.hxx>
+#include <vcl/virdev.hxx>
+#include <vcl/cvtgrf.hxx>
+#include <vcl/BitmapTools.hxx>
+#include <unotools/configmgr.hxx>
+#include <unotools/tempfile.hxx>
+#include <osl/process.h>
+#include <osl/file.hxx>
+#include <osl/thread.h>
+#include <rtl/byteseq.hxx>
+#include <sal/log.hxx>
+#include <o3tl/char16_t2wchar_t.hxx>
+#include <o3tl/safeint.hxx>
+#include <memory>
+#include <string_view>
+
+class FilterConfigItem;
+
+/*************************************************************************
+|*
+|* ImpSearchEntry()
+|*
+|* Description Checks if there is a string(pDest) of length nSize
+|* inside the memory area pSource which is nComp bytes long.
+|* Check is NON-CASE-SENSITIVE. The return value is the
+|* address where the string is found or NULL
+|*
+*************************************************************************/
+
+static sal_uInt8* ImplSearchEntry( sal_uInt8* pSource, sal_uInt8 const * pDest, size_t nComp, size_t nSize )
+{
+ while ( nComp-- >= nSize )
+ {
+ size_t i;
+ for ( i = 0; i < nSize; i++ )
+ {
+ if ( ( pSource[i]&~0x20 ) != ( pDest[i]&~0x20 ) )
+ break;
+ }
+ if ( i == nSize )
+ return pSource;
+ pSource++;
+ }
+ return nullptr;
+}
+
+
+// SecurityCount is the buffersize of the buffer in which we will parse for a number
+static tools::Long ImplGetNumber(sal_uInt8* &rBuf, sal_uInt32& nSecurityCount)
+{
+ bool bValid = true;
+ bool bNegative = false;
+ tools::Long nRetValue = 0;
+ while (nSecurityCount && (*rBuf == ' ' || *rBuf == 0x9))
+ {
+ ++rBuf;
+ --nSecurityCount;
+ }
+ while ( nSecurityCount && ( *rBuf != ' ' ) && ( *rBuf != 0x9 ) && ( *rBuf != 0xd ) && ( *rBuf != 0xa ) )
+ {
+ switch ( *rBuf )
+ {
+ case '.' :
+ // we'll only use the integer format
+ bValid = false;
+ break;
+ case '-' :
+ bNegative = true;
+ break;
+ default :
+ if ( ( *rBuf < '0' ) || ( *rBuf > '9' ) )
+ nSecurityCount = 1; // error parsing the bounding box values
+ else if ( bValid )
+ {
+ const bool bFail = o3tl::checked_multiply<tools::Long>(nRetValue, 10, nRetValue) ||
+ o3tl::checked_add<tools::Long>(nRetValue, *rBuf - '0', nRetValue);
+ if (bFail)
+ return 0;
+ }
+ break;
+ }
+ nSecurityCount--;
+ ++rBuf;
+ }
+ if ( bNegative )
+ nRetValue = -nRetValue;
+ return nRetValue;
+}
+
+
+static int ImplGetLen( sal_uInt8* pBuf, int nMax )
+{
+ int nLen = 0;
+ while( nLen != nMax )
+ {
+ sal_uInt8 nDat = *pBuf++;
+ if ( nDat == 0x0a || nDat == 0x25 )
+ break;
+ nLen++;
+ }
+ return nLen;
+}
+
+static void MakeAsMeta(Graphic &rGraphic)
+{
+ ScopedVclPtrInstance< VirtualDevice > pVDev;
+ GDIMetaFile aMtf;
+ Size aSize = rGraphic.GetPrefSize();
+
+ if( !aSize.Width() || !aSize.Height() )
+ aSize = Application::GetDefaultDevice()->PixelToLogic(
+ rGraphic.GetSizePixel(), MapMode(MapUnit::Map100thMM));
+ else
+ aSize = OutputDevice::LogicToLogic( aSize,
+ rGraphic.GetPrefMapMode(), MapMode(MapUnit::Map100thMM));
+
+ pVDev->EnableOutput( false );
+ aMtf.Record( pVDev );
+ pVDev->DrawBitmapEx( Point(), aSize, rGraphic.GetBitmapEx() );
+ aMtf.Stop();
+ aMtf.WindStart();
+ aMtf.SetPrefMapMode(MapMode(MapUnit::Map100thMM));
+ aMtf.SetPrefSize( aSize );
+ rGraphic = aMtf;
+}
+
+static oslProcessError runProcessWithPathSearch(const OUString &rProgName,
+ rtl_uString* pArgs[], sal_uInt32 nArgs, oslProcess *pProcess,
+ oslFileHandle *pIn, oslFileHandle *pOut, oslFileHandle *pErr)
+{
+ oslProcessError result = osl_Process_E_None;
+ oslSecurity pSecurity = osl_getCurrentSecurity();
+#ifdef _WIN32
+ /*
+ * ooo#72096
+ * On Window the underlying SearchPath searches in order of...
+ * The directory from which the application loaded.
+ * The current directory.
+ * The Windows system directory.
+ * The Windows directory.
+ * The directories that are listed in the PATH environment variable.
+ *
+ * Because one of our programs is called "convert" and there is a convert
+ * in the windows system directory, we want to explicitly search the PATH
+ * to avoid picking up on that one if ImageMagick's convert precedes it in
+ * PATH.
+ *
+ */
+ OUString url;
+ OUString path(o3tl::toU(_wgetenv(L"PATH")));
+
+ oslFileError err = osl_searchFileURL(rProgName.pData, path.pData, &url.pData);
+ if (err != osl_File_E_None)
+ result = osl_Process_E_NotFound;
+ else
+ result = osl_executeProcess_WithRedirectedIO(url.pData,
+ pArgs, nArgs, osl_Process_HIDDEN,
+ pSecurity, nullptr, nullptr, 0, pProcess, pIn, pOut, pErr);
+#else
+ result = osl_executeProcess_WithRedirectedIO(rProgName.pData,
+ pArgs, nArgs, osl_Process_SEARCHPATH | osl_Process_HIDDEN,
+ pSecurity, nullptr, nullptr, 0, pProcess, pIn, pOut, pErr);
+#endif
+ osl_freeSecurityHandle( pSecurity );
+ return result;
+}
+
+#if defined(_WIN32)
+# define EXESUFFIX ".exe"
+#else
+# define EXESUFFIX ""
+#endif
+
+static bool RenderAsEMF(const sal_uInt8* pBuf, sal_uInt32 nBytesRead, Graphic &rGraphic)
+{
+ utl::TempFile aTempOutput;
+ utl::TempFile aTempInput;
+ aTempOutput.EnableKillingFile();
+ aTempInput.EnableKillingFile();
+ OUString output;
+ osl::FileBase::getSystemPathFromFileURL(aTempOutput.GetURL(), output);
+ OUString input;
+ osl::FileBase::getSystemPathFromFileURL(aTempInput.GetURL(), input);
+
+ SvStream* pInputStream = aTempInput.GetStream(StreamMode::WRITE);
+ sal_uInt64 nCount = pInputStream->WriteBytes(pBuf, nBytesRead);
+ aTempInput.CloseStream();
+
+ //fdo#64161 pstoedit under non-windows uses libEMF to output the EMF, but
+ //libEMF cannot calculate the bounding box of text, so the overall bounding
+ //box is not increased to include that of any text in the eps
+ //
+ //-drawbb will force pstoedit to draw a pair of pixels with the bg color to
+ //the topleft and bottom right of the bounding box as pstoedit sees it,
+ //which libEMF will then extend its bounding box to fit
+ //
+ //-usebbfrominput forces pstoedit to take the original ps bounding box
+ //as the bounding box as it sees it, instead of calculating its own
+ //which also doesn't work for this example
+ //
+ //Under Linux, positioning of letters within pstoedit is very approximate.
+ //Using the -nfw option delegates the positioning to the reader, and we
+ //will do a proper job. The option is ignored on Windows.
+ OUString arg1("-usebbfrominput"); //-usebbfrominput use the original ps bounding box
+ OUString arg2("-f");
+ OUString arg3("emf:-OO -drawbb -nfw"); //-drawbb mark out the bounding box extent with bg pixels
+ //-nfw delegate letter placement to us
+ rtl_uString *args[] =
+ {
+ arg1.pData, arg2.pData, arg3.pData, input.pData, output.pData
+ };
+ oslProcess aProcess;
+ oslFileHandle pIn = nullptr;
+ oslFileHandle pOut = nullptr;
+ oslFileHandle pErr = nullptr;
+ oslProcessError eErr = runProcessWithPathSearch(
+ "pstoedit" EXESUFFIX,
+ args, SAL_N_ELEMENTS(args),
+ &aProcess, &pIn, &pOut, &pErr);
+
+ if (eErr!=osl_Process_E_None)
+ return false;
+
+ bool bRet = false;
+ if (pIn) osl_closeFile(pIn);
+ osl_joinProcess(aProcess);
+ osl_freeProcessHandle(aProcess);
+ bool bEMFSupported=true;
+ if (pOut)
+ {
+ rtl::ByteSequence seq;
+ if (osl_File_E_None == osl_readLine(pOut, reinterpret_cast<sal_Sequence **>(&seq)))
+ {
+ OString line( reinterpret_cast<const char *>(seq.getConstArray()), seq.getLength() );
+ if (line.startsWith("Unsupported output format"))
+ bEMFSupported=false;
+ }
+ osl_closeFile(pOut);
+ }
+ if (pErr) osl_closeFile(pErr);
+ if (nCount == nBytesRead && bEMFSupported)
+ {
+ SvFileStream aFile(output, StreamMode::READ);
+ if (GraphicConverter::Import(aFile, rGraphic, ConvertDataFormat::EMF) == ERRCODE_NONE)
+ bRet = true;
+ }
+
+ return bRet;
+}
+
+namespace {
+
+struct WriteData
+{
+ oslFileHandle m_pFile;
+ const sal_uInt8 *m_pBuf;
+ sal_uInt32 m_nBytesToWrite;
+};
+
+}
+
+extern "C" {
+
+static void WriteFileInThread(void *wData)
+{
+ sal_uInt64 nCount;
+ WriteData *wdata = static_cast<WriteData *>(wData);
+ osl_writeFile(wdata->m_pFile, wdata->m_pBuf, wdata->m_nBytesToWrite, &nCount);
+ // The number of bytes written does not matter.
+ // The helper process may close its input stream before reading it all.
+ // (e.g. at "showpage" in EPS)
+
+ // File must be closed here.
+ // Otherwise, the helper process may wait for the next input,
+ // then its stdout is not closed and osl_readFile() blocks.
+ if (wdata->m_pFile) osl_closeFile(wdata->m_pFile);
+}
+
+}
+
+static bool RenderAsBMPThroughHelper(const sal_uInt8* pBuf, sal_uInt32 nBytesRead,
+ Graphic& rGraphic,
+ std::initializer_list<std::u16string_view> aProgNames,
+ rtl_uString* pArgs[], size_t nArgs)
+{
+ oslProcess aProcess = nullptr;
+ oslFileHandle pIn = nullptr;
+ oslFileHandle pOut = nullptr;
+ oslFileHandle pErr = nullptr;
+ oslProcessError eErr = osl_Process_E_Unknown;
+ for (const auto& rProgName : aProgNames)
+ {
+ eErr = runProcessWithPathSearch(OUString(rProgName), pArgs, nArgs, &aProcess, &pIn, &pOut, &pErr);
+ if (eErr == osl_Process_E_None)
+ break;
+ }
+ if (eErr!=osl_Process_E_None)
+ return false;
+
+ WriteData Data;
+ Data.m_pFile = pIn;
+ Data.m_pBuf = pBuf;
+ Data.m_nBytesToWrite = nBytesRead;
+ oslThread hThread = osl_createThread(WriteFileInThread, &Data);
+
+ bool bRet = false;
+ sal_uInt64 nCount;
+ {
+ SvMemoryStream aMemStm;
+ sal_uInt8 aBuf[32000];
+ oslFileError eFileErr = osl_readFile(pOut, aBuf, 32000, &nCount);
+ while (eFileErr == osl_File_E_None && nCount)
+ {
+ aMemStm.WriteBytes(aBuf, sal::static_int_cast<std::size_t>(nCount));
+ eFileErr = osl_readFile(pOut, aBuf, 32000, &nCount);
+ }
+
+ aMemStm.Seek(0);
+ if (
+ aMemStm.GetEndOfData() &&
+ GraphicConverter::Import(aMemStm, rGraphic, ConvertDataFormat::BMP) == ERRCODE_NONE
+ )
+ {
+ MakeAsMeta(rGraphic);
+ bRet = true;
+ }
+ }
+ if (pOut) osl_closeFile(pOut);
+ if (pErr) osl_closeFile(pErr);
+ osl_joinProcess(aProcess);
+ osl_freeProcessHandle(aProcess);
+ osl_joinWithThread(hThread);
+ osl_destroyThread(hThread);
+ return bRet;
+}
+
+static bool RenderAsBMPThroughConvert(const sal_uInt8* pBuf, sal_uInt32 nBytesRead,
+ Graphic &rGraphic)
+{
+ // density in pixel/inch
+ OUString arg1("-density");
+ // since the preview is also used for PDF-Export & printing on non-PS-printers,
+ // use some better quality - 300x300 should allow some resizing as well
+ OUString arg2("300x300");
+ // read eps from STDIN
+ OUString arg3("eps:-");
+ // write bmp to STDOUT
+ OUString arg4("bmp:-");
+ rtl_uString *args[] =
+ {
+ arg1.pData, arg2.pData, arg3.pData, arg4.pData
+ };
+ return RenderAsBMPThroughHelper(pBuf, nBytesRead, rGraphic,
+ { u"convert" EXESUFFIX },
+ args,
+ SAL_N_ELEMENTS(args));
+}
+
+static bool RenderAsBMPThroughGS(const sal_uInt8* pBuf, sal_uInt32 nBytesRead,
+ Graphic &rGraphic)
+{
+ OUString arg1("-q");
+ OUString arg2("-dBATCH");
+ OUString arg3("-dNOPAUSE");
+ OUString arg4("-dPARANOIDSAFER");
+ OUString arg5("-dEPSCrop");
+ OUString arg6("-dTextAlphaBits=4");
+ OUString arg7("-dGraphicsAlphaBits=4");
+ OUString arg8("-r300x300");
+ OUString arg9("-sDEVICE=bmp16m");
+ OUString arg10("-sOutputFile=-");
+ OUString arg11("-");
+ rtl_uString *args[] =
+ {
+ arg1.pData, arg2.pData, arg3.pData, arg4.pData, arg5.pData,
+ arg6.pData, arg7.pData, arg8.pData, arg9.pData, arg10.pData,
+ arg11.pData
+ };
+ return RenderAsBMPThroughHelper(pBuf, nBytesRead, rGraphic,
+#ifdef _WIN32
+ // Try both 32-bit and 64-bit ghostscript executable name
+ {
+ u"gswin32c" EXESUFFIX,
+ u"gswin64c" EXESUFFIX,
+ },
+#else
+ { u"gs" EXESUFFIX },
+#endif
+ args,
+ SAL_N_ELEMENTS(args));
+}
+
+static bool RenderAsBMP(const sal_uInt8* pBuf, sal_uInt32 nBytesRead, Graphic &rGraphic)
+{
+ if (RenderAsBMPThroughGS(pBuf, nBytesRead, rGraphic))
+ return true;
+ else
+ return RenderAsBMPThroughConvert(pBuf, nBytesRead, rGraphic);
+}
+
+// this method adds a replacement action containing the original wmf or tiff replacement,
+// so the original eps can be written when storing to ODF.
+static void CreateMtfReplacementAction( GDIMetaFile& rMtf, SvStream& rStrm, sal_uInt32 nOrigPos, sal_uInt32 nPSSize,
+ sal_uInt32 nPosWMF, sal_uInt32 nSizeWMF, sal_uInt32 nPosTIFF, sal_uInt32 nSizeTIFF )
+{
+ OString aComment("EPSReplacementGraphic");
+ if ( nSizeWMF || nSizeTIFF )
+ {
+ std::vector<sal_uInt8> aWMFBuf;
+ if (nSizeWMF && checkSeek(rStrm, nOrigPos + nPosWMF) && rStrm.remainingSize() >= nSizeWMF)
+ {
+ aWMFBuf.resize(nSizeWMF);
+ aWMFBuf.resize(rStrm.ReadBytes(aWMFBuf.data(), nSizeWMF));
+ }
+ nSizeWMF = aWMFBuf.size();
+
+ std::vector<sal_uInt8> aTIFFBuf;
+ if (nSizeTIFF && checkSeek(rStrm, nOrigPos + nPosTIFF) && rStrm.remainingSize() >= nSizeTIFF)
+ {
+ aTIFFBuf.resize(nSizeTIFF);
+ aTIFFBuf.resize(rStrm.ReadBytes(aTIFFBuf.data(), nSizeTIFF));
+ }
+ nSizeTIFF = aTIFFBuf.size();
+
+ SvMemoryStream aReplacement( nSizeWMF + nSizeTIFF + 28 );
+ sal_uInt32 const nMagic = 0xc6d3d0c5;
+ sal_uInt32 nPPos = 28 + nSizeWMF + nSizeTIFF;
+ sal_uInt32 nWPos = nSizeWMF ? 28 : 0;
+ sal_uInt32 nTPos = nSizeTIFF ? 28 + nSizeWMF : 0;
+
+ aReplacement.WriteUInt32( nMagic ).WriteUInt32( nPPos ).WriteUInt32( nPSSize )
+ .WriteUInt32( nWPos ).WriteUInt32( nSizeWMF )
+ .WriteUInt32( nTPos ).WriteUInt32( nSizeTIFF );
+
+ aReplacement.WriteBytes(aWMFBuf.data(), nSizeWMF);
+ aReplacement.WriteBytes(aTIFFBuf.data(), nSizeTIFF);
+ rMtf.AddAction( static_cast<MetaAction*>( new MetaCommentAction( aComment, 0, static_cast<const sal_uInt8*>(aReplacement.GetData()), aReplacement.Tell() ) ) );
+ }
+ else
+ rMtf.AddAction( static_cast<MetaAction*>( new MetaCommentAction( aComment, 0, nullptr, 0 ) ) );
+}
+
+//there is no preview -> make a red box
+static void MakePreview(sal_uInt8* pBuf, sal_uInt32 nBytesRead,
+ tools::Long nWidth, tools::Long nHeight, Graphic &rGraphic)
+{
+ GDIMetaFile aMtf;
+ ScopedVclPtrInstance< VirtualDevice > pVDev;
+ vcl::Font aFont;
+
+ pVDev->EnableOutput( false );
+ aMtf.Record( pVDev );
+ pVDev->SetLineColor( COL_RED );
+ pVDev->SetFillColor();
+
+ aFont.SetColor( COL_LIGHTRED );
+
+ pVDev->Push( vcl::PushFlags::FONT );
+ pVDev->SetFont( aFont );
+
+ tools::Rectangle aRect( Point( 1, 1 ), Size( nWidth - 2, nHeight - 2 ) );
+ pVDev->DrawRect( aRect );
+
+ OUString aString;
+ int nLen;
+ sal_uInt8* pDest = ImplSearchEntry( pBuf, reinterpret_cast<sal_uInt8 const *>("%%Title:"), nBytesRead - 32, 8 );
+ sal_uInt32 nRemainingBytes = pDest ? (nBytesRead - (pDest - pBuf)) : 0;
+ if (nRemainingBytes >= 8)
+ {
+ pDest += 8;
+ nRemainingBytes -= 8;
+ if (nRemainingBytes && *pDest == ' ')
+ {
+ ++pDest;
+ --nRemainingBytes;
+ }
+ nLen = ImplGetLen(pDest, std::min<sal_uInt32>(nRemainingBytes, 32));
+ if (o3tl::make_unsigned(nLen) < nRemainingBytes)
+ {
+ sal_uInt8 aOldValue(pDest[ nLen ]); pDest[ nLen ] = 0;
+ if ( strcmp( reinterpret_cast<char*>(pDest), "none" ) != 0 )
+ {
+ const char* pStr = reinterpret_cast<char*>(pDest);
+ aString += " Title:" + OUString(pStr, strlen(pStr), RTL_TEXTENCODING_ASCII_US) + "\n";
+ }
+ pDest[ nLen ] = aOldValue;
+ }
+ }
+ pDest = ImplSearchEntry( pBuf, reinterpret_cast<sal_uInt8 const *>("%%Creator:"), nBytesRead - 32, 10 );
+ nRemainingBytes = pDest ? (nBytesRead - (pDest - pBuf)) : 0;
+ if (nRemainingBytes >= 10)
+ {
+ pDest += 10;
+ nRemainingBytes -= 10;
+ if (nRemainingBytes && *pDest == ' ')
+ {
+ ++pDest;
+ --nRemainingBytes;
+ }
+ nLen = ImplGetLen(pDest, std::min<sal_uInt32>(nRemainingBytes, 32));
+ if (o3tl::make_unsigned(nLen) < nRemainingBytes)
+ {
+ sal_uInt8 aOldValue(pDest[nLen]); pDest[nLen] = 0;
+ const char* pStr = reinterpret_cast<char*>(pDest);
+ aString += " Creator:" + OUString(pStr, strlen(pStr), RTL_TEXTENCODING_ASCII_US) + "\n";
+ pDest[nLen] = aOldValue;
+ }
+ }
+ pDest = ImplSearchEntry( pBuf, reinterpret_cast<sal_uInt8 const *>("%%CreationDate:"), nBytesRead - 32, 15 );
+ nRemainingBytes = pDest ? (nBytesRead - (pDest - pBuf)) : 0;
+ if (nRemainingBytes >= 15)
+ {
+ pDest += 15;
+ nRemainingBytes -= 15;
+ if (nRemainingBytes && *pDest == ' ')
+ {
+ ++pDest;
+ --nRemainingBytes;
+ }
+ nLen = ImplGetLen(pDest, std::min<sal_uInt32>(nRemainingBytes, 32));
+ if (o3tl::make_unsigned(nLen) < nRemainingBytes)
+ {
+ sal_uInt8 aOldValue(pDest[ nLen ]); pDest[ nLen ] = 0;
+ if ( strcmp( reinterpret_cast<char*>(pDest), "none" ) != 0 )
+ {
+ aString += " CreationDate:" + OUString::createFromAscii( reinterpret_cast<char*>(pDest) ) + "\n";
+ const char* pStr = reinterpret_cast<char*>(pDest);
+ aString += " CreationDate:" + OUString(pStr, strlen(pStr), RTL_TEXTENCODING_ASCII_US) + "\n";
+ }
+ pDest[ nLen ] = aOldValue;
+ }
+ }
+ pDest = ImplSearchEntry( pBuf, reinterpret_cast<sal_uInt8 const *>("%%LanguageLevel:"), nBytesRead - 4, 16 );
+ nRemainingBytes = pDest ? (nBytesRead - (pDest - pBuf)) : 0;
+ if (nRemainingBytes >= 16)
+ {
+ pDest += 16;
+ nRemainingBytes -= 16;
+ sal_uInt32 nCount = std::min<sal_uInt32>(nRemainingBytes, 4U);
+ sal_uInt32 nNumber = ImplGetNumber(pDest, nCount);
+ if (nCount && nNumber < 10)
+ {
+ aString += " LanguageLevel:" + OUString::number( nNumber );
+ }
+ }
+ pVDev->DrawText( aRect, aString, DrawTextFlags::Clip | DrawTextFlags::MultiLine );
+ pVDev->Pop();
+ aMtf.Stop();
+ aMtf.WindStart();
+ aMtf.SetPrefMapMode(MapMode(MapUnit::MapPoint));
+ aMtf.SetPrefSize( Size( nWidth, nHeight ) );
+ rGraphic = aMtf;
+}
+
+//================== GraphicImport - the exported function ================
+
+
+bool ImportEpsGraphic( SvStream & rStream, Graphic & rGraphic)
+{
+ if ( rStream.GetError() )
+ return false;
+
+ Graphic aGraphic;
+ bool bRetValue = false;
+ bool bHasPreview = false;
+ sal_uInt32 nSignature = 0, nPSStreamPos, nPSSize = 0;
+ sal_uInt32 nSizeWMF = 0;
+ sal_uInt32 nPosWMF = 0;
+ sal_uInt32 nSizeTIFF = 0;
+ sal_uInt32 nPosTIFF = 0;
+
+ auto nOrigPos = nPSStreamPos = rStream.Tell();
+ SvStreamEndian nOldFormat = rStream.GetEndian();
+
+ rStream.SetEndian( SvStreamEndian::LITTLE );
+ rStream.ReadUInt32( nSignature );
+ if ( nSignature == 0xc6d3d0c5 )
+ {
+ rStream.ReadUInt32( nPSStreamPos ).ReadUInt32( nPSSize ).ReadUInt32( nPosWMF ).ReadUInt32( nSizeWMF );
+
+ // first we try to get the metafile grafix
+
+ if ( nSizeWMF )
+ {
+ if (nPosWMF && checkSeek(rStream, nOrigPos + nPosWMF))
+ {
+ if (GraphicConverter::Import(rStream, aGraphic, ConvertDataFormat::WMF) == ERRCODE_NONE)
+ bHasPreview = bRetValue = true;
+ }
+ }
+ else
+ {
+ rStream.ReadUInt32( nPosTIFF ).ReadUInt32( nSizeTIFF );
+
+ // else we have to get the tiff grafix
+
+ if (nPosTIFF && nSizeTIFF && checkSeek(rStream, nOrigPos + nPosTIFF))
+ {
+ if ( GraphicConverter::Import( rStream, aGraphic, ConvertDataFormat::TIF ) == ERRCODE_NONE )
+ {
+ MakeAsMeta(aGraphic);
+ rStream.Seek( nOrigPos + nPosTIFF );
+ bHasPreview = bRetValue = true;
+ }
+ }
+ }
+ }
+ else
+ {
+ nPSStreamPos = nOrigPos; // no preview available _>so we must get the size manually
+ nPSSize = rStream.Seek( STREAM_SEEK_TO_END ) - nOrigPos;
+ }
+
+ std::vector<sal_uInt8> aHeader(22, 0);
+ rStream.Seek( nPSStreamPos );
+ rStream.ReadBytes(aHeader.data(), 22); // check PostScript header
+ sal_uInt8* pHeader = aHeader.data();
+ bool bOk = ImplSearchEntry(pHeader, reinterpret_cast<sal_uInt8 const *>("%!PS-Adobe"), 10, 10) &&
+ ImplSearchEntry(pHeader + 15, reinterpret_cast<sal_uInt8 const *>("EPS"), 3, 3);
+ if (bOk)
+ {
+ rStream.Seek(nPSStreamPos);
+ bOk = rStream.remainingSize() >= nPSSize;
+ SAL_WARN_IF(!bOk, "filter.eps", "eps claims to be: " << nPSSize << " in size, but only " << rStream.remainingSize() << " remains");
+ }
+ if (bOk)
+ {
+ std::unique_ptr<sal_uInt8[]> pBuf( new sal_uInt8[ nPSSize ] );
+
+ sal_uInt32 nBufStartPos = rStream.Tell();
+ sal_uInt32 nBytesRead = rStream.ReadBytes(pBuf.get(), nPSSize);
+ if ( nBytesRead == nPSSize )
+ {
+ sal_uInt32 nSecurityCount = 32;
+ // if there is no tiff/wmf preview, we will parse for a preview in
+ // the eps prolog
+ if (!bHasPreview && nBytesRead >= nSecurityCount)
+ {
+ sal_uInt8* pDest = ImplSearchEntry( pBuf.get(), reinterpret_cast<sal_uInt8 const *>("%%BeginPreview:"), nBytesRead - nSecurityCount, 15 );
+ sal_uInt32 nRemainingBytes = pDest ? (nBytesRead - (pDest - pBuf.get())) : 0;
+ if (nRemainingBytes >= 15)
+ {
+ pDest += 15;
+ nSecurityCount = nRemainingBytes - 15;
+ tools::Long nWidth = ImplGetNumber(pDest, nSecurityCount);
+ tools::Long nHeight = ImplGetNumber(pDest, nSecurityCount);
+ tools::Long nBitDepth = ImplGetNumber(pDest, nSecurityCount);
+ tools::Long nScanLines = ImplGetNumber(pDest, nSecurityCount);
+ pDest = ImplSearchEntry(pDest, reinterpret_cast<sal_uInt8 const *>("%"), nSecurityCount, 1); // go to the first Scanline
+ bOk = pDest && nWidth > 0 && nHeight > 0 && ( ( nBitDepth == 1 ) || ( nBitDepth == 8 ) ) && nScanLines;
+ if (bOk)
+ {
+ tools::Long nResult;
+ bOk = !o3tl::checked_multiply(nWidth, nHeight, nResult) && nResult <= SAL_MAX_INT32/2/3;
+ }
+ if (bOk)
+ {
+ rStream.Seek( nBufStartPos + ( pDest - pBuf.get() ) );
+
+ vcl::bitmap::RawBitmap aBitmap( Size( nWidth, nHeight ), 24 );
+ {
+ bool bIsValid = true;
+ sal_uInt8 nDat = 0;
+ char nByte;
+ for (tools::Long y = 0; bIsValid && y < nHeight; ++y)
+ {
+ int nBitsLeft = 0;
+ for (tools::Long x = 0; x < nWidth; ++x)
+ {
+ if ( --nBitsLeft < 0 )
+ {
+ while ( bIsValid && ( nBitsLeft != 7 ) )
+ {
+ rStream.ReadChar(nByte);
+ bIsValid = rStream.good();
+ if (!bIsValid)
+ break;
+ switch (nByte)
+ {
+ case 0x0a :
+ if ( --nScanLines < 0 )
+ bIsValid = false;
+ break;
+ case 0x09 :
+ case 0x0d :
+ case 0x20 :
+ case 0x25 :
+ break;
+ default:
+ {
+ if ( nByte >= '0' )
+ {
+ if ( nByte > '9' )
+ {
+ nByte &=~0x20; // case none sensitive for hexadecimal values
+ nByte -= ( 'A' - 10 );
+ if ( nByte > 15 )
+ bIsValid = false;
+ }
+ else
+ nByte -= '0';
+ nBitsLeft += 4;
+ nDat <<= 4;
+ nDat |= ( nByte ^ 0xf ); // in epsi a zero bit represents white color
+ }
+ else
+ bIsValid = false;
+ }
+ break;
+ }
+ }
+ }
+ if (!bIsValid)
+ break;
+ if ( nBitDepth == 1 )
+ aBitmap.SetPixel( y, x, Color(ColorTransparency, static_cast<sal_uInt8>(nDat >> nBitsLeft) & 1) );
+ else
+ {
+ aBitmap.SetPixel( y, x, nDat ? COL_WHITE : COL_BLACK ); // nBitDepth == 8
+ nBitsLeft = 0;
+ }
+ }
+ }
+ if (bIsValid)
+ {
+ ScopedVclPtrInstance<VirtualDevice> pVDev;
+ GDIMetaFile aMtf;
+ Size aSize( nWidth, nHeight );
+ pVDev->EnableOutput( false );
+ aMtf.Record( pVDev );
+ aSize = OutputDevice::LogicToLogic(aSize, MapMode(), MapMode(MapUnit::Map100thMM));
+ pVDev->DrawBitmapEx( Point(), aSize, vcl::bitmap::CreateFromData(std::move(aBitmap)) );
+ aMtf.Stop();
+ aMtf.WindStart();
+ aMtf.SetPrefMapMode(MapMode(MapUnit::Map100thMM));
+ aMtf.SetPrefSize( aSize );
+ aGraphic = aMtf;
+ bHasPreview = bRetValue = true;
+ }
+ }
+ }
+ }
+ }
+
+ sal_uInt8* pDest = ImplSearchEntry( pBuf.get(), reinterpret_cast<sal_uInt8 const *>("%%BoundingBox:"), nBytesRead, 14 );
+ sal_uInt32 nRemainingBytes = pDest ? (nBytesRead - (pDest - pBuf.get())) : 0;
+ if (nRemainingBytes >= 14)
+ {
+ pDest += 14;
+ nSecurityCount = std::min<sal_uInt32>(nRemainingBytes - 14, 100);
+ tools::Long nNumb[4];
+ nNumb[0] = nNumb[1] = nNumb[2] = nNumb[3] = 0;
+ for ( int i = 0; ( i < 4 ) && nSecurityCount; i++ )
+ {
+ nNumb[ i ] = ImplGetNumber(pDest, nSecurityCount);
+ }
+ bool bFail = nSecurityCount == 0;
+ tools::Long nWidth(0), nHeight(0);
+ if (!bFail)
+ bFail = o3tl::checked_sub(nNumb[2], nNumb[0], nWidth) || o3tl::checked_add(nWidth, tools::Long(1), nWidth);
+ if (!bFail)
+ bFail = o3tl::checked_sub(nNumb[3], nNumb[1], nHeight) || o3tl::checked_add(nHeight, tools::Long(1), nHeight);
+ if (!bFail && nWidth > 0 && nHeight > 0)
+ {
+ GDIMetaFile aMtf;
+
+ // if there is no preview -> try with gs to make one
+ if (!bHasPreview && !utl::ConfigManager::IsFuzzing())
+ {
+ bHasPreview = RenderAsEMF(pBuf.get(), nBytesRead, aGraphic);
+ if (!bHasPreview)
+ bHasPreview = RenderAsBMP(pBuf.get(), nBytesRead, aGraphic);
+ }
+
+ // if there is no preview -> make a red box
+ if( !bHasPreview )
+ {
+ MakePreview(pBuf.get(), nBytesRead, nWidth, nHeight,
+ aGraphic);
+ }
+
+ GfxLink aGfxLink( std::move(pBuf), nPSSize, GfxLinkType::EpsBuffer ) ;
+ aMtf.AddAction( static_cast<MetaAction*>( new MetaEPSAction( Point(), Size( nWidth, nHeight ),
+ aGfxLink, aGraphic.GetGDIMetaFile() ) ) );
+ CreateMtfReplacementAction( aMtf, rStream, nOrigPos, nPSSize, nPosWMF, nSizeWMF, nPosTIFF, nSizeTIFF );
+ aMtf.WindStart();
+ aMtf.SetPrefMapMode(MapMode(MapUnit::MapPoint));
+ aMtf.SetPrefSize( Size( nWidth, nHeight ) );
+ rGraphic = aMtf;
+ bRetValue = true;
+ }
+ }
+ }
+ }
+
+ rStream.SetEndian(nOldFormat);
+ rStream.Seek( nOrigPos );
+ return bRetValue;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/vcl/source/filter/igif/decode.cxx b/vcl/source/filter/igif/decode.cxx
new file mode 100644
index 000000000..b062593a9
--- /dev/null
+++ b/vcl/source/filter/igif/decode.cxx
@@ -0,0 +1,214 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include "decode.hxx"
+
+#include <cstdlib>
+#include <cstring>
+
+struct GIFLZWTableEntry
+{
+ GIFLZWTableEntry* pPrev;
+ GIFLZWTableEntry* pFirst;
+ sal_uInt8 nData;
+};
+
+GIFLZWDecompressor::GIFLZWDecompressor(sal_uInt8 cDataSize)
+ : pTable(new GIFLZWTableEntry[4098])
+ , pOutBufData(pOutBuf.data() + 4096)
+ , pBlockBuf(nullptr)
+ , nInputBitsBuf(0)
+ , bEOIFound(false)
+ , nDataSize(cDataSize)
+ , nBlockBufSize(0)
+ , nBlockBufPos(0)
+ , nClearCode(1 << nDataSize)
+ , nEOICode(nClearCode + 1)
+ , nTableSize(nEOICode + 1)
+ , nCodeSize(nDataSize + 1)
+ , nOldCode(0xffff)
+ , nOutBufDataLen(0)
+ , nInputBitsBufSize(0)
+{
+ for (sal_uInt16 i = 0; i < nTableSize; ++i)
+ {
+ pTable[i].pPrev = nullptr;
+ pTable[i].pFirst = &pTable[i];
+ pTable[i].nData = static_cast<sal_uInt8>(i);
+ }
+
+ memset(pTable.get() + nTableSize, 0, sizeof(GIFLZWTableEntry) * (4098 - nTableSize));
+}
+
+GIFLZWDecompressor::~GIFLZWDecompressor()
+{
+}
+
+Scanline GIFLZWDecompressor::DecompressBlock( sal_uInt8* pSrc, sal_uInt8 cBufSize,
+ sal_uLong& rCount, bool& rEOI )
+{
+ sal_uLong nTargetSize = 4096;
+ sal_uLong nCount = 0;
+ sal_uInt8* pTarget = static_cast<sal_uInt8*>(std::malloc( nTargetSize ));
+ sal_uInt8* pTmpTarget = pTarget;
+
+ nBlockBufSize = cBufSize;
+ nBlockBufPos = 0;
+ pBlockBuf = pSrc;
+
+ while (pTarget && ProcessOneCode())
+ {
+ nCount += nOutBufDataLen;
+
+ if( nCount > nTargetSize )
+ {
+ sal_uLong nNewSize = nTargetSize << 1;
+ sal_uLong nOffset = pTmpTarget - pTarget;
+ if (auto p = static_cast<sal_uInt8*>(std::realloc(pTarget, nNewSize)))
+ pTarget = p;
+ else
+ {
+ free(pTarget);
+ pTarget = nullptr;
+ break;
+ }
+
+ nTargetSize = nNewSize;
+ pTmpTarget = pTarget + nOffset;
+ }
+
+ memcpy( pTmpTarget, pOutBufData, nOutBufDataLen );
+ pTmpTarget += nOutBufDataLen;
+ pOutBufData += nOutBufDataLen;
+ nOutBufDataLen = 0;
+
+ if ( bEOIFound )
+ break;
+ }
+
+ rCount = nCount;
+ rEOI = bEOIFound;
+
+ return pTarget;
+}
+
+bool GIFLZWDecompressor::AddToTable( sal_uInt16 nPrevCode, sal_uInt16 nCodeFirstData )
+{
+ if( nTableSize < 4096 )
+ {
+ GIFLZWTableEntry* pE = pTable.get() + nTableSize;
+ pE->pPrev = pTable.get() + nPrevCode;
+ pE->pFirst = pE->pPrev->pFirst;
+ GIFLZWTableEntry *pEntry = pTable[nCodeFirstData].pFirst;
+ if (!pEntry)
+ return false;
+ pE->nData = pEntry->nData;
+ nTableSize++;
+
+ if ( ( nTableSize == static_cast<sal_uInt16>(1 << nCodeSize) ) && ( nTableSize < 4096 ) )
+ nCodeSize++;
+ }
+ return true;
+}
+
+bool GIFLZWDecompressor::ProcessOneCode()
+{
+ bool bRet = false;
+ bool bEndOfBlock = false;
+
+ while( nInputBitsBufSize < nCodeSize )
+ {
+ if( nBlockBufPos >= nBlockBufSize )
+ {
+ bEndOfBlock = true;
+ break;
+ }
+
+ nInputBitsBuf |= static_cast<sal_uLong>(pBlockBuf[ nBlockBufPos++ ]) << nInputBitsBufSize;
+ nInputBitsBufSize += 8;
+ }
+
+ if ( !bEndOfBlock )
+ {
+ // fetch code from input buffer
+ sal_uInt16 nCode = sal::static_int_cast< sal_uInt16 >(
+ static_cast<sal_uInt16>(nInputBitsBuf) & ( ~( 0xffff << nCodeSize ) ));
+ nInputBitsBuf >>= nCodeSize;
+ nInputBitsBufSize = nInputBitsBufSize - nCodeSize;
+
+ if ( nCode < nClearCode )
+ {
+ bool bOk = true;
+ if ( nOldCode != 0xffff )
+ bOk = AddToTable(nOldCode, nCode);
+ if (!bOk)
+ return false;
+ }
+ else if ( ( nCode > nEOICode ) && ( nCode <= nTableSize ) )
+ {
+ if ( nOldCode != 0xffff )
+ {
+ bool bOk;
+ if ( nCode == nTableSize )
+ bOk = AddToTable( nOldCode, nOldCode );
+ else
+ bOk = AddToTable( nOldCode, nCode );
+ if (!bOk)
+ return false;
+ }
+ }
+ else
+ {
+ if ( nCode == nClearCode )
+ {
+ nTableSize = nEOICode + 1;
+ nCodeSize = nDataSize + 1;
+ nOldCode = 0xffff;
+ nOutBufDataLen = 0;
+ }
+ else
+ bEOIFound = true;
+
+ return true;
+ }
+
+ nOldCode = nCode;
+
+ if (nCode >= 4096)
+ return false;
+
+ // write character(/-sequence) of code nCode in the output buffer:
+ GIFLZWTableEntry* pE = pTable.get() + nCode;
+ do
+ {
+ if (pOutBufData == pOutBuf.data()) //can't go back past start
+ return false;
+ nOutBufDataLen++;
+ *(--pOutBufData) = pE->nData;
+ pE = pE->pPrev;
+ }
+ while( pE );
+
+ bRet = true;
+ }
+
+ return bRet;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/vcl/source/filter/igif/decode.hxx b/vcl/source/filter/igif/decode.hxx
new file mode 100644
index 000000000..6e16730a3
--- /dev/null
+++ b/vcl/source/filter/igif/decode.hxx
@@ -0,0 +1,66 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#ifndef INCLUDED_VCL_SOURCE_FILTER_IGIF_DECODE_HXX
+#define INCLUDED_VCL_SOURCE_FILTER_IGIF_DECODE_HXX
+
+#include <tools/solar.h>
+#include <vcl/Scanline.hxx>
+#include <array>
+#include <memory>
+
+struct GIFLZWTableEntry;
+
+class GIFLZWDecompressor
+{
+ std::unique_ptr<GIFLZWTableEntry[]>
+ pTable;
+ std::array<sal_uInt8, 4096>
+ pOutBuf;
+ sal_uInt8* pOutBufData;
+ sal_uInt8* pBlockBuf;
+ sal_uLong nInputBitsBuf;
+ bool bEOIFound;
+ sal_uInt8 nDataSize;
+ sal_uInt8 nBlockBufSize;
+ sal_uInt8 nBlockBufPos;
+ sal_uInt16 nClearCode;
+ sal_uInt16 nEOICode;
+ sal_uInt16 nTableSize;
+ sal_uInt16 nCodeSize;
+ sal_uInt16 nOldCode;
+ sal_uInt16 nOutBufDataLen;
+ sal_uInt16 nInputBitsBufSize;
+
+ bool AddToTable(sal_uInt16 nPrevCode, sal_uInt16 nCodeFirstData);
+ bool ProcessOneCode();
+
+ GIFLZWDecompressor(const GIFLZWDecompressor&) = delete;
+ GIFLZWDecompressor& operator=(const GIFLZWDecompressor&) = delete;
+public:
+
+ explicit GIFLZWDecompressor( sal_uInt8 cDataSize );
+ ~GIFLZWDecompressor();
+
+ Scanline DecompressBlock( sal_uInt8* pSrc, sal_uInt8 cBufSize, sal_uLong& rCount, bool& rEOI );
+};
+
+#endif
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/vcl/source/filter/igif/gifread.cxx b/vcl/source/filter/igif/gifread.cxx
new file mode 100644
index 000000000..fa1270e6a
--- /dev/null
+++ b/vcl/source/filter/igif/gifread.cxx
@@ -0,0 +1,1002 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <sal/log.hxx>
+#include <tools/stream.hxx>
+#include "decode.hxx"
+#include "gifread.hxx"
+#include <memory>
+#include <bitmap/BitmapWriteAccess.hxx>
+#include <graphic/GraphicReader.hxx>
+
+#define NO_PENDING( rStm ) ( ( rStm ).GetError() != ERRCODE_IO_PENDING )
+
+namespace {
+
+enum GIFAction
+{
+ GLOBAL_HEADER_READING,
+ MARKER_READING,
+ EXTENSION_READING,
+ LOCAL_HEADER_READING,
+ FIRST_BLOCK_READING,
+ NEXT_BLOCK_READING,
+ ABORT_READING,
+ END_READING
+};
+
+enum ReadState
+{
+ GIFREAD_OK,
+ GIFREAD_ERROR,
+ GIFREAD_NEED_MORE
+};
+
+}
+
+class GIFLZWDecompressor;
+
+class SvStream;
+
+namespace {
+
+class GIFReader : public GraphicReader
+{
+ Animation aAnimation;
+ sal_uInt64 nAnimationByteSize;
+ sal_uInt64 nAnimationMinFileData;
+ Bitmap aBmp8;
+ Bitmap aBmp1;
+ BitmapPalette aGPalette;
+ BitmapPalette aLPalette;
+ SvStream& rIStm;
+ std::vector<sal_uInt8> aSrcBuf;
+ std::unique_ptr<GIFLZWDecompressor> pDecomp;
+ BitmapScopedWriteAccess pAcc8;
+ BitmapScopedWriteAccess pAcc1;
+ tools::Long nYAcc;
+ tools::Long nLastPos;
+ sal_uInt64 nMaxStreamData;
+ sal_uInt32 nLogWidth100;
+ sal_uInt32 nLogHeight100;
+ sal_uInt16 nTimer;
+ sal_uInt16 nGlobalWidth; // maximum imagewidth from header
+ sal_uInt16 nGlobalHeight; // maximum imageheight from header
+ sal_uInt16 nImageWidth; // maximum screenwidth from header
+ sal_uInt16 nImageHeight; // maximum screenheight from header
+ sal_uInt16 nImagePosX;
+ sal_uInt16 nImagePosY;
+ sal_uInt16 nImageX; // maximum screenwidth from header
+ sal_uInt16 nImageY; // maximum screenheight from header
+ sal_uInt16 nLastImageY;
+ sal_uInt16 nLastInterCount;
+ sal_uInt16 nLoops;
+ GIFAction eActAction;
+ bool bStatus;
+ bool bGCTransparent; // is the image transparent, if yes:
+ bool bInterlaced;
+ bool bOverreadBlock;
+ bool bImGraphicReady;
+ bool bGlobalPalette;
+ sal_uInt8 nBackgroundColor; // backgroundcolour
+ sal_uInt8 nGCTransparentIndex; // pixels of this index are transparent
+ sal_uInt8 nGCDisposalMethod; // 'Disposal Method' (see GIF docs)
+ sal_uInt8 cTransIndex1;
+ sal_uInt8 cNonTransIndex1;
+
+ void ReadPaletteEntries( BitmapPalette* pPal, sal_uLong nCount );
+ void ClearImageExtensions();
+ void CreateBitmaps( tools::Long nWidth, tools::Long nHeight, BitmapPalette* pPal, bool bWatchForBackgroundColor );
+ bool ReadGlobalHeader();
+ bool ReadExtension();
+ bool ReadLocalHeader();
+ sal_uLong ReadNextBlock();
+ void FillImages( const sal_uInt8* pBytes, sal_uLong nCount );
+ void CreateNewBitmaps();
+ bool ProcessGIF();
+
+public:
+
+ ReadState ReadGIF( Graphic& rGraphic );
+ bool ReadIsAnimated();
+ void GetLogicSize(Size& rLogicSize);
+ Graphic GetIntermediateGraphic();
+
+ explicit GIFReader( SvStream& rStm );
+};
+
+}
+
+GIFReader::GIFReader( SvStream& rStm )
+ : nAnimationByteSize(0)
+ , nAnimationMinFileData(0)
+ , aGPalette ( 256 )
+ , aLPalette ( 256 )
+ , rIStm ( rStm )
+ , nYAcc ( 0 )
+ , nLastPos ( rStm.Tell() )
+ , nMaxStreamData( rStm.remainingSize() )
+ , nLogWidth100 ( 0 )
+ , nLogHeight100 ( 0 )
+ , nGlobalWidth ( 0 )
+ , nGlobalHeight ( 0 )
+ , nImageWidth ( 0 )
+ , nImageHeight ( 0 )
+ , nImagePosX ( 0 )
+ , nImagePosY ( 0 )
+ , nImageX ( 0 )
+ , nImageY ( 0 )
+ , nLastImageY ( 0 )
+ , nLastInterCount ( 0 )
+ , nLoops ( 1 )
+ , eActAction ( GLOBAL_HEADER_READING )
+ , bStatus ( false )
+ , bGCTransparent ( false )
+ , bInterlaced ( false)
+ , bOverreadBlock ( false )
+ , bImGraphicReady ( false )
+ , bGlobalPalette ( false )
+ , nBackgroundColor ( 0 )
+ , nGCTransparentIndex ( 0 )
+ , cTransIndex1 ( 0 )
+ , cNonTransIndex1 ( 0 )
+{
+ maUpperName = "SVIGIF";
+ aSrcBuf.resize(256); // Memory buffer for ReadNextBlock
+ ClearImageExtensions();
+}
+
+void GIFReader::ClearImageExtensions()
+{
+ nGCDisposalMethod = 0;
+ bGCTransparent = false;
+ nTimer = 0;
+}
+
+void GIFReader::CreateBitmaps(tools::Long nWidth, tools::Long nHeight, BitmapPalette* pPal,
+ bool bWatchForBackgroundColor)
+{
+ const Size aSize(nWidth, nHeight);
+
+ sal_uInt64 nCombinedPixSize = nWidth * nHeight;
+ if (bGCTransparent)
+ nCombinedPixSize += (nCombinedPixSize/8);
+
+ // "Overall data compression asymptotically approaches 3839 × 8 / 12 = 2559 1/3"
+ // so assume compression of 1:2560 is possible
+ // (http://cloudinary.com/blog/a_one_color_image_is_worth_two_thousand_words suggests
+ // 1:1472.88 [184.11 x 8] is more realistic)
+
+ sal_uInt64 nMinFileData = nWidth * nHeight / 2560;
+
+ nMinFileData += nAnimationMinFileData;
+ nCombinedPixSize += nAnimationByteSize;
+
+ if (nMaxStreamData < nMinFileData)
+ {
+ //there is nowhere near enough data in this stream to fill the claimed dimensions
+ SAL_WARN("vcl.filter", "in gif frame index " << aAnimation.Count() << " gif claims dimensions " << nWidth << " x " << nHeight <<
+ " but filesize of " << nMaxStreamData << " is surely insufficiently large to fill all frame images");
+ bStatus = false;
+ return;
+ }
+
+ // Don't bother allocating a bitmap of a size that would fail on a
+ // 32-bit system. We have at least one unit tests that is expected
+ // to fail (loading a 65535*65535 size GIF
+ // svtools/qa/cppunit/data/gif/fail/CVE-2008-5937-1.gif), but
+ // which doesn't fail on 64-bit macOS at least. Why the loading
+ // fails on 64-bit Linux, no idea.
+ if (nCombinedPixSize >= SAL_MAX_INT32/3*2)
+ {
+ bStatus = false;
+ return;
+ }
+
+ if (!aSize.Width() || !aSize.Height())
+ {
+ bStatus = false;
+ return;
+ }
+
+ if (bGCTransparent)
+ {
+ const Color aWhite(COL_WHITE);
+
+ aBmp1 = Bitmap(aSize, vcl::PixelFormat::N1_BPP);
+
+ if (!aAnimation.Count())
+ aBmp1.Erase(aWhite);
+
+ pAcc1 = BitmapScopedWriteAccess(aBmp1);
+
+ if (pAcc1)
+ {
+ cTransIndex1 = static_cast<sal_uInt8>(pAcc1->GetBestPaletteIndex(aWhite));
+ cNonTransIndex1 = cTransIndex1 ? 0 : 1;
+ }
+ else
+ {
+ bStatus = false;
+ }
+ }
+
+ if (bStatus)
+ {
+ aBmp8 = Bitmap(aSize, vcl::PixelFormat::N8_BPP, pPal);
+
+ if (!aBmp8.IsEmpty() && bWatchForBackgroundColor && aAnimation.Count())
+ aBmp8.Erase((*pPal)[nBackgroundColor]);
+ else
+ aBmp8.Erase(COL_WHITE);
+
+ pAcc8 = BitmapScopedWriteAccess(aBmp8);
+ bStatus = bool(pAcc8);
+ }
+}
+
+bool GIFReader::ReadGlobalHeader()
+{
+ char pBuf[ 7 ];
+ bool bRet = false;
+
+ auto nRead = rIStm.ReadBytes(pBuf, 6);
+ if (nRead == 6 && NO_PENDING(rIStm))
+ {
+ pBuf[ 6 ] = 0;
+ if( !strcmp( pBuf, "GIF87a" ) || !strcmp( pBuf, "GIF89a" ) )
+ {
+ nRead = rIStm.ReadBytes(pBuf, 7);
+ if (nRead == 7 && NO_PENDING(rIStm))
+ {
+ sal_uInt8 nAspect;
+ sal_uInt8 nRF;
+ SvMemoryStream aMemStm;
+
+ aMemStm.SetBuffer( pBuf, 7, 7 );
+ aMemStm.ReadUInt16( nGlobalWidth );
+ aMemStm.ReadUInt16( nGlobalHeight );
+ aMemStm.ReadUChar( nRF );
+ aMemStm.ReadUChar( nBackgroundColor );
+ aMemStm.ReadUChar( nAspect );
+
+ bGlobalPalette = ( nRF & 0x80 );
+
+ if( bGlobalPalette )
+ ReadPaletteEntries( &aGPalette, sal_uLong(1) << ( ( nRF & 7 ) + 1 ) );
+ else
+ nBackgroundColor = 0;
+
+ if( NO_PENDING( rIStm ) )
+ bRet = true;
+ }
+ }
+ else
+ bStatus = false;
+ }
+
+ return bRet;
+}
+
+void GIFReader::ReadPaletteEntries( BitmapPalette* pPal, sal_uLong nCount )
+{
+ sal_uLong nLen = 3 * nCount;
+ const sal_uInt64 nMaxPossible = rIStm.remainingSize();
+ if (nLen > nMaxPossible)
+ nLen = nMaxPossible;
+ std::unique_ptr<sal_uInt8[]> pBuf(new sal_uInt8[ nLen ]);
+ std::size_t nRead = rIStm.ReadBytes(pBuf.get(), nLen);
+ nCount = nRead/3UL;
+ if( !(NO_PENDING( rIStm )) )
+ return;
+
+ sal_uInt8* pTmp = pBuf.get();
+
+ for (sal_uLong i = 0; i < nCount; ++i)
+ {
+ BitmapColor& rColor = (*pPal)[i];
+
+ rColor.SetRed( *pTmp++ );
+ rColor.SetGreen( *pTmp++ );
+ rColor.SetBlue( *pTmp++ );
+ }
+
+ // if possible accommodate some standard colours
+ if( nCount < 256 )
+ {
+ (*pPal)[ 255UL ] = COL_WHITE;
+
+ if( nCount < 255 )
+ (*pPal)[ 254UL ] = COL_BLACK;
+ }
+}
+
+bool GIFReader::ReadExtension()
+{
+ bool bRet = false;
+
+ // Extension-Label
+ sal_uInt8 cFunction(0);
+ rIStm.ReadUChar( cFunction );
+ if( NO_PENDING( rIStm ) )
+ {
+ bool bOverreadDataBlocks = false;
+ sal_uInt8 cSize(0);
+ // Block length
+ rIStm.ReadUChar( cSize );
+ switch( cFunction )
+ {
+ // 'Graphic Control Extension'
+ case 0xf9 :
+ {
+ sal_uInt8 cFlags(0);
+ rIStm.ReadUChar(cFlags);
+ rIStm.ReadUInt16(nTimer);
+ rIStm.ReadUChar(nGCTransparentIndex);
+ sal_uInt8 cByte(0);
+ rIStm.ReadUChar(cByte);
+
+ if ( NO_PENDING( rIStm ) )
+ {
+ nGCDisposalMethod = ( cFlags >> 2) & 7;
+ bGCTransparent = ( cFlags & 1 );
+ bStatus = ( cSize == 4 ) && ( cByte == 0 );
+ bRet = true;
+ }
+ }
+ break;
+
+ // Application extension
+ case 0xff :
+ {
+ if ( NO_PENDING( rIStm ) )
+ {
+ // by default overread this extension
+ bOverreadDataBlocks = true;
+
+ // Appl. extension has length 11
+ if ( cSize == 0x0b )
+ {
+ OString aAppId = read_uInt8s_ToOString(rIStm, 8);
+ OString aAppCode = read_uInt8s_ToOString(rIStm, 3);
+ rIStm.ReadUChar( cSize );
+
+ // NetScape-Extension
+ if( aAppId == "NETSCAPE" && aAppCode == "2.0" && cSize == 3 )
+ {
+ sal_uInt8 cByte(0);
+ rIStm.ReadUChar( cByte );
+
+ // Loop-Extension
+ if ( cByte == 0x01 )
+ {
+ rIStm.ReadUChar( cByte );
+ nLoops = cByte;
+ rIStm.ReadUChar( cByte );
+ nLoops |= ( static_cast<sal_uInt16>(cByte) << 8 );
+ rIStm.ReadUChar( cByte );
+
+ bStatus = ( cByte == 0 );
+ bRet = NO_PENDING( rIStm );
+ bOverreadDataBlocks = false;
+
+ // Netscape interprets the loop count
+ // as pure number of _repeats_;
+ // here it is the total number of loops
+ if( nLoops )
+ nLoops++;
+ }
+ else
+ rIStm.SeekRel( -1 );
+ }
+ else if ( aAppId == "STARDIV " && aAppCode == "5.0" && cSize == 9 )
+ {
+ sal_uInt8 cByte(0);
+ rIStm.ReadUChar( cByte );
+
+ // Loop extension
+ if ( cByte == 0x01 )
+ {
+ rIStm.ReadUInt32( nLogWidth100 ).ReadUInt32( nLogHeight100 );
+ rIStm.ReadUChar( cByte );
+ bStatus = ( cByte == 0 );
+ bRet = NO_PENDING( rIStm );
+ bOverreadDataBlocks = false;
+ }
+ else
+ rIStm.SeekRel( -1 );
+ }
+
+ }
+ }
+ }
+ break;
+
+ // overread everything else
+ default:
+ bOverreadDataBlocks = true;
+ break;
+ }
+
+ // overread sub-blocks
+ if ( bOverreadDataBlocks )
+ {
+ bRet = true;
+ while( cSize && bStatus && !rIStm.eof() )
+ {
+ sal_uInt16 nCount = static_cast<sal_uInt16>(cSize) + 1;
+ const sal_uInt64 nMaxPossible = rIStm.remainingSize();
+ if (nCount > nMaxPossible)
+ nCount = nMaxPossible;
+
+ if (nCount)
+ rIStm.SeekRel( nCount - 1 ); // Skip subblock data
+
+ bRet = false;
+ std::size_t nRead = rIStm.ReadBytes(&cSize, 1);
+ if (NO_PENDING(rIStm) && nRead == 1)
+ {
+ bRet = true;
+ }
+ else
+ cSize = 0;
+ }
+ }
+ }
+
+ return bRet;
+}
+
+bool GIFReader::ReadLocalHeader()
+{
+ sal_uInt8 pBuf[ 9 ];
+ bool bRet = false;
+
+ std::size_t nRead = rIStm.ReadBytes(pBuf, 9);
+ if (NO_PENDING(rIStm) && nRead == 9)
+ {
+ SvMemoryStream aMemStm;
+ BitmapPalette* pPal;
+
+ aMemStm.SetBuffer( pBuf, 9, 9 );
+ aMemStm.ReadUInt16( nImagePosX );
+ aMemStm.ReadUInt16( nImagePosY );
+ aMemStm.ReadUInt16( nImageWidth );
+ aMemStm.ReadUInt16( nImageHeight );
+ sal_uInt8 nFlags(0);
+ aMemStm.ReadUChar(nFlags);
+
+ // if interlaced, first define startvalue
+ bInterlaced = ( ( nFlags & 0x40 ) == 0x40 );
+ nLastInterCount = 7;
+ nLastImageY = 0;
+
+ if( nFlags & 0x80 )
+ {
+ pPal = &aLPalette;
+ ReadPaletteEntries( pPal, sal_uLong(1) << ( (nFlags & 7 ) + 1 ) );
+ }
+ else
+ pPal = &aGPalette;
+
+ // if we could read everything, we will create the local image;
+ // if the global colour table is valid for the image, we will
+ // consider the BackGroundColorIndex.
+ if( NO_PENDING( rIStm ) )
+ {
+ CreateBitmaps( nImageWidth, nImageHeight, pPal, bGlobalPalette && ( pPal == &aGPalette ) );
+ bRet = true;
+ }
+ }
+
+ return bRet;
+}
+
+sal_uLong GIFReader::ReadNextBlock()
+{
+ sal_uLong nRet = 0;
+ sal_uInt8 cBlockSize;
+
+ rIStm.ReadUChar( cBlockSize );
+
+ if ( rIStm.eof() )
+ nRet = 4;
+ else if ( NO_PENDING( rIStm ) )
+ {
+ if ( cBlockSize == 0 )
+ nRet = 2;
+ else
+ {
+ rIStm.ReadBytes( aSrcBuf.data(), cBlockSize );
+
+ if( NO_PENDING( rIStm ) )
+ {
+ if( bOverreadBlock )
+ nRet = 3;
+ else
+ {
+ bool bEOI;
+ sal_uLong nRead;
+ sal_uInt8* pTarget = pDecomp->DecompressBlock( aSrcBuf.data(), cBlockSize, nRead, bEOI );
+
+ nRet = ( bEOI ? 3 : 1 );
+
+ if( nRead && !bOverreadBlock )
+ FillImages( pTarget, nRead );
+
+ std::free( pTarget );
+ }
+ }
+ }
+ }
+
+ return nRet;
+}
+
+void GIFReader::FillImages( const sal_uInt8* pBytes, sal_uLong nCount )
+{
+ for( sal_uLong i = 0; i < nCount; i++ )
+ {
+ if( nImageX >= nImageWidth )
+ {
+ if( bInterlaced )
+ {
+ tools::Long nT1;
+
+ // lines will be copied if interlaced
+ if( nLastInterCount )
+ {
+ tools::Long nMinY = std::min( static_cast<tools::Long>(nLastImageY) + 1, static_cast<tools::Long>(nImageHeight) - 1 );
+ tools::Long nMaxY = std::min( static_cast<tools::Long>(nLastImageY) + nLastInterCount, static_cast<tools::Long>(nImageHeight) - 1 );
+
+ // copy last line read, if lines do not coincide
+ // ( happens at the end of the image )
+ if( ( nMinY > nLastImageY ) && ( nLastImageY < ( nImageHeight - 1 ) ) )
+ {
+ sal_uInt8* pScanline8 = pAcc8->GetScanline( nYAcc );
+ sal_uInt32 nSize8 = pAcc8->GetScanlineSize();
+ sal_uInt8* pScanline1 = nullptr;
+ sal_uInt32 nSize1 = 0;
+
+ if( bGCTransparent )
+ {
+ pScanline1 = pAcc1->GetScanline( nYAcc );
+ nSize1 = pAcc1->GetScanlineSize();
+ }
+
+ for( tools::Long j = nMinY; j <= nMaxY; j++ )
+ {
+ memcpy( pAcc8->GetScanline( j ), pScanline8, nSize8 );
+
+ if( bGCTransparent )
+ memcpy( pAcc1->GetScanline( j ), pScanline1, nSize1 );
+ }
+ }
+ }
+
+ nT1 = ( ++nImageY ) << 3;
+ nLastInterCount = 7;
+
+ if( nT1 >= nImageHeight )
+ {
+ tools::Long nT2 = nImageY - ( ( nImageHeight + 7 ) >> 3 );
+ nT1 = ( nT2 << 3 ) + 4;
+ nLastInterCount = 3;
+
+ if( nT1 >= nImageHeight )
+ {
+ nT2 -= ( nImageHeight + 3 ) >> 3;
+ nT1 = ( nT2 << 2 ) + 2;
+ nLastInterCount = 1;
+
+ if( nT1 >= nImageHeight )
+ {
+ nT2 -= ( nImageHeight + 1 ) >> 2;
+ nT1 = ( nT2 << 1 ) + 1;
+ nLastInterCount = 0;
+ }
+ }
+ }
+
+ nLastImageY = static_cast<sal_uInt16>(nT1);
+ nYAcc = nT1;
+ }
+ else
+ {
+ nLastImageY = ++nImageY;
+ nYAcc = nImageY;
+ }
+
+ // line starts from the beginning
+ nImageX = 0;
+ }
+
+ if( nImageY < nImageHeight )
+ {
+ const sal_uInt8 cTmp = pBytes[ i ];
+
+ if( bGCTransparent )
+ {
+ if( cTmp == nGCTransparentIndex )
+ pAcc1->SetPixelIndex( nYAcc, nImageX++, cTransIndex1 );
+ else
+ {
+ pAcc8->SetPixelIndex( nYAcc, nImageX, cTmp );
+ pAcc1->SetPixelIndex( nYAcc, nImageX++, cNonTransIndex1 );
+ }
+ }
+ else
+ pAcc8->SetPixelIndex( nYAcc, nImageX++, cTmp );
+ }
+ else
+ {
+ bOverreadBlock = true;
+ break;
+ }
+ }
+}
+
+void GIFReader::CreateNewBitmaps()
+{
+ AnimationBitmap aAnimationBitmap;
+
+ pAcc8.reset();
+
+ if( bGCTransparent )
+ {
+ pAcc1.reset();
+ aAnimationBitmap.maBitmapEx = BitmapEx( aBmp8, aBmp1 );
+ }
+ else
+ aAnimationBitmap.maBitmapEx = BitmapEx( aBmp8 );
+
+ aAnimationBitmap.maPositionPixel = Point( nImagePosX, nImagePosY );
+ aAnimationBitmap.maSizePixel = Size( nImageWidth, nImageHeight );
+ aAnimationBitmap.mnWait = ( nTimer != 65535 ) ? nTimer : ANIMATION_TIMEOUT_ON_CLICK;
+ aAnimationBitmap.mbUserInput = false;
+
+ // tdf#104121 . Internet Explorer, Firefox, Chrome and Safari all set a minimum default playback speed.
+ // IE10 Consumer Preview sets default of 100ms for rates less that 20ms. We do the same
+ if (aAnimationBitmap.mnWait < 2) // 20ms, specified in 100's of a second
+ aAnimationBitmap.mnWait = 10;
+
+ if( nGCDisposalMethod == 2 )
+ aAnimationBitmap.meDisposal = Disposal::Back;
+ else if( nGCDisposalMethod == 3 )
+ aAnimationBitmap.meDisposal = Disposal::Previous;
+ else
+ aAnimationBitmap.meDisposal = Disposal::Not;
+
+ nAnimationByteSize += aAnimationBitmap.maBitmapEx.GetSizeBytes();
+ nAnimationMinFileData += static_cast<sal_uInt64>(nImageWidth) * nImageHeight / 2560;
+ aAnimation.Insert(aAnimationBitmap);
+
+ if( aAnimation.Count() == 1 )
+ {
+ aAnimation.SetDisplaySizePixel( Size( nGlobalWidth, nGlobalHeight ) );
+ aAnimation.SetLoopCount( nLoops );
+ }
+}
+
+Graphic GIFReader::GetIntermediateGraphic()
+{
+ Graphic aImGraphic;
+
+ // only create intermediate graphic, if data is available
+ // but graphic still not completely read
+ if ( bImGraphicReady && !aAnimation.Count() )
+ {
+ pAcc8.reset();
+
+ if ( bGCTransparent )
+ {
+ pAcc1.reset();
+ aImGraphic = BitmapEx( aBmp8, aBmp1 );
+
+ pAcc1 = BitmapScopedWriteAccess(aBmp1);
+ bStatus = bStatus && pAcc1;
+ }
+ else
+ aImGraphic = BitmapEx(aBmp8);
+
+ pAcc8 = BitmapScopedWriteAccess(aBmp8);
+ bStatus = bStatus && pAcc8;
+ }
+
+ return aImGraphic;
+}
+
+bool GIFReader::ProcessGIF()
+{
+ bool bRead = false;
+ bool bEnd = false;
+
+ if ( !bStatus )
+ eActAction = ABORT_READING;
+
+ // set stream to right position
+ rIStm.Seek( nLastPos );
+
+ switch( eActAction )
+ {
+ // read next marker
+ case MARKER_READING:
+ {
+ sal_uInt8 cByte;
+
+ rIStm.ReadUChar( cByte );
+
+ if( rIStm.eof() )
+ eActAction = END_READING;
+ else if( NO_PENDING( rIStm ) )
+ {
+ bRead = true;
+
+ if( cByte == '!' )
+ eActAction = EXTENSION_READING;
+ else if( cByte == ',' )
+ eActAction = LOCAL_HEADER_READING;
+ else if( cByte == ';' )
+ eActAction = END_READING;
+ else
+ eActAction = ABORT_READING;
+ }
+ }
+ break;
+
+ // read ScreenDescriptor
+ case GLOBAL_HEADER_READING:
+ {
+ bRead = ReadGlobalHeader();
+ if( bRead )
+ {
+ ClearImageExtensions();
+ eActAction = MARKER_READING;
+ }
+ }
+ break;
+
+ // read extension
+ case EXTENSION_READING:
+ {
+ bRead = ReadExtension();
+ if( bRead )
+ eActAction = MARKER_READING;
+ }
+ break;
+
+ // read Image-Descriptor
+ case LOCAL_HEADER_READING:
+ {
+ bRead = ReadLocalHeader();
+ if( bRead )
+ {
+ nYAcc = nImageX = nImageY = 0;
+ eActAction = FIRST_BLOCK_READING;
+ }
+ }
+ break;
+
+ // read first data block
+ case FIRST_BLOCK_READING:
+ {
+ sal_uInt8 cDataSize;
+
+ rIStm.ReadUChar( cDataSize );
+
+ if( rIStm.eof() )
+ eActAction = ABORT_READING;
+ else if( cDataSize > 12 )
+ bStatus = false;
+ else if( NO_PENDING( rIStm ) )
+ {
+ bRead = true;
+ pDecomp = std::make_unique<GIFLZWDecompressor>( cDataSize );
+ eActAction = NEXT_BLOCK_READING;
+ bOverreadBlock = false;
+ }
+ else
+ eActAction = FIRST_BLOCK_READING;
+ }
+ break;
+
+ // read next data block
+ case NEXT_BLOCK_READING:
+ {
+ sal_uInt16 nLastX = nImageX;
+ sal_uInt16 nLastY = nImageY;
+ sal_uLong nRet = ReadNextBlock();
+
+ // Return: 0:Pending / 1:OK; / 2:OK and last block: / 3:EOI / 4:HardAbort
+ if( nRet )
+ {
+ bRead = true;
+
+ if ( nRet == 1 )
+ {
+ bImGraphicReady = true;
+ eActAction = NEXT_BLOCK_READING;
+ bOverreadBlock = false;
+ }
+ else
+ {
+ if( nRet == 2 )
+ {
+ pDecomp.reset();
+ CreateNewBitmaps();
+ eActAction = MARKER_READING;
+ ClearImageExtensions();
+ }
+ else if( nRet == 3 )
+ {
+ eActAction = NEXT_BLOCK_READING;
+ bOverreadBlock = true;
+ }
+ else
+ {
+ pDecomp.reset();
+ CreateNewBitmaps();
+ eActAction = ABORT_READING;
+ ClearImageExtensions();
+ }
+ }
+ }
+ else
+ {
+ nImageX = nLastX;
+ nImageY = nLastY;
+ }
+ }
+ break;
+
+ // an error occurred
+ case ABORT_READING:
+ {
+ bEnd = true;
+ eActAction = END_READING;
+ }
+ break;
+
+ default:
+ break;
+ }
+
+ // set stream to right position,
+ // if data could be read put it at the old
+ // position otherwise at the actual one
+ if( bRead || bEnd )
+ nLastPos = rIStm.Tell();
+
+ return bRead;
+}
+
+bool GIFReader::ReadIsAnimated()
+{
+ ReadState eReadState;
+
+ bStatus = true;
+
+ while( ProcessGIF() && ( eActAction != END_READING ) ) {}
+
+ if( !bStatus )
+ eReadState = GIFREAD_ERROR;
+ else if( eActAction == END_READING )
+ eReadState = GIFREAD_OK;
+ else
+ {
+ if ( rIStm.GetError() == ERRCODE_IO_PENDING )
+ rIStm.ResetError();
+
+ eReadState = GIFREAD_NEED_MORE;
+ }
+
+ if (eReadState == GIFREAD_OK)
+ return aAnimation.Count() > 1;
+ return false;
+}
+
+void GIFReader::GetLogicSize(Size& rLogicSize)
+{
+ rLogicSize.setWidth(nLogWidth100);
+ rLogicSize.setHeight(nLogHeight100);
+}
+
+ReadState GIFReader::ReadGIF( Graphic& rGraphic )
+{
+ ReadState eReadState;
+
+ bStatus = true;
+
+ while( ProcessGIF() && ( eActAction != END_READING ) ) {}
+
+ if( !bStatus )
+ eReadState = GIFREAD_ERROR;
+ else if( eActAction == END_READING )
+ eReadState = GIFREAD_OK;
+ else
+ {
+ if ( rIStm.GetError() == ERRCODE_IO_PENDING )
+ rIStm.ResetError();
+
+ eReadState = GIFREAD_NEED_MORE;
+ }
+
+ if( aAnimation.Count() == 1 )
+ {
+ rGraphic = aAnimation.Get(0).maBitmapEx;
+
+ if( nLogWidth100 && nLogHeight100 )
+ {
+ rGraphic.SetPrefSize( Size( nLogWidth100, nLogHeight100 ) );
+ rGraphic.SetPrefMapMode(MapMode(MapUnit::Map100thMM));
+ }
+ }
+ else
+ rGraphic = aAnimation;
+
+ return eReadState;
+}
+
+bool IsGIFAnimated(SvStream & rStm, Size& rLogicSize)
+{
+ GIFReader aReader(rStm);
+
+ SvStreamEndian nOldFormat = rStm.GetEndian();
+ rStm.SetEndian(SvStreamEndian::LITTLE);
+ bool bResult = aReader.ReadIsAnimated();
+ aReader.GetLogicSize(rLogicSize);
+ rStm.SetEndian(nOldFormat);
+
+ return bResult;
+}
+
+VCL_DLLPUBLIC bool ImportGIF( SvStream & rStm, Graphic& rGraphic )
+{
+ std::shared_ptr<GraphicReader> pContext = rGraphic.GetReaderContext();
+ rGraphic.SetReaderContext(nullptr);
+ GIFReader* pGIFReader = dynamic_cast<GIFReader*>( pContext.get() );
+ if (!pGIFReader)
+ {
+ pContext = std::make_shared<GIFReader>( rStm );
+ pGIFReader = static_cast<GIFReader*>( pContext.get() );
+ }
+
+ SvStreamEndian nOldFormat = rStm.GetEndian();
+ rStm.SetEndian( SvStreamEndian::LITTLE );
+
+ bool bRet = true;
+
+ ReadState eReadState = pGIFReader->ReadGIF(rGraphic);
+
+ if (eReadState == GIFREAD_ERROR)
+ {
+ bRet = false;
+ }
+ else if (eReadState == GIFREAD_NEED_MORE)
+ {
+ rGraphic = pGIFReader->GetIntermediateGraphic();
+ rGraphic.SetReaderContext(pContext);
+ }
+
+ rStm.SetEndian(nOldFormat);
+
+ return bRet;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/vcl/source/filter/igif/gifread.hxx b/vcl/source/filter/igif/gifread.hxx
new file mode 100644
index 000000000..de9a506f2
--- /dev/null
+++ b/vcl/source/filter/igif/gifread.hxx
@@ -0,0 +1,30 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#ifndef INCLUDED_VCL_SOURCE_FILTER_IGIF_GIFREAD_HXX
+#define INCLUDED_VCL_SOURCE_FILTER_IGIF_GIFREAD_HXX
+
+#include <vcl/graph.hxx>
+
+VCL_DLLPUBLIC bool ImportGIF(SvStream& rStream, Graphic& rGraphic);
+bool IsGIFAnimated(SvStream& rStream, Size& rLogicSize);
+
+#endif // INCLUDED_VCL_SOURCE_FILTER_IGIF_GIFREAD_HXX
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/vcl/source/filter/imet/ios2met.cxx b/vcl/source/filter/imet/ios2met.cxx
new file mode 100644
index 000000000..fe856c1d7
--- /dev/null
+++ b/vcl/source/filter/imet/ios2met.cxx
@@ -0,0 +1,2883 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <osl/thread.h>
+#include <o3tl/safeint.hxx>
+#include <tools/poly.hxx>
+#include <tools/fract.hxx>
+#include <tools/stream.hxx>
+#include <sal/log.hxx>
+#include <vcl/graph.hxx>
+#include <vcl/dibtools.hxx>
+#include <vcl/virdev.hxx>
+#include <vcl/lineinfo.hxx>
+#include <vcl/gdimtf.hxx>
+#include <filter/MetReader.hxx>
+#include <basegfx/numeric/ftools.hxx>
+
+#include <cmath>
+#include <memory>
+
+class FilterConfigItem;
+
+namespace {
+
+enum PenStyle { PEN_NULL, PEN_SOLID, PEN_DOT, PEN_DASH, PEN_DASHDOT };
+
+}
+
+// -----------------------------Field Types-------------------------------
+
+#define BegDocumnMagic 0xA8A8 /* Begin Document */
+#define EndDocumnMagic 0xA8A9 /* End Document */
+
+#define BegResGrpMagic 0xC6A8 /* Begin Resource Group */
+#define EndResGrpMagic 0xC6A9 /* End Resource Group */
+
+#define BegColAtrMagic 0x77A8 /* Begin Color Attribute Table */
+#define EndColAtrMagic 0x77A9 /* End Color Attribute Table */
+#define BlkColAtrMagic 0x77B0 /* Color Attribute Table */
+#define MapColAtrMagic 0x77AB /* Map Color Attribute Table */
+
+#define BegImgObjMagic 0xFBA8 /* Begin Image Object */
+#define EndImgObjMagic 0xFBA9 /* End Image Object */
+#define DscImgObjMagic 0xFBA6 /* Image Data Descriptor */
+#define DatImgObjMagic 0xFBEE /* Image Picture Data */
+
+#define BegObEnv1Magic 0xC7A8 /* Begin Object Environment Group */
+#define EndObEnv1Magic 0xC7A9 /* End Object Environment Group */
+
+#define BegGrfObjMagic 0xBBA8 /* Begin Graphics Object */
+#define EndGrfObjMagic 0xBBA9 /* End Graphics Object */
+#define DscGrfObjMagic 0xBBA6 /* Graphics Data Descriptor */
+#define DatGrfObjMagic 0xBBEE /* Graphics Data */
+
+#define MapCodFntMagic 0x8AAB /* Map Coded Font */
+#define MapDatResMagic 0xC3AB /* Map Data Resource */
+
+// -----------------------------Order Types-------------------------------
+
+#define GOrdGivArc 0xC6 /* 1 Arc at given position */
+#define GOrdCurArc 0x86 /* 1 Arc at current position */
+#define GOrdGivBzr 0xE5 /* 1 Beziercurve at given position */
+#define GOrdCurBzr 0xA5 /* 1 Beziercurve at current position */
+#define GOrdGivBox 0xC0 /* 1 Box at given position */
+#define GOrdCurBox 0x80 /* 1 Box at current position */
+#define GOrdGivFil 0xC5 /* 1 Fillet at given position */
+#define GOrdCurFil 0x85 /* 1 Fillet at current position */
+#define GOrdGivCrc 0xC7 /* 1 Full arc (circle) at given position */
+#define GOrdCurCrc 0x87 /* 1 Full arc (circle) at current position */
+#define GOrdGivLin 0xC1 /* 1 Line at given position */
+#define GOrdCurLin 0x81 /* 1 Line at current position */
+#define GOrdGivMrk 0xC2 /* 1 Marker at given position */
+#define GOrdCurMrk 0x82 /* 1 Marker at current position */
+#define GOrdGivArP 0xE3 /* 1 Partial arc at given position */
+#define GOrdCurArP 0xA3 /* 1 Partial arc at current position */
+#define GOrdGivRLn 0xE1 /* 1 Relative line at given position */
+#define GOrdCurRLn 0xA1 /* 1 Relative line at current position */
+#define GOrdGivSFl 0xE4 /* 1 Sharp fillet at given position */
+#define GOrdCurSFl 0xA4 /* 1 Sharp fillet at current position */
+
+#define GOrdGivStM 0xF1 /* 1 Character string move at given position */
+#define GOrdCurStM 0xB1 /* 1 Character string move at current position */
+#define GOrdGivStr 0xC3 /* 1 Character string at given position */
+#define GOrdCurStr 0x83 /* 1 Character string at current position */
+#define GOrdGivStx 0xFEF0 /* 2 Character string extended at given position */
+#define GOrdCurStx 0xFEB0 /* 2 Character string extended at current position */
+
+#define GOrdGivImg 0xD1 /* 1 Begin Image at given position */
+#define GOrdCurImg 0x91 /* 1 Begin Image at current position */
+#define GOrdImgDat 0x92 /* 1 Image data */
+#define GOrdEndImg 0x93 /* 1 End Image */
+#define GOrdBegAra 0x68 /* 0 1 Begin area */
+#define GOrdEndAra 0x60 /* 1 End area */
+#define GOrdBegElm 0xD2 /* 1 Begin element */
+#define GOrdEndElm 0x49 /* 0 1 End element */
+
+#define GOrdBegPth 0xD0 /* 1 Begin path */
+#define GOrdEndPth 0x7F /* 0 1 End path */
+#define GOrdFilPth 0xD7 /* 1 Fill path */
+#define GOrdModPth 0xD8 /* 1 Modify path */
+#define GOrdOutPth 0xD4 /* 1 Outline path */
+#define GOrdSClPth 0xB4 /* 1 Set clip path */
+
+#define GOrdNopNop 0x00 /* 0 0 No operation */
+#define GOrdRemark 0x01 /* 1 Comment */
+#define GOrdSegLab 0xD3 /* 1 Label */
+#define GOrdBitBlt 0xD6 /* 1 Bitblt */
+#define GOrdCalSeg 0x07 /* 1 Call Segment */
+#define GOrdSSgBnd 0x32 /* 1 Set segment boundary */
+#define GOrdSegChr 0x04 /* 1 Segment characteristics */
+#define GOrdCloFig 0x7D /* 0 1 Close Figure */
+#define GOrdEndSym 0xFF /* 0 0 End of symbol definition */
+#define GOrdEndPlg 0x3E /* 0 1 End prolog */
+#define GOrdEscape 0xD5 /* 1 Escape */
+#define GOrdExtEsc 0xFED5 /* 2 Extended Escape */
+#define GOrdPolygn 0xF3 /* 2 Polygons */
+
+#define GOrdStkPop 0x3F /* 0 1 Pop */
+
+#define GOrdSIvAtr 0x14 /* 1 Set individual attribute */
+#define GOrdPIvAtr 0x54 /* 1 Push and set individual attribute */
+#define GOrdSColor 0x0A /* 0 1 Set color */
+#define GOrdPColor 0x4A /* 0 1 Push and set color */
+#define GOrdSIxCol 0xA6 /* 1 Set indexed color */
+#define GOrdPIxCol 0xE6 /* 1 Push and set indexed color */
+#define GOrdSXtCol 0x26 /* 1 Set extended color */
+#define GOrdPXtCol 0x66 /* 1 Push and set extended color */
+#define GOrdSBgCol 0x25 /* 1 Set background color */
+#define GOrdPBgCol 0x65 /* 1 Push and set background color */
+#define GOrdSBxCol 0xA7 /* 1 Set background indexed color */
+#define GOrdPBxCol 0xE7 /* 1 Push and set background indexed color */
+#define GOrdSMixMd 0x0C /* 0 1 Set mix */
+#define GOrdPMixMd 0x4C /* 0 1 Push and set mix */
+#define GOrdSBgMix 0x0D /* 0 1 Set background mix */
+#define GOrdPBgMix 0x4D /* 0 1 Push and set background mix */
+
+#define GOrdSPtSet 0x08 /* 0 1 Set pattern set */
+#define GOrdPPtSet 0x48 /* 0 1 Push and set pattern set */
+#define GOrdSPtSym 0x28 /* 0 1 Set pattern symbol */
+#define GOrdPPtSym 0x09 /* 0 1 Push and set pattern symbol */
+#define GOrdSPtRef 0xA0 /* 1 Set model pattern reference */
+#define GOrdPPtRef 0xE0 /* 1 Push and set pattern reference point */
+
+#define GOrdSLnEnd 0x1A /* 0 1 Set line end */
+#define GOrdPLnEnd 0x5A /* 0 1 Push and set line end */
+#define GOrdSLnJoi 0x1B /* 0 1 Set line join */
+#define GOrdPLnJoi 0x5B /* 0 1 Push and set line join */
+#define GOrdSLnTyp 0x18 /* 0 1 Set line type */
+#define GOrdPLnTyp 0x58 /* 0 1 Push and set line type */
+#define GOrdSLnWdt 0x19 /* 0 1 Set line width */
+#define GOrdPLnWdt 0x59 /* 0 1 Push and set line width */
+#define GOrdSFrLWd 0x11 /* 1 Set fractional line width */
+#define GOrdPFrLWd 0x51 /* 1 Push and set fractional line width */
+#define GOrdSStLWd 0x15 /* 1 Set stroke line width */
+#define GOrdPStLWd 0x55 /* 1 Push and set stroke line width */
+
+#define GOrdSChDir 0x3A /* 0 1 Set character direction */
+#define GOrdPChDir 0x7A /* 0 1 Push and set character direction */
+#define GOrdSChPrc 0x39 /* 0 1 Set character precision */
+#define GOrdPChPrc 0x79 /* 0 1 Push and set character precision */
+#define GOrdSChSet 0x38 /* 0 1 Set character set */
+#define GOrdPChSet 0x78 /* 0 1 Push and set character set */
+#define GOrdSChAng 0x34 /* 1 Set character angle */
+#define GOrdPChAng 0x74 /* 1 Push and set character angle */
+#define GOrdSChBrx 0x05 /* 1 Set character break extra */
+#define GOrdPChBrx 0x45 /* 1 Push and set character break extra */
+#define GOrdSChCel 0x33 /* 1 Set character cell */
+#define GOrdPChCel 0x03 /* 1 Push and set character cell */
+#define GOrdSChXtr 0x17 /* 1 Set character extra */
+#define GOrdPChXtr 0x57 /* 1 Push and set character extra */
+#define GOrdSChShr 0x35 /* 1 Set character shear */
+#define GOrdPChShr 0x75 /* 1 Push and set character shear */
+#define GOrdSTxAlg 0x36 /* 0 2 Set text allingment */
+#define GOrdPTxAlg 0x76 /* 0 2 Push and set text allingment */
+
+#define GOrdSMkPrc 0x3B /* 0 1 Set marker precision */
+#define GOrdPMkPrc 0x7B /* 0 1 Push and set marker precision */
+#define GOrdSMkSet 0x3C /* 0 1 Set marker set */
+#define GOrdPMkSet 0x7C /* 0 1 Push and set marker set */
+#define GOrdSMkSym 0x29 /* 0 1 Set marker symbol */
+#define GOrdPMkSym 0x69 /* 0 1 Push and set marker symbol */
+#define GOrdSMkCel 0x37 /* 1 Set marker cell */
+#define GOrdPMkCel 0x77 /* 1 Push and set marker cell */
+
+#define GOrdSArcPa 0x22 /* 1 Set arc parameters */
+#define GOrdPArcPa 0x62 /* 1 Push and set arc parameters */
+
+#define GOrdSCrPos 0x21 /* 1 Set current position */
+#define GOrdPCrPos 0x61 /* 1 Push and set current position */
+
+#define GOrdSMdTrn 0x24 /* 1 Set model transform */
+#define GOrdPMdTrn 0x64 /* 1 Push and set model transform */
+#define GOrdSPkIdn 0x43 /* 1 Set pick identifier */
+#define GOrdPPkIdn 0x23 /* 1 Push and set pick identifier */
+#define GOrdSVwTrn 0x31 /* 1 Set viewing transform */
+#define GOrdSVwWin 0x27 /* 1 Set viewing window */
+#define GOrdPVwWin 0x67 /* 1 Push and set viewing window */
+
+//============================ OS2METReader ==================================
+
+namespace {
+
+struct OSPalette {
+ OSPalette * pSucc;
+ sal_uInt32 * p0RGB; // May be NULL!
+ size_t nSize;
+};
+
+struct OSArea {
+ OSArea * pSucc;
+ sal_uInt8 nFlags;
+ tools::PolyPolygon aPPoly;
+ bool bClosed;
+ Color aCol;
+ Color aBgCol;
+ RasterOp eMix;
+ RasterOp eBgMix;
+ bool bFill;
+
+ OSArea()
+ : pSucc(nullptr)
+ , nFlags(0)
+ , bClosed(false)
+ , eMix(RasterOp::OverPaint)
+ , eBgMix(RasterOp::OverPaint)
+ , bFill(false)
+ {
+ }
+};
+
+struct OSPath
+{
+ OSPath* pSucc;
+ sal_uInt32 nID;
+ tools::PolyPolygon aPPoly;
+ bool bClosed;
+ bool bStroke;
+
+ OSPath()
+ : pSucc(nullptr)
+ , nID(0)
+ , bClosed(false)
+ , bStroke(false)
+ {
+ }
+};
+
+struct OSFont {
+ OSFont * pSucc;
+ sal_uInt32 nID;
+ vcl::Font aFont;
+
+ OSFont()
+ : pSucc(nullptr)
+ , nID(0)
+ {
+ }
+};
+
+struct OSBitmap {
+ OSBitmap * pSucc;
+ sal_uInt32 nID;
+ BitmapEx aBitmapEx;
+
+ // required during reading of the bitmap:
+ SvStream * pBMP; // pointer to temporary Windows-BMP file or NULL
+ sal_uInt32 nWidth, nHeight;
+ sal_uInt16 nBitsPerPixel;
+ sal_uInt32 nMapPos;
+};
+
+struct OSAttr
+{
+ OSAttr * pSucc;
+ sal_uInt16 nPushOrder;
+ sal_uInt8 nIvAttrA, nIvAttrP; // special variables for the Order "GOrdPIvAtr"
+
+ Color aLinCol;
+ Color aLinBgCol;
+ RasterOp eLinMix;
+ RasterOp eLinBgMix;
+ Color aChrCol;
+ Color aChrBgCol;
+ RasterOp eChrMix;
+ RasterOp eChrBgMix;
+ Color aMrkCol;
+ Color aMrkBgCol;
+ RasterOp eMrkMix;
+ RasterOp eMrkBgMix;
+ Color aPatCol;
+ Color aPatBgCol;
+ RasterOp ePatMix;
+ RasterOp ePatBgMix;
+ Color aImgCol;
+ Color aImgBgCol;
+ RasterOp eImgMix;
+ RasterOp eImgBgMix;
+ sal_Int32 nArcP, nArcQ, nArcR, nArcS;
+ Degree10 nChrAng;
+ sal_Int32 nChrCellHeight;
+ sal_uInt32 nChrSet;
+ Point aCurPos;
+ PenStyle eLinStyle;
+ sal_uInt16 nLinWidth;
+ Size aMrkCellSize;
+ sal_uInt8 nMrkPrec;
+ sal_uInt8 nMrkSet;
+ sal_uInt8 nMrkSymbol;
+ bool bFill;
+ sal_uInt16 nStrLinWidth;
+
+ OSAttr()
+ : pSucc(nullptr)
+ , nPushOrder(0)
+ , nIvAttrA(0)
+ , nIvAttrP(0)
+ , eLinMix(RasterOp::OverPaint)
+ , eLinBgMix(RasterOp::OverPaint)
+ , eChrMix(RasterOp::OverPaint)
+ , eChrBgMix(RasterOp::OverPaint)
+ , eMrkMix(RasterOp::OverPaint)
+ , eMrkBgMix(RasterOp::OverPaint)
+ , ePatMix(RasterOp::OverPaint)
+ , ePatBgMix(RasterOp::OverPaint)
+ , eImgMix(RasterOp::OverPaint)
+ , eImgBgMix(RasterOp::OverPaint)
+ , nArcP(0)
+ , nArcQ(0)
+ , nArcR(0)
+ , nArcS(0)
+ , nChrAng(0)
+ , nChrCellHeight(0)
+ , nChrSet(0)
+ , eLinStyle(PEN_NULL)
+ , nLinWidth(0)
+ , nMrkPrec(0)
+ , nMrkSet(0)
+ , nMrkSymbol(0)
+ , bFill(false)
+ , nStrLinWidth(0)
+ {
+ }
+};
+
+class OS2METReader {
+
+private:
+
+ int ErrorCode;
+
+ SvStream * pOS2MET; // the OS2MET file to be read
+ VclPtr<VirtualDevice> pVirDev; // here the drawing methods are being called
+ // While doing this a recording in the GDIMetaFile
+ // will take place.
+ tools::Rectangle aBoundingRect; // bounding rectangle as stored in the file
+ tools::Rectangle aCalcBndRect; // bounding rectangle calculated on our own
+ MapMode aGlobMapMode; // resolution of the picture
+ bool bCoord32;
+
+ OSPalette * pPaletteStack;
+
+ LineInfo aLineInfo;
+
+ OSArea * pAreaStack; // Areas that are being worked on
+
+ OSPath * pPathStack; // Paths that are being worked on
+ OSPath * pPathList; // finished Paths
+
+ OSFont * pFontList;
+
+ OSBitmap * pBitmapList;
+
+ OSAttr aDefAttr;
+ OSAttr aAttr;
+ OSAttr * pAttrStack;
+
+ std::unique_ptr<SvMemoryStream> xOrdFile;
+
+ void AddPointsToPath(const tools::Polygon & rPoly);
+ void AddPointsToArea(const tools::Polygon & rPoly);
+ void CloseFigure();
+ void PushAttr(sal_uInt16 nPushOrder);
+ void PopAttr();
+
+ void ChangeBrush( const Color& rPatColor, bool bFill );
+ void SetPen( const Color& rColor, sal_uInt16 nStrLinWidth = 0, PenStyle ePenStyle = PEN_SOLID );
+ void SetRasterOp(RasterOp eROP);
+
+ void SetPalette0RGB(sal_uInt16 nIndex, sal_uInt32 nCol);
+ sal_uInt32 GetPalette0RGB(sal_uInt32 nIndex) const;
+ // gets color from palette, or, if it doesn't exist,
+ // interprets nIndex as immediate RGB value.
+ Color GetPaletteColor(sal_uInt32 nIndex) const;
+
+
+ bool IsLineInfo() const;
+ void DrawPolyLine( const tools::Polygon& rPolygon );
+ void DrawPolygon( const tools::Polygon& rPolygon );
+ void DrawPolyPolygon( const tools::PolyPolygon& rPolygon );
+ sal_uInt16 ReadBigEndianWord();
+ sal_uInt32 ReadBigEndian3BytesLong();
+ sal_uInt32 ReadLittleEndian3BytesLong();
+ sal_Int32 ReadCoord(bool b32);
+ Point ReadPoint( const bool bAdjustBoundRect = true );
+ static RasterOp OS2MixToRasterOp(sal_uInt8 nMix);
+ void ReadLine(bool bGivenPos, sal_uInt16 nOrderLen);
+ void ReadRelLine(bool bGivenPos, sal_uInt16 nOrderLen);
+ void ReadBox(bool bGivenPos);
+ void ReadBitBlt();
+ void ReadChrStr(bool bGivenPos, bool bMove, bool bExtra, sal_uInt16 nOrderLen);
+ void ReadArc(bool bGivenPos);
+ void ReadFullArc(bool bGivenPos, sal_uInt16 nOrderSize);
+ void ReadPartialArc(bool bGivenPos, sal_uInt16 nOrderSize);
+ void ReadPolygons();
+ void ReadBezier(bool bGivenPos, sal_uInt16 nOrderLen);
+ void ReadFillet(bool bGivenPos, sal_uInt16 nOrderLen);
+ void ReadFilletSharp(bool bGivenPos, sal_uInt16 nOrderLen);
+ void ReadMarker(bool bGivenPos, sal_uInt16 nOrderLen);
+ void ReadOrder(sal_uInt16 nOrderID, sal_uInt16 nOrderLen);
+ void ReadDsc(sal_uInt16 nDscID);
+ void ReadImageData(sal_uInt16 nDataID, sal_uInt16 nDataLen);
+ void ReadFont(sal_uInt16 nFieldSize);
+ void ReadField(sal_uInt16 nFieldType, sal_uInt16 nFieldSize);
+
+public:
+
+ OS2METReader();
+ ~OS2METReader();
+
+ void ReadOS2MET( SvStream & rStreamOS2MET, GDIMetaFile & rGDIMetaFile );
+ // Reads from the stream an OS2MET file and fills up the GDIMetaFile
+
+};
+
+}
+
+//=================== Methods of OS2METReader ==============================
+
+OS2METReader::OS2METReader()
+ : ErrorCode(0)
+ , pOS2MET(nullptr)
+ , pVirDev(VclPtr<VirtualDevice>::Create())
+ , bCoord32(false)
+ , pPaletteStack(nullptr)
+ , pAreaStack(nullptr)
+ , pPathStack(nullptr)
+ , pPathList(nullptr)
+ , pFontList(nullptr)
+ , pBitmapList(nullptr)
+ , pAttrStack(nullptr)
+{
+ pVirDev->EnableOutput(false);
+}
+
+OS2METReader::~OS2METReader()
+{
+ pVirDev.disposeAndClear();
+
+ while (pAreaStack!=nullptr) {
+ OSArea * p=pAreaStack;
+ pAreaStack=p->pSucc;
+ delete p;
+ }
+
+ while (pPathStack!=nullptr) {
+ OSPath * p=pPathStack;
+ pPathStack=p->pSucc;
+ delete p;
+ }
+
+ while (pPathList!=nullptr) {
+ OSPath * p=pPathList;
+ pPathList=p->pSucc;
+ delete p;
+ }
+
+ while (pFontList!=nullptr) {
+ OSFont * p=pFontList;
+ pFontList=p->pSucc;
+ delete p;
+ }
+
+ while (pBitmapList!=nullptr) {
+ OSBitmap * p=pBitmapList;
+ pBitmapList=p->pSucc;
+ delete p->pBMP;
+ delete p;
+ }
+
+ while (pAttrStack!=nullptr) {
+ OSAttr * p=pAttrStack;
+ pAttrStack=p->pSucc;
+ delete p;
+ }
+
+ while (pPaletteStack!=nullptr) {
+ OSPalette * p=pPaletteStack;
+ pPaletteStack=p->pSucc;
+ delete[] p->p0RGB;
+ delete p;
+ }
+}
+
+bool OS2METReader::IsLineInfo() const
+{
+ return ( ! ( aLineInfo.IsDefault() || ( aLineInfo.GetStyle() == LineStyle::NONE ) || ( pVirDev->GetLineColor() == COL_TRANSPARENT ) ) );
+}
+
+void OS2METReader::DrawPolyLine( const tools::Polygon& rPolygon )
+{
+ if ( aLineInfo.GetStyle() == LineStyle::Dash || ( aLineInfo.GetWidth() > 1 ) )
+ pVirDev->DrawPolyLine( rPolygon, aLineInfo );
+ else
+ pVirDev->DrawPolyLine( rPolygon );
+}
+
+void OS2METReader::DrawPolygon( const tools::Polygon& rPolygon )
+{
+ if ( IsLineInfo() )
+ {
+ pVirDev->Push( vcl::PushFlags::LINECOLOR );
+ pVirDev->SetLineColor( COL_TRANSPARENT );
+ pVirDev->DrawPolygon( rPolygon );
+ pVirDev->Pop();
+ pVirDev->DrawPolyLine( rPolygon, aLineInfo );
+ }
+ else
+ pVirDev->DrawPolygon( rPolygon );
+}
+
+void OS2METReader::DrawPolyPolygon( const tools::PolyPolygon& rPolyPolygon )
+{
+ if ( IsLineInfo() )
+ {
+ pVirDev->Push( vcl::PushFlags::LINECOLOR );
+ pVirDev->SetLineColor( COL_TRANSPARENT );
+ pVirDev->DrawPolyPolygon( rPolyPolygon );
+ pVirDev->Pop();
+ for ( sal_uInt16 i = 0; i < rPolyPolygon.Count(); i++ )
+ pVirDev->DrawPolyLine( rPolyPolygon.GetObject( i ), aLineInfo );
+ }
+ else
+ pVirDev->DrawPolyPolygon( rPolyPolygon );
+}
+
+void OS2METReader::AddPointsToArea(const tools::Polygon & rPoly)
+{
+ sal_uInt16 nOldSize, nNewSize,i;
+
+ if (pAreaStack==nullptr || rPoly.GetSize()==0) return;
+ tools::PolyPolygon * pPP=&(pAreaStack->aPPoly);
+ if (pPP->Count()==0 || pAreaStack->bClosed) pPP->Insert(rPoly);
+ else {
+ tools::Polygon aLastPoly(pPP->GetObject(pPP->Count()-1));
+ nOldSize=aLastPoly.GetSize();
+ if (nOldSize && aLastPoly.GetPoint(nOldSize-1)==rPoly.GetPoint(0)) nOldSize--;
+ nNewSize=nOldSize+rPoly.GetSize();
+ aLastPoly.SetSize(nNewSize);
+ for (i=nOldSize; i<nNewSize; i++) {
+ aLastPoly.SetPoint(rPoly.GetPoint(i-nOldSize),i);
+ }
+ pPP->Replace(aLastPoly,pPP->Count()-1);
+ }
+ pAreaStack->bClosed=false;
+}
+
+void OS2METReader::AddPointsToPath(const tools::Polygon & rPoly)
+{
+ sal_uInt16 nOldSize, nNewSize,i;
+
+ if (pPathStack==nullptr || rPoly.GetSize()==0) return;
+ tools::PolyPolygon * pPP=&(pPathStack->aPPoly);
+ if (pPP->Count()==0 /*|| pPathStack->bClosed==sal_True*/) pPP->Insert(rPoly);
+ else {
+ tools::Polygon aLastPoly(pPP->GetObject(pPP->Count()-1));
+ nOldSize=aLastPoly.GetSize();
+ if (nOldSize && aLastPoly.GetPoint(nOldSize-1)!=rPoly.GetPoint(0)) pPP->Insert(rPoly);
+ else {
+ nOldSize--;
+ nNewSize=nOldSize+rPoly.GetSize();
+ aLastPoly.SetSize(nNewSize);
+ for (i=nOldSize; i<nNewSize; i++) {
+ aLastPoly.SetPoint(rPoly.GetPoint(i-nOldSize),i);
+ }
+ pPP->Replace(aLastPoly,pPP->Count()-1);
+ }
+ }
+ pPathStack->bClosed=false;
+}
+
+void OS2METReader::CloseFigure()
+{
+ if (pAreaStack!=nullptr) pAreaStack->bClosed=true;
+ else if (pPathStack!=nullptr) pPathStack->bClosed=true;
+}
+
+void OS2METReader::PushAttr(sal_uInt16 nPushOrder)
+{
+ OSAttr * p;
+ p=new OSAttr;
+ *p=aAttr;
+ p->pSucc=pAttrStack; pAttrStack=p;
+ p->nPushOrder=nPushOrder;
+}
+
+void OS2METReader::PopAttr()
+{
+ OSAttr * p=pAttrStack;
+
+ if (p==nullptr) return;
+ switch (p->nPushOrder) {
+
+ case GOrdPIvAtr:
+ switch (p->nIvAttrA) {
+ case 1: switch (p->nIvAttrP) {
+ case 1: aAttr.aLinCol=p->aLinCol; break;
+ case 2: aAttr.aChrCol=p->aChrCol; break;
+ case 3: aAttr.aMrkCol=p->aMrkCol; break;
+ case 4: aAttr.aPatCol=p->aPatCol; break;
+ case 5: aAttr.aImgCol=p->aImgCol; break;
+ } break;
+ case 2: switch (p->nIvAttrP) {
+ case 1: aAttr.aLinBgCol=p->aLinBgCol; break;
+ case 2: aAttr.aChrBgCol=p->aChrBgCol; break;
+ case 3: aAttr.aMrkBgCol=p->aMrkBgCol; break;
+ case 4: aAttr.aPatBgCol=p->aPatBgCol; break;
+ case 5: aAttr.aImgBgCol=p->aImgBgCol; break;
+ } break;
+ case 3: switch (p->nIvAttrP) {
+ case 1: aAttr.eLinMix=p->eLinMix; break;
+ case 2: aAttr.eChrMix=p->eChrMix; break;
+ case 3: aAttr.eMrkMix=p->eMrkMix; break;
+ case 4: aAttr.ePatMix=p->ePatMix; break;
+ case 5: aAttr.eImgMix=p->eImgMix; break;
+ } break;
+ case 4: switch (p->nIvAttrP) {
+ case 1: aAttr.eLinBgMix=p->eLinBgMix; break;
+ case 2: aAttr.eChrBgMix=p->eChrBgMix; break;
+ case 3: aAttr.eMrkBgMix=p->eMrkBgMix; break;
+ case 4: aAttr.ePatBgMix=p->ePatBgMix; break;
+ case 5: aAttr.eImgBgMix=p->eImgBgMix; break;
+ } break;
+ }
+ break;
+
+ case GOrdPLnTyp: aAttr.eLinStyle=p->eLinStyle; break;
+
+ case GOrdPLnWdt: aAttr.nLinWidth=p->nLinWidth; break;
+
+ case GOrdPStLWd: aAttr.nStrLinWidth=p->nStrLinWidth; break;
+
+ case GOrdPChSet: aAttr.nChrSet=p->nChrSet; break;
+
+ case GOrdPChAng: aAttr.nChrAng=p->nChrAng; break;
+
+ case GOrdPMixMd:
+ aAttr.eLinMix=p->eLinMix;
+ aAttr.eChrMix=p->eChrMix;
+ aAttr.eMrkMix=p->eMrkMix;
+ aAttr.ePatMix=p->ePatMix;
+ aAttr.eImgMix=p->eImgMix;
+ break;
+
+ case GOrdPBgMix:
+ aAttr.eLinBgMix=p->eLinBgMix;
+ aAttr.eChrBgMix=p->eChrBgMix;
+ aAttr.eMrkBgMix=p->eMrkBgMix;
+ aAttr.ePatBgMix=p->ePatBgMix;
+ aAttr.eImgBgMix=p->eImgBgMix;
+ break;
+
+ case GOrdPPtSym: aAttr.bFill = p->bFill; break;
+
+ case GOrdPColor:
+ case GOrdPIxCol:
+ case GOrdPXtCol:
+ aAttr.aLinCol=p->aLinCol;
+ aAttr.aChrCol=p->aChrCol;
+ aAttr.aMrkCol=p->aMrkCol;
+ aAttr.aPatCol=p->aPatCol;
+ aAttr.aImgCol=p->aImgCol;
+ break;
+
+ case GOrdPBgCol:
+ case GOrdPBxCol:
+ aAttr.aLinBgCol=p->aLinBgCol;
+ aAttr.aChrBgCol=p->aChrBgCol;
+ aAttr.aMrkBgCol=p->aMrkBgCol;
+ aAttr.aPatBgCol=p->aPatBgCol;
+ aAttr.aImgBgCol=p->aImgBgCol;
+ break;
+
+ case GOrdPMkPrc: aAttr.nMrkPrec=aDefAttr.nMrkPrec; break;
+
+ case GOrdPMkSet: aAttr.nMrkSet=aDefAttr.nMrkSet; break;
+
+ case GOrdPMkSym: aAttr.nMrkSymbol=aDefAttr.nMrkSymbol; break;
+
+ case GOrdPMkCel: aAttr.aMrkCellSize=aDefAttr.aMrkCellSize; break;
+
+ case GOrdPArcPa:
+ aAttr.nArcP=p->nArcP; aAttr.nArcQ=p->nArcQ;
+ aAttr.nArcR=p->nArcR; aAttr.nArcS=p->nArcS;
+ break;
+
+ case GOrdPCrPos:
+ aAttr.aCurPos=p->aCurPos;
+ break;
+ }
+ pAttrStack=p->pSucc;
+ delete p;
+}
+
+void OS2METReader::ChangeBrush(const Color& rPatColor, bool bFill )
+{
+ Color aColor;
+
+ if( bFill )
+ aColor = rPatColor;
+ else
+ aColor = COL_TRANSPARENT;
+
+ if( pVirDev->GetFillColor() != aColor )
+ pVirDev->SetFillColor( aColor );
+}
+
+void OS2METReader::SetPen( const Color& rColor, sal_uInt16 nLineWidth, PenStyle ePenStyle )
+{
+ LineStyle eLineStyle( LineStyle::Solid );
+
+ if ( pVirDev->GetLineColor() != rColor )
+ pVirDev->SetLineColor( rColor );
+ aLineInfo.SetWidth( nLineWidth );
+
+ sal_uInt16 nDotCount = 0;
+ sal_uInt16 nDashCount = 0;
+ switch ( ePenStyle )
+ {
+ case PEN_NULL :
+ eLineStyle = LineStyle::NONE;
+ break;
+ case PEN_DASHDOT :
+ nDashCount++;
+ [[fallthrough]];
+ case PEN_DOT :
+ nDotCount++;
+ nDashCount--;
+ [[fallthrough]];
+ case PEN_DASH :
+ nDashCount++;
+ aLineInfo.SetDotCount( nDotCount );
+ aLineInfo.SetDashCount( nDashCount );
+ aLineInfo.SetDistance( nLineWidth );
+ aLineInfo.SetDotLen( nLineWidth );
+ aLineInfo.SetDashLen( nLineWidth << 2 );
+ eLineStyle = LineStyle::Dash;
+ break;
+ case PEN_SOLID:
+ break; // -Wall not handled...
+ }
+ aLineInfo.SetStyle( eLineStyle );
+}
+
+void OS2METReader::SetRasterOp(RasterOp eROP)
+{
+ if (pVirDev->GetRasterOp()!=eROP) pVirDev->SetRasterOp(eROP);
+}
+
+void OS2METReader::SetPalette0RGB(sal_uInt16 nIndex, sal_uInt32 nCol)
+{
+ if (pPaletteStack==nullptr) {
+ pPaletteStack=new OSPalette;
+ pPaletteStack->pSucc=nullptr;
+ pPaletteStack->p0RGB=nullptr;
+ pPaletteStack->nSize=0;
+ }
+ if (pPaletteStack->p0RGB==nullptr || nIndex>=pPaletteStack->nSize) {
+ sal_uInt32 * pOld0RGB=pPaletteStack->p0RGB;
+ size_t nOldSize = pPaletteStack->nSize;
+ if (pOld0RGB==nullptr) nOldSize=0;
+ pPaletteStack->nSize=2*(nIndex+1);
+ if (pPaletteStack->nSize<256) pPaletteStack->nSize=256;
+ pPaletteStack->p0RGB = new sal_uInt32[pPaletteStack->nSize];
+ for (size_t i=0; i < pPaletteStack->nSize; ++i)
+ {
+ if (i<nOldSize) pPaletteStack->p0RGB[i]=pOld0RGB[i];
+ else if (i==0) pPaletteStack->p0RGB[i]=0x00ffffff;
+ else pPaletteStack->p0RGB[i]=0;
+ }
+ delete[] pOld0RGB;
+ }
+ pPaletteStack->p0RGB[nIndex]=nCol;
+}
+
+sal_uInt32 OS2METReader::GetPalette0RGB(sal_uInt32 nIndex) const
+{
+ if (pPaletteStack!=nullptr && pPaletteStack->p0RGB!=nullptr &&
+ pPaletteStack->nSize>nIndex) nIndex=pPaletteStack->p0RGB[nIndex];
+ return nIndex;
+}
+
+Color OS2METReader::GetPaletteColor(sal_uInt32 nIndex) const
+{
+ nIndex=GetPalette0RGB(nIndex);
+ return Color(sal::static_int_cast< sal_uInt8 >((nIndex>>16)&0xff),
+ sal::static_int_cast< sal_uInt8 >((nIndex>>8)&0xff),
+ sal::static_int_cast< sal_uInt8 >(nIndex&0xff));
+}
+
+sal_uInt16 OS2METReader::ReadBigEndianWord()
+{
+ sal_uInt8 nLo(0), nHi(0);
+ pOS2MET->ReadUChar( nHi ).ReadUChar( nLo );
+ return (static_cast<sal_uInt16>(nHi)<<8)|(static_cast<sal_uInt16>(nLo)&0x00ff);
+}
+
+sal_uInt32 OS2METReader::ReadBigEndian3BytesLong()
+{
+ sal_uInt8 nHi(0);
+ pOS2MET->ReadUChar( nHi );
+ sal_uInt16 nLo = ReadBigEndianWord();
+ return ((static_cast<sal_uInt32>(nHi)<<16)&0x00ff0000)|static_cast<sal_uInt32>(nLo);
+}
+
+sal_uInt32 OS2METReader::ReadLittleEndian3BytesLong()
+{
+ sal_uInt8 nHi(0), nMed(0), nLo(0);
+
+ pOS2MET->ReadUChar( nLo ).ReadUChar( nMed ).ReadUChar( nHi );
+ return ((static_cast<sal_uInt32>(nHi)&0xff)<<16)|((static_cast<sal_uInt32>(nMed)&0xff)<<8)|(static_cast<sal_uInt32>(nLo)&0xff);
+}
+
+sal_Int32 OS2METReader::ReadCoord(bool b32)
+{
+ sal_Int32 l(0);
+
+ if (b32) pOS2MET->ReadInt32( l );
+ else { short s(0); pOS2MET->ReadInt16( s ); l = static_cast<sal_Int32>(s); }
+ return l;
+}
+
+Point OS2METReader::ReadPoint( const bool bAdjustBoundRect )
+{
+ sal_Int32 x = ReadCoord(bCoord32);
+ sal_Int32 y = ReadCoord(bCoord32);
+ x=x-aBoundingRect.Left();
+ y=aBoundingRect.Bottom()-y;
+
+ if (bAdjustBoundRect)
+ {
+ if (x == SAL_MAX_INT32 || y == SAL_MAX_INT32)
+ pOS2MET->SetError(SVSTREAM_FILEFORMAT_ERROR);
+ else
+ aCalcBndRect.Union(tools::Rectangle(x, y, x + 1, y + 1));
+ }
+
+ return Point(x,y);
+}
+
+RasterOp OS2METReader::OS2MixToRasterOp(sal_uInt8 nMix)
+{
+ switch (nMix) {
+ case 0x0c: return RasterOp::Invert;
+ case 0x04: return RasterOp::Xor;
+ case 0x0b: return RasterOp::Xor;
+ default: return RasterOp::OverPaint;
+ }
+}
+
+void OS2METReader::ReadLine(bool bGivenPos, sal_uInt16 nOrderLen)
+{
+ sal_uInt16 i,nPolySize;
+
+ if (bCoord32) nPolySize=nOrderLen/8; else nPolySize=nOrderLen/4;
+ if (!bGivenPos) nPolySize++;
+ if (nPolySize==0) return;
+ tools::Polygon aPolygon(nPolySize);
+ for (i=0; i<nPolySize; i++) {
+ if (i==0 && !bGivenPos) aPolygon.SetPoint(aAttr.aCurPos,i);
+ else aPolygon.SetPoint(ReadPoint(),i);
+ }
+ aAttr.aCurPos=aPolygon.GetPoint(nPolySize-1);
+ if (pAreaStack!=nullptr) AddPointsToArea(aPolygon);
+ else if (pPathStack!=nullptr) AddPointsToPath(aPolygon);
+ else
+ {
+ SetPen( aAttr.aLinCol, aAttr.nStrLinWidth, aAttr.eLinStyle );
+ SetRasterOp(aAttr.eLinMix);
+ DrawPolyLine( aPolygon );
+ }
+}
+
+void OS2METReader::ReadRelLine(bool bGivenPos, sal_uInt16 nOrderLen)
+{
+ sal_uInt16 i,nPolySize;
+ Point aP0;
+
+ if (bGivenPos) {
+ aP0=ReadPoint();
+ if (bCoord32) nOrderLen-=8; else nOrderLen-=4;
+ }
+ else aP0=aAttr.aCurPos;
+ if (nOrderLen > pOS2MET->remainingSize())
+ throw css::uno::Exception("attempt to read past end of input", nullptr);
+ nPolySize=nOrderLen/2;
+ if (nPolySize==0) return;
+ tools::Polygon aPolygon(nPolySize);
+ for (i=0; i<nPolySize; i++) {
+ sal_Int8 nsignedbyte;
+ pOS2MET->ReadSChar( nsignedbyte ); aP0.AdjustX(static_cast<sal_Int32>(nsignedbyte));
+ pOS2MET->ReadSChar( nsignedbyte ); aP0.AdjustY(-static_cast<sal_Int32>(nsignedbyte));
+ aCalcBndRect.Union(tools::Rectangle(aP0,Size(1,1)));
+ aPolygon.SetPoint(aP0,i);
+ }
+ aAttr.aCurPos=aPolygon.GetPoint(nPolySize-1);
+ if (pAreaStack!=nullptr) AddPointsToArea(aPolygon);
+ else if (pPathStack!=nullptr) AddPointsToPath(aPolygon);
+ else
+ {
+ SetPen( aAttr.aLinCol, aAttr.nStrLinWidth, aAttr.eLinStyle );
+ SetRasterOp(aAttr.eLinMix);
+ DrawPolyLine( aPolygon );
+ }
+}
+
+void OS2METReader::ReadBox(bool bGivenPos)
+{
+ sal_uInt8 nFlags;
+ Point P0;
+
+ pOS2MET->ReadUChar( nFlags );
+ pOS2MET->SeekRel(1);
+
+ if ( bGivenPos )
+ P0 = ReadPoint();
+ else
+ P0 = aAttr.aCurPos;
+
+ aAttr.aCurPos = ReadPoint();
+ sal_Int32 nHRound = ReadCoord(bCoord32);
+ sal_Int32 nVRound = ReadCoord(bCoord32);
+
+ if (!pOS2MET->good())
+ {
+ SAL_WARN("filter.os2met", "OS2METReader::ReadBox: short read");
+ return;
+ }
+
+ tools::Rectangle aBoxRect( P0, aAttr.aCurPos );
+
+ if ( pAreaStack )
+ AddPointsToArea( tools::Polygon( aBoxRect ) );
+ else if ( pPathStack )
+ AddPointsToPath( tools::Polygon( aBoxRect ) );
+ else
+ {
+ if ( nFlags & 0x20 )
+ SetPen( aAttr.aLinCol, aAttr.nStrLinWidth, aAttr.eLinStyle );
+ else
+ SetPen( COL_TRANSPARENT );
+
+ if ( nFlags & 0x40 )
+ {
+ ChangeBrush(aAttr.aPatCol, aAttr.bFill);
+ SetRasterOp(aAttr.ePatMix);
+ }
+ else
+ {
+ ChangeBrush( COL_TRANSPARENT, false );
+ SetRasterOp(aAttr.eLinMix);
+ }
+
+ if ( IsLineInfo() )
+ {
+ tools::Polygon aPolygon( aBoxRect, nHRound, nVRound );
+ if ( nFlags & 0x40 )
+ {
+ pVirDev->Push( vcl::PushFlags::LINECOLOR );
+ pVirDev->SetLineColor( COL_TRANSPARENT );
+ pVirDev->DrawRect( aBoxRect, nHRound, nVRound );
+ pVirDev->Pop();
+ }
+ pVirDev->DrawPolyLine( aPolygon, aLineInfo );
+ }
+ else
+ pVirDev->DrawRect( aBoxRect, nHRound, nVRound );
+ }
+}
+
+void OS2METReader::ReadBitBlt()
+{
+ pOS2MET->SeekRel(4);
+ sal_uInt32 nID(0);
+ pOS2MET->ReadUInt32( nID );
+ pOS2MET->SeekRel(4);
+ Point aP1 = ReadPoint();
+ Point aP2 = ReadPoint();
+ if (aP1.X() > aP2.X()) { auto nt=aP1.X(); aP1.setX(aP2.X() ); aP2.setX(nt ); }
+ if (aP1.Y() > aP2.Y()) { auto nt=aP1.Y(); aP1.setY(aP2.Y() ); aP2.setY(nt ); }
+ Size aSize(aP2.X() - aP1.X(), aP2.Y() - aP1.Y());
+
+ OSBitmap* pB = pBitmapList;
+ while (pB!=nullptr && pB->nID!=nID) pB=pB->pSucc;
+ if (pB!=nullptr) {
+ SetRasterOp(aAttr.ePatMix);
+ pVirDev->DrawBitmapEx(aP1,aSize,pB->aBitmapEx);
+ }
+}
+
+void OS2METReader::ReadChrStr(bool bGivenPos, bool bMove, bool bExtra, sal_uInt16 nOrderLen)
+{
+ Point aP0;
+ sal_uInt16 nLen;
+ OSFont * pF;
+ vcl::Font aFont;
+ Size aSize;
+
+ pF = pFontList;
+ while (pF!=nullptr && pF->nID!=aAttr.nChrSet) pF=pF->pSucc;
+ if (pF!=nullptr)
+ aFont = pF->aFont;
+ aFont.SetColor(aAttr.aChrCol);
+ aFont.SetFontSize(Size(0,aAttr.nChrCellHeight));
+ if ( aAttr.nChrAng )
+ aFont.SetOrientation(aAttr.nChrAng);
+
+ if (bGivenPos)
+ aP0 = ReadPoint();
+ else
+ aP0 = aAttr.aCurPos;
+ if (bExtra)
+ {
+ pOS2MET->SeekRel(2);
+ ReadPoint( false );
+ ReadPoint( false );
+ pOS2MET->ReadUInt16( nLen );
+ }
+ else
+ {
+ if ( !bGivenPos )
+ nLen = nOrderLen;
+ else if ( bCoord32 )
+ nLen = nOrderLen-8;
+ else
+ nLen = nOrderLen-4;
+ }
+ if (!pOS2MET->good() || nLen > pOS2MET->remainingSize())
+ throw css::uno::Exception("attempt to read past end of input", nullptr);
+ std::unique_ptr<char[]> pChr(new char[nLen+1]);
+ for (sal_uInt16 i=0; i<nLen; i++)
+ pOS2MET->ReadChar( pChr[i] );
+ pChr[nLen] = 0;
+ OUString aStr( pChr.get(), strlen(pChr.get()), osl_getThreadTextEncoding() );
+ SetRasterOp(aAttr.eChrMix);
+ if (pVirDev->GetFont()!=aFont)
+ pVirDev->SetFont(aFont);
+ pVirDev->DrawText(aP0,aStr);
+
+ aSize = Size( pVirDev->GetTextWidth(aStr), pVirDev->GetTextHeight() );
+ if ( !aAttr.nChrAng )
+ {
+ aCalcBndRect.Union(tools::Rectangle( Point(aP0.X(),aP0.Y()-aSize.Height()),
+ Size(aSize.Width(),aSize.Height()*2)));
+ if (bMove)
+ aAttr.aCurPos = Point( aP0.X() + aSize.Width(), aP0.Y());
+ }
+ else
+ {
+ tools::Polygon aDummyPoly(4);
+
+ aDummyPoly.SetPoint( Point( aP0.X(), aP0.Y() ), 0); // TOP LEFT
+ aDummyPoly.SetPoint( Point( aP0.X(), aP0.Y() - aSize.Height() ), 1); // BOTTOM LEFT
+ aDummyPoly.SetPoint( Point( aP0.X() + aSize.Width(), aP0.Y() ), 2); // TOP RIGHT
+ aDummyPoly.SetPoint( Point( aP0.X() + aSize.Width(), aP0.Y() - aSize.Height() ), 3);// BOTTOM RIGHT
+ aDummyPoly.Rotate( aP0, aAttr.nChrAng );
+ if ( bMove )
+ aAttr.aCurPos = aDummyPoly.GetPoint( 0 );
+ aCalcBndRect.Union( tools::Rectangle( aDummyPoly.GetPoint( 0 ), aDummyPoly.GetPoint( 3 ) ) );
+ aCalcBndRect.Union( tools::Rectangle( aDummyPoly.GetPoint( 1 ), aDummyPoly.GetPoint( 2 ) ) );
+ }
+}
+
+void OS2METReader::ReadArc(bool bGivenPos)
+{
+ Point aP1, aP2, aP3;
+ double x1,y1,x2,y2,x3,y3,p,q,cx,cy,ncx,ncy,r,rx,ry,w1,w3;
+ if (bGivenPos) aP1=ReadPoint(); else aP1=aAttr.aCurPos;
+ aP2=ReadPoint(); aP3=ReadPoint();
+ aAttr.aCurPos=aP3;
+ SetPen( aAttr.aLinCol, aAttr.nStrLinWidth, aAttr.eLinStyle );
+ SetRasterOp(aAttr.eLinMix);
+ // Ok, given are 3 point of the ellipse, and the relation
+ // of width and height (as p to q):
+ x1=aP1.X(); y1=aP1.Y();
+ x2=aP2.X(); y2=aP2.Y();
+ x3=aP3.X(); y3=aP3.Y();
+ p=aAttr.nArcP;q=aAttr.nArcQ;
+ // Calculation of the center point cx, cy of the ellipse:
+ ncy=2*p*p*((y3-y1)*(x2-x1)-(y1-y2)*(x1-x3));
+ ncx=2*q*q*(x2-x1);
+ if ( (ncx<0.001 && ncx>-0.001) || (ncy<0.001 && ncy>-0.001) ) {
+ // Calculation impossible, points are all on the same straight line
+ pVirDev->DrawLine(aP1,aP2);
+ pVirDev->DrawLine(aP2,aP3);
+ return;
+ }
+ cy=( q*q*((x3*x3-x1*x1)*(x2-x1)+(x2*x2-x1*x1)*(x1-x3)) +
+ p*p*((y3*y3-y1*y1)*(x2-x1)+(y2*y2-y1*y1)*(x1-x3)) ) / ncy;
+ cx=( q*q*(x2*x2-x1*x1)+p*p*(y2*y2-y1*y1)+cy*2*p*p*(y1-y2) ) / ncx;
+ // now we still need the radius in x and y direction:
+ r=sqrt(q*q*(x1-cx)*(x1-cx)+p*p*(y1-cy)*(y1-cy));
+ rx=r/q; ry=r/p;
+ // We now have to find out how the starting and the end point
+ // have to be chosen so that point no. 2 lies inside the drawn arc:
+ w1=fmod((atan2(x1-cx,y1-cy)-atan2(x2-cx,y2-cy)),6.28318530718); if (w1<0) w1+=6.28318530718;
+ w3=fmod((atan2(x3-cx,y3-cy)-atan2(x2-cx,y2-cy)),6.28318530718); if (w3<0) w3+=6.28318530718;
+ if (w3<w1) {
+ pVirDev->DrawArc(tools::Rectangle(static_cast<sal_Int32>(cx-rx),static_cast<sal_Int32>(cy-ry),
+ static_cast<sal_Int32>(cx+rx),static_cast<sal_Int32>(cy+ry)),aP1,aP3);
+ }
+ else {
+ pVirDev->DrawArc(tools::Rectangle(static_cast<sal_Int32>(cx-rx),static_cast<sal_Int32>(cy-ry),
+ static_cast<sal_Int32>(cx+rx),static_cast<sal_Int32>(cy+ry)),aP3,aP1);
+ }
+}
+
+void OS2METReader::ReadFullArc(bool bGivenPos, sal_uInt16 nOrderSize)
+{
+ Point aCenter;
+ tools::Rectangle aRect;
+
+ if (bGivenPos) {
+ aCenter=ReadPoint();
+ if (bCoord32) nOrderSize-=8; else nOrderSize-=4;
+ }
+ else aCenter=aAttr.aCurPos;
+
+ sal_Int32 nP = aAttr.nArcP;
+ sal_Int32 nQ = aAttr.nArcQ;
+ if (nP < 0)
+ nP = o3tl::saturating_toggle_sign(nP);
+ if (nQ < 0)
+ nQ = o3tl::saturating_toggle_sign(nQ);
+ sal_uInt32 nMul(0);
+ if (nOrderSize>=4)
+ pOS2MET->ReadUInt32( nMul );
+ else {
+ sal_uInt16 nMulS(0);
+ pOS2MET->ReadUInt16( nMulS );
+ nMul=static_cast<sal_uInt32>(nMulS)<<8;
+ }
+ if (nMul!=0x00010000) {
+ nP=(nP*nMul)>>16;
+ nQ=(nQ*nMul)>>16;
+ }
+
+ aRect=tools::Rectangle(aCenter.X()-nP,aCenter.Y()-nQ,
+ aCenter.X()+nP,aCenter.Y()+nQ);
+ aCalcBndRect.Union(aRect);
+
+ if (pAreaStack!=nullptr) {
+ ChangeBrush(aAttr.aPatCol, aAttr.bFill);
+ SetRasterOp(aAttr.ePatMix);
+ if ((pAreaStack->nFlags&0x40)!=0)
+ SetPen( aAttr.aLinCol, aAttr.nStrLinWidth, aAttr.eLinStyle );
+ else
+ SetPen( COL_TRANSPARENT, 0, PEN_NULL );
+ }
+ else
+ {
+ SetPen( aAttr.aLinCol, aAttr.nStrLinWidth, aAttr.eLinStyle );
+ ChangeBrush(COL_TRANSPARENT, false);
+ SetRasterOp(aAttr.eLinMix);
+ }
+ pVirDev->DrawEllipse(aRect);
+}
+
+void OS2METReader::ReadPartialArc(bool bGivenPos, sal_uInt16 nOrderSize)
+{
+ Point aP0, aCenter,aPStart,aPEnd;
+ tools::Rectangle aRect;
+
+ if (bGivenPos) {
+ aP0=ReadPoint();
+ if (bCoord32) nOrderSize-=8; else nOrderSize-=4;
+ }
+ else aP0=aAttr.aCurPos;
+ aCenter=ReadPoint();
+
+ sal_Int32 nP = aAttr.nArcP;
+ sal_Int32 nQ = aAttr.nArcQ;
+ if (nP < 0)
+ nP = o3tl::saturating_toggle_sign(nP);
+ if (nQ < 0)
+ nQ = o3tl::saturating_toggle_sign(nQ);
+ sal_uInt32 nMul(0);
+ if (nOrderSize>=12)
+ pOS2MET->ReadUInt32( nMul );
+ else {
+ sal_uInt16 nMulS(0);
+ pOS2MET->ReadUInt16( nMulS );
+ nMul=static_cast<sal_uInt32>(nMulS)<<8;
+ }
+ if (nMul!=0x00010000) {
+ nP=(nP*nMul)>>16;
+ nQ=(nQ*nMul)>>16;
+ }
+
+ sal_Int32 nStart(0), nSweep(0);
+ pOS2MET->ReadInt32( nStart ).ReadInt32( nSweep );
+ double fStart = basegfx::deg2rad<65536>(static_cast<double>(nStart));
+ double fEnd = fStart+ basegfx::deg2rad<65536>(static_cast<double>(nSweep));
+ aPStart=Point(aCenter.X()+static_cast<sal_Int32>( cos(fStart)*nP),
+ aCenter.Y()+static_cast<sal_Int32>(-sin(fStart)*nQ));
+ aPEnd= Point(aCenter.X()+static_cast<sal_Int32>( cos(fEnd)*nP),
+ aCenter.Y()+static_cast<sal_Int32>(-sin(fEnd)*nQ));
+
+ aRect=tools::Rectangle(aCenter.X()-nP,aCenter.Y()-nQ,
+ aCenter.X()+nP,aCenter.Y()+nQ);
+ aCalcBndRect.Union(aRect);
+
+ SetPen( aAttr.aLinCol, aAttr.nStrLinWidth, aAttr.eLinStyle );
+ SetRasterOp(aAttr.eLinMix);
+
+ pVirDev->DrawLine(aP0,aPStart);
+ pVirDev->DrawArc(aRect,aPStart,aPEnd);
+ aAttr.aCurPos=aPEnd;
+}
+
+void OS2METReader::ReadPolygons()
+{
+ tools::PolyPolygon aPolyPoly;
+ tools::Polygon aPoly;
+ Point aPoint;
+
+ sal_uInt8 nFlags(0);
+ sal_uInt32 nNumPolys(0);
+ pOS2MET->ReadUChar(nFlags).ReadUInt32(nNumPolys);
+
+ if (!pOS2MET->good() || nNumPolys > SAL_MAX_UINT16)
+ {
+ pOS2MET->SetError(SVSTREAM_FILEFORMAT_ERROR);
+ ErrorCode=11;
+ return;
+ }
+
+ for (sal_uInt32 i=0; i<nNumPolys; ++i)
+ {
+ sal_uInt32 nNumPoints(0);
+ pOS2MET->ReadUInt32(nNumPoints);
+ sal_uInt32 nLimit = SAL_MAX_UINT16;
+ if (i==0) --nLimit;
+ if (!pOS2MET->good() || nNumPoints > nLimit)
+ {
+ pOS2MET->SetError(SVSTREAM_FILEFORMAT_ERROR);
+ ErrorCode=11;
+ return;
+ }
+ if (i==0) ++nNumPoints;
+ aPoly.SetSize(static_cast<short>(nNumPoints));
+ for (sal_uInt32 j=0; j<nNumPoints; ++j)
+ {
+ if (i==0 && j==0) aPoint=aAttr.aCurPos;
+ else aPoint=ReadPoint();
+ aPoly.SetPoint(aPoint,static_cast<short>(j));
+ if (i==nNumPolys-1 && j==nNumPoints-1) aAttr.aCurPos=aPoint;
+ }
+ aPolyPoly.Insert(aPoly);
+ }
+
+ ChangeBrush(aAttr.aPatCol, aAttr.bFill);
+ SetRasterOp(aAttr.ePatMix);
+ if ((nFlags&0x01)!=0)
+ SetPen( aAttr.aLinCol, aAttr.nStrLinWidth, aAttr.eLinStyle );
+ else
+ SetPen( COL_TRANSPARENT, 0, PEN_NULL );
+ DrawPolyPolygon( aPolyPoly );
+}
+
+void OS2METReader::ReadBezier(bool bGivenPos, sal_uInt16 nOrderLen)
+{
+ sal_uInt16 i, nNumPoints = nOrderLen / ( bCoord32 ? 8 : 4 );
+
+ if( !bGivenPos )
+ nNumPoints++;
+
+ if( !nNumPoints )
+ return;
+
+ tools::Polygon aPolygon( nNumPoints );
+
+ for( i=0; i < nNumPoints; i++ )
+ {
+ if( i==0 && !bGivenPos)
+ aPolygon.SetPoint( aAttr.aCurPos, i );
+ else
+ aPolygon.SetPoint( ReadPoint(), i );
+ }
+
+ if( !( nNumPoints % 4 ) )
+ {
+ // create bezier polygon
+ const sal_uInt16 nSegPoints = 25;
+ const sal_uInt16 nSegments = aPolygon.GetSize() >> 2;
+ tools::Polygon aBezPoly( nSegments * nSegPoints );
+
+ sal_uInt16 nSeg, nBezPos, nStartPos;
+ for( nSeg = 0, nBezPos = 0, nStartPos = 0; nSeg < nSegments; nSeg++, nStartPos += 4 )
+ {
+ const tools::Polygon aSegPoly( aPolygon[ nStartPos ], aPolygon[ nStartPos + 1 ],
+ aPolygon[ nStartPos + 3 ], aPolygon[ nStartPos + 2 ],
+ nSegPoints );
+
+ for( sal_uInt16 nSegPos = 0; nSegPos < nSegPoints; )
+ aBezPoly[ nBezPos++ ] = aSegPoly[ nSegPos++ ];
+ }
+
+ nNumPoints = nBezPos;
+
+ if( nNumPoints != aBezPoly.GetSize() )
+ aBezPoly.SetSize( nNumPoints );
+
+ aPolygon = aBezPoly;
+ }
+
+ aAttr.aCurPos = aPolygon[ nNumPoints - 1 ];
+
+ if (pAreaStack!=nullptr)
+ AddPointsToArea(aPolygon);
+ else if (pPathStack!=nullptr)
+ AddPointsToPath(aPolygon);
+ else
+ {
+ SetPen( aAttr.aLinCol, aAttr.nStrLinWidth, aAttr.eLinStyle );
+ SetRasterOp(aAttr.eLinMix);
+ DrawPolyLine( aPolygon );
+ }
+}
+
+void OS2METReader::ReadFillet(bool bGivenPos, sal_uInt16 nOrderLen)
+{
+ sal_uInt16 i,nNumPoints;
+
+ if (bCoord32) nNumPoints=nOrderLen/8; else nNumPoints=nOrderLen/4;
+ if (!bGivenPos) nNumPoints++;
+ if (nNumPoints==0) return;
+ tools::Polygon aPolygon(nNumPoints);
+ for (i=0; i<nNumPoints; i++) {
+ if (i==0 && !bGivenPos) aPolygon.SetPoint(aAttr.aCurPos,i);
+ else aPolygon.SetPoint(ReadPoint(),i);
+ }
+ aAttr.aCurPos=aPolygon.GetPoint(nNumPoints-1);
+ if (pAreaStack!=nullptr) AddPointsToArea(aPolygon);
+ else if (pPathStack!=nullptr) AddPointsToPath(aPolygon);
+ else {
+ SetPen( aAttr.aLinCol, aAttr.nStrLinWidth, aAttr.eLinStyle );
+ SetRasterOp(aAttr.eLinMix);
+ DrawPolyLine( aPolygon );
+ }
+}
+
+void OS2METReader::ReadFilletSharp(bool bGivenPos, sal_uInt16 nOrderLen)
+{
+ if (bGivenPos) {
+ aAttr.aCurPos=ReadPoint();
+ if (bCoord32) nOrderLen-=8; else nOrderLen-=4;
+ }
+
+ sal_uInt16 nNumPoints;
+ if (bCoord32) nNumPoints=1+nOrderLen/10;
+ else nNumPoints=1+nOrderLen/6;
+
+ tools::Polygon aPolygon(nNumPoints);
+ aPolygon.SetPoint(aAttr.aCurPos, 0);
+ for (sal_uInt16 i = 1; i <nNumPoints; ++i)
+ aPolygon.SetPoint(ReadPoint(), i);
+
+ if (!pOS2MET->good())
+ {
+ SAL_WARN("filter.os2met", "OS2METReader::ReadFilletSharp: short read");
+ return;
+ }
+
+ aAttr.aCurPos=aPolygon.GetPoint(nNumPoints-1);
+ if (pAreaStack!=nullptr) AddPointsToArea(aPolygon);
+ else if (pPathStack!=nullptr) AddPointsToPath(aPolygon);
+ else
+ {
+ SetPen( aAttr.aLinCol, aAttr.nStrLinWidth, aAttr.eLinStyle );
+ SetRasterOp(aAttr.eLinMix);
+ DrawPolyLine( aPolygon );
+ }
+}
+
+void OS2METReader::ReadMarker(bool bGivenPos, sal_uInt16 nOrderLen)
+{
+ sal_uInt16 i,nNumPoints;
+
+ SetPen( aAttr.aMrkCol );
+ SetRasterOp(aAttr.eMrkMix);
+ if (aAttr.nMrkSymbol>=5 && aAttr.nMrkSymbol<=9)
+ {
+ ChangeBrush(aAttr.aMrkCol, true);
+ }
+ else
+ {
+ ChangeBrush(COL_TRANSPARENT, false);
+ }
+ if (bCoord32) nNumPoints=nOrderLen/8; else nNumPoints=nOrderLen/4;
+ if (!bGivenPos) nNumPoints++;
+ for (i=0; i<nNumPoints; i++) {
+ if (i!=0 || bGivenPos) aAttr.aCurPos=ReadPoint();
+ const auto x = aAttr.aCurPos.X();
+ const auto y = aAttr.aCurPos.Y();
+ aCalcBndRect.Union(tools::Rectangle(x-5,y-5,x+5,y+5));
+ switch (aAttr.nMrkSymbol) {
+ case 2: // PLUS
+ pVirDev->DrawLine(Point(x-4,y),Point(x+4,y));
+ pVirDev->DrawLine(Point(x,y-4),Point(x,y+4));
+ break;
+ case 3: // DIAMOND
+ case 7: { // SOLIDDIAMOND
+ tools::Polygon aPoly(4);
+ aPoly.SetPoint(Point(x,y+4),0);
+ aPoly.SetPoint(Point(x+4,y),1);
+ aPoly.SetPoint(Point(x,y-4),2);
+ aPoly.SetPoint(Point(x-4,y),3);
+ pVirDev->DrawPolygon(aPoly);
+ break;
+ }
+ case 4: // SQUARE
+ case 8: { // SOLIDSUARE
+ tools::Polygon aPoly(4);
+ aPoly.SetPoint(Point(x+4,y+4),0);
+ aPoly.SetPoint(Point(x+4,y-4),1);
+ aPoly.SetPoint(Point(x-4,y-4),2);
+ aPoly.SetPoint(Point(x-4,y+4),3);
+ pVirDev->DrawPolygon(aPoly);
+ break;
+ }
+ case 5: { // SIXPOINTSTAR
+ tools::Polygon aPoly(12);
+ aPoly.SetPoint(Point(x ,y-4),0);
+ aPoly.SetPoint(Point(x+2,y-2),1);
+ aPoly.SetPoint(Point(x+4,y-2),2);
+ aPoly.SetPoint(Point(x+2,y ),3);
+ aPoly.SetPoint(Point(x+4,y+2),4);
+ aPoly.SetPoint(Point(x+2,y+2),5);
+ aPoly.SetPoint(Point(x ,y+4),6);
+ aPoly.SetPoint(Point(x-2,y+2),7);
+ aPoly.SetPoint(Point(x-4,y+2),8);
+ aPoly.SetPoint(Point(x-2,y ),9);
+ aPoly.SetPoint(Point(x-4,y-2),10);
+ aPoly.SetPoint(Point(x-2,y-2),11);
+ pVirDev->DrawPolygon(aPoly);
+ break;
+ }
+ case 6: { // EIGHTPOINTSTAR
+ tools::Polygon aPoly(16);
+ aPoly.SetPoint(Point(x ,y-4),0);
+ aPoly.SetPoint(Point(x+1,y-2),1);
+ aPoly.SetPoint(Point(x+3,y-3),2);
+ aPoly.SetPoint(Point(x+2,y-1),3);
+ aPoly.SetPoint(Point(x+4,y ),4);
+ aPoly.SetPoint(Point(x+2,y+1),5);
+ aPoly.SetPoint(Point(x+3,y+3),6);
+ aPoly.SetPoint(Point(x+1,y+2),7);
+ aPoly.SetPoint(Point(x ,y+4),8);
+ aPoly.SetPoint(Point(x-1,y+2),9);
+ aPoly.SetPoint(Point(x-3,y+3),10);
+ aPoly.SetPoint(Point(x-2,y+1),11);
+ aPoly.SetPoint(Point(x-4,y ),12);
+ aPoly.SetPoint(Point(x-2,y-1),13);
+ aPoly.SetPoint(Point(x-3,y-3),14);
+ aPoly.SetPoint(Point(x-1,y-2),15);
+ pVirDev->DrawPolygon(aPoly);
+ break;
+ }
+ case 9: // DOT
+ pVirDev->DrawEllipse(tools::Rectangle(x-1,y-1,x+1,y+1));
+ break;
+ case 10: // SMALLCIRCLE
+ pVirDev->DrawEllipse(tools::Rectangle(x-2,y-2,x+2,y+2));
+ break;
+ case 64: // BLANK
+ break;
+ default: // (=1) CROSS
+ pVirDev->DrawLine(Point(x-4,y-4),Point(x+4,y+4));
+ pVirDev->DrawLine(Point(x-4,y+4),Point(x+4,y-4));
+ break;
+ }
+ }
+}
+
+void OS2METReader::ReadOrder(sal_uInt16 nOrderID, sal_uInt16 nOrderLen)
+{
+ switch (nOrderID) {
+
+ case GOrdGivArc: ReadArc(true); break;
+ case GOrdCurArc: ReadArc(false); break;
+
+ case GOrdGivBzr: ReadBezier(true,nOrderLen); break;
+ case GOrdCurBzr: ReadBezier(false,nOrderLen); break;
+
+ case GOrdGivBox: ReadBox(true); break;
+ case GOrdCurBox: ReadBox(false); break;
+
+ case GOrdGivFil: ReadFillet(true,nOrderLen); break;
+ case GOrdCurFil: ReadFillet(false,nOrderLen); break;
+
+ case GOrdGivCrc: ReadFullArc(true,nOrderLen); break;
+ case GOrdCurCrc: ReadFullArc(false,nOrderLen); break;
+
+ case GOrdGivLin: ReadLine(true, nOrderLen); break;
+ case GOrdCurLin: ReadLine(false, nOrderLen); break;
+
+ case GOrdGivMrk: ReadMarker(true, nOrderLen); break;
+ case GOrdCurMrk: ReadMarker(false, nOrderLen); break;
+
+ case GOrdGivArP: ReadPartialArc(true,nOrderLen); break;
+ case GOrdCurArP: ReadPartialArc(false,nOrderLen); break;
+
+ case GOrdGivRLn: ReadRelLine(true,nOrderLen); break;
+ case GOrdCurRLn: ReadRelLine(false,nOrderLen); break;
+
+ case GOrdGivSFl: ReadFilletSharp(true,nOrderLen); break;
+ case GOrdCurSFl: ReadFilletSharp(false,nOrderLen); break;
+
+ case GOrdGivStM: ReadChrStr(true , true , false, nOrderLen); break;
+ case GOrdCurStM: ReadChrStr(false, true , false, nOrderLen); break;
+ case GOrdGivStr: ReadChrStr(true , false, false, nOrderLen); break;
+ case GOrdCurStr: ReadChrStr(false, false, false, nOrderLen); break;
+ case GOrdGivStx: ReadChrStr(true , false, true , nOrderLen); break;
+ case GOrdCurStx: ReadChrStr(false, false, true , nOrderLen); break;
+
+ case GOrdGivImg: SAL_INFO("filter.os2met","GOrdGivImg");
+ break;
+ case GOrdCurImg: SAL_INFO("filter.os2met","GOrdCurImg");
+ break;
+ case GOrdImgDat: SAL_INFO("filter.os2met","GOrdImgDat");
+ break;
+ case GOrdEndImg: SAL_INFO("filter.os2met","GOrdEndImg");
+ break;
+
+ case GOrdBegAra: {
+ OSArea * p=new OSArea;
+ p->bClosed=false;
+ p->pSucc=pAreaStack; pAreaStack=p;
+ pOS2MET->ReadUChar( p->nFlags );
+ p->aCol=aAttr.aPatCol;
+ p->aBgCol=aAttr.aPatBgCol;
+ p->eMix=aAttr.ePatMix;
+ p->eBgMix=aAttr.ePatBgMix;
+ p->bFill=aAttr.bFill;
+ break;
+ }
+ case GOrdEndAra:
+ {
+ OSArea * p=pAreaStack;
+ if ( p )
+ {
+ pAreaStack = p->pSucc;
+ if ( pPathStack )
+ {
+ for ( sal_uInt16 i=0; i<p->aPPoly.Count(); i++ )
+ {
+ AddPointsToPath( p->aPPoly.GetObject( i ) );
+ CloseFigure();
+ }
+ }
+ else
+ {
+ if ( ( p->nFlags & 0x40 ) == 0 )
+ SetPen( COL_TRANSPARENT, 0, PEN_NULL );
+ else
+ SetPen( aAttr.aLinCol, aAttr.nStrLinWidth, aAttr.eLinStyle );
+
+ ChangeBrush(p->aCol, p->bFill);
+ SetRasterOp(p->eMix);
+ DrawPolyPolygon( p->aPPoly );
+ }
+ delete p;
+ }
+ }
+ break;
+
+ case GOrdBegElm: SAL_INFO("filter.os2met","GOrdBegElm");
+ break;
+ case GOrdEndElm: SAL_INFO("filter.os2met","GOrdEndElm");
+ break;
+
+ case GOrdBegPth: {
+ OSPath * p=new OSPath;
+ p->pSucc=pPathStack; pPathStack=p;
+ pOS2MET->SeekRel(2);
+ pOS2MET->ReadUInt32( p->nID );
+ p->bClosed=false;
+ p->bStroke=false;
+ break;
+ }
+ case GOrdEndPth: {
+ OSPath * p, * pprev, * psucc;
+ if (pPathStack==nullptr) break;
+ p=pPathList; pprev=nullptr;
+ while (p!=nullptr) {
+ psucc=p->pSucc;
+ if (p->nID==pPathStack->nID) {
+ if (pprev==nullptr) pPathList=psucc; else pprev->pSucc=psucc;
+ delete p;
+ }
+ else pprev=p;
+ p=psucc;
+ }
+ p=pPathStack;
+ pPathStack=p->pSucc;
+ p->pSucc=pPathList; pPathList=p;
+ break;
+ }
+ case GOrdFilPth:
+ {
+ sal_uInt32 nID(0);
+ sal_uInt16 nDummy(0);
+ OSPath* p = pPathList;
+
+ pOS2MET->ReadUInt16( nDummy )
+ .ReadUInt32( nID );
+
+ if ( ! ( nDummy & 0x20 ) ) // #30933# i do not know the exact meaning of this bit,
+ { // but if set it seems to be better not to fill this path
+ while( p && p->nID != nID )
+ p = p->pSucc;
+
+ if( p )
+ {
+ if( p->bStroke )
+ {
+ SetPen( aAttr.aPatCol, aAttr.nStrLinWidth );
+ ChangeBrush(COL_TRANSPARENT, false);
+ SetRasterOp( aAttr.ePatMix );
+ if ( IsLineInfo() )
+ {
+ for ( sal_uInt16 i = 0; i < p->aPPoly.Count(); i++ )
+ pVirDev->DrawPolyLine( p->aPPoly.GetObject( i ), aLineInfo );
+ }
+ else
+ pVirDev->DrawPolyPolygon( p->aPPoly );
+ }
+ else
+ {
+ SetPen( COL_TRANSPARENT, 0, PEN_NULL );
+ ChangeBrush( aAttr.aPatCol, aAttr.bFill );
+ SetRasterOp( aAttr.ePatMix );
+ pVirDev->DrawPolyPolygon( p->aPPoly );
+ }
+ }
+ }
+ }
+ break;
+
+ case GOrdModPth:
+ {
+ OSPath* p = pPathList;
+
+ while( p && p->nID != 1 )
+ p = p->pSucc;
+
+ if( p )
+ p->bStroke = true;
+ }
+ break;
+
+ case GOrdOutPth:
+ {
+ sal_uInt32 nID;
+ sal_uInt16 i,nC;
+ OSPath* p=pPathList;
+ pOS2MET->SeekRel(2);
+ pOS2MET->ReadUInt32( nID );
+ while (p && pOS2MET->good() && p->nID != nID)
+ p = p->pSucc;
+
+ if (p)
+ {
+ SetPen( aAttr.aLinCol, aAttr.nStrLinWidth, aAttr.eLinStyle );
+ SetRasterOp(aAttr.eLinMix);
+ ChangeBrush(COL_TRANSPARENT, false);
+ nC=p->aPPoly.Count();
+ for (i=0; i<nC; i++)
+ {
+ if (i+1<nC || p->bClosed)
+ DrawPolygon( p->aPPoly.GetObject( i ) );
+ else
+ DrawPolyLine( p->aPPoly.GetObject( i ) );
+ }
+ }
+ break;
+ }
+ case GOrdSClPth: {
+ SAL_INFO("filter.os2met","GOrdSClPth");
+ sal_uInt32 nID(0);
+ OSPath * p=pPathList;
+ pOS2MET->SeekRel(2);
+ pOS2MET->ReadUInt32( nID );
+ if (nID==0) p=nullptr;
+ while (p!=nullptr && p->nID!=nID) p=p->pSucc;
+ if (p!=nullptr) pVirDev->SetClipRegion(vcl::Region(p->aPPoly));
+ else pVirDev->SetClipRegion();
+ break;
+ }
+ case GOrdNopNop:
+ break;
+ case GOrdRemark: SAL_INFO("filter.os2met","GOrdRemark");
+ break;
+ case GOrdSegLab: SAL_INFO("filter.os2met","GOrdSegLab");
+ break;
+
+ case GOrdBitBlt: ReadBitBlt(); break;
+
+ case GOrdCalSeg: SAL_INFO("filter.os2met","GOrdCalSeg");
+ break;
+ case GOrdSSgBnd: SAL_INFO("filter.os2met","GOrdSSgBnd");
+ break;
+ case GOrdSegChr: SAL_INFO("filter.os2met","GOrdSegChr");
+ break;
+ case GOrdCloFig:
+ CloseFigure();
+ break;
+ case GOrdEndSym: SAL_INFO("filter.os2met","GOrdEndSym");
+ break;
+ case GOrdEndPlg: SAL_INFO("filter.os2met","GOrdEndPlg");
+ break;
+ case GOrdEscape: SAL_INFO("filter.os2met","GOrdEscape");
+ break;
+ case GOrdExtEsc: SAL_INFO("filter.os2met","GOrdExtEsc");
+ break;
+
+ case GOrdPolygn: ReadPolygons(); break;
+
+ case GOrdStkPop: PopAttr(); break;
+
+ case GOrdPIvAtr: PushAttr(nOrderID);
+ [[fallthrough]];
+ case GOrdSIvAtr: {
+ sal_uInt8 nA(0), nP(0), nFlags(0);
+ Color aCol;
+ RasterOp eROP;
+ pOS2MET->ReadUChar( nA ).ReadUChar( nP ).ReadUChar( nFlags );
+ if (nOrderID==GOrdPIvAtr) {
+ pAttrStack->nIvAttrA=nA;
+ pAttrStack->nIvAttrP=nP;
+ }
+ if (nA<=2) {
+ if ((nFlags&0x80)!=0) {
+ if (nA==1) switch (nP) {
+ case 1: aAttr.aLinCol=aDefAttr.aLinCol; break;
+ case 2: aAttr.aChrCol=aDefAttr.aChrCol; break;
+ case 3: aAttr.aMrkCol=aDefAttr.aMrkCol; break;
+ case 4: aAttr.aPatCol=aDefAttr.aPatCol; break;
+ case 5: aAttr.aImgCol=aDefAttr.aImgCol; break;
+ }
+ else switch (nP) {
+ case 1: aAttr.aLinBgCol=aDefAttr.aLinBgCol; break;
+ case 2: aAttr.aChrBgCol=aDefAttr.aChrBgCol; break;
+ case 3: aAttr.aMrkBgCol=aDefAttr.aMrkBgCol; break;
+ case 4: aAttr.aPatBgCol=aDefAttr.aPatBgCol; break;
+ case 5: aAttr.aImgBgCol=aDefAttr.aImgBgCol; break;
+ }
+ }
+ else {
+ const auto nVal = ReadLittleEndian3BytesLong();
+ if ((nFlags&0x40)!=0 && nVal==1) aCol=COL_BLACK;
+ else if ((nFlags&0x40)!=0 && nVal==2) aCol=COL_WHITE;
+ else if ((nFlags&0x40)!=0 && nVal==4) aCol=COL_WHITE;
+ else if ((nFlags&0x40)!=0 && nVal==5) aCol=COL_BLACK;
+ else aCol=GetPaletteColor(nVal);
+ if (nA==1) switch (nP) {
+ case 1: aAttr.aLinCol=aCol; break;
+ case 2: aAttr.aChrCol=aCol; break;
+ case 3: aAttr.aMrkCol=aCol; break;
+ case 4: aAttr.aPatCol=aCol; break;
+ case 5: aAttr.aImgCol=aCol; break;
+ }
+ else switch (nP) {
+ case 1: aAttr.aLinBgCol=aCol; break;
+ case 2: aAttr.aChrBgCol=aCol; break;
+ case 3: aAttr.aMrkBgCol=aCol; break;
+ case 4: aAttr.aPatBgCol=aCol; break;
+ case 5: aAttr.aImgBgCol=aCol; break;
+ }
+ }
+ }
+ else {
+ sal_uInt8 nMix(0);
+ pOS2MET->ReadUChar( nMix );
+ if (nMix==0) {
+ switch (nP) {
+ case 1: aAttr.eLinBgMix=aDefAttr.eLinBgMix; break;
+ case 2: aAttr.eChrBgMix=aDefAttr.eChrBgMix; break;
+ case 3: aAttr.eMrkBgMix=aDefAttr.eMrkBgMix; break;
+ case 4: aAttr.ePatBgMix=aDefAttr.ePatBgMix; break;
+ case 5: aAttr.eImgBgMix=aDefAttr.eImgBgMix; break;
+ }
+ }
+ else {
+ eROP=OS2MixToRasterOp(nMix);
+ switch (nP) {
+ case 1: aAttr.eLinBgMix=eROP; break;
+ case 2: aAttr.eChrBgMix=eROP; break;
+ case 3: aAttr.eMrkBgMix=eROP; break;
+ case 4: aAttr.ePatBgMix=eROP; break;
+ case 5: aAttr.eImgBgMix=eROP; break;
+ }
+ }
+ }
+ break;
+ }
+ case GOrdPIxCol: PushAttr(nOrderID);
+ [[fallthrough]];
+ case GOrdSIxCol: {
+ sal_uInt8 nFlags(0);
+ pOS2MET->ReadUChar( nFlags );
+ if ((nFlags&0x80)!=0) {
+ aAttr.aLinCol=aDefAttr.aLinCol;
+ aAttr.aChrCol=aDefAttr.aChrCol;
+ aAttr.aMrkCol=aDefAttr.aMrkCol;
+ aAttr.aPatCol=aDefAttr.aPatCol;
+ aAttr.aImgCol=aDefAttr.aImgCol;
+ }
+ else {
+ Color aCol;
+ const auto nVal = ReadLittleEndian3BytesLong();
+ if ((nFlags&0x40)!=0 && nVal==1) aCol=COL_BLACK;
+ else if ((nFlags&0x40)!=0 && nVal==2) aCol=COL_WHITE;
+ else if ((nFlags&0x40)!=0 && nVal==4) aCol=COL_WHITE;
+ else if ((nFlags&0x40)!=0 && nVal==5) aCol=COL_BLACK;
+ else aCol=GetPaletteColor(nVal);
+ aAttr.aLinCol = aAttr.aChrCol = aAttr.aMrkCol = aAttr.aPatCol =
+ aAttr.aImgCol = aCol;
+ }
+ break;
+ }
+
+ case GOrdPColor:
+ case GOrdPXtCol: PushAttr(nOrderID);
+ [[fallthrough]];
+ case GOrdSColor:
+ case GOrdSXtCol: {
+ sal_uInt16 nVal(0);
+ if (nOrderID==GOrdPColor || nOrderID==GOrdSColor) {
+ sal_uInt8 nbyte(0);
+ pOS2MET->ReadUChar( nbyte ); nVal=static_cast<sal_uInt16>(nbyte)|0xff00;
+ }
+ else pOS2MET->ReadUInt16( nVal );
+ if (nVal==0x0000 || nVal==0xff00) {
+ aAttr.aLinCol=aDefAttr.aLinCol;
+ aAttr.aChrCol=aDefAttr.aChrCol;
+ aAttr.aMrkCol=aDefAttr.aMrkCol;
+ aAttr.aPatCol=aDefAttr.aPatCol;
+ aAttr.aImgCol=aDefAttr.aImgCol;
+ }
+ else {
+ Color aCol;
+ if (nVal==0x0007) aCol=COL_WHITE;
+ else if (nVal==0x0008) aCol=COL_BLACK;
+ else if (nVal==0xff08) aCol=GetPaletteColor(1);
+ else aCol=GetPaletteColor(static_cast<sal_uInt32>(nVal) & 0x000000ff);
+ aAttr.aLinCol = aAttr.aChrCol = aAttr.aMrkCol = aAttr.aPatCol =
+ aAttr.aImgCol = aCol;
+ }
+ break;
+ }
+
+ case GOrdPBgCol: PushAttr(nOrderID);
+ [[fallthrough]];
+ case GOrdSBgCol: {
+ sal_uInt16 nVal(0);
+ pOS2MET->ReadUInt16( nVal );
+ if (nVal==0x0000 || nVal==0xff00) {
+ aAttr.aLinBgCol=aDefAttr.aLinBgCol;
+ aAttr.aChrBgCol=aDefAttr.aChrBgCol;
+ aAttr.aMrkBgCol=aDefAttr.aMrkBgCol;
+ aAttr.aPatBgCol=aDefAttr.aPatBgCol;
+ aAttr.aImgBgCol=aDefAttr.aImgBgCol;
+ }
+ else {
+ Color aCol;
+ if (nVal==0x0007) aCol=COL_WHITE;
+ else if (nVal==0x0008) aCol=COL_BLACK;
+ else if (nVal==0xff08) aCol=GetPaletteColor(0);
+ else aCol=GetPaletteColor(static_cast<sal_uInt32>(nVal) & 0x000000ff);
+ aAttr.aLinBgCol = aAttr.aChrBgCol = aAttr.aMrkBgCol =
+ aAttr.aPatBgCol = aAttr.aImgBgCol = aCol;
+ }
+ break;
+ }
+ case GOrdPBxCol: PushAttr(nOrderID);
+ [[fallthrough]];
+ case GOrdSBxCol: {
+ sal_uInt8 nFlags(0);
+ pOS2MET->ReadUChar( nFlags );
+ if ((nFlags&0x80)!=0) {
+ aAttr.aLinBgCol=aDefAttr.aLinBgCol;
+ aAttr.aChrBgCol=aDefAttr.aChrBgCol;
+ aAttr.aMrkBgCol=aDefAttr.aMrkBgCol;
+ aAttr.aPatBgCol=aDefAttr.aPatBgCol;
+ aAttr.aImgBgCol=aDefAttr.aImgBgCol;
+ }
+ else {
+ Color aCol;
+ const auto nVal = ReadLittleEndian3BytesLong();
+ if ((nFlags&0x40)!=0 && nVal==1) aCol=COL_BLACK;
+ else if ((nFlags&0x40)!=0 && nVal==2) aCol=COL_WHITE;
+ else if ((nFlags&0x40)!=0 && nVal==4) aCol=COL_WHITE;
+ else if ((nFlags&0x40)!=0 && nVal==5) aCol=COL_BLACK;
+ else aCol=GetPaletteColor(nVal);
+ aAttr.aLinBgCol = aAttr.aChrBgCol = aAttr.aMrkBgCol =
+ aAttr.aPatBgCol = aAttr.aImgBgCol = aCol;
+ }
+ break;
+ }
+
+ case GOrdPMixMd: PushAttr(nOrderID);
+ [[fallthrough]];
+ case GOrdSMixMd: {
+ sal_uInt8 nMix(0);
+ pOS2MET->ReadUChar( nMix );
+ if (nMix==0) {
+ aAttr.eLinMix=aDefAttr.eLinMix;
+ aAttr.eChrMix=aDefAttr.eChrMix;
+ aAttr.eMrkMix=aDefAttr.eMrkMix;
+ aAttr.ePatMix=aDefAttr.ePatMix;
+ aAttr.eImgMix=aDefAttr.eImgMix;
+ }
+ else {
+ aAttr.eLinMix = aAttr.eChrMix = aAttr.eMrkMix =
+ aAttr.ePatMix = aAttr.eImgMix = OS2MixToRasterOp(nMix);
+ }
+ break;
+ }
+ case GOrdPBgMix: PushAttr(nOrderID);
+ [[fallthrough]];
+ case GOrdSBgMix: {
+ sal_uInt8 nMix(0);
+ pOS2MET->ReadUChar( nMix );
+ if (nMix==0) {
+ aAttr.eLinBgMix=aDefAttr.eLinBgMix;
+ aAttr.eChrBgMix=aDefAttr.eChrBgMix;
+ aAttr.eMrkBgMix=aDefAttr.eMrkBgMix;
+ aAttr.ePatBgMix=aDefAttr.ePatBgMix;
+ aAttr.eImgBgMix=aDefAttr.eImgBgMix;
+ }
+ else {
+ aAttr.eLinBgMix = aAttr.eChrBgMix = aAttr.eMrkBgMix =
+ aAttr.ePatBgMix = aAttr.eImgBgMix = OS2MixToRasterOp(nMix);
+ }
+ break;
+ }
+ case GOrdPPtSet: PushAttr(nOrderID);
+ [[fallthrough]];
+ case GOrdSPtSet: SAL_INFO("filter.os2met","GOrdSPtSet");
+ break;
+
+ case GOrdPPtSym: PushAttr(nOrderID);
+ [[fallthrough]];
+ case GOrdSPtSym: {
+ sal_uInt8 nPatt(0);
+ pOS2MET->ReadUChar( nPatt );
+ aAttr.bFill = ( nPatt != 0x0f );
+ break;
+ }
+
+ case GOrdPPtRef: PushAttr(nOrderID);
+ [[fallthrough]];
+ case GOrdSPtRef: SAL_INFO("filter.os2met","GOrdSPtRef");
+ break;
+
+ case GOrdPLnEnd: PushAttr(nOrderID);
+ [[fallthrough]];
+ case GOrdSLnEnd:
+ break;
+
+ case GOrdPLnJoi: PushAttr(nOrderID);
+ [[fallthrough]];
+ case GOrdSLnJoi:
+ break;
+
+ case GOrdPLnTyp: PushAttr(nOrderID);
+ [[fallthrough]];
+ case GOrdSLnTyp: {
+ sal_uInt8 nType(0);
+ pOS2MET->ReadUChar( nType );
+ switch (nType) {
+ case 0: aAttr.eLinStyle=aDefAttr.eLinStyle; break;
+ case 1: case 4: aAttr.eLinStyle=PEN_DOT; break;
+ case 2: case 5: aAttr.eLinStyle=PEN_DASH; break;
+ case 3: case 6: aAttr.eLinStyle=PEN_DASHDOT; break;
+ case 8: aAttr.eLinStyle=PEN_NULL; break;
+ default: aAttr.eLinStyle=PEN_SOLID;
+ }
+ break;
+ }
+ case GOrdPLnWdt: PushAttr(nOrderID);
+ [[fallthrough]];
+ case GOrdSLnWdt: {
+ sal_uInt8 nbyte(0);
+ pOS2MET->ReadUChar( nbyte );
+ if (nbyte==0) aAttr.nLinWidth=aDefAttr.nLinWidth;
+ else aAttr.nLinWidth=static_cast<sal_uInt16>(nbyte)-1;
+ break;
+ }
+ case GOrdPFrLWd: PushAttr(nOrderID);
+ [[fallthrough]];
+ case GOrdSFrLWd:
+ break;
+
+ case GOrdPStLWd: PushAttr(nOrderID);
+ [[fallthrough]];
+ case GOrdSStLWd :
+ {
+ sal_uInt8 nFlags(0);
+
+ pOS2MET->ReadUChar( nFlags );
+ if ( nFlags & 0x80 )
+ aAttr.nStrLinWidth = aDefAttr.nStrLinWidth;
+ else
+ {
+ pOS2MET->SeekRel( 1 );
+ sal_Int32 nWd = ReadCoord( bCoord32 );
+ if (nWd < 0)
+ nWd = o3tl::saturating_toggle_sign(nWd);
+ aAttr.nStrLinWidth = static_cast<sal_uInt16>(nWd);
+ }
+ break;
+ }
+ case GOrdPChDir: PushAttr(nOrderID);
+ [[fallthrough]];
+ case GOrdSChDir:
+ break;
+
+ case GOrdPChPrc: PushAttr(nOrderID);
+ [[fallthrough]];
+ case GOrdSChPrc:
+ break;
+
+ case GOrdPChSet: PushAttr(nOrderID);
+ [[fallthrough]];
+ case GOrdSChSet: {
+ sal_uInt8 nbyte(0);
+ pOS2MET->ReadUChar( nbyte );
+ aAttr.nChrSet=static_cast<sal_uInt32>(nbyte)&0xff;
+ break;
+ }
+ case GOrdPChAng: PushAttr(nOrderID);
+ [[fallthrough]];
+ case GOrdSChAng: {
+ sal_Int32 nX = ReadCoord(bCoord32);
+ sal_Int32 nY = ReadCoord(bCoord32);
+ if (nX>=0 && nY==0) aAttr.nChrAng=0_deg10;
+ else {
+ aAttr.nChrAng = Degree10(static_cast<short>(basegfx::rad2deg<10>(atan2(static_cast<double>(nY),static_cast<double>(nX)))));
+ while (aAttr.nChrAng < 0_deg10) aAttr.nChrAng += 3600_deg10;
+ aAttr.nChrAng %= 3600_deg10;
+ }
+ break;
+ }
+ case GOrdPChBrx: PushAttr(nOrderID);
+ [[fallthrough]];
+ case GOrdSChBrx:
+ break;
+
+ case GOrdPChCel: PushAttr(nOrderID);
+ [[fallthrough]];
+ case GOrdSChCel: {
+ sal_uInt16 nLen=nOrderLen;
+ (void) ReadCoord(bCoord32); // Width, unused
+ auto nHeight = ReadCoord(bCoord32);
+ if (nHeight < 0 || nHeight > SAL_MAX_INT16)
+ {
+ SAL_WARN("filter.os2met", "ignoring out of sane range font height: " << nHeight);
+ aAttr.nChrCellHeight = aDefAttr.nChrCellHeight;
+ }
+ else
+ aAttr.nChrCellHeight = nHeight;
+ if (bCoord32) nLen-=8; else nLen-=4;
+ if (nLen>=4) {
+ pOS2MET->SeekRel(4); nLen-=4;
+ }
+ if (nLen>=2) {
+ sal_uInt8 nbyte(0);
+ pOS2MET->ReadUChar( nbyte );
+ if ((nbyte&0x80)==0 && aAttr.nChrCellHeight == 0)
+ aAttr.nChrCellHeight = aDefAttr.nChrCellHeight;
+ }
+ break;
+ }
+ case GOrdPChXtr: PushAttr(nOrderID);
+ [[fallthrough]];
+ case GOrdSChXtr:
+ break;
+
+ case GOrdPChShr: PushAttr(nOrderID);
+ [[fallthrough]];
+ case GOrdSChShr:
+ break;
+
+ case GOrdPTxAlg: PushAttr(nOrderID);
+ [[fallthrough]];
+ case GOrdSTxAlg: SAL_INFO("filter.os2met","GOrdSTxAlg");
+ break;
+
+ case GOrdPMkPrc: PushAttr(nOrderID);
+ [[fallthrough]];
+ case GOrdSMkPrc: {
+ sal_uInt8 nbyte(0);
+ pOS2MET->ReadUChar( nbyte );
+ if (nbyte==0) aAttr.nMrkPrec=aDefAttr.nMrkPrec;
+ else aAttr.nMrkPrec=nbyte;
+ break;
+ }
+
+ case GOrdPMkSet: PushAttr(nOrderID);
+ [[fallthrough]];
+ case GOrdSMkSet: {
+ sal_uInt8 nbyte(0);
+ pOS2MET->ReadUChar( nbyte );
+ if (nbyte==0) aAttr.nMrkSet=aDefAttr.nMrkSet;
+ else aAttr.nMrkSet=nbyte;
+ break;
+ }
+
+ case GOrdPMkSym: PushAttr(nOrderID);
+ [[fallthrough]];
+ case GOrdSMkSym: {
+ sal_uInt8 nbyte(0);
+ pOS2MET->ReadUChar( nbyte );
+ if (nbyte==0) aAttr.nMrkSymbol=aDefAttr.nMrkSymbol;
+ else aAttr.nMrkSymbol=nbyte;
+ break;
+ }
+
+ case GOrdPMkCel: PushAttr(nOrderID);
+ [[fallthrough]];
+ case GOrdSMkCel: {
+ sal_uInt16 nLen=nOrderLen;
+ aAttr.aMrkCellSize.setWidth(ReadCoord(bCoord32) );
+ aAttr.aMrkCellSize.setHeight(ReadCoord(bCoord32) );
+ if (bCoord32) nLen-=8; else nLen-=4;
+ if (nLen>=2) {
+ sal_uInt8 nbyte(0);
+ pOS2MET->ReadUChar( nbyte );
+ if ((nbyte&0x80)==0 && aAttr.aMrkCellSize==Size(0,0))
+ aAttr.aMrkCellSize=aDefAttr.aMrkCellSize;
+ }
+ break;
+ }
+
+ case GOrdPArcPa: PushAttr(nOrderID);
+ [[fallthrough]];
+ case GOrdSArcPa:
+ aAttr.nArcP=ReadCoord(bCoord32);
+ aAttr.nArcQ=ReadCoord(bCoord32);
+ aAttr.nArcR=ReadCoord(bCoord32);
+ aAttr.nArcS=ReadCoord(bCoord32);
+ break;
+
+ case GOrdPCrPos: PushAttr(nOrderID);
+ [[fallthrough]];
+ case GOrdSCrPos:
+ aAttr.aCurPos=ReadPoint();
+ break;
+
+ case GOrdPMdTrn: PushAttr(nOrderID);
+ [[fallthrough]];
+ case GOrdSMdTrn: SAL_INFO("filter.os2met","GOrdSMdTrn");
+ break;
+
+ case GOrdPPkIdn: PushAttr(nOrderID);
+ [[fallthrough]];
+ case GOrdSPkIdn: SAL_INFO("filter.os2met","GOrdSPkIdn");
+ break;
+
+ case GOrdSVwTrn: SAL_INFO("filter.os2met","GOrdSVwTrn");
+ break;
+
+ case GOrdPVwWin: PushAttr(nOrderID);
+ [[fallthrough]];
+ case GOrdSVwWin: SAL_INFO("filter.os2met","GOrdSVwWin");
+ break;
+ default: SAL_INFO("filter.os2met","Unknown order: " << nOrderID);
+ }
+}
+
+void OS2METReader::ReadDsc(sal_uInt16 nDscID)
+{
+ switch (nDscID) {
+ case 0x00f7: { // 'Specify GVM Subset'
+ sal_uInt8 nbyte(0);
+ pOS2MET->SeekRel(6);
+ pOS2MET->ReadUChar( nbyte );
+ if (nbyte==0x05) bCoord32=true;
+ else if (nbyte==0x04) bCoord32=false;
+ else {
+ pOS2MET->SetError(SVSTREAM_FILEFORMAT_ERROR);
+ ErrorCode=1;
+ }
+ break;
+ }
+ case 0x00f6:
+ {
+ // 'Set Picture Descriptor'
+ bool b32;
+ sal_uInt8 nbyte(0), nUnitType(0);
+
+ pOS2MET->SeekRel(2);
+ pOS2MET->ReadUChar( nbyte );
+
+ if (nbyte==0x05)
+ b32=true;
+ else if(nbyte==0x04)
+ b32=false;
+ else
+ {
+ b32 = false; // -Wall added the case.
+ pOS2MET->SetError(SVSTREAM_FILEFORMAT_ERROR);
+ ErrorCode=2;
+ }
+
+ pOS2MET->ReadUChar( nUnitType );
+
+ sal_Int32 xr = ReadCoord(b32);
+ sal_Int32 yr = ReadCoord(b32);
+
+ ReadCoord(b32);
+
+ if (nUnitType==0x00 && xr>0 && yr>0)
+ aGlobMapMode=MapMode(MapUnit::MapInch,Point(0,0),Fraction(10,xr),Fraction(10,yr));
+ else if (nUnitType==0x01 && xr>0 && yr>0)
+ aGlobMapMode=MapMode(MapUnit::MapCM,Point(0,0),Fraction(10,xr),Fraction(10,yr));
+ else
+ aGlobMapMode=MapMode();
+
+ sal_Int32 x1 = ReadCoord(b32);
+ sal_Int32 x2 = ReadCoord(b32);
+ sal_Int32 y1 = ReadCoord(b32);
+ sal_Int32 y2 = ReadCoord(b32);
+
+ if (x1>x2)
+ {
+ const auto nt = x1;
+ x1=x2;
+ x2=nt;
+ }
+
+ if (y1>y2)
+ {
+ const auto nt = y1;
+ y1=y2;
+ y2=nt;
+ }
+
+ aBoundingRect.SetLeft( x1 );
+ aBoundingRect.SetRight( x2 );
+ aBoundingRect.SetTop( y1 );
+ aBoundingRect.SetBottom( y2 );
+
+ // no output beside this bounding rect
+ pVirDev->IntersectClipRegion( tools::Rectangle( Point(), aBoundingRect.GetSize() ) );
+
+ break;
+ }
+ case 0x0021: // 'Set Current Defaults'
+ break;
+ }
+}
+
+void OS2METReader::ReadImageData(sal_uInt16 nDataID, sal_uInt16 nDataLen)
+{
+ OSBitmap * p=pBitmapList; if (p==nullptr) return;
+
+ switch (nDataID) {
+
+ case 0x0070: // Begin Segment
+ break;
+
+ case 0x0091: // Begin Image Content
+ break;
+
+ case 0x0094: // Image Size
+ pOS2MET->SeekRel(5);
+ p->nHeight=ReadBigEndianWord();
+ p->nWidth=ReadBigEndianWord();
+ break;
+
+ case 0x0095: // Image Encoding
+ break;
+
+ case 0x0096: { // Image IDE-Size
+ sal_uInt8 nbyte(0);
+ pOS2MET->ReadUChar( nbyte ); p->nBitsPerPixel=nbyte;
+ break;
+ }
+
+ case 0x0097: // Image LUT-ID
+ break;
+
+ case 0x009b: // IDE Structure
+ break;
+
+ case 0xfe92: { // Image Data
+ // At the latest we now need the temporary BMP file and
+ // inside this file we need the header and the palette.
+ if (p->pBMP==nullptr) {
+ p->pBMP=new SvMemoryStream();
+ p->pBMP->SetEndian(SvStreamEndian::LITTLE);
+ if (p->nWidth==0 || p->nHeight==0 || p->nBitsPerPixel==0) {
+ pOS2MET->SetError(SVSTREAM_FILEFORMAT_ERROR);
+ ErrorCode=3;
+ return;
+ }
+ // write (Windows-)BITMAPINFOHEADER:
+ p->pBMP->WriteUInt32( 40 ).WriteUInt32( p->nWidth ).WriteUInt32( p->nHeight );
+ p->pBMP->WriteUInt16( 1 ).WriteUInt16( p->nBitsPerPixel );
+ p->pBMP->WriteUInt32( 0 ).WriteUInt32( 0 ).WriteUInt32( 0 ).WriteUInt32( 0 );
+ p->pBMP->WriteUInt32( 0 ).WriteUInt32( 0 );
+ // write color table:
+ if (p->nBitsPerPixel<=8) {
+ sal_uInt16 i, nColTabSize=1<<(p->nBitsPerPixel);
+ for (i=0; i<nColTabSize; i++) p->pBMP->WriteUInt32( GetPalette0RGB(i) );
+ }
+ }
+ // OK, now the map data is being pushed. Unfortunately OS2 and BMP
+ // do have a different RGB ordering when using 24-bit
+ std::unique_ptr<sal_uInt8[]> pBuf(new sal_uInt8[nDataLen]);
+ pOS2MET->ReadBytes(pBuf.get(), nDataLen);
+ sal_uInt32 nBytesPerLineToSwap = (p->nBitsPerPixel == 24) ?
+ ((p->nWidth * 3 + 3) & 0xfffffffc) : 0;
+ if (nBytesPerLineToSwap) {
+ sal_uInt32 nAlign = p->nMapPos - (p->nMapPos % nBytesPerLineToSwap);
+ sal_uInt32 i=0;
+ while (nAlign+i+2<p->nMapPos+nDataLen) {
+ if (nAlign+i>=p->nMapPos) {
+ sal_uInt32 j = nAlign + i - p->nMapPos;
+ std::swap(pBuf[j], pBuf[j+2]);
+ }
+ i+=3;
+ if (i + 2 >= nBytesPerLineToSwap) {
+ nAlign += nBytesPerLineToSwap;
+ i=0;
+ }
+ }
+ }
+ p->pBMP->WriteBytes(pBuf.get(), nDataLen);
+ p->nMapPos+=nDataLen;
+ break;
+ }
+ case 0x0093: // End Image Content
+ break;
+
+ case 0x0071: // End Segment
+ break;
+ }
+}
+
+void OS2METReader::ReadFont(sal_uInt16 nFieldSize)
+{
+ OSFont * pF=new OSFont;
+ pF->pSucc=pFontList; pFontList=pF;
+ pF->nID=0;
+ pF->aFont.SetTransparent(true);
+ pF->aFont.SetAlignment(ALIGN_BASELINE);
+
+ auto nPos=pOS2MET->Tell();
+ auto nMaxPos = nPos + nFieldSize;
+ pOS2MET->SeekRel(2); nPos+=2;
+ while (nPos<nMaxPos && pOS2MET->good()) {
+ sal_uInt8 nByte(0);
+ pOS2MET->ReadUChar(nByte);
+ sal_uInt16 nLen = static_cast<sal_uInt16>(nByte) & 0x00ff;
+ if (nLen == 0)
+ {
+ pOS2MET->SetError(SVSTREAM_FILEFORMAT_ERROR);
+ ErrorCode=4;
+ }
+ sal_uInt8 nTripType(0);
+ pOS2MET->ReadUChar( nTripType );
+ switch (nTripType) {
+ case 0x02:
+ {
+ sal_uInt8 nTripType2(0);
+ pOS2MET->ReadUChar( nTripType2 );
+ switch (nTripType2) {
+ case 0x84: // Font name
+ break;
+ case 0x08: { // Font Typeface
+ char str[33];
+ pOS2MET->SeekRel(1);
+ str[pOS2MET->ReadBytes(str, 32)] = 0;
+ OUString aStr( str, strlen(str), osl_getThreadTextEncoding() );
+ if ( aStr.equalsIgnoreAsciiCase( "Helv" ) )
+ aStr = "Helvetica";
+ pF->aFont.SetFamilyName( aStr );
+ break;
+ }
+ }
+ break;
+ }
+ case 0x24: // Icid
+ {
+ sal_uInt8 nTripType2(0);
+ pOS2MET->ReadUChar( nTripType2 );
+ switch (nTripType2) {
+ case 0x05: //Icid
+ pOS2MET->ReadUChar( nByte );
+ pF->nID=static_cast<sal_uInt32>(nByte)&0xff;
+ break;
+ }
+ break;
+ }
+ case 0x20: // Font Binary GCID
+ break;
+ case 0x1f: { // Font Attributes
+ FontWeight eWeight;
+ sal_uInt8 nbyte(0);
+ pOS2MET->ReadUChar( nbyte );
+ switch (nbyte) {
+ case 1: eWeight=WEIGHT_THIN; break;
+ case 2: eWeight=WEIGHT_ULTRALIGHT; break;
+ case 3: eWeight=WEIGHT_LIGHT; break;
+ case 4: eWeight=WEIGHT_SEMILIGHT; break;
+ case 5: eWeight=WEIGHT_NORMAL; break;
+ case 6: eWeight=WEIGHT_SEMIBOLD; break;
+ case 7: eWeight=WEIGHT_BOLD; break;
+ case 8: eWeight=WEIGHT_ULTRABOLD; break;
+ case 9: eWeight=WEIGHT_BLACK; break;
+ default: eWeight=WEIGHT_DONTKNOW;
+ }
+ pF->aFont.SetWeight(eWeight);
+ break;
+ }
+ }
+ nPos+=nLen;
+ pOS2MET->Seek(nPos);
+ }
+}
+
+void OS2METReader::ReadField(sal_uInt16 nFieldType, sal_uInt16 nFieldSize)
+{
+ switch (nFieldType) {
+ case BegDocumnMagic:
+ break;
+ case EndDocumnMagic:
+ break;
+ case BegResGrpMagic:
+ break;
+ case EndResGrpMagic:
+ break;
+ case BegColAtrMagic:
+ break;
+ case EndColAtrMagic:
+ break;
+ case BlkColAtrMagic: {
+ sal_uInt8 nbyte;
+ sal_uInt16 nStartIndex, nEndIndex, i, nElemLen, nBytesPerCol;
+
+ auto nPos = pOS2MET->Tell();
+ auto nMaxPos = nPos + nFieldSize;
+ pOS2MET->SeekRel(3); nPos+=3;
+ while (nPos<nMaxPos && pOS2MET->GetError()==ERRCODE_NONE) {
+ pOS2MET->ReadUChar( nbyte ); nElemLen=static_cast<sal_uInt16>(nbyte) & 0x00ff;
+ if (nElemLen>11) {
+ pOS2MET->SeekRel(4);
+ nStartIndex=ReadBigEndianWord();
+ pOS2MET->SeekRel(3);
+ pOS2MET->ReadUChar( nbyte );
+ nBytesPerCol=static_cast<sal_uInt16>(nbyte) & 0x00ff;
+ if (nBytesPerCol == 0)
+ {
+ pOS2MET->SetError(SVSTREAM_FILEFORMAT_ERROR);
+ ErrorCode=4;
+ break;
+ }
+ nEndIndex=nStartIndex+(nElemLen-11)/nBytesPerCol;
+ for (i=nStartIndex; i<nEndIndex; i++) {
+ if (nBytesPerCol > 3) pOS2MET->SeekRel(nBytesPerCol-3);
+ auto nCol = ReadBigEndian3BytesLong();
+ SetPalette0RGB(i, nCol);
+ }
+ }
+ else if (nElemLen<10) {
+ pOS2MET->SetError(SVSTREAM_FILEFORMAT_ERROR);
+ ErrorCode=4;
+ }
+ nPos += nElemLen;
+ pOS2MET->Seek(nPos);
+ }
+ break;
+ }
+ case MapColAtrMagic:
+ break;
+ case BegImgObjMagic: {
+ // create new bitmap by now: (will be filled later)
+ OSBitmap * pB=new OSBitmap;
+ pB->pSucc=pBitmapList; pBitmapList=pB;
+ pB->pBMP=nullptr; pB->nWidth=0; pB->nHeight=0; pB->nBitsPerPixel=0;
+ pB->nMapPos=0;
+ // determine ID of the bitmap:
+ pB->nID=0;
+ for (sal_uInt8 i = 0; i < 4; ++i) {
+ sal_uInt8 nbyte(0),nbyte2(0);
+ pOS2MET->ReadUChar(nbyte).ReadUChar(nbyte2);
+ nbyte -= 0x30;
+ nbyte2 -= 0x30;
+ nbyte = (nbyte << 4) | nbyte2;
+ pB->nID=(pB->nID>>8)|(static_cast<sal_uInt32>(nbyte)<<24);
+ }
+ // put new palette on the palette stack: (will be filled later)
+ OSPalette * pP=new OSPalette;
+ pP->pSucc=pPaletteStack; pPaletteStack=pP;
+ pP->p0RGB=nullptr; pP->nSize=0;
+ break;
+ }
+ case EndImgObjMagic: {
+ // read temporary Windows BMP file:
+ if (pBitmapList==nullptr || pBitmapList->pBMP==nullptr ||
+ pBitmapList->pBMP->GetError()!=ERRCODE_NONE) {
+ pOS2MET->SetError(SVSTREAM_FILEFORMAT_ERROR);
+ ErrorCode=5;
+ return;
+ }
+ pBitmapList->pBMP->Seek(0);
+
+ ReadDIBBitmapEx(pBitmapList->aBitmapEx, *(pBitmapList->pBMP), false);
+
+ if (pBitmapList->pBMP->GetError()!=ERRCODE_NONE) {
+ pOS2MET->SetError(SVSTREAM_FILEFORMAT_ERROR);
+ ErrorCode=6;
+ }
+ delete pBitmapList->pBMP; pBitmapList->pBMP=nullptr;
+ // kill palette from stack:
+ OSPalette * pP=pPaletteStack;
+ if (pP!=nullptr) {
+ pPaletteStack=pP->pSucc;
+ delete[] pP->p0RGB;
+ delete pP;
+ }
+ break;
+ }
+ case DscImgObjMagic:
+ break;
+ case DatImgObjMagic: {
+ sal_uInt16 nDataID, nDataLen;
+ sal_uInt8 nbyte;
+
+ auto nPos = pOS2MET->Tell();
+ auto nMaxPos = nPos + nFieldSize;
+ while (nPos<nMaxPos && pOS2MET->GetError()==ERRCODE_NONE) {
+ pOS2MET->ReadUChar( nbyte ); nDataID=static_cast<sal_uInt16>(nbyte)&0x00ff;
+ if (nDataID==0x00fe) {
+ pOS2MET->ReadUChar( nbyte );
+ nDataID=(nDataID<<8)|(static_cast<sal_uInt16>(nbyte)&0x00ff);
+ nDataLen=ReadBigEndianWord();
+ nPos+=4;
+ }
+ else {
+ pOS2MET->ReadUChar( nbyte ); nDataLen=static_cast<sal_uInt16>(nbyte)&0x00ff;
+ nPos+=2;
+ }
+ ReadImageData(nDataID, nDataLen);
+ nPos += nDataLen;
+ pOS2MET->Seek(nPos);
+ }
+ break;
+ }
+
+ case BegObEnv1Magic:
+ break;
+ case EndObEnv1Magic:
+ break;
+ case BegGrfObjMagic:
+ break;
+ case EndGrfObjMagic: {
+ if (!xOrdFile)
+ break;
+
+ auto nMaxPos = xOrdFile->Tell();
+ if (!nMaxPos)
+ break;
+
+ // In xOrdFile all "DatGrfObj" fields were collected so that the
+ // therein contained "Orders" are continuous and not segmented by fields.
+ // To read them from the memory stream without having any trouble,
+ // we use a little trick:
+
+ SvStream *pSave = pOS2MET;
+ pOS2MET=xOrdFile.get(); //(!)
+ pOS2MET->Seek(0);
+
+ // in a sane world this block is just: pOS2MET->SetStreamSize(nMaxPos);
+ if (nMaxPos)
+ {
+#ifndef NDEBUG
+ const sal_uInt8 nLastByte = static_cast<const sal_uInt8*>(xOrdFile->GetData())[nMaxPos-1];
+#endif
+ pOS2MET->SetStreamSize(nMaxPos); // shrink stream to written portion
+ assert(pOS2MET->remainingSize() == nMaxPos || pOS2MET->remainingSize() == nMaxPos - 1);
+ SAL_WARN_IF(pOS2MET->remainingSize() == nMaxPos, "filter.os2met", "this SetStreamSize workaround is no longer needed");
+ // The shrink case of SvMemoryStream::ReAllocateMemory, i.e. nEndOfData = nNewSize - 1, looks buggy to me, workaround
+ // it by using Seek to move the nEndOfData to the sane position
+ if (pOS2MET->remainingSize() < nMaxPos)
+ {
+ pOS2MET->Seek(nMaxPos);
+ pOS2MET->Seek(0);
+ }
+
+ assert(nLastByte == static_cast<const sal_uInt8*>(xOrdFile->GetData())[nMaxPos-1]);
+ }
+
+ assert(pOS2MET->remainingSize() == nMaxPos);
+
+ // disable stream growing past its current size
+ xOrdFile->SetResizeOffset(0);
+
+
+ // "Segment header":
+ sal_uInt8 nbyte(0);
+ pOS2MET->ReadUChar( nbyte );
+ if (nbyte==0x70) { // header exists
+ pOS2MET->SeekRel(15); // but we don't need it
+ }
+ else pOS2MET->SeekRel(-1); // no header, go back one byte
+
+ // loop through Order:
+ while (pOS2MET->Tell()<nMaxPos && pOS2MET->GetError()==ERRCODE_NONE) {
+ pOS2MET->ReadUChar( nbyte );
+ sal_uInt16 nOrderID = static_cast<sal_uInt16>(nbyte) & 0x00ff;
+ if (nOrderID==0x00fe) {
+ pOS2MET->ReadUChar( nbyte );
+ nOrderID=(nOrderID << 8) | (static_cast<sal_uInt16>(nbyte) & 0x00ff);
+ }
+ sal_uInt16 nOrderLen;
+ if (nOrderID>0x00ff || nOrderID==GOrdPolygn) {
+ // ooo: As written in OS2 documentation, the order length should now
+ // be written as big endian word. (Quote: "Highorder byte precedes loworder byte").
+ // In reality there are files in which the length is stored as little endian word
+ // (at least for nOrderID==GOrdPolygn)
+ // So we throw a coin or what else can we do?
+ pOS2MET->ReadUChar( nbyte ); nOrderLen=static_cast<sal_uInt16>(nbyte)&0x00ff;
+ pOS2MET->ReadUChar( nbyte ); if (nbyte!=0) nOrderLen=nOrderLen<<8|(static_cast<sal_uInt16>(nbyte)&0x00ff);
+ }
+ else if (nOrderID==GOrdSTxAlg || nOrderID==GOrdPTxAlg) nOrderLen=2;
+ else if ((nOrderID&0xff88)==0x0008) nOrderLen=1;
+ else if (nOrderID==0x0000 || nOrderID==0x00ff) nOrderLen=0;
+ else { pOS2MET->ReadUChar( nbyte ); nOrderLen=static_cast<sal_uInt16>(nbyte) & 0x00ff; }
+ auto nPos=pOS2MET->Tell();
+ ReadOrder(nOrderID, nOrderLen);
+ if (nPos+nOrderLen < pOS2MET->Tell()) {
+ SAL_INFO("filter.os2met","Order is shorter than expected. OrderID: " << nOrderID << " Position: " << nPos);
+ }
+ else if (nPos+nOrderLen != pOS2MET->Tell()) {
+ SAL_INFO("filter.os2met","Order was not read completely. OrderID: " << nOrderID << " Position: " << nPos);
+ }
+ pOS2MET->Seek(nPos+nOrderLen);
+ }
+
+ pOS2MET=pSave;
+ if (xOrdFile->GetError()) {
+ pOS2MET->SetError(SVSTREAM_FILEFORMAT_ERROR);
+ ErrorCode=10;
+ }
+ xOrdFile.reset();
+ break;
+ }
+ case DscGrfObjMagic: {
+ sal_uInt16 nDscID, nDscLen;
+ sal_uInt8 nbyte;
+
+ auto nMaxPos = pOS2MET->Tell() + nFieldSize;
+ while (pOS2MET->Tell()<nMaxPos && pOS2MET->GetError()==ERRCODE_NONE) {
+ pOS2MET->ReadUChar( nbyte ); nDscID =static_cast<sal_uInt16>(nbyte) & 0x00ff;
+ pOS2MET->ReadUChar( nbyte ); nDscLen=static_cast<sal_uInt16>(nbyte) & 0x00ff;
+ auto nPos = pOS2MET->Tell();
+ ReadDsc(nDscID);
+ pOS2MET->Seek(nPos+nDscLen);
+ }
+ break;
+ }
+ case DatGrfObjMagic: {
+ if (!xOrdFile) {
+ xOrdFile.reset(new SvMemoryStream);
+ xOrdFile->SetEndian(SvStreamEndian::LITTLE);
+ }
+ std::unique_ptr<sal_uInt8[]> pBuf(new sal_uInt8[nFieldSize]);
+ pOS2MET->ReadBytes(pBuf.get(), nFieldSize);
+ xOrdFile->WriteBytes(pBuf.get(), nFieldSize);
+ break;
+ }
+ case MapCodFntMagic:
+ ReadFont(nFieldSize);
+ break;
+
+ case MapDatResMagic:
+ break;
+ }
+}
+
+void OS2METReader::ReadOS2MET( SvStream & rStreamOS2MET, GDIMetaFile & rGDIMetaFile )
+{
+ ErrorCode=0;
+
+ pOS2MET = &rStreamOS2MET;
+ auto nOrigPos = pOS2MET->Tell();
+ SvStreamEndian nOrigNumberFormat = pOS2MET->GetEndian();
+
+ bCoord32 = true;
+ pPaletteStack=nullptr;
+ pAreaStack=nullptr;
+ pPathStack=nullptr;
+ pPathList=nullptr;
+ pFontList=nullptr;
+ pBitmapList=nullptr;
+ pAttrStack=nullptr;
+
+ aDefAttr.aLinCol =COL_BLACK;
+ aDefAttr.aLinBgCol =COL_WHITE;
+ aDefAttr.eLinMix =RasterOp::OverPaint;
+ aDefAttr.eLinBgMix =RasterOp::OverPaint;
+ aDefAttr.aChrCol =COL_BLACK;
+ aDefAttr.aChrBgCol =COL_WHITE;
+ aDefAttr.eChrMix =RasterOp::OverPaint;
+ aDefAttr.eChrBgMix =RasterOp::OverPaint;
+ aDefAttr.aMrkCol =COL_BLACK;
+ aDefAttr.aMrkBgCol =COL_WHITE;
+ aDefAttr.eMrkMix =RasterOp::OverPaint;
+ aDefAttr.eMrkBgMix =RasterOp::OverPaint;
+ aDefAttr.aPatCol =COL_BLACK;
+ aDefAttr.aPatBgCol =COL_WHITE;
+ aDefAttr.ePatMix =RasterOp::OverPaint;
+ aDefAttr.ePatBgMix =RasterOp::OverPaint;
+ aDefAttr.aImgCol =COL_BLACK;
+ aDefAttr.aImgBgCol =COL_WHITE;
+ aDefAttr.eImgMix =RasterOp::OverPaint;
+ aDefAttr.eImgBgMix =RasterOp::OverPaint;
+ aDefAttr.nArcP =1;
+ aDefAttr.nArcQ =1;
+ aDefAttr.nArcR =0;
+ aDefAttr.nArcS =0;
+ aDefAttr.nChrAng =0_deg10;
+ aDefAttr.nChrCellHeight = 12;
+ aDefAttr.nChrSet =0;
+ aDefAttr.aCurPos =Point(0,0);
+ aDefAttr.eLinStyle =PEN_SOLID;
+ aDefAttr.nLinWidth =0;
+ aDefAttr.aMrkCellSize=Size(10,10);
+ aDefAttr.nMrkPrec =0x01;
+ aDefAttr.nMrkSet =0xff;
+ aDefAttr.nMrkSymbol =0x01;
+ aDefAttr.bFill =true;
+ aDefAttr.nStrLinWidth=0;
+
+ aAttr=aDefAttr;
+
+ xOrdFile.reset();
+
+ rGDIMetaFile.Record(pVirDev);
+
+ pOS2MET->SetEndian(SvStreamEndian::LITTLE);
+
+ sal_uInt64 nPos = pOS2MET->Tell();
+
+ for (;;) {
+
+ sal_uInt16 nFieldSize = ReadBigEndianWord();
+ sal_uInt8 nMagicByte(0);
+ pOS2MET->ReadUChar( nMagicByte );
+ if (nMagicByte!=0xd3) {
+ pOS2MET->SetError(SVSTREAM_FILEFORMAT_ERROR);
+ ErrorCode=7;
+ break;
+ }
+
+ sal_uInt16 nFieldType(0);
+ pOS2MET->ReadUInt16(nFieldType);
+
+ pOS2MET->SeekRel(3);
+
+ if (pOS2MET->GetError())
+ break;
+
+ if (nFieldType==EndDocumnMagic)
+ break;
+
+ if (pOS2MET->eof() || nFieldSize < 8)
+ {
+ pOS2MET->SetError(SVSTREAM_FILEFORMAT_ERROR);
+ ErrorCode=8;
+ break;
+ }
+
+ nPos+=8; nFieldSize-=8;
+
+ if (nFieldSize > pOS2MET->remainingSize())
+ {
+ pOS2MET->SetError(SVSTREAM_FILEFORMAT_ERROR);
+ ErrorCode=8;
+ break;
+ }
+
+ ReadField(nFieldType, nFieldSize);
+ nPos += nFieldSize;
+
+ if (pOS2MET->Tell() > nPos)
+ {
+ pOS2MET->SetError(SVSTREAM_FILEFORMAT_ERROR);
+ ErrorCode=9;
+ break;
+ }
+ pOS2MET->Seek(nPos);
+ }
+
+ rGDIMetaFile.Stop();
+
+ rGDIMetaFile.SetPrefMapMode( aGlobMapMode );
+
+ if( aBoundingRect.GetWidth() && aBoundingRect.GetHeight() )
+ rGDIMetaFile.SetPrefSize( aBoundingRect.GetSize() );
+ else
+ {
+ if( aCalcBndRect.Left() || aCalcBndRect.Top() )
+ rGDIMetaFile.Move( -aCalcBndRect.Left(), -aCalcBndRect.Top() );
+
+ rGDIMetaFile.SetPrefSize( aCalcBndRect.GetSize() );
+ }
+
+ pOS2MET->SetEndian(nOrigNumberFormat);
+
+ if (pOS2MET->GetError()) {
+ SAL_INFO("filter.os2met","Error code: " << ErrorCode);
+ pOS2MET->Seek(nOrigPos);
+ }
+}
+
+//================== GraphicImport - the exported function ================
+
+bool ImportMetGraphic(SvStream & rStream, Graphic & rGraphic)
+{
+ OS2METReader aOS2METReader;
+ GDIMetaFile aMTF;
+ bool bRet = false;
+
+ try
+ {
+ aOS2METReader.ReadOS2MET( rStream, aMTF );
+
+ if ( !rStream.GetError() )
+ {
+ rGraphic=Graphic( aMTF );
+ bRet = true;
+ }
+ }
+ catch (const css::uno::Exception&)
+ {
+ }
+
+ return bRet;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/vcl/source/filter/ipbm/ipbm.cxx b/vcl/source/filter/ipbm/ipbm.cxx
new file mode 100644
index 000000000..37736a48f
--- /dev/null
+++ b/vcl/source/filter/ipbm/ipbm.cxx
@@ -0,0 +1,541 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <sal/config.h>
+#include <o3tl/safeint.hxx>
+#include <vcl/FilterConfigItem.hxx>
+#include <vcl/graph.hxx>
+#include <vcl/BitmapTools.hxx>
+#include <tools/stream.hxx>
+#include <filter/PbmReader.hxx>
+
+//============================ PBMReader ==================================
+
+namespace {
+
+class PBMReader {
+
+private:
+
+ SvStream& mrPBM; // the PBM file to read
+
+ bool mbStatus;
+ bool mbRemark; // sal_False if the stream is in a comment
+ bool mbRaw; // RAW/ASCII MODE
+ sal_uInt8 mnMode; // 0->PBM, 1->PGM, 2->PPM
+ std::unique_ptr<vcl::bitmap::RawBitmap> mpRawBmp;
+ std::vector<Color> mvPalette;
+ sal_Int32 mnWidth, mnHeight; // dimensions in pixel
+ sal_uInt16 mnCol;
+ sal_uInt64 mnMaxVal; // max value in the <missing comment>
+ bool ImplReadBody();
+ bool ImplReadHeader();
+
+public:
+ explicit PBMReader(SvStream & rPBM);
+ bool ReadPBM(Graphic & rGraphic );
+};
+
+}
+
+//=================== Methods of PBMReader ==============================
+
+PBMReader::PBMReader(SvStream & rPBM)
+ : mrPBM(rPBM)
+ , mbStatus(true)
+ , mbRemark(false)
+ , mbRaw(true)
+ , mnMode(0)
+ , mnWidth(0)
+ , mnHeight(0)
+ , mnCol(0)
+ , mnMaxVal(0)
+{
+}
+
+bool PBMReader::ReadPBM(Graphic & rGraphic )
+{
+ if ( mrPBM.GetError() )
+ return false;
+
+ mrPBM.SetEndian( SvStreamEndian::LITTLE );
+
+ // read header:
+
+ mbStatus = ImplReadHeader();
+ if ( !mbStatus )
+ return false;
+
+ if ( ( mnMaxVal == 0 ) || ( mnWidth <= 0 ) || ( mnHeight <= 0 ) )
+ return false;
+
+ sal_uInt32 nPixelsRequired;
+ if (o3tl::checked_multiply<sal_uInt32>(mnWidth, mnHeight, nPixelsRequired))
+ return false;
+ const auto nRemainingSize = mrPBM.remainingSize();
+
+ // 0->PBM, 1->PGM, 2->PPM
+ switch ( mnMode )
+ {
+ case 0:
+ {
+ if (nRemainingSize < nPixelsRequired / 8)
+ return false;
+
+ mpRawBmp.reset( new vcl::bitmap::RawBitmap( Size( mnWidth, mnHeight ), 24 ) );
+ mvPalette.resize( 2 );
+ mvPalette[0] = Color( 0xff, 0xff, 0xff );
+ mvPalette[1] = Color( 0x00, 0x00, 0x00 );
+ break;
+ }
+ case 1 :
+ if (nRemainingSize < nPixelsRequired)
+ return false;
+
+ mpRawBmp.reset( new vcl::bitmap::RawBitmap( Size( mnWidth, mnHeight ), 24 ) );
+ mnCol = static_cast<sal_uInt16>(mnMaxVal) + 1;
+ if ( mnCol > 256 )
+ mnCol = 256;
+
+ mvPalette.resize( 256 );
+ for ( sal_uInt16 i = 0; i < mnCol; i++ )
+ {
+ sal_uInt16 nCount = 255 * i / mnCol;
+ mvPalette[i] = Color( static_cast<sal_uInt8>(nCount), static_cast<sal_uInt8>(nCount), static_cast<sal_uInt8>(nCount) );
+ }
+ break;
+ case 2 :
+ if (nRemainingSize / 3 < nPixelsRequired)
+ return false;
+
+ mpRawBmp.reset( new vcl::bitmap::RawBitmap( Size( mnWidth, mnHeight ), 24 ) );
+ break;
+ }
+
+ // read bitmap data
+ mbStatus = ImplReadBody();
+
+ if ( mbStatus )
+ rGraphic = vcl::bitmap::CreateFromData(std::move(*mpRawBmp));
+
+ return mbStatus;
+}
+
+bool PBMReader::ImplReadHeader()
+{
+ sal_uInt8 nID[ 2 ];
+ sal_uInt8 nDat;
+ sal_uInt8 nMax, nCount = 0;
+ bool bFinished = false;
+
+ mrPBM.ReadUChar( nID[ 0 ] ).ReadUChar( nID[ 1 ] );
+ if (!mrPBM.good() || nID[0] != 'P')
+ return false;
+ mnMaxVal = mnWidth = mnHeight = 0;
+ switch ( nID[ 1 ] )
+ {
+ case '1' :
+ mbRaw = false;
+ [[fallthrough]];
+ case '4' :
+ mnMode = 0;
+ nMax = 2; // number of parameters in Header
+ mnMaxVal = 1;
+ break;
+ case '2' :
+ mbRaw = false;
+ [[fallthrough]];
+ case '5' :
+ mnMode = 1;
+ nMax = 3;
+ break;
+ case '3' :
+ mbRaw = false;
+ [[fallthrough]];
+ case '6' :
+ mnMode = 2;
+ nMax = 3;
+ break;
+ default:
+ return false;
+ }
+ while ( !bFinished )
+ {
+ mrPBM.ReadUChar( nDat );
+
+ if (!mrPBM.good())
+ return false;
+
+ if ( nDat == '#' )
+ {
+ mbRemark = true;
+ continue;
+ }
+ else if ( ( nDat == 0x0d ) || ( nDat == 0x0a ) )
+ {
+ mbRemark = false;
+ nDat = 0x20;
+ }
+ if ( mbRemark )
+ continue;
+
+ if ( ( nDat == 0x20 ) || ( nDat == 0x09 ) )
+ {
+ if ( ( nCount == 0 ) && mnWidth )
+ nCount++;
+ else if ( ( nCount == 1 ) && mnHeight )
+ {
+ if ( ++nCount == nMax )
+ bFinished = true;
+ }
+ else if ( ( nCount == 2 ) && mnMaxVal )
+ {
+ bFinished = true;
+ }
+ continue;
+ }
+ if ( ( nDat >= '0' ) && ( nDat <= '9' ) )
+ {
+ nDat -= '0';
+ if ( nCount == 0 )
+ {
+ if (mnWidth > SAL_MAX_INT32 / 10)
+ {
+ return false;
+ }
+ mnWidth *= 10;
+ if (nDat > SAL_MAX_INT32 - mnWidth)
+ {
+ return false;
+ }
+ mnWidth += nDat;
+ }
+ else if ( nCount == 1 )
+ {
+ if (mnHeight > SAL_MAX_INT32 / 10)
+ {
+ return false;
+ }
+ mnHeight *= 10;
+ if (nDat > SAL_MAX_INT32 - mnHeight)
+ {
+ return false;
+ }
+ mnHeight += nDat;
+ }
+ else if ( nCount == 2 )
+ {
+ if (mnMaxVal > std::numeric_limits<sal_uInt64>::max() / 10)
+ {
+ return false;
+ }
+ mnMaxVal *= 10;
+ if (nDat > std::numeric_limits<sal_uInt64>::max() - mnMaxVal)
+ {
+ return false;
+ }
+ mnMaxVal += nDat;
+ }
+ }
+ else
+ return false;
+ }
+ return mbStatus;
+}
+
+bool PBMReader::ImplReadBody()
+{
+ sal_uInt8 nDat = 0, nCount;
+ sal_uInt64 nGrey, nRGB[3];
+ sal_Int32 nWidth = 0;
+ sal_Int32 nHeight = 0;
+
+ if ( mbRaw )
+ {
+ signed char nShift = 0;
+ switch ( mnMode )
+ {
+
+ // PBM
+ case 0 :
+ while ( nHeight != mnHeight )
+ {
+ if (!mrPBM.good())
+ return false;
+
+ if ( --nShift < 0 )
+ {
+ mrPBM.ReadUChar( nDat );
+ nShift = 7;
+ }
+ mpRawBmp->SetPixel( nHeight, nWidth, mvPalette[(nDat >> nShift) & 0x01] );
+ if ( ++nWidth == mnWidth )
+ {
+ nShift = 0;
+ nWidth = 0;
+ nHeight++;
+ }
+ }
+ break;
+
+ // PGM
+ case 1 :
+ while ( nHeight != mnHeight )
+ {
+ if (!mrPBM.good())
+ return false;
+
+ mrPBM.ReadUChar( nDat );
+ mpRawBmp->SetPixel( nHeight, nWidth++, mvPalette[nDat]);
+
+ if ( nWidth == mnWidth )
+ {
+ nWidth = 0;
+ nHeight++;
+ }
+ }
+ break;
+
+ // PPM
+ case 2 :
+ while ( nHeight != mnHeight )
+ {
+ if (!mrPBM.good())
+ return false;
+
+ sal_uInt8 nR, nG, nB;
+ sal_uInt8 nRed, nGreen, nBlue;
+ mrPBM.ReadUChar( nR ).ReadUChar( nG ).ReadUChar( nB );
+ nRed = 255 * nR / mnMaxVal;
+ nGreen = 255 * nG / mnMaxVal;
+ nBlue = 255 * nB / mnMaxVal;
+ mpRawBmp->SetPixel( nHeight, nWidth++, Color( nRed, nGreen, nBlue ) );
+ if ( nWidth == mnWidth )
+ {
+ nWidth = 0;
+ nHeight++;
+ }
+ }
+ break;
+ }
+ }
+ else
+ {
+ bool bPara = false;
+ bool bFinished = false;
+
+ switch ( mnMode )
+ {
+ // PBM
+ case 0 :
+ while ( !bFinished )
+ {
+ if (!mrPBM.good())
+ return false;
+
+ mrPBM.ReadUChar( nDat );
+
+ if ( nDat == '#' )
+ {
+ mbRemark = true;
+ continue;
+ }
+ else if ( ( nDat == 0x0d ) || ( nDat == 0x0a ) )
+ {
+ mbRemark = false;
+ continue;
+ }
+ if ( mbRemark || nDat == 0x20 || nDat == 0x09 )
+ continue;
+
+ if ( nDat == '0' || nDat == '1' )
+ {
+ mpRawBmp->SetPixel( nHeight, nWidth, mvPalette[static_cast<sal_uInt8>(nDat - '0')] );
+ nWidth++;
+ if ( nWidth == mnWidth )
+ {
+ nWidth = 0;
+ if ( ++nHeight == mnHeight )
+ bFinished = true;
+ }
+ }
+ else
+ return false;
+ }
+ break;
+
+ // PGM
+ case 1 :
+
+ bPara = false;
+ nCount = 0;
+ nGrey = 0;
+
+ while ( !bFinished )
+ {
+ if ( nCount )
+ {
+ nCount--;
+ if ( nGrey <= mnMaxVal )
+ nGrey = 255 * nGrey / mnMaxVal;
+ mpRawBmp->SetPixel( nHeight, nWidth++, mvPalette[static_cast<sal_uInt8>(nGrey)] );
+ nGrey = 0;
+ if ( nWidth == mnWidth )
+ {
+ nWidth = 0;
+ if ( ++nHeight == mnHeight )
+ bFinished = true;
+ }
+ continue;
+ }
+
+ if (!mrPBM.good())
+ return false;
+
+ mrPBM.ReadUChar( nDat );
+
+ if ( nDat == '#' )
+ {
+ mbRemark = true;
+ if ( bPara )
+ {
+ bPara = false;
+ nCount++;
+ }
+ continue;
+ }
+ else if ( ( nDat == 0x0d ) || ( nDat == 0x0a ) )
+ {
+ mbRemark = false;
+ if ( bPara )
+ {
+ bPara = false;
+ nCount++;
+ }
+ continue;
+ }
+
+ if ( nDat == 0x20 || nDat == 0x09 )
+ {
+ if ( bPara )
+ {
+ bPara = false;
+ nCount++;
+ }
+ continue;
+ }
+ if ( nDat >= '0' && nDat <= '9' )
+ {
+ bPara = true;
+ nGrey *= 10;
+ nGrey += nDat-'0';
+ continue;
+ }
+ else
+ return false;
+ }
+ break;
+
+
+ // PPM
+ case 2 :
+
+ bPara = false;
+ nCount = 0;
+ nRGB[ 0 ] = nRGB[ 1 ] = nRGB[ 2 ] = 0;
+
+ while ( !bFinished )
+ {
+ if ( nCount == 3 )
+ {
+ nCount = 0;
+ mpRawBmp->SetPixel( nHeight, nWidth++, Color( static_cast< sal_uInt8 >( ( nRGB[ 0 ] * 255 ) / mnMaxVal ),
+ static_cast< sal_uInt8 >( ( nRGB[ 1 ] * 255 ) / mnMaxVal ),
+ static_cast< sal_uInt8 >( ( nRGB[ 2 ] * 255 ) / mnMaxVal ) ) );
+ nRGB[ 0 ] = nRGB[ 1 ] = nRGB[ 2 ] = 0;
+ if ( nWidth == mnWidth )
+ {
+ nWidth = 0;
+ if ( ++nHeight == mnHeight )
+ bFinished = true;
+ }
+ continue;
+ }
+
+ if (!mrPBM.good())
+ return false;
+
+ mrPBM.ReadUChar( nDat );
+
+ if ( nDat == '#' )
+ {
+ mbRemark = true;
+ if ( bPara )
+ {
+ bPara = false;
+ nCount++;
+ }
+ continue;
+ }
+ else if ( ( nDat == 0x0d ) || ( nDat == 0x0a ) )
+ {
+ mbRemark = false;
+ if ( bPara )
+ {
+ bPara = false;
+ nCount++;
+ }
+ continue;
+ }
+
+ if ( nDat == 0x20 || nDat == 0x09 )
+ {
+ if ( bPara )
+ {
+ bPara = false;
+ nCount++;
+ }
+ continue;
+ }
+ if ( nDat >= '0' && nDat <= '9' )
+ {
+ bPara = true;
+ nRGB[ nCount ] *= 10;
+ nRGB[ nCount ] += nDat-'0';
+ continue;
+ }
+ else
+ return false;
+ }
+ break;
+ }
+ }
+ return mbStatus;
+}
+
+//================== GraphicImport - the exported function ================
+
+bool ImportPbmGraphic( SvStream & rStream, Graphic & rGraphic)
+{
+ PBMReader aPBMReader(rStream);
+
+ return aPBMReader.ReadPBM(rGraphic );
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/vcl/source/filter/ipcd/ipcd.cxx b/vcl/source/filter/ipcd/ipcd.cxx
new file mode 100644
index 000000000..220ac6111
--- /dev/null
+++ b/vcl/source/filter/ipcd/ipcd.cxx
@@ -0,0 +1,352 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <sal/config.h>
+
+#include <string_view>
+
+#include <vcl/graph.hxx>
+#include <vcl/BitmapTools.hxx>
+#include <vcl/FilterConfigItem.hxx>
+#include <tools/stream.hxx>
+#include <filter/PcdReader.hxx>
+
+//============================ PCDReader ==================================
+
+namespace {
+
+// these resolutions are contained in a PCD file:
+enum PCDResolution {
+ PCDRES_BASE16, // 192 x 128
+ PCDRES_BASE4, // 384 x 256
+ PCDRES_BASE, // 768 x 512
+ // the following ones are compressed
+ // and CANNOT be read by us
+ PCDRES_4BASE, // 1536 x 1024
+ PCDRES_16BASE // 3072 x 3072
+};
+
+class PCDReader {
+
+private:
+
+ bool bStatus;
+
+ SvStream &m_rPCD;
+ std::unique_ptr<vcl::bitmap::RawBitmap> mpBitmap;
+
+ sal_uInt8 nOrientation; // orientation of the picture within the PCD file:
+ // 0 - spire point up
+ // 1 - spire points to the right
+ // 2 - spire points down
+ // 3 - spire points to the left
+
+ PCDResolution eResolution; // which resolution we want
+
+ sal_uInt32 nWidth; // width of the PCD picture
+ sal_uInt32 nHeight; // height of the PCD picture
+ sal_uInt32 nImagePos; // position of the picture within the PCD file
+
+ // temporary lLue-Green-Red-Bitmap
+ sal_uInt32 nBMPWidth;
+ sal_uInt32 nBMPHeight;
+
+ void CheckPCDImagePacFile();
+ // checks whether it's a Photo-CD file with 'Image Pac'
+
+ void ReadOrientation();
+ // reads the orientation and sets nOrientation
+
+ void ReadImage();
+
+public:
+
+ explicit PCDReader(SvStream &rStream)
+ : bStatus(false)
+ , m_rPCD(rStream)
+ , nOrientation(0)
+ , eResolution(PCDRES_BASE16)
+ , nWidth(0)
+ , nHeight(0)
+ , nImagePos(0)
+ , nBMPWidth(0)
+ , nBMPHeight(0)
+ {
+ }
+
+ bool ReadPCD( Graphic & rGraphic, FilterConfigItem* pConfigItem );
+};
+
+}
+
+//=================== Methods of PCDReader ==============================
+
+bool PCDReader::ReadPCD( Graphic & rGraphic, FilterConfigItem* pConfigItem )
+{
+ bStatus = true;
+
+ // is it a PCD file with a picture? ( sets bStatus == sal_False, if that's not the case):
+ CheckPCDImagePacFile();
+
+ // read orientation of the picture:
+ ReadOrientation();
+
+ // which resolution do we want?:
+ eResolution = PCDRES_BASE;
+ if ( pConfigItem )
+ {
+ sal_Int32 nResolution = pConfigItem->ReadInt32( "Resolution", 2 );
+ if ( nResolution == 1 )
+ eResolution = PCDRES_BASE4;
+ else if ( nResolution == 0 )
+ eResolution = PCDRES_BASE16;
+ }
+ // determine size and position (position within the PCD file) of the picture:
+ switch (eResolution)
+ {
+ case PCDRES_BASE16 :
+ nWidth = 192;
+ nHeight = 128;
+ nImagePos = 8192;
+ break;
+
+ case PCDRES_BASE4 :
+ nWidth = 384;
+ nHeight = 256;
+ nImagePos = 47104;
+ break;
+
+ case PCDRES_BASE :
+ nWidth = 768;
+ nHeight = 512;
+ nImagePos = 196608;
+ break;
+
+ default:
+ bStatus = false;
+ }
+ if ( bStatus )
+ {
+ if ( ( nOrientation & 0x01 ) == 0 )
+ {
+ nBMPWidth = nWidth;
+ nBMPHeight = nHeight;
+ }
+ else
+ {
+ nBMPWidth = nHeight;
+ nBMPHeight = nWidth;
+ }
+ mpBitmap.reset(new vcl::bitmap::RawBitmap( Size( nBMPWidth, nBMPHeight ), 24 ));
+
+ ReadImage();
+
+ rGraphic = vcl::bitmap::CreateFromData(std::move(*mpBitmap));
+ }
+ return bStatus;
+}
+
+void PCDReader::CheckPCDImagePacFile()
+{
+ char Buf[ 8 ];
+
+ m_rPCD.Seek( 2048 );
+ m_rPCD.ReadBytes(Buf, 7);
+ Buf[ 7 ] = 0;
+ if (!m_rPCD.good() || Buf != std::string_view("PCD_IPI"))
+ bStatus = false;
+}
+
+void PCDReader::ReadOrientation()
+{
+ if ( !bStatus )
+ return;
+ m_rPCD.Seek( 194635 );
+ m_rPCD.ReadUChar( nOrientation );
+ nOrientation &= 0x03;
+}
+
+void PCDReader::ReadImage()
+{
+ tools::Long nL,nCb,nCr;
+
+ if ( !bStatus )
+ return;
+
+ sal_uInt32 nW2 = nWidth>>1;
+ sal_uInt32 nH2 = nHeight>>1;
+
+ // luminance for each pixel of the 1st row of the current pair of rows
+ std::vector<sal_uInt8> aL0(nWidth);
+ // luminance for each pixel of the 2nd row of the current pair of rows
+ std::vector<sal_uInt8> aL1(nWidth);
+ // blue chrominance for each 2x2 pixel of the current pair of rows
+ std::vector<sal_uInt8> aCb(nW2 + 1);
+ // red chrominance for each 2x2 pixel of the current pair of rows
+ std::vector<sal_uInt8> aCr(nW2 + 1);
+ // like above, but for the next pair of rows
+ std::vector<sal_uInt8> aL0N(nWidth);
+ std::vector<sal_uInt8> aL1N(nWidth);
+ std::vector<sal_uInt8> aCbN(nW2 + 1);
+ std::vector<sal_uInt8> aCrN(nW2 + 1);
+
+ sal_uInt8* pL0 = aL0.data();
+ sal_uInt8* pL1 = aL1.data();
+ sal_uInt8* pCb = aCb.data();
+ sal_uInt8* pCr = aCr.data();
+ sal_uInt8* pL0N = aL0N.data();
+ sal_uInt8* pL1N = aL1N.data();
+ sal_uInt8* pCbN = aCbN.data();
+ sal_uInt8* pCrN = aCrN.data();
+
+ m_rPCD.Seek( nImagePos );
+
+ // next pair of rows := first pair of rows:
+ if (m_rPCD.ReadBytes(pL0N, nWidth) != nWidth ||
+ m_rPCD.ReadBytes(pL1N, nWidth) != nWidth ||
+ m_rPCD.ReadBytes(pCbN, nW2) != nW2 ||
+ m_rPCD.ReadBytes(pCrN, nW2) != nW2)
+ {
+ bStatus = false;
+ return;
+ }
+ pCbN[ nW2 ] = pCbN[ nW2 - 1 ];
+ pCrN[ nW2 ] = pCrN[ nW2 - 1 ];
+
+ for (sal_uInt32 nYPair = 0; nYPair < nH2; ++nYPair)
+ {
+ sal_uInt8 * pt;
+ // current pair of rows := next pair of rows:
+ pt=pL0; pL0=pL0N; pL0N=pt;
+ pt=pL1; pL1=pL1N; pL1N=pt;
+ pt=pCb; pCb=pCbN; pCbN=pt;
+ pt=pCr; pCr=pCrN; pCrN=pt;
+
+ // get the next pair of rows:
+ if ( nYPair < nH2 - 1 )
+ {
+ m_rPCD.ReadBytes( pL0N, nWidth );
+ m_rPCD.ReadBytes( pL1N, nWidth );
+ m_rPCD.ReadBytes( pCbN, nW2 );
+ m_rPCD.ReadBytes( pCrN, nW2 );
+ pCbN[nW2]=pCbN[ nW2 - 1 ];
+ pCrN[nW2]=pCrN[ nW2 - 1 ];
+ }
+ else
+ {
+ for (sal_uInt32 nXPair = 0; nXPair < nW2; ++nXPair)
+ {
+ pCbN[ nXPair ] = pCb[ nXPair ];
+ pCrN[ nXPair ] = pCr[ nXPair ];
+ }
+ }
+
+ // loop through both rows of the pair of rows:
+ for (sal_uInt32 ndy = 0; ndy < 2; ++ndy)
+ {
+ sal_uInt32 ny = ( nYPair << 1 ) + ndy;
+
+ // loop through X:
+ for (sal_uInt32 nx = 0; nx < nWidth; ++nx)
+ {
+ // get/calculate nL,nCb,nCr for the pixel nx,ny:
+ sal_uInt32 nXPair = nx >> 1;
+ if ( ndy == 0 )
+ {
+ nL = static_cast<tools::Long>(pL0[ nx ]);
+ if (( nx & 1 ) == 0 )
+ {
+ nCb = static_cast<tools::Long>(pCb[ nXPair ]);
+ nCr = static_cast<tools::Long>(pCr[ nXPair ]);
+ }
+ else
+ {
+ nCb = ( static_cast<tools::Long>(pCb[ nXPair ]) + static_cast<tools::Long>(pCb[ nXPair + 1 ]) ) >> 1;
+ nCr = ( static_cast<tools::Long>(pCr[ nXPair ]) + static_cast<tools::Long>(pCr[ nXPair + 1 ]) ) >> 1;
+ }
+ }
+ else {
+ nL = pL1[ nx ];
+ if ( ( nx & 1 ) == 0 )
+ {
+ nCb = ( static_cast<tools::Long>(pCb[ nXPair ]) + static_cast<tools::Long>(pCbN[ nXPair ]) ) >> 1;
+ nCr = ( static_cast<tools::Long>(pCr[ nXPair ]) + static_cast<tools::Long>(pCrN[ nXPair ]) ) >> 1;
+ }
+ else
+ {
+ nCb = ( static_cast<tools::Long>(pCb[ nXPair ]) + static_cast<tools::Long>(pCb[ nXPair + 1 ]) +
+ static_cast<tools::Long>(pCbN[ nXPair ]) + static_cast<tools::Long>(pCbN[ nXPair + 1 ]) ) >> 2;
+ nCr = ( static_cast<tools::Long>(pCr[ nXPair ]) + static_cast<tools::Long>(pCr[ nXPair + 1]) +
+ static_cast<tools::Long>(pCrN[ nXPair ]) + static_cast<tools::Long>(pCrN[ nXPair + 1 ]) ) >> 2;
+ }
+ }
+ // conversion of nL,nCb,nCr in nRed,nGreen,nBlue:
+ nL *= 89024;
+ nCb -= 156;
+ nCr -= 137;
+ tools::Long nRed = ( nL + nCr * 119374 + 0x8000 ) >> 16;
+ if ( nRed < 0 )
+ nRed = 0;
+ if ( nRed > 255)
+ nRed = 255;
+ tools::Long nGreen = ( nL - nCb * 28198 - nCr * 60761 + 0x8000 ) >> 16;
+ if ( nGreen < 0 )
+ nGreen = 0;
+ if ( nGreen > 255 )
+ nGreen = 255;
+ tools::Long nBlue = ( nL + nCb * 145352 + 0x8000 ) >> 16;
+ if ( nBlue < 0 )
+ nBlue = 0;
+ if ( nBlue > 255 )
+ nBlue = 255;
+
+ // register color value in pBMPMap:
+ if ( nOrientation < 2 )
+ {
+ if ( nOrientation == 0 )
+ mpBitmap->SetPixel( ny, nx, Color( static_cast<sal_uInt8>(nRed), static_cast<sal_uInt8>(nGreen), static_cast<sal_uInt8>(nBlue) ) );
+ else
+ mpBitmap->SetPixel( nWidth - 1 - nx, ny, Color( static_cast<sal_uInt8>(nRed), static_cast<sal_uInt8>(nGreen), static_cast<sal_uInt8>(nBlue) ) );
+ }
+ else
+ {
+ if ( nOrientation == 2 )
+ mpBitmap->SetPixel( nHeight - 1 - ny, ( nWidth - 1 - nx ), Color( static_cast<sal_uInt8>(nRed), static_cast<sal_uInt8>(nGreen), static_cast<sal_uInt8>(nBlue) ) );
+ else
+ mpBitmap->SetPixel( nx, ( nHeight - 1 - ny ), Color( static_cast<sal_uInt8>(nRed), static_cast<sal_uInt8>(nGreen), static_cast<sal_uInt8>(nBlue) ) );
+ }
+ }
+ }
+
+ if ( m_rPCD.GetError() )
+ bStatus = false;
+ if ( !bStatus )
+ break;
+ }
+}
+
+//================== GraphicImport - the exported Function ================
+
+bool ImportPcdGraphic(SvStream & rStream, Graphic & rGraphic, FilterConfigItem* pConfigItem)
+{
+ PCDReader aPCDReader(rStream);
+ return aPCDReader.ReadPCD(rGraphic, pConfigItem);
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/vcl/source/filter/ipcx/ipcx.cxx b/vcl/source/filter/ipcx/ipcx.cxx
new file mode 100644
index 000000000..b1162d5ec
--- /dev/null
+++ b/vcl/source/filter/ipcx/ipcx.cxx
@@ -0,0 +1,411 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+
+#include <memory>
+#include <vcl/graph.hxx>
+#include <vcl/BitmapTools.hxx>
+#include <tools/stream.hxx>
+#include <filter/PcxReader.hxx>
+
+class FilterConfigItem;
+
+//============================ PCXReader ==================================
+
+namespace {
+
+class PCXReader {
+
+private:
+
+ SvStream& m_rPCX; // the PCX file to read
+
+ std::unique_ptr<vcl::bitmap::RawBitmap> mpBitmap;
+ std::vector<Color> mvPalette;
+ sal_uInt8 nVersion; // PCX-Version
+ sal_uInt8 nEncoding; // compression type
+ sal_uInt16 nBitsPerPlanePix; // bits per plane per pixel
+ sal_uInt16 nPlanes; // no of planes
+ sal_uInt16 nBytesPerPlaneLin; // bytes per plane line
+
+ sal_uInt32 nWidth, nHeight; // dimension in pixel
+ sal_uInt16 nResX, nResY; // resolution in pixel per inch or 0,0
+ sal_uInt16 nDestBitsPerPixel; // bits per pixel in destination bitmap 1,4,8 or 24
+ std::unique_ptr<sal_uInt8[]>
+ pPalette;
+ bool bStatus; // from now on do not read status from stream ( SJ )
+
+
+ void ImplReadBody();
+ void ImplReadPalette( unsigned int nCol );
+ void ImplReadHeader();
+
+public:
+ explicit PCXReader(SvStream &rStream);
+ bool ReadPCX(Graphic & rGraphic );
+ // Reads a PCX file from the stream and fills the GDIMetaFile
+};
+
+}
+
+//=================== methods of PCXReader ==============================
+
+PCXReader::PCXReader(SvStream &rStream)
+ : m_rPCX(rStream)
+ , nVersion(0)
+ , nEncoding(0)
+ , nBitsPerPlanePix(0)
+ , nPlanes(0)
+ , nBytesPerPlaneLin(0)
+ , nWidth(0)
+ , nHeight(0)
+ , nResX(0)
+ , nResY(0)
+ , nDestBitsPerPixel(0)
+ , pPalette(new sal_uInt8[ 768 ])
+ , bStatus(false)
+{
+}
+
+bool PCXReader::ReadPCX(Graphic & rGraphic)
+{
+ if ( m_rPCX.GetError() )
+ return false;
+
+ m_rPCX.SetEndian(SvStreamEndian::LITTLE);
+
+ // read header:
+
+ bStatus = true;
+
+ ImplReadHeader();
+
+ // sanity check there is enough data before trying allocation
+ if (bStatus && nBytesPerPlaneLin > m_rPCX.remainingSize() / nPlanes)
+ {
+ bStatus = false;
+ }
+
+ if (bStatus)
+ {
+ sal_uInt32 nResult;
+ bStatus = !o3tl::checked_multiply(nWidth, nHeight, nResult) && nResult <= SAL_MAX_INT32/2/3;
+ }
+
+ // Write BMP header and conditionally (maybe invalid for now) color palette:
+ if (bStatus)
+ {
+ mpBitmap.reset( new vcl::bitmap::RawBitmap( Size( nWidth, nHeight ), 24 ) );
+
+ if ( nDestBitsPerPixel <= 8 )
+ {
+ sal_uInt16 nColors = 1 << nDestBitsPerPixel;
+ sal_uInt8* pPal = pPalette.get();
+ mvPalette.resize( nColors );
+ for ( sal_uInt16 i = 0; i < nColors; i++, pPal += 3 )
+ {
+ mvPalette[i] = Color( pPal[ 0 ], pPal[ 1 ], pPal[ 2 ] );
+ }
+ }
+
+ // read bitmap data
+ ImplReadBody();
+
+ // If an extended color palette exists at the end of the file, then read it and
+ // and write again in palette:
+ if ( nDestBitsPerPixel == 8 && bStatus )
+ {
+ sal_uInt8* pPal = pPalette.get();
+ m_rPCX.SeekRel(1);
+ ImplReadPalette(256);
+ mvPalette.resize( 256 );
+ for ( sal_uInt16 i = 0; i < 256; i++, pPal += 3 )
+ {
+ mvPalette[i] = Color( pPal[ 0 ], pPal[ 1 ], pPal[ 2 ] );
+ }
+ }
+
+ if ( bStatus )
+ {
+ rGraphic = vcl::bitmap::CreateFromData(std::move(*mpBitmap));
+ return true;
+ }
+ }
+ return false;
+}
+
+void PCXReader::ImplReadHeader()
+{
+ sal_uInt8 nbyte(0);
+ m_rPCX.ReadUChar( nbyte ).ReadUChar( nVersion ).ReadUChar( nEncoding );
+ if ( nbyte!=0x0a || (nVersion != 0 && nVersion != 2 && nVersion != 3 && nVersion != 5) || nEncoding > 1 )
+ {
+ bStatus = false;
+ return;
+ }
+
+ nbyte = 0;
+ m_rPCX.ReadUChar( nbyte ); nBitsPerPlanePix = static_cast<sal_uInt16>(nbyte);
+ sal_uInt16 nMinX(0),nMinY(0),nMaxX(0),nMaxY(0);
+ m_rPCX.ReadUInt16( nMinX ).ReadUInt16( nMinY ).ReadUInt16( nMaxX ).ReadUInt16( nMaxY );
+
+ if ((nMinX > nMaxX) || (nMinY > nMaxY))
+ {
+ bStatus = false;
+ return;
+ }
+
+ nWidth = nMaxX-nMinX+1;
+ nHeight = nMaxY-nMinY+1;
+
+ m_rPCX.ReadUInt16( nResX );
+ m_rPCX.ReadUInt16( nResY );
+ if ( nResX >= nWidth || nResY >= nHeight || ( nResX != nResY ) )
+ nResX = nResY = 0;
+
+ ImplReadPalette( 16 );
+
+ m_rPCX.SeekRel( 1 );
+ nbyte = 0;
+ m_rPCX.ReadUChar( nbyte ); nPlanes = static_cast<sal_uInt16>(nbyte);
+ sal_uInt16 nushort(0);
+ m_rPCX.ReadUInt16( nushort ); nBytesPerPlaneLin = nushort;
+ sal_uInt16 nPaletteInfo;
+ m_rPCX.ReadUInt16( nPaletteInfo );
+
+ m_rPCX.SeekRel( 58 );
+
+ nDestBitsPerPixel = nBitsPerPlanePix * nPlanes;
+ if (nDestBitsPerPixel == 2 || nDestBitsPerPixel == 3) nDestBitsPerPixel = 4;
+
+ if ( ( nDestBitsPerPixel != 1 && nDestBitsPerPixel != 4 && nDestBitsPerPixel != 8 && nDestBitsPerPixel != 24 )
+ || nPlanes > 4 || nBytesPerPlaneLin < ( ( nWidth * nBitsPerPlanePix+7 ) >> 3 ) )
+ {
+ bStatus = false;
+ return;
+ }
+
+ // If the bitmap has only 2 colors, the palette is most often invalid and it is always(?)
+ // a black and white image:
+ if ( nPlanes == 1 && nBitsPerPlanePix == 1 )
+ {
+ pPalette[ 0 ] = pPalette[ 1 ] = pPalette[ 2 ] = 0x00;
+ pPalette[ 3 ] = pPalette[ 4 ] = pPalette[ 5 ] = 0xff;
+ }
+}
+
+void PCXReader::ImplReadBody()
+{
+ std::unique_ptr<sal_uInt8[]> pPlane[ 4 ];
+ sal_uInt8 * pDest;
+ sal_uInt32 i, ny, nLastPercent = 0, nPercent;
+ sal_uInt16 nCount, nx;
+ sal_uInt8 nDat = 0, nCol = 0;
+
+ for (sal_uInt16 np = 0; np < nPlanes; ++np)
+ pPlane[np].reset(new sal_uInt8[nBytesPerPlaneLin]());
+
+ nCount = 0;
+ for ( ny = 0; ny < nHeight; ny++ )
+ {
+ if (!m_rPCX.good())
+ {
+ bStatus = false;
+ break;
+ }
+ nPercent = ny * 60 / nHeight + 10;
+ if ( ny == 0 || nLastPercent + 4 <= nPercent )
+ {
+ nLastPercent = nPercent;
+ }
+ for (sal_uInt16 np = 0; np < nPlanes; ++np)
+ {
+ if ( nEncoding == 0)
+ m_rPCX.ReadBytes( static_cast<void *>(pPlane[ np ].get()), nBytesPerPlaneLin );
+ else
+ {
+ pDest = pPlane[ np ].get();
+ nx = nBytesPerPlaneLin;
+ while ( nCount > 0 && nx > 0)
+ {
+ *(pDest++) = nDat;
+ nx--;
+ nCount--;
+ }
+ while (nx > 0 && m_rPCX.good())
+ {
+ m_rPCX.ReadUChar( nDat );
+ if ( ( nDat & 0xc0 ) == 0xc0 )
+ {
+ nCount =static_cast<sal_uInt64>(nDat) & 0x003f;
+ m_rPCX.ReadUChar( nDat );
+ if ( nCount < nx )
+ {
+ nx -= nCount;
+ while ( nCount > 0)
+ {
+ *(pDest++) = nDat;
+ nCount--;
+ }
+ }
+ else
+ {
+ nCount -= nx;
+ do
+ {
+ *(pDest++) = nDat;
+ nx--;
+ }
+ while ( nx > 0 );
+ break;
+ }
+ }
+ else
+ {
+ *(pDest++) = nDat;
+ nx--;
+ }
+ }
+ }
+ }
+ sal_uInt8 *pSource1 = pPlane[ 0 ].get();
+ sal_uInt8 *pSource2 = pPlane[ 1 ].get();
+ sal_uInt8 *pSource3 = pPlane[ 2 ].get();
+ sal_uInt8 *pSource4 = pPlane[ 3 ].get();
+ switch ( nBitsPerPlanePix + ( nPlanes << 8 ) )
+ {
+ // 2 colors
+ case 0x101 :
+ for ( i = 0; i < nWidth; i++ )
+ {
+ sal_uInt32 nShift = ( i & 7 ) ^ 7;
+ if ( nShift == 0 )
+ mpBitmap->SetPixel( ny, i, mvPalette[*(pSource1++) & 1] );
+ else
+ mpBitmap->SetPixel( ny, i, mvPalette[(*pSource1 >> nShift ) & 1] );
+ }
+ break;
+ // 4 colors
+ case 0x102 :
+ for ( i = 0; i < nWidth; i++ )
+ {
+ switch( i & 3 )
+ {
+ case 0 :
+ nCol = *pSource1 >> 6;
+ break;
+ case 1 :
+ nCol = ( *pSource1 >> 4 ) & 0x03 ;
+ break;
+ case 2 :
+ nCol = ( *pSource1 >> 2 ) & 0x03;
+ break;
+ case 3 :
+ nCol = ( *pSource1++ ) & 0x03;
+ break;
+ }
+ mpBitmap->SetPixel( ny, i, mvPalette[nCol] );
+ }
+ break;
+ // 256 colors
+ case 0x108 :
+ for ( i = 0; i < nWidth; i++ )
+ {
+ mpBitmap->SetPixel( ny, i, mvPalette[*pSource1++] );
+ }
+ break;
+ // 8 colors
+ case 0x301 :
+ for ( i = 0; i < nWidth; i++ )
+ {
+ sal_uInt32 nShift = ( i & 7 ) ^ 7;
+ if ( nShift == 0 )
+ {
+ nCol = ( *pSource1++ & 1) + ( ( *pSource2++ << 1 ) & 2 ) + ( ( *pSource3++ << 2 ) & 4 );
+ mpBitmap->SetPixel( ny, i, mvPalette[nCol] );
+ }
+ else
+ {
+ nCol = sal::static_int_cast< sal_uInt8 >(
+ ( ( *pSource1 >> nShift ) & 1) + ( ( ( *pSource2 >> nShift ) << 1 ) & 2 ) +
+ ( ( ( *pSource3 >> nShift ) << 2 ) & 4 ));
+ mpBitmap->SetPixel( ny, i, mvPalette[nCol] );
+ }
+ }
+ break;
+ // 16 colors
+ case 0x401 :
+ for ( i = 0; i < nWidth; i++ )
+ {
+ sal_uInt32 nShift = ( i & 7 ) ^ 7;
+ if ( nShift == 0 )
+ {
+ nCol = ( *pSource1++ & 1) + ( ( *pSource2++ << 1 ) & 2 ) + ( ( *pSource3++ << 2 ) & 4 ) +
+ ( ( *pSource4++ << 3 ) & 8 );
+ mpBitmap->SetPixel( ny, i, mvPalette[nCol] );
+ }
+ else
+ {
+ nCol = sal::static_int_cast< sal_uInt8 >(
+ ( ( *pSource1 >> nShift ) & 1) + ( ( ( *pSource2 >> nShift ) << 1 ) & 2 ) +
+ ( ( ( *pSource3 >> nShift ) << 2 ) & 4 ) + ( ( ( *pSource4 >> nShift ) << 3 ) & 8 ));
+ mpBitmap->SetPixel( ny, i, mvPalette[nCol] );
+ }
+ }
+ break;
+ // 16m colors
+ case 0x308 :
+ for ( i = 0; i < nWidth; i++ )
+ {
+ mpBitmap->SetPixel( ny, i, Color( *pSource1++, *pSource2++, *pSource3++ ) );
+
+ }
+ break;
+ default :
+ bStatus = false;
+ break;
+ }
+ }
+}
+
+void PCXReader::ImplReadPalette( unsigned int nCol )
+{
+ sal_uInt8 r, g, b;
+ sal_uInt8* pPtr = pPalette.get();
+ for ( unsigned int i = 0; i < nCol; i++ )
+ {
+ m_rPCX.ReadUChar( r ).ReadUChar( g ).ReadUChar( b );
+ *pPtr++ = r;
+ *pPtr++ = g;
+ *pPtr++ = b;
+ }
+}
+
+//================== GraphicImport - the exported function ================
+
+bool ImportPcxGraphic(SvStream & rStream, Graphic & rGraphic)
+{
+ PCXReader aPCXReader(rStream);
+ bool bRetValue = aPCXReader.ReadPCX(rGraphic);
+ if ( !bRetValue )
+ rStream.SetError( SVSTREAM_FILEFORMAT_ERROR );
+ return bRetValue;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/vcl/source/filter/ipdf/pdfcompat.cxx b/vcl/source/filter/ipdf/pdfcompat.cxx
new file mode 100644
index 000000000..62413e585
--- /dev/null
+++ b/vcl/source/filter/ipdf/pdfcompat.cxx
@@ -0,0 +1,114 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ */
+
+#include <pdf/pdfcompat.hxx>
+
+#include <o3tl/string_view.hxx>
+#include <vcl/filter/PDFiumLibrary.hxx>
+#include <sal/log.hxx>
+
+namespace vcl::pdf
+{
+/// Decide if PDF data is old enough to be compatible.
+bool isCompatible(SvStream& rInStream, sal_uInt64 nPos, sal_uInt64 nSize)
+{
+ if (nSize < 8)
+ return false;
+
+ // %PDF-x.y
+ sal_uInt8 aFirstBytes[8];
+ rInStream.Seek(nPos);
+ sal_uLong nRead = rInStream.ReadBytes(aFirstBytes, 8);
+ if (nRead < 8)
+ return false;
+
+ if (aFirstBytes[0] != '%' || aFirstBytes[1] != 'P' || aFirstBytes[2] != 'D'
+ || aFirstBytes[3] != 'F' || aFirstBytes[4] != '-')
+ return false;
+
+ sal_Int32 nMajor = o3tl::toInt32(std::string_view(reinterpret_cast<char*>(&aFirstBytes[5]), 1));
+ sal_Int32 nMinor = o3tl::toInt32(std::string_view(reinterpret_cast<char*>(&aFirstBytes[7]), 1));
+ return !(nMajor > 1 || (nMajor == 1 && nMinor > 6));
+}
+
+/// Converts to highest supported format version (1.6).
+/// Usually used to deal with missing referenced objects in source
+/// pdf stream.
+bool convertToHighestSupported(SvStream& rInStream, SvStream& rOutStream)
+{
+ sal_uInt64 nPos = STREAM_SEEK_TO_BEGIN;
+ sal_uInt64 nSize = STREAM_SEEK_TO_END;
+ rInStream.Seek(nPos);
+ // Convert to PDF-1.6.
+ auto pPdfium = vcl::pdf::PDFiumLibrary::get();
+ if (!pPdfium)
+ return false;
+
+ // Read input into a buffer.
+ SvMemoryStream aInBuffer;
+ aInBuffer.WriteStream(rInStream, nSize);
+
+ SvMemoryStream aSaved;
+ {
+ // Load the buffer using pdfium.
+ std::unique_ptr<vcl::pdf::PDFiumDocument> pPdfDocument
+ = pPdfium->openDocument(aInBuffer.GetData(), aInBuffer.GetSize(), OString());
+ if (!pPdfDocument)
+ return false;
+
+ // 16 means PDF-1.6.
+ if (!pPdfDocument->saveWithVersion(aSaved, 16))
+ return false;
+ }
+
+ aSaved.Seek(STREAM_SEEK_TO_BEGIN);
+ rOutStream.WriteStream(aSaved);
+
+ return rOutStream.good();
+}
+
+/// Takes care of transparently downgrading the version of the PDF stream in
+/// case it's too new for our PDF export.
+bool getCompatibleStream(SvStream& rInStream, SvStream& rOutStream)
+{
+ sal_uInt64 nPos = STREAM_SEEK_TO_BEGIN;
+ sal_uInt64 nSize = STREAM_SEEK_TO_END;
+ bool bCompatible = isCompatible(rInStream, nPos, nSize);
+ rInStream.Seek(nPos);
+ if (bCompatible)
+ // Not converting.
+ rOutStream.WriteStream(rInStream, nSize);
+ else
+ convertToHighestSupported(rInStream, rOutStream);
+
+ return rOutStream.good();
+}
+
+BinaryDataContainer createBinaryDataContainer(SvStream& rStream)
+{
+ // Save the original PDF stream for later use.
+ SvMemoryStream aMemoryStream;
+ if (!getCompatibleStream(rStream, aMemoryStream))
+ return {};
+
+ const sal_uInt32 nStreamLength = aMemoryStream.TellEnd();
+
+ auto aPdfData = std::make_unique<std::vector<sal_uInt8>>(nStreamLength);
+
+ aMemoryStream.Seek(STREAM_SEEK_TO_BEGIN);
+ aMemoryStream.ReadBytes(aPdfData->data(), aPdfData->size());
+ if (aMemoryStream.GetError())
+ return {};
+
+ return { std::move(aPdfData) };
+}
+
+} // end vcl::filter::ipdf namespace
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/vcl/source/filter/ipdf/pdfdocument.cxx b/vcl/source/filter/ipdf/pdfdocument.cxx
new file mode 100644
index 000000000..a93083ce8
--- /dev/null
+++ b/vcl/source/filter/ipdf/pdfdocument.cxx
@@ -0,0 +1,3325 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ */
+
+#include <vcl/filter/pdfdocument.hxx>
+#include <pdf/pdfcompat.hxx>
+#include <config_features.h>
+
+#include <map>
+#include <memory>
+#include <vector>
+
+#include <com/sun/star/uno/Sequence.hxx>
+#include <com/sun/star/security/XCertificate.hpp>
+
+#include <comphelper/scopeguard.hxx>
+#include <comphelper/string.hxx>
+#include <o3tl/string_view.hxx>
+#include <rtl/character.hxx>
+#include <rtl/strbuf.hxx>
+#include <rtl/string.hxx>
+#include <sal/log.hxx>
+#include <sal/types.h>
+#include <svl/cryptosign.hxx>
+#include <tools/zcodec.hxx>
+#include <vcl/pdfwriter.hxx>
+#include <o3tl/safeint.hxx>
+
+#include <pdf/objectcopier.hxx>
+
+using namespace com::sun::star;
+
+namespace vcl::filter
+{
+XRefEntry::XRefEntry() = default;
+
+PDFDocument::PDFDocument() = default;
+
+PDFDocument::~PDFDocument() = default;
+
+bool PDFDocument::RemoveSignature(size_t nPosition)
+{
+ std::vector<PDFObjectElement*> aSignatures = GetSignatureWidgets();
+ if (nPosition >= aSignatures.size())
+ {
+ SAL_WARN("vcl.filter", "PDFDocument::RemoveSignature: invalid nPosition");
+ return false;
+ }
+
+ if (aSignatures.size() != m_aEOFs.size() - 1)
+ {
+ SAL_WARN("vcl.filter", "PDFDocument::RemoveSignature: no 1:1 mapping between signatures "
+ "and incremental updates");
+ return false;
+ }
+
+ // The EOF offset is the end of the original file, without the signature at
+ // nPosition.
+ m_aEditBuffer.Seek(m_aEOFs[nPosition]);
+ // Drop all bytes after the current position.
+ m_aEditBuffer.SetStreamSize(m_aEditBuffer.Tell() + 1);
+
+ return m_aEditBuffer.good();
+}
+
+sal_Int32 PDFDocument::createObject()
+{
+ sal_Int32 nObject = m_aXRef.size();
+ m_aXRef[nObject] = XRefEntry();
+ return nObject;
+}
+
+bool PDFDocument::updateObject(sal_Int32 nObject)
+{
+ if (o3tl::make_unsigned(nObject) >= m_aXRef.size())
+ {
+ SAL_WARN("vcl.filter", "PDFDocument::updateObject: invalid nObject");
+ return false;
+ }
+
+ XRefEntry aEntry;
+ aEntry.SetOffset(m_aEditBuffer.Tell());
+ aEntry.SetDirty(true);
+ m_aXRef[nObject] = aEntry;
+ return true;
+}
+
+bool PDFDocument::writeBuffer(const void* pBuffer, sal_uInt64 nBytes)
+{
+ std::size_t nWritten = m_aEditBuffer.WriteBytes(pBuffer, nBytes);
+ return nWritten == nBytes;
+}
+
+void PDFDocument::SetSignatureLine(std::vector<sal_Int8>&& rSignatureLine)
+{
+ m_aSignatureLine = std::move(rSignatureLine);
+}
+
+void PDFDocument::SetSignaturePage(size_t nPage) { m_nSignaturePage = nPage; }
+
+sal_uInt32 PDFDocument::GetNextSignature()
+{
+ sal_uInt32 nRet = 0;
+ for (const auto& pSignature : GetSignatureWidgets())
+ {
+ auto pT = dynamic_cast<PDFLiteralStringElement*>(pSignature->Lookup("T"));
+ if (!pT)
+ continue;
+
+ const OString& rValue = pT->GetValue();
+ const OString aPrefix = "Signature";
+ if (!rValue.startsWith(aPrefix))
+ continue;
+
+ nRet = std::max(nRet, o3tl::toUInt32(rValue.subView(aPrefix.getLength())));
+ }
+
+ return nRet + 1;
+}
+
+sal_Int32 PDFDocument::WriteSignatureObject(const OUString& rDescription, bool bAdES,
+ sal_uInt64& rLastByteRangeOffset,
+ sal_Int64& rContentOffset)
+{
+ // Write signature object.
+ sal_Int32 nSignatureId = m_aXRef.size();
+ XRefEntry aSignatureEntry;
+ aSignatureEntry.SetOffset(m_aEditBuffer.Tell());
+ aSignatureEntry.SetDirty(true);
+ m_aXRef[nSignatureId] = aSignatureEntry;
+ OStringBuffer aSigBuffer;
+ aSigBuffer.append(nSignatureId);
+ aSigBuffer.append(" 0 obj\n");
+ aSigBuffer.append("<</Contents <");
+ rContentOffset = aSignatureEntry.GetOffset() + aSigBuffer.getLength();
+ // Reserve space for the PKCS#7 object.
+ OStringBuffer aContentFiller(MAX_SIGNATURE_CONTENT_LENGTH);
+ comphelper::string::padToLength(aContentFiller, MAX_SIGNATURE_CONTENT_LENGTH, '0');
+ aSigBuffer.append(aContentFiller);
+ aSigBuffer.append(">\n/Type/Sig/SubFilter");
+ if (bAdES)
+ aSigBuffer.append("/ETSI.CAdES.detached");
+ else
+ aSigBuffer.append("/adbe.pkcs7.detached");
+
+ // Time of signing.
+ aSigBuffer.append(" /M (");
+ aSigBuffer.append(vcl::PDFWriter::GetDateTime());
+ aSigBuffer.append(")");
+
+ // Byte range: we can write offset1-length1 and offset2 right now, will
+ // write length2 later.
+ aSigBuffer.append(" /ByteRange [ 0 ");
+ // -1 and +1 is the leading "<" and the trailing ">" around the hex string.
+ aSigBuffer.append(rContentOffset - 1);
+ aSigBuffer.append(" ");
+ aSigBuffer.append(rContentOffset + MAX_SIGNATURE_CONTENT_LENGTH + 1);
+ aSigBuffer.append(" ");
+ rLastByteRangeOffset = aSignatureEntry.GetOffset() + aSigBuffer.getLength();
+ // We don't know how many bytes we need for the last ByteRange value, this
+ // should be enough.
+ OStringBuffer aByteRangeFiller;
+ comphelper::string::padToLength(aByteRangeFiller, 100, ' ');
+ aSigBuffer.append(aByteRangeFiller);
+ // Finish the Sig obj.
+ aSigBuffer.append(" /Filter/Adobe.PPKMS");
+
+ if (!rDescription.isEmpty())
+ {
+ aSigBuffer.append("/Reason<");
+ vcl::PDFWriter::AppendUnicodeTextString(rDescription, aSigBuffer);
+ aSigBuffer.append(">");
+ }
+
+ aSigBuffer.append(" >>\nendobj\n\n");
+ m_aEditBuffer.WriteOString(aSigBuffer);
+
+ return nSignatureId;
+}
+
+sal_Int32 PDFDocument::WriteAppearanceObject(tools::Rectangle& rSignatureRectangle)
+{
+ PDFDocument aPDFDocument;
+ filter::PDFObjectElement* pPage = nullptr;
+ std::vector<filter::PDFObjectElement*> aContentStreams;
+
+ if (!m_aSignatureLine.empty())
+ {
+ // Parse the PDF data of signature line: we can set the signature rectangle to non-empty
+ // based on it.
+ SvMemoryStream aPDFStream;
+ aPDFStream.WriteBytes(m_aSignatureLine.data(), m_aSignatureLine.size());
+ aPDFStream.Seek(0);
+ if (!aPDFDocument.Read(aPDFStream))
+ {
+ SAL_WARN("vcl.filter",
+ "PDFDocument::WriteAppearanceObject: failed to read the PDF document");
+ return -1;
+ }
+
+ std::vector<filter::PDFObjectElement*> aPages = aPDFDocument.GetPages();
+ if (aPages.empty())
+ {
+ SAL_WARN("vcl.filter", "PDFDocument::WriteAppearanceObject: no pages");
+ return -1;
+ }
+
+ pPage = aPages[0];
+ if (!pPage)
+ {
+ SAL_WARN("vcl.filter", "PDFDocument::WriteAppearanceObject: no page");
+ return -1;
+ }
+
+ // Calculate the bounding box.
+ PDFElement* pMediaBox = pPage->Lookup("MediaBox");
+ auto pMediaBoxArray = dynamic_cast<PDFArrayElement*>(pMediaBox);
+ if (!pMediaBoxArray || pMediaBoxArray->GetElements().size() < 4)
+ {
+ SAL_WARN("vcl.filter",
+ "PDFDocument::WriteAppearanceObject: MediaBox is not an array of 4");
+ return -1;
+ }
+ const std::vector<PDFElement*>& rMediaBoxElements = pMediaBoxArray->GetElements();
+ auto pWidth = dynamic_cast<PDFNumberElement*>(rMediaBoxElements[2]);
+ if (!pWidth)
+ {
+ SAL_WARN("vcl.filter", "PDFDocument::WriteAppearanceObject: MediaBox has no width");
+ return -1;
+ }
+ rSignatureRectangle.setWidth(pWidth->GetValue());
+ auto pHeight = dynamic_cast<PDFNumberElement*>(rMediaBoxElements[3]);
+ if (!pHeight)
+ {
+ SAL_WARN("vcl.filter", "PDFDocument::WriteAppearanceObject: MediaBox has no height");
+ return -1;
+ }
+ rSignatureRectangle.setHeight(pHeight->GetValue());
+
+ if (PDFObjectElement* pContentStream = pPage->LookupObject("Contents"))
+ {
+ aContentStreams.push_back(pContentStream);
+ }
+
+ if (aContentStreams.empty())
+ {
+ SAL_WARN("vcl.filter", "PDFDocument::WriteAppearanceObject: no content stream");
+ return -1;
+ }
+ }
+ m_aSignatureLine.clear();
+
+ // Write appearance object: allocate an ID.
+ sal_Int32 nAppearanceId = m_aXRef.size();
+ m_aXRef[nAppearanceId] = XRefEntry();
+
+ // Write the object content.
+ SvMemoryStream aEditBuffer;
+ aEditBuffer.WriteUInt32AsString(nAppearanceId);
+ aEditBuffer.WriteCharPtr(" 0 obj\n");
+ aEditBuffer.WriteCharPtr("<</Type/XObject\n/Subtype/Form\n");
+
+ PDFObjectCopier aCopier(*this);
+ if (!aContentStreams.empty())
+ {
+ assert(pPage && "aContentStreams is only filled if there was a pPage");
+ OStringBuffer aBuffer;
+ aCopier.copyPageResources(pPage, aBuffer);
+ aEditBuffer.WriteOString(aBuffer);
+ }
+
+ aEditBuffer.WriteCharPtr("/BBox[0 0 ");
+ aEditBuffer.WriteOString(OString::number(rSignatureRectangle.getWidth()));
+ aEditBuffer.WriteCharPtr(" ");
+ aEditBuffer.WriteOString(OString::number(rSignatureRectangle.getHeight()));
+ aEditBuffer.WriteCharPtr("]\n/Length ");
+
+ // Add the object to the doc-level edit buffer and update the offset.
+ SvMemoryStream aStream;
+ bool bCompressed = false;
+ sal_Int32 nLength = 0;
+ if (!aContentStreams.empty())
+ {
+ nLength = PDFObjectCopier::copyPageStreams(aContentStreams, aStream, bCompressed);
+ }
+ aEditBuffer.WriteOString(OString::number(nLength));
+ if (bCompressed)
+ {
+ aEditBuffer.WriteOString(" /Filter/FlateDecode");
+ }
+
+ aEditBuffer.WriteCharPtr("\n>>\n");
+
+ aEditBuffer.WriteCharPtr("stream\n");
+
+ // Copy the original page streams to the form XObject stream.
+ aStream.Seek(0);
+ aEditBuffer.WriteStream(aStream);
+
+ aEditBuffer.WriteCharPtr("\nendstream\nendobj\n\n");
+
+ aEditBuffer.Seek(0);
+ XRefEntry aAppearanceEntry;
+ aAppearanceEntry.SetOffset(m_aEditBuffer.Tell());
+ aAppearanceEntry.SetDirty(true);
+ m_aXRef[nAppearanceId] = aAppearanceEntry;
+ m_aEditBuffer.WriteStream(aEditBuffer);
+
+ return nAppearanceId;
+}
+
+sal_Int32 PDFDocument::WriteAnnotObject(PDFObjectElement const& rFirstPage, sal_Int32 nSignatureId,
+ sal_Int32 nAppearanceId,
+ const tools::Rectangle& rSignatureRectangle)
+{
+ // Decide what identifier to use for the new signature.
+ sal_uInt32 nNextSignature = GetNextSignature();
+
+ // Write the Annot object, references nSignatureId and nAppearanceId.
+ sal_Int32 nAnnotId = m_aXRef.size();
+ XRefEntry aAnnotEntry;
+ aAnnotEntry.SetOffset(m_aEditBuffer.Tell());
+ aAnnotEntry.SetDirty(true);
+ m_aXRef[nAnnotId] = aAnnotEntry;
+ m_aEditBuffer.WriteUInt32AsString(nAnnotId);
+ m_aEditBuffer.WriteCharPtr(" 0 obj\n");
+ m_aEditBuffer.WriteCharPtr("<</Type/Annot/Subtype/Widget/F 132\n");
+ m_aEditBuffer.WriteCharPtr("/Rect[0 0 ");
+ m_aEditBuffer.WriteOString(OString::number(rSignatureRectangle.getWidth()));
+ m_aEditBuffer.WriteCharPtr(" ");
+ m_aEditBuffer.WriteOString(OString::number(rSignatureRectangle.getHeight()));
+ m_aEditBuffer.WriteCharPtr("]\n");
+ m_aEditBuffer.WriteCharPtr("/FT/Sig\n");
+ m_aEditBuffer.WriteCharPtr("/P ");
+ m_aEditBuffer.WriteUInt32AsString(rFirstPage.GetObjectValue());
+ m_aEditBuffer.WriteCharPtr(" 0 R\n");
+ m_aEditBuffer.WriteCharPtr("/T(Signature");
+ m_aEditBuffer.WriteUInt32AsString(nNextSignature);
+ m_aEditBuffer.WriteCharPtr(")\n");
+ m_aEditBuffer.WriteCharPtr("/V ");
+ m_aEditBuffer.WriteUInt32AsString(nSignatureId);
+ m_aEditBuffer.WriteCharPtr(" 0 R\n");
+ m_aEditBuffer.WriteCharPtr("/DV ");
+ m_aEditBuffer.WriteUInt32AsString(nSignatureId);
+ m_aEditBuffer.WriteCharPtr(" 0 R\n");
+ m_aEditBuffer.WriteCharPtr("/AP<<\n/N ");
+ m_aEditBuffer.WriteUInt32AsString(nAppearanceId);
+ m_aEditBuffer.WriteCharPtr(" 0 R\n>>\n");
+ m_aEditBuffer.WriteCharPtr(">>\nendobj\n\n");
+
+ return nAnnotId;
+}
+
+bool PDFDocument::WritePageObject(PDFObjectElement& rFirstPage, sal_Int32 nAnnotId)
+{
+ PDFElement* pAnnots = rFirstPage.Lookup("Annots");
+ auto pAnnotsReference = dynamic_cast<PDFReferenceElement*>(pAnnots);
+ if (pAnnotsReference)
+ {
+ // Write the updated Annots key of the Page object.
+ PDFObjectElement* pAnnotsObject = pAnnotsReference->LookupObject();
+ if (!pAnnotsObject)
+ {
+ SAL_WARN("vcl.filter", "PDFDocument::Sign: invalid Annots reference");
+ return false;
+ }
+
+ sal_uInt32 nAnnotsId = pAnnotsObject->GetObjectValue();
+ m_aXRef[nAnnotsId].SetType(XRefEntryType::NOT_COMPRESSED);
+ m_aXRef[nAnnotsId].SetOffset(m_aEditBuffer.Tell());
+ m_aXRef[nAnnotsId].SetDirty(true);
+ m_aEditBuffer.WriteUInt32AsString(nAnnotsId);
+ m_aEditBuffer.WriteCharPtr(" 0 obj\n[");
+
+ // Write existing references.
+ PDFArrayElement* pArray = pAnnotsObject->GetArray();
+ if (!pArray)
+ {
+ SAL_WARN("vcl.filter", "PDFDocument::Sign: Page Annots is a reference to a non-array");
+ return false;
+ }
+
+ for (size_t i = 0; i < pArray->GetElements().size(); ++i)
+ {
+ auto pReference = dynamic_cast<PDFReferenceElement*>(pArray->GetElements()[i]);
+ if (!pReference)
+ continue;
+
+ if (i)
+ m_aEditBuffer.WriteCharPtr(" ");
+ m_aEditBuffer.WriteUInt32AsString(pReference->GetObjectValue());
+ m_aEditBuffer.WriteCharPtr(" 0 R");
+ }
+ // Write our reference.
+ m_aEditBuffer.WriteCharPtr(" ");
+ m_aEditBuffer.WriteUInt32AsString(nAnnotId);
+ m_aEditBuffer.WriteCharPtr(" 0 R");
+
+ m_aEditBuffer.WriteCharPtr("]\nendobj\n\n");
+ }
+ else
+ {
+ // Write the updated first page object, references nAnnotId.
+ sal_uInt32 nFirstPageId = rFirstPage.GetObjectValue();
+ if (nFirstPageId >= m_aXRef.size())
+ {
+ SAL_WARN("vcl.filter", "PDFDocument::Sign: invalid first page obj id");
+ return false;
+ }
+ m_aXRef[nFirstPageId].SetOffset(m_aEditBuffer.Tell());
+ m_aXRef[nFirstPageId].SetDirty(true);
+ m_aEditBuffer.WriteUInt32AsString(nFirstPageId);
+ m_aEditBuffer.WriteCharPtr(" 0 obj\n");
+ m_aEditBuffer.WriteCharPtr("<<");
+ auto pAnnotsArray = dynamic_cast<PDFArrayElement*>(pAnnots);
+ if (!pAnnotsArray)
+ {
+ // No Annots key, just write the key with a single reference.
+ m_aEditBuffer.WriteBytes(static_cast<const char*>(m_aEditBuffer.GetData())
+ + rFirstPage.GetDictionaryOffset(),
+ rFirstPage.GetDictionaryLength());
+ m_aEditBuffer.WriteCharPtr("/Annots[");
+ m_aEditBuffer.WriteUInt32AsString(nAnnotId);
+ m_aEditBuffer.WriteCharPtr(" 0 R]");
+ }
+ else
+ {
+ // Annots key is already there, insert our reference at the end.
+ PDFDictionaryElement* pDictionary = rFirstPage.GetDictionary();
+
+ // Offset right before the end of the Annots array.
+ sal_uInt64 nAnnotsEndOffset = pDictionary->GetKeyOffset("Annots")
+ + pDictionary->GetKeyValueLength("Annots") - 1;
+ // Length of beginning of the dictionary -> Annots end.
+ sal_uInt64 nAnnotsBeforeEndLength = nAnnotsEndOffset - rFirstPage.GetDictionaryOffset();
+ m_aEditBuffer.WriteBytes(static_cast<const char*>(m_aEditBuffer.GetData())
+ + rFirstPage.GetDictionaryOffset(),
+ nAnnotsBeforeEndLength);
+ m_aEditBuffer.WriteCharPtr(" ");
+ m_aEditBuffer.WriteUInt32AsString(nAnnotId);
+ m_aEditBuffer.WriteCharPtr(" 0 R");
+ // Length of Annots end -> end of the dictionary.
+ sal_uInt64 nAnnotsAfterEndLength = rFirstPage.GetDictionaryOffset()
+ + rFirstPage.GetDictionaryLength()
+ - nAnnotsEndOffset;
+ m_aEditBuffer.WriteBytes(static_cast<const char*>(m_aEditBuffer.GetData())
+ + nAnnotsEndOffset,
+ nAnnotsAfterEndLength);
+ }
+ m_aEditBuffer.WriteCharPtr(">>");
+ m_aEditBuffer.WriteCharPtr("\nendobj\n\n");
+ }
+
+ return true;
+}
+
+bool PDFDocument::WriteCatalogObject(sal_Int32 nAnnotId, PDFReferenceElement*& pRoot)
+{
+ if (m_pXRefStream)
+ pRoot = dynamic_cast<PDFReferenceElement*>(m_pXRefStream->Lookup("Root"));
+ else
+ {
+ if (!m_pTrailer)
+ {
+ SAL_WARN("vcl.filter", "PDFDocument::Sign: found no trailer");
+ return false;
+ }
+ pRoot = dynamic_cast<PDFReferenceElement*>(m_pTrailer->Lookup("Root"));
+ }
+ if (!pRoot)
+ {
+ SAL_WARN("vcl.filter", "PDFDocument::Sign: trailer has no root reference");
+ return false;
+ }
+ PDFObjectElement* pCatalog = pRoot->LookupObject();
+ if (!pCatalog)
+ {
+ SAL_WARN("vcl.filter", "PDFDocument::Sign: invalid catalog reference");
+ return false;
+ }
+ sal_uInt32 nCatalogId = pCatalog->GetObjectValue();
+ if (nCatalogId >= m_aXRef.size())
+ {
+ SAL_WARN("vcl.filter", "PDFDocument::Sign: invalid catalog obj id");
+ return false;
+ }
+ PDFElement* pAcroForm = pCatalog->Lookup("AcroForm");
+ auto pAcroFormReference = dynamic_cast<PDFReferenceElement*>(pAcroForm);
+ if (pAcroFormReference)
+ {
+ // Write the updated AcroForm key of the Catalog object.
+ PDFObjectElement* pAcroFormObject = pAcroFormReference->LookupObject();
+ if (!pAcroFormObject)
+ {
+ SAL_WARN("vcl.filter", "PDFDocument::Sign: invalid AcroForm reference");
+ return false;
+ }
+
+ sal_uInt32 nAcroFormId = pAcroFormObject->GetObjectValue();
+ m_aXRef[nAcroFormId].SetType(XRefEntryType::NOT_COMPRESSED);
+ m_aXRef[nAcroFormId].SetOffset(m_aEditBuffer.Tell());
+ m_aXRef[nAcroFormId].SetDirty(true);
+ m_aEditBuffer.WriteUInt32AsString(nAcroFormId);
+ m_aEditBuffer.WriteCharPtr(" 0 obj\n");
+
+ // If this is nullptr, then the AcroForm object is not in an object stream.
+ SvMemoryStream* pStreamBuffer = pAcroFormObject->GetStreamBuffer();
+
+ if (!pAcroFormObject->Lookup("Fields"))
+ {
+ SAL_WARN("vcl.filter",
+ "PDFDocument::Sign: AcroForm object without required Fields key");
+ return false;
+ }
+
+ PDFDictionaryElement* pAcroFormDictionary = pAcroFormObject->GetDictionary();
+ if (!pAcroFormDictionary)
+ {
+ SAL_WARN("vcl.filter", "PDFDocument::Sign: AcroForm object has no dictionary");
+ return false;
+ }
+
+ // Offset right before the end of the Fields array.
+ sal_uInt64 nFieldsEndOffset = pAcroFormDictionary->GetKeyOffset("Fields")
+ + pAcroFormDictionary->GetKeyValueLength("Fields")
+ - strlen("]");
+
+ // Length of beginning of the object dictionary -> Fields end.
+ sal_uInt64 nFieldsBeforeEndLength = nFieldsEndOffset;
+ if (pStreamBuffer)
+ m_aEditBuffer.WriteBytes(pStreamBuffer->GetData(), nFieldsBeforeEndLength);
+ else
+ {
+ nFieldsBeforeEndLength -= pAcroFormObject->GetDictionaryOffset();
+ m_aEditBuffer.WriteCharPtr("<<");
+ m_aEditBuffer.WriteBytes(static_cast<const char*>(m_aEditBuffer.GetData())
+ + pAcroFormObject->GetDictionaryOffset(),
+ nFieldsBeforeEndLength);
+ }
+
+ // Append our reference at the end of the Fields array.
+ m_aEditBuffer.WriteCharPtr(" ");
+ m_aEditBuffer.WriteUInt32AsString(nAnnotId);
+ m_aEditBuffer.WriteCharPtr(" 0 R");
+
+ // Length of Fields end -> end of the object dictionary.
+ if (pStreamBuffer)
+ {
+ sal_uInt64 nFieldsAfterEndLength = pStreamBuffer->GetSize() - nFieldsEndOffset;
+ m_aEditBuffer.WriteBytes(static_cast<const char*>(pStreamBuffer->GetData())
+ + nFieldsEndOffset,
+ nFieldsAfterEndLength);
+ }
+ else
+ {
+ sal_uInt64 nFieldsAfterEndLength = pAcroFormObject->GetDictionaryOffset()
+ + pAcroFormObject->GetDictionaryLength()
+ - nFieldsEndOffset;
+ m_aEditBuffer.WriteBytes(static_cast<const char*>(m_aEditBuffer.GetData())
+ + nFieldsEndOffset,
+ nFieldsAfterEndLength);
+ m_aEditBuffer.WriteCharPtr(">>");
+ }
+
+ m_aEditBuffer.WriteCharPtr("\nendobj\n\n");
+ }
+ else
+ {
+ // Write the updated Catalog object, references nAnnotId.
+ auto pAcroFormDictionary = dynamic_cast<PDFDictionaryElement*>(pAcroForm);
+ m_aXRef[nCatalogId].SetOffset(m_aEditBuffer.Tell());
+ m_aXRef[nCatalogId].SetDirty(true);
+ m_aEditBuffer.WriteUInt32AsString(nCatalogId);
+ m_aEditBuffer.WriteCharPtr(" 0 obj\n");
+ m_aEditBuffer.WriteCharPtr("<<");
+ if (!pAcroFormDictionary)
+ {
+ // No AcroForm key, assume no signatures.
+ m_aEditBuffer.WriteBytes(static_cast<const char*>(m_aEditBuffer.GetData())
+ + pCatalog->GetDictionaryOffset(),
+ pCatalog->GetDictionaryLength());
+ m_aEditBuffer.WriteCharPtr("/AcroForm<</Fields[\n");
+ m_aEditBuffer.WriteUInt32AsString(nAnnotId);
+ m_aEditBuffer.WriteCharPtr(" 0 R\n]/SigFlags 3>>\n");
+ }
+ else
+ {
+ // AcroForm key is already there, insert our reference at the Fields end.
+ auto it = pAcroFormDictionary->GetItems().find("Fields");
+ if (it == pAcroFormDictionary->GetItems().end())
+ {
+ SAL_WARN("vcl.filter", "PDFDocument::Sign: AcroForm without required Fields key");
+ return false;
+ }
+
+ auto pFields = dynamic_cast<PDFArrayElement*>(it->second);
+ if (!pFields)
+ {
+ SAL_WARN("vcl.filter", "PDFDocument::Sign: AcroForm Fields is not an array");
+ return false;
+ }
+
+ // Offset right before the end of the Fields array.
+ sal_uInt64 nFieldsEndOffset = pAcroFormDictionary->GetKeyOffset("Fields")
+ + pAcroFormDictionary->GetKeyValueLength("Fields") - 1;
+ // Length of beginning of the Catalog dictionary -> Fields end.
+ sal_uInt64 nFieldsBeforeEndLength = nFieldsEndOffset - pCatalog->GetDictionaryOffset();
+ m_aEditBuffer.WriteBytes(static_cast<const char*>(m_aEditBuffer.GetData())
+ + pCatalog->GetDictionaryOffset(),
+ nFieldsBeforeEndLength);
+ m_aEditBuffer.WriteCharPtr(" ");
+ m_aEditBuffer.WriteUInt32AsString(nAnnotId);
+ m_aEditBuffer.WriteCharPtr(" 0 R");
+ // Length of Fields end -> end of the Catalog dictionary.
+ sal_uInt64 nFieldsAfterEndLength = pCatalog->GetDictionaryOffset()
+ + pCatalog->GetDictionaryLength() - nFieldsEndOffset;
+ m_aEditBuffer.WriteBytes(static_cast<const char*>(m_aEditBuffer.GetData())
+ + nFieldsEndOffset,
+ nFieldsAfterEndLength);
+ }
+ m_aEditBuffer.WriteCharPtr(">>\nendobj\n\n");
+ }
+
+ return true;
+}
+
+void PDFDocument::WriteXRef(sal_uInt64 nXRefOffset, PDFReferenceElement const* pRoot)
+{
+ if (m_pXRefStream)
+ {
+ // Write the xref stream.
+ // This is a bit meta: the xref stream stores its own offset.
+ sal_Int32 nXRefStreamId = m_aXRef.size();
+ XRefEntry aXRefStreamEntry;
+ aXRefStreamEntry.SetOffset(nXRefOffset);
+ aXRefStreamEntry.SetDirty(true);
+ m_aXRef[nXRefStreamId] = aXRefStreamEntry;
+
+ // Write stream data.
+ SvMemoryStream aXRefStream;
+ const size_t nOffsetLen = 3;
+ // 3 additional bytes: predictor, the first and the third field.
+ const size_t nLineLength = nOffsetLen + 3;
+ // This is the line as it appears before tweaking according to the predictor.
+ std::vector<unsigned char> aOrigLine(nLineLength);
+ // This is the previous line.
+ std::vector<unsigned char> aPrevLine(nLineLength);
+ // This is the line as written to the stream.
+ std::vector<unsigned char> aFilteredLine(nLineLength);
+ for (const auto& rXRef : m_aXRef)
+ {
+ const XRefEntry& rEntry = rXRef.second;
+
+ if (!rEntry.GetDirty())
+ continue;
+
+ // Predictor.
+ size_t nPos = 0;
+ // PNG prediction: up (on all rows).
+ aOrigLine[nPos++] = 2;
+
+ // First field.
+ unsigned char nType = 0;
+ switch (rEntry.GetType())
+ {
+ case XRefEntryType::FREE:
+ nType = 0;
+ break;
+ case XRefEntryType::NOT_COMPRESSED:
+ nType = 1;
+ break;
+ case XRefEntryType::COMPRESSED:
+ nType = 2;
+ break;
+ }
+ aOrigLine[nPos++] = nType;
+
+ // Second field.
+ for (size_t i = 0; i < nOffsetLen; ++i)
+ {
+ size_t nByte = nOffsetLen - i - 1;
+ // Fields requiring more than one byte are stored with the
+ // high-order byte first.
+ unsigned char nCh = (rEntry.GetOffset() & (0xff << (nByte * 8))) >> (nByte * 8);
+ aOrigLine[nPos++] = nCh;
+ }
+
+ // Third field.
+ aOrigLine[nPos++] = 0;
+
+ // Now apply the predictor.
+ aFilteredLine[0] = aOrigLine[0];
+ for (size_t i = 1; i < nLineLength; ++i)
+ {
+ // Count the delta vs the previous line.
+ aFilteredLine[i] = aOrigLine[i] - aPrevLine[i];
+ // Remember the new reference.
+ aPrevLine[i] = aOrigLine[i];
+ }
+
+ aXRefStream.WriteBytes(aFilteredLine.data(), aFilteredLine.size());
+ }
+
+ m_aEditBuffer.WriteUInt32AsString(nXRefStreamId);
+ m_aEditBuffer.WriteCharPtr(
+ " 0 obj\n<</DecodeParms<</Columns 5/Predictor 12>>/Filter/FlateDecode");
+
+ // ID.
+ auto pID = dynamic_cast<PDFArrayElement*>(m_pXRefStream->Lookup("ID"));
+ if (pID)
+ {
+ const std::vector<PDFElement*>& rElements = pID->GetElements();
+ m_aEditBuffer.WriteCharPtr("/ID [ <");
+ for (size_t i = 0; i < rElements.size(); ++i)
+ {
+ auto pIDString = dynamic_cast<PDFHexStringElement*>(rElements[i]);
+ if (!pIDString)
+ continue;
+
+ m_aEditBuffer.WriteOString(pIDString->GetValue());
+ if ((i + 1) < rElements.size())
+ m_aEditBuffer.WriteCharPtr("> <");
+ }
+ m_aEditBuffer.WriteCharPtr("> ] ");
+ }
+
+ // Index.
+ m_aEditBuffer.WriteCharPtr("/Index [ ");
+ for (const auto& rXRef : m_aXRef)
+ {
+ if (!rXRef.second.GetDirty())
+ continue;
+
+ m_aEditBuffer.WriteUInt32AsString(rXRef.first);
+ m_aEditBuffer.WriteCharPtr(" 1 ");
+ }
+ m_aEditBuffer.WriteCharPtr("] ");
+
+ // Info.
+ auto pInfo = dynamic_cast<PDFReferenceElement*>(m_pXRefStream->Lookup("Info"));
+ if (pInfo)
+ {
+ m_aEditBuffer.WriteCharPtr("/Info ");
+ m_aEditBuffer.WriteUInt32AsString(pInfo->GetObjectValue());
+ m_aEditBuffer.WriteCharPtr(" ");
+ m_aEditBuffer.WriteUInt32AsString(pInfo->GetGenerationValue());
+ m_aEditBuffer.WriteCharPtr(" R ");
+ }
+
+ // Length.
+ m_aEditBuffer.WriteCharPtr("/Length ");
+ {
+ ZCodec aZCodec;
+ aZCodec.BeginCompression();
+ aXRefStream.Seek(0);
+ SvMemoryStream aStream;
+ aZCodec.Compress(aXRefStream, aStream);
+ aZCodec.EndCompression();
+ aXRefStream.Seek(0);
+ aXRefStream.SetStreamSize(0);
+ aStream.Seek(0);
+ aXRefStream.WriteStream(aStream);
+ }
+ m_aEditBuffer.WriteUInt32AsString(aXRefStream.GetSize());
+
+ if (!m_aStartXRefs.empty())
+ {
+ // Write location of the previous cross-reference section.
+ m_aEditBuffer.WriteCharPtr("/Prev ");
+ m_aEditBuffer.WriteUInt32AsString(m_aStartXRefs.back());
+ }
+
+ // Root.
+ m_aEditBuffer.WriteCharPtr("/Root ");
+ m_aEditBuffer.WriteUInt32AsString(pRoot->GetObjectValue());
+ m_aEditBuffer.WriteCharPtr(" ");
+ m_aEditBuffer.WriteUInt32AsString(pRoot->GetGenerationValue());
+ m_aEditBuffer.WriteCharPtr(" R ");
+
+ // Size.
+ m_aEditBuffer.WriteCharPtr("/Size ");
+ m_aEditBuffer.WriteUInt32AsString(m_aXRef.size());
+
+ m_aEditBuffer.WriteCharPtr("/Type/XRef/W[1 3 1]>>\nstream\n");
+ aXRefStream.Seek(0);
+ m_aEditBuffer.WriteStream(aXRefStream);
+ m_aEditBuffer.WriteCharPtr("\nendstream\nendobj\n\n");
+ }
+ else
+ {
+ // Write the xref table.
+ m_aEditBuffer.WriteCharPtr("xref\n");
+ for (const auto& rXRef : m_aXRef)
+ {
+ size_t nObject = rXRef.first;
+ size_t nOffset = rXRef.second.GetOffset();
+ if (!rXRef.second.GetDirty())
+ continue;
+
+ m_aEditBuffer.WriteUInt32AsString(nObject);
+ m_aEditBuffer.WriteCharPtr(" 1\n");
+ OStringBuffer aBuffer;
+ aBuffer.append(static_cast<sal_Int32>(nOffset));
+ while (aBuffer.getLength() < 10)
+ aBuffer.insert(0, "0");
+ if (nObject == 0)
+ aBuffer.append(" 65535 f \n");
+ else
+ aBuffer.append(" 00000 n \n");
+ m_aEditBuffer.WriteOString(aBuffer);
+ }
+
+ // Write the trailer.
+ m_aEditBuffer.WriteCharPtr("trailer\n<</Size ");
+ m_aEditBuffer.WriteUInt32AsString(m_aXRef.size());
+ m_aEditBuffer.WriteCharPtr("/Root ");
+ m_aEditBuffer.WriteUInt32AsString(pRoot->GetObjectValue());
+ m_aEditBuffer.WriteCharPtr(" ");
+ m_aEditBuffer.WriteUInt32AsString(pRoot->GetGenerationValue());
+ m_aEditBuffer.WriteCharPtr(" R\n");
+ auto pInfo = dynamic_cast<PDFReferenceElement*>(m_pTrailer->Lookup("Info"));
+ if (pInfo)
+ {
+ m_aEditBuffer.WriteCharPtr("/Info ");
+ m_aEditBuffer.WriteUInt32AsString(pInfo->GetObjectValue());
+ m_aEditBuffer.WriteCharPtr(" ");
+ m_aEditBuffer.WriteUInt32AsString(pInfo->GetGenerationValue());
+ m_aEditBuffer.WriteCharPtr(" R\n");
+ }
+ auto pID = dynamic_cast<PDFArrayElement*>(m_pTrailer->Lookup("ID"));
+ if (pID)
+ {
+ const std::vector<PDFElement*>& rElements = pID->GetElements();
+ m_aEditBuffer.WriteCharPtr("/ID [ <");
+ for (size_t i = 0; i < rElements.size(); ++i)
+ {
+ auto pIDString = dynamic_cast<PDFHexStringElement*>(rElements[i]);
+ if (!pIDString)
+ continue;
+
+ m_aEditBuffer.WriteOString(pIDString->GetValue());
+ if ((i + 1) < rElements.size())
+ m_aEditBuffer.WriteCharPtr(">\n<");
+ }
+ m_aEditBuffer.WriteCharPtr("> ]\n");
+ }
+
+ if (!m_aStartXRefs.empty())
+ {
+ // Write location of the previous cross-reference section.
+ m_aEditBuffer.WriteCharPtr("/Prev ");
+ m_aEditBuffer.WriteUInt32AsString(m_aStartXRefs.back());
+ }
+
+ m_aEditBuffer.WriteCharPtr(">>\n");
+ }
+}
+
+bool PDFDocument::Sign(const uno::Reference<security::XCertificate>& xCertificate,
+ const OUString& rDescription, bool bAdES)
+{
+ m_aEditBuffer.Seek(STREAM_SEEK_TO_END);
+ m_aEditBuffer.WriteCharPtr("\n");
+
+ sal_uInt64 nSignatureLastByteRangeOffset = 0;
+ sal_Int64 nSignatureContentOffset = 0;
+ sal_Int32 nSignatureId = WriteSignatureObject(
+ rDescription, bAdES, nSignatureLastByteRangeOffset, nSignatureContentOffset);
+
+ tools::Rectangle aSignatureRectangle;
+ sal_Int32 nAppearanceId = WriteAppearanceObject(aSignatureRectangle);
+
+ std::vector<PDFObjectElement*> aPages = GetPages();
+ if (aPages.empty())
+ {
+ SAL_WARN("vcl.filter", "PDFDocument::Sign: found no pages");
+ return false;
+ }
+
+ size_t nPage = 0;
+ if (m_nSignaturePage < aPages.size())
+ {
+ nPage = m_nSignaturePage;
+ }
+ if (!aPages[nPage])
+ {
+ SAL_WARN("vcl.filter", "PDFDocument::Sign: failed to find page #" << nPage);
+ return false;
+ }
+
+ PDFObjectElement& rPage = *aPages[nPage];
+ sal_Int32 nAnnotId = WriteAnnotObject(rPage, nSignatureId, nAppearanceId, aSignatureRectangle);
+
+ if (!WritePageObject(rPage, nAnnotId))
+ {
+ SAL_WARN("vcl.filter", "PDFDocument::Sign: failed to write the updated Page object");
+ return false;
+ }
+
+ PDFReferenceElement* pRoot = nullptr;
+ if (!WriteCatalogObject(nAnnotId, pRoot))
+ {
+ SAL_WARN("vcl.filter", "PDFDocument::Sign: failed to write the updated Catalog object");
+ return false;
+ }
+
+ sal_uInt64 nXRefOffset = m_aEditBuffer.Tell();
+ WriteXRef(nXRefOffset, pRoot);
+
+ // Write startxref.
+ m_aEditBuffer.WriteCharPtr("startxref\n");
+ m_aEditBuffer.WriteUInt32AsString(nXRefOffset);
+ m_aEditBuffer.WriteCharPtr("\n%%EOF\n");
+
+ // Finalize the signature, now that we know the total file size.
+ // Calculate the length of the last byte range.
+ sal_uInt64 nFileEnd = m_aEditBuffer.Tell();
+ sal_Int64 nLastByteRangeLength
+ = nFileEnd - (nSignatureContentOffset + MAX_SIGNATURE_CONTENT_LENGTH + 1);
+ // Write the length to the buffer.
+ m_aEditBuffer.Seek(nSignatureLastByteRangeOffset);
+ OString aByteRangeBuffer = OString::number(nLastByteRangeLength) + " ]";
+ m_aEditBuffer.WriteOString(aByteRangeBuffer);
+
+ // Create the PKCS#7 object.
+ css::uno::Sequence<sal_Int8> aDerEncoded = xCertificate->getEncoded();
+ if (!aDerEncoded.hasElements())
+ {
+ SAL_WARN("vcl.filter", "PDFDocument::Sign: empty certificate");
+ return false;
+ }
+
+ m_aEditBuffer.Seek(0);
+ sal_uInt64 nBufferSize1 = nSignatureContentOffset - 1;
+ std::unique_ptr<char[]> aBuffer1(new char[nBufferSize1]);
+ m_aEditBuffer.ReadBytes(aBuffer1.get(), nBufferSize1);
+
+ m_aEditBuffer.Seek(nSignatureContentOffset + MAX_SIGNATURE_CONTENT_LENGTH + 1);
+ sal_uInt64 nBufferSize2 = nLastByteRangeLength;
+ std::unique_ptr<char[]> aBuffer2(new char[nBufferSize2]);
+ m_aEditBuffer.ReadBytes(aBuffer2.get(), nBufferSize2);
+
+ OStringBuffer aCMSHexBuffer;
+ svl::crypto::Signing aSigning(xCertificate);
+ aSigning.AddDataRange(aBuffer1.get(), nBufferSize1);
+ aSigning.AddDataRange(aBuffer2.get(), nBufferSize2);
+ if (!aSigning.Sign(aCMSHexBuffer))
+ {
+ SAL_WARN("vcl.filter", "PDFDocument::Sign: PDFWriter::Sign() failed");
+ return false;
+ }
+
+ assert(aCMSHexBuffer.getLength() <= MAX_SIGNATURE_CONTENT_LENGTH);
+
+ m_aEditBuffer.Seek(nSignatureContentOffset);
+ m_aEditBuffer.WriteOString(aCMSHexBuffer);
+
+ return true;
+}
+
+bool PDFDocument::Write(SvStream& rStream)
+{
+ m_aEditBuffer.Seek(0);
+ rStream.WriteStream(m_aEditBuffer);
+ return rStream.good();
+}
+
+bool PDFDocument::Tokenize(SvStream& rStream, TokenizeMode eMode,
+ std::vector<std::unique_ptr<PDFElement>>& rElements,
+ PDFObjectElement* pObjectElement)
+{
+ // Last seen object token.
+ PDFObjectElement* pObject = pObjectElement;
+ PDFNameElement* pObjectKey = nullptr;
+ PDFObjectElement* pObjectStream = nullptr;
+ bool bInXRef = false;
+ // The next number will be an xref offset.
+ bool bInStartXRef = false;
+ // Dictionary depth, so we know when we're outside any dictionaries.
+ int nDepth = 0;
+ // Last seen array token that's outside any dictionaries.
+ PDFArrayElement* pArray = nullptr;
+ // If we're inside an obj/endobj pair.
+ bool bInObject = false;
+
+ while (true)
+ {
+ char ch;
+ rStream.ReadChar(ch);
+ if (rStream.eof())
+ break;
+
+ switch (ch)
+ {
+ case '%':
+ {
+ auto pComment = new PDFCommentElement(*this);
+ rElements.push_back(std::unique_ptr<PDFElement>(pComment));
+ rStream.SeekRel(-1);
+ if (!rElements.back()->Read(rStream))
+ {
+ SAL_WARN("vcl.filter",
+ "PDFDocument::Tokenize: PDFCommentElement::Read() failed");
+ return false;
+ }
+ if (eMode == TokenizeMode::EOF_TOKEN && !m_aEOFs.empty()
+ && m_aEOFs.back() == rStream.Tell())
+ {
+ // Found EOF and partial parsing requested, we're done.
+ return true;
+ }
+ break;
+ }
+ case '<':
+ {
+ // Dictionary or hex string.
+ rStream.ReadChar(ch);
+ rStream.SeekRel(-2);
+ if (ch == '<')
+ {
+ rElements.push_back(std::unique_ptr<PDFElement>(new PDFDictionaryElement()));
+ ++nDepth;
+ }
+ else
+ rElements.push_back(std::unique_ptr<PDFElement>(new PDFHexStringElement));
+ if (!rElements.back()->Read(rStream))
+ {
+ SAL_WARN("vcl.filter",
+ "PDFDocument::Tokenize: PDFDictionaryElement::Read() failed");
+ return false;
+ }
+ break;
+ }
+ case '>':
+ {
+ rElements.push_back(std::unique_ptr<PDFElement>(new PDFEndDictionaryElement()));
+ --nDepth;
+ rStream.SeekRel(-1);
+ if (!rElements.back()->Read(rStream))
+ {
+ SAL_WARN("vcl.filter",
+ "PDFDocument::Tokenize: PDFEndDictionaryElement::Read() failed");
+ return false;
+ }
+ break;
+ }
+ case '[':
+ {
+ auto pArr = new PDFArrayElement(pObject);
+ rElements.push_back(std::unique_ptr<PDFElement>(pArr));
+ if (nDepth == 0)
+ {
+ // The array is attached directly, inform the object.
+ pArray = pArr;
+ if (pObject)
+ {
+ pObject->SetArray(pArray);
+ pObject->SetArrayOffset(rStream.Tell());
+ }
+ }
+ ++nDepth;
+ rStream.SeekRel(-1);
+ if (!rElements.back()->Read(rStream))
+ {
+ SAL_WARN("vcl.filter", "PDFDocument::Tokenize: PDFArrayElement::Read() failed");
+ return false;
+ }
+ break;
+ }
+ case ']':
+ {
+ rElements.push_back(std::unique_ptr<PDFElement>(new PDFEndArrayElement()));
+ --nDepth;
+ rStream.SeekRel(-1);
+ if (nDepth == 0)
+ {
+ if (pObject)
+ {
+ pObject->SetArrayLength(rStream.Tell() - pObject->GetArrayOffset());
+ }
+ }
+ if (!rElements.back()->Read(rStream))
+ {
+ SAL_WARN("vcl.filter",
+ "PDFDocument::Tokenize: PDFEndArrayElement::Read() failed");
+ return false;
+ }
+ break;
+ }
+ case '/':
+ {
+ auto pNameElement = new PDFNameElement();
+ rElements.push_back(std::unique_ptr<PDFElement>(pNameElement));
+ rStream.SeekRel(-1);
+ if (!pNameElement->Read(rStream))
+ {
+ SAL_WARN("vcl.filter", "PDFDocument::Tokenize: PDFNameElement::Read() failed");
+ return false;
+ }
+
+ if (pObject && pObjectKey && pObjectKey->GetValue() == "Type"
+ && pNameElement->GetValue() == "ObjStm")
+ pObjectStream = pObject;
+ else
+ pObjectKey = pNameElement;
+ break;
+ }
+ case '(':
+ {
+ rElements.push_back(std::unique_ptr<PDFElement>(new PDFLiteralStringElement));
+ rStream.SeekRel(-1);
+ if (!rElements.back()->Read(rStream))
+ {
+ SAL_WARN("vcl.filter",
+ "PDFDocument::Tokenize: PDFLiteralStringElement::Read() failed");
+ return false;
+ }
+ break;
+ }
+ default:
+ {
+ if (rtl::isAsciiDigit(static_cast<unsigned char>(ch)) || ch == '-' || ch == '+'
+ || ch == '.')
+ {
+ // Numbering object: an integer or a real.
+ auto pNumberElement = new PDFNumberElement();
+ rElements.push_back(std::unique_ptr<PDFElement>(pNumberElement));
+ rStream.SeekRel(-1);
+ if (!pNumberElement->Read(rStream))
+ {
+ SAL_WARN("vcl.filter",
+ "PDFDocument::Tokenize: PDFNumberElement::Read() failed");
+ return false;
+ }
+ if (bInStartXRef)
+ {
+ bInStartXRef = false;
+ m_aStartXRefs.push_back(pNumberElement->GetValue());
+
+ auto it = m_aOffsetObjects.find(pNumberElement->GetValue());
+ if (it != m_aOffsetObjects.end())
+ m_pXRefStream = it->second;
+ }
+ else if (bInObject && !nDepth && pObject)
+ // Number element inside an object, but outside a
+ // dictionary / array: remember it.
+ pObject->SetNumberElement(pNumberElement);
+ }
+ else if (rtl::isAsciiAlpha(static_cast<unsigned char>(ch)))
+ {
+ // Possible keyword, like "obj".
+ rStream.SeekRel(-1);
+ OString aKeyword = ReadKeyword(rStream);
+
+ bool bObj = aKeyword == "obj";
+ if (bObj || aKeyword == "R")
+ {
+ size_t nElements = rElements.size();
+ if (nElements < 2)
+ {
+ SAL_WARN("vcl.filter", "PDFDocument::Tokenize: expected at least two "
+ "tokens before 'obj' or 'R' keyword");
+ return false;
+ }
+
+ auto pObjectNumber
+ = dynamic_cast<PDFNumberElement*>(rElements[nElements - 2].get());
+ auto pGenerationNumber
+ = dynamic_cast<PDFNumberElement*>(rElements[nElements - 1].get());
+ if (!pObjectNumber || !pGenerationNumber)
+ {
+ SAL_WARN("vcl.filter", "PDFDocument::Tokenize: missing object or "
+ "generation number before 'obj' or 'R' keyword");
+ return false;
+ }
+
+ if (bObj)
+ {
+ pObject = new PDFObjectElement(*this, pObjectNumber->GetValue(),
+ pGenerationNumber->GetValue());
+ rElements.push_back(std::unique_ptr<PDFElement>(pObject));
+ m_aOffsetObjects[pObjectNumber->GetLocation()] = pObject;
+ m_aIDObjects[pObjectNumber->GetValue()] = pObject;
+ bInObject = true;
+ }
+ else
+ {
+ auto pReference = new PDFReferenceElement(*this, *pObjectNumber,
+ *pGenerationNumber);
+ rElements.push_back(std::unique_ptr<PDFElement>(pReference));
+ if (bInObject && nDepth > 0 && pObject)
+ // Inform the object about a new in-dictionary reference.
+ pObject->AddDictionaryReference(pReference);
+ }
+ if (!rElements.back()->Read(rStream))
+ {
+ SAL_WARN("vcl.filter",
+ "PDFDocument::Tokenize: PDFElement::Read() failed");
+ return false;
+ }
+ }
+ else if (aKeyword == "stream")
+ {
+ // Look up the length of the stream from the parent object's dictionary.
+ size_t nLength = 0;
+ for (size_t nElement = 0; nElement < rElements.size(); ++nElement)
+ {
+ // Iterate in reverse order.
+ size_t nIndex = rElements.size() - nElement - 1;
+ PDFElement* pElement = rElements[nIndex].get();
+ auto pObj = dynamic_cast<PDFObjectElement*>(pElement);
+ if (!pObj)
+ continue;
+
+ PDFElement* pLookup = pObj->Lookup("Length");
+ auto pReference = dynamic_cast<PDFReferenceElement*>(pLookup);
+ if (pReference)
+ {
+ // Length is provided as a reference.
+ nLength = pReference->LookupNumber(rStream);
+ break;
+ }
+
+ auto pNumber = dynamic_cast<PDFNumberElement*>(pLookup);
+ if (pNumber)
+ {
+ // Length is provided directly.
+ nLength = pNumber->GetValue();
+ break;
+ }
+
+ SAL_WARN(
+ "vcl.filter",
+ "PDFDocument::Tokenize: found no Length key for stream keyword");
+ return false;
+ }
+
+ PDFDocument::SkipLineBreaks(rStream);
+ auto pStreamElement = new PDFStreamElement(nLength);
+ if (pObject)
+ pObject->SetStream(pStreamElement);
+ rElements.push_back(std::unique_ptr<PDFElement>(pStreamElement));
+ if (!rElements.back()->Read(rStream))
+ {
+ SAL_WARN("vcl.filter",
+ "PDFDocument::Tokenize: PDFStreamElement::Read() failed");
+ return false;
+ }
+ }
+ else if (aKeyword == "endstream")
+ {
+ rElements.push_back(std::unique_ptr<PDFElement>(new PDFEndStreamElement));
+ if (!rElements.back()->Read(rStream))
+ {
+ SAL_WARN("vcl.filter",
+ "PDFDocument::Tokenize: PDFEndStreamElement::Read() failed");
+ return false;
+ }
+ }
+ else if (aKeyword == "endobj")
+ {
+ rElements.push_back(std::unique_ptr<PDFElement>(new PDFEndObjectElement));
+ if (!rElements.back()->Read(rStream))
+ {
+ SAL_WARN("vcl.filter",
+ "PDFDocument::Tokenize: PDFEndObjectElement::Read() failed");
+ return false;
+ }
+ if (eMode == TokenizeMode::END_OF_OBJECT)
+ {
+ // Found endobj and only object parsing was requested, we're done.
+ return true;
+ }
+
+ if (pObjectStream)
+ {
+ // We're at the end of an object stream, parse the stored objects.
+ pObjectStream->ParseStoredObjects();
+ pObjectStream = nullptr;
+ pObjectKey = nullptr;
+ }
+ bInObject = false;
+ }
+ else if (aKeyword == "true" || aKeyword == "false")
+ rElements.push_back(std::unique_ptr<PDFElement>(
+ new PDFBooleanElement(aKeyword.toBoolean())));
+ else if (aKeyword == "null")
+ rElements.push_back(std::unique_ptr<PDFElement>(new PDFNullElement));
+ else if (aKeyword == "xref")
+ // Allow 'f' and 'n' keywords.
+ bInXRef = true;
+ else if (bInXRef && (aKeyword == "f" || aKeyword == "n"))
+ {
+ }
+ else if (aKeyword == "trailer")
+ {
+ auto pTrailer = new PDFTrailerElement(*this);
+
+ // Make it possible to find this trailer later by offset.
+ pTrailer->Read(rStream);
+ m_aOffsetTrailers[pTrailer->GetLocation()] = pTrailer;
+
+ // When reading till the first EOF token only, remember
+ // just the first trailer token.
+ if (eMode != TokenizeMode::EOF_TOKEN || !m_pTrailer)
+ m_pTrailer = pTrailer;
+ rElements.push_back(std::unique_ptr<PDFElement>(pTrailer));
+ }
+ else if (aKeyword == "startxref")
+ {
+ bInStartXRef = true;
+ }
+ else
+ {
+ SAL_WARN("vcl.filter", "PDFDocument::Tokenize: unexpected '"
+ << aKeyword << "' keyword at byte position "
+ << rStream.Tell());
+ return false;
+ }
+ }
+ else
+ {
+ auto uChar = static_cast<unsigned char>(ch);
+ // Be more lenient and allow unexpected null char
+ if (!rtl::isAsciiWhiteSpace(uChar) && uChar != 0)
+ {
+ SAL_WARN("vcl.filter",
+ "PDFDocument::Tokenize: unexpected character with code "
+ << sal_Int32(ch) << " at byte position " << rStream.Tell());
+ return false;
+ }
+ SAL_WARN_IF(uChar == 0, "vcl.filter",
+ "PDFDocument::Tokenize: unexpected null character at "
+ << rStream.Tell() << " - ignoring");
+ }
+ break;
+ }
+ }
+ }
+
+ return true;
+}
+
+void PDFDocument::SetIDObject(size_t nID, PDFObjectElement* pObject)
+{
+ m_aIDObjects[nID] = pObject;
+}
+
+bool PDFDocument::ReadWithPossibleFixup(SvStream& rStream)
+{
+ if (Read(rStream))
+ return true;
+
+ // Read failed, try a roundtrip through pdfium and then retry.
+ rStream.Seek(0);
+ SvMemoryStream aStandardizedStream;
+ vcl::pdf::convertToHighestSupported(rStream, aStandardizedStream);
+ return Read(aStandardizedStream);
+}
+
+bool PDFDocument::Read(SvStream& rStream)
+{
+ // Check file magic.
+ std::vector<sal_Int8> aHeader(5);
+ rStream.Seek(0);
+ rStream.ReadBytes(aHeader.data(), aHeader.size());
+ if (aHeader[0] != '%' || aHeader[1] != 'P' || aHeader[2] != 'D' || aHeader[3] != 'F'
+ || aHeader[4] != '-')
+ {
+ SAL_WARN("vcl.filter", "PDFDocument::Read: header mismatch");
+ return false;
+ }
+
+ // Allow later editing of the contents in-memory.
+ rStream.Seek(0);
+ m_aEditBuffer.WriteStream(rStream);
+
+ // Look up the offset of the xref table.
+ size_t nStartXRef = FindStartXRef(rStream);
+ SAL_INFO("vcl.filter", "PDFDocument::Read: nStartXRef is " << nStartXRef);
+ if (nStartXRef == 0)
+ {
+ SAL_WARN("vcl.filter", "PDFDocument::Read: found no xref start offset");
+ return false;
+ }
+ while (true)
+ {
+ rStream.Seek(nStartXRef);
+ OString aKeyword = ReadKeyword(rStream);
+ if (aKeyword.isEmpty())
+ ReadXRefStream(rStream);
+
+ else
+ {
+ if (aKeyword != "xref")
+ {
+ SAL_WARN("vcl.filter", "PDFDocument::Read: xref is not the first keyword");
+ return false;
+ }
+ ReadXRef(rStream);
+ if (!Tokenize(rStream, TokenizeMode::EOF_TOKEN, m_aElements, nullptr))
+ {
+ SAL_WARN("vcl.filter", "PDFDocument::Read: failed to tokenizer trailer after xref");
+ return false;
+ }
+ }
+
+ PDFNumberElement* pPrev = nullptr;
+ if (m_pTrailer)
+ {
+ pPrev = dynamic_cast<PDFNumberElement*>(m_pTrailer->Lookup("Prev"));
+
+ // Remember the offset of this trailer in the correct order. It's
+ // possible that newer trailers don't have a larger offset.
+ m_aTrailerOffsets.push_back(m_pTrailer->GetLocation());
+ }
+ else if (m_pXRefStream)
+ pPrev = dynamic_cast<PDFNumberElement*>(m_pXRefStream->Lookup("Prev"));
+ if (pPrev)
+ nStartXRef = pPrev->GetValue();
+
+ // Reset state, except the edit buffer.
+ m_aElements.clear();
+ m_aOffsetObjects.clear();
+ m_aIDObjects.clear();
+ m_aStartXRefs.clear();
+ m_aEOFs.clear();
+ m_pTrailer = nullptr;
+ m_pXRefStream = nullptr;
+ if (!pPrev)
+ break;
+ }
+
+ // Then we can tokenize the stream.
+ rStream.Seek(0);
+ return Tokenize(rStream, TokenizeMode::END_OF_STREAM, m_aElements, nullptr);
+}
+
+OString PDFDocument::ReadKeyword(SvStream& rStream)
+{
+ OStringBuffer aBuf;
+ char ch;
+ rStream.ReadChar(ch);
+ if (rStream.eof())
+ return {};
+ while (rtl::isAsciiAlpha(static_cast<unsigned char>(ch)))
+ {
+ aBuf.append(ch);
+ rStream.ReadChar(ch);
+ if (rStream.eof())
+ return aBuf.toString();
+ }
+ rStream.SeekRel(-1);
+ return aBuf.toString();
+}
+
+size_t PDFDocument::FindStartXRef(SvStream& rStream)
+{
+ // Find the "startxref" token, somewhere near the end of the document.
+ std::vector<char> aBuf(1024);
+ rStream.Seek(STREAM_SEEK_TO_END);
+ if (rStream.Tell() > aBuf.size())
+ rStream.SeekRel(static_cast<sal_Int64>(-1) * aBuf.size());
+ else
+ // The document is really short, then just read it from the start.
+ rStream.Seek(0);
+ size_t nBeforePeek = rStream.Tell();
+ size_t nSize = rStream.ReadBytes(aBuf.data(), aBuf.size());
+ rStream.Seek(nBeforePeek);
+ if (nSize != aBuf.size())
+ aBuf.resize(nSize);
+ OString aPrefix("startxref");
+ // Find the last startxref at the end of the document.
+ auto itLastValid = aBuf.end();
+ auto it = aBuf.begin();
+ while (true)
+ {
+ it = std::search(it, aBuf.end(), aPrefix.getStr(), aPrefix.getStr() + aPrefix.getLength());
+ if (it == aBuf.end())
+ break;
+
+ itLastValid = it;
+ ++it;
+ }
+ if (itLastValid == aBuf.end())
+ {
+ SAL_WARN("vcl.filter", "PDFDocument::FindStartXRef: found no startxref");
+ return 0;
+ }
+
+ rStream.SeekRel(itLastValid - aBuf.begin() + aPrefix.getLength());
+ if (rStream.eof())
+ {
+ SAL_WARN("vcl.filter",
+ "PDFDocument::FindStartXRef: unexpected end of stream after startxref");
+ return 0;
+ }
+
+ PDFDocument::SkipWhitespace(rStream);
+ PDFNumberElement aNumber;
+ if (!aNumber.Read(rStream))
+ return 0;
+ return aNumber.GetValue();
+}
+
+void PDFDocument::ReadXRefStream(SvStream& rStream)
+{
+ // Look up the stream length in the object dictionary.
+ if (!Tokenize(rStream, TokenizeMode::END_OF_OBJECT, m_aElements, nullptr))
+ {
+ SAL_WARN("vcl.filter", "PDFDocument::ReadXRefStream: failed to read object");
+ return;
+ }
+
+ if (m_aElements.empty())
+ {
+ SAL_WARN("vcl.filter", "PDFDocument::ReadXRefStream: no tokens found");
+ return;
+ }
+
+ PDFObjectElement* pObject = nullptr;
+ for (const auto& pElement : m_aElements)
+ {
+ if (auto pObj = dynamic_cast<PDFObjectElement*>(pElement.get()))
+ {
+ pObject = pObj;
+ break;
+ }
+ }
+ if (!pObject)
+ {
+ SAL_WARN("vcl.filter", "PDFDocument::ReadXRefStream: no object token found");
+ return;
+ }
+
+ // So that the Prev key can be looked up later.
+ m_pXRefStream = pObject;
+
+ PDFElement* pLookup = pObject->Lookup("Length");
+ auto pNumber = dynamic_cast<PDFNumberElement*>(pLookup);
+ if (!pNumber)
+ {
+ SAL_WARN("vcl.filter", "PDFDocument::ReadXRefStream: stream length is not provided");
+ return;
+ }
+ sal_uInt64 nLength = pNumber->GetValue();
+
+ // Look up the stream offset.
+ PDFStreamElement* pStream = nullptr;
+ for (const auto& pElement : m_aElements)
+ {
+ if (auto pS = dynamic_cast<PDFStreamElement*>(pElement.get()))
+ {
+ pStream = pS;
+ break;
+ }
+ }
+ if (!pStream)
+ {
+ SAL_WARN("vcl.filter", "PDFDocument::ReadXRefStream: no stream token found");
+ return;
+ }
+
+ // Read and decompress it.
+ rStream.Seek(pStream->GetOffset());
+ std::vector<char> aBuf(nLength);
+ rStream.ReadBytes(aBuf.data(), aBuf.size());
+
+ auto pFilter = dynamic_cast<PDFNameElement*>(pObject->Lookup("Filter"));
+ if (!pFilter)
+ {
+ SAL_WARN("vcl.filter", "PDFDocument::ReadXRefStream: no Filter found");
+ return;
+ }
+
+ if (pFilter->GetValue() != "FlateDecode")
+ {
+ SAL_WARN("vcl.filter",
+ "PDFDocument::ReadXRefStream: unexpected filter: " << pFilter->GetValue());
+ return;
+ }
+
+ int nColumns = 1;
+ int nPredictor = 1;
+ if (auto pDecodeParams = dynamic_cast<PDFDictionaryElement*>(pObject->Lookup("DecodeParms")))
+ {
+ const std::map<OString, PDFElement*>& rItems = pDecodeParams->GetItems();
+ auto it = rItems.find("Columns");
+ if (it != rItems.end())
+ if (auto pColumns = dynamic_cast<PDFNumberElement*>(it->second))
+ nColumns = pColumns->GetValue();
+ it = rItems.find("Predictor");
+ if (it != rItems.end())
+ if (auto pPredictor = dynamic_cast<PDFNumberElement*>(it->second))
+ nPredictor = pPredictor->GetValue();
+ }
+
+ SvMemoryStream aSource(aBuf.data(), aBuf.size(), StreamMode::READ);
+ SvMemoryStream aStream;
+ ZCodec aZCodec;
+ aZCodec.BeginCompression();
+ aZCodec.Decompress(aSource, aStream);
+ if (!aZCodec.EndCompression())
+ {
+ SAL_WARN("vcl.filter", "PDFDocument::ReadXRefStream: decompression failed");
+ return;
+ }
+
+ // Look up the first and the last entry we need to read.
+ auto pIndex = dynamic_cast<PDFArrayElement*>(pObject->Lookup("Index"));
+ std::vector<size_t> aFirstObjects;
+ std::vector<size_t> aNumberOfObjects;
+ if (!pIndex)
+ {
+ auto pSize = dynamic_cast<PDFNumberElement*>(pObject->Lookup("Size"));
+ if (pSize)
+ {
+ aFirstObjects.push_back(0);
+ aNumberOfObjects.push_back(pSize->GetValue());
+ }
+ else
+ {
+ SAL_WARN("vcl.filter", "PDFDocument::ReadXRefStream: Index and Size not found");
+ return;
+ }
+ }
+ else
+ {
+ const std::vector<PDFElement*>& rIndexElements = pIndex->GetElements();
+ size_t nFirstObject = 0;
+ for (size_t i = 0; i < rIndexElements.size(); ++i)
+ {
+ if (i % 2 == 0)
+ {
+ auto pFirstObject = dynamic_cast<PDFNumberElement*>(rIndexElements[i]);
+ if (!pFirstObject)
+ {
+ SAL_WARN("vcl.filter",
+ "PDFDocument::ReadXRefStream: Index has no first object");
+ return;
+ }
+ nFirstObject = pFirstObject->GetValue();
+ continue;
+ }
+
+ auto pNumberOfObjects = dynamic_cast<PDFNumberElement*>(rIndexElements[i]);
+ if (!pNumberOfObjects)
+ {
+ SAL_WARN("vcl.filter",
+ "PDFDocument::ReadXRefStream: Index has no number of objects");
+ return;
+ }
+ aFirstObjects.push_back(nFirstObject);
+ aNumberOfObjects.push_back(pNumberOfObjects->GetValue());
+ }
+ }
+
+ // Look up the format of a single entry.
+ const int nWSize = 3;
+ auto pW = dynamic_cast<PDFArrayElement*>(pObject->Lookup("W"));
+ if (!pW || pW->GetElements().size() < nWSize)
+ {
+ SAL_WARN("vcl.filter", "PDFDocument::ReadXRefStream: W not found or has < 3 elements");
+ return;
+ }
+ int aW[nWSize];
+ // First character is the (kind of) repeated predictor.
+ int nLineLength = 1;
+ for (size_t i = 0; i < nWSize; ++i)
+ {
+ auto pI = dynamic_cast<PDFNumberElement*>(pW->GetElements()[i]);
+ if (!pI)
+ {
+ SAL_WARN("vcl.filter", "PDFDocument::ReadXRefStream: W contains non-number");
+ return;
+ }
+ aW[i] = pI->GetValue();
+ nLineLength += aW[i];
+ }
+
+ if (nPredictor > 1 && nLineLength - 1 != nColumns)
+ {
+ SAL_WARN("vcl.filter",
+ "PDFDocument::ReadXRefStream: /DecodeParms/Columns is inconsistent with /W");
+ return;
+ }
+
+ aStream.Seek(0);
+ for (size_t nSubSection = 0; nSubSection < aFirstObjects.size(); ++nSubSection)
+ {
+ size_t nFirstObject = aFirstObjects[nSubSection];
+ size_t nNumberOfObjects = aNumberOfObjects[nSubSection];
+
+ // This is the line as read from the stream.
+ std::vector<unsigned char> aOrigLine(nLineLength);
+ // This is the line as it appears after tweaking according to nPredictor.
+ std::vector<unsigned char> aFilteredLine(nLineLength);
+ for (size_t nEntry = 0; nEntry < nNumberOfObjects; ++nEntry)
+ {
+ size_t nIndex = nFirstObject + nEntry;
+
+ aStream.ReadBytes(aOrigLine.data(), aOrigLine.size());
+ if (nPredictor > 1 && aOrigLine[0] + 10 != nPredictor)
+ {
+ SAL_WARN("vcl.filter", "PDFDocument::ReadXRefStream: in-stream predictor is "
+ "inconsistent with /DecodeParms/Predictor for object #"
+ << nIndex);
+ return;
+ }
+
+ for (int i = 0; i < nLineLength; ++i)
+ {
+ switch (nPredictor)
+ {
+ case 1:
+ // No prediction.
+ break;
+ case 12:
+ // PNG prediction: up (on all rows).
+ aFilteredLine[i] = aFilteredLine[i] + aOrigLine[i];
+ break;
+ default:
+ SAL_WARN("vcl.filter", "PDFDocument::ReadXRefStream: unexpected predictor: "
+ << nPredictor);
+ return;
+ }
+ }
+
+ // First character is already handled above.
+ int nPos = 1;
+ size_t nType = 0;
+ // Start of the current field in the stream data.
+ int nOffset = nPos;
+ for (; nPos < nOffset + aW[0]; ++nPos)
+ {
+ unsigned char nCh = aFilteredLine[nPos];
+ nType = (nType << 8) + nCh;
+ }
+
+ // Start of the object in the file stream.
+ size_t nStreamOffset = 0;
+ nOffset = nPos;
+ for (; nPos < nOffset + aW[1]; ++nPos)
+ {
+ unsigned char nCh = aFilteredLine[nPos];
+ nStreamOffset = (nStreamOffset << 8) + nCh;
+ }
+
+ // Generation number of the object.
+ size_t nGenerationNumber = 0;
+ nOffset = nPos;
+ for (; nPos < nOffset + aW[2]; ++nPos)
+ {
+ unsigned char nCh = aFilteredLine[nPos];
+ nGenerationNumber = (nGenerationNumber << 8) + nCh;
+ }
+
+ // Ignore invalid nType.
+ if (nType <= 2)
+ {
+ if (m_aXRef.find(nIndex) == m_aXRef.end())
+ {
+ XRefEntry aEntry;
+ switch (nType)
+ {
+ case 0:
+ aEntry.SetType(XRefEntryType::FREE);
+ break;
+ case 1:
+ aEntry.SetType(XRefEntryType::NOT_COMPRESSED);
+ break;
+ case 2:
+ aEntry.SetType(XRefEntryType::COMPRESSED);
+ break;
+ }
+ aEntry.SetOffset(nStreamOffset);
+ m_aXRef[nIndex] = aEntry;
+ }
+ }
+ }
+ }
+}
+
+void PDFDocument::ReadXRef(SvStream& rStream)
+{
+ PDFDocument::SkipWhitespace(rStream);
+
+ while (true)
+ {
+ PDFNumberElement aFirstObject;
+ if (!aFirstObject.Read(rStream))
+ {
+ // Next token is not a number, it'll be the trailer.
+ return;
+ }
+
+ if (aFirstObject.GetValue() < 0)
+ {
+ SAL_WARN("vcl.filter", "PDFDocument::ReadXRef: expected first object number >= 0");
+ return;
+ }
+
+ PDFDocument::SkipWhitespace(rStream);
+ PDFNumberElement aNumberOfEntries;
+ if (!aNumberOfEntries.Read(rStream))
+ {
+ SAL_WARN("vcl.filter", "PDFDocument::ReadXRef: failed to read number of entries");
+ return;
+ }
+
+ if (aNumberOfEntries.GetValue() < 0)
+ {
+ SAL_WARN("vcl.filter", "PDFDocument::ReadXRef: expected zero or more entries");
+ return;
+ }
+
+ size_t nSize = aNumberOfEntries.GetValue();
+ for (size_t nEntry = 0; nEntry < nSize; ++nEntry)
+ {
+ size_t nIndex = aFirstObject.GetValue() + nEntry;
+ PDFDocument::SkipWhitespace(rStream);
+ PDFNumberElement aOffset;
+ if (!aOffset.Read(rStream))
+ {
+ SAL_WARN("vcl.filter", "PDFDocument::ReadXRef: failed to read offset");
+ return;
+ }
+
+ PDFDocument::SkipWhitespace(rStream);
+ PDFNumberElement aGenerationNumber;
+ if (!aGenerationNumber.Read(rStream))
+ {
+ SAL_WARN("vcl.filter", "PDFDocument::ReadXRef: failed to read generation number");
+ return;
+ }
+
+ PDFDocument::SkipWhitespace(rStream);
+ OString aKeyword = ReadKeyword(rStream);
+ if (aKeyword != "f" && aKeyword != "n")
+ {
+ SAL_WARN("vcl.filter", "PDFDocument::ReadXRef: unexpected keyword");
+ return;
+ }
+ // xrefs are read in reverse order, so never update an existing
+ // offset with an older one.
+ if (m_aXRef.find(nIndex) == m_aXRef.end())
+ {
+ XRefEntry aEntry;
+ aEntry.SetOffset(aOffset.GetValue());
+ // Initially only the first entry is dirty.
+ if (nIndex == 0)
+ aEntry.SetDirty(true);
+ m_aXRef[nIndex] = aEntry;
+ }
+ PDFDocument::SkipWhitespace(rStream);
+ }
+ }
+}
+
+void PDFDocument::SkipWhitespace(SvStream& rStream)
+{
+ char ch = 0;
+
+ while (true)
+ {
+ rStream.ReadChar(ch);
+ if (rStream.eof())
+ break;
+
+ if (!rtl::isAsciiWhiteSpace(static_cast<unsigned char>(ch)))
+ {
+ rStream.SeekRel(-1);
+ return;
+ }
+ }
+}
+
+void PDFDocument::SkipLineBreaks(SvStream& rStream)
+{
+ char ch = 0;
+
+ while (true)
+ {
+ rStream.ReadChar(ch);
+ if (rStream.eof())
+ break;
+
+ if (ch != '\n' && ch != '\r')
+ {
+ rStream.SeekRel(-1);
+ return;
+ }
+ }
+}
+
+size_t PDFDocument::GetObjectOffset(size_t nIndex) const
+{
+ auto it = m_aXRef.find(nIndex);
+ if (it == m_aXRef.end() || it->second.GetType() == XRefEntryType::COMPRESSED)
+ {
+ SAL_WARN("vcl.filter", "PDFDocument::GetObjectOffset: wanted to look up index #"
+ << nIndex << ", but failed");
+ return 0;
+ }
+
+ return it->second.GetOffset();
+}
+
+const std::vector<std::unique_ptr<PDFElement>>& PDFDocument::GetElements() const
+{
+ return m_aElements;
+}
+
+/// Visits the page tree recursively, looking for page objects.
+static void visitPages(PDFObjectElement* pPages, std::vector<PDFObjectElement*>& rRet)
+{
+ auto pKids = dynamic_cast<PDFArrayElement*>(pPages->Lookup("Kids"));
+ if (!pKids)
+ {
+ SAL_WARN("vcl.filter", "visitPages: pages has no kids");
+ return;
+ }
+
+ pPages->setVisiting(true);
+
+ for (const auto& pKid : pKids->GetElements())
+ {
+ auto pReference = dynamic_cast<PDFReferenceElement*>(pKid);
+ if (!pReference)
+ continue;
+
+ PDFObjectElement* pKidObject = pReference->LookupObject();
+ if (!pKidObject)
+ continue;
+
+ // detect if visiting reenters itself
+ if (pKidObject->alreadyVisiting())
+ {
+ SAL_WARN("vcl.filter", "visitPages: loop in hierarchy");
+ continue;
+ }
+
+ auto pName = dynamic_cast<PDFNameElement*>(pKidObject->Lookup("Type"));
+ if (pName && pName->GetValue() == "Pages")
+ // Pages inside pages: recurse.
+ visitPages(pKidObject, rRet);
+ else
+ // Found an actual page.
+ rRet.push_back(pKidObject);
+ }
+
+ pPages->setVisiting(false);
+}
+
+PDFObjectElement* PDFDocument::GetCatalog()
+{
+ PDFReferenceElement* pRoot = nullptr;
+
+ PDFTrailerElement* pTrailer = nullptr;
+ if (!m_aTrailerOffsets.empty())
+ {
+ // Get access to the latest trailer, and work with the keys of that
+ // one.
+ auto it = m_aOffsetTrailers.find(m_aTrailerOffsets[0]);
+ if (it != m_aOffsetTrailers.end())
+ pTrailer = it->second;
+ }
+
+ if (pTrailer)
+ pRoot = dynamic_cast<PDFReferenceElement*>(pTrailer->Lookup("Root"));
+ else if (m_pXRefStream)
+ pRoot = dynamic_cast<PDFReferenceElement*>(m_pXRefStream->Lookup("Root"));
+
+ if (!pRoot)
+ {
+ SAL_WARN("vcl.filter", "PDFDocument::GetCatalog: trailer has no Root key");
+ return nullptr;
+ }
+
+ return pRoot->LookupObject();
+}
+
+std::vector<PDFObjectElement*> PDFDocument::GetPages()
+{
+ std::vector<PDFObjectElement*> aRet;
+
+ PDFObjectElement* pCatalog = GetCatalog();
+ if (!pCatalog)
+ {
+ SAL_WARN("vcl.filter", "PDFDocument::GetPages: trailer has no catalog");
+ return aRet;
+ }
+
+ PDFObjectElement* pPages = pCatalog->LookupObject("Pages");
+ if (!pPages)
+ {
+ SAL_WARN("vcl.filter", "PDFDocument::GetPages: catalog (obj " << pCatalog->GetObjectValue()
+ << ") has no pages");
+ return aRet;
+ }
+
+ visitPages(pPages, aRet);
+
+ return aRet;
+}
+
+void PDFDocument::PushBackEOF(size_t nOffset) { m_aEOFs.push_back(nOffset); }
+
+std::vector<PDFObjectElement*> PDFDocument::GetSignatureWidgets()
+{
+ std::vector<PDFObjectElement*> aRet;
+
+ std::vector<PDFObjectElement*> aPages = GetPages();
+
+ for (const auto& pPage : aPages)
+ {
+ if (!pPage)
+ continue;
+
+ PDFElement* pAnnotsElement = pPage->Lookup("Annots");
+ auto pAnnots = dynamic_cast<PDFArrayElement*>(pAnnotsElement);
+ if (!pAnnots)
+ {
+ // Annots is not an array, see if it's a reference to an object
+ // with a direct array.
+ auto pAnnotsRef = dynamic_cast<PDFReferenceElement*>(pAnnotsElement);
+ if (pAnnotsRef)
+ {
+ if (PDFObjectElement* pAnnotsObject = pAnnotsRef->LookupObject())
+ {
+ pAnnots = pAnnotsObject->GetArray();
+ }
+ }
+ }
+
+ if (!pAnnots)
+ continue;
+
+ for (const auto& pAnnot : pAnnots->GetElements())
+ {
+ auto pReference = dynamic_cast<PDFReferenceElement*>(pAnnot);
+ if (!pReference)
+ continue;
+
+ PDFObjectElement* pAnnotObject = pReference->LookupObject();
+ if (!pAnnotObject)
+ continue;
+
+ auto pFT = dynamic_cast<PDFNameElement*>(pAnnotObject->Lookup("FT"));
+ if (!pFT || pFT->GetValue() != "Sig")
+ continue;
+
+ aRet.push_back(pAnnotObject);
+ }
+ }
+
+ return aRet;
+}
+
+std::vector<unsigned char> PDFDocument::DecodeHexString(PDFHexStringElement const* pElement)
+{
+ return svl::crypto::DecodeHexString(pElement->GetValue());
+}
+
+OUString PDFDocument::DecodeHexStringUTF16BE(PDFHexStringElement const& rElement)
+{
+ std::vector<unsigned char> const encoded(DecodeHexString(&rElement));
+ // Text strings can be PDF-DocEncoding or UTF-16BE with mandatory BOM;
+ // only the latter supported is here
+ if (encoded.size() < 2 || encoded[0] != 0xFE || encoded[1] != 0xFF || (encoded.size() & 1) != 0)
+ {
+ return {};
+ }
+ OUStringBuffer buf(encoded.size() - 2);
+ for (size_t i = 2; i < encoded.size(); i += 2)
+ {
+ buf.append(sal_Unicode((static_cast<sal_uInt16>(encoded[i]) << 8) | encoded[i + 1]));
+ }
+ return buf.makeStringAndClear();
+}
+
+PDFCommentElement::PDFCommentElement(PDFDocument& rDoc)
+ : m_rDoc(rDoc)
+{
+}
+
+bool PDFCommentElement::Read(SvStream& rStream)
+{
+ // Read from (including) the % char till (excluding) the end of the line/stream.
+ OStringBuffer aBuf;
+ char ch;
+ rStream.ReadChar(ch);
+ while (true)
+ {
+ if (ch == '\n' || ch == '\r' || rStream.eof())
+ {
+ m_aComment = aBuf.makeStringAndClear();
+
+ if (m_aComment.startsWith("%%EOF"))
+ {
+ sal_uInt64 nPos = rStream.Tell();
+ if (ch == '\r')
+ {
+ rStream.ReadChar(ch);
+ rStream.SeekRel(-1);
+ // If the comment ends with a \r\n, count the \n as well to match Adobe Acrobat
+ // behavior.
+ if (ch == '\n')
+ {
+ nPos += 1;
+ }
+ }
+ m_rDoc.PushBackEOF(nPos);
+ }
+
+ SAL_INFO("vcl.filter", "PDFCommentElement::Read: m_aComment is '" << m_aComment << "'");
+ return true;
+ }
+ aBuf.append(ch);
+ rStream.ReadChar(ch);
+ }
+
+ return false;
+}
+
+PDFNumberElement::PDFNumberElement() = default;
+
+bool PDFNumberElement::Read(SvStream& rStream)
+{
+ OStringBuffer aBuf;
+ m_nOffset = rStream.Tell();
+ char ch;
+ rStream.ReadChar(ch);
+ if (rStream.eof())
+ {
+ return false;
+ }
+ if (!rtl::isAsciiDigit(static_cast<unsigned char>(ch)) && ch != '-' && ch != '+' && ch != '.')
+ {
+ rStream.SeekRel(-1);
+ return false;
+ }
+ while (!rStream.eof())
+ {
+ if (!rtl::isAsciiDigit(static_cast<unsigned char>(ch)) && ch != '-' && ch != '+'
+ && ch != '.')
+ {
+ rStream.SeekRel(-1);
+ m_nLength = rStream.Tell() - m_nOffset;
+ m_fValue = o3tl::toDouble(aBuf);
+ aBuf.setLength(0);
+ SAL_INFO("vcl.filter", "PDFNumberElement::Read: m_fValue is '" << m_fValue << "'");
+ return true;
+ }
+ aBuf.append(ch);
+ rStream.ReadChar(ch);
+ }
+
+ return false;
+}
+
+sal_uInt64 PDFNumberElement::GetLocation() const { return m_nOffset; }
+
+sal_uInt64 PDFNumberElement::GetLength() const { return m_nLength; }
+
+bool PDFBooleanElement::Read(SvStream& /*rStream*/) { return true; }
+
+bool PDFNullElement::Read(SvStream& /*rStream*/) { return true; }
+
+bool PDFHexStringElement::Read(SvStream& rStream)
+{
+ char ch;
+ rStream.ReadChar(ch);
+ if (ch != '<')
+ {
+ SAL_INFO("vcl.filter", "PDFHexStringElement::Read: expected '<' as first character");
+ return false;
+ }
+ rStream.ReadChar(ch);
+
+ OStringBuffer aBuf;
+ while (!rStream.eof())
+ {
+ if (ch == '>')
+ {
+ m_aValue = aBuf.makeStringAndClear();
+ SAL_INFO("vcl.filter",
+ "PDFHexStringElement::Read: m_aValue length is " << m_aValue.getLength());
+ return true;
+ }
+ aBuf.append(ch);
+ rStream.ReadChar(ch);
+ }
+
+ return false;
+}
+
+const OString& PDFHexStringElement::GetValue() const { return m_aValue; }
+
+bool PDFLiteralStringElement::Read(SvStream& rStream)
+{
+ char nPrevCh = 0;
+ char ch = 0;
+ rStream.ReadChar(ch);
+ if (ch != '(')
+ {
+ SAL_INFO("vcl.filter", "PDFHexStringElement::Read: expected '(' as first character");
+ return false;
+ }
+ nPrevCh = ch;
+ rStream.ReadChar(ch);
+
+ // Start with 1 nesting level as we read a '(' above already.
+ int nDepth = 1;
+ OStringBuffer aBuf;
+ while (!rStream.eof())
+ {
+ if (ch == '(' && nPrevCh != '\\')
+ ++nDepth;
+
+ if (ch == ')' && nPrevCh != '\\')
+ --nDepth;
+
+ if (nDepth == 0)
+ {
+ // ')' of the outermost '(' is reached.
+ m_aValue = aBuf.makeStringAndClear();
+ SAL_INFO("vcl.filter",
+ "PDFLiteralStringElement::Read: m_aValue is '" << m_aValue << "'");
+ return true;
+ }
+ aBuf.append(ch);
+ nPrevCh = ch;
+ rStream.ReadChar(ch);
+ }
+
+ return false;
+}
+
+const OString& PDFLiteralStringElement::GetValue() const { return m_aValue; }
+
+PDFTrailerElement::PDFTrailerElement(PDFDocument& rDoc)
+ : m_rDoc(rDoc)
+ , m_pDictionaryElement(nullptr)
+{
+}
+
+bool PDFTrailerElement::Read(SvStream& rStream)
+{
+ m_nOffset = rStream.Tell();
+ return true;
+}
+
+PDFElement* PDFTrailerElement::Lookup(const OString& rDictionaryKey)
+{
+ if (!m_pDictionaryElement)
+ {
+ PDFObjectParser aParser(m_rDoc.GetElements());
+ aParser.parse(this);
+ }
+ if (!m_pDictionaryElement)
+ return nullptr;
+ return m_pDictionaryElement->LookupElement(rDictionaryKey);
+}
+
+sal_uInt64 PDFTrailerElement::GetLocation() const { return m_nOffset; }
+
+double PDFNumberElement::GetValue() const { return m_fValue; }
+
+PDFObjectElement::PDFObjectElement(PDFDocument& rDoc, double fObjectValue, double fGenerationValue)
+ : m_rDoc(rDoc)
+ , m_fObjectValue(fObjectValue)
+ , m_fGenerationValue(fGenerationValue)
+ , m_pNumberElement(nullptr)
+ , m_nDictionaryOffset(0)
+ , m_nDictionaryLength(0)
+ , m_pDictionaryElement(nullptr)
+ , m_nArrayOffset(0)
+ , m_nArrayLength(0)
+ , m_pArrayElement(nullptr)
+ , m_pStreamElement(nullptr)
+ , m_bParsed(false)
+{
+}
+
+bool PDFObjectElement::Read(SvStream& /*rStream*/)
+{
+ SAL_INFO("vcl.filter",
+ "PDFObjectElement::Read: " << m_fObjectValue << " " << m_fGenerationValue << " obj");
+ return true;
+}
+
+PDFDictionaryElement::PDFDictionaryElement() = default;
+
+PDFElement* PDFDictionaryElement::Lookup(const std::map<OString, PDFElement*>& rDictionary,
+ const OString& rKey)
+{
+ auto it = rDictionary.find(rKey);
+ if (it == rDictionary.end())
+ return nullptr;
+
+ return it->second;
+}
+
+PDFObjectElement* PDFDictionaryElement::LookupObject(const OString& rDictionaryKey)
+{
+ auto pKey = dynamic_cast<PDFReferenceElement*>(
+ PDFDictionaryElement::Lookup(m_aItems, rDictionaryKey));
+ if (!pKey)
+ {
+ SAL_WARN("vcl.filter",
+ "PDFDictionaryElement::LookupObject: no such key with reference value: "
+ << rDictionaryKey);
+ return nullptr;
+ }
+
+ return pKey->LookupObject();
+}
+
+PDFElement* PDFDictionaryElement::LookupElement(const OString& rDictionaryKey)
+{
+ return PDFDictionaryElement::Lookup(m_aItems, rDictionaryKey);
+}
+
+void PDFObjectElement::parseIfNecessary()
+{
+ if (m_bParsed)
+ return;
+
+ if (!m_aElements.empty())
+ {
+ // This is a stored object in an object stream.
+ PDFObjectParser aParser(m_aElements);
+ aParser.parse(this);
+ }
+ else
+ {
+ // Normal object: elements are stored as members of the document itself.
+ PDFObjectParser aParser(m_rDoc.GetElements());
+ aParser.parse(this);
+ }
+ m_bParsed = true;
+}
+
+PDFElement* PDFObjectElement::Lookup(const OString& rDictionaryKey)
+{
+ parseIfNecessary();
+ if (!m_pDictionaryElement)
+ return nullptr;
+ return PDFDictionaryElement::Lookup(GetDictionaryItems(), rDictionaryKey);
+}
+
+PDFObjectElement* PDFObjectElement::LookupObject(const OString& rDictionaryKey)
+{
+ auto pKey = dynamic_cast<PDFReferenceElement*>(Lookup(rDictionaryKey));
+ if (!pKey)
+ {
+ SAL_WARN("vcl.filter", "PDFObjectElement::LookupObject: no such key with reference value: "
+ << rDictionaryKey);
+ return nullptr;
+ }
+
+ return pKey->LookupObject();
+}
+
+double PDFObjectElement::GetObjectValue() const { return m_fObjectValue; }
+
+void PDFObjectElement::SetDictionaryOffset(sal_uInt64 nDictionaryOffset)
+{
+ m_nDictionaryOffset = nDictionaryOffset;
+}
+
+sal_uInt64 PDFObjectElement::GetDictionaryOffset()
+{
+ parseIfNecessary();
+ return m_nDictionaryOffset;
+}
+
+void PDFObjectElement::SetArrayOffset(sal_uInt64 nArrayOffset) { m_nArrayOffset = nArrayOffset; }
+
+sal_uInt64 PDFObjectElement::GetArrayOffset() const { return m_nArrayOffset; }
+
+void PDFDictionaryElement::SetKeyOffset(const OString& rKey, sal_uInt64 nOffset)
+{
+ m_aDictionaryKeyOffset[rKey] = nOffset;
+}
+
+void PDFDictionaryElement::SetKeyValueLength(const OString& rKey, sal_uInt64 nLength)
+{
+ m_aDictionaryKeyValueLength[rKey] = nLength;
+}
+
+sal_uInt64 PDFDictionaryElement::GetKeyOffset(const OString& rKey) const
+{
+ auto it = m_aDictionaryKeyOffset.find(rKey);
+ if (it == m_aDictionaryKeyOffset.end())
+ return 0;
+
+ return it->second;
+}
+
+sal_uInt64 PDFDictionaryElement::GetKeyValueLength(const OString& rKey) const
+{
+ auto it = m_aDictionaryKeyValueLength.find(rKey);
+ if (it == m_aDictionaryKeyValueLength.end())
+ return 0;
+
+ return it->second;
+}
+
+const std::map<OString, PDFElement*>& PDFDictionaryElement::GetItems() const { return m_aItems; }
+
+void PDFObjectElement::SetDictionaryLength(sal_uInt64 nDictionaryLength)
+{
+ m_nDictionaryLength = nDictionaryLength;
+}
+
+sal_uInt64 PDFObjectElement::GetDictionaryLength()
+{
+ parseIfNecessary();
+ return m_nDictionaryLength;
+}
+
+void PDFObjectElement::SetArrayLength(sal_uInt64 nArrayLength) { m_nArrayLength = nArrayLength; }
+
+sal_uInt64 PDFObjectElement::GetArrayLength() const { return m_nArrayLength; }
+
+PDFDictionaryElement* PDFObjectElement::GetDictionary()
+{
+ parseIfNecessary();
+ return m_pDictionaryElement;
+}
+
+void PDFObjectElement::SetDictionary(PDFDictionaryElement* pDictionaryElement)
+{
+ m_pDictionaryElement = pDictionaryElement;
+}
+
+void PDFObjectElement::SetNumberElement(PDFNumberElement* pNumberElement)
+{
+ m_pNumberElement = pNumberElement;
+}
+
+PDFNumberElement* PDFObjectElement::GetNumberElement() const { return m_pNumberElement; }
+
+const std::vector<PDFReferenceElement*>& PDFObjectElement::GetDictionaryReferences() const
+{
+ return m_aDictionaryReferences;
+}
+
+void PDFObjectElement::AddDictionaryReference(PDFReferenceElement* pReference)
+{
+ m_aDictionaryReferences.push_back(pReference);
+}
+
+const std::map<OString, PDFElement*>& PDFObjectElement::GetDictionaryItems()
+{
+ parseIfNecessary();
+ return m_pDictionaryElement->GetItems();
+}
+
+void PDFObjectElement::SetArray(PDFArrayElement* pArrayElement) { m_pArrayElement = pArrayElement; }
+
+void PDFObjectElement::SetStream(PDFStreamElement* pStreamElement)
+{
+ m_pStreamElement = pStreamElement;
+}
+
+PDFStreamElement* PDFObjectElement::GetStream() const { return m_pStreamElement; }
+
+PDFArrayElement* PDFObjectElement::GetArray()
+{
+ parseIfNecessary();
+ return m_pArrayElement;
+}
+
+void PDFObjectElement::ParseStoredObjects()
+{
+ if (!m_pStreamElement)
+ {
+ SAL_WARN("vcl.filter", "PDFObjectElement::ParseStoredObjects: no stream");
+ return;
+ }
+
+ auto pType = dynamic_cast<PDFNameElement*>(Lookup("Type"));
+ if (!pType || pType->GetValue() != "ObjStm")
+ {
+ if (!pType)
+ SAL_WARN("vcl.filter", "PDFDocument::ReadXRefStream: missing unexpected type");
+ else
+ SAL_WARN("vcl.filter",
+ "PDFDocument::ReadXRefStream: unexpected type: " << pType->GetValue());
+ return;
+ }
+
+ auto pFilter = dynamic_cast<PDFNameElement*>(Lookup("Filter"));
+ if (!pFilter || pFilter->GetValue() != "FlateDecode")
+ {
+ if (!pFilter)
+ SAL_WARN("vcl.filter", "PDFDocument::ReadXRefStream: missing filter");
+ else
+ SAL_WARN("vcl.filter",
+ "PDFDocument::ReadXRefStream: unexpected filter: " << pFilter->GetValue());
+ return;
+ }
+
+ auto pFirst = dynamic_cast<PDFNumberElement*>(Lookup("First"));
+ if (!pFirst)
+ {
+ SAL_WARN("vcl.filter", "PDFObjectElement::ParseStoredObjects: no First");
+ return;
+ }
+
+ auto pN = dynamic_cast<PDFNumberElement*>(Lookup("N"));
+ if (!pN)
+ {
+ SAL_WARN("vcl.filter", "PDFObjectElement::ParseStoredObjects: no N");
+ return;
+ }
+ size_t nN = pN->GetValue();
+
+ auto pLength = dynamic_cast<PDFNumberElement*>(Lookup("Length"));
+ if (!pLength)
+ {
+ SAL_WARN("vcl.filter", "PDFObjectElement::ParseStoredObjects: no length");
+ return;
+ }
+ size_t nLength = pLength->GetValue();
+
+ // Read and decompress it.
+ SvMemoryStream& rEditBuffer = m_rDoc.GetEditBuffer();
+ rEditBuffer.Seek(m_pStreamElement->GetOffset());
+ std::vector<char> aBuf(nLength);
+ rEditBuffer.ReadBytes(aBuf.data(), aBuf.size());
+ SvMemoryStream aSource(aBuf.data(), aBuf.size(), StreamMode::READ);
+ SvMemoryStream aStream;
+ ZCodec aZCodec;
+ aZCodec.BeginCompression();
+ aZCodec.Decompress(aSource, aStream);
+ if (!aZCodec.EndCompression())
+ {
+ SAL_WARN("vcl.filter", "PDFObjectElement::ParseStoredObjects: decompression failed");
+ return;
+ }
+
+ nLength = aStream.TellEnd();
+ aStream.Seek(0);
+ std::vector<size_t> aObjNums;
+ std::vector<size_t> aOffsets;
+ std::vector<size_t> aLengths;
+ // First iterate over and find out the lengths.
+ for (size_t nObject = 0; nObject < nN; ++nObject)
+ {
+ PDFNumberElement aObjNum;
+ if (!aObjNum.Read(aStream))
+ {
+ SAL_WARN("vcl.filter",
+ "PDFObjectElement::ParseStoredObjects: failed to read object number");
+ return;
+ }
+ aObjNums.push_back(aObjNum.GetValue());
+
+ PDFDocument::SkipWhitespace(aStream);
+
+ PDFNumberElement aByteOffset;
+ if (!aByteOffset.Read(aStream))
+ {
+ SAL_WARN("vcl.filter",
+ "PDFObjectElement::ParseStoredObjects: failed to read byte offset");
+ return;
+ }
+ aOffsets.push_back(pFirst->GetValue() + aByteOffset.GetValue());
+
+ if (aOffsets.size() > 1)
+ aLengths.push_back(aOffsets.back() - aOffsets[aOffsets.size() - 2]);
+ if (nObject + 1 == nN)
+ aLengths.push_back(nLength - aOffsets.back());
+
+ PDFDocument::SkipWhitespace(aStream);
+ }
+
+ // Now create streams with the proper length and tokenize the data.
+ for (size_t nObject = 0; nObject < nN; ++nObject)
+ {
+ size_t nObjNum = aObjNums[nObject];
+ size_t nOffset = aOffsets[nObject];
+ size_t nLen = aLengths[nObject];
+
+ aStream.Seek(nOffset);
+ m_aStoredElements.push_back(std::make_unique<PDFObjectElement>(m_rDoc, nObjNum, 0));
+ PDFObjectElement* pStored = m_aStoredElements.back().get();
+
+ aBuf.clear();
+ aBuf.resize(nLen);
+ aStream.ReadBytes(aBuf.data(), aBuf.size());
+ SvMemoryStream aStoredStream(aBuf.data(), aBuf.size(), StreamMode::READ);
+
+ m_rDoc.Tokenize(aStoredStream, TokenizeMode::STORED_OBJECT, pStored->GetStoredElements(),
+ pStored);
+ // This is how references know the object is stored inside this object stream.
+ m_rDoc.SetIDObject(nObjNum, pStored);
+
+ // Store the stream of the object in the object stream for later use.
+ std::unique_ptr<SvMemoryStream> pStreamBuffer(new SvMemoryStream());
+ aStoredStream.Seek(0);
+ pStreamBuffer->WriteStream(aStoredStream);
+ pStored->SetStreamBuffer(pStreamBuffer);
+ }
+}
+
+std::vector<std::unique_ptr<PDFElement>>& PDFObjectElement::GetStoredElements()
+{
+ return m_aElements;
+}
+
+SvMemoryStream* PDFObjectElement::GetStreamBuffer() const { return m_pStreamBuffer.get(); }
+
+void PDFObjectElement::SetStreamBuffer(std::unique_ptr<SvMemoryStream>& pStreamBuffer)
+{
+ m_pStreamBuffer = std::move(pStreamBuffer);
+}
+
+PDFDocument& PDFObjectElement::GetDocument() { return m_rDoc; }
+
+PDFReferenceElement::PDFReferenceElement(PDFDocument& rDoc, PDFNumberElement& rObject,
+ PDFNumberElement const& rGeneration)
+ : m_rDoc(rDoc)
+ , m_fObjectValue(rObject.GetValue())
+ , m_fGenerationValue(rGeneration.GetValue())
+ , m_rObject(rObject)
+{
+}
+
+PDFNumberElement& PDFReferenceElement::GetObjectElement() const { return m_rObject; }
+
+bool PDFReferenceElement::Read(SvStream& rStream)
+{
+ SAL_INFO("vcl.filter",
+ "PDFReferenceElement::Read: " << m_fObjectValue << " " << m_fGenerationValue << " R");
+ m_nOffset = rStream.Tell();
+ return true;
+}
+
+sal_uInt64 PDFReferenceElement::GetOffset() const { return m_nOffset; }
+
+double PDFReferenceElement::LookupNumber(SvStream& rStream) const
+{
+ size_t nOffset = m_rDoc.GetObjectOffset(m_fObjectValue);
+ if (nOffset == 0)
+ {
+ SAL_WARN("vcl.filter", "PDFReferenceElement::LookupNumber: found no offset for object #"
+ << m_fObjectValue);
+ return 0;
+ }
+
+ sal_uInt64 nOrigPos = rStream.Tell();
+ comphelper::ScopeGuard g([&]() { rStream.Seek(nOrigPos); });
+
+ rStream.Seek(nOffset);
+ {
+ PDFDocument::SkipWhitespace(rStream);
+ PDFNumberElement aNumber;
+ bool bRet = aNumber.Read(rStream);
+ if (!bRet || aNumber.GetValue() != m_fObjectValue)
+ {
+ SAL_WARN("vcl.filter",
+ "PDFReferenceElement::LookupNumber: offset points to not matching object");
+ return 0;
+ }
+ }
+
+ {
+ PDFDocument::SkipWhitespace(rStream);
+ PDFNumberElement aNumber;
+ bool bRet = aNumber.Read(rStream);
+ if (!bRet || aNumber.GetValue() != m_fGenerationValue)
+ {
+ SAL_WARN("vcl.filter",
+ "PDFReferenceElement::LookupNumber: offset points to not matching generation");
+ return 0;
+ }
+ }
+
+ {
+ PDFDocument::SkipWhitespace(rStream);
+ OString aKeyword = PDFDocument::ReadKeyword(rStream);
+ if (aKeyword != "obj")
+ {
+ SAL_WARN("vcl.filter",
+ "PDFReferenceElement::LookupNumber: offset doesn't point to an obj keyword");
+ return 0;
+ }
+ }
+
+ PDFDocument::SkipWhitespace(rStream);
+ PDFNumberElement aNumber;
+ if (!aNumber.Read(rStream))
+ {
+ SAL_WARN("vcl.filter",
+ "PDFReferenceElement::LookupNumber: failed to read referenced number");
+ return 0;
+ }
+
+ return aNumber.GetValue();
+}
+
+PDFObjectElement* PDFReferenceElement::LookupObject()
+{
+ return m_rDoc.LookupObject(m_fObjectValue);
+}
+
+PDFObjectElement* PDFDocument::LookupObject(size_t nObjectNumber)
+{
+ auto itIDObjects = m_aIDObjects.find(nObjectNumber);
+
+ if (itIDObjects != m_aIDObjects.end())
+ return itIDObjects->second;
+
+ SAL_WARN("vcl.filter", "PDFDocument::LookupObject: can't find obj " << nObjectNumber);
+ return nullptr;
+}
+
+SvMemoryStream& PDFDocument::GetEditBuffer() { return m_aEditBuffer; }
+
+int PDFReferenceElement::GetObjectValue() const { return m_fObjectValue; }
+
+int PDFReferenceElement::GetGenerationValue() const { return m_fGenerationValue; }
+
+bool PDFDictionaryElement::Read(SvStream& rStream)
+{
+ char ch;
+ rStream.ReadChar(ch);
+ if (ch != '<')
+ {
+ SAL_WARN("vcl.filter", "PDFDictionaryElement::Read: unexpected character: " << ch);
+ return false;
+ }
+
+ if (rStream.eof())
+ {
+ SAL_WARN("vcl.filter", "PDFDictionaryElement::Read: unexpected end of file");
+ return false;
+ }
+
+ rStream.ReadChar(ch);
+ if (ch != '<')
+ {
+ SAL_WARN("vcl.filter", "PDFDictionaryElement::Read: unexpected character: " << ch);
+ return false;
+ }
+
+ m_nLocation = rStream.Tell();
+
+ SAL_INFO("vcl.filter", "PDFDictionaryElement::Read: '<<'");
+
+ return true;
+}
+
+PDFEndDictionaryElement::PDFEndDictionaryElement() = default;
+
+sal_uInt64 PDFEndDictionaryElement::GetLocation() const { return m_nLocation; }
+
+bool PDFEndDictionaryElement::Read(SvStream& rStream)
+{
+ m_nLocation = rStream.Tell();
+ char ch;
+ rStream.ReadChar(ch);
+ if (ch != '>')
+ {
+ SAL_WARN("vcl.filter", "PDFEndDictionaryElement::Read: unexpected character: " << ch);
+ return false;
+ }
+
+ if (rStream.eof())
+ {
+ SAL_WARN("vcl.filter", "PDFEndDictionaryElement::Read: unexpected end of file");
+ return false;
+ }
+
+ rStream.ReadChar(ch);
+ if (ch != '>')
+ {
+ SAL_WARN("vcl.filter", "PDFEndDictionaryElement::Read: unexpected character: " << ch);
+ return false;
+ }
+
+ SAL_INFO("vcl.filter", "PDFEndDictionaryElement::Read: '>>'");
+
+ return true;
+}
+
+PDFNameElement::PDFNameElement() = default;
+
+bool PDFNameElement::Read(SvStream& rStream)
+{
+ char ch;
+ rStream.ReadChar(ch);
+ if (ch != '/')
+ {
+ SAL_WARN("vcl.filter", "PDFNameElement::Read: unexpected character: " << ch);
+ return false;
+ }
+ m_nLocation = rStream.Tell();
+
+ if (rStream.eof())
+ {
+ SAL_WARN("vcl.filter", "PDFNameElement::Read: unexpected end of file");
+ return false;
+ }
+
+ // Read till the first white-space.
+ OStringBuffer aBuf;
+ rStream.ReadChar(ch);
+ while (!rStream.eof())
+ {
+ if (rtl::isAsciiWhiteSpace(static_cast<unsigned char>(ch)) || ch == '/' || ch == '['
+ || ch == ']' || ch == '<' || ch == '>' || ch == '(')
+ {
+ rStream.SeekRel(-1);
+ m_aValue = aBuf.makeStringAndClear();
+ SAL_INFO("vcl.filter", "PDFNameElement::Read: m_aValue is '" << m_aValue << "'");
+ return true;
+ }
+ aBuf.append(ch);
+ rStream.ReadChar(ch);
+ }
+
+ return false;
+}
+
+const OString& PDFNameElement::GetValue() const { return m_aValue; }
+
+sal_uInt64 PDFNameElement::GetLocation() const { return m_nLocation; }
+
+PDFStreamElement::PDFStreamElement(size_t nLength)
+ : m_nLength(nLength)
+ , m_nOffset(0)
+{
+}
+
+bool PDFStreamElement::Read(SvStream& rStream)
+{
+ SAL_INFO("vcl.filter", "PDFStreamElement::Read: length is " << m_nLength);
+ m_nOffset = rStream.Tell();
+ std::vector<unsigned char> aBytes(m_nLength);
+ rStream.ReadBytes(aBytes.data(), aBytes.size());
+ m_aMemory.WriteBytes(aBytes.data(), aBytes.size());
+
+ return rStream.good();
+}
+
+SvMemoryStream& PDFStreamElement::GetMemory() { return m_aMemory; }
+
+sal_uInt64 PDFStreamElement::GetOffset() const { return m_nOffset; }
+
+bool PDFEndStreamElement::Read(SvStream& /*rStream*/) { return true; }
+
+bool PDFEndObjectElement::Read(SvStream& /*rStream*/) { return true; }
+
+PDFArrayElement::PDFArrayElement(PDFObjectElement* pObject)
+ : m_pObject(pObject)
+{
+}
+
+bool PDFArrayElement::Read(SvStream& rStream)
+{
+ char ch;
+ rStream.ReadChar(ch);
+ if (ch != '[')
+ {
+ SAL_WARN("vcl.filter", "PDFArrayElement::Read: unexpected character: " << ch);
+ return false;
+ }
+
+ SAL_INFO("vcl.filter", "PDFArrayElement::Read: '['");
+
+ return true;
+}
+
+void PDFArrayElement::PushBack(PDFElement* pElement)
+{
+ if (m_pObject)
+ SAL_INFO("vcl.filter",
+ "PDFArrayElement::PushBack: object is " << m_pObject->GetObjectValue());
+ m_aElements.push_back(pElement);
+}
+
+const std::vector<PDFElement*>& PDFArrayElement::GetElements() const { return m_aElements; }
+
+PDFEndArrayElement::PDFEndArrayElement() = default;
+
+bool PDFEndArrayElement::Read(SvStream& rStream)
+{
+ m_nOffset = rStream.Tell();
+ char ch;
+ rStream.ReadChar(ch);
+ if (ch != ']')
+ {
+ SAL_WARN("vcl.filter", "PDFEndArrayElement::Read: unexpected character: " << ch);
+ return false;
+ }
+
+ SAL_INFO("vcl.filter", "PDFEndArrayElement::Read: ']'");
+
+ return true;
+}
+
+sal_uInt64 PDFEndArrayElement::GetOffset() const { return m_nOffset; }
+
+// PDFObjectParser
+
+size_t PDFObjectParser::parse(PDFElement* pParsingElement, size_t nStartIndex, int nCurrentDepth)
+{
+ // The index of last parsed element
+ size_t nReturnIndex = 0;
+
+ pParsingElement->setParsing(true);
+
+ comphelper::ScopeGuard aGuard([pParsingElement]() { pParsingElement->setParsing(false); });
+
+ // Current object, if root is an object, else nullptr
+ auto pParsingObject = dynamic_cast<PDFObjectElement*>(pParsingElement);
+ auto pParsingTrailer = dynamic_cast<PDFTrailerElement*>(pParsingElement);
+
+ // Current dictionary, if root is an dictionary, else nullptr
+ auto pParsingDictionary = dynamic_cast<PDFDictionaryElement*>(pParsingElement);
+
+ // Current parsing array, if root is an array, else nullptr
+ auto pParsingArray = dynamic_cast<PDFArrayElement*>(pParsingElement);
+
+ // Find out where the dictionary for this object starts.
+ size_t nIndex = nStartIndex;
+ for (size_t i = nStartIndex; i < mrElements.size(); ++i)
+ {
+ if (mrElements[i].get() == pParsingElement)
+ {
+ nIndex = i;
+ break;
+ }
+ }
+
+ OString aName;
+ sal_uInt64 nNameOffset = 0;
+ std::vector<PDFNumberElement*> aNumbers;
+
+ sal_uInt64 nDictionaryOffset = 0;
+
+ // Current depth; 1 is current
+ int nDepth = 0;
+
+ for (size_t i = nIndex; i < mrElements.size(); ++i)
+ {
+ auto* pCurrentElement = mrElements[i].get();
+
+ // Dictionary tokens can be nested, track enter/leave.
+ if (auto pCurrentDictionary = dynamic_cast<PDFDictionaryElement*>(pCurrentElement))
+ {
+ // Handle previously stored number
+ if (!aNumbers.empty())
+ {
+ if (pParsingDictionary)
+ {
+ PDFNumberElement* pNumber = aNumbers.back();
+ sal_uInt64 nLength
+ = pNumber->GetLocation() + pNumber->GetLength() - nNameOffset;
+
+ pParsingDictionary->insert(aName, pNumber);
+ pParsingDictionary->SetKeyOffset(aName, nNameOffset);
+ pParsingDictionary->SetKeyValueLength(aName, nLength);
+ }
+ else if (pParsingArray)
+ {
+ for (auto& pNumber : aNumbers)
+ pParsingArray->PushBack(pNumber);
+ }
+ else
+ {
+ SAL_INFO("vcl.filter", "neither Dictionary nor Array available");
+ }
+ aName.clear();
+ aNumbers.clear();
+ }
+
+ nDepth++;
+
+ if (nDepth == 1) // pParsingDictionary is the current one
+ {
+ // First dictionary start, track start offset.
+ nDictionaryOffset = pCurrentDictionary->GetLocation();
+
+ if (pParsingObject)
+ {
+ // Then the toplevel dictionary of the object.
+ pParsingObject->SetDictionary(pCurrentDictionary);
+ pParsingObject->SetDictionaryOffset(nDictionaryOffset);
+ pParsingDictionary = pCurrentDictionary;
+ }
+ else if (pParsingTrailer)
+ {
+ pParsingTrailer->SetDictionary(pCurrentDictionary);
+ pParsingDictionary = pCurrentDictionary;
+ }
+ }
+ else if (!pCurrentDictionary->alreadyParsing())
+ {
+ if (pParsingArray)
+ {
+ pParsingArray->PushBack(pCurrentDictionary);
+ }
+ else if (pParsingDictionary)
+ {
+ // Dictionary toplevel value.
+ pParsingDictionary->insert(aName, pCurrentDictionary);
+ }
+ else
+ {
+ SAL_INFO("vcl.filter", "neither Dictionary nor Array available");
+ }
+ // Nested dictionary.
+ const size_t nNextElementIndex = parse(pCurrentDictionary, i, nCurrentDepth + 1);
+ i = std::max(i, nNextElementIndex - 1);
+ }
+ }
+ else if (auto pCurrentEndDictionary
+ = dynamic_cast<PDFEndDictionaryElement*>(pCurrentElement))
+ {
+ // Handle previously stored number
+ if (!aNumbers.empty())
+ {
+ if (pParsingDictionary)
+ {
+ PDFNumberElement* pNumber = aNumbers.back();
+ sal_uInt64 nLength
+ = pNumber->GetLocation() + pNumber->GetLength() - nNameOffset;
+
+ pParsingDictionary->insert(aName, pNumber);
+ pParsingDictionary->SetKeyOffset(aName, nNameOffset);
+ pParsingDictionary->SetKeyValueLength(aName, nLength);
+ }
+ else if (pParsingArray)
+ {
+ for (auto& pNumber : aNumbers)
+ pParsingArray->PushBack(pNumber);
+ }
+ else
+ {
+ SAL_INFO("vcl.filter", "neither Dictionary nor Array available");
+ }
+ aName.clear();
+ aNumbers.clear();
+ }
+
+ if (pParsingDictionary)
+ {
+ pParsingDictionary->SetKeyOffset(aName, nNameOffset);
+ sal_uInt64 nLength = pCurrentEndDictionary->GetLocation() - nNameOffset + 2;
+ pParsingDictionary->SetKeyValueLength(aName, nLength);
+ aName.clear();
+ }
+
+ if (nDepth == 1) // did the parsing ended
+ {
+ // Last dictionary end, track length and stop parsing.
+ if (pParsingObject)
+ {
+ sal_uInt64 nDictionaryLength
+ = pCurrentEndDictionary->GetLocation() - nDictionaryOffset;
+ pParsingObject->SetDictionaryLength(nDictionaryLength);
+ }
+ nReturnIndex = i;
+ break;
+ }
+
+ nDepth--;
+ }
+ else if (auto pCurrentArray = dynamic_cast<PDFArrayElement*>(pCurrentElement))
+ {
+ // Handle previously stored number
+ if (!aNumbers.empty())
+ {
+ if (pParsingDictionary)
+ {
+ PDFNumberElement* pNumber = aNumbers.back();
+
+ sal_uInt64 nLength
+ = pNumber->GetLocation() + pNumber->GetLength() - nNameOffset;
+ pParsingDictionary->insert(aName, pNumber);
+ pParsingDictionary->SetKeyOffset(aName, nNameOffset);
+ pParsingDictionary->SetKeyValueLength(aName, nLength);
+ }
+ else if (pParsingArray)
+ {
+ for (auto& pNumber : aNumbers)
+ pParsingArray->PushBack(pNumber);
+ }
+ else
+ {
+ SAL_INFO("vcl.filter", "neither Dictionary nor Array available");
+ }
+ aName.clear();
+ aNumbers.clear();
+ }
+
+ nDepth++;
+ if (nDepth == 1) // pParsingDictionary is the current one
+ {
+ if (pParsingObject)
+ {
+ pParsingObject->SetArray(pCurrentArray);
+ pParsingArray = pCurrentArray;
+ }
+ }
+ else if (!pCurrentArray->alreadyParsing())
+ {
+ if (pParsingArray)
+ {
+ // Array is toplevel
+ pParsingArray->PushBack(pCurrentArray);
+ }
+ else if (pParsingDictionary)
+ {
+ // Dictionary toplevel value.
+ pParsingDictionary->insert(aName, pCurrentArray);
+ }
+
+ const size_t nNextElementIndex = parse(pCurrentArray, i, nCurrentDepth + 1);
+
+ // ensure we go forwards and not endlessly loop
+ i = std::max(i, nNextElementIndex - 1);
+ }
+ }
+ else if (auto pCurrentEndArray = dynamic_cast<PDFEndArrayElement*>(pCurrentElement))
+ {
+ // Handle previously stored number
+ if (!aNumbers.empty())
+ {
+ if (pParsingDictionary)
+ {
+ PDFNumberElement* pNumber = aNumbers.back();
+
+ sal_uInt64 nLength
+ = pNumber->GetLocation() + pNumber->GetLength() - nNameOffset;
+ pParsingDictionary->insert(aName, pNumber);
+ pParsingDictionary->SetKeyOffset(aName, nNameOffset);
+ pParsingDictionary->SetKeyValueLength(aName, nLength);
+ }
+ else if (pParsingArray)
+ {
+ for (auto& pNumber : aNumbers)
+ pParsingArray->PushBack(pNumber);
+ }
+ else
+ {
+ SAL_INFO("vcl.filter", "neither Dictionary nor Array available");
+ }
+ aName.clear();
+ aNumbers.clear();
+ }
+
+ if (nDepth == 1) // did the pParsing ended
+ {
+ // Last array end, track length and stop parsing.
+ nReturnIndex = i;
+ break;
+ }
+
+ if (pParsingDictionary)
+ {
+ pParsingDictionary->SetKeyOffset(aName, nNameOffset);
+ // Include the ending ']' in the length of the key - (array)value pair length.
+ sal_uInt64 nLength = pCurrentEndArray->GetOffset() - nNameOffset + 1;
+ pParsingDictionary->SetKeyValueLength(aName, nLength);
+ aName.clear();
+ }
+ nDepth--;
+ }
+ else if (auto pCurrentName = dynamic_cast<PDFNameElement*>(pCurrentElement))
+ {
+ // Handle previously stored number
+ if (!aNumbers.empty())
+ {
+ if (pParsingDictionary)
+ {
+ PDFNumberElement* pNumber = aNumbers.back();
+
+ sal_uInt64 nLength
+ = pNumber->GetLocation() + pNumber->GetLength() - nNameOffset;
+ pParsingDictionary->insert(aName, pNumber);
+ pParsingDictionary->SetKeyOffset(aName, nNameOffset);
+ pParsingDictionary->SetKeyValueLength(aName, nLength);
+ }
+ else if (pParsingArray)
+ {
+ for (auto& pNumber : aNumbers)
+ pParsingArray->PushBack(pNumber);
+ }
+ aName.clear();
+ aNumbers.clear();
+ }
+
+ // Now handle name
+ if (pParsingArray)
+ {
+ // if we are in an array, just push the name to array
+ pParsingArray->PushBack(pCurrentName);
+ }
+ else if (pParsingDictionary)
+ {
+ // if we are in a dictionary, we need to store the name as a possible key
+ if (aName.isEmpty())
+ {
+ aName = pCurrentName->GetValue();
+ nNameOffset = pCurrentName->GetLocation();
+ }
+ else
+ {
+ sal_uInt64 nKeyLength
+ = pCurrentName->GetLocation() + pCurrentName->GetLength() - nNameOffset;
+ pParsingDictionary->insert(aName, pCurrentName);
+ pParsingDictionary->SetKeyOffset(aName, nNameOffset);
+ pParsingDictionary->SetKeyValueLength(aName, nKeyLength);
+ aName.clear();
+ }
+ }
+ }
+ else if (auto pReference = dynamic_cast<PDFReferenceElement*>(pCurrentElement))
+ {
+ if (pParsingArray)
+ {
+ pParsingArray->PushBack(pReference);
+ }
+ else if (pParsingDictionary)
+ {
+ sal_uInt64 nLength = pReference->GetOffset() - nNameOffset;
+ pParsingDictionary->insert(aName, pReference);
+ pParsingDictionary->SetKeyOffset(aName, nNameOffset);
+ pParsingDictionary->SetKeyValueLength(aName, nLength);
+ aName.clear();
+ }
+ else
+ {
+ SAL_INFO("vcl.filter", "neither Dictionary nor Array available");
+ }
+ aNumbers.clear();
+ }
+ else if (auto pLiteralString = dynamic_cast<PDFLiteralStringElement*>(pCurrentElement))
+ {
+ if (pParsingArray)
+ {
+ pParsingArray->PushBack(pLiteralString);
+ }
+ else if (pParsingDictionary)
+ {
+ pParsingDictionary->insert(aName, pLiteralString);
+ pParsingDictionary->SetKeyOffset(aName, nNameOffset);
+ aName.clear();
+ }
+ else
+ {
+ SAL_INFO("vcl.filter", "neither Dictionary nor Array available");
+ }
+ }
+ else if (auto pBoolean = dynamic_cast<PDFBooleanElement*>(pCurrentElement))
+ {
+ if (pParsingArray)
+ {
+ pParsingArray->PushBack(pBoolean);
+ }
+ else if (pParsingDictionary)
+ {
+ pParsingDictionary->insert(aName, pBoolean);
+ pParsingDictionary->SetKeyOffset(aName, nNameOffset);
+ aName.clear();
+ }
+ else
+ {
+ SAL_INFO("vcl.filter", "neither Dictionary nor Array available");
+ }
+ }
+ else if (auto pHexString = dynamic_cast<PDFHexStringElement*>(pCurrentElement))
+ {
+ if (pParsingArray)
+ {
+ pParsingArray->PushBack(pHexString);
+ }
+ else if (pParsingDictionary)
+ {
+ pParsingDictionary->insert(aName, pHexString);
+ pParsingDictionary->SetKeyOffset(aName, nNameOffset);
+ aName.clear();
+ }
+ }
+ else if (auto pNumberElement = dynamic_cast<PDFNumberElement*>(pCurrentElement))
+ {
+ // Just remember this, so that in case it's not a reference parameter,
+ // we can handle it later.
+ aNumbers.push_back(pNumberElement);
+ }
+ else if (dynamic_cast<PDFEndObjectElement*>(pCurrentElement))
+ {
+ // parsing of the object is finished
+ break;
+ }
+ else if (dynamic_cast<PDFObjectElement*>(pCurrentElement)
+ || dynamic_cast<PDFTrailerElement*>(pCurrentElement))
+ {
+ continue;
+ }
+ else
+ {
+ SAL_INFO("vcl.filter", "Unhandled element while parsing.");
+ }
+ }
+
+ return nReturnIndex;
+}
+
+} // namespace vcl
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/vcl/source/filter/ipdf/pdfread.cxx b/vcl/source/filter/ipdf/pdfread.cxx
new file mode 100644
index 000000000..c6bc4fd5b
--- /dev/null
+++ b/vcl/source/filter/ipdf/pdfread.cxx
@@ -0,0 +1,399 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ */
+
+#include <vcl/pdfread.hxx>
+#include <pdf/pdfcompat.hxx>
+
+#include <pdf/PdfConfig.hxx>
+#include <vcl/graph.hxx>
+#include <bitmap/BitmapWriteAccess.hxx>
+#include <unotools/ucbstreamhelper.hxx>
+#include <unotools/datetime.hxx>
+
+#include <vcl/filter/PDFiumLibrary.hxx>
+#include <sal/log.hxx>
+#include <o3tl/string_view.hxx>
+
+using namespace com::sun::star;
+
+namespace vcl
+{
+size_t RenderPDFBitmaps(const void* pBuffer, int nSize, std::vector<BitmapEx>& rBitmaps,
+ const size_t nFirstPage, int nPages, const basegfx::B2DTuple* pSizeHint)
+{
+ auto pPdfium = vcl::pdf::PDFiumLibrary::get();
+ if (!pPdfium)
+ {
+ return 0;
+ }
+
+ // Load the buffer using pdfium.
+ std::unique_ptr<vcl::pdf::PDFiumDocument> pPdfDocument
+ = pPdfium->openDocument(pBuffer, nSize, OString());
+ if (!pPdfDocument)
+ return 0;
+
+ static const double fResolutionDPI = vcl::pdf::getDefaultPdfResolutionDpi();
+
+ const int nPageCount = pPdfDocument->getPageCount();
+ if (nPages <= 0)
+ nPages = nPageCount;
+ const size_t nLastPage = std::min<int>(nPageCount, nFirstPage + nPages) - 1;
+ for (size_t nPageIndex = nFirstPage; nPageIndex <= nLastPage; ++nPageIndex)
+ {
+ // Render next page.
+ std::unique_ptr<vcl::pdf::PDFiumPage> pPdfPage = pPdfDocument->openPage(nPageIndex);
+ if (!pPdfPage)
+ break;
+
+ // Calculate the bitmap size in points.
+ double nPageWidthPoints = pPdfPage->getWidth();
+ double nPageHeightPoints = pPdfPage->getHeight();
+ if (pSizeHint && pSizeHint->getX() && pSizeHint->getY())
+ {
+ // Have a size hint, prefer that over the logic size from the PDF.
+ nPageWidthPoints
+ = o3tl::convert(pSizeHint->getX(), o3tl::Length::mm100, o3tl::Length::pt);
+ nPageHeightPoints
+ = o3tl::convert(pSizeHint->getY(), o3tl::Length::mm100, o3tl::Length::pt);
+ }
+
+ // Returned unit is points, convert that to pixel.
+
+ const size_t nPageWidth
+ = std::round(vcl::pdf::pointToPixel(nPageWidthPoints, fResolutionDPI)
+ * PDF_INSERT_MAGIC_SCALE_FACTOR);
+ const size_t nPageHeight
+ = std::round(vcl::pdf::pointToPixel(nPageHeightPoints, fResolutionDPI)
+ * PDF_INSERT_MAGIC_SCALE_FACTOR);
+ std::unique_ptr<vcl::pdf::PDFiumBitmap> pPdfBitmap
+ = pPdfium->createBitmap(nPageWidth, nPageHeight, /*nAlpha=*/1);
+ if (!pPdfBitmap)
+ break;
+
+ bool bTransparent = pPdfPage->hasTransparency();
+ if (pSizeHint)
+ {
+ // This is the PDF-in-EMF case: force transparency, even in case pdfium would tell us
+ // the PDF is not transparent.
+ bTransparent = true;
+ }
+ const sal_uInt32 nColor = bTransparent ? 0x00000000 : 0xFFFFFFFF;
+ pPdfBitmap->fillRect(0, 0, nPageWidth, nPageHeight, nColor);
+ pPdfBitmap->renderPageBitmap(pPdfDocument.get(), pPdfPage.get(), /*nStartX=*/0,
+ /*nStartY=*/0, nPageWidth, nPageHeight);
+
+ // Save the buffer as a bitmap.
+ Bitmap aBitmap(Size(nPageWidth, nPageHeight), vcl::PixelFormat::N24_BPP);
+ AlphaMask aMask(Size(nPageWidth, nPageHeight));
+ {
+ BitmapScopedWriteAccess pWriteAccess(aBitmap);
+ AlphaScopedWriteAccess pMaskAccess(aMask);
+ ConstScanline pPdfBuffer = pPdfBitmap->getBuffer();
+ const int nStride = pPdfBitmap->getStride();
+ std::vector<sal_uInt8> aScanlineAlpha(nPageWidth);
+ for (size_t nRow = 0; nRow < nPageHeight; ++nRow)
+ {
+ ConstScanline pPdfLine = pPdfBuffer + (nStride * nRow);
+ // pdfium byte order is BGRA.
+ pWriteAccess->CopyScanline(nRow, pPdfLine, ScanlineFormat::N32BitTcBgra, nStride);
+ for (size_t nCol = 0; nCol < nPageWidth; ++nCol)
+ {
+ // Invert alpha (source is alpha, target is opacity).
+ aScanlineAlpha[nCol] = ~pPdfLine[3];
+ pPdfLine += 4;
+ }
+ pMaskAccess->CopyScanline(nRow, aScanlineAlpha.data(), ScanlineFormat::N8BitPal,
+ nPageWidth);
+ }
+ }
+
+ if (bTransparent)
+ {
+ rBitmaps.emplace_back(aBitmap, aMask);
+ }
+ else
+ {
+ rBitmaps.emplace_back(std::move(aBitmap));
+ }
+ }
+
+ return rBitmaps.size();
+}
+
+bool importPdfVectorGraphicData(SvStream& rStream,
+ std::shared_ptr<VectorGraphicData>& rVectorGraphicData)
+{
+ BinaryDataContainer aDataContainer = vcl::pdf::createBinaryDataContainer(rStream);
+ if (aDataContainer.isEmpty())
+ {
+ SAL_WARN("vcl.filter", "ImportPDF: empty PDF data array");
+ return false;
+ }
+
+ rVectorGraphicData
+ = std::make_shared<VectorGraphicData>(aDataContainer, VectorGraphicDataType::Pdf);
+
+ return true;
+}
+
+bool ImportPDF(SvStream& rStream, Graphic& rGraphic)
+{
+ std::shared_ptr<VectorGraphicData> pVectorGraphicData;
+ if (!importPdfVectorGraphicData(rStream, pVectorGraphicData))
+ return false;
+ rGraphic = Graphic(pVectorGraphicData);
+ return true;
+}
+
+namespace
+{
+basegfx::B2DPoint convertFromPDFInternalToHMM(basegfx::B2DSize const& rInputPoint,
+ basegfx::B2DSize const& rPageSize)
+{
+ double x = convertPointToMm100(rInputPoint.getX());
+ double y = convertPointToMm100(rPageSize.getY() - rInputPoint.getY());
+ return { x, y };
+}
+
+std::vector<PDFGraphicAnnotation>
+findAnnotations(const std::unique_ptr<vcl::pdf::PDFiumPage>& pPage, basegfx::B2DSize aPageSize)
+{
+ std::vector<PDFGraphicAnnotation> aPDFGraphicAnnotations;
+ if (!pPage)
+ {
+ return aPDFGraphicAnnotations;
+ }
+
+ for (int nAnnotation = 0; nAnnotation < pPage->getAnnotationCount(); nAnnotation++)
+ {
+ auto pAnnotation = pPage->getAnnotation(nAnnotation);
+ if (pAnnotation)
+ {
+ auto eSubtype = pAnnotation->getSubType();
+
+ if (eSubtype == vcl::pdf::PDFAnnotationSubType::Text
+ || eSubtype == vcl::pdf::PDFAnnotationSubType::Polygon
+ || eSubtype == vcl::pdf::PDFAnnotationSubType::Circle
+ || eSubtype == vcl::pdf::PDFAnnotationSubType::Square
+ || eSubtype == vcl::pdf::PDFAnnotationSubType::Ink
+ || eSubtype == vcl::pdf::PDFAnnotationSubType::Highlight
+ || eSubtype == vcl::pdf::PDFAnnotationSubType::Line)
+ {
+ OUString sAuthor = pAnnotation->getString(vcl::pdf::constDictionaryKeyTitle);
+ OUString sText = pAnnotation->getString(vcl::pdf::constDictionaryKeyContents);
+
+ basegfx::B2DRectangle rRectangle = pAnnotation->getRectangle();
+ basegfx::B2DRectangle rRectangleHMM(
+ convertPointToMm100(rRectangle.getMinX()),
+ convertPointToMm100(aPageSize.getY() - rRectangle.getMinY()),
+ convertPointToMm100(rRectangle.getMaxX()),
+ convertPointToMm100(aPageSize.getY() - rRectangle.getMaxY()));
+
+ OUString sDateTimeString
+ = pAnnotation->getString(vcl::pdf::constDictionaryKeyModificationDate);
+ OUString sISO8601String = vcl::pdf::convertPdfDateToISO8601(sDateTimeString);
+
+ css::util::DateTime aDateTime;
+ if (!sISO8601String.isEmpty())
+ {
+ utl::ISO8601parseDateTime(sISO8601String, aDateTime);
+ }
+
+ Color aColor = pAnnotation->getColor();
+
+ aPDFGraphicAnnotations.emplace_back();
+
+ auto& rPDFGraphicAnnotation = aPDFGraphicAnnotations.back();
+ rPDFGraphicAnnotation.maRectangle = rRectangleHMM;
+ rPDFGraphicAnnotation.maAuthor = sAuthor;
+ rPDFGraphicAnnotation.maText = sText;
+ rPDFGraphicAnnotation.maDateTime = aDateTime;
+ rPDFGraphicAnnotation.meSubType = eSubtype;
+ rPDFGraphicAnnotation.maColor = aColor;
+
+ if (eSubtype == vcl::pdf::PDFAnnotationSubType::Polygon)
+ {
+ auto const& rVertices = pAnnotation->getVertices();
+ if (!rVertices.empty())
+ {
+ auto pMarker = std::make_shared<vcl::pdf::PDFAnnotationMarkerPolygon>();
+ rPDFGraphicAnnotation.mpMarker = pMarker;
+ for (auto const& rVertex : rVertices)
+ {
+ auto aPoint = convertFromPDFInternalToHMM(rVertex, aPageSize);
+ pMarker->maPolygon.append(aPoint);
+ }
+ pMarker->maPolygon.setClosed(true);
+ pMarker->mnWidth = convertPointToMm100(pAnnotation->getBorderWidth());
+ if (pAnnotation->hasKey(vcl::pdf::constDictionaryKeyInteriorColor))
+ pMarker->maFillColor = pAnnotation->getInteriorColor();
+ }
+ }
+ else if (eSubtype == vcl::pdf::PDFAnnotationSubType::Square)
+ {
+ auto pMarker = std::make_shared<vcl::pdf::PDFAnnotationMarkerSquare>();
+ rPDFGraphicAnnotation.mpMarker = pMarker;
+ pMarker->mnWidth = convertPointToMm100(pAnnotation->getBorderWidth());
+ if (pAnnotation->hasKey(vcl::pdf::constDictionaryKeyInteriorColor))
+ pMarker->maFillColor = pAnnotation->getInteriorColor();
+ }
+ else if (eSubtype == vcl::pdf::PDFAnnotationSubType::Circle)
+ {
+ auto pMarker = std::make_shared<vcl::pdf::PDFAnnotationMarkerCircle>();
+ rPDFGraphicAnnotation.mpMarker = pMarker;
+ pMarker->mnWidth = convertPointToMm100(pAnnotation->getBorderWidth());
+ if (pAnnotation->hasKey(vcl::pdf::constDictionaryKeyInteriorColor))
+ pMarker->maFillColor = pAnnotation->getInteriorColor();
+ }
+ else if (eSubtype == vcl::pdf::PDFAnnotationSubType::Ink)
+ {
+ auto const& rStrokesList = pAnnotation->getInkStrokes();
+ if (!rStrokesList.empty())
+ {
+ auto pMarker = std::make_shared<vcl::pdf::PDFAnnotationMarkerInk>();
+ rPDFGraphicAnnotation.mpMarker = pMarker;
+ for (auto const& rStrokes : rStrokesList)
+ {
+ basegfx::B2DPolygon aPolygon;
+ for (auto const& rVertex : rStrokes)
+ {
+ auto aPoint = convertFromPDFInternalToHMM(rVertex, aPageSize);
+ aPolygon.append(aPoint);
+ }
+ pMarker->maStrokes.push_back(aPolygon);
+ }
+ float fWidth = pAnnotation->getBorderWidth();
+ pMarker->mnWidth = convertPointToMm100(fWidth);
+ if (pAnnotation->hasKey(vcl::pdf::constDictionaryKeyInteriorColor))
+ pMarker->maFillColor = pAnnotation->getInteriorColor();
+ }
+ }
+ else if (eSubtype == vcl::pdf::PDFAnnotationSubType::Highlight)
+ {
+ size_t nCount = pAnnotation->getAttachmentPointsCount();
+ if (nCount > 0)
+ {
+ auto pMarker = std::make_shared<vcl::pdf::PDFAnnotationMarkerHighlight>(
+ vcl::pdf::PDFTextMarkerType::Highlight);
+ rPDFGraphicAnnotation.mpMarker = pMarker;
+ for (size_t i = 0; i < nCount; ++i)
+ {
+ auto aAttachmentPoints = pAnnotation->getAttachmentPoints(i);
+ if (!aAttachmentPoints.empty())
+ {
+ basegfx::B2DPolygon aPolygon;
+ aPolygon.setClosed(true);
+
+ auto aPoint1
+ = convertFromPDFInternalToHMM(aAttachmentPoints[0], aPageSize);
+ aPolygon.append(aPoint1);
+ auto aPoint2
+ = convertFromPDFInternalToHMM(aAttachmentPoints[1], aPageSize);
+ aPolygon.append(aPoint2);
+ auto aPoint3
+ = convertFromPDFInternalToHMM(aAttachmentPoints[3], aPageSize);
+ aPolygon.append(aPoint3);
+ auto aPoint4
+ = convertFromPDFInternalToHMM(aAttachmentPoints[2], aPageSize);
+ aPolygon.append(aPoint4);
+
+ pMarker->maQuads.push_back(aPolygon);
+ }
+ }
+ }
+ }
+ else if (eSubtype == vcl::pdf::PDFAnnotationSubType::Line)
+ {
+ auto const& rLineGeometry = pAnnotation->getLineGeometry();
+ if (!rLineGeometry.empty())
+ {
+ auto pMarker = std::make_shared<vcl::pdf::PDFAnnotationMarkerLine>();
+ rPDFGraphicAnnotation.mpMarker = pMarker;
+
+ auto aPoint1 = convertFromPDFInternalToHMM(rLineGeometry[0], aPageSize);
+ pMarker->maLineStart = aPoint1;
+
+ auto aPoint2 = convertFromPDFInternalToHMM(rLineGeometry[1], aPageSize);
+ pMarker->maLineEnd = aPoint2;
+
+ float fWidth = pAnnotation->getBorderWidth();
+ pMarker->mnWidth = convertPointToMm100(fWidth);
+ }
+ }
+ }
+ }
+ }
+ return aPDFGraphicAnnotations;
+}
+
+} // end anonymous namespace
+
+size_t ImportPDFUnloaded(const OUString& rURL, std::vector<PDFGraphicResult>& rGraphics)
+{
+ std::unique_ptr<SvStream> xStream(
+ ::utl::UcbStreamHelper::CreateStream(rURL, StreamMode::READ | StreamMode::SHARE_DENYNONE));
+
+ // Save the original PDF stream for later use.
+ BinaryDataContainer aDataContainer = vcl::pdf::createBinaryDataContainer(*xStream);
+ if (aDataContainer.isEmpty())
+ return 0;
+
+ // Prepare the link with the PDF stream.
+ auto pGfxLink = std::make_shared<GfxLink>(aDataContainer, GfxLinkType::NativePdf);
+
+ auto pPdfium = vcl::pdf::PDFiumLibrary::get();
+ if (!pPdfium)
+ {
+ return 0;
+ }
+
+ // Load the buffer using pdfium.
+ auto pPdfDocument
+ = pPdfium->openDocument(pGfxLink->GetData(), pGfxLink->GetDataSize(), OString());
+
+ if (!pPdfDocument)
+ return 0;
+
+ const int nPageCount = pPdfDocument->getPageCount();
+ if (nPageCount <= 0)
+ return 0;
+
+ for (int nPageIndex = 0; nPageIndex < nPageCount; ++nPageIndex)
+ {
+ basegfx::B2DSize aPageSize = pPdfDocument->getPageSize(nPageIndex);
+ if (aPageSize.getX() <= 0.0 || aPageSize.getY() <= 0.0)
+ continue;
+
+ // Returned unit is points, convert that to twip
+ // 1 pt = 20 twips
+ constexpr double pointToTwipconversionRatio = 20;
+
+ tools::Long nPageWidth = convertTwipToMm100(aPageSize.getX() * pointToTwipconversionRatio);
+ tools::Long nPageHeight = convertTwipToMm100(aPageSize.getY() * pointToTwipconversionRatio);
+
+ // Create the Graphic with the VectorGraphicDataPtr and link the original PDF stream.
+ // We swap out this Graphic as soon as possible, and a later swap in
+ // actually renders the correct Bitmap on demand.
+ Graphic aGraphic(pGfxLink, nPageIndex);
+
+ auto pPage = pPdfDocument->openPage(nPageIndex);
+
+ std::vector<PDFGraphicAnnotation> aPDFGraphicAnnotations
+ = findAnnotations(pPage, aPageSize);
+
+ rGraphics.emplace_back(std::move(aGraphic), Size(nPageWidth, nPageHeight),
+ aPDFGraphicAnnotations);
+ }
+
+ return rGraphics.size();
+}
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/vcl/source/filter/ipict/ipict.cxx b/vcl/source/filter/ipict/ipict.cxx
new file mode 100644
index 000000000..0f8fd5ba0
--- /dev/null
+++ b/vcl/source/filter/ipict/ipict.cxx
@@ -0,0 +1,2041 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <filter/PictReader.hxx>
+#include <string.h>
+#include <osl/thread.h>
+#include <sal/log.hxx>
+#include <vcl/BitmapTools.hxx>
+#include <vcl/graph.hxx>
+#include <vcl/gdimtf.hxx>
+#include <tools/poly.hxx>
+#include <tools/fract.hxx>
+#include <tools/stream.hxx>
+#include <vcl/virdev.hxx>
+#include <math.h>
+#include "shape.hxx"
+#include <memory>
+
+#include <vcl/FilterConfigItem.hxx>
+ // complete FilterConfigItem for GraphicImport under -fsanitize=function
+
+namespace PictReaderInternal {
+ namespace {
+
+ //! utilitary class to store a pattern, ...
+ class Pattern {
+ public:
+ //! constructor
+ Pattern() : penStyle(PEN_SOLID),
+ brushStyle(BRUSH_SOLID),
+ nBitCount(64),
+ isColor(false),
+ isRead(false)
+ {}
+
+ //! reads black/white pattern from SvStream
+ sal_uInt8 read(SvStream &stream);
+ //! sets the color
+ void setColor(Color col) { isColor = true; color = col; }
+ /** returns a color which can be "used" to replace the pattern,
+ * created from ForeColor and BackColor, ...
+ *
+ * note: maybe, we must also use some mode PatCopy, ... to define the color
+ */
+ Color getColor(Color bkColor, Color fgColor) const {
+ if (isColor) return color;
+ // we create a gray pattern from nBitCount
+ double alpha = nBitCount / 64.0;
+ return Color(sal_uInt8(alpha*fgColor.GetRed()+(1.0-alpha)*bkColor.GetRed()),
+ sal_uInt8(alpha*fgColor.GetGreen()+(1.0-alpha)*bkColor.GetGreen()),
+ sal_uInt8(alpha*fgColor.GetBlue()+(1.0-alpha)*bkColor.GetBlue()));
+ }
+
+ //! returns true if this is the default pattern
+ bool isDefault() const { return !isRead; }
+
+ enum PenStyle { PEN_NULL, PEN_SOLID, PEN_DOT, PEN_DASH, PEN_DASHDOT };
+ enum BrushStyle { BRUSH_SOLID, BRUSH_HORZ, BRUSH_VERT,
+ BRUSH_CROSS, BRUSH_DIAGCROSS, BRUSH_UPDIAG, BRUSH_DOWNDIAG,
+ BRUSH_25, BRUSH_50, BRUSH_75 };
+ // Data
+ enum PenStyle penStyle;
+ enum BrushStyle brushStyle;
+ short nBitCount;
+
+ bool isColor; // true if it is a color pattern
+ Color color;
+
+ protected:
+ // flag to know if the pattern came from reading the picture, or if it is the default pattern
+ bool isRead;
+ };
+
+ }
+
+ sal_uInt8 Pattern::read(SvStream &stream) {
+ unsigned char nbyte[8] = {0};
+ isColor = false;
+
+ // count the no of bits in pattern which are set to 1:
+ nBitCount=0;
+ for (unsigned char & ny : nbyte) {
+ stream.ReadChar( reinterpret_cast<char&>(ny) );
+ for (short nx=0; nx<8; nx++) {
+ if ( (ny & (1<<nx)) != 0 ) nBitCount++;
+ }
+ }
+
+ // store pattern in 2 long words:
+ sal_uInt32 nHiBytes = (((((static_cast<sal_uInt32>(nbyte[0])<<8)|
+ static_cast<sal_uInt32>(nbyte[1]))<<8)|
+ static_cast<sal_uInt32>(nbyte[2]))<<8)|
+ static_cast<sal_uInt32>(nbyte[3]);
+ sal_uInt32 nLoBytes = (((((static_cast<sal_uInt32>(nbyte[4])<<8)|
+ static_cast<sal_uInt32>(nbyte[5]))<<8)|
+ static_cast<sal_uInt32>(nbyte[6]))<<8)|
+ static_cast<sal_uInt32>(nbyte[7]);
+
+ // create a PenStyle:
+ if (nBitCount<=0) penStyle=PEN_NULL;
+ else if (nBitCount<=16) penStyle=PEN_DOT;
+ else if (nBitCount<=32) penStyle=PEN_DASHDOT;
+ else if (nBitCount<=48) penStyle=PEN_DASH;
+ else penStyle=PEN_SOLID;
+
+ // create a BrushStyle:
+ if (nHiBytes==0xffffffff && nLoBytes==0xffffffff) brushStyle=BRUSH_SOLID;
+ else if (nHiBytes==0xff000000 && nLoBytes==0x00000000) brushStyle=BRUSH_HORZ;
+ else if (nHiBytes==0x80808080 && nLoBytes==0x80808080) brushStyle=BRUSH_VERT;
+ else if (nHiBytes==0xff808080 && nLoBytes==0x80808080) brushStyle=BRUSH_CROSS;
+ else if (nHiBytes==0x01824428 && nLoBytes==0x10284482) brushStyle=BRUSH_DIAGCROSS;
+ else if (nHiBytes==0x80402010 && nLoBytes==0x08040201) brushStyle=BRUSH_UPDIAG;
+ else if (nHiBytes==0x01020408 && nLoBytes==0x10204080) brushStyle=BRUSH_DOWNDIAG;
+ else if (nBitCount<=24) brushStyle=BRUSH_25;
+ else if (nBitCount<=40) brushStyle=BRUSH_50;
+ else if (nBitCount<=56) brushStyle=BRUSH_75;
+ else brushStyle=BRUSH_SOLID;
+
+ isRead = true;
+
+ return 8;
+ }
+}
+
+//============================ PictReader ==================================
+
+namespace {
+
+enum class PictDrawingMethod {
+ FRAME, PAINT, ERASE, INVERT, FILL,
+ TEXT, UNDEFINED
+};
+
+class PictReader {
+ typedef class PictReaderInternal::Pattern Pattern;
+private:
+
+ SvStream * pPict; // The Pict file to read.
+ VclPtr<VirtualDevice> pVirDev; // Here the drawing method will be called.
+ // A recording into the GDIMetaFile will take place.
+
+ sal_uInt64 nOrigPos; // Initial position in pPict.
+ bool IsVersion2; // If it is a version 2 Pictfile.
+ tools::Rectangle aBoundingRect; // Min/Max-Rectangle for the whole drawing.
+
+ Point aPenPosition;
+ Point aTextPosition;
+ Color aActForeColor;
+ Color aActBackColor;
+ Pattern eActPenPattern;
+ Pattern eActFillPattern;
+ Pattern eActBackPattern;
+ Size nActPenSize;
+ // Note: Postscript mode is stored by setting eActRop to RasterOp::N1
+ RasterOp eActROP;
+ PictDrawingMethod eActMethod;
+ Size aActOvalSize;
+ vcl::Font aActFont;
+
+ Fraction aHRes;
+ Fraction aVRes;
+
+ Point ReadPoint();
+
+ Point ReadDeltaH(Point aBase);
+ Point ReadDeltaV(Point aBase);
+
+ Point ReadUnsignedDeltaH(Point aBase);
+ Point ReadUnsignedDeltaV(Point aBase);
+
+ Size ReadSize();
+
+ Color ReadColor();
+
+ Color ReadRGBColor();
+
+ void ReadRectangle(tools::Rectangle & rRect);
+
+ sal_uInt64 ReadPolygon(tools::Polygon & rPoly);
+
+ sal_uInt64 ReadPixPattern(Pattern &pattern);
+
+ tools::Rectangle aLastRect;
+ sal_uInt8 ReadAndDrawRect(PictDrawingMethod eMethod);
+ sal_uInt8 ReadAndDrawSameRect(PictDrawingMethod eMethod);
+
+ tools::Rectangle aLastRoundRect;
+ sal_uInt8 ReadAndDrawRoundRect(PictDrawingMethod eMethod);
+ sal_uInt8 ReadAndDrawSameRoundRect(PictDrawingMethod eMethod);
+
+ tools::Rectangle aLastOval;
+ sal_uInt8 ReadAndDrawOval(PictDrawingMethod eMethod);
+ sal_uInt8 ReadAndDrawSameOval(PictDrawingMethod eMethod);
+
+ tools::Polygon aLastPolygon;
+ sal_uInt64 ReadAndDrawPolygon(PictDrawingMethod eMethod);
+ sal_uInt8 ReadAndDrawSamePolygon(PictDrawingMethod eMethod);
+
+ tools::Rectangle aLastArcRect;
+ sal_uInt8 ReadAndDrawArc(PictDrawingMethod eMethod);
+ sal_uInt8 ReadAndDrawSameArc(PictDrawingMethod eMethod);
+
+ sal_uInt64 ReadAndDrawRgn(PictDrawingMethod eMethod);
+ sal_uInt8 ReadAndDrawSameRgn(PictDrawingMethod eMethod);
+
+ // returns true if there's no need to print the shape/text/frame
+ bool IsInvisible( PictDrawingMethod eMethod ) const {
+ if ( eActROP == RasterOp::N1 ) return true;
+ if ( eMethod == PictDrawingMethod::FRAME && nActPenSize.IsEmpty() ) return true;
+ return false;
+ }
+
+ void DrawingMethod(PictDrawingMethod eMethod);
+
+ sal_uInt64 ReadAndDrawText();
+
+ sal_uInt64 ReadPixMapEtc(BitmapEx & rBitmap, bool bBaseAddr, bool bColorTable,
+ tools::Rectangle * pSrcRect, tools::Rectangle * pDestRect,
+ bool bMode, bool bMaskRgn);
+
+ void ReadHeader();
+ // Reads the header of the Pict file, set IsVersion and aBoundingRect
+
+ sal_uInt64 ReadData(sal_uInt16 nOpcode);
+ // Reads the date of anOopcode and executes the operation.
+ // The number of data bytes belonging to the opcode will be returned
+ // in any case.
+
+ void SetLineColor( const Color& rColor );
+ void SetFillColor( const Color& rColor );
+
+ // OSNOLA: returns the text encoding which must be used for system id
+ static rtl_TextEncoding GetTextEncoding (sal_uInt16 fId = 0xFFFF);
+
+public:
+
+ PictReader()
+ : pPict(nullptr)
+ , pVirDev(nullptr)
+ , nOrigPos(0)
+ , IsVersion2(false)
+ , eActROP(RasterOp::OverPaint)
+ , eActMethod(PictDrawingMethod::UNDEFINED)
+ {
+ aActFont.SetCharSet(GetTextEncoding());
+ }
+
+ void ReadPict( SvStream & rStreamPict, GDIMetaFile & rGDIMetaFile );
+ // reads a pict file from the stream and fills the GDIMetaFile
+
+};
+
+}
+
+static void SetByte(sal_uInt16& nx, sal_uInt16 ny, vcl::bitmap::RawBitmap& rBitmap, sal_uInt16 nPixelSize, sal_uInt8 nDat, sal_uInt16 nWidth, std::vector<Color> const & rvPalette)
+{
+ switch (nPixelSize)
+ {
+ case 1:
+ rBitmap.SetPixel(ny, nx++, rvPalette[(nDat >> 7) & 1]);
+ if ( nx == nWidth ) break;
+ rBitmap.SetPixel(ny, nx++, rvPalette[(nDat >> 6) & 1]);
+ if ( nx == nWidth ) break;
+ rBitmap.SetPixel(ny, nx++, rvPalette[(nDat >> 5) & 1]);
+ if ( nx == nWidth ) break;
+ rBitmap.SetPixel(ny, nx++, rvPalette[(nDat >> 4) & 1]);
+ if ( nx == nWidth ) break;
+ rBitmap.SetPixel(ny, nx++, rvPalette[(nDat >> 3) & 1]);
+ if ( nx == nWidth ) break;
+ rBitmap.SetPixel(ny, nx++, rvPalette[(nDat >> 2) & 1]);
+ if ( nx == nWidth ) break;
+ rBitmap.SetPixel(ny, nx++, rvPalette[(nDat >> 1) & 1]);
+ if ( nx == nWidth ) break;
+ rBitmap.SetPixel(ny, nx++, rvPalette[nDat & 1]);
+ break;
+ case 2:
+ rBitmap.SetPixel(ny, nx++, rvPalette[nDat >> 6]);
+ if ( nx == nWidth ) break;
+ rBitmap.SetPixel(ny, nx++, rvPalette[(nDat>>4)&3]);
+ if ( nx == nWidth ) break;
+ rBitmap.SetPixel(ny, nx++, rvPalette[(nDat>>2)&3]);
+ if ( nx == nWidth ) break;
+ rBitmap.SetPixel(ny, nx++, rvPalette[nDat & 3]);
+ break;
+ case 4:
+ rBitmap.SetPixel(ny, nx++, rvPalette[nDat >> 4]);
+ if ( nx == nWidth ) break;
+ rBitmap.SetPixel(ny, nx++, rvPalette[nDat & 0x0f]);
+ break;
+ case 8:
+ rBitmap.SetPixel(ny, nx++, rvPalette[nDat]);
+ break;
+ }
+}
+
+//=================== methods of PictReader ==============================
+rtl_TextEncoding PictReader::GetTextEncoding (sal_uInt16 fId) {
+ static rtl_TextEncoding enc = []()
+ {
+ rtl_TextEncoding def = osl_getThreadTextEncoding();
+ // we keep osl_getThreadTextEncoding only if it is a mac encoding
+ switch(def) {
+ case RTL_TEXTENCODING_APPLE_ROMAN:
+ case RTL_TEXTENCODING_APPLE_ARABIC:
+ case RTL_TEXTENCODING_APPLE_CENTEURO:
+ case RTL_TEXTENCODING_APPLE_CROATIAN:
+ case RTL_TEXTENCODING_APPLE_CYRILLIC:
+ case RTL_TEXTENCODING_APPLE_DEVANAGARI:
+ case RTL_TEXTENCODING_APPLE_FARSI:
+ case RTL_TEXTENCODING_APPLE_GREEK:
+ case RTL_TEXTENCODING_APPLE_GUJARATI:
+ case RTL_TEXTENCODING_APPLE_GURMUKHI:
+ case RTL_TEXTENCODING_APPLE_HEBREW:
+ case RTL_TEXTENCODING_APPLE_ICELAND:
+ case RTL_TEXTENCODING_APPLE_ROMANIAN:
+ case RTL_TEXTENCODING_APPLE_THAI:
+ case RTL_TEXTENCODING_APPLE_TURKISH:
+ case RTL_TEXTENCODING_APPLE_UKRAINIAN:
+ case RTL_TEXTENCODING_APPLE_CHINSIMP:
+ case RTL_TEXTENCODING_APPLE_CHINTRAD:
+ case RTL_TEXTENCODING_APPLE_JAPANESE:
+ case RTL_TEXTENCODING_APPLE_KOREAN:
+ return def; break;
+ default:
+ break;
+ }
+ return RTL_TEXTENCODING_APPLE_ROMAN;
+ }();
+ if (fId == 13) return RTL_TEXTENCODING_ADOBE_DINGBATS; // CHECKME
+ if (fId == 23) return RTL_TEXTENCODING_ADOBE_SYMBOL;
+ return enc;
+}
+
+void PictReader::SetLineColor( const Color& rColor )
+{
+ pVirDev->SetLineColor( rColor );
+}
+
+void PictReader::SetFillColor( const Color& rColor )
+{
+ pVirDev->SetFillColor( rColor );
+}
+
+Point PictReader::ReadPoint()
+{
+ short nx(0), ny(0);
+
+ pPict->ReadInt16( ny ).ReadInt16( nx );
+
+ Point aPoint(nx - aBoundingRect.Left(), ny - aBoundingRect.Top());
+
+ SAL_INFO("filter.pict", "ReadPoint: " << aPoint);
+ return aPoint;
+}
+
+Point PictReader::ReadDeltaH(Point aBase)
+{
+ signed char ndh(0);
+
+ pPict->ReadChar( reinterpret_cast<char&>(ndh) );
+
+ return Point( aBase.X() + static_cast<tools::Long>(ndh), aBase.Y() );
+}
+
+Point PictReader::ReadDeltaV(Point aBase)
+{
+ signed char ndv(0);
+
+ pPict->ReadChar( reinterpret_cast<char&>(ndv) );
+
+ return Point( aBase.X(), aBase.Y() + static_cast<tools::Long>(ndv) );
+}
+
+Point PictReader::ReadUnsignedDeltaH(Point aBase)
+{
+ sal_uInt8 ndh(0);
+
+ pPict->ReadUChar( ndh );
+
+ return Point( aBase.X() + static_cast<tools::Long>(ndh), aBase.Y() );
+}
+
+Point PictReader::ReadUnsignedDeltaV(Point aBase)
+{
+ sal_uInt8 ndv(0);
+
+ pPict->ReadUChar( ndv );
+
+ return Point( aBase.X(), aBase.Y() + static_cast<tools::Long>(ndv) );
+}
+
+Size PictReader::ReadSize()
+{
+ short nx(0), ny(0);
+
+ pPict->ReadInt16( ny ).ReadInt16( nx );
+
+ return Size(nx, ny);
+}
+
+Color PictReader::ReadColor()
+{
+ Color aCol;
+
+ sal_uInt32 nCol(0);
+ pPict->ReadUInt32( nCol );
+ switch (nCol)
+ {
+ case 33: aCol=COL_BLACK; break;
+ case 30: aCol=COL_WHITE; break;
+ case 205: aCol=COL_LIGHTRED; break;
+ case 341: aCol=COL_LIGHTGREEN; break;
+ case 409: aCol=COL_LIGHTBLUE; break;
+ case 273: aCol=COL_LIGHTCYAN; break;
+ case 137: aCol=COL_LIGHTMAGENTA; break;
+ case 69: aCol=COL_YELLOW; break;
+ default: aCol=COL_LIGHTGRAY;
+ }
+ return aCol;
+}
+
+Color PictReader::ReadRGBColor()
+{
+ sal_uInt16 nR(0), nG(0), nB(0);
+
+ pPict->ReadUInt16( nR ).ReadUInt16( nG ).ReadUInt16( nB );
+ return Color( static_cast<sal_uInt8>( nR >> 8 ), static_cast<sal_uInt8>( nG >> 8 ), static_cast<sal_uInt8>( nB >> 8 ) );
+}
+
+void PictReader::ReadRectangle(tools::Rectangle & rRect)
+{
+ Point aTopLeft = ReadPoint();
+ Point aBottomRight = ReadPoint();
+ if (!pPict->good() || aTopLeft.X() > aBottomRight.X() || aTopLeft.Y() > aBottomRight.Y())
+ {
+ SAL_WARN("filter.pict", "broken rectangle");
+ pPict->SetError( SVSTREAM_FILEFORMAT_ERROR );
+ rRect = tools::Rectangle();
+ return;
+ }
+ rRect=tools::Rectangle(aTopLeft,aBottomRight);
+
+ SAL_INFO("filter.pict", "ReadRectangle: " << rRect);
+}
+
+sal_uInt64 PictReader::ReadPolygon(tools::Polygon & rPoly)
+{
+ sal_uInt16 nSize(0);
+ pPict->ReadUInt16(nSize);
+ pPict->SeekRel(8);
+ sal_uInt64 nDataSize = static_cast<sal_uInt64>(nSize);
+ nSize=(nSize-10)/4;
+ const size_t nMaxPossiblePoints = pPict->remainingSize() / 2 * sizeof(sal_uInt16);
+ if (nSize > nMaxPossiblePoints)
+ {
+ SAL_WARN("filter.pict", "pict record claims to have: " << nSize << " points, but only " << nMaxPossiblePoints << " possible, clamping");
+ nSize = nMaxPossiblePoints;
+ }
+ rPoly.SetSize(nSize);
+ for (sal_uInt16 i = 0; i < nSize; ++i)
+ {
+ rPoly.SetPoint(ReadPoint(), i);
+ if (!pPict->good())
+ {
+ rPoly.SetSize(i);
+ break;
+ }
+ }
+ return nDataSize;
+}
+
+sal_uInt64 PictReader::ReadPixPattern(PictReader::Pattern &pattern)
+{
+ // Don't know if this is correct because no picture which contains PixPatterns found.
+ // Here again the attempt to calculate the size of the date to create simple StarView-Styles
+ // from them. Luckily a PixPattern always contains a normal pattern.
+
+ sal_uInt64 nDataSize;
+
+ sal_uInt16 nPatType(0);
+ pPict->ReadUInt16(nPatType);
+ if (nPatType==1) {
+ pattern.read(*pPict);
+ BitmapEx aBMP;
+ nDataSize=ReadPixMapEtc(aBMP,false,true,nullptr,nullptr,false,false);
+ // CHANGEME: use average pixmap colors to update the pattern, ...
+ if (nDataSize!=0xffffffff) nDataSize+=10;
+ }
+ else if (nPatType==2) {
+ pattern.read(*pPict);
+ // RGBColor
+ sal_uInt16 nR, nG, nB;
+ pPict->ReadUInt16( nR ).ReadUInt16( nG ).ReadUInt16( nB );
+ Color col(static_cast<sal_uInt8>( nR >> 8 ), static_cast<sal_uInt8>( nG >> 8 ), static_cast<sal_uInt8>( nB >> 8 ) );
+ pattern.setColor(col);
+ nDataSize=16;
+ }
+ else nDataSize=0xffffffff;
+
+ return nDataSize;
+}
+
+sal_uInt8 PictReader::ReadAndDrawRect(PictDrawingMethod eMethod)
+{
+ ReadRectangle(aLastRect);
+ ReadAndDrawSameRect(eMethod);
+ return 8;
+}
+
+sal_uInt8 PictReader::ReadAndDrawSameRect(PictDrawingMethod eMethod)
+{
+ if (IsInvisible(eMethod)) return 0;
+ DrawingMethod(eMethod);
+ PictReaderShape::drawRectangle( pVirDev, eMethod == PictDrawingMethod::FRAME, aLastRect, nActPenSize );
+ return 0;
+}
+
+sal_uInt8 PictReader::ReadAndDrawRoundRect(PictDrawingMethod eMethod)
+{
+ ReadRectangle(aLastRoundRect);
+ ReadAndDrawSameRoundRect(eMethod);
+ return 8;
+}
+
+sal_uInt8 PictReader::ReadAndDrawSameRoundRect(PictDrawingMethod eMethod)
+{
+ if (IsInvisible(eMethod)) return 0;
+ DrawingMethod(eMethod);
+ PictReaderShape::drawRoundRectangle( pVirDev, eMethod == PictDrawingMethod::FRAME, aLastRoundRect, aActOvalSize, nActPenSize );
+ return 0;
+}
+
+sal_uInt8 PictReader::ReadAndDrawOval(PictDrawingMethod eMethod)
+{
+ ReadRectangle(aLastOval);
+ ReadAndDrawSameOval(eMethod);
+ return 8;
+}
+
+sal_uInt8 PictReader::ReadAndDrawSameOval(PictDrawingMethod eMethod)
+{
+ if (IsInvisible(eMethod)) return 0;
+ DrawingMethod(eMethod);
+ PictReaderShape::drawEllipse( pVirDev, eMethod == PictDrawingMethod::FRAME, aLastOval, nActPenSize );
+ return 0;
+}
+
+sal_uInt64 PictReader::ReadAndDrawPolygon(PictDrawingMethod eMethod)
+{
+ sal_uInt64 nDataSize;
+ nDataSize=ReadPolygon(aLastPolygon);
+ ReadAndDrawSamePolygon(eMethod);
+ return nDataSize;
+}
+
+sal_uInt8 PictReader::ReadAndDrawSamePolygon(PictDrawingMethod eMethod)
+{
+ if (IsInvisible(eMethod)) return 0;
+ DrawingMethod(eMethod);
+ PictReaderShape::drawPolygon( pVirDev, eMethod == PictDrawingMethod::FRAME, aLastPolygon, nActPenSize );
+ return 0;
+}
+
+
+sal_uInt8 PictReader::ReadAndDrawArc(PictDrawingMethod eMethod)
+{
+ ReadRectangle(aLastArcRect);
+ ReadAndDrawSameArc(eMethod);
+ return 12;
+}
+
+sal_uInt8 PictReader::ReadAndDrawSameArc(PictDrawingMethod eMethod)
+{
+ short nstartAngle, narcAngle;
+
+ pPict->ReadInt16( nstartAngle ).ReadInt16( narcAngle );
+ if (!pPict->good() || IsInvisible(eMethod)) return 4;
+ DrawingMethod(eMethod);
+
+ if (narcAngle<0) {
+ nstartAngle = nstartAngle + narcAngle;
+ narcAngle=-narcAngle;
+ }
+ const double pi = 2 * acos(0.0);
+ double fAng1 = static_cast<double>(nstartAngle) * pi / 180.0;
+ double fAng2 = static_cast<double>(nstartAngle + narcAngle) * pi / 180.0;
+ PictReaderShape::drawArc( pVirDev, eMethod == PictDrawingMethod::FRAME, aLastArcRect, fAng1, fAng2, nActPenSize );
+ return 4;
+}
+
+sal_uInt64 PictReader::ReadAndDrawRgn(PictDrawingMethod eMethod)
+{
+ sal_uInt16 nSize(0);
+ pPict->ReadUInt16( nSize );
+
+ // read the DATA
+ //
+ // a region data is a mask and is probably coded as
+ // - the first 8 bytes: bdbox ( which can be read by ReadRectangle )
+ // - then a list of line modifiers: y_i, a_0, b_0, a_1, b_1, ..., a_{n_i}, b_{n_i}, 0x7fff
+ // - 0x7fff
+ // where y_i is the increasing sequences of line coordinates
+ // and on each line: a0 < b0 < a1 < b1 < ... < a_{n_i} < b_{n_i}
+
+ // it can be probably decoded as :
+ // M=an empty mask: ie. (0, 0, ... ) with (left_box-right_box+1) zeroes
+ // then for each line (y_i):
+ // - takes M and inverts all values in [a_0,b_0-1], in [a_1,b_1-1] ...
+ // - sets M = new y_i line mask
+ ReadAndDrawSameRgn(eMethod);
+ return static_cast<sal_uInt64>(nSize);
+}
+
+sal_uInt8 PictReader::ReadAndDrawSameRgn(PictDrawingMethod eMethod)
+{
+ if (IsInvisible(eMethod)) return 0;
+ DrawingMethod(eMethod);
+ // DISPLAY: ...???...
+ return 0;
+}
+
+void PictReader::DrawingMethod(PictDrawingMethod eMethod)
+{
+ if( eActMethod==eMethod ) return;
+ switch (eMethod) {
+ case PictDrawingMethod::FRAME:
+ if (eActPenPattern.isDefault())
+ SetLineColor( aActForeColor );
+ else
+ SetLineColor(eActPenPattern.getColor(aActBackColor, aActForeColor));
+ SetFillColor( COL_TRANSPARENT );
+ pVirDev->SetRasterOp(eActROP);
+ break;
+ case PictDrawingMethod::PAINT:
+ SetLineColor( COL_TRANSPARENT );
+ if (eActPenPattern.isDefault())
+ SetFillColor( aActForeColor );
+ else
+ SetFillColor(eActPenPattern.getColor(aActBackColor, aActForeColor));
+ pVirDev->SetRasterOp(eActROP);
+ break;
+ case PictDrawingMethod::ERASE:
+ SetLineColor( COL_TRANSPARENT );
+ if (eActBackPattern.isDefault())
+ SetFillColor( aActBackColor );// Osnola: previously aActForeColor
+ else // checkMe
+ SetFillColor(eActBackPattern.getColor(COL_BLACK, aActBackColor));
+ pVirDev->SetRasterOp(RasterOp::OverPaint);
+ break;
+ case PictDrawingMethod::INVERT: // checkme
+ SetLineColor( COL_TRANSPARENT);
+ SetFillColor( COL_BLACK );
+ pVirDev->SetRasterOp(RasterOp::Invert);
+ break;
+ case PictDrawingMethod::FILL:
+ SetLineColor( COL_TRANSPARENT );
+ if (eActFillPattern.isDefault())
+ SetFillColor( aActForeColor );
+ else
+ SetFillColor(eActFillPattern.getColor(aActBackColor, aActForeColor));
+ pVirDev->SetRasterOp(RasterOp::OverPaint);
+ break;
+ case PictDrawingMethod::TEXT:
+ aActFont.SetColor(aActForeColor);
+ aActFont.SetFillColor(aActBackColor);
+ aActFont.SetTransparent(true);
+ pVirDev->SetFont(aActFont);
+ pVirDev->SetRasterOp(RasterOp::OverPaint);
+ break;
+ default:
+ break; // -Wall undefined not handled...
+ }
+ eActMethod=eMethod;
+}
+
+sal_uInt64 PictReader::ReadAndDrawText()
+{
+ char sText[256];
+
+ char nByteLen(0);
+ pPict->ReadChar(nByteLen);
+ sal_uInt32 nLen = static_cast<sal_uInt32>(nByteLen)&0x000000ff;
+ sal_uInt32 nDataLen = nLen + 1;
+ nLen = pPict->ReadBytes(&sText, nLen);
+
+ if (IsInvisible( PictDrawingMethod::TEXT )) return nDataLen;
+ DrawingMethod( PictDrawingMethod::TEXT );
+
+ // remove annoying control characters:
+ while ( nLen > 0 && static_cast<unsigned char>(sText[ nLen - 1 ]) < 32 )
+ nLen--;
+ sText[ nLen ] = 0;
+ OUString aString( sText, strlen(sText), aActFont.GetCharSet());
+ pVirDev->DrawText( Point( aTextPosition.X(), aTextPosition.Y() ), aString );
+ return nDataLen;
+}
+
+sal_uInt64 PictReader::ReadPixMapEtc( BitmapEx &rBitmap, bool bBaseAddr, bool bColorTable, tools::Rectangle* pSrcRect,
+ tools::Rectangle* pDestRect, bool bMode, bool bMaskRgn )
+{
+ std::unique_ptr<vcl::bitmap::RawBitmap> pBitmap;
+ sal_uInt16 nPackType(0), nPixelSize(0), nCmpCount(0), nCmpSize(0);
+ sal_uInt8 nDat(0), nRed(0), nGreen(0), nBlue(0);
+
+ // The calculation of nDataSize is considering the size of the whole data.
+ size_t nDataSize = 0;
+
+ // conditionally skip BaseAddr
+ if ( bBaseAddr )
+ {
+ pPict->SeekRel( 4 );
+ nDataSize += 4;
+ }
+
+ // Read PixMap or Bitmap structure;
+ sal_uInt16 nRowBytes(0), nBndX(0), nBndY(0), nWidth(0), nHeight(0);
+ pPict->ReadUInt16(nRowBytes).ReadUInt16(nBndY).ReadUInt16(nBndX).ReadUInt16(nHeight).ReadUInt16(nWidth);
+ if (nBndY > nHeight)
+ return 0xffffffff;
+ nHeight = nHeight - nBndY;
+ if (nHeight == 0)
+ return 0xffffffff;
+ if (nBndX > nWidth)
+ return 0xffffffff;
+ nWidth = nWidth - nBndX;
+ if (nWidth == 0)
+ return 0xffffffff;
+
+ std::vector<Color> aPalette;
+ const bool bNotMonoChrome = (nRowBytes & 0x8000) != 0;
+ if (bNotMonoChrome)
+ { // it is a PixMap
+ nRowBytes &= 0x3fff;
+ sal_uInt16 nVersion;
+ sal_uInt32 nPackSize;
+ sal_uInt16 nPixelType;
+ sal_uInt32 nPlaneBytes;
+ sal_uInt32 nHRes, nVRes;
+ pPict->ReadUInt16( nVersion ).ReadUInt16( nPackType ).ReadUInt32( nPackSize ).ReadUInt32( nHRes ).ReadUInt32( nVRes ).ReadUInt16( nPixelType ).ReadUInt16( nPixelSize ).ReadUInt16( nCmpCount ).ReadUInt16( nCmpSize ).ReadUInt32( nPlaneBytes );
+
+ pPict->SeekRel( 8 );
+ nDataSize += 46;
+
+ if ( bColorTable )
+ {
+ pPict->SeekRel( 6 );
+ sal_uInt16 nColTabSize(0);
+ pPict->ReadUInt16(nColTabSize);
+
+ if (nColTabSize > 255)
+ return 0xffffffff;
+
+ ++nColTabSize;
+
+ aPalette.resize(nColTabSize);
+
+ for (size_t i = 0; i < nColTabSize; ++i)
+ {
+ pPict->SeekRel(2);
+ sal_uInt8 nDummy;
+ pPict->ReadUChar( nRed ).ReadUChar( nDummy ).ReadUChar( nGreen ).ReadUChar( nDummy ).ReadUChar( nBlue ).ReadUChar( nDummy );
+ aPalette[i] = Color(nRed, nGreen, nBlue);
+ }
+
+ nDataSize += 8 + nColTabSize * 8;
+ }
+ }
+ else
+ {
+ nRowBytes &= 0x3fff;
+ nPixelSize = nCmpCount = nCmpSize = 1;
+ nDataSize += 10;
+ aPalette.resize(2);
+ aPalette[0] = Color(0xff, 0xff, 0xff);
+ aPalette[1] = Color(0, 0, 0);
+ }
+
+ // conditionally read source rectangle:
+ if ( pSrcRect != nullptr)
+ {
+ sal_uInt16 nTop, nLeft, nBottom, nRight;
+ pPict->ReadUInt16( nTop ).ReadUInt16( nLeft ).ReadUInt16( nBottom ).ReadUInt16( nRight );
+ *pSrcRect = tools::Rectangle(nLeft, nTop, nRight, nBottom);
+ nDataSize += 8;
+ }
+
+ // conditionally read destination rectangle:
+ if ( pDestRect != nullptr )
+ {
+ Point aTL = ReadPoint();
+ Point aBR = ReadPoint();
+ *pDestRect = tools::Rectangle( aTL, aBR );
+ nDataSize += 8;
+ }
+
+ // conditionally read mode (or skip it):
+ if ( bMode )
+ {
+ pPict->SeekRel(2);
+ nDataSize += 2;
+ }
+
+ // conditionally read region (or skip it):
+ if ( bMaskRgn )
+ {
+ sal_uInt16 nSize(0);
+ pPict->ReadUInt16( nSize );
+ pPict->SeekRel( nSize - 2 );
+ nDataSize += nSize;
+ }
+
+ // read and write Bitmap bits:
+ if ( nPixelSize == 1 || nPixelSize == 2 || nPixelSize == 4 || nPixelSize == 8 )
+ {
+ sal_uInt16 nSrcBPL, nDestBPL;
+ size_t nCount;
+
+ if ( nPixelSize == 1 ) nSrcBPL = ( nWidth + 7 ) >> 3;
+ else if ( nPixelSize == 2 ) nSrcBPL = ( nWidth + 3 ) >> 2;
+ else if ( nPixelSize == 4 ) nSrcBPL = ( nWidth + 1 ) >> 1;
+ else nSrcBPL = nWidth;
+ nDestBPL = ( nSrcBPL + 3 ) & 0xfffc;
+ if (!nRowBytes || nRowBytes < nSrcBPL || nRowBytes > nDestBPL)
+ return 0xffffffff;
+
+ if (nRowBytes < 8 || nPackType == 1)
+ {
+ if (nHeight > pPict->remainingSize() / (sizeof(sal_uInt8) * nRowBytes))
+ return 0xffffffff;
+ }
+ else
+ {
+ size_t nByteCountSize = nRowBytes > 250 ? sizeof(sal_uInt16) : sizeof(sal_uInt8);
+ if (nHeight > pPict->remainingSize() / nByteCountSize)
+ return 0xffffffff;
+ }
+
+ pBitmap.reset(new vcl::bitmap::RawBitmap( Size(nWidth, nHeight), 24 ));
+
+ for (sal_uInt16 ny = 0; ny < nHeight; ++ny)
+ {
+ sal_uInt16 nx = 0;
+ if ( nRowBytes < 8 || nPackType == 1 )
+ {
+ for (size_t i = 0; i < nRowBytes; ++i)
+ {
+ pPict->ReadUChar( nDat );
+ if ( nx < nWidth )
+ SetByte(nx, ny, *pBitmap, nPixelSize, nDat, nWidth, aPalette);
+ }
+ nDataSize += nRowBytes;
+ }
+ else
+ {
+ sal_uInt16 nByteCount(0);
+ if ( nRowBytes > 250 )
+ {
+ pPict->ReadUInt16( nByteCount );
+ nDataSize += 2 + static_cast<sal_uInt32>(nByteCount);
+ }
+ else
+ {
+ sal_uInt8 nByteCountAsByte(0);
+ pPict->ReadUChar( nByteCountAsByte );
+ nByteCount = static_cast<sal_uInt16>(nByteCountAsByte) & 0x00ff;
+ nDataSize += 1 + nByteCount;
+ }
+
+ while (pPict->good() && nByteCount)
+ {
+ sal_uInt8 nFlagCounterByte(0);
+ pPict->ReadUChar(nFlagCounterByte);
+ if ( ( nFlagCounterByte & 0x80 ) == 0 )
+ {
+ nCount = static_cast<sal_uInt16>(nFlagCounterByte) + 1;
+ for (size_t i = 0; i < nCount; ++i)
+ {
+ pPict->ReadUChar( nDat );
+ if ( nx < nWidth )
+ SetByte(nx, ny, *pBitmap, nPixelSize, nDat, nWidth, aPalette);
+ }
+ nByteCount -= 1 + nCount;
+ }
+ else
+ {
+ nCount = static_cast<sal_uInt16>( 1 - sal_Int16( static_cast<sal_uInt16>(nFlagCounterByte) | 0xff00 ) );
+ pPict->ReadUChar( nDat );
+ for (size_t i = 0; i < nCount; ++i)
+ {
+ if ( nx < nWidth )
+ SetByte(nx, ny, *pBitmap, nPixelSize, nDat, nWidth, aPalette);
+ }
+ nByteCount -= 2;
+ }
+ }
+ }
+ }
+ }
+ else if ( nPixelSize == 16 )
+ {
+ sal_uInt8 nByteCountAsByte, nFlagCounterByte;
+ sal_uInt16 nByteCount, nCount, nD;
+ sal_uInt64 nSrcBitsPos;
+
+ if (nWidth > nRowBytes / 2)
+ return 0xffffffff;
+
+ if (nRowBytes < 8 || nPackType == 1)
+ {
+ if (nHeight > pPict->remainingSize() / (sizeof(sal_uInt8) * nRowBytes))
+ return 0xffffffff;
+ }
+ else
+ {
+ size_t nByteCountSize = nRowBytes > 250 ? sizeof(sal_uInt16) : sizeof(sal_uInt8);
+ if (nHeight > pPict->remainingSize() / nByteCountSize)
+ return 0xffffffff;
+ }
+
+ pBitmap.reset(new vcl::bitmap::RawBitmap( Size(nWidth, nHeight), 24 ));
+
+ for (sal_uInt16 ny = 0; ny < nHeight; ++ny)
+ {
+ sal_uInt16 nx = 0;
+ if ( nRowBytes < 8 || nPackType == 1 )
+ {
+ for (size_t i = 0; i < nWidth; ++i)
+ {
+ pPict->ReadUInt16( nD );
+ nRed = static_cast<sal_uInt8>( nD >> 7 );
+ nGreen = static_cast<sal_uInt8>( nD >> 2 );
+ nBlue = static_cast<sal_uInt8>( nD << 3 );
+ pBitmap->SetPixel(ny, nx++, Color(nRed, nGreen, nBlue));
+ }
+ nDataSize += static_cast<sal_uInt32>(nWidth) * 2;
+ }
+ else
+ {
+ nSrcBitsPos = pPict->Tell();
+ if ( nRowBytes > 250 )
+ {
+ pPict->ReadUInt16( nByteCount );
+ nByteCount += 2;
+ }
+ else
+ {
+ pPict->ReadUChar( nByteCountAsByte );
+ nByteCount = static_cast<sal_uInt16>(nByteCountAsByte) & 0x00ff;
+ nByteCount++;
+ }
+ while ( nx != nWidth )
+ {
+ pPict->ReadUChar( nFlagCounterByte );
+ if ( (nFlagCounterByte & 0x80) == 0)
+ {
+ nCount=static_cast<sal_uInt16>(nFlagCounterByte)+1;
+ if ( nCount + nx > nWidth)
+ nCount = nWidth - nx;
+ if (pPict->remainingSize() < sizeof(sal_uInt16) * nCount)
+ return 0xffffffff;
+ /* SJ: the RLE decoding seems not to be correct here,
+ I don't want to change this until I have a bugdoc for
+ this case. Have a look at 32bit, there I changed the
+ encoding, so that it is used a straight forward array
+ */
+ for (size_t i = 0; i < nCount; ++i)
+ {
+ pPict->ReadUInt16( nD );
+ nRed = static_cast<sal_uInt8>( nD >> 7 );
+ nGreen = static_cast<sal_uInt8>( nD >> 2 );
+ nBlue = static_cast<sal_uInt8>( nD << 3 );
+ pBitmap->SetPixel(ny, nx++, Color(nRed, nGreen, nBlue));
+ }
+ }
+ else
+ {
+ if (pPict->remainingSize() < sizeof(sal_uInt16))
+ return 0xffffffff;
+ nCount=(1-sal_Int16(static_cast<sal_uInt16>(nFlagCounterByte)|0xff00));
+ if ( nCount + nx > nWidth )
+ nCount = nWidth - nx;
+ pPict->ReadUInt16( nD );
+ nRed = static_cast<sal_uInt8>( nD >> 7 );
+ nGreen = static_cast<sal_uInt8>( nD >> 2 );
+ nBlue = static_cast<sal_uInt8>( nD << 3 );
+ for (size_t i = 0; i < nCount; ++i)
+ {
+ pBitmap->SetPixel(ny, nx++, Color(nRed, nGreen, nBlue));
+ }
+ }
+ }
+ nDataSize += nByteCount;
+ pPict->Seek(nSrcBitsPos+nByteCount);
+ }
+ }
+ }
+ else if ( nPixelSize == 32 )
+ {
+ sal_uInt16 nByteCount;
+ size_t nCount;
+ sal_uInt64 nSrcBitsPos;
+ if ( nRowBytes != 4*nWidth )
+ return 0xffffffff;
+
+ if ( nRowBytes < 8 || nPackType == 1 )
+ {
+ const size_t nMaxPixels = pPict->remainingSize() / 4;
+ const size_t nMaxRows = nMaxPixels / nWidth;
+ if (nHeight > nMaxRows)
+ return 0xffffffff;
+ const size_t nMaxCols = nMaxPixels / nHeight;
+ if (nWidth > nMaxCols)
+ return 0xffffffff;
+
+ pBitmap.reset(new vcl::bitmap::RawBitmap( Size(nWidth, nHeight), 24 ));
+
+ for (sal_uInt16 ny = 0; ny < nHeight; ++ny)
+ {
+ for (sal_uInt16 nx = 0; nx < nWidth; ++nx)
+ {
+ sal_uInt8 nDummy;
+ pPict->ReadUChar( nDummy ).ReadUChar( nRed ).ReadUChar( nGreen ).ReadUChar( nBlue );
+ pBitmap->SetPixel(ny, nx, Color(nRed, nGreen, nBlue));
+ }
+ nDataSize += static_cast<sal_uInt32>(nWidth) * 4;
+ }
+ }
+ else if ( nPackType == 2 )
+ {
+ const size_t nMaxPixels = pPict->remainingSize() / 3;
+ const size_t nMaxRows = nMaxPixels / nWidth;
+ if (nHeight > nMaxRows)
+ return 0xffffffff;
+ const size_t nMaxCols = nMaxPixels / nHeight;
+ if (nWidth > nMaxCols)
+ return 0xffffffff;
+
+ pBitmap.reset(new vcl::bitmap::RawBitmap( Size(nWidth, nHeight), 24 ));
+
+ for (sal_uInt16 ny = 0; ny < nHeight; ++ny)
+ {
+ for (sal_uInt16 nx = 0; nx < nWidth; ++nx)
+ {
+ pPict->ReadUChar( nRed ).ReadUChar( nGreen ).ReadUChar( nBlue );
+ pBitmap->SetPixel(ny, nx, Color(nRed, nGreen, nBlue));
+ }
+ nDataSize += static_cast<sal_uInt32>(nWidth) * 3;
+ }
+ }
+ else
+ {
+ sal_uInt8 nByteCountAsByte;
+ sal_uInt8 nFlagCounterByte;
+ if ( ( nCmpCount == 3 ) || ( nCmpCount == 4 ) )
+ {
+ size_t nByteCountSize = nRowBytes > 250 ? sizeof(sal_uInt16) : sizeof(sal_uInt8);
+ if (nHeight > pPict->remainingSize() / nByteCountSize)
+ return 0xffffffff;
+
+ pBitmap.reset(new vcl::bitmap::RawBitmap( Size(nWidth, nHeight), 24 ));
+
+ // cid#1458434 to sanitize Untrusted loop bound
+ nWidth = pBitmap->Width();
+
+ size_t nByteWidth = static_cast<size_t>(nWidth) * nCmpCount;
+ std::vector<sal_uInt8> aScanline(nByteWidth);
+ for (sal_uInt16 ny = 0; ny < nHeight; ++ny)
+ {
+ nSrcBitsPos = pPict->Tell();
+ if ( nRowBytes > 250 )
+ {
+ pPict->ReadUInt16( nByteCount );
+ nByteCount += 2;
+ }
+ else
+ {
+ pPict->ReadUChar( nByteCountAsByte );
+ nByteCount = nByteCountAsByte;
+ nByteCount++;
+ }
+ size_t i = 0;
+ while (i < aScanline.size())
+ {
+ pPict->ReadUChar( nFlagCounterByte );
+ if ( ( nFlagCounterByte & 0x80 ) == 0)
+ {
+ nCount = static_cast<sal_uInt16>(nFlagCounterByte) + 1;
+ if ((i + nCount) > aScanline.size())
+ nCount = aScanline.size() - i;
+ if (pPict->remainingSize() < nCount)
+ return 0xffffffff;
+ while( nCount-- )
+ {
+ pPict->ReadUChar( nDat );
+ aScanline[ i++ ] = nDat;
+ }
+ }
+ else
+ {
+ if (pPict->remainingSize() < 1)
+ return 0xffffffff;
+ nCount = ( 1 - sal_Int16( static_cast<sal_uInt16>(nFlagCounterByte) | 0xff00 ) );
+ if (( i + nCount) > aScanline.size())
+ nCount = aScanline.size() - i;
+ pPict->ReadUChar( nDat );
+ while( nCount-- )
+ aScanline[ i++ ] = nDat;
+ }
+ }
+ sal_uInt8* pTmp = aScanline.data();
+ if ( nCmpCount == 4 )
+ pTmp += nWidth;
+ for (sal_uInt16 nx = 0; nx < nWidth; pTmp++)
+ pBitmap->SetPixel(ny, nx++, Color(*pTmp, pTmp[ nWidth ], pTmp[ 2 * nWidth ]));
+ nDataSize += nByteCount;
+ pPict->Seek( nSrcBitsPos + nByteCount );
+ }
+ }
+ }
+ }
+ else
+ return 0xffffffff;
+ rBitmap = vcl::bitmap::CreateFromData(std::move(*pBitmap));
+ return nDataSize;
+}
+
+void PictReader::ReadHeader()
+{
+ short y1,x1,y2,x2;
+
+ char sBuf[ 2 ];
+ // previous code considers pPict->Tell() as the normal starting position,
+ // nStartPos can be != 0 f.e. a pict embedded in a microsoft word document
+ sal_uInt64 nStartPos = pPict->Tell();
+ // Standard:
+ // a picture file begins by 512 bytes (reserved to the application) followed by the picture data
+ // while clipboard, pictures stored in a document often contain only the picture data.
+
+ // Special cases:
+ // - some Pict v.1 use 0x00 0x11 0x01 ( instead of 0x11 0x01) to store the version op
+ // (we consider here this as another standard for Pict. v.1 )
+ // - some files seem to contain extra garbage data at the beginning
+ // - some picture data seem to contain extra NOP opcode(0x00) between the bounding box and the version opcode
+
+ // This code looks hard to find a picture header, ie. it looks at positions
+ // - nStartPos+0, nStartPos+512 with potential extra NOP codes between bdbox and version (at most 9 extra NOP)
+ // - 512..1024 with more strict bdbox checking and no extra NOP codes
+
+ // Notes:
+ // - if the header can begin at nStartPos+0 and at nStartPos+512, we try to choose the more
+ // <<probable>> ( using the variable confidence)
+ // - svtools/source/filter.vcl/filter/{filter.cxx,filter2.cxx} only check for standard Pict,
+ // this may cause future problems
+ int st;
+ sal_uInt32 nOffset;
+ int confidence[2] = { 0, 0};
+ for ( st = 0; st < 3 + 513; st++ )
+ {
+ int actualConfid = 20; // the actual confidence
+ pPict->ResetError();
+ if (st < 2) nOffset = nStartPos+st*512;
+ else if (st == 2) {
+ // choose nStartPos+0 or nStartPos+512 even if there are a little dubious
+ int actPos = -1, actConf=0;
+ if (confidence[0] > 0) { actPos = 0; actConf = confidence[0]; }
+ if (confidence[1] > 0 && confidence[1] >= actConf) actPos = 1;
+ if (actPos < 0) continue;
+ nOffset = nStartPos+actPos*512;
+ }
+ else {
+ nOffset = nStartPos+509+st;
+ // a small test to check if versionOp code exists after the bdbox ( with no extra NOP codes)
+ pPict->Seek(nOffset+10);
+ pPict->ReadBytes(sBuf, 2);
+ if (!pPict->good()) break;
+ if (sBuf[0] == 0x11 || (sBuf[0] == 0x00 && sBuf[1] == 0x11)) ; // maybe ok
+ else continue;
+ }
+ pPict->Seek(nOffset);
+
+ // 2 bytes to store size ( version 1 ) ignored
+ pPict->SeekRel( 2 );
+ pPict->ReadInt16( y1 ).ReadInt16( x1 ).ReadInt16( y2 ).ReadInt16( x2 ); // frame rectangle of the picture
+ if (x1 > x2 || y1 > y2) continue; // bad bdbox
+ if (x1 < -2048 || x2 > 2048 || y1 < -2048 || y2 > 2048 || // origin|dest is very small|large
+ (x1 == x2 && y1 == y2) ) // 1 pixel pict is dubious
+ actualConfid-=3;
+ else if (x2 < x1+8 || y2 < y1+8) // a little dubious
+ actualConfid-=1;
+ if (st >= 3 && actualConfid != 20) continue;
+ aBoundingRect=tools::Rectangle( x1,y1, x2, y2 );
+
+ if (!pPict->good()) continue;
+ // read version
+ pPict->ReadBytes(sBuf, 2);
+ // version 1 file
+ if ( sBuf[ 0 ] == 0x11 && sBuf[ 1 ] == 0x01 ) {
+ // pict v1 must be rare and we do only few tests
+ if (st < 2) { confidence[st] = --actualConfid; continue; }
+ IsVersion2 = false; return;
+ }
+ if (sBuf[0] != 0x00) continue; // unrecoverable error
+ int numZero = 0;
+ do
+ {
+ numZero++;
+ pPict->SeekRel(-1);
+ pPict->ReadBytes(sBuf, 2);
+ }
+ while ( sBuf[0] == 0x00 && numZero < 10);
+ actualConfid -= (numZero-1); // extra nop are dubious
+ if (!pPict->good()) continue;
+ if (sBuf[0] != 0x11) continue; // not a version opcode
+ // abnormal version 1 file
+ if (sBuf[1] == 0x01 ) {
+ // pict v1 must be rare and we do only few tests
+ if (st < 2) { confidence[st] = --actualConfid; continue; }
+ IsVersion2 = false; return;
+ }
+ if (sBuf[1] != 0x02 ) continue; // not a version 2 file
+
+ IsVersion2=true;
+ short nExtVer, nReserved;
+ // 3 Bytes ignored : end of version arg 0x02FF (ie: 0xFF), HeaderOp : 0x0C00
+ pPict->SeekRel( 3 );
+ pPict->ReadInt16( nExtVer ).ReadInt16( nReserved );
+ if (!pPict->good()) continue;
+
+ if ( nExtVer == -2 ) // extended version 2 picture
+ {
+ sal_Int32 nHResFixed, nVResFixed;
+ pPict->ReadInt32( nHResFixed ).ReadInt32( nVResFixed );
+ pPict->ReadInt16( y1 ).ReadInt16( x1 ).ReadInt16( y2 ).ReadInt16( x2 ); // reading the optimal bounding rect
+ if (x1 > x2 || y1 > y2) continue; // bad bdbox
+ if (st < 2 && actualConfid != 20) { confidence[st] = actualConfid; continue; }
+
+ double fHRes = nHResFixed;
+ fHRes /= 65536;
+ double fVRes = nVResFixed;
+ fVRes /= 65536;
+ aHRes /= fHRes;
+ aVRes /= fVRes;
+ aBoundingRect=tools::Rectangle( x1,y1, x2, y2 );
+ pPict->SeekRel( 4 ); // 4 bytes reserved
+ return;
+ }
+ else if (nExtVer == -1 ) { // basic version 2 picture
+ if (st < 2 && actualConfid != 20) { confidence[st] = actualConfid; continue; }
+ pPict->SeekRel( 16); // bdbox(4 fixed number)
+ pPict->SeekRel(4); // 4 bytes reserved
+ return;
+ }
+ }
+ pPict->SetError(SVSTREAM_FILEFORMAT_ERROR);
+}
+
+#if OSL_DEBUG_LEVEL > 0
+static const char* operationName(sal_uInt16 nOpcode)
+{
+ // add here whatever makes the debugging easier for you, otherwise you'll
+ // see only the operation's opcode
+ switch (nOpcode)
+ {
+ case 0x0001: return "Clip";
+ case 0x0003: return "TxFont";
+ case 0x0004: return "TxFace";
+ case 0x0008: return "PnMode";
+ case 0x0009: return "PnPat";
+ case 0x000d: return "TxSize";
+ case 0x001a: return "RGBFgCol";
+ case 0x001d: return "HiliteColor";
+ case 0x0020: return "Line";
+ case 0x0022: return "ShortLine";
+ case 0x0028: return "LongText";
+ case 0x0029: return "DHText";
+ case 0x002a: return "DVText";
+ case 0x002c: return "fontName";
+ case 0x002e: return "glyphState";
+ case 0x0031: return "paintRect";
+ case 0x0038: return "frameSameRect";
+ case 0x0070: return "framePoly";
+ case 0x0071: return "paintPoly";
+ case 0x00a1: return "LongComment";
+ default: return "?";
+ }
+}
+#endif
+
+sal_uInt64 PictReader::ReadData(sal_uInt16 nOpcode)
+{
+ Point aPoint;
+ sal_uInt64 nDataSize=0;
+ PictDrawingMethod shapeDMethod = PictDrawingMethod::UNDEFINED;
+ switch (nOpcode & 7) {
+ case 0: shapeDMethod = PictDrawingMethod::FRAME; break;
+ case 1: shapeDMethod = PictDrawingMethod::PAINT; break;
+ case 2: shapeDMethod = PictDrawingMethod::ERASE; break;
+ case 3: shapeDMethod = PictDrawingMethod::INVERT; break;
+ case 4: shapeDMethod = PictDrawingMethod::FILL; break;
+ default: break;
+ }
+
+#if OSL_DEBUG_LEVEL > 0
+ SAL_INFO("filter.pict", "Operation: 0x" << OUString::number(nOpcode, 16) << " [" << operationName(nOpcode) << "]");
+#endif
+
+ switch(nOpcode) {
+
+ case 0x0000: // NOP
+ nDataSize=0;
+ break;
+
+ case 0x0001: { // Clip
+ sal_uInt16 nUSHORT(0);
+ tools::Rectangle aRect;
+ pPict->ReadUInt16( nUSHORT );
+ nDataSize=nUSHORT;
+ ReadRectangle(aRect);
+ // checkme: do we really want to extend the rectangle here ?
+ // I do that because the clipping is often used to clean a region,
+ // before drawing some text and also to draw this text.
+ // So using a too small region can lead to clip the end of the text ;
+ // but this can be discussable...
+ aRect.setWidth(aRect.getWidth()+1);
+ aRect.setHeight(aRect.getHeight()+1);
+ pVirDev->SetClipRegion( vcl::Region( aRect ) );
+ break;
+ }
+ case 0x0002: // BkPat
+ nDataSize = eActBackPattern.read(*pPict);
+ eActMethod = PictDrawingMethod::UNDEFINED;
+ break;
+
+ case 0x0003: // TxFont
+ {
+ sal_uInt16 nUSHORT(0);
+ pPict->ReadUInt16( nUSHORT );
+ if (nUSHORT <= 1) aActFont.SetFamily(FAMILY_SWISS);
+ else if (nUSHORT <= 12) aActFont.SetFamily(FAMILY_DECORATIVE);
+ else if (nUSHORT <= 20) aActFont.SetFamily(FAMILY_ROMAN);
+ else if (nUSHORT == 21) aActFont.SetFamily(FAMILY_SWISS);
+ else if (nUSHORT == 22) aActFont.SetFamily(FAMILY_MODERN);
+ else if (nUSHORT <= 1023) aActFont.SetFamily(FAMILY_SWISS);
+ else aActFont.SetFamily(FAMILY_ROMAN);
+ aActFont.SetCharSet(GetTextEncoding(nUSHORT));
+ eActMethod = PictDrawingMethod::UNDEFINED;
+ nDataSize=2;
+ break;
+ }
+ case 0x0004: { // TxFace
+ char nFace(0);
+ pPict->ReadChar( nFace );
+ if ( (nFace & 0x01)!=0 ) aActFont.SetWeight(WEIGHT_BOLD);
+ else aActFont.SetWeight(WEIGHT_NORMAL);
+ if ( (nFace & 0x02)!=0 ) aActFont.SetItalic(ITALIC_NORMAL);
+ else aActFont.SetItalic(ITALIC_NONE);
+ if ( (nFace & 0x04)!=0 ) aActFont.SetUnderline(LINESTYLE_SINGLE);
+ else aActFont.SetUnderline(LINESTYLE_NONE);
+ if ( (nFace & 0x08)!=0 ) aActFont.SetOutline(true);
+ else aActFont.SetOutline(false);
+ if ( (nFace & 0x10)!=0 ) aActFont.SetShadow(true);
+ else aActFont.SetShadow(false);
+ eActMethod = PictDrawingMethod::UNDEFINED;
+ nDataSize=1;
+ break;
+ }
+ case 0x0005: // TxMode
+ nDataSize=2;
+ break;
+
+ case 0x0006: // SpExtra
+ nDataSize=4;
+ break;
+
+ case 0x0007: { // PnSize
+ nActPenSize=ReadSize();
+ eActMethod = PictDrawingMethod::UNDEFINED;
+ nDataSize=4;
+ break;
+ }
+ case 0x0008: // PnMode
+ {
+ sal_uInt16 nUSHORT(0);
+ pPict->ReadUInt16( nUSHORT );
+ // internal code for postscript command (Quickdraw Reference Drawing B-30,B-34)
+ if (nUSHORT==23) eActROP = RasterOp::N1;
+ else {
+ switch (nUSHORT & 0x0007) {
+ case 0: eActROP=RasterOp::OverPaint; break; // Copy
+ case 1: eActROP=RasterOp::OverPaint; break; // Or
+ case 2: eActROP=RasterOp::Xor; break; // Xor
+ case 3: eActROP=RasterOp::OverPaint; break; // Bic
+ case 4: eActROP=RasterOp::Invert; break; // notCopy
+ case 5: eActROP=RasterOp::OverPaint; break; // notOr
+ case 6: eActROP=RasterOp::Xor; break; // notXor
+ case 7: eActROP=RasterOp::OverPaint; break; // notBic
+ }
+ }
+ eActMethod = PictDrawingMethod::UNDEFINED;
+ nDataSize=2;
+ break;
+ }
+ case 0x0009: // PnPat
+ nDataSize=eActPenPattern.read(*pPict);
+ eActMethod = PictDrawingMethod::UNDEFINED;
+ break;
+
+ case 0x000a: // FillPat
+ nDataSize=eActFillPattern.read(*pPict);
+ eActMethod = PictDrawingMethod::UNDEFINED;
+ break;
+
+ case 0x000b: // OvSize
+ aActOvalSize=ReadSize();
+ nDataSize=4;
+ break;
+
+ case 0x000c: // Origin
+ nDataSize=4;
+ break;
+
+ case 0x000d: // TxSize
+ {
+ sal_uInt16 nUSHORT(0);
+ pPict->ReadUInt16( nUSHORT );
+ aActFont.SetFontSize( Size( 0, static_cast<tools::Long>(nUSHORT) ) );
+ eActMethod = PictDrawingMethod::UNDEFINED;
+ nDataSize=2;
+ }
+ break;
+
+ case 0x000e: // FgColor
+ aActForeColor=ReadColor();
+ eActMethod = PictDrawingMethod::UNDEFINED;
+ nDataSize=4;
+ break;
+
+ case 0x000f: // BkColor
+ aActBackColor=ReadColor();
+ nDataSize=4;
+ break;
+
+ case 0x0010: // TxRatio
+ nDataSize=8;
+ break;
+
+ case 0x0011: // VersionOp
+ nDataSize=1;
+ break;
+
+ case 0x0012: // BkPixPat
+ nDataSize=ReadPixPattern(eActBackPattern);
+ eActMethod = PictDrawingMethod::UNDEFINED;
+ break;
+
+ case 0x0013: // PnPixPat
+ nDataSize=ReadPixPattern(eActPenPattern);
+ eActMethod = PictDrawingMethod::UNDEFINED;
+ break;
+
+ case 0x0014: // FillPixPat
+ nDataSize=ReadPixPattern(eActFillPattern);
+ eActMethod = PictDrawingMethod::UNDEFINED;
+ break;
+
+ case 0x0015: // PnLocHFrac
+ nDataSize=2;
+ break;
+
+ case 0x0016: // ChExtra
+ nDataSize=2;
+ break;
+
+ case 0x0017: // Reserved (0 Bytes)
+ case 0x0018: // Reserved (0 Bytes)
+ case 0x0019: // Reserved (0 Bytes)
+ nDataSize=0;
+ break;
+
+ case 0x001a: // RGBFgCol
+ aActForeColor=ReadRGBColor();
+ eActMethod = PictDrawingMethod::UNDEFINED;
+ nDataSize=6;
+ break;
+
+ case 0x001b: // RGBBkCol
+ aActBackColor=ReadRGBColor();
+ eActMethod = PictDrawingMethod::UNDEFINED;
+ nDataSize=6;
+ break;
+
+ case 0x001c: // HiliteMode
+ nDataSize=0;
+ break;
+
+ case 0x001d: // HiliteColor
+ nDataSize=6;
+ break;
+
+ case 0x001e: // DefHilite
+ nDataSize=0;
+ break;
+
+ case 0x001f: // OpColor
+ nDataSize=6;
+ break;
+
+ case 0x0020: // Line
+ aPoint=ReadPoint(); aPenPosition=ReadPoint();
+ nDataSize=8;
+
+ if (!pPict->good())
+ break;
+
+ if (IsInvisible( PictDrawingMethod::FRAME )) break;
+ DrawingMethod( PictDrawingMethod::FRAME );
+ PictReaderShape::drawLine(pVirDev, aPoint,aPenPosition, nActPenSize);
+ break;
+
+ case 0x0021: // LineFrom
+ aPoint=aPenPosition; aPenPosition=ReadPoint();
+ nDataSize=4;
+
+ if (!pPict->good())
+ break;
+
+ if (IsInvisible( PictDrawingMethod::FRAME )) break;
+ DrawingMethod( PictDrawingMethod::FRAME );
+ PictReaderShape::drawLine(pVirDev, aPoint,aPenPosition, nActPenSize);
+ break;
+
+ case 0x0022: // ShortLine
+ aPoint=ReadPoint();
+ aPenPosition=ReadDeltaH(aPoint);
+ aPenPosition=ReadDeltaV(aPenPosition);
+ nDataSize=6;
+
+ if (!pPict->good())
+ break;
+
+ if ( IsInvisible(PictDrawingMethod::FRAME) ) break;
+ DrawingMethod( PictDrawingMethod::FRAME );
+ PictReaderShape::drawLine(pVirDev, aPoint,aPenPosition, nActPenSize);
+ break;
+
+ case 0x0023: // ShortLineFrom
+ aPoint=aPenPosition;
+ aPenPosition=ReadDeltaH(aPoint);
+ aPenPosition=ReadDeltaV(aPenPosition);
+ nDataSize=2;
+
+ if (!pPict->good())
+ break;
+
+ if (IsInvisible( PictDrawingMethod::FRAME )) break;
+ DrawingMethod( PictDrawingMethod::FRAME );
+ PictReaderShape::drawLine(pVirDev, aPoint,aPenPosition, nActPenSize);
+ break;
+
+ case 0x0024: // Reserved (n Bytes)
+ case 0x0025: // Reserved (n Bytes)
+ case 0x0026: // Reserved (n Bytes)
+ case 0x0027: // Reserved (n Bytes)
+ {
+ sal_uInt16 nUSHORT(0);
+ pPict->ReadUInt16( nUSHORT );
+ nDataSize=2+nUSHORT;
+ break;
+ }
+ case 0x0028: // LongText
+ aTextPosition=ReadPoint();
+ nDataSize=4+ReadAndDrawText();
+ break;
+
+ case 0x0029: // DHText
+ aTextPosition=ReadUnsignedDeltaH(aTextPosition);
+ nDataSize=1+ReadAndDrawText();
+ break;
+
+ case 0x002a: // DVText
+ aTextPosition=ReadUnsignedDeltaV(aTextPosition);
+ nDataSize=1+ReadAndDrawText();
+ break;
+
+ case 0x002b: // DHDVText
+ aTextPosition=ReadUnsignedDeltaH(aTextPosition);
+ aTextPosition=ReadUnsignedDeltaV(aTextPosition);
+ nDataSize=2+ReadAndDrawText();
+ break;
+
+ case 0x002c: { // fontName
+ sal_uInt16 nUSHORT(0);
+ pPict->ReadUInt16( nUSHORT ); nDataSize=nUSHORT+2;
+ pPict->ReadUInt16( nUSHORT );
+ if (nUSHORT <= 1) aActFont.SetFamily(FAMILY_SWISS);
+ else if (nUSHORT <= 12) aActFont.SetFamily(FAMILY_DECORATIVE);
+ else if (nUSHORT <= 20) aActFont.SetFamily(FAMILY_ROMAN);
+ else if (nUSHORT == 21) aActFont.SetFamily(FAMILY_SWISS);
+ else if (nUSHORT == 22) aActFont.SetFamily(FAMILY_MODERN);
+ else if (nUSHORT <= 1023) aActFont.SetFamily(FAMILY_SWISS);
+ else aActFont.SetFamily(FAMILY_ROMAN);
+ aActFont.SetCharSet(GetTextEncoding(nUSHORT));
+ char nByteLen(0);
+ pPict->ReadChar( nByteLen );
+ sal_uInt16 nLen = static_cast<sal_uInt16>(nByteLen)&0x00ff;
+ char sFName[ 256 ];
+ sFName[pPict->ReadBytes(sFName, nLen)] = 0;
+ OUString aString( sFName, strlen(sFName), osl_getThreadTextEncoding() );
+ aActFont.SetFamilyName( aString );
+ eActMethod = PictDrawingMethod::UNDEFINED;
+ break;
+ }
+ case 0x002d: // lineJustify
+ nDataSize=10;
+ break;
+
+ case 0x002e: // glyphState
+ {
+ sal_uInt16 nUSHORT(0);
+ pPict->ReadUInt16( nUSHORT );
+ nDataSize=2+nUSHORT;
+ break;
+ }
+ case 0x002f: // Reserved (n Bytes)
+ {
+ sal_uInt16 nUSHORT(0);
+ pPict->ReadUInt16( nUSHORT );
+ nDataSize=2+nUSHORT;
+ break;
+ }
+ case 0x0030: // frameRect
+ case 0x0031: // paintRect
+ case 0x0032: // eraseRect
+ case 0x0033: // invertRect
+ case 0x0034: // fillRect
+ nDataSize=ReadAndDrawRect(shapeDMethod);
+ break;
+
+ case 0x0035: // Reserved (8 Bytes)
+ case 0x0036: // Reserved (8 Bytes)
+ case 0x0037: // Reserved (8 Bytes)
+ nDataSize=8;
+ break;
+
+ case 0x0038: // frameSameRect
+ case 0x0039: // paintSameRect
+ case 0x003a: // eraseSameRect
+ case 0x003b: // invertSameRect
+ case 0x003c: // fillSameRect
+ nDataSize=ReadAndDrawSameRect(shapeDMethod);
+ break;
+
+ case 0x003d: // Reserved (0 Bytes)
+ case 0x003e: // Reserved (0 Bytes)
+ case 0x003f: // Reserved (0 Bytes)
+ nDataSize=0;
+ break;
+
+ case 0x0040: // frameRRect
+ case 0x0041: // paintRRect
+ case 0x0042: // eraseRRect
+ case 0x0043: // invertRRect
+ case 0x0044: // fillRRect
+ nDataSize=ReadAndDrawRoundRect(shapeDMethod);
+ break;
+
+ case 0x0045: // Reserved (8 Bytes)
+ case 0x0046: // Reserved (8 Bytes)
+ case 0x0047: // Reserved (8 Bytes)
+ nDataSize=8;
+ break;
+
+ case 0x0048: // frameSameRRect
+ case 0x0049: // paintSameRRect
+ case 0x004a: // eraseSameRRect
+ case 0x004b: // invertSameRRect
+ case 0x004c: // fillSameRRect
+ nDataSize=ReadAndDrawSameRoundRect(shapeDMethod);
+ break;
+
+ case 0x004d: // Reserved (0 Bytes)
+ case 0x004e: // Reserved (0 Bytes)
+ case 0x004f: // Reserved (0 Bytes)
+ nDataSize=0;
+ break;
+
+ case 0x0050: // frameOval
+ case 0x0051: // paintOval
+ case 0x0052: // eraseOval
+ case 0x0053: // invertOval
+ case 0x0054: // fillOval
+ nDataSize=ReadAndDrawOval(shapeDMethod);
+ break;
+
+ case 0x0055: // Reserved (8 Bytes)
+ case 0x0056: // Reserved (8 Bytes)
+ case 0x0057: // Reserved (8 Bytes)
+ nDataSize=8;
+ break;
+
+ case 0x0058: // frameSameOval
+ case 0x0059: // paintSameOval
+ case 0x005a: // eraseSameOval
+ case 0x005b: // invertSameOval
+ case 0x005c: // fillSameOval
+ nDataSize=ReadAndDrawSameOval(shapeDMethod);
+ break;
+
+ case 0x005d: // Reserved (0 Bytes)
+ case 0x005e: // Reserved (0 Bytes)
+ case 0x005f: // Reserved (0 Bytes)
+ nDataSize=0;
+ break;
+
+ case 0x0060: // frameArc
+ case 0x0061: // paintArc
+ case 0x0062: // eraseArc
+ case 0x0063: // invertArc
+ case 0x0064: // fillArc
+ nDataSize=ReadAndDrawArc(shapeDMethod);
+ break;
+
+ case 0x0065: // Reserved (12 Bytes)
+ case 0x0066: // Reserved (12 Bytes)
+ case 0x0067: // Reserved (12 Bytes)
+ nDataSize=12;
+ break;
+
+ case 0x0068: // frameSameArc
+ case 0x0069: // paintSameArc
+ case 0x006a: // eraseSameArc
+ case 0x006b: // invertSameArc
+ case 0x006c: // fillSameArc
+ nDataSize=ReadAndDrawSameArc(shapeDMethod);
+ break;
+
+ case 0x006d: // Reserved (4 Bytes)
+ case 0x006e: // Reserved (4 Bytes)
+ case 0x006f: // Reserved (4 Bytes)
+ nDataSize=4;
+ break;
+
+ case 0x0070: // framePoly
+ case 0x0071: // paintPoly
+ case 0x0072: // erasePoly
+ case 0x0073: // invertPoly
+ case 0x0074: // fillPoly
+ nDataSize=ReadAndDrawPolygon(shapeDMethod);
+ break;
+
+ case 0x0075: // Reserved (Polygon-Size)
+ case 0x0076: // Reserved (Polygon-Size)
+ case 0x0077: // Reserved (Polygon-Size)
+ {
+ sal_uInt16 nUSHORT(0);
+ pPict->ReadUInt16( nUSHORT ); nDataSize=nUSHORT;
+ break;
+ }
+ case 0x0078: // frameSamePoly
+ case 0x0079: // paintSamePoly
+ case 0x007a: // eraseSamePoly
+ case 0x007b: // invertSamePoly
+ case 0x007c: // fillSamePoly
+ nDataSize=ReadAndDrawSamePolygon(shapeDMethod);
+ break;
+
+ case 0x007d: // Reserved (0 Bytes)
+ case 0x007e: // Reserved (0 Bytes)
+ case 0x007f: // Reserved (0 Bytes)
+ nDataSize=0;
+ break;
+
+ case 0x0080: // frameRgn
+ case 0x0081: // paintRgn
+ case 0x0082: // eraseRgn
+ case 0x0083: // invertRgn
+ case 0x0084: // fillRgn
+ nDataSize=ReadAndDrawRgn(shapeDMethod);
+ break;
+
+ case 0x0085: // Reserved (Region-Size)
+ case 0x0086: // Reserved (Region-Size)
+ case 0x0087: // Reserved (Region-Size)
+ {
+ sal_uInt16 nUSHORT(0);
+ pPict->ReadUInt16( nUSHORT ); nDataSize=nUSHORT;
+ break;
+ }
+ case 0x0088: // frameSameRgn
+ case 0x0089: // paintSameRgn
+ case 0x008a: // eraseSameRgn
+ case 0x008b: // invertSameRgn
+ case 0x008c: // fillSameRgn
+ nDataSize=ReadAndDrawSameRgn(shapeDMethod);
+ break;
+
+ case 0x008d: // Reserved (0 Bytes)
+ case 0x008e: // Reserved (0 Bytes)
+ case 0x008f: // Reserved (0 Bytes)
+ nDataSize=0;
+ break;
+
+ case 0x0090: { // BitsRect
+ BitmapEx aBmp;
+ tools::Rectangle aSrcRect, aDestRect;
+ nDataSize=ReadPixMapEtc(aBmp, false, true, &aSrcRect, &aDestRect, true, false);
+ DrawingMethod( PictDrawingMethod::PAINT );
+ pVirDev->DrawBitmapEx(aDestRect.TopLeft(),aDestRect.GetSize(),aBmp);
+ break;
+ }
+ case 0x0091: { // BitsRgn
+ BitmapEx aBmp;
+ tools::Rectangle aSrcRect, aDestRect;
+ nDataSize=ReadPixMapEtc(aBmp, false, true, &aSrcRect, &aDestRect, true, true);
+ DrawingMethod( PictDrawingMethod::PAINT );
+ pVirDev->DrawBitmapEx(aDestRect.TopLeft(),aDestRect.GetSize(),aBmp);
+ break;
+ }
+ case 0x0092: // Reserved (n Bytes)
+ case 0x0093: // Reserved (n Bytes)
+ case 0x0094: // Reserved (n Bytes)
+ case 0x0095: // Reserved (n Bytes)
+ case 0x0096: // Reserved (n Bytes)
+ case 0x0097: // Reserved (n Bytes)
+ {
+ sal_uInt16 nUSHORT(0);
+ pPict->ReadUInt16( nUSHORT ); nDataSize=2+nUSHORT;
+ break;
+ }
+ case 0x0098: { // PackBitsRect
+ BitmapEx aBmp;
+ tools::Rectangle aSrcRect, aDestRect;
+ nDataSize=ReadPixMapEtc(aBmp, false, true, &aSrcRect, &aDestRect, true, false);
+ DrawingMethod( PictDrawingMethod::PAINT );
+ pVirDev->DrawBitmapEx(aDestRect.TopLeft(),aDestRect.GetSize(),aBmp);
+ break;
+ }
+ case 0x0099: { // PackBitsRgn
+ BitmapEx aBmp;
+ tools::Rectangle aSrcRect, aDestRect;
+ nDataSize=ReadPixMapEtc(aBmp, false, true, &aSrcRect, &aDestRect, true, true);
+ DrawingMethod( PictDrawingMethod::PAINT );
+ pVirDev->DrawBitmapEx(aDestRect.TopLeft(),aDestRect.GetSize(),aBmp);
+ break;
+ }
+ case 0x009a: { // DirectBitsRect
+ BitmapEx aBmp;
+ tools::Rectangle aSrcRect, aDestRect;
+ nDataSize=ReadPixMapEtc(aBmp, true, false, &aSrcRect, &aDestRect, true, false);
+ DrawingMethod( PictDrawingMethod::PAINT );
+ pVirDev->DrawBitmapEx(aDestRect.TopLeft(),aDestRect.GetSize(),aBmp);
+ break;
+ }
+ case 0x009b: { // DirectBitsRgn
+ BitmapEx aBmp;
+ tools::Rectangle aSrcRect, aDestRect;
+ nDataSize=ReadPixMapEtc(aBmp, true, false, &aSrcRect, &aDestRect, true, true);
+ DrawingMethod( PictDrawingMethod::PAINT );
+ pVirDev->DrawBitmapEx(aDestRect.TopLeft(),aDestRect.GetSize(),aBmp);
+ break;
+ }
+ case 0x009c: // Reserved (n Bytes)
+ case 0x009d: // Reserved (n Bytes)
+ case 0x009e: // Reserved (n Bytes)
+ case 0x009f: // Reserved (n Bytes)
+ {
+ sal_uInt16 nUSHORT(0);
+ pPict->ReadUInt16( nUSHORT ); nDataSize=2+nUSHORT;
+ break;
+ }
+ case 0x00a0: // ShortComment
+ nDataSize=2;
+ break;
+
+ case 0x00a1: // LongComment
+ {
+ sal_uInt16 nUSHORT(0);
+ pPict->SeekRel(2); pPict->ReadUInt16( nUSHORT ); nDataSize=4+nUSHORT;
+ break;
+ }
+ default: // 0x00a2 bis 0xffff (most times reserved)
+ sal_uInt16 nUSHORT(0);
+ if (nOpcode<=0x00af) { pPict->ReadUInt16( nUSHORT ); nDataSize=2+nUSHORT; }
+ else if (nOpcode<=0x00cf) { nDataSize=0; }
+ else if (nOpcode<=0x00fe) { sal_uInt32 nTemp(0); pPict->ReadUInt32(nTemp) ; nDataSize = nTemp; nDataSize+=4; }
+ // Osnola: checkme: in the Quickdraw Ref examples ( for pict v2)
+ // 0x00ff(EndOfPict) is also not followed by any data...
+ else if (nOpcode==0x00ff) { nDataSize=IsVersion2 ? 2 : 0; } // OpEndPic
+ else if (nOpcode<=0x01ff) { nDataSize=2; }
+ else if (nOpcode<=0x0bfe) { nDataSize=4; }
+ else if (nOpcode<=0x0bff) { nDataSize=22; }
+ else if (nOpcode==0x0c00) { nDataSize=24; } // HeaderOp
+ else if (nOpcode<=0x7eff) { nDataSize=24; }
+ else if (nOpcode<=0x7fff) { nDataSize=254; }
+ else if (nOpcode<=0x80ff) { nDataSize=0; }
+ else { sal_uInt32 nTemp(0); pPict->ReadUInt32(nTemp) ; nDataSize = nTemp; nDataSize+=4; }
+ }
+
+ if (nDataSize==0xffffffff) {
+ pPict->SetError(SVSTREAM_FILEFORMAT_ERROR);
+ return 0;
+ }
+ return nDataSize;
+}
+
+void PictReader::ReadPict( SvStream & rStreamPict, GDIMetaFile & rGDIMetaFile )
+{
+ try {
+ sal_uInt16 nOpcode;
+ sal_uInt8 nOneByteOpcode;
+ sal_uInt64 nSize;
+
+ pPict = &rStreamPict;
+ nOrigPos = pPict->Tell();
+ SvStreamEndian nOrigNumberFormat = pPict->GetEndian();
+
+ aActForeColor = COL_BLACK;
+ aActBackColor = COL_WHITE;
+ nActPenSize = Size(1,1);
+ eActROP = RasterOp::OverPaint;
+ eActMethod = PictDrawingMethod::UNDEFINED;
+ aActOvalSize = Size(1,1);
+
+ aActFont.SetCharSet( GetTextEncoding());
+ aActFont.SetFamily(FAMILY_SWISS);
+ aActFont.SetFontSize(Size(0,12));
+ aActFont.SetAlignment(ALIGN_BASELINE);
+
+ aHRes = aVRes = Fraction( 1, 1 );
+
+ pVirDev = VclPtr<VirtualDevice>::Create();
+ pVirDev->EnableOutput(false);
+ rGDIMetaFile.Record(pVirDev);
+
+ pPict->SetEndian(SvStreamEndian::BIG);
+
+ ReadHeader();
+
+ aPenPosition=Point(-aBoundingRect.Left(),-aBoundingRect.Top());
+ aTextPosition=aPenPosition;
+
+ sal_uInt64 nPos=pPict->Tell();
+
+ for (;;) {
+
+ if (IsVersion2 )
+ pPict->ReadUInt16( nOpcode );
+ else
+ {
+ pPict->ReadUChar( nOneByteOpcode );
+ nOpcode=static_cast<sal_uInt16>(nOneByteOpcode);
+ }
+
+ if (pPict->GetError())
+ break;
+
+ if (pPict->eof())
+ {
+ pPict->SetError(SVSTREAM_FILEFORMAT_ERROR);
+ break;
+ }
+
+ if (nOpcode==0x00ff)
+ break;
+
+ nSize=ReadData(nOpcode);
+
+ if ( IsVersion2 )
+ {
+ if ( nSize & 1 )
+ nSize++;
+
+ nPos+=2+nSize;
+ }
+ else
+ nPos+=1+nSize;
+
+ if (!checkSeek(*pPict, nPos))
+ {
+ pPict->SetError(SVSTREAM_FILEFORMAT_ERROR);
+ break;
+ }
+ }
+
+ pVirDev->SetClipRegion();
+ rGDIMetaFile.Stop();
+ pVirDev.disposeAndClear();
+
+ rGDIMetaFile.SetPrefMapMode( MapMode( MapUnit::MapInch, Point(), aHRes, aVRes ) );
+ rGDIMetaFile.SetPrefSize( aBoundingRect.GetSize() );
+
+ pPict->SetEndian(nOrigNumberFormat);
+
+ if (pPict->GetError()) pPict->Seek(nOrigPos);
+ } catch (...)
+ {
+ rStreamPict.SetError(SVSTREAM_FILEFORMAT_ERROR);
+ }
+}
+
+void ReadPictFile(SvStream &rStreamPict, GDIMetaFile& rGDIMetaFile)
+{
+ PictReader aPictReader;
+ aPictReader.ReadPict(rStreamPict, rGDIMetaFile);
+}
+
+//================== GraphicImport - the exported function ================
+
+bool ImportPictGraphic( SvStream& rIStm, Graphic & rGraphic)
+{
+ GDIMetaFile aMTF;
+ bool bRet = false;
+
+ ReadPictFile(rIStm, aMTF);
+
+ if ( !rIStm.GetError() )
+ {
+ rGraphic = Graphic( aMTF );
+ bRet = true;
+ }
+
+ return bRet;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/vcl/source/filter/ipict/shape.cxx b/vcl/source/filter/ipict/shape.cxx
new file mode 100644
index 000000000..88a62cfd2
--- /dev/null
+++ b/vcl/source/filter/ipict/shape.cxx
@@ -0,0 +1,259 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+/** Osnola:
+IMPORTANT NOTE: some Quickdraw lines/frames can not be "quickly" drawn exactly:
+for instance, when PenSize=(1,1), the line from (0,0) to (8,0)
+corresponds to the rectangle (0,0)(0,1)(9,1)(9,0), which can only be drawn
+ by drawing a rectangle. Drawing a non horizontal/vertical will imply to draw
+a polygon, ...
+Similarly, drawing the frame of a rectangle (0,0)(0,1)(9,1)(9,0) when PenSize=(1,1),
+will imply to draw a rectangle (0.5,0.5)(0.5,8.5)(8.5,8.5)(8.5,0.5) with linewidth=1...
+
+Here, we choose:
+- for horizontal/vertical lines and line with length less than five to draw the real line,
+- in the other case, we keep the same shape (even if this means some "bad" coordinates)
+*/
+
+#include <basegfx/polygon/b2dpolygon.hxx>
+#include <basegfx/polygon/b2dpolygontools.hxx>
+#include "shape.hxx"
+
+namespace PictReaderShapePrivate {
+ /** returns an inside rectangle knowing the penSize in order to obtain the ``correct'' position
+ when we draw a frame in wide length*/
+ static tools::Rectangle contractRectangle(bool drawFrame, tools::Rectangle const &rect, Size const &pSize) {
+ if (!drawFrame) return rect;
+ tools::Long penSize=(pSize.Width()+pSize.Height())/2;
+ if (2*penSize > rect.Right()-rect.Left()) penSize = (rect.Right()-rect.Left()+1)/2;
+ if (2*penSize > rect.Bottom()-rect.Top()) penSize = (rect.Bottom()-rect.Top()+1)/2;
+ tools::Long const X[2] = { rect.Left()+penSize/2, rect.Right()-(penSize+1)/2 };
+ tools::Long const Y[2] = { rect.Top()+penSize/2, rect.Bottom()-(penSize+1)/2 };
+ return tools::Rectangle(Point(X[0],Y[0]), Point(X[1], Y[1]));
+ }
+}
+
+namespace PictReaderShape {
+ //--------- draws a horizontal/vertical/small line (by creating a "rectangle/polygon") ---------
+ static bool drawLineHQ(VirtualDevice *dev, Point const &orig, Point const &dest, Size const &pSize) {
+ tools::Long dir[2] = { dest.X()-orig.X(), dest.Y()-orig.Y() };
+ bool vertic = dir[0] == 0;
+ bool horiz = dir[1] == 0;
+ if (!horiz && !vertic && dir[0]*dir[0]+dir[1]*dir[1] > 25) return false;
+
+ using namespace basegfx;
+ B2DPolygon poly;
+ if (horiz || vertic) {
+ tools::Long X[2]={ orig.X(), dest.X() }, Y[2] = { orig.Y(), dest.Y() };
+ if (horiz) {
+ if (X[0] < X[1]) X[1]+=pSize.Width();
+ else X[0]+=pSize.Width();
+ Y[1] += pSize.Height();
+ }
+ else {
+ if (Y[0] < Y[1]) Y[1]+=pSize.Height();
+ else Y[0]+=pSize.Height();
+ X[1] += pSize.Width();
+ }
+ poly.append(B2DPoint(X[0], Y[0])); poly.append(B2DPoint(X[1], Y[0]));
+ poly.append(B2DPoint(X[1], Y[1])); poly.append(B2DPoint(X[0], Y[1]));
+ poly.append(B2DPoint(X[0], Y[0]));
+ }
+ else {
+ tools::Long origPt[4][2] = { { orig.X(), orig.Y() }, { orig.X()+pSize.Width(), orig.Y() },
+ { orig.X()+pSize.Width(), orig.Y()+pSize.Height() },
+ { orig.X(), orig.Y()+pSize.Height() }};
+ int origAvoid = dir[0] > 0 ? (dir[1] > 0 ? 2 : 1) : (dir[1] > 0 ? 3 : 0);
+ tools::Long destPt[4][2] = { { dest.X(), dest.Y() }, { dest.X()+pSize.Width(), dest.Y() },
+ { dest.X()+pSize.Width(), dest.Y()+pSize.Height() },
+ { dest.X(), dest.Y()+pSize.Height() }};
+ for (int w = origAvoid+1; w < origAvoid+4; w++) {
+ int wh = w%4;
+ poly.append(B2DPoint(origPt[wh][0], origPt[wh][1]));
+ }
+ for (int w = origAvoid+3; w < origAvoid+6; w++) {
+ int wh = w%4;
+ poly.append(B2DPoint(destPt[wh][0], destPt[wh][1]));
+ }
+ int wh = (origAvoid+1)%4;
+ poly.append(B2DPoint(origPt[wh][0], origPt[wh][1]));
+ }
+
+ // HACK: here we use the line coloring when drawing the shape
+ // must be changed if other parameter are changed to draw
+ // a line/fill shape
+ Color oldFColor = dev->GetFillColor(), oldLColor = dev->GetLineColor();
+ dev->SetFillColor(oldLColor); dev->SetLineColor(COL_TRANSPARENT);
+ dev->DrawPolygon(poly);
+ dev->SetLineColor(oldLColor); dev->SetFillColor(oldFColor);
+ return true;
+ }
+
+
+ //-------------------- draws a line --------------------
+
+ void drawLine(VirtualDevice *dev, Point const &orig, Point const &dest, Size const &pSize) {
+ if (drawLineHQ(dev,orig,dest,pSize)) return;
+
+ tools::Long penSize=(pSize.Width()+pSize.Height())/2;
+ tools::Long decal[2] = { pSize.Width()/2, pSize.Height()/2};
+
+ using namespace basegfx;
+ B2DPolygon poly;
+ poly.append(B2DPoint(double(orig.X()+decal[0]), double(orig.Y()+decal[1])));
+ poly.append(B2DPoint(double(dest.X()+decal[0]), double(dest.Y()+decal[1])));
+ dev->DrawPolyLine(poly, double(penSize), basegfx::B2DLineJoin::NONE);
+ }
+
+ //-------------------- draws a rectangle --------------------
+ /* Note(checkme): contradictally with the QuickDraw's reference 3-23, it seems better to consider
+ that the frame/content of a rectangle appears inside the given rectangle. Does a conversion
+ appear between the pascal functions and the data stored in the file ? */
+ void drawRectangle(VirtualDevice *dev, bool drawFrame, tools::Rectangle const &orig, Size const &pSize) {
+ int penSize=(pSize.Width()+pSize.Height())/2;
+ tools::Rectangle rect = PictReaderShapePrivate::contractRectangle(drawFrame, orig, pSize);
+ tools::Long const X[2] = { rect.Left(), rect.Right() };
+ tools::Long const Y[2] = { rect.Top(), rect.Bottom() };
+
+ using namespace basegfx;
+ B2DPolygon poly;
+ poly.append(B2DPoint(X[0], Y[0])); poly.append(B2DPoint(X[1], Y[0]));
+ poly.append(B2DPoint(X[1], Y[1])); poly.append(B2DPoint(X[0], Y[1]));
+ poly.append(B2DPoint(X[0], Y[0]));
+
+ if (drawFrame)
+ dev->DrawPolyLine(poly, double(penSize), basegfx::B2DLineJoin::NONE);
+ else
+ dev->DrawPolygon(poly);
+ }
+
+ //-------------------- draws an ellipse --------------------
+ void drawEllipse(VirtualDevice *dev, bool drawFrame, tools::Rectangle const &orig, Size const &pSize) {
+ int penSize=(pSize.Width()+pSize.Height())/2;
+ tools::Rectangle oval = PictReaderShapePrivate::contractRectangle(drawFrame, orig, pSize);
+ using namespace basegfx;
+ tools::Long const X[2] = { oval.Left(), oval.Right() };
+ tools::Long const Y[2] = { oval.Top(), oval.Bottom() };
+ B2DPoint center(0.5*(X[1]+X[0]), 0.5*(Y[1]+Y[0]));
+ B2DPolygon poly = basegfx::utils::createPolygonFromEllipse(center, 0.5*(X[1]-X[0]), 0.5*(Y[1]-Y[0]));
+ if (drawFrame)
+ dev->DrawPolyLine(poly, double(penSize), basegfx::B2DLineJoin::NONE);
+ else
+ dev->DrawPolygon(poly);
+ }
+
+ //-------------------- draws an arc/pie --------------------
+ void drawArc(VirtualDevice *dev, bool drawFrame, tools::Rectangle const &orig, const double& angle1, const double& angle2, Size const &pSize) {
+ int penSize=(pSize.Width()+pSize.Height())/2;
+ tools::Rectangle arc = PictReaderShapePrivate::contractRectangle(drawFrame, orig, pSize);
+ using namespace basegfx;
+
+ // pict angle are CW with 0 at twelve o'clock (with Y-axis inverted)...
+ double angl1 = angle1-M_PI_2;
+ double angl2 = angle2-M_PI_2;
+ tools::Long const X[2] = { arc.Left(), arc.Right() };
+ tools::Long const Y[2] = { arc.Top(), arc.Bottom() };
+ B2DPoint center(0.5*(X[1]+X[0]), 0.5*(Y[1]+Y[0]));
+
+ // We must have angl1 between 0 and 2PI
+ while (angl1 < 0.0) { angl1 += 2 * M_PI; angl2 += 2 * M_PI; }
+ while (angl1 >= 2 * M_PI) { angl1 -= 2 * M_PI; angl2 -= 2 * M_PI; }
+
+ // if this happen, we want a complete circle
+ // so we set angl2 slightly less than angl1
+ if (angl2 >= angl1 + 2 * M_PI) angl2 = angl1-0.001;
+
+ // We must have angl2 between 0 and 2PI
+ while (angl2 < 0.0) angl2 += 2 * M_PI;
+ while (angl2 >= 2 * M_PI) angl2 -= 2 * M_PI;
+
+ B2DPolygon poly = basegfx::utils::createPolygonFromEllipseSegment(center, 0.5*(X[1]-X[0]), 0.5*(Y[1]-Y[0]), angl1, angl2);
+ if (drawFrame)
+ dev->DrawPolyLine(poly, double(penSize), basegfx::B2DLineJoin::NONE);
+ else {
+ // adds circle's center
+ poly.append(center);
+ dev->DrawPolygon(poly);
+ }
+ }
+ //-------------------- draws a rectangle with round corner --------------------
+ void drawRoundRectangle(VirtualDevice *dev, bool drawFrame, tools::Rectangle const &orig, Size const &ovalSize, Size const &pSize) {
+ int penSize=(pSize.Width()+pSize.Height())/2;
+ tools::Rectangle oval = PictReaderShapePrivate::contractRectangle(drawFrame, orig, pSize);
+ int ovalW=ovalSize.Width(), ovalH=ovalSize.Height();
+ using namespace basegfx;
+ tools::Long const X[2] = { oval.Left(), oval.Right() };
+ tools::Long const Y[2] = { oval.Top(), oval.Bottom() };
+ tools::Long width = X[1] - X[0];
+ tools::Long height = Y[1] - Y[0];
+ if (ovalW > width) ovalW = static_cast< int >( width );
+ if (ovalH > height) ovalH = static_cast< int >( height );
+
+ B2DRectangle rect(B2DPoint(X[0],Y[0]), B2DPoint(X[1],Y[1]));
+ B2DPolygon poly = basegfx::utils::createPolygonFromRect(rect, (width != 0.0) ? ovalW/width : 0.0, (height != 0.0) ? ovalH/height : 0.0);
+
+ if (drawFrame)
+ dev->DrawPolyLine(poly, double(penSize), basegfx::B2DLineJoin::NONE);
+ else
+ dev->DrawPolygon(poly);
+ }
+
+ //-------------------- draws a polygon --------------------
+void drawPolygon(VirtualDevice *dev, bool drawFrame, tools::Polygon const &orig, Size const &pSize) {
+ int penSize=(pSize.Width()+pSize.Height())/2;
+ tools::Long decalTL[2] = {0, 0}, decalBR[2] = { pSize.Width(), pSize.Height()};
+ if (drawFrame) {
+ decalTL[0] += penSize/2; decalTL[1] += penSize/2;
+ decalBR[0] -= (penSize+1)/2; decalBR[1] -= (penSize+1)/2;
+ }
+ // Quickdraw Drawing Reference 3-82: the pen size is only used for frame
+ else decalBR[0] = decalBR[1] = 0;
+
+
+ int numPt = orig.GetSize();
+ if (numPt <= 1) return;
+
+ // we compute a barycenter of the point to define the extended direction of each point
+ double bary[2] = { 0.0, 0.0 };
+ for (int i = 0; i < numPt; i++) {
+ Point const &pt = orig.GetPoint(i);
+ bary[0] += double(pt.X()); bary[1] += double(pt.Y());
+ }
+ bary[0]/=double(numPt); bary[1]/=double(numPt);
+
+ using namespace basegfx;
+ B2DPolygon poly;
+ poly.reserve(numPt);
+ // Note: a polygon can be open, so we must not close it when we draw the frame
+ for (int i = 0; i < numPt; i++) {
+ Point const &pt = orig.GetPoint(i);
+ double x = (double(pt.X()) < bary[0]) ? pt.X()+decalTL[0] : pt.X()+decalBR[0];
+ double y = (double(pt.Y()) < bary[1]) ? pt.Y()+decalTL[1] : pt.Y()+decalBR[1];
+ poly.append(B2DPoint(x, y));
+ }
+ if (drawFrame)
+ dev->DrawPolyLine(poly, double(penSize), basegfx::B2DLineJoin::NONE);
+ else
+ dev->DrawPolygon(poly);
+ }
+
+
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/vcl/source/filter/ipict/shape.hxx b/vcl/source/filter/ipict/shape.hxx
new file mode 100644
index 000000000..bccd39e63
--- /dev/null
+++ b/vcl/source/filter/ipict/shape.hxx
@@ -0,0 +1,57 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+#ifndef INCLUDED_FILTER_SOURCE_GRAPHICFILTER_IPICT_SHAPE_HXX
+#define INCLUDED_FILTER_SOURCE_GRAPHICFILTER_IPICT_SHAPE_HXX
+
+#include <vcl/virdev.hxx>
+
+namespace PictReaderShape {
+ /** draws a line from orig to dest knowing penSize
+
+ Attention: in order to draw horizontal/vertical/small lines, this function can instead draw a rectangle or
+ a polygon. In this case, we retrieve the line information from VirtualDev ( GetLineColor )
+ and we use them as fill information ( SetFillColor ). We restore after the VirtualDev state.
+
+ This implies also that this function must be modified if we use real pattern to draw these primitives.
+ */
+ void drawLine(VirtualDevice *dev, Point const &orig, Point const &dest, Size const &pSize);
+
+ /** draws a rectangle knowing penSize */
+ void drawRectangle(VirtualDevice *dev, bool drawFrame, tools::Rectangle const &rect, Size const &pSize);
+
+ /** draws a polygon knowing penSize */
+void drawPolygon(VirtualDevice *dev, bool drawFrame, tools::Polygon const &rect, Size const &pSize);
+
+ /** draws an ellipse knowing penSize */
+ void drawEllipse(VirtualDevice *dev, bool drawFrame, tools::Rectangle const &orig, Size const &pSize);
+
+ /** draws a rounded rectangle knowing penSize
+ \note ovalSize is two time the size of the corner
+ */
+ void drawRoundRectangle(VirtualDevice *dev, bool drawFrame, tools::Rectangle const &orig, Size const &ovalS, Size const &pSize);
+
+ /** draws an arc in a b2dpolygon knowing penSize
+ \note - it supposes that angl1 < angl2
+ */
+ void drawArc(VirtualDevice *dev, bool drawFrame, tools::Rectangle const &orig, const double& angle1, const double& angle2, Size const &pSize);
+}
+
+#endif // INCLUDED_FILTER_SOURCE_GRAPHICFILTER_IPICT_SHAPE_HXX
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/vcl/source/filter/ipsd/ipsd.cxx b/vcl/source/filter/ipsd/ipsd.cxx
new file mode 100644
index 000000000..4c1277027
--- /dev/null
+++ b/vcl/source/filter/ipsd/ipsd.cxx
@@ -0,0 +1,776 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+
+#include <vcl/graph.hxx>
+#include <vcl/BitmapTools.hxx>
+#include <vcl/outdev.hxx>
+#include <sal/log.hxx>
+#include <tools/fract.hxx>
+#include <tools/helpers.hxx>
+#include <tools/stream.hxx>
+#include <memory>
+#include <filter/PsdReader.hxx>
+
+
+class FilterConfigItem;
+
+//============================ PSDReader ==================================
+
+#define PSD_BITMAP 0
+#define PSD_GRAYSCALE 1
+#define PSD_INDEXED 2
+#define PSD_RGB 3
+#define PSD_CMYK 4
+#define PSD_MULTICHANNEL 7
+#define PSD_DUOTONE 8
+#define PSD_LAB 9
+
+namespace {
+
+struct PSDFileHeader
+{
+ sal_uInt32 nSignature;
+ sal_uInt16 nVersion;
+ sal_uInt32 nPad1;
+ sal_uInt16 nPad2;
+ sal_uInt16 nChannels;
+ sal_uInt32 nRows;
+ sal_uInt32 nColumns;
+ sal_uInt16 nDepth;
+ sal_uInt16 nMode;
+};
+
+class PSDReader {
+
+private:
+
+ SvStream& m_rPSD; // the PSD file to be read in
+ std::unique_ptr<PSDFileHeader>
+ mpFileHeader;
+
+ sal_uInt32 mnXResFixed;
+ sal_uInt32 mnYResFixed;
+
+ bool mbStatus;
+ bool mbTransparent;
+
+ std::unique_ptr<vcl::bitmap::RawBitmap> mpBitmap;
+ std::vector<Color> mvPalette;
+ sal_uInt16 mnDestBitDepth;
+ bool mbCompression; // RLE decoding
+ std::unique_ptr<sal_uInt8[]>
+ mpPalette;
+
+ bool ImplReadBody();
+ bool ImplReadHeader();
+
+public:
+ explicit PSDReader(SvStream &rStream);
+ bool ReadPSD(Graphic & rGraphic);
+};
+
+}
+
+//=================== Methods of PSDReader ==============================
+
+PSDReader::PSDReader(SvStream &rStream)
+ : m_rPSD(rStream)
+ , mnXResFixed(0)
+ , mnYResFixed(0)
+ , mbStatus(true)
+ , mbTransparent(false)
+ , mnDestBitDepth(0)
+ , mbCompression(false)
+{
+}
+
+bool PSDReader::ReadPSD(Graphic & rGraphic )
+{
+ if (m_rPSD.GetError())
+ return false;
+
+ m_rPSD.SetEndian( SvStreamEndian::BIG );
+
+ // read header:
+
+ if ( !ImplReadHeader() )
+ return false;
+
+ if (mbStatus)
+ {
+ sal_uInt32 nResult;
+ if (o3tl::checked_multiply(mpFileHeader->nColumns, mpFileHeader->nRows, nResult) || nResult > SAL_MAX_INT32/2/3)
+ return false;
+ }
+
+ Size aBitmapSize( mpFileHeader->nColumns, mpFileHeader->nRows );
+ mpBitmap.reset( new vcl::bitmap::RawBitmap( aBitmapSize, mbTransparent ? 32 : 24 ) );
+ if ( mpPalette && mbStatus )
+ {
+ mvPalette.resize( 256 );
+ for ( sal_uInt16 i = 0; i < 256; i++ )
+ {
+ mvPalette[i] = Color( mpPalette[ i ], mpPalette[ i + 256 ], mpPalette[ i + 512 ] );
+ }
+ }
+
+ if ((mnDestBitDepth == 1 || mnDestBitDepth == 8) && mvPalette.empty())
+ {
+ SAL_WARN("vcl", "no palette, but bit depth is " << mnDestBitDepth);
+ mbStatus = false;
+ return mbStatus;
+ }
+
+ // read bitmap data
+ if ( mbStatus && ImplReadBody() )
+ {
+ rGraphic = Graphic( vcl::bitmap::CreateFromData( std::move(*mpBitmap) ) );
+
+ if ( mnXResFixed && mnYResFixed )
+ {
+ Fraction aFractX( 1, mnXResFixed >> 16 );
+ Fraction aFractY( 1, mnYResFixed >> 16 );
+ MapMode aMapMode( MapUnit::MapInch, Point(), aFractX, aFractY );
+ Size aPrefSize = OutputDevice::LogicToLogic(aBitmapSize, aMapMode, MapMode(MapUnit::Map100thMM));
+ rGraphic.SetPrefSize( aPrefSize );
+ rGraphic.SetPrefMapMode( MapMode( MapUnit::Map100thMM ) );
+ }
+ }
+ else
+ mbStatus = false;
+ return mbStatus;
+}
+
+
+bool PSDReader::ImplReadHeader()
+{
+ mpFileHeader.reset( new PSDFileHeader );
+
+ m_rPSD.ReadUInt32( mpFileHeader->nSignature ).ReadUInt16( mpFileHeader->nVersion ).ReadUInt32( mpFileHeader->nPad1 ). ReadUInt16( mpFileHeader->nPad2 ).ReadUInt16( mpFileHeader->nChannels ).ReadUInt32( mpFileHeader->nRows ). ReadUInt32( mpFileHeader->nColumns ).ReadUInt16( mpFileHeader->nDepth ).ReadUInt16( mpFileHeader->nMode );
+
+ if (!m_rPSD.good())
+ return false;
+
+ if ( ( mpFileHeader->nSignature != 0x38425053 ) || ( mpFileHeader->nVersion != 1 ) )
+ return false;
+
+ if ( mpFileHeader->nRows == 0 || mpFileHeader->nColumns == 0 )
+ return false;
+
+ if ( ( mpFileHeader->nRows > 30000 ) || ( mpFileHeader->nColumns > 30000 ) )
+ return false;
+
+ sal_uInt16 nDepth = mpFileHeader->nDepth;
+ if (!( ( nDepth == 1 ) || ( nDepth == 8 ) || ( nDepth == 16 ) ) )
+ return false;
+
+ mnDestBitDepth = ( nDepth == 16 ) ? 8 : nDepth;
+
+ sal_uInt32 nColorLength(0);
+ m_rPSD.ReadUInt32( nColorLength );
+ if ( mpFileHeader->nMode == PSD_CMYK )
+ {
+ switch ( mpFileHeader->nChannels )
+ {
+ case 5 :
+ mbTransparent = true;
+ [[fallthrough]];
+ case 4 :
+ mnDestBitDepth = 24;
+ break;
+ default :
+ return false;
+ }
+ }
+ else switch ( mpFileHeader->nChannels )
+ {
+ case 2 :
+ mbTransparent = true;
+ break;
+ case 1 :
+ break;
+ case 4 :
+ mbTransparent = true;
+ [[fallthrough]];
+ case 3 :
+ mnDestBitDepth = 24;
+ break;
+ default:
+ return false;
+ }
+
+ switch ( mpFileHeader->nMode )
+ {
+ case PSD_BITMAP :
+ {
+ if ( nColorLength || ( nDepth != 1 ) )
+ return false;
+ }
+ break;
+
+ case PSD_INDEXED :
+ {
+ if ( nColorLength != 768 ) // we need the color map
+ return false;
+ mpPalette.reset( new sal_uInt8[ 768 ] );
+ m_rPSD.ReadBytes(mpPalette.get(), 768);
+ }
+ break;
+
+ case PSD_DUOTONE : // we'll handle the duotone color like a normal grayscale picture
+ m_rPSD.SeekRel( nColorLength );
+ nColorLength = 0;
+ [[fallthrough]];
+ case PSD_GRAYSCALE :
+ {
+ if ( nColorLength )
+ return false;
+ mpPalette.reset( new sal_uInt8[ 768 ] );
+ for ( sal_uInt16 i = 0; i < 256; i++ )
+ {
+ mpPalette[ i ] = mpPalette[ i + 256 ] = mpPalette[ i + 512 ] = static_cast<sal_uInt8>(i);
+ }
+ }
+ break;
+
+ case PSD_CMYK :
+ case PSD_RGB :
+ case PSD_MULTICHANNEL :
+ case PSD_LAB :
+ {
+ if ( nColorLength ) // color table is not supported by the other graphic modes
+ return false;
+ }
+ break;
+
+ default:
+ return false;
+ }
+ sal_uInt32 nResourceLength(0);
+ m_rPSD.ReadUInt32(nResourceLength);
+ if (nResourceLength > m_rPSD.remainingSize())
+ return false;
+ sal_uInt32 nLayerPos = m_rPSD.Tell() + nResourceLength;
+
+ // this is a loop over the resource entries to get the resolution info
+ while( m_rPSD.Tell() < nLayerPos )
+ {
+ sal_uInt32 nType(0);
+ sal_uInt16 nUniqueID(0);
+ sal_uInt8 n8(0);
+ m_rPSD.ReadUInt32(nType).ReadUInt16(nUniqueID).ReadUChar(n8);
+ if (nType != 0x3842494d)
+ break;
+ sal_uInt32 nPStringLen = n8;
+ if ( ! ( nPStringLen & 1 ) )
+ nPStringLen++;
+ m_rPSD.SeekRel( nPStringLen ); // skipping the pstring
+ sal_uInt32 nResEntryLen(0);
+ m_rPSD.ReadUInt32( nResEntryLen );
+ if ( nResEntryLen & 1 )
+ nResEntryLen++; // the resource entries are padded
+ sal_uInt32 nCurrentPos = m_rPSD.Tell();
+ if (nCurrentPos > nLayerPos || nResEntryLen > (nLayerPos - nCurrentPos)) // check if size
+ break; // is possible
+ switch( nUniqueID )
+ {
+ case 0x3ed : // UID for the resolution info
+ {
+ sal_Int16 nUnit;
+
+ m_rPSD.ReadUInt32( mnXResFixed ).ReadInt16( nUnit ).ReadInt16( nUnit )
+ .ReadUInt32( mnYResFixed ).ReadInt16( nUnit ).ReadInt16( nUnit );
+ }
+ break;
+ }
+ m_rPSD.Seek( nCurrentPos + nResEntryLen ); // set the stream to the next
+ } // resource entry
+ m_rPSD.Seek( nLayerPos );
+ sal_uInt32 nLayerMaskLength(0);
+ m_rPSD.ReadUInt32( nLayerMaskLength );
+ m_rPSD.SeekRel( nLayerMaskLength );
+
+ sal_uInt16 nCompression(0);
+ m_rPSD.ReadUInt16(nCompression);
+ if ( nCompression == 0 )
+ {
+ mbCompression = false;
+ }
+ else if ( nCompression == 1 )
+ {
+ m_rPSD.SeekRel( ( mpFileHeader->nRows * mpFileHeader->nChannels ) << 1 );
+ mbCompression = true;
+ }
+ else
+ return false;
+
+ return true;
+}
+
+namespace
+{
+ const Color& SanitizePaletteIndex(std::vector<Color> const & rvPalette, sal_uInt8 nIndex)
+ {
+ if (nIndex >= rvPalette.size())
+ {
+ auto nSanitizedIndex = nIndex % rvPalette.size();
+ SAL_WARN_IF(nIndex != nSanitizedIndex, "filter.psd", "invalid colormap index: "
+ << static_cast<unsigned int>(nIndex) << ", colormap len is: "
+ << rvPalette.size());
+ nIndex = nSanitizedIndex;
+ }
+ return rvPalette[nIndex];
+ }
+}
+
+bool PSDReader::ImplReadBody()
+{
+ sal_uInt32 nX, nY;
+ signed char nRunCount = 0;
+ sal_uInt8 nDat = 0, nDummy, nRed, nGreen, nBlue;
+ BitmapColor aBitmapColor;
+ nX = nY = 0;
+
+ switch ( mnDestBitDepth )
+ {
+ case 1 :
+ {
+ signed char nBitCount = -1;
+ while (nY < mpFileHeader->nRows && m_rPSD.good())
+ {
+ if ( nBitCount == -1 )
+ {
+ if ( mbCompression ) // else nRunCount = 0 -> so we use only single raw packets
+ {
+ char nTmp(0);
+ m_rPSD.ReadChar(nTmp);
+ nRunCount = nTmp;
+ }
+ }
+ if ( nRunCount & 0x80 ) // a run length packet
+ {
+ const sal_uInt16 nCount = -nRunCount + 1;
+ for (sal_uInt16 i = 0; i < nCount && m_rPSD.good(); ++i)
+ {
+ if ( nBitCount == -1 ) // bits left in nDat?
+ {
+ m_rPSD.ReadUChar( nDat );
+ nDat ^= 0xff;
+ nBitCount = 7;
+ }
+ mpBitmap->SetPixel(nY, nX, SanitizePaletteIndex(mvPalette, nDat >> nBitCount--));
+ if ( ++nX == mpFileHeader->nColumns )
+ {
+ nX = 0;
+ nY++;
+ nBitCount = -1;
+ if ( nY == mpFileHeader->nRows )
+ break;
+ }
+ }
+ }
+ else // a raw packet
+ {
+ const sal_uInt16 nCount = (nRunCount & 0x7f) + 1;
+ for (sal_uInt16 i = 0; i < nCount && m_rPSD.good(); ++i)
+ {
+ if ( nBitCount == -1 ) // bits left in nDat ?
+ {
+ m_rPSD.ReadUChar( nDat );
+ nDat ^= 0xff;
+ nBitCount = 7;
+ }
+ mpBitmap->SetPixel(nY, nX, SanitizePaletteIndex(mvPalette, nDat >> nBitCount--));
+ if ( ++nX == mpFileHeader->nColumns )
+ {
+ nX = 0;
+ nY++;
+ nBitCount = -1;
+ if ( nY == mpFileHeader->nRows )
+ break;
+ }
+ }
+ }
+ }
+ }
+ break;
+
+ case 8 :
+ {
+ while (nY < mpFileHeader->nRows && m_rPSD.good())
+ {
+ if ( mbCompression ) // else nRunCount = 0 -> so we use only single raw packets
+ {
+ char nTmp(0);
+ m_rPSD.ReadChar(nTmp);
+ nRunCount = nTmp;
+ }
+
+ if ( nRunCount & 0x80 ) // a run length packet
+ {
+ m_rPSD.ReadUChar( nDat );
+ if ( mpFileHeader->nDepth == 16 ) // 16 bit depth is to be skipped
+ m_rPSD.ReadUChar( nDummy );
+ const sal_uInt16 nCount = -nRunCount + 1;
+ for (sal_uInt16 i = 0; i < nCount && m_rPSD.good(); ++i)
+ {
+ mpBitmap->SetPixel(nY, nX, SanitizePaletteIndex(mvPalette, nDat));
+ if ( ++nX == mpFileHeader->nColumns )
+ {
+ nX = 0;
+ nY++;
+ if ( nY == mpFileHeader->nRows )
+ break;
+ }
+ }
+ }
+ else // a raw packet
+ {
+ const sal_uInt16 nCount = (nRunCount & 0x7f) + 1;
+ for (sal_uInt16 i = 0; i < nCount && m_rPSD.good(); ++i)
+ {
+ m_rPSD.ReadUChar( nDat );
+ if ( mpFileHeader->nDepth == 16 ) // 16 bit depth is to be skipped
+ m_rPSD.ReadUChar( nDummy );
+ mpBitmap->SetPixel(nY, nX, SanitizePaletteIndex(mvPalette, nDat));
+ if ( ++nX == mpFileHeader->nColumns )
+ {
+ nX = 0;
+ nY++;
+ if ( nY == mpFileHeader->nRows )
+ break;
+ }
+ }
+ }
+ }
+ }
+ break;
+
+ case 24 :
+ {
+
+ // the psd format is in plain order (RRRR GGGG BBBB) so we have to set each pixel three times
+ // maybe the format is CCCC MMMM YYYY KKKK
+
+ while (nY < mpFileHeader->nRows && m_rPSD.good())
+ {
+ if ( mbCompression ) // else nRunCount = 0 -> so we use only single raw packets
+ {
+ char nTmp(0);
+ m_rPSD.ReadChar(nTmp);
+ nRunCount = nTmp;
+ }
+
+ if ( nRunCount & 0x80 ) // a run length packet
+ {
+ m_rPSD.ReadUChar( nRed );
+ if ( mpFileHeader->nDepth == 16 ) // 16 bit depth is to be skipped
+ m_rPSD.ReadUChar( nDummy );
+ const sal_uInt16 nCount = -nRunCount + 1;
+ for (sal_uInt16 i = 0; i < nCount && m_rPSD.good(); ++i)
+ {
+ mpBitmap->SetPixel( nY, nX, Color( nRed, sal_uInt8(0), sal_uInt8(0) ) );
+ if ( ++nX == mpFileHeader->nColumns )
+ {
+ nX = 0;
+ nY++;
+ if ( nY == mpFileHeader->nRows )
+ break;
+ }
+ }
+ }
+ else // a raw packet
+ {
+ const sal_uInt16 nCount = (nRunCount & 0x7f) + 1;
+ for (sal_uInt16 i = 0; i < nCount && m_rPSD.good(); ++i)
+ {
+ m_rPSD.ReadUChar( nRed );
+ if ( mpFileHeader->nDepth == 16 ) // 16 bit depth is to be skipped
+ m_rPSD.ReadUChar( nDummy );
+ mpBitmap->SetPixel( nY, nX, Color( nRed, sal_uInt8(0), sal_uInt8(0) ) );
+ if ( ++nX == mpFileHeader->nColumns )
+ {
+ nX = 0;
+ nY++;
+ if ( nY == mpFileHeader->nRows )
+ break;
+ }
+ }
+ }
+ }
+ nY = 0;
+ while (nY < mpFileHeader->nRows && m_rPSD.good())
+ {
+ if ( mbCompression )
+ {
+ char nTmp(0);
+ m_rPSD.ReadChar(nTmp);
+ nRunCount = nTmp;
+ }
+
+ if ( nRunCount & 0x80 ) // a run length packet
+ {
+ m_rPSD.ReadUChar( nGreen );
+ if ( mpFileHeader->nDepth == 16 ) // 16 bit depth is to be skipped
+ m_rPSD.ReadUChar( nDummy );
+ const sal_uInt16 nCount = -nRunCount + 1;
+ for (sal_uInt16 i = 0; i < nCount && m_rPSD.good(); ++i)
+ {
+ aBitmapColor = mpBitmap->GetPixel( nY, nX );
+ mpBitmap->SetPixel( nY, nX, Color( aBitmapColor.GetRed(), nGreen, aBitmapColor.GetBlue() ) );
+ if ( ++nX == mpFileHeader->nColumns )
+ {
+ nX = 0;
+ nY++;
+ if ( nY == mpFileHeader->nRows )
+ break;
+ }
+ }
+ }
+ else // a raw packet
+ {
+ const sal_uInt16 nCount = (nRunCount & 0x7f) + 1;
+ for (sal_uInt16 i = 0; i < nCount && m_rPSD.good(); ++i)
+ {
+ m_rPSD.ReadUChar( nGreen );
+ if ( mpFileHeader->nDepth == 16 ) // 16 bit depth is to be skipped
+ m_rPSD.ReadUChar( nDummy );
+ aBitmapColor = mpBitmap->GetPixel( nY, nX );
+ mpBitmap->SetPixel( nY, nX, Color( aBitmapColor.GetRed(), nGreen, aBitmapColor.GetBlue() ) );
+ if ( ++nX == mpFileHeader->nColumns )
+ {
+ nX = 0;
+ nY++;
+ if ( nY == mpFileHeader->nRows )
+ break;
+ }
+ }
+ }
+ }
+ nY = 0;
+ while (nY < mpFileHeader->nRows && m_rPSD.good())
+ {
+ if ( mbCompression )
+ {
+ char nTmp(0);
+ m_rPSD.ReadChar(nTmp);
+ nRunCount = nTmp;
+ }
+
+ if ( nRunCount & 0x80 ) // a run length packet
+ {
+ m_rPSD.ReadUChar( nBlue );
+ if ( mpFileHeader->nDepth == 16 ) // 16 bit depth is to be skipped
+ m_rPSD.ReadUChar( nDummy );
+ const sal_uInt16 nCount = -nRunCount + 1;
+ for (sal_uInt16 i = 0; i < nCount && m_rPSD.good(); ++i)
+ {
+ aBitmapColor = mpBitmap->GetPixel( nY, nX );
+ mpBitmap->SetPixel( nY, nX, Color( aBitmapColor.GetRed(), aBitmapColor.GetGreen(), nBlue ) );
+ if ( ++nX == mpFileHeader->nColumns )
+ {
+ nX = 0;
+ nY++;
+ if ( nY == mpFileHeader->nRows )
+ break;
+ }
+ }
+ }
+ else // a raw packet
+ {
+ const sal_uInt16 nCount = (nRunCount & 0x7f) + 1;
+ for (sal_uInt16 i = 0; i < nCount && m_rPSD.good(); ++i)
+ {
+ m_rPSD.ReadUChar( nBlue );
+ if ( mpFileHeader->nDepth == 16 ) // 16 bit depth is to be skipped
+ m_rPSD.ReadUChar( nDummy );
+ aBitmapColor = mpBitmap->GetPixel( nY, nX );
+ mpBitmap->SetPixel( nY, nX, Color( aBitmapColor.GetRed(), aBitmapColor.GetGreen(), nBlue ) );
+ if ( ++nX == mpFileHeader->nColumns )
+ {
+ nX = 0;
+ nY++;
+ if ( nY == mpFileHeader->nRows )
+ break;
+ }
+ }
+ }
+ }
+ if (mpFileHeader->nMode == PSD_CMYK && m_rPSD.good())
+ {
+ sal_uInt32 nBlack, nBlackMax = 0;
+ std::vector<sal_uInt8> aBlack(mpFileHeader->nRows * mpFileHeader->nColumns, 0);
+ nY = 0;
+ while (nY < mpFileHeader->nRows && m_rPSD.good())
+ {
+ if ( mbCompression ) // else nRunCount = 0 -> so we use only single raw packets
+ {
+ char nTmp(0);
+ m_rPSD.ReadChar(nTmp);
+ nRunCount = nTmp;
+ }
+
+ if ( nRunCount & 0x80 ) // a run length packet
+ {
+ m_rPSD.ReadUChar( nDat );
+
+ if ( mpFileHeader->nDepth == 16 ) // 16 bit depth is to be skipped
+ m_rPSD.ReadUChar( nDummy );
+
+ for ( sal_uInt16 i = 0; i < ( -nRunCount + 1 ); i++ )
+ {
+ nBlack = mpBitmap->GetPixel( nY, nX ).GetRed() + nDat;
+ if ( nBlack > nBlackMax )
+ nBlackMax = nBlack;
+ nBlack = mpBitmap->GetPixel( nY, nX ).GetGreen() + nDat;
+ if ( nBlack > nBlackMax )
+ nBlackMax = nBlack;
+ nBlack = mpBitmap->GetPixel( nY, nX ).GetBlue() + nDat;
+ if ( nBlack > nBlackMax )
+ nBlackMax = nBlack;
+ aBlack[ nX + nY * mpFileHeader->nColumns ] = nDat ^ 0xff;
+ if ( ++nX == mpFileHeader->nColumns )
+ {
+ nX = 0;
+ nY++;
+ if ( nY == mpFileHeader->nRows )
+ break;
+ }
+ }
+ }
+ else // a raw packet
+ {
+ for ( sal_uInt16 i = 0; i < ( ( nRunCount & 0x7f ) + 1 ); i++ )
+ {
+ m_rPSD.ReadUChar( nDat );
+
+ if ( mpFileHeader->nDepth == 16 ) // 16 bit depth is to be skipped
+ m_rPSD.ReadUChar( nDummy );
+ nBlack = mpBitmap->GetPixel( nY, nX ).GetRed() + nDat;
+ if ( nBlack > nBlackMax )
+ nBlackMax = nBlack;
+ nBlack = mpBitmap->GetPixel( nY, nX ).GetGreen() + nDat;
+ if ( nBlack > nBlackMax )
+ nBlackMax = nBlack;
+ nBlack = mpBitmap->GetPixel( nY, nX ).GetBlue() + nDat;
+ if ( nBlack > nBlackMax )
+ nBlackMax = nBlack;
+ aBlack[ nX + nY * mpFileHeader->nColumns ] = nDat ^ 0xff;
+ if ( ++nX == mpFileHeader->nColumns )
+ {
+ nX = 0;
+ nY++;
+ if ( nY == mpFileHeader->nRows )
+ break;
+ }
+ }
+ }
+ }
+
+ for ( nY = 0; nY < mpFileHeader->nRows; nY++ )
+ {
+ for ( nX = 0; nX < mpFileHeader->nColumns; nX++ )
+ {
+ sal_Int32 nDAT = aBlack[ nX + nY * mpFileHeader->nColumns ] * ( nBlackMax - 256 ) / 0x1ff;
+
+ aBitmapColor = mpBitmap->GetPixel( nY, nX );
+ sal_uInt8 cR = static_cast<sal_uInt8>(MinMax( aBitmapColor.GetRed() - nDAT, 0, 255L ));
+ sal_uInt8 cG = static_cast<sal_uInt8>(MinMax( aBitmapColor.GetGreen() - nDAT, 0, 255L ));
+ sal_uInt8 cB = static_cast<sal_uInt8>(MinMax( aBitmapColor.GetBlue() - nDAT, 0, 255L ));
+ mpBitmap->SetPixel( nY, nX, Color( cR, cG, cB ) );
+ }
+ }
+ }
+ }
+ break;
+ }
+
+ if (mbTransparent && m_rPSD.good())
+ {
+ // the psd is 24 or 8 bit grafix + alphachannel
+
+ nY = nX = 0;
+ while ( nY < mpFileHeader->nRows )
+ {
+ if ( mbCompression ) // else nRunCount = 0 -> so we use only single raw packets
+ {
+ char nTmp(0);
+ m_rPSD.ReadChar(nTmp);
+ nRunCount = nTmp;
+ }
+
+ if ( nRunCount & 0x80 ) // a run length packet
+ {
+ m_rPSD.ReadUChar( nDat );
+ if ( nDat )
+ nDat = 0;
+ else
+ nDat = 1;
+ if ( mpFileHeader->nDepth == 16 ) // 16 bit depth is to be skipped
+ m_rPSD.ReadUChar( nDummy );
+ for ( sal_uInt16 i = 0; i < ( -nRunCount + 1 ); i++ )
+ {
+ mpBitmap->SetAlpha(nY, nX, nDat ? 0 : 255);
+ if ( ++nX == mpFileHeader->nColumns )
+ {
+ nX = 0;
+ nY++;
+ if ( nY == mpFileHeader->nRows )
+ break;
+ }
+ }
+ }
+ else // a raw packet
+ {
+ for ( sal_uInt16 i = 0; i < ( ( nRunCount & 0x7f ) + 1 ); i++ )
+ {
+ m_rPSD.ReadUChar( nDat );
+ if ( nDat )
+ nDat = 0;
+ else
+ nDat = 1;
+ if ( mpFileHeader->nDepth == 16 ) // 16 bit depth is to be skipped
+ m_rPSD.ReadUChar( nDummy );
+ mpBitmap->SetAlpha(nY, nX, nDat ? 0 : 255);
+ if ( ++nX == mpFileHeader->nColumns )
+ {
+ nX = 0;
+ nY++;
+ if ( nY == mpFileHeader->nRows )
+ break;
+ }
+ }
+ }
+ }
+ }
+
+ return m_rPSD.good();
+}
+
+//================== GraphicImport - the exported function ================
+
+bool ImportPsdGraphic(SvStream& rStream, Graphic& rGraphic)
+{
+ PSDReader aPSDReader(rStream);
+ return aPSDReader.ReadPSD(rGraphic);
+}
+
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/vcl/source/filter/iras/iras.cxx b/vcl/source/filter/iras/iras.cxx
new file mode 100644
index 000000000..49cfe2bef
--- /dev/null
+++ b/vcl/source/filter/iras/iras.cxx
@@ -0,0 +1,414 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+
+#include <vcl/graph.hxx>
+#include <vcl/BitmapTools.hxx>
+#include <sal/log.hxx>
+#include <tools/stream.hxx>
+#include <filter/RasReader.hxx>
+
+class FilterConfigItem;
+
+#define RAS_TYPE_OLD 0x00000000 // supported formats by this filter
+#define RAS_TYPE_STANDARD 0x00000001
+#define RAS_TYPE_BYTE_ENCODED 0x00000002
+#define RAS_TYPE_RGB_FORMAT 0x00000003
+
+#define RAS_COLOR_NO_MAP 0x00000000
+#define RAS_COLOR_RGB_MAP 0x00000001
+#define RAS_COLOR_RAW_MAP 0x00000002
+
+#define SUNRASTER_MAGICNUMBER 0x59a66a95
+
+//============================ RASReader ==================================
+
+namespace {
+
+class RASReader {
+
+private:
+
+ SvStream& m_rRAS; // the RAS file to be read in
+
+ bool mbStatus;
+ sal_Int32 mnWidth, mnHeight; // image dimensions in pixels
+ sal_uInt16 mnDstBitsPerPix;
+ sal_uInt16 mnDstColors;
+ sal_Int32 mnDepth, mnImageDatSize, mnType;
+ sal_Int32 mnColorMapType, mnColorMapSize;
+ sal_uInt8 mnRepCount, mnRepVal; // RLE Decoding
+
+ bool ImplReadBody(vcl::bitmap::RawBitmap&, std::vector<Color> const & rvPalette);
+ bool ImplReadHeader();
+ sal_uInt8 ImplGetByte();
+
+public:
+ explicit RASReader(SvStream &rRAS);
+ bool ReadRAS(Graphic & rGraphic);
+};
+
+}
+
+//=================== Methods of RASReader ==============================
+
+RASReader::RASReader(SvStream &rRAS)
+ : m_rRAS(rRAS)
+ , mbStatus(true)
+ , mnWidth(0)
+ , mnHeight(0)
+ , mnDstBitsPerPix(0)
+ , mnDstColors(0)
+ , mnDepth(0)
+ , mnImageDatSize(0)
+ , mnType(0)
+ , mnColorMapType(0)
+ , mnColorMapSize(0)
+ , mnRepCount(0)
+ , mnRepVal(0)
+{
+}
+
+bool RASReader::ReadRAS(Graphic & rGraphic)
+{
+ sal_uInt32 nMagicNumber;
+
+ if ( m_rRAS.GetError() )
+ return false;
+
+ m_rRAS.SetEndian( SvStreamEndian::BIG );
+ m_rRAS.ReadUInt32( nMagicNumber );
+ if (!m_rRAS.good() || nMagicNumber != SUNRASTER_MAGICNUMBER)
+ return false;
+
+ // Kopf einlesen:
+
+ mbStatus = ImplReadHeader();
+ if ( !mbStatus )
+ return false;
+
+ std::vector<Color> aPalette;
+ bool bOk = true;
+
+ if ( mnDstBitsPerPix <= 8 ) // pallets pictures
+ {
+ bool bPalette(false);
+
+ if ( mnColorMapType == RAS_COLOR_RAW_MAP ) // RAW color map is skipped
+ {
+ sal_uInt64 nCurPos = m_rRAS.Tell();
+ bOk = checkSeek(m_rRAS, nCurPos + mnColorMapSize);
+ }
+ else if ( mnColorMapType == RAS_COLOR_RGB_MAP ) // we can read out the RGB
+ {
+ mnDstColors = static_cast<sal_uInt16>( mnColorMapSize / 3 );
+
+ if ( ( 1 << mnDstBitsPerPix ) < mnDstColors )
+ return false;
+
+ if ( ( mnDstColors >= 2 ) && ( ( mnColorMapSize % 3 ) == 0 ) )
+ {
+ aPalette.resize(mnDstColors);
+ sal_uInt16 i;
+ sal_uInt8 nRed[256], nGreen[256], nBlue[256];
+ for ( i = 0; i < mnDstColors; i++ ) m_rRAS.ReadUChar( nRed[ i ] );
+ for ( i = 0; i < mnDstColors; i++ ) m_rRAS.ReadUChar( nGreen[ i ] );
+ for ( i = 0; i < mnDstColors; i++ ) m_rRAS.ReadUChar( nBlue[ i ] );
+ for ( i = 0; i < mnDstColors; i++ )
+ {
+ aPalette[i] = Color(nRed[ i ], nGreen[ i ], nBlue[ i ]);
+ }
+ bPalette = true;
+ }
+ else
+ return false;
+
+ }
+ else if ( mnColorMapType != RAS_COLOR_NO_MAP ) // everything else is not standard
+ return false;
+
+ if (!bPalette)
+ {
+ mnDstColors = 1 << mnDstBitsPerPix;
+ aPalette.resize(mnDstColors);
+ for ( sal_uInt16 i = 0; i < mnDstColors; i++ )
+ {
+ sal_uInt8 nCount = 255 - ( 255 * i / ( mnDstColors - 1 ) );
+ aPalette[i] = Color(nCount, nCount, nCount);
+ }
+ }
+ }
+ else
+ {
+ if ( mnColorMapType != RAS_COLOR_NO_MAP ) // when graphic has more than 256 colors and a color map we skip
+ { // the colormap
+ sal_uInt64 nCurPos = m_rRAS.Tell();
+ bOk = checkSeek(m_rRAS, nCurPos + mnColorMapSize);
+ }
+ }
+
+ if (!bOk)
+ return false;
+
+ //The RLE packets are typically three bytes in size:
+ //The first byte is a Flag Value indicating the type of RLE packet.
+ //The second byte is the Run Count.
+ //The third byte is the Run Value.
+ //
+ //for the sake of simplicity we'll assume that RAS_TYPE_BYTE_ENCODED can
+ //describe data 255 times larger than the data stored
+ size_t nMaxCompression = mnType != RAS_TYPE_BYTE_ENCODED ? 1 : 255;
+ sal_Int32 nBitSize;
+ if (o3tl::checked_multiply<sal_Int32>(mnWidth, mnHeight, nBitSize) || o3tl::checked_multiply<sal_Int32>(nBitSize, mnDepth, nBitSize))
+ return false;
+ if (m_rRAS.remainingSize() * nMaxCompression < static_cast<sal_uInt32>(nBitSize) / 8)
+ return false;
+
+ vcl::bitmap::RawBitmap aBmp(Size(mnWidth, mnHeight), 24);
+
+ // read in the bitmap data
+ mbStatus = ImplReadBody(aBmp, aPalette);
+
+ if ( mbStatus )
+ rGraphic = vcl::bitmap::CreateFromData(std::move(aBmp));
+
+ return mbStatus;
+}
+
+bool RASReader::ImplReadHeader()
+{
+ m_rRAS.ReadInt32(mnWidth).ReadInt32(mnHeight).ReadInt32(mnDepth).ReadInt32(mnImageDatSize).ReadInt32(mnType).ReadInt32(mnColorMapType).ReadInt32(mnColorMapSize);
+
+ if (!m_rRAS.good() || mnWidth <= 0 || mnHeight <= 0 || mnImageDatSize <= 0)
+ mbStatus = false;
+
+ switch ( mnDepth )
+ {
+ case 24 :
+ case 8 :
+ case 1 :
+ mnDstBitsPerPix = static_cast<sal_uInt16>(mnDepth);
+ break;
+ case 32 :
+ mnDstBitsPerPix = 24;
+ break;
+
+ default :
+ mbStatus = false;
+ }
+
+ switch ( mnType )
+ {
+ case RAS_TYPE_OLD :
+ case RAS_TYPE_STANDARD :
+ case RAS_TYPE_RGB_FORMAT :
+ case RAS_TYPE_BYTE_ENCODED : // this type will be supported later
+ break;
+
+ default:
+ mbStatus = false;
+ }
+ return mbStatus;
+}
+
+namespace
+{
+ const Color& SanitizePaletteIndex(std::vector<Color> const & rvPalette, sal_uInt8 nIndex)
+ {
+ if (nIndex >= rvPalette.size())
+ {
+ auto nSanitizedIndex = nIndex % rvPalette.size();
+ SAL_WARN_IF(nIndex != nSanitizedIndex, "filter.ras", "invalid colormap index: "
+ << static_cast<unsigned int>(nIndex) << ", colormap len is: "
+ << rvPalette.size());
+ nIndex = nSanitizedIndex;
+ }
+ return rvPalette[nIndex];
+ }
+}
+
+bool RASReader::ImplReadBody(vcl::bitmap::RawBitmap& rBitmap, std::vector<Color> const & rvPalette)
+{
+ sal_Int32 x, y;
+ sal_uInt8 nRed, nGreen, nBlue;
+ switch ( mnDstBitsPerPix )
+ {
+ case 1 :
+ {
+ sal_uInt8 nDat = 0;
+ for (y = 0; y < mnHeight && mbStatus; ++y)
+ {
+ for (x = 0; x < mnWidth && mbStatus; ++x)
+ {
+ if (!(x & 7))
+ {
+ nDat = ImplGetByte();
+ if (!m_rRAS.good())
+ mbStatus = false;
+ }
+ rBitmap.SetPixel(y, x, SanitizePaletteIndex(rvPalette,
+ sal::static_int_cast< sal_uInt8 >(
+ nDat >> ( ( x & 7 ) ^ 7 ))));
+ }
+ if (!( ( x - 1 ) & 0x8 ) )
+ {
+ ImplGetByte(); // WORD ALIGNMENT ???
+ if (!m_rRAS.good())
+ mbStatus = false;
+ }
+ }
+ break;
+ }
+
+ case 8 :
+ for (y = 0; y < mnHeight && mbStatus; ++y)
+ {
+ for (x = 0; x < mnWidth && mbStatus; ++x)
+ {
+ sal_uInt8 nDat = ImplGetByte();
+ rBitmap.SetPixel(y, x, SanitizePaletteIndex(rvPalette, nDat));
+ if (!m_rRAS.good())
+ mbStatus = false;
+ }
+ if ( x & 1 )
+ {
+ ImplGetByte(); // WORD ALIGNMENT ???
+ if (!m_rRAS.good())
+ mbStatus = false;
+ }
+ }
+ break;
+
+ case 24 :
+ switch ( mnDepth )
+ {
+
+ case 24 :
+ for (y = 0; y < mnHeight && mbStatus; ++y)
+ {
+ for (x = 0; x < mnWidth && mbStatus; ++x)
+ {
+ if ( mnType == RAS_TYPE_RGB_FORMAT )
+ {
+ nRed = ImplGetByte();
+ nGreen = ImplGetByte();
+ nBlue = ImplGetByte();
+ }
+ else
+ {
+ nBlue = ImplGetByte();
+ nGreen = ImplGetByte();
+ nRed = ImplGetByte();
+ }
+ rBitmap.SetPixel(y, x, Color(nRed, nGreen, nBlue));
+ if (!m_rRAS.good())
+ mbStatus = false;
+ }
+ if ( x & 1 )
+ {
+ ImplGetByte(); // WORD ALIGNMENT ???
+ if (!m_rRAS.good())
+ mbStatus = false;
+ }
+ }
+ break;
+
+ case 32 :
+ for (y = 0; y < mnHeight && mbStatus; ++y)
+ {
+ for (x = 0; x < mnWidth && mbStatus; ++x)
+ {
+ ImplGetByte(); // pad byte > nil
+ if ( mnType == RAS_TYPE_RGB_FORMAT )
+ {
+ nRed = ImplGetByte();
+ nGreen = ImplGetByte();
+ nBlue = ImplGetByte();
+ }
+ else
+ {
+ nBlue = ImplGetByte();
+ nGreen = ImplGetByte();
+ nRed = ImplGetByte();
+ }
+ rBitmap.SetPixel(y, x, Color(nRed, nGreen, nBlue));
+ if (!m_rRAS.good())
+ mbStatus = false;
+ }
+ }
+ break;
+ }
+ break;
+
+ default:
+ mbStatus = false;
+ break;
+ }
+ return mbStatus;
+}
+
+sal_uInt8 RASReader::ImplGetByte()
+{
+ sal_uInt8 nRetVal(0);
+ if ( mnType != RAS_TYPE_BYTE_ENCODED )
+ {
+ m_rRAS.ReadUChar( nRetVal );
+ return nRetVal;
+ }
+ else
+ {
+ if ( mnRepCount )
+ {
+ mnRepCount--;
+ return mnRepVal;
+ }
+ else
+ {
+ m_rRAS.ReadUChar( nRetVal );
+ if ( nRetVal != 0x80 )
+ return nRetVal;
+ m_rRAS.ReadUChar( nRetVal );
+ if ( nRetVal == 0 )
+ return 0x80;
+ mnRepCount = nRetVal ;
+ m_rRAS.ReadUChar( mnRepVal );
+ return mnRepVal;
+ }
+ }
+}
+
+//================== GraphicImport - the exported function ================
+
+bool ImportRasGraphic( SvStream & rStream, Graphic & rGraphic)
+{
+ bool bRet = false;
+
+ try
+ {
+ RASReader aRASReader(rStream);
+ bRet = aRASReader.ReadRAS(rGraphic );
+ }
+ catch (...)
+ {
+ }
+
+ return bRet;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/vcl/source/filter/itga/itga.cxx b/vcl/source/filter/itga/itga.cxx
new file mode 100644
index 000000000..6b3b3037f
--- /dev/null
+++ b/vcl/source/filter/itga/itga.cxx
@@ -0,0 +1,790 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+
+#include <vcl/graph.hxx>
+#include <vcl/BitmapTools.hxx>
+#include <tools/stream.hxx>
+#include <memory>
+#include <filter/TgaReader.hxx>
+
+class FilterConfigItem;
+
+//============================ TGAReader ==================================
+
+namespace {
+
+struct TGAFileHeader
+{
+ sal_uInt8 nImageIDLength;
+ sal_uInt8 nColorMapType;
+ sal_uInt8 nImageType;
+ sal_uInt16 nColorMapFirstEntryIndex;
+ sal_uInt16 nColorMapLength;
+ sal_uInt8 nColorMapEntrySize;
+ sal_uInt16 nColorMapXOrigin;
+ sal_uInt16 nColorMapYOrigin;
+ sal_uInt16 nImageWidth;
+ sal_uInt16 nImageHeight;
+ sal_uInt8 nPixelDepth;
+ sal_uInt8 nImageDescriptor;
+};
+
+#define SizeOfTGAFileFooter 26
+
+struct TGAFileFooter
+{
+ sal_uInt32 nExtensionFileOffset;
+ sal_uInt32 nDeveloperDirectoryOffset;
+ sal_uInt32 nSignature[4];
+ sal_uInt8 nPadByte;
+ sal_uInt8 nStringTerminator;
+};
+
+#define SizeOfTGAExtension 495
+
+struct TGAExtension
+{
+ sal_uInt16 nExtensionSize;
+ char sAuthorName[41];
+ char sAuthorComment[324];
+ char sDateTimeStamp[12];
+ char sJobNameID[41];
+ char sSoftwareID[41];
+ sal_uInt16 nSoftwareVersionNumber;
+ sal_uInt8 nSoftwareVersionLetter;
+ sal_uInt32 nKeyColor;
+ sal_uInt16 nPixelAspectRatioNumerator;
+ sal_uInt16 nPixelAspectRatioDeNumerator;
+ sal_uInt16 nGammaValueNumerator;
+ sal_uInt16 nGammaValueDeNumerator;
+ sal_uInt32 nColorCorrectionOffset;
+ sal_uInt32 nPostageStampOffset;
+ sal_uInt32 nScanLineOffset;
+ sal_uInt8 nAttributesType;
+};
+
+class TGAReader {
+
+private:
+
+ SvStream& m_rTGA;
+
+ std::unique_ptr<vcl::bitmap::RawBitmap> mpBitmap;
+ std::vector<Color> mvPalette;
+ std::unique_ptr<TGAFileHeader>
+ mpFileHeader;
+ std::unique_ptr<TGAFileFooter>
+ mpFileFooter;
+ std::unique_ptr<TGAExtension>
+ mpExtension;
+ std::unique_ptr<sal_uInt32[]>
+ mpColorMap;
+
+ bool mbStatus;
+
+ sal_uInt8 mnTGAVersion; // Enhanced TGA is defined as Version 2.0
+ sal_uInt16 mnDestBitDepth;
+ bool mbIndexing; // sal_True if source contains indexing color values
+ bool mbEncoding; // sal_True if source is compressed
+
+ bool ImplReadHeader();
+ bool ImplReadPalette();
+ bool ImplReadBody();
+
+public:
+ explicit TGAReader(SvStream &rTGA);
+ bool ReadTGA(Graphic &rGraphic);
+};
+
+}
+
+//=================== Methods of TGAReader ==============================
+
+TGAReader::TGAReader(SvStream &rTGA)
+ : m_rTGA(rTGA)
+ , mbStatus(true)
+ , mnTGAVersion(1)
+ , mnDestBitDepth(8)
+ , mbIndexing(false)
+ , mbEncoding(false)
+{
+}
+
+bool TGAReader::ReadTGA(Graphic & rGraphic)
+{
+ if ( m_rTGA.GetError() )
+ return false;
+
+ m_rTGA.SetEndian( SvStreamEndian::LITTLE );
+
+ // Kopf einlesen:
+
+ if ( !m_rTGA.GetError() )
+ {
+ mbStatus = ImplReadHeader();
+ if (mbStatus)
+ mbStatus = mpFileHeader->nImageWidth && mpFileHeader->nImageHeight;
+ if (mbStatus)
+ {
+ sal_Size nSize = mpFileHeader->nImageWidth;
+ nSize *= mpFileHeader->nImageHeight;
+ if (nSize > SAL_MAX_INT32/2/3)
+ return false;
+
+ mpBitmap.reset( new vcl::bitmap::RawBitmap( Size( mpFileHeader->nImageWidth, mpFileHeader->nImageHeight ), 24 ) );
+ if ( mbIndexing )
+ mbStatus = ImplReadPalette();
+ if ( mbStatus )
+ mbStatus = ImplReadBody();
+
+ if ( mbStatus )
+ rGraphic = vcl::bitmap::CreateFromData(std::move(*mpBitmap));
+ }
+ }
+ return mbStatus;
+}
+
+
+bool TGAReader::ImplReadHeader()
+{
+ mpFileHeader.reset( new TGAFileHeader );
+
+ m_rTGA.ReadUChar( mpFileHeader->nImageIDLength ).ReadUChar( mpFileHeader->nColorMapType ).ReadUChar( mpFileHeader->nImageType ). ReadUInt16( mpFileHeader->nColorMapFirstEntryIndex ).ReadUInt16( mpFileHeader->nColorMapLength ).ReadUChar( mpFileHeader->nColorMapEntrySize ). ReadUInt16( mpFileHeader->nColorMapXOrigin ).ReadUInt16( mpFileHeader->nColorMapYOrigin ).ReadUInt16( mpFileHeader->nImageWidth ). ReadUInt16( mpFileHeader->nImageHeight ).ReadUChar( mpFileHeader->nPixelDepth ).ReadUChar( mpFileHeader->nImageDescriptor );
+
+ if ( !m_rTGA.good())
+ return false;
+
+ if ( mpFileHeader->nColorMapType > 1 )
+ return false;
+ if ( mpFileHeader->nColorMapType == 1 )
+ mbIndexing = true;
+
+ // first we want to get the version
+ mpFileFooter.reset( new TGAFileFooter ); // read the TGA-File-Footer to determine whether
+ // we got an old TGA format or the new one
+
+ sal_uInt64 nCurStreamPos = m_rTGA.Tell();
+ m_rTGA.Seek( STREAM_SEEK_TO_END );
+ sal_uInt64 nTemp = m_rTGA.Tell();
+ m_rTGA.Seek( nTemp - SizeOfTGAFileFooter );
+
+ m_rTGA.ReadUInt32( mpFileFooter->nExtensionFileOffset ).ReadUInt32( mpFileFooter->nDeveloperDirectoryOffset ). ReadUInt32( mpFileFooter->nSignature[0] ).ReadUInt32( mpFileFooter->nSignature[1] ).ReadUInt32( mpFileFooter->nSignature[2] ). ReadUInt32( mpFileFooter->nSignature[3] ).ReadUChar( mpFileFooter->nPadByte ).ReadUChar( mpFileFooter->nStringTerminator );
+
+
+ if ( !m_rTGA.good())
+ return false;
+
+ // check for sal_True, VISI, ON-X, FILE in the signatures
+ if ( mpFileFooter->nSignature[ 0 ] == (('T'<<24)|('R'<<16)|('U'<<8)|'E') &&
+ mpFileFooter->nSignature[ 1 ] == (('V'<<24)|('I'<<16)|('S'<<8)|'I') &&
+ mpFileFooter->nSignature[ 2 ] == (('O'<<24)|('N'<<16)|('-'<<8)|'X') &&
+ mpFileFooter->nSignature[ 3 ] == (('F'<<24)|('I'<<16)|('L'<<8)|'E') )
+ {
+ mpExtension.reset( new TGAExtension );
+
+ m_rTGA.Seek( mpFileFooter->nExtensionFileOffset );
+ m_rTGA.ReadUInt16( mpExtension->nExtensionSize );
+ if ( !m_rTGA.good())
+ return false;
+ if ( mpExtension->nExtensionSize >= SizeOfTGAExtension )
+ {
+ mnTGAVersion = 2;
+
+ m_rTGA.ReadBytes(mpExtension->sAuthorName, 41);
+ m_rTGA.ReadBytes(mpExtension->sAuthorComment, 324);
+ m_rTGA.ReadBytes(mpExtension->sDateTimeStamp, 12);
+ m_rTGA.ReadBytes(mpExtension->sJobNameID, 12);
+ m_rTGA.ReadChar( mpExtension->sJobNameID[ 0 ] ).ReadChar( mpExtension->sJobNameID[ 1 ] ).ReadChar( mpExtension->sJobNameID[ 2 ] );
+ m_rTGA.ReadBytes(mpExtension->sSoftwareID, 41);
+ m_rTGA.ReadUInt16( mpExtension->nSoftwareVersionNumber ).ReadUChar( mpExtension->nSoftwareVersionLetter )
+ .ReadUInt32( mpExtension->nKeyColor ).ReadUInt16( mpExtension->nPixelAspectRatioNumerator )
+ .ReadUInt16( mpExtension->nPixelAspectRatioDeNumerator ).ReadUInt16( mpExtension->nGammaValueNumerator )
+ .ReadUInt16( mpExtension->nGammaValueDeNumerator ).ReadUInt32( mpExtension->nColorCorrectionOffset )
+ .ReadUInt32( mpExtension->nPostageStampOffset ).ReadUInt32( mpExtension->nScanLineOffset )
+ .ReadUChar( mpExtension->nAttributesType );
+
+ if ( !m_rTGA.good())
+ return false;
+ }
+ }
+ m_rTGA.Seek( nCurStreamPos );
+
+ // using the TGA file specification this was the correct form but adobe photoshop sets nImageDescriptor
+ // equal to nPixelDepth
+ // mnDestBitDepth = mpFileHeader->nPixelDepth - ( mpFileHeader->nImageDescriptor & 0xf );
+ mnDestBitDepth = mpFileHeader->nPixelDepth;
+
+ if ( mnDestBitDepth == 8 ) // this is a patch for grayscale pictures not including a palette
+ mbIndexing = true;
+
+ if ( mnDestBitDepth > 32 ) // maybe the pixeldepth is invalid
+ return false;
+ else if ( mnDestBitDepth > 8 )
+ mnDestBitDepth = 24;
+ else if ( mnDestBitDepth > 4 )
+ mnDestBitDepth = 8;
+ else if ( mnDestBitDepth > 2 )
+ mnDestBitDepth = 4;
+
+ if ( !mbIndexing && ( mnDestBitDepth < 15 ) )
+ return false;
+
+ switch ( mpFileHeader->nImageType )
+ {
+ case 9 : // encoding for colortype 9, 10, 11
+ case 10 :
+ case 11 :
+ mbEncoding = true;
+ break;
+ }
+
+ if ( mpFileHeader->nImageIDLength ) // skip the Image ID
+ m_rTGA.SeekRel( mpFileHeader->nImageIDLength );
+
+ return mbStatus;
+}
+
+
+bool TGAReader::ImplReadBody()
+{
+
+ sal_uInt16 nXCount, nYCount, nRGB16;
+ sal_uInt8 nRed, nGreen, nBlue, nRunCount, nDummy, nDepth;
+
+ // this four variables match the image direction
+ tools::Long nY, nYAdd, nX, nXAdd, nXStart;
+
+ nX = nXStart = nY = 0;
+ nXCount = nYCount = 0;
+ nYAdd = nXAdd = 1;
+
+ if ( mpFileHeader->nImageDescriptor & 0x10 )
+ {
+ nX = nXStart = mpFileHeader->nImageWidth - 1;
+ nXAdd -= 2;
+ }
+
+ if ( !(mpFileHeader->nImageDescriptor & 0x20 ) )
+ {
+ nY = mpFileHeader->nImageHeight - 1;
+ nYAdd -=2;
+ }
+
+ nDepth = mpFileHeader->nPixelDepth;
+
+ if ( mbEncoding )
+ {
+ if ( mbIndexing )
+ {
+ switch( nDepth )
+ {
+ // 16 bit encoding + indexing
+ case 16 :
+ while ( nYCount < mpFileHeader->nImageHeight )
+ {
+ m_rTGA.ReadUChar( nRunCount );
+ if ( !m_rTGA.good())
+ return false;
+ if ( nRunCount & 0x80 ) // a run length packet
+ {
+ m_rTGA.ReadUInt16( nRGB16 );
+ if (!m_rTGA.good())
+ return false;
+ if ( nRGB16 >= mpFileHeader->nColorMapLength )
+ return false;
+ nRed = static_cast<sal_uInt8>( mpColorMap[ nRGB16 ] >> 16 );
+ nGreen = static_cast<sal_uInt8>( mpColorMap[ nRGB16 ] >> 8 );
+ nBlue = static_cast<sal_uInt8>( mpColorMap[ nRGB16 ] );
+ for ( sal_uInt16 i = 0; i < ( ( nRunCount & 0x7f ) + 1 ); i++ )
+ {
+ mpBitmap->SetPixel( nY, nX, Color( nRed, nGreen, nBlue ) );
+ nX += nXAdd;
+ nXCount++;
+ if ( nXCount == mpFileHeader->nImageWidth )
+ {
+ nX = nXStart;
+ nXCount = 0;
+ nY += nYAdd;
+ nYCount++;
+
+ if( nYCount >= mpFileHeader->nImageHeight )
+ break;
+ }
+ }
+ }
+ else // a raw packet
+ {
+ for ( sal_uInt16 i = 0; i < ( ( nRunCount & 0x7f ) + 1 ); i++ )
+ {
+ m_rTGA.ReadUInt16( nRGB16 );
+ if ( !m_rTGA.good())
+ return false;
+ if ( nRGB16 >= mpFileHeader->nColorMapLength )
+ return false;
+ nRed = static_cast<sal_uInt8>( mpColorMap[ nRGB16 ] >> 16 );
+ nGreen = static_cast<sal_uInt8>( mpColorMap[ nRGB16 ] >> 8 );
+ nBlue = static_cast<sal_uInt8>( mpColorMap[ nRGB16 ] );
+ if ( !m_rTGA.good())
+ return false;
+ mpBitmap->SetPixel( nY, nX, Color( nRed, nGreen, nBlue ) );
+ nX += nXAdd;
+ nXCount++;
+ if ( nXCount == mpFileHeader->nImageWidth )
+ {
+ nX = nXStart;
+ nXCount = 0;
+ nY += nYAdd;
+ nYCount++;
+
+ if( nYCount >= mpFileHeader->nImageHeight )
+ break;
+ }
+ }
+ }
+ }
+ break;
+
+ // 8 bit encoding + indexing
+ case 8 :
+ while ( nYCount < mpFileHeader->nImageHeight )
+ {
+ m_rTGA.ReadUChar( nRunCount );
+ if ( !m_rTGA.good())
+ return false;
+ if ( nRunCount & 0x80 ) // a run length packet
+ {
+ m_rTGA.ReadUChar( nDummy );
+ if ( !m_rTGA.good())
+ return false;
+ if ( nDummy >= mpFileHeader->nColorMapLength )
+ return false;
+ for ( sal_uInt16 i = 0; i < ( ( nRunCount & 0x7f ) + 1 ); i++ )
+ {
+ mpBitmap->SetPixel( nY, nX, mvPalette[nDummy] );
+ nX += nXAdd;
+ nXCount++;
+ if ( nXCount == mpFileHeader->nImageWidth )
+ {
+ nX = nXStart;
+ nXCount = 0;
+ nY += nYAdd;
+ nYCount++;
+
+ if( nYCount >= mpFileHeader->nImageHeight )
+ break;
+ }
+ }
+ }
+ else // a raw packet
+ {
+ for ( sal_uInt16 i = 0; i < ( ( nRunCount & 0x7f ) + 1 ); i++ )
+ {
+ m_rTGA.ReadUChar( nDummy );
+ if ( !m_rTGA.good())
+ return false;
+ if ( nDummy >= mpFileHeader->nColorMapLength )
+ return false;
+ mpBitmap->SetPixel( nY, nX, mvPalette[nDummy] );
+ nX += nXAdd;
+ nXCount++;
+ if ( nXCount == mpFileHeader->nImageWidth )
+ {
+ nX = nXStart;
+ nXCount = 0;
+ nY += nYAdd;
+ nYCount++;
+
+ if( nYCount >= mpFileHeader->nImageHeight )
+ break;
+ }
+ }
+ }
+ }
+ break;
+ default:
+ return false;
+ }
+ }
+ else
+ {
+ switch( nDepth )
+ {
+ // 32 bit transparent true color encoding
+ case 32 :
+ {
+ while ( nYCount < mpFileHeader->nImageHeight )
+ {
+ m_rTGA.ReadUChar( nRunCount );
+ if ( !m_rTGA.good())
+ return false;
+ if ( nRunCount & 0x80 ) // a run length packet
+ {
+ m_rTGA.ReadUChar( nBlue ).ReadUChar( nGreen ).ReadUChar( nRed ).ReadUChar( nDummy );
+ if ( !m_rTGA.good())
+ return false;
+ for ( sal_uInt16 i = 0; i < ( ( nRunCount & 0x7f ) + 1 ); i++ )
+ {
+ mpBitmap->SetPixel( nY, nX, Color( nRed, nGreen, nBlue ) );
+ nX += nXAdd;
+ nXCount++;
+ if ( nXCount == mpFileHeader->nImageWidth )
+ {
+ nX = nXStart;
+ nXCount = 0;
+ nY += nYAdd;
+ nYCount++;
+
+ if( nYCount >= mpFileHeader->nImageHeight )
+ break;
+ }
+ }
+ }
+ else // a raw packet
+ {
+ for ( sal_uInt16 i = 0; i < ( ( nRunCount & 0x7f ) + 1 ); i++ )
+ {
+ m_rTGA.ReadUChar( nBlue ).ReadUChar( nGreen ).ReadUChar( nRed ).ReadUChar( nDummy );
+ if ( !m_rTGA.good())
+ return false;
+ mpBitmap->SetPixel( nY, nX, Color( nRed, nGreen, nBlue ) );
+ nX += nXAdd;
+ nXCount++;
+ if ( nXCount == mpFileHeader->nImageWidth )
+ {
+ nX = nXStart;
+ nXCount = 0;
+ nY += nYAdd;
+ nYCount++;
+
+ if( nYCount >= mpFileHeader->nImageHeight )
+ break;
+ }
+ }
+ }
+ }
+ }
+ break;
+
+ // 24 bit true color encoding
+ case 24 :
+ while ( nYCount < mpFileHeader->nImageHeight )
+ {
+ m_rTGA.ReadUChar( nRunCount );
+ if ( !m_rTGA.good())
+ return false;
+ if ( nRunCount & 0x80 ) // a run length packet
+ {
+ m_rTGA.ReadUChar( nBlue ).ReadUChar( nGreen ).ReadUChar( nRed );
+ if ( !m_rTGA.good())
+ return false;
+ for ( sal_uInt16 i = 0; i < ( ( nRunCount & 0x7f ) + 1 ); i++ )
+ {
+ mpBitmap->SetPixel( nY, nX, Color( nRed, nGreen, nBlue ) );
+ nX += nXAdd;
+ nXCount++;
+ if ( nXCount == mpFileHeader->nImageWidth )
+ {
+ nX = nXStart;
+ nXCount = 0;
+ nY += nYAdd;
+ nYCount++;
+
+ if( nYCount >= mpFileHeader->nImageHeight )
+ break;
+ }
+ }
+ }
+ else // a raw packet
+ {
+ for ( sal_uInt16 i = 0; i < ( ( nRunCount & 0x7f ) + 1 ); i++ )
+ {
+ m_rTGA.ReadUChar( nBlue ).ReadUChar( nGreen ).ReadUChar( nRed );
+ if ( !m_rTGA.good())
+ return false;
+ mpBitmap->SetPixel( nY, nX, Color( nRed, nGreen, nBlue ) );
+ nX += nXAdd;
+ nXCount++;
+ if ( nXCount == mpFileHeader->nImageWidth )
+ {
+ nX = nXStart;
+ nXCount = 0;
+ nY += nYAdd;
+ nYCount++;
+
+ if( nYCount >= mpFileHeader->nImageHeight )
+ break;
+ }
+ }
+ }
+ }
+ break;
+
+ // 16 bit true color encoding
+ case 16 :
+ while ( nYCount < mpFileHeader->nImageHeight )
+ {
+ m_rTGA.ReadUChar( nRunCount );
+ if ( !m_rTGA.good())
+ return false;
+ if ( nRunCount & 0x80 ) // a run length packet
+ {
+ m_rTGA.ReadUInt16( nRGB16 );
+ if ( !m_rTGA.good())
+ return false;
+ nRed = static_cast<sal_uInt8>( nRGB16 >> 7 ) & 0xf8;
+ nGreen = static_cast<sal_uInt8>( nRGB16 >> 2 ) & 0xf8;
+ nBlue = static_cast<sal_uInt8>( nRGB16 << 3 ) & 0xf8;
+ for ( sal_uInt16 i = 0; i < ( ( nRunCount & 0x7f ) + 1 ); i++ )
+ {
+ mpBitmap->SetPixel( nY, nX, Color( nRed, nGreen, nBlue ) );
+ nX += nXAdd;
+ nXCount++;
+ if ( nXCount == mpFileHeader->nImageWidth )
+ {
+ nX = nXStart;
+ nXCount = 0;
+ nY += nYAdd;
+ nYCount++;
+
+ if( nYCount >= mpFileHeader->nImageHeight )
+ break;
+ }
+ }
+ }
+ else // a raw packet
+ {
+ for ( sal_uInt16 i = 0; i < ( ( nRunCount & 0x7f ) + 1 ); i++ )
+ {
+ m_rTGA.ReadUInt16( nRGB16 );
+ if ( !m_rTGA.good())
+ return false;
+ nRed = static_cast<sal_uInt8>( nRGB16 >> 7 ) & 0xf8;
+ nGreen = static_cast<sal_uInt8>( nRGB16 >> 2 ) & 0xf8;
+ nBlue = static_cast<sal_uInt8>( nRGB16 << 3 ) & 0xf8;
+ mpBitmap->SetPixel( nY, nX, Color( nRed, nGreen, nBlue ) );
+ nX += nXAdd;
+ nXCount++;
+ if ( nXCount == mpFileHeader->nImageWidth )
+ {
+ nX = nXStart;
+ nXCount = 0;
+ nY += nYAdd;
+ nYCount++;
+
+ if( nYCount >= mpFileHeader->nImageHeight )
+ break;
+ }
+ }
+ }
+ }
+ break;
+
+ default:
+ return false;
+ }
+ }
+ }
+ else
+ {
+ for ( nYCount = 0; nYCount < mpFileHeader->nImageHeight; nYCount++, nY += nYAdd )
+ {
+ nX = nXStart;
+ nXCount = 0;
+
+ if ( mbIndexing )
+ {
+ switch( nDepth )
+ {
+ // 16 bit indexing
+ case 16 :
+ for (;nXCount < mpFileHeader->nImageWidth; nXCount++, nX += nXAdd )
+ {
+ m_rTGA.ReadUInt16( nRGB16 );
+ if ( !m_rTGA.good())
+ return false;
+ if ( nRGB16 >= mpFileHeader->nColorMapLength )
+ return false;
+ nRed = static_cast<sal_uInt8>( mpColorMap[ nRGB16 ] >> 16 );
+ nGreen = static_cast<sal_uInt8>( mpColorMap[ nRGB16 ] >> 8 );
+ nBlue = static_cast<sal_uInt8>( mpColorMap[ nRGB16 ] );
+ mpBitmap->SetPixel( nY, nX, Color( nRed, nGreen, nBlue ) );
+ }
+ break;
+
+ // 8 bit indexing
+ case 8 :
+ for (;nXCount < mpFileHeader->nImageWidth; nXCount++, nX += nXAdd )
+ {
+ m_rTGA.ReadUChar( nDummy );
+ if ( !m_rTGA.good())
+ return false;
+ if ( nDummy >= mpFileHeader->nColorMapLength )
+ return false;
+ mpBitmap->SetPixel( nY, nX, Color(ColorTransparency, nDummy) );
+ }
+ break;
+ default:
+ return false;
+ }
+ }
+ else
+ {
+ switch( nDepth )
+ {
+ // 32 bit true color
+ case 32 :
+ {
+ for (;nXCount < mpFileHeader->nImageWidth; nXCount++, nX += nXAdd )
+ {
+ m_rTGA.ReadUChar( nBlue ).ReadUChar( nGreen ).ReadUChar( nRed ).ReadUChar( nDummy );
+ if ( !m_rTGA.good())
+ return false;
+ mpBitmap->SetPixel( nY, nX, Color( nRed, nGreen, nBlue ) );
+ }
+ }
+ break;
+
+ // 24 bit true color
+ case 24 :
+ for (;nXCount < mpFileHeader->nImageWidth; nXCount++, nX += nXAdd )
+ {
+ m_rTGA.ReadUChar( nBlue ).ReadUChar( nGreen ).ReadUChar( nRed );
+ if ( !m_rTGA.good())
+ return false;
+ mpBitmap->SetPixel( nY, nX, Color( nRed, nGreen, nBlue ) );
+ }
+ break;
+
+ // 16 bit true color
+ case 16 :
+ for (;nXCount < mpFileHeader->nImageWidth; nXCount++, nX += nXAdd )
+ {
+ m_rTGA.ReadUInt16( nRGB16 );
+ if ( !m_rTGA.good())
+ return false;
+ nRed = static_cast<sal_uInt8>( nRGB16 >> 7 ) & 0xf8;
+ nGreen = static_cast<sal_uInt8>( nRGB16 >> 2 ) & 0xf8;
+ nBlue = static_cast<sal_uInt8>( nRGB16 << 3 ) & 0xf8;
+ mpBitmap->SetPixel( nY, nX, Color( nRed, nGreen, nBlue ) );
+ }
+ break;
+ default:
+ return false;
+ }
+ }
+ }
+ }
+ return mbStatus;
+}
+
+
+bool TGAReader::ImplReadPalette()
+{
+ if ( mbIndexing ) // read the colormap
+ {
+ sal_uInt16 nColors = mpFileHeader->nColorMapLength;
+
+ if ( !nColors ) // colors == 0 ? -> we will build a grayscale palette
+ {
+ if ( mpFileHeader->nPixelDepth != 8 )
+ return false;
+ nColors = 256;
+ mpFileHeader->nColorMapLength = 256;
+ mpFileHeader->nColorMapEntrySize = 0x3f; // patch for the following switch routine
+ }
+ mpColorMap.reset( new sal_uInt32[ nColors ] ); // we will always index dwords
+
+ switch( mpFileHeader->nColorMapEntrySize )
+ {
+ case 0x3f :
+ {
+ for (sal_uInt32 i = 0; i < nColors; ++i)
+ {
+ mpColorMap[ i ] = ( i << 16 ) + ( i << 8 ) + i;
+ }
+ }
+ break;
+
+ case 32 :
+ for (sal_uInt16 i = 0; i < nColors; i++)
+ {
+ m_rTGA.ReadUInt32(mpColorMap[i]);
+ }
+ break;
+
+ case 24 :
+ {
+ for ( sal_uInt16 i = 0; i < nColors; i++ )
+ {
+ sal_uInt8 nBlue;
+ sal_uInt8 nGreen;
+ sal_uInt8 nRed;
+ m_rTGA.ReadUChar(nBlue).ReadUChar(nGreen).ReadUChar(nRed);
+ mpColorMap[i] = (nRed << 16) | (nGreen << 8) | nBlue;
+ }
+ }
+ break;
+
+ case 15 :
+ case 16 :
+ {
+ for ( sal_uInt16 i = 0; i < nColors; i++ )
+ {
+ sal_uInt16 nTemp;
+ m_rTGA.ReadUInt16( nTemp );
+ if ( !m_rTGA.good() )
+ return false;
+ mpColorMap[ i ] = ( ( nTemp & 0x7c00 ) << 9 ) + ( ( nTemp & 0x01e0 ) << 6 ) +
+ ( ( nTemp & 0x1f ) << 3 );
+ }
+ }
+ break;
+
+ default :
+ return false;
+ }
+ if ( mnDestBitDepth <= 8 )
+ {
+ sal_uInt16 nDestColors = ( 1 << mnDestBitDepth );
+ if ( nColors > nDestColors )
+ return false;
+
+ mvPalette.resize( nColors );
+ for ( sal_uInt16 i = 0; i < nColors; i++ )
+ {
+ mvPalette[i] = Color( static_cast<sal_uInt8>( mpColorMap[ i ] >> 16 ),
+ static_cast<sal_uInt8>( mpColorMap[ i ] >> 8 ), static_cast<sal_uInt8>(mpColorMap[ i ] ) );
+ }
+ }
+ }
+
+ return mbStatus;
+}
+
+//================== GraphicImport - the exported function ================
+
+bool ImportTgaGraphic(SvStream & rStream, Graphic & rGraphic)
+{
+ TGAReader aTGAReader(rStream);
+
+ return aTGAReader.ReadTGA(rGraphic);
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/vcl/source/filter/itiff/itiff.cxx b/vcl/source/filter/itiff/itiff.cxx
new file mode 100644
index 000000000..e6d243b57
--- /dev/null
+++ b/vcl/source/filter/itiff/itiff.cxx
@@ -0,0 +1,294 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <sal/config.h>
+#include <sal/log.hxx>
+
+#include <comphelper/scopeguard.hxx>
+#include <vcl/graph.hxx>
+#include <vcl/BitmapTools.hxx>
+#include <vcl/animate/Animation.hxx>
+#include <bitmap/BitmapWriteAccess.hxx>
+#include <tools/fract.hxx>
+#include <tools/stream.hxx>
+#include <unotools/configmgr.hxx>
+
+#include <tiffio.h>
+
+#include <filter/TiffReader.hxx>
+
+namespace
+{
+ struct Context
+ {
+ SvStream& rStream;
+ tsize_t nSize;
+ int nShortReads;
+ Context(SvStream& rInStream, tsize_t nInSize)
+ : rStream(rInStream)
+ , nSize(nInSize)
+ , nShortReads(0)
+ {
+ }
+ };
+}
+
+static tsize_t tiff_read(thandle_t handle, tdata_t buf, tsize_t size)
+{
+ Context* pContext = static_cast<Context*>(handle);
+ tsize_t nRead = pContext->rStream.ReadBytes(buf, size);
+ // tdf#149417 allow one short read, which is similar to what
+ // we do for jpeg since tdf#138950
+ if (nRead < size && !pContext->nShortReads)
+ {
+ memset(static_cast<char*>(buf) + nRead, 0, size - nRead);
+ ++pContext->nShortReads;
+ return size;
+ }
+ return nRead;
+}
+
+static tsize_t tiff_write(thandle_t, tdata_t, tsize_t)
+{
+ return -1;
+}
+
+static toff_t tiff_seek(thandle_t handle, toff_t offset, int whence)
+{
+ Context* pContext = static_cast<Context*>(handle);
+
+ switch (whence)
+ {
+ case SEEK_SET:
+ pContext->rStream.Seek(offset);
+ break;
+ case SEEK_CUR:
+ pContext->rStream.SeekRel(offset);
+ break;
+ case SEEK_END:
+ pContext->rStream.Seek(STREAM_SEEK_TO_END);
+ pContext->rStream.SeekRel(offset);
+ break;
+ default:
+ assert(false && "unknown seek type");
+ break;
+ }
+
+ return pContext->rStream.Tell();
+}
+
+static int tiff_close(thandle_t)
+{
+ return 0;
+}
+
+static toff_t tiff_size(thandle_t handle)
+{
+ Context* pContext = static_cast<Context*>(handle);
+ return pContext->nSize;
+}
+
+bool ImportTiffGraphicImport(SvStream& rTIFF, Graphic& rGraphic)
+{
+ auto origErrorHandler = TIFFSetErrorHandler(nullptr);
+ auto origWarningHandler = TIFFSetWarningHandler(nullptr);
+ comphelper::ScopeGuard restoreDefaultHandlers([&]() {
+ TIFFSetErrorHandler(origErrorHandler);
+ TIFFSetWarningHandler(origWarningHandler);
+ });
+
+ Context aContext(rTIFF, rTIFF.remainingSize());
+ TIFF* tif = TIFFClientOpen("libtiff-svstream", "r", &aContext,
+ tiff_read, tiff_write,
+ tiff_seek, tiff_close,
+ tiff_size, nullptr, nullptr);
+
+ if (!tif)
+ return false;
+
+ const auto nOrigPos = rTIFF.Tell();
+
+ Animation aAnimation;
+
+ do
+ {
+ uint32_t w, h;
+
+ if (TIFFGetField(tif, TIFFTAG_IMAGEWIDTH, &w) != 1)
+ {
+ SAL_WARN("filter.tiff", "missing width");
+ break;
+ }
+
+ if (TIFFGetField(tif, TIFFTAG_IMAGELENGTH, &h) != 1)
+ {
+ SAL_WARN("filter.tiff", "missing height");
+ break;
+ }
+
+ if (w > SAL_MAX_INT32 / 32 || h > SAL_MAX_INT32 / 32)
+ {
+ SAL_WARN("filter.tiff", "image too large");
+ break;
+ }
+
+ if (utl::ConfigManager::IsFuzzing())
+ {
+ const uint64_t MAX_SIZE = 500000000;
+ if (TIFFTileSize64(tif) > MAX_SIZE)
+ {
+ SAL_WARN("filter.tiff", "skipping large tiffs");
+ break;
+ }
+ }
+
+ uint32_t nPixelsRequired;
+ constexpr size_t nMaxPixelsAllowed = SAL_MAX_INT32/4;
+ // two buffers currently required, so limit further
+ bool bOk = !o3tl::checked_multiply(w, h, nPixelsRequired) && nPixelsRequired <= nMaxPixelsAllowed / 2;
+ if (!bOk)
+ {
+ SAL_WARN("filter.tiff", "skipping oversized tiff image " << w << " x " << h);
+ break;
+ }
+
+ std::vector<uint32_t> raster(nPixelsRequired);
+ if (TIFFReadRGBAImageOriented(tif, w, h, raster.data(), ORIENTATION_TOPLEFT, 1))
+ {
+ Bitmap bitmap(Size(w, h), vcl::PixelFormat::N24_BPP);
+ BitmapScopedWriteAccess access(bitmap);
+ if (!access)
+ {
+ SAL_WARN("filter.tiff", "cannot create image " << w << " x " << h);
+ break;
+ }
+
+ AlphaMask bitmapAlpha(Size(w, h));
+ AlphaScopedWriteAccess accessAlpha(bitmapAlpha);
+ if (!accessAlpha)
+ {
+ SAL_WARN("filter.tiff", "cannot create alpha " << w << " x " << h);
+ break;
+ }
+
+ /*
+ ORIENTATION_TOPLEFT = 1
+ ORIENTATION_TOPRIGHT = 2
+ ORIENTATION_BOTRIGHT = 3
+ ORIENTATION_BOTLEFT = 4
+ ORIENTATION_LEFTTOP = 5
+ ORIENTATION_RIGHTTOP = 6
+ ORIENTATION_RIGHTBOT = 7
+ ORIENTATION_LEFTBOT = 8
+ */
+ uint16_t nOrientation;
+ if (TIFFGetField(tif, TIFFTAG_ORIENTATION, &nOrientation) != 1)
+ nOrientation = 0;
+
+ for (uint32_t y = 0; y < h; ++y)
+ {
+ const uint32_t* src = raster.data() + w * y;
+ for (uint32_t x = 0; x < w; ++x)
+ {
+ sal_uInt8 r = TIFFGetR(*src);
+ sal_uInt8 g = TIFFGetG(*src);
+ sal_uInt8 b = TIFFGetB(*src);
+ sal_uInt8 a = TIFFGetA(*src);
+
+ uint32_t dest;
+ switch (nOrientation)
+ {
+ case ORIENTATION_LEFTBOT:
+ dest = w - 1 - x;
+ break;
+ default:
+ dest = x;
+ break;
+ }
+
+ access->SetPixel(y, dest, Color(r, g, b));
+ accessAlpha->SetPixelIndex(y, dest, 255 - a);
+ ++src;
+ }
+ }
+
+ raster.clear();
+
+ access.reset();
+ accessAlpha.reset();
+
+ BitmapEx aBitmapEx(bitmap, bitmapAlpha);
+
+ switch (nOrientation)
+ {
+ case ORIENTATION_LEFTBOT:
+ aBitmapEx.Rotate(2700_deg10, COL_BLACK);
+ break;
+ default:
+ break;
+ }
+
+ MapMode aMapMode;
+ uint16_t ResolutionUnit = RESUNIT_NONE;
+ if (TIFFGetField(tif, TIFFTAG_RESOLUTIONUNIT, &ResolutionUnit) == 1 && ResolutionUnit != RESUNIT_NONE)
+ {
+ float xres = 0, yres = 0;
+
+ if (TIFFGetField(tif, TIFFTAG_XRESOLUTION, &xres) == 1 &&
+ TIFFGetField(tif, TIFFTAG_YRESOLUTION, &yres) == 1 &&
+ xres != 0 && yres != 0)
+ {
+ if (ResolutionUnit == RESUNIT_INCH)
+ aMapMode = MapMode(MapUnit::MapInch, Point(0,0), Fraction(1/xres), Fraction(1/yres));
+ else if (ResolutionUnit == RESUNIT_CENTIMETER)
+ aMapMode = MapMode(MapUnit::MapCM, Point(0,0), Fraction(1/xres), Fraction(1/yres));
+ }
+ }
+ aBitmapEx.SetPrefMapMode(aMapMode);
+ aBitmapEx.SetPrefSize(Size(w, h));
+
+ AnimationBitmap aAnimationBitmap(aBitmapEx, Point(0, 0), aBitmapEx.GetSizePixel(),
+ ANIMATION_TIMEOUT_ON_CLICK, Disposal::Back);
+ aAnimation.Insert(aAnimationBitmap);
+ }
+ else
+ break;
+ } while (TIFFReadDirectory(tif));
+
+ TIFFClose(tif);
+
+ const auto nImages = aAnimation.Count();
+ if (nImages)
+ {
+ if (nImages == 1)
+ rGraphic = aAnimation.GetBitmapEx();
+ else
+ rGraphic = aAnimation;
+
+ // seek to end of TIFF if succeeded
+ rTIFF.Seek(STREAM_SEEK_TO_END);
+
+ return true;
+ }
+
+ rTIFF.Seek(nOrigPos);
+ return false;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/vcl/source/filter/ixbm/xbmread.cxx b/vcl/source/filter/ixbm/xbmread.cxx
new file mode 100644
index 000000000..9368f9f9a
--- /dev/null
+++ b/vcl/source/filter/ixbm/xbmread.cxx
@@ -0,0 +1,396 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <memory>
+#include <sal/config.h>
+#include <tools/stream.hxx>
+
+#include <rtl/character.hxx>
+
+#include <bitmap/BitmapWriteAccess.hxx>
+#include <graphic/GraphicReader.hxx>
+
+#include "xbmread.hxx"
+
+namespace {
+
+enum XBMFormat
+{
+ XBM10,
+ XBM11
+};
+
+enum ReadState
+{
+ XBMREAD_OK,
+ XBMREAD_ERROR,
+ XBMREAD_NEED_MORE
+};
+
+class XBMReader : public GraphicReader
+{
+ SvStream& rIStm;
+ Bitmap aBmp1;
+ BitmapScopedWriteAccess pAcc1;
+ std::unique_ptr<short[]>
+ pHexTable;
+ BitmapColor aWhite;
+ BitmapColor aBlack;
+ tools::Long nLastPos;
+ tools::Long nWidth;
+ tools::Long nHeight;
+ bool bStatus;
+
+ void InitTable();
+ OString FindTokenLine( SvStream* pInStm, const char* pTok1, const char* pTok2 );
+ int ParseDefine( const char* pDefine );
+ void ParseData( SvStream* pInStm, const OString& aLastLine, XBMFormat eFormat );
+
+public:
+
+ explicit XBMReader( SvStream& rStm );
+
+ ReadState ReadXBM( Graphic& rGraphic );
+};
+
+}
+
+XBMReader::XBMReader( SvStream& rStm ) :
+ rIStm ( rStm ),
+ nLastPos ( rStm.Tell() ),
+ nWidth ( 0 ),
+ nHeight ( 0 ),
+ bStatus ( true )
+{
+ pHexTable.reset( new short[ 256 ] );
+ maUpperName = "SVIXBM";
+ InitTable();
+}
+
+void XBMReader::InitTable()
+{
+ memset( pHexTable.get(), 0, sizeof( short ) * 256 );
+
+ pHexTable[int('0')] = 0;
+ pHexTable[int('1')] = 1;
+ pHexTable[int('2')] = 2;
+ pHexTable[int('3')] = 3;
+ pHexTable[int('4')] = 4;
+ pHexTable[int('5')] = 5;
+ pHexTable[int('6')] = 6;
+ pHexTable[int('7')] = 7;
+ pHexTable[int('8')] = 8;
+ pHexTable[int('9')] = 9;
+ pHexTable[int('A')] = 10;
+ pHexTable[int('B')] = 11;
+ pHexTable[int('C')] = 12;
+ pHexTable[int('D')] = 13;
+ pHexTable[int('E')] = 14;
+ pHexTable[int('F')] = 15;
+ pHexTable[int('X')] = 0;
+ pHexTable[int('a')] = 10;
+ pHexTable[int('b')] = 11;
+ pHexTable[int('c')] = 12;
+ pHexTable[int('d')] = 13;
+ pHexTable[int('e')] = 14;
+ pHexTable[int('f')] = 15;
+ pHexTable[int('x')] = 0;
+ pHexTable[int(' ')] = -1;
+ pHexTable[int(',')] = -1;
+ pHexTable[int('}')] = -1;
+ pHexTable[int('\n')] = -1;
+ pHexTable[int('\t')] = -1;
+ pHexTable[int('\0')] = -1;
+}
+
+OString XBMReader::FindTokenLine( SvStream* pInStm, const char* pTok1,
+ const char* pTok2 )
+{
+ OString aRet;
+ sal_Int32 nPos1, nPos2;
+
+ bStatus = false;
+
+ do
+ {
+ if( !pInStm->ReadLine( aRet ) )
+ break;
+
+ if( pTok1 )
+ {
+ if( ( nPos1 = aRet.indexOf( pTok1 ) ) != -1 )
+ {
+ bStatus = true;
+
+ if( pTok2 )
+ {
+ bStatus = false;
+
+ nPos2 = aRet.indexOf( pTok2 );
+ if( ( nPos2 != -1 ) && ( nPos2 > nPos1 ) )
+ {
+ bStatus = true;
+ }
+ }
+ }
+ }
+ }
+ while( !bStatus );
+
+ return aRet;
+}
+
+int XBMReader::ParseDefine( const char* pDefine )
+{
+ sal_Int32 nRet = 0;
+ const char* pTmp = pDefine;
+ unsigned char cTmp;
+
+ // move to end
+ pTmp += ( strlen( pDefine ) - 1 );
+ cTmp = *pTmp--;
+
+ // search last digit
+ while (pHexTable[ cTmp ] == -1 && pTmp >= pDefine)
+ cTmp = *pTmp--;
+
+ // move before number
+ while (pHexTable[ cTmp ] != -1 && pTmp >= pDefine)
+ cTmp = *pTmp--;
+
+ // move to start of number
+ pTmp += 2;
+
+ // read Hex
+ if( ( pTmp[0] == '0' ) && ( ( pTmp[1] == 'X' ) || ( pTmp[1] == 'x' ) ) )
+ {
+ pTmp += 2;
+ nRet = OString(pTmp, strlen(pTmp)).toInt32(16);
+ }
+ else // read decimal
+ {
+ nRet = OString(pTmp, strlen(pTmp)).toInt32();
+ }
+
+ return nRet;
+}
+
+void XBMReader::ParseData( SvStream* pInStm, const OString& aLastLine, XBMFormat eFormat )
+{
+ OString aLine;
+ tools::Long nRow = 0;
+ tools::Long nCol = 0;
+ tools::Long nBits = ( eFormat == XBM10 ) ? 16 : 8;
+ tools::Long nBit;
+ sal_uInt16 nValue;
+ sal_uInt16 nDigits;
+ bool bFirstLine = true;
+
+ while( nRow < nHeight )
+ {
+ if( bFirstLine )
+ {
+ sal_Int32 nPos;
+
+ // delete opening curly bracket
+ aLine = aLastLine;
+ nPos = aLine.indexOf('{');
+ if( nPos != -1 )
+ aLine = aLine.copy(nPos + 1);
+
+ bFirstLine = false;
+ }
+ else if( !pInStm->ReadLine( aLine ) )
+ break;
+
+ if (!aLine.isEmpty())
+ {
+ sal_Int32 nIndex = 0;
+ const sal_Int32 nLen {aLine.getLength()};
+ while (nRow<nHeight && nIndex<nLen)
+ {
+ bool bProcessed = false;
+
+ nBit = nDigits = nValue = 0;
+
+ while (nIndex<nLen)
+ {
+ const unsigned char cChar = aLine[nIndex];
+
+ ++nIndex;
+ if (cChar==',') // sequence completed, ',' already skipped for next loop
+ break;
+
+ const short nTable = pHexTable[ cChar ];
+
+ if( rtl::isAsciiHexDigit( cChar ) || !nTable )
+ {
+ nValue = ( nValue << 4 ) + nTable;
+ nDigits++;
+ bProcessed = true;
+ }
+ else if( ( nTable < 0 ) && nDigits )
+ {
+ bProcessed = true;
+ break;
+ }
+ }
+
+ if( bProcessed )
+ {
+ Scanline pScanline = pAcc1->GetScanline(nRow);
+ while( ( nCol < nWidth ) && ( nBit < nBits ) )
+ pAcc1->SetPixelOnData(pScanline, nCol++, ( nValue & ( 1 << nBit++ ) ) ? aBlack : aWhite);
+
+ if( nCol == nWidth )
+ {
+ nCol = 0;
+ nRow++;
+ }
+ }
+ }
+ }
+ }
+}
+
+ReadState XBMReader::ReadXBM( Graphic& rGraphic )
+{
+ ReadState eReadState;
+ sal_uInt8 cDummy;
+
+ // check if we can read ALL
+ rIStm.Seek( STREAM_SEEK_TO_END );
+ rIStm.ReadUChar( cDummy );
+
+ // if we cannot read all
+ // we return and wait for new data
+ if ( rIStm.GetError() != ERRCODE_IO_PENDING )
+ {
+ rIStm.Seek( nLastPos );
+ bStatus = false;
+ OString aLine = FindTokenLine( &rIStm, "#define", "_width" );
+
+ if ( bStatus )
+ {
+ int nValue;
+ if ( ( nValue = ParseDefine( aLine.getStr() ) ) > 0 )
+ {
+ nWidth = nValue;
+ aLine = FindTokenLine( &rIStm, "#define", "_height" );
+
+ // if height was not received, we search again
+ // from start of the file
+ if ( !bStatus )
+ {
+ rIStm.Seek( nLastPos );
+ aLine = FindTokenLine( &rIStm, "#define", "_height" );
+ }
+ }
+ else
+ bStatus = false;
+
+ if ( bStatus )
+ {
+ if ( ( nValue = ParseDefine( aLine.getStr() ) ) > 0 )
+ {
+ nHeight = nValue;
+ aLine = FindTokenLine( &rIStm, "static", "_bits" );
+
+ if ( bStatus )
+ {
+ XBMFormat eFormat = XBM10;
+
+ if (aLine.indexOf("short") != -1)
+ eFormat = XBM10;
+ else if (aLine.indexOf("char") != -1)
+ eFormat = XBM11;
+ else
+ bStatus = false;
+
+ //xbms are a minimum of one character per 8 pixels, so if the file isn't
+ //even that long, it's not all there
+ if (rIStm.remainingSize() < (static_cast<sal_uInt64>(nWidth) * nHeight) / 8)
+ bStatus = false;
+
+ if ( bStatus && nWidth && nHeight )
+ {
+ aBmp1 = Bitmap(Size(nWidth, nHeight), vcl::PixelFormat::N1_BPP);
+ pAcc1 = BitmapScopedWriteAccess(aBmp1);
+
+ if( pAcc1 )
+ {
+ aWhite = pAcc1->GetBestMatchingColor( COL_WHITE );
+ aBlack = pAcc1->GetBestMatchingColor( COL_BLACK );
+ ParseData( &rIStm, aLine, eFormat );
+ }
+ else
+ bStatus = false;
+ }
+ }
+ }
+ }
+ }
+
+ if (bStatus && pAcc1)
+ {
+ Bitmap aBlackBmp(Size(pAcc1->Width(), pAcc1->Height()), vcl::PixelFormat::N1_BPP);
+
+ pAcc1.reset();
+ aBlackBmp.Erase( COL_BLACK );
+ rGraphic = BitmapEx( aBlackBmp, aBmp1 );
+ eReadState = XBMREAD_OK;
+ }
+ else
+ eReadState = XBMREAD_ERROR;
+ }
+ else
+ {
+ rIStm.ResetError();
+ eReadState = XBMREAD_NEED_MORE;
+ }
+
+ return eReadState;
+}
+
+VCL_DLLPUBLIC bool ImportXBM( SvStream& rStm, Graphic& rGraphic )
+{
+ std::shared_ptr<GraphicReader> pContext = rGraphic.GetReaderContext();
+ rGraphic.SetReaderContext(nullptr);
+ XBMReader* pXBMReader = dynamic_cast<XBMReader*>( pContext.get() );
+ if (!pXBMReader)
+ {
+ pContext = std::make_shared<XBMReader>( rStm );
+ pXBMReader = static_cast<XBMReader*>( pContext.get() );
+ }
+
+ bool bRet = true;
+
+ ReadState eReadState = pXBMReader->ReadXBM( rGraphic );
+
+ if( eReadState == XBMREAD_ERROR )
+ {
+ bRet = false;
+ }
+ else if( eReadState == XBMREAD_NEED_MORE )
+ rGraphic.SetReaderContext( pContext );
+
+ return bRet;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/vcl/source/filter/ixbm/xbmread.hxx b/vcl/source/filter/ixbm/xbmread.hxx
new file mode 100644
index 000000000..bee3eadc9
--- /dev/null
+++ b/vcl/source/filter/ixbm/xbmread.hxx
@@ -0,0 +1,29 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#ifndef INCLUDED_VCL_SOURCE_FILTER_IXBM_XBMREAD_HXX
+#define INCLUDED_VCL_SOURCE_FILTER_IXBM_XBMREAD_HXX
+
+#include <vcl/graph.hxx>
+
+VCL_DLLPUBLIC bool ImportXBM(SvStream& rStream, Graphic& rGraphic);
+
+#endif // INCLUDED_VCL_SOURCE_FILTER_IXBM_XBMREAD_HXX
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/vcl/source/filter/ixpm/rgbtable.hxx b/vcl/source/filter/ixpm/rgbtable.hxx
new file mode 100644
index 000000000..1ded061e7
--- /dev/null
+++ b/vcl/source/filter/ixpm/rgbtable.hxx
@@ -0,0 +1,696 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#ifndef INCLUDED_VCL_SOURCE_FILTER_IXPM_RGBTABLE_HXX
+#define INCLUDED_VCL_SOURCE_FILTER_IXPM_RGBTABLE_HXX
+
+#include <sal/types.h>
+
+struct XPMRGBTab
+{
+ const char* name;
+ sal_uInt8 red;
+ sal_uInt8 green;
+ sal_uInt8 blue;
+};
+
+const XPMRGBTab pRGBTable[] = {
+{ "white", 255, 255, 255 },
+{ "black", 0, 0, 0 },
+{ "snow", 255, 250, 250 },
+{ "GhostWhite", 248, 248, 255 },
+{ "WhiteSmoke", 245, 245, 245 },
+{ "gainsboro", 220, 220, 220 },
+{ "FloralWhite", 255, 250, 240 },
+{ "OldLace", 253, 245, 230 },
+{ "linen", 250, 240, 230 },
+{ "AntiqueWhite", 250, 235, 215 },
+{ "PapayaWhip", 255, 239, 213 },
+{ "BlanchedAlmond", 255, 235, 205 },
+{ "bisque", 255, 228, 196 },
+{ "PeachPuff", 255, 218, 185 },
+{ "NavajoWhite", 255, 222, 173 },
+{ "moccasin", 255, 228, 181 },
+{ "cornsilk", 255, 248, 220 },
+{ "ivory", 255, 255, 240 },
+{ "LemonChiffon", 255, 250, 205 },
+{ "seashell", 255, 245, 238 },
+{ "honeydew", 240, 255, 240 },
+{ "MintCream", 245, 255, 250 },
+{ "azure", 240, 255, 255 },
+{ "AliceBlue", 240, 248, 255 },
+{ "lavender", 230, 230, 250 },
+{ "LavenderBlush", 255, 240, 245 },
+{ "MistyRose", 255, 228, 225 },
+{ "DarkSlateGray", 47, 79, 79 },
+{ "DarkSlateGrey", 47, 79, 79 },
+{ "DimGray", 105, 105, 105 },
+{ "DimGrey", 105, 105, 105 },
+{ "SlateGray", 112, 128, 144 },
+{ "SlateGrey", 112, 128, 144 },
+{ "LightSlateGray", 119, 136, 153 },
+{ "LightSlateGrey", 119, 136, 153 },
+{ "gray", 190, 190, 190 },
+{ "grey", 190, 190, 190 },
+{ "LightGrey", 211, 211, 211 },
+{ "LightGray", 211, 211, 211 },
+{ "MidnightBlue", 25, 25, 112 },
+{ "navy", 0, 0, 128 },
+{ "NavyBlue", 0, 0, 128 },
+{ "CornflowerBlue", 100, 149, 237 },
+{ "DarkSlateBlue", 72, 61, 139 },
+{ "SlateBlue", 106, 90, 205 },
+{ "MediumSlateBlue", 123, 104, 238 },
+{ "LightSlateBlue", 132, 112, 255 },
+{ "MediumBlue", 0, 0, 205 },
+{ "RoyalBlue", 65, 105, 225 },
+{ "blue", 0, 0, 255 },
+{ "DodgerBlue", 30, 144, 255 },
+{ "DeepSkyBlue", 0, 191, 255 },
+{ "SkyBlue", 135, 206, 235 },
+{ "LightSkyBlue", 135, 206, 250 },
+{ "SteelBlue", 70, 130, 180 },
+{ "LightSteelBlue", 176, 196, 222 },
+{ "LightBlue", 173, 216, 230 },
+{ "PowderBlue", 176, 224, 230 },
+{ "PaleTurquoise", 175, 238, 238 },
+{ "DarkTurquoise", 0, 206, 209 },
+{ "MediumTurquoise", 72, 209, 204 },
+{ "turquoise", 64, 224, 208 },
+{ "cyan", 0, 255, 255 },
+{ "LightCyan", 224, 255, 255 },
+{ "CadetBlue", 95, 158, 160 },
+{ "MediumAquamarine", 102, 205, 170 },
+{ "aquamarine", 127, 255, 212 },
+{ "DarkGreen", 0, 100, 0 },
+{ "DarkOliveGreen", 85, 107, 47 },
+{ "DarkSeaGreen", 143, 188, 143 },
+{ "SeaGreen", 46, 139, 87 },
+{ "MediumSeaGreen", 60, 179, 113 },
+{ "LightSeaGreen", 32, 178, 170 },
+{ "PaleGreen", 152, 251, 152 },
+{ "SpringGreen", 0, 255, 127 },
+{ "LawnGreen", 124, 252, 0 },
+{ "green", 0, 255, 0 },
+{ "chartreuse", 127, 255, 0 },
+{ "MediumSpringGreen", 0, 250, 154 },
+{ "GreenYellow", 173, 255 , 47 },
+{ "LimeGreen", 50, 205, 50 },
+{ "YellowGreen", 154, 205, 50 },
+{ "ForestGreen", 34, 139, 34 },
+{ "OliveDrab", 107, 142, 35 },
+{ "DarkKhaki", 189, 183, 107 },
+{ "khaki", 240, 230, 140 },
+{ "PaleGoldenrod", 238, 232, 170 },
+{ "LightGoldenrodYellow", 250, 250, 210 },
+{ "LightYellow", 255, 255, 224 },
+{ "yellow", 255, 255, 0 },
+{ "gold", 255, 215, 0 },
+{ "LightGoldenrod", 238, 221, 130 },
+{ "goldenrod", 218, 165, 32 },
+{ "DarkGoldenrod", 184, 134, 11 },
+{ "RosyBrown", 188, 143, 143 },
+{ "IndianRed", 205, 92, 92 },
+{ "SaddleBrown", 139, 69, 19 },
+{ "sienna", 160, 82, 45 },
+{ "peru", 205, 133, 63 },
+{ "burlywood", 222, 184, 135 },
+{ "beige", 245, 245, 220 },
+{ "wheat", 245, 222, 179 },
+{ "SandyBrown", 244, 164, 96 },
+{ "tan", 210, 180, 140 },
+{ "chocolate", 210, 105, 30 },
+{ "firebrick", 178, 34, 34 },
+{ "brown", 165, 42, 42 },
+{ "DarkSalmon", 233, 150, 122 },
+{ "salmon", 250, 128, 114 },
+{ "LightSalmon", 255, 160, 122 },
+{ "orange", 255, 165, 0 },
+{ "DarkOrange", 255, 140, 0 },
+{ "coral", 255, 127, 80 },
+{ "LightCoral", 240, 128, 128 },
+{ "tomato", 255, 99, 71 },
+{ "OrangeRed", 255, 69, 0 },
+{ "red", 255, 0, 0 },
+{ "HotPink", 255, 105, 180 },
+{ "DeepPink", 255, 20, 147 },
+{ "pink", 255, 192, 203 },
+{ "LightPink", 255, 182, 193 },
+{ "PaleVioletRed", 219, 112, 147 },
+{ "maroon", 176, 48, 96 },
+{ "MediumVioletRed", 199, 21, 133 },
+{ "VioletRed", 208, 32, 144 },
+{ "magenta", 255, 0, 255 },
+{ "violet", 238, 130, 238 },
+{ "plum", 221, 160, 221 },
+{ "orchid", 218, 112, 214 },
+{ "MediumOrchid", 186, 85, 211 },
+{ "DarkOrchid", 153, 50, 204 },
+{ "DarkViolet", 148, 0, 211 },
+{ "BlueViolet", 138, 43, 226 },
+{ "purple", 160, 32, 240 },
+{ "MediumPurple", 147, 112, 219 },
+{ "thistle", 216, 191, 216 },
+{ "snow1", 255, 250, 250 },
+{ "snow2", 238, 233, 233 },
+{ "snow3", 205, 201, 201 },
+{ "snow4", 139, 137, 137 },
+{ "seashell1", 255, 245, 238 },
+{ "seashell2", 238, 229, 222 },
+{ "seashell3", 205, 197, 191 },
+{ "seashell4", 139, 134, 130 },
+{ "AntiqueWhite1", 255, 239, 219 },
+{ "AntiqueWhite2", 238, 223, 204 },
+{ "AntiqueWhite3", 205, 192, 176 },
+{ "AntiqueWhite4", 139, 131, 120 },
+{ "bisque1", 255, 228, 196 },
+{ "bisque2", 238, 213, 183 },
+{ "bisque3", 205, 183, 158 },
+{ "bisque4", 139, 125, 107 },
+{ "PeachPuff1", 255, 218, 185 },
+{ "PeachPuff2", 238, 203, 173 },
+{ "PeachPuff3", 205, 175, 149 },
+{ "PeachPuff4", 139, 119, 101 },
+{ "NavajoWhite1", 255, 222, 173 },
+{ "NavajoWhite2", 238, 207, 161 },
+{ "NavajoWhite3", 205, 179, 139 },
+{ "NavajoWhite4", 139, 121, 94 },
+{ "LemonChiffon1", 255, 250, 205 },
+{ "LemonChiffon2", 238, 233, 191 },
+{ "LemonChiffon3", 205, 201, 165 },
+{ "LemonChiffon4", 139, 137, 112 },
+{ "cornsilk1", 255, 248, 220 },
+{ "cornsilk2", 238, 232, 205 },
+{ "cornsilk3", 205, 200, 177 },
+{ "cornsilk4", 139, 136, 120 },
+{ "ivory1", 255, 255, 240 },
+{ "ivory2", 238, 238, 224 },
+{ "ivory3", 205, 205, 193 },
+{ "ivory4", 139, 139, 131 },
+{ "honeydew1", 240, 255, 240 },
+{ "honeydew2", 224, 238, 224 },
+{ "honeydew3", 193, 205, 193 },
+{ "honeydew4", 131, 139, 131 },
+{ "LavenderBlush1", 255, 240, 245 },
+{ "LavenderBlush2", 238, 224, 229 },
+{ "LavenderBlush3", 205, 193, 197 },
+{ "LavenderBlush4", 139, 131, 134 },
+{ "MistyRose1", 255, 228, 225 },
+{ "MistyRose2", 238, 213, 210 },
+{ "MistyRose3", 205, 183, 181 },
+{ "MistyRose4", 139, 125, 123 },
+{ "azure1", 240, 255, 255 },
+{ "azure2", 224, 238, 238 },
+{ "azure3", 193, 205, 205 },
+{ "azure4", 131, 139, 139 },
+{ "SlateBlue1", 131, 111, 255 },
+{ "SlateBlue2", 122, 103, 238 },
+{ "SlateBlue3", 105, 89, 205 },
+{ "SlateBlue4", 71, 60, 139 },
+{ "RoyalBlue1", 72, 118, 255 },
+{ "RoyalBlue2", 67, 110, 238 },
+{ "RoyalBlue3", 58, 95, 205 },
+{ "RoyalBlue4", 39, 64, 139 },
+{ "blue1", 0, 0, 255 },
+{ "blue2", 0, 0, 238 },
+{ "blue3", 0, 0, 205 },
+{ "blue4", 0, 0, 139 },
+{ "DodgerBlue1", 30, 144, 255 },
+{ "DodgerBlue2", 28, 134, 238 },
+{ "DodgerBlue3", 24, 116, 205 },
+{ "DodgerBlue4", 16, 78, 139 },
+{ "SteelBlue1", 99, 184, 255 },
+{ "SteelBlue2", 92, 172, 238 },
+{ "SteelBlue3", 79, 148, 205 },
+{ "SteelBlue4", 54, 100, 139 },
+{ "DeepSkyBlue1", 0, 191, 255 },
+{ "DeepSkyBlue2", 0, 178, 238 },
+{ "DeepSkyBlue3", 0, 154, 205 },
+{ "DeepSkyBlue4", 0, 104, 139 },
+{ "SkyBlue1", 135, 206, 255 },
+{ "SkyBlue2", 126, 192, 238 },
+{ "SkyBlue3", 108, 166, 205 },
+{ "SkyBlue4", 74, 112, 139 },
+{ "LightSkyBlue1", 176, 226, 255 },
+{ "LightSkyBlue2", 164, 211, 238 },
+{ "LightSkyBlue3", 141, 182, 205 },
+{ "LightSkyBlue4", 96, 123, 139 },
+{ "SlateGray1", 198, 226, 255 },
+{ "SlateGray2", 185, 211, 238 },
+{ "SlateGray3", 159, 182, 205 },
+{ "SlateGray4", 108, 123, 139 },
+{ "LightSteelBlue1", 202, 225, 255 },
+{ "LightSteelBlue2", 188, 210, 238 },
+{ "LightSteelBlue3", 162, 181, 205 },
+{ "LightSteelBlue4", 110, 123, 139 },
+{ "LightBlue1", 191, 239, 255 },
+{ "LightBlue2", 178, 223, 238 },
+{ "LightBlue3", 154, 192, 205 },
+{ "LightBlue4", 104, 131, 139 },
+{ "LightCyan1", 224, 255, 255 },
+{ "LightCyan2", 209, 238, 238 },
+{ "LightCyan3", 180, 205, 205 },
+{ "LightCyan4", 122, 139, 139 },
+{ "PaleTurquoise1", 187, 255, 255 },
+{ "PaleTurquoise2", 174, 238, 238 },
+{ "PaleTurquoise3", 150, 205, 205 },
+{ "PaleTurquoise4", 102, 139, 139 },
+{ "CadetBlue1", 152, 245, 255 },
+{ "CadetBlue2", 142, 229, 238 },
+{ "CadetBlue3", 122, 197, 205 },
+{ "CadetBlue4", 83, 134, 139 },
+{ "turquoise1", 0, 245, 255 },
+{ "turquoise2", 0, 229, 238 },
+{ "turquoise3", 0, 197, 205 },
+{ "turquoise4", 0, 134, 139 },
+{ "cyan1", 0, 255, 255 },
+{ "cyan2", 0, 238, 238 },
+{ "cyan3", 0, 205, 205 },
+{ "cyan4", 0, 139, 139 },
+{ "DarkSlateGray1", 151, 255, 255 },
+{ "DarkSlateGray2", 141, 238, 238 },
+{ "DarkSlateGray3", 121, 205, 205 },
+{ "DarkSlateGray4", 82, 139, 139 },
+{ "aquamarine1", 127, 255, 212 },
+{ "aquamarine2", 118, 238, 198 },
+{ "aquamarine3", 102, 205, 170 },
+{ "aquamarine4", 69, 139, 116 },
+{ "DarkSeaGreen1", 193, 255, 193 },
+{ "DarkSeaGreen2", 180, 238, 180 },
+{ "DarkSeaGreen3", 155, 205, 155 },
+{ "DarkSeaGreen4", 105, 139, 105 },
+{ "SeaGreen1", 84, 255, 159 },
+{ "SeaGreen2", 78, 238, 148 },
+{ "SeaGreen3", 67, 205, 128 },
+{ "SeaGreen4", 46, 139, 87 },
+{ "PaleGreen1", 154, 255, 154 },
+{ "PaleGreen2", 144, 238, 144 },
+{ "PaleGreen3", 124, 205, 124 },
+{ "PaleGreen4", 84, 139, 84 },
+{ "SpringGreen1", 0, 255, 127 },
+{ "SpringGreen2", 0, 238, 118 },
+{ "SpringGreen3", 0, 205, 102 },
+{ "SpringGreen4", 0, 139, 69 },
+{ "green1", 0, 255, 0 },
+{ "green2", 0, 238, 0 },
+{ "green3", 0, 205, 0 },
+{ "green4", 0, 139, 0 },
+{ "chartreuse1", 127, 255, 0 },
+{ "chartreuse2", 118, 238, 0 },
+{ "chartreuse3", 102, 205, 0 },
+{ "chartreuse4", 69, 139, 0 },
+{ "OliveDrab1", 192, 255, 62 },
+{ "OliveDrab2", 179, 238, 58 },
+{ "OliveDrab3", 154, 205, 50 },
+{ "OliveDrab4", 105, 139, 34 },
+{ "DarkOliveGreen1", 202, 255, 112 },
+{ "DarkOliveGreen2", 188, 238, 104 },
+{ "DarkOliveGreen3", 162, 205, 90 },
+{ "DarkOliveGreen4", 110, 139, 61 },
+{ "khaki1", 255, 246, 143 },
+{ "khaki2", 238, 230, 133 },
+{ "khaki3", 205, 198, 115 },
+{ "khaki4", 139, 134, 78 },
+{ "LightGoldenrod1", 255, 236, 139 },
+{ "LightGoldenrod2", 238, 220, 130 },
+{ "LightGoldenrod3", 205, 190, 112 },
+{ "LightGoldenrod4", 139, 129, 76 },
+{ "LightYellow1", 255, 255, 224 },
+{ "LightYellow2", 238, 238, 209 },
+{ "LightYellow3", 205, 205, 180 },
+{ "LightYellow4", 139, 139, 122 },
+{ "yellow1", 255, 255, 0 },
+{ "yellow2", 238, 238, 0 },
+{ "yellow3", 205, 205, 0 },
+{ "yellow4", 139, 139, 0 },
+{ "gold1", 255, 215, 0 },
+{ "gold2", 238, 201, 0 },
+{ "gold3", 205, 173, 0 },
+{ "gold4", 139, 117, 0 },
+{ "goldenrod1", 255, 193, 37 },
+{ "goldenrod2", 238, 180, 34 },
+{ "goldenrod3", 205, 155, 29 },
+{ "goldenrod4", 139, 105, 20 },
+{ "DarkGoldenrod1", 255, 185, 15 },
+{ "DarkGoldenrod2", 238, 173, 14 },
+{ "DarkGoldenrod3", 205, 149, 12 },
+{ "DarkGoldenrod4", 139, 101, 8 },
+{ "RosyBrown1", 255, 193, 193 },
+{ "RosyBrown2", 238, 180, 180 },
+{ "RosyBrown3", 205, 155, 155 },
+{ "RosyBrown4", 139, 105, 105 },
+{ "IndianRed1", 255, 106, 106 },
+{ "IndianRed2", 238, 99, 99 },
+{ "IndianRed3", 205, 85, 85 },
+{ "IndianRed4", 139, 58, 58 },
+{ "sienna1", 255, 130, 71 },
+{ "sienna2", 238, 121, 66 },
+{ "sienna3", 205, 104, 57 },
+{ "sienna4", 139, 71, 38 },
+{ "burlywood1", 255, 211, 155 },
+{ "burlywood2", 238, 197, 145 },
+{ "burlywood3", 205, 170, 125 },
+{ "burlywood4", 139, 115, 85 },
+{ "wheat1", 255, 231, 186 },
+{ "wheat2", 238, 216, 174 },
+{ "wheat3", 205, 186, 150 },
+{ "wheat4", 139, 126, 102 },
+{ "tan1", 255, 165, 79 },
+{ "tan2", 238, 154, 73 },
+{ "tan3", 205, 133, 63 },
+{ "tan4", 139 , 90, 43 },
+{ "chocolate1", 255, 127, 36 },
+{ "chocolate2", 238, 118, 33 },
+{ "chocolate3", 205, 102, 29 },
+{ "chocolate4", 139, 69, 19 },
+{ "firebrick1", 255, 48, 48 },
+{ "firebrick2", 238, 44, 44 },
+{ "firebrick3", 205, 38, 38 },
+{ "firebrick4", 139, 26, 26 },
+{ "brown1", 255, 64, 64 },
+{ "brown2", 238, 59, 59 },
+{ "brown3", 205, 51, 51 },
+{ "brown4", 139, 35, 35 },
+{ "salmon1", 255, 140, 105 },
+{ "salmon2", 238, 130, 98 },
+{ "salmon3", 205, 112, 84 },
+{ "salmon4", 139, 76, 57 },
+{ "LightSalmon1", 255, 160, 122 },
+{ "LightSalmon2", 238, 149, 114 },
+{ "LightSalmon3", 205, 129, 98 },
+{ "LightSalmon4", 139, 87, 66 },
+{ "orange1", 255, 165, 0 },
+{ "orange2", 238, 154, 0 },
+{ "orange3", 205, 133, 0 },
+{ "orange4", 139 , 90, 0 },
+{ "DarkOrange1", 255, 127, 0 },
+{ "DarkOrange2", 238, 118, 0 },
+{ "DarkOrange3", 205, 102, 0 },
+{ "DarkOrange4", 139 , 69, 0 },
+{ "coral1", 255, 114, 86 },
+{ "coral2", 238, 106, 80 },
+{ "coral3", 205, 91, 69 },
+{ "coral4", 139, 62, 47 },
+{ "tomato1", 255, 99, 71 },
+{ "tomato2", 238, 92, 66 },
+{ "tomato3", 205, 79, 57 },
+{ "tomato4", 139, 54, 38 },
+{ "OrangeRed1", 255, 69, 0 },
+{ "OrangeRed2", 238, 64, 0 },
+{ "OrangeRed3", 205, 55, 0 },
+{ "OrangeRed4", 139, 37, 0 },
+{ "red1", 255, 0, 0 },
+{ "red2", 238, 0, 0 },
+{ "red3", 205, 0, 0 },
+{ "red4", 139, 0, 0 },
+{ "DeepPink1", 255, 20, 147 },
+{ "DeepPink2", 238, 18, 137 },
+{ "DeepPink3", 205, 16, 118 },
+{ "DeepPink4", 139, 10, 80 },
+{ "HotPink1", 255, 110, 180 },
+{ "HotPink2", 238, 106, 167 },
+{ "HotPink3", 205, 96, 144 },
+{ "HotPink4", 139, 58, 98 },
+{ "pink1", 255, 181, 197 },
+{ "pink2", 238, 169, 184 },
+{ "pink3", 205, 145, 158 },
+{ "pink4", 139, 99, 108 },
+{ "LightPink1", 255, 174, 185 },
+{ "LightPink2", 238, 162, 173 },
+{ "LightPink3", 205, 140, 149 },
+{ "LightPink4", 139, 95, 101 },
+{ "PaleVioletRed1", 255, 130, 171 },
+{ "PaleVioletRed2", 238, 121, 159 },
+{ "PaleVioletRed3", 205, 104, 137 },
+{ "PaleVioletRed4", 139, 71, 93 },
+{ "maroon1", 255, 52, 179 },
+{ "maroon2", 238, 48, 167 },
+{ "maroon3", 205, 41, 144 },
+{ "maroon4", 139, 28, 98 },
+{ "VioletRed1", 255, 62, 150 },
+{ "VioletRed2", 238, 58, 140 },
+{ "VioletRed3", 205, 50, 120 },
+{ "VioletRed4", 139, 34, 82 },
+{ "magenta1", 255, 0, 255 },
+{ "magenta2", 238, 0, 238 },
+{ "magenta3", 205, 0, 205 },
+{ "magenta4", 139, 0, 139 },
+{ "orchid1", 255, 131, 250 },
+{ "orchid2", 238, 122, 233 },
+{ "orchid3", 205, 105, 201 },
+{ "orchid4", 139, 71, 137 },
+{ "plum1", 255, 187, 255 },
+{ "plum2", 238, 174, 238 },
+{ "plum3", 205, 150, 205 },
+{ "plum4", 139, 102, 139 },
+{ "MediumOrchid1", 224, 102, 255 },
+{ "MediumOrchid2", 209, 95, 238 },
+{ "MediumOrchid3", 180, 82, 205 },
+{ "MediumOrchid4", 122, 55, 139 },
+{ "DarkOrchid1", 191, 62, 255 },
+{ "DarkOrchid2", 178, 58, 238 },
+{ "DarkOrchid3", 154, 50, 205 },
+{ "DarkOrchid4", 104, 34, 139 },
+{ "purple1", 155, 48, 255 },
+{ "purple2", 145, 44, 238 },
+{ "purple3", 125, 38, 205 },
+{ "purple4", 85, 26, 139 },
+{ "MediumPurple1", 171, 130, 255 },
+{ "MediumPurple2", 159, 121, 238 },
+{ "MediumPurple3", 137, 104, 205 },
+{ "MediumPurple4", 93, 71, 139 },
+{ "thistle1", 255, 225, 255 },
+{ "thistle2", 238, 210, 238 },
+{ "thistle3", 205, 181, 205 },
+{ "thistle4", 139, 123, 139 },
+{ "gray0", 0, 0, 0 },
+{ "grey0", 0, 0, 0 },
+{ "gray1", 3, 3, 3 },
+{ "grey1", 3, 3, 3 },
+{ "gray2", 5, 5, 5 },
+{ "grey2", 5, 5, 5 },
+{ "gray3", 8, 8, 8 },
+{ "grey3", 8, 8, 8 },
+{ "gray4", 10, 10, 10 },
+{ "grey4", 10, 10, 10 },
+{ "gray5", 13, 13, 13 },
+{ "grey5", 13, 13, 13 },
+{ "gray6", 15, 15, 15 },
+{ "grey6", 15, 15, 15 },
+{ "gray7", 18, 18, 18 },
+{ "grey7", 18, 18, 18 },
+{ "gray8", 20, 20, 20 },
+{ "grey8", 20, 20, 20 },
+{ "gray9", 23, 23, 23 },
+{ "grey9", 23, 23, 23 },
+{ "gray10", 26, 26, 26 },
+{ "grey10", 26, 26, 26 },
+{ "gray11", 28, 28, 28 },
+{ "grey11", 28, 28, 28 },
+{ "gray12", 31, 31, 31 },
+{ "grey12", 31, 31, 31 },
+{ "gray13", 33, 33, 33 },
+{ "grey13", 33, 33, 33 },
+{ "gray14", 36, 36, 36 },
+{ "grey14", 36, 36, 36 },
+{ "gray15", 38, 38, 38 },
+{ "grey15", 38, 38, 38 },
+{ "gray16", 41, 41, 41 },
+{ "grey16", 41, 41, 41 },
+{ "gray17", 43, 43, 43 },
+{ "grey17", 43, 43, 43 },
+{ "gray18", 46, 46, 46 },
+{ "grey18", 46, 46, 46 },
+{ "gray19", 48, 48, 48 },
+{ "grey19", 48, 48, 48 },
+{ "gray20", 51, 51, 51 },
+{ "grey20", 51, 51, 51 },
+{ "gray21", 54, 54, 54 },
+{ "grey21", 54, 54, 54 },
+{ "gray22", 56, 56, 56 },
+{ "grey22", 56, 56, 56 },
+{ "gray23", 59, 59, 59 },
+{ "grey23", 59, 59, 59 },
+{ "gray24", 61, 61, 61 },
+{ "grey24", 61, 61, 61 },
+{ "gray25", 64, 64, 64 },
+{ "grey25", 64, 64, 64 },
+{ "gray26", 66, 66, 66 },
+{ "grey26", 66, 66, 66 },
+{ "gray27", 69, 69, 69 },
+{ "grey27", 69, 69, 69 },
+{ "gray28", 71, 71, 71 },
+{ "grey28", 71, 71, 71 },
+{ "gray29", 74, 74, 74 },
+{ "grey29", 74, 74, 74 },
+{ "gray30", 77, 77, 77 },
+{ "grey30", 77, 77, 77 },
+{ "gray31", 79, 79, 79 },
+{ "grey31", 79, 79, 79 },
+{ "gray32", 82, 82, 82 },
+{ "grey32", 82, 82, 82 },
+{ "gray33", 84, 84, 84 },
+{ "grey33", 84, 84, 84 },
+{ "gray34", 87, 87, 87 },
+{ "grey34", 87, 87, 87 },
+{ "gray35", 89, 89, 89 },
+{ "grey35", 89, 89, 89 },
+{ "gray36", 92, 92, 92 },
+{ "grey36", 92, 92, 92 },
+{ "gray37", 94, 94, 94 },
+{ "grey37", 94, 94, 94 },
+{ "gray38", 97, 97, 97 },
+{ "grey38", 97, 97, 97 },
+{ "gray39", 99, 99, 99 },
+{ "grey39", 99, 99, 99 },
+{ "gray40", 102, 102, 102 },
+{ "grey40", 102, 102, 102 },
+{ "gray41", 105, 105, 105 },
+{ "grey41", 105, 105, 105 },
+{ "gray42", 107, 107, 107 },
+{ "grey42", 107, 107, 107 },
+{ "gray43", 110, 110, 110 },
+{ "grey43", 110, 110, 110 },
+{ "gray44", 112, 112, 112 },
+{ "grey44", 112, 112, 112 },
+{ "gray45", 115, 115, 115 },
+{ "grey45", 115, 115, 115 },
+{ "gray46", 117, 117, 117 },
+{ "grey46", 117, 117, 117 },
+{ "gray47", 120, 120, 120 },
+{ "grey47", 120, 120, 120 },
+{ "gray48", 122, 122, 122 },
+{ "grey48", 122, 122, 122 },
+{ "gray49", 125, 125, 125 },
+{ "grey49", 125, 125, 125 },
+{ "gray50", 127, 127, 127 },
+{ "grey50", 127, 127, 127 },
+{ "gray51", 130, 130, 130 },
+{ "grey51", 130, 130, 130 },
+{ "gray52", 133, 133, 133 },
+{ "grey52", 133, 133, 133 },
+{ "gray53", 135, 135, 135 },
+{ "grey53", 135, 135, 135 },
+{ "gray54", 138, 138, 138 },
+{ "grey54", 138, 138, 138 },
+{ "gray55", 140, 140, 140 },
+{ "grey55", 140, 140, 140 },
+{ "gray56", 143, 143, 143 },
+{ "grey56", 143, 143, 143 },
+{ "gray57", 145, 145, 145 },
+{ "grey57", 145, 145, 145 },
+{ "gray58", 148, 148, 148 },
+{ "grey58", 148, 148, 148 },
+{ "gray59", 150, 150, 150 },
+{ "grey59", 150, 150, 150 },
+{ "gray60", 153, 153, 153 },
+{ "grey60", 153, 153, 153 },
+{ "gray61", 156, 156, 156 },
+{ "grey61", 156, 156, 156 },
+{ "gray62", 158, 158, 158 },
+{ "grey62", 158, 158, 158 },
+{ "gray63", 161, 161, 161 },
+{ "grey63", 161, 161, 161 },
+{ "gray64", 163, 163, 163 },
+{ "grey64", 163, 163, 163 },
+{ "gray65", 166, 166, 166 },
+{ "grey65", 166, 166, 166 },
+{ "gray66", 168, 168, 168 },
+{ "grey66", 168, 168, 168 },
+{ "gray67", 171, 171, 171 },
+{ "grey67", 171, 171, 171 },
+{ "gray68", 173, 173, 173 },
+{ "grey68", 173, 173, 173 },
+{ "gray69", 176, 176, 176 },
+{ "grey69", 176, 176, 176 },
+{ "gray70", 179, 179, 179 },
+{ "grey70", 179, 179, 179 },
+{ "gray71", 181, 181, 181 },
+{ "grey71", 181, 181, 181 },
+{ "gray72", 184, 184, 184 },
+{ "grey72", 184, 184, 184 },
+{ "gray73", 186, 186, 186 },
+{ "grey73", 186, 186, 186 },
+{ "gray74", 189, 189, 189 },
+{ "grey74", 189, 189, 189 },
+{ "gray75", 191, 191, 191 },
+{ "grey75", 191, 191, 191 },
+{ "gray76", 194, 194, 194 },
+{ "grey76", 194, 194, 194 },
+{ "gray77", 196, 196, 196 },
+{ "grey77", 196, 196, 196 },
+{ "gray78", 199, 199, 199 },
+{ "grey78", 199, 199, 199 },
+{ "gray79", 201, 201, 201 },
+{ "grey79", 201, 201, 201 },
+{ "gray80", 204, 204, 204 },
+{ "grey80", 204, 204, 204 },
+{ "gray81", 207, 207, 207 },
+{ "grey81", 207, 207, 207 },
+{ "gray82", 209, 209, 209 },
+{ "grey82", 209, 209, 209 },
+{ "gray83", 212, 212, 212 },
+{ "grey83", 212, 212, 212 },
+{ "gray84", 214, 214, 214 },
+{ "grey84", 214, 214, 214 },
+{ "gray85", 217, 217, 217 },
+{ "grey85", 217, 217, 217 },
+{ "gray86", 219, 219, 219 },
+{ "grey86", 219, 219, 219 },
+{ "gray87", 222, 222, 222 },
+{ "grey87", 222, 222, 222 },
+{ "gray88", 224, 224, 224 },
+{ "grey88", 224, 224, 224 },
+{ "gray89", 227, 227, 227 },
+{ "grey89", 227, 227, 227 },
+{ "gray90", 229, 229, 229 },
+{ "grey90", 229, 229, 229 },
+{ "gray91", 232, 232, 232 },
+{ "grey91", 232, 232, 232 },
+{ "gray92", 235, 235, 235 },
+{ "grey92", 235, 235, 235 },
+{ "gray93", 237, 237, 237 },
+{ "grey93", 237, 237, 237 },
+{ "gray94", 240, 240, 240 },
+{ "grey94", 240, 240, 240 },
+{ "gray95", 242, 242, 242 },
+{ "grey95", 242, 242, 242 },
+{ "gray96", 245, 245, 245 },
+{ "grey96", 245, 245, 245 },
+{ "gray97", 247, 247, 247 },
+{ "grey97", 247, 247, 247 },
+{ "gray98", 250, 250, 250 },
+{ "grey98", 250, 250, 250 },
+{ "gray99", 252, 252, 252 },
+{ "grey99", 252, 252, 252 },
+{ "gray100", 255, 255, 255 },
+{ "grey100", 255, 255, 255 },
+{ "DarkGrey", 169, 169, 169 },
+{ "DarkGray", 169, 169, 169 },
+{ "DarkBlue", 0, 0, 139 },
+{ "DarkCyan", 0, 139, 139 },
+{ "DarkMagenta", 139, 0, 139 },
+{ "DarkRed", 139, 0, 0 },
+{ "LightGreen", 144, 238, 144 },
+{ nullptr, 0 , 0, 0}
+};
+
+#endif // INCLUDED_VCL_SOURCE_FILTER_IXPM_RGBTABLE_HXX
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/vcl/source/filter/ixpm/xpmread.cxx b/vcl/source/filter/ixpm/xpmread.cxx
new file mode 100644
index 000000000..2a979d2df
--- /dev/null
+++ b/vcl/source/filter/ixpm/xpmread.cxx
@@ -0,0 +1,697 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <filter/XpmReader.hxx>
+
+#include <vcl/graph.hxx>
+#include <tools/stream.hxx>
+
+#include <bitmap/BitmapWriteAccess.hxx>
+#include <graphic/GraphicReader.hxx>
+
+#include "rgbtable.hxx"
+
+#include <cstring>
+#include <array>
+#include <map>
+
+#define XPMTEMPBUFSIZE 0x00008000
+#define XPMSTRINGBUF 0x00008000
+
+#define XPMIDENTIFIER 0x00000001 // mnIdentifier includes one of the six phases
+#define XPMDEFINITION 0x00000002 // the XPM format consists of
+#define XPMVALUES 0x00000003
+#define XPMCOLORS 0x00000004
+#define XPMPIXELS 0x00000005
+#define XPMEXTENSIONS 0x00000006
+#define XPMENDEXT 0x00000007
+
+#define XPMREMARK 0x00000001 // defines used by mnStatus
+#define XPMDOUBLE 0x00000002
+#define XPMSTRING 0x00000004
+#define XPMFINISHED 0x00000008
+
+namespace {
+
+enum ReadState
+{
+ XPMREAD_OK,
+ XPMREAD_ERROR,
+ XPMREAD_NEED_MORE
+};
+
+}
+
+class BitmapWriteAccess;
+class Graphic;
+
+namespace {
+
+class XPMReader : public GraphicReader
+{
+private:
+
+ SvStream& mrIStm;
+ Bitmap maBmp;
+ BitmapScopedWriteAccess mpAcc;
+ Bitmap maMaskBmp;
+ BitmapScopedWriteAccess mpMaskAcc;
+ tools::Long mnLastPos;
+
+ sal_uLong mnWidth;
+ sal_uLong mnHeight;
+ sal_uLong mnColors;
+ sal_uInt32 mnCpp; // characters per pix
+ bool mbTransparent;
+ bool mbStatus;
+ sal_uLong mnStatus;
+ sal_uLong mnIdentifier;
+ sal_uInt8 mcThisByte;
+ sal_uInt8 mcLastByte;
+ sal_uLong mnTempAvail;
+ sal_uInt8* mpTempBuf;
+ sal_uInt8* mpTempPtr;
+ // each key is ( mnCpp )Byte(s)-> ASCII entry assigned to the colour
+ // each colordata is
+ // 1 Byte -> 0xFF if colour is transparent
+ // 3 Bytes -> RGB value of the colour
+ typedef std::array<sal_uInt8,4> colordata;
+ typedef std::map<OString, colordata> colormap;
+ colormap maColMap;
+ sal_uLong mnStringSize;
+ sal_uInt8* mpStringBuf;
+ sal_uLong mnParaSize;
+ sal_uInt8* mpPara;
+
+ bool ImplGetString();
+ bool ImplGetColor();
+ bool ImplGetScanLine( sal_uLong );
+ bool ImplGetColSub(colordata &rDest);
+ bool ImplGetColKey( sal_uInt8 );
+ void ImplGetRGBHex(colordata &rDest, sal_uLong);
+ bool ImplGetPara( sal_uLong numb );
+ static bool ImplCompare(sal_uInt8 const *, sal_uInt8 const *, sal_uLong);
+ sal_uLong ImplGetULONG( sal_uLong nPara );
+
+public:
+ explicit XPMReader( SvStream& rStm );
+
+ ReadState ReadXPM( Graphic& rGraphic );
+};
+
+}
+
+XPMReader::XPMReader(SvStream& rStm)
+ : mrIStm(rStm)
+ , mnLastPos(rStm.Tell())
+ , mnWidth(0)
+ , mnHeight(0)
+ , mnColors(0)
+ , mnCpp(0)
+ , mbTransparent(false)
+ , mbStatus(true)
+ , mnStatus( 0 )
+ , mnIdentifier(XPMIDENTIFIER)
+ , mcThisByte(0)
+ , mcLastByte(0)
+ , mnTempAvail(0)
+ , mpTempBuf(nullptr)
+ , mpTempPtr(nullptr)
+ , mnStringSize(0)
+ , mpStringBuf(nullptr)
+ , mnParaSize(0)
+ , mpPara(nullptr)
+{
+}
+
+ReadState XPMReader::ReadXPM( Graphic& rGraphic )
+{
+ ReadState eReadState;
+ sal_uInt8 cDummy;
+
+ // check if we can real ALL
+ mrIStm.Seek( STREAM_SEEK_TO_END );
+ mrIStm.ReadUChar( cDummy );
+
+ // if we could not read all
+ // return and wait for new data
+ if ( mrIStm.GetError() != ERRCODE_IO_PENDING )
+ {
+ mrIStm.Seek( mnLastPos );
+ mbStatus = true;
+
+ if ( mbStatus )
+ {
+ mpStringBuf = new sal_uInt8 [ XPMSTRINGBUF ];
+ mpTempBuf = new sal_uInt8 [ XPMTEMPBUFSIZE ];
+
+ mbStatus = ImplGetString();
+ if ( mbStatus )
+ {
+ mnIdentifier = XPMVALUES; // fetch Bitmap information
+ mnWidth = ImplGetULONG( 0 );
+ mnHeight = ImplGetULONG( 1 );
+ mnColors = ImplGetULONG( 2 );
+ mnCpp = ImplGetULONG( 3 );
+ }
+ if ( mnColors > ( SAL_MAX_UINT32 / ( 4 + mnCpp ) ) )
+ mbStatus = false;
+ if ( ( mnWidth * mnCpp ) >= XPMSTRINGBUF )
+ mbStatus = false;
+ //xpms are a minimum of one character (one byte) per pixel, so if the file isn't
+ //even that long, it's not all there
+ if (mrIStm.remainingSize() + mnTempAvail < static_cast<sal_uInt64>(mnWidth) * mnHeight)
+ mbStatus = false;
+ if ( mbStatus && mnWidth && mnHeight && mnColors && mnCpp )
+ {
+ mnIdentifier = XPMCOLORS;
+
+ for (sal_uLong i = 0; i < mnColors; ++i)
+ {
+ if (!ImplGetColor())
+ {
+ mbStatus = false;
+ break;
+ }
+ }
+
+ if ( mbStatus )
+ {
+ // create a 24bit graphic when more as 256 colours present
+ auto ePixelFormat = vcl::PixelFormat::INVALID;
+ if ( mnColors > 256 )
+ ePixelFormat = vcl::PixelFormat::N24_BPP;
+ else if ( mnColors > 2 )
+ ePixelFormat = vcl::PixelFormat::N8_BPP;
+ else
+ ePixelFormat = vcl::PixelFormat::N1_BPP;
+
+ maBmp = Bitmap(Size(mnWidth, mnHeight), ePixelFormat);
+ mpAcc = BitmapScopedWriteAccess(maBmp);
+
+ // mbTransparent is TRUE if at least one colour is transparent
+ if ( mbTransparent )
+ {
+ maMaskBmp = Bitmap(Size(mnWidth, mnHeight), vcl::PixelFormat::N1_BPP);
+ mpMaskAcc = BitmapScopedWriteAccess(maMaskBmp);
+ if ( !mpMaskAcc )
+ mbStatus = false;
+ }
+ if( mpAcc && mbStatus )
+ {
+ if (mnColors <= 256) // palette is only needed by using less than 257
+ { // colors
+ sal_uInt8 i = 0;
+ for (auto& elem : maColMap)
+ {
+ mpAcc->SetPaletteColor(i, Color(elem.second[1], elem.second[2], elem.second[3]));
+ //reuse map entry, overwrite color with palette index
+ elem.second[1] = i;
+ i++;
+ }
+ }
+
+ // now we get the bitmap data
+ mnIdentifier = XPMPIXELS;
+ for (sal_uLong i = 0; i < mnHeight; ++i)
+ {
+ if ( !ImplGetScanLine( i ) )
+ {
+ mbStatus = false;
+ break;
+ }
+ }
+ mnIdentifier = XPMEXTENSIONS;
+ }
+ }
+ }
+
+ delete[] mpStringBuf;
+ delete[] mpTempBuf;
+
+ }
+ if( mbStatus )
+ {
+ mpAcc.reset();
+ if ( mpMaskAcc )
+ {
+ mpMaskAcc.reset();
+ rGraphic = Graphic( BitmapEx( maBmp, maMaskBmp ) );
+ }
+ else
+ {
+ rGraphic = BitmapEx(maBmp);
+ }
+ eReadState = XPMREAD_OK;
+ }
+ else
+ {
+ mpMaskAcc.reset();
+ mpAcc.reset();
+
+ eReadState = XPMREAD_ERROR;
+ }
+ }
+ else
+ {
+ mrIStm.ResetError();
+ eReadState = XPMREAD_NEED_MORE;
+ }
+ return eReadState;
+}
+
+// ImplGetColor returns various colour values,
+// returns TRUE if various colours could be assigned
+bool XPMReader::ImplGetColor()
+{
+ sal_uInt8* pString = mpStringBuf;
+ if (!ImplGetString())
+ return false;
+
+ if (mnStringSize < mnCpp)
+ return false;
+
+ OString aKey(reinterpret_cast<char*>(pString), mnCpp);
+ colordata aValue{0};
+ bool bStatus = ImplGetColSub(aValue);
+ if (bStatus)
+ {
+ maColMap[aKey] = aValue;
+ }
+ return bStatus;
+}
+
+// ImpGetScanLine reads the string mpBufSize and writes the pixel in the
+// Bitmap. Parameter nY is the horizontal position.
+bool XPMReader::ImplGetScanLine( sal_uLong nY )
+{
+ bool bStatus = ImplGetString();
+ sal_uInt8* pString = mpStringBuf;
+ BitmapColor aWhite;
+ BitmapColor aBlack;
+
+ if ( bStatus )
+ {
+ if ( mpMaskAcc )
+ {
+ aWhite = mpMaskAcc->GetBestMatchingColor( COL_WHITE );
+ aBlack = mpMaskAcc->GetBestMatchingColor( COL_BLACK );
+ }
+ if ( mnStringSize != ( mnWidth * mnCpp ))
+ bStatus = false;
+ else
+ {
+ Scanline pScanline = mpAcc->GetScanline(nY);
+ Scanline pMaskScanline = mpMaskAcc ? mpMaskAcc->GetScanline(nY) : nullptr;
+ for (sal_uLong i = 0; i < mnWidth; ++i)
+ {
+ OString aKey(reinterpret_cast<char*>(pString), mnCpp);
+ auto it = maColMap.find(aKey);
+ if (it != maColMap.end())
+ {
+ if (mnColors > 256)
+ mpAcc->SetPixelOnData(pScanline, i, Color(it->second[1], it->second[2], it->second[3]));
+ else
+ mpAcc->SetPixelOnData(pScanline, i, BitmapColor(it->second[1]));
+ if (pMaskScanline)
+ mpMaskAcc->SetPixelOnData(pMaskScanline, i, it->second[0] ? aWhite : aBlack);
+ }
+ pString += mnCpp;
+ }
+ }
+ }
+ return bStatus;
+}
+
+// tries to determine a colour value from mpStringBuf
+// if a colour was found the RGB value is written a pDest[1]..pDest[2]
+// pDest[0] contains 0xFF if the colour is transparent otherwise 0
+
+bool XPMReader::ImplGetColSub(colordata &rDest)
+{
+ unsigned char cTransparent[] = "None";
+
+ bool bColStatus = false;
+
+ if ( ImplGetColKey( 'c' ) || ImplGetColKey( 'm' ) || ImplGetColKey( 'g' ) )
+ {
+ // hexentry for RGB or HSV color ?
+ if (*mpPara == '#')
+ {
+ rDest[0] = 0;
+ bColStatus = true;
+ switch ( mnParaSize )
+ {
+ case 25 :
+ ImplGetRGBHex(rDest, 6);
+ break;
+ case 13 :
+ ImplGetRGBHex(rDest, 2);
+ break;
+ case 7 :
+ ImplGetRGBHex(rDest, 0);
+ break;
+ default:
+ bColStatus = false;
+ break;
+ }
+ }
+ // maybe pixel is transparent
+ else if ( ImplCompare( &cTransparent[0], mpPara, 4 ))
+ {
+ rDest[0] = 0xff;
+ bColStatus = true;
+ mbTransparent = true;
+ }
+ // last we will try to get the colorname
+ else if ( mnParaSize > 2 ) // name must enlarge the minimum size
+ {
+ sal_uLong i = 0;
+ while ( true )
+ {
+ if ( pRGBTable[ i ].name == nullptr )
+ break;
+ if ( std::strlen(pRGBTable[i].name) > mnParaSize &&
+ pRGBTable[ i ].name[ mnParaSize ] == 0 )
+ {
+ if ( ImplCompare ( reinterpret_cast<unsigned char const *>(pRGBTable[ i ].name),
+ mpPara, mnParaSize ) )
+ {
+ bColStatus = true;
+ rDest[0] = 0;
+ rDest[1] = pRGBTable[i].red;
+ rDest[2] = pRGBTable[i].green;
+ rDest[3] = pRGBTable[i].blue;
+ break;
+ }
+ }
+ i++;
+ }
+ }
+ }
+ return bColStatus;
+}
+
+// ImplGetColKey searches string mpStringBuf for a parameter 'nKey'
+// and returns a boolean. (if TRUE mpPara and mnParaSize will be set)
+
+bool XPMReader::ImplGetColKey( sal_uInt8 nKey )
+{
+ sal_uInt8 nTemp, nPrev = ' ';
+
+ if (mnStringSize < mnCpp + 1)
+ return false;
+
+ mpPara = mpStringBuf + mnCpp + 1;
+ mnParaSize = 0;
+
+ while ( *mpPara != 0 )
+ {
+ if ( *mpPara == nKey )
+ {
+ nTemp = *( mpPara + 1 );
+ if ( nTemp == ' ' || nTemp == 0x09 )
+ {
+ if ( nPrev == ' ' || nPrev == 0x09 )
+ break;
+ }
+ }
+ nPrev = *mpPara;
+ mpPara++;
+ }
+ if ( *mpPara )
+ {
+ mpPara++;
+ while ( (*mpPara == ' ') || (*mpPara == 0x09) )
+ {
+ mpPara++;
+ }
+ if ( *mpPara != 0 )
+ {
+ while ( *(mpPara+mnParaSize) != ' ' && *(mpPara+mnParaSize) != 0x09 &&
+ *(mpPara+mnParaSize) != 0 )
+ {
+ mnParaSize++;
+ }
+ }
+ }
+ return mnParaSize != 0;
+}
+
+// ImplGetRGBHex translates the ASCII-Hexadecimalvalue belonging to mpPara
+// in a RGB value and writes this to rDest
+// below formats should be contained in mpPara:
+// if nAdd = 0 : '#12ab12' -> RGB = 0x12, 0xab, 0x12
+// 2 : '#1234abcd1234' " " " "
+// 6 : '#12345678abcdefab12345678' " " " "
+
+void XPMReader::ImplGetRGBHex(colordata &rDest, sal_uLong nAdd)
+{
+ sal_uInt8* pPtr = mpPara+1;
+
+ for (sal_uLong i = 1; i < 4; ++i)
+ {
+ sal_uInt8 nHex = (*pPtr++) - '0';
+ if ( nHex > 9 )
+ nHex = ((nHex - 'A' + '0') & 7) + 10;
+
+ sal_uInt8 nTemp = (*pPtr++) - '0';
+ if ( nTemp > 9 )
+ nTemp = ((nTemp - 'A' + '0') & 7) + 10;
+ nHex = ( nHex << 4 ) + nTemp;
+
+ pPtr += nAdd;
+ rDest[i] = nHex;
+ }
+}
+
+// ImplGetUlong returns the value of a up to 6-digit long ASCII-decimal number.
+
+sal_uLong XPMReader::ImplGetULONG( sal_uLong nPara )
+{
+ if ( ImplGetPara ( nPara ) )
+ {
+ sal_uLong nRetValue = 0;
+ sal_uInt8* pPtr = mpPara;
+
+ if ( ( mnParaSize > 6 ) || ( mnParaSize == 0 ) ) return 0;
+ for ( sal_uLong i = 0; i < mnParaSize; i++ )
+ {
+ sal_uInt8 j = (*pPtr++) - 48;
+ if ( j > 9 ) return 0; // ascii is invalid
+ nRetValue*=10;
+ nRetValue+=j;
+ }
+ return nRetValue;
+ }
+ else return 0;
+}
+
+bool XPMReader::ImplCompare(sal_uInt8 const * pSource, sal_uInt8 const * pDest, sal_uLong nSize)
+{
+ for (sal_uLong i = 0; i < nSize; ++i)
+ {
+ if ( ( pSource[i]&~0x20 ) != ( pDest[i]&~0x20 ) )
+ {
+ return false;
+ }
+ }
+ return true;
+}
+
+// ImplGetPara tries to retrieve nNumb (0...x) parameters from mpStringBuf.
+// Parameters are separated by spaces or tabs.
+// If a parameter was found then the return value is TRUE and mpPara + mnParaSize
+// are set.
+
+bool XPMReader::ImplGetPara ( sal_uLong nNumb )
+{
+ sal_uInt8 nByte;
+ sal_uLong nSize = 0;
+ sal_uInt8* pPtr = mpStringBuf;
+ sal_uLong nCount = 0;
+
+ if ( ( *pPtr != ' ' ) && ( *pPtr != 0x09 ) )
+ {
+ mpPara = pPtr;
+ mnParaSize = 0;
+ nCount = 0;
+ }
+ else
+ {
+ mpPara = nullptr;
+ nCount = 0xffffffff;
+ }
+
+ while ( nSize < mnStringSize )
+ {
+ nByte = *pPtr;
+
+ if ( mpPara )
+ {
+ if ( ( nByte == ' ' ) || ( nByte == 0x09 ) )
+ {
+ if ( nCount == nNumb )
+ break;
+ else
+ mpPara = nullptr;
+ }
+ else
+ mnParaSize++;
+ }
+ else
+ {
+ if ( ( nByte != ' ' ) && ( nByte != 0x09 ) )
+ {
+ mpPara = pPtr;
+ mnParaSize = 1;
+ nCount++;
+ }
+ }
+ nSize++;
+ pPtr++;
+ }
+ return ( ( nCount == nNumb ) && mpPara );
+}
+
+// The next string is read and stored in mpStringBuf (terminated with 0);
+// mnStringSize contains the size of the string read.
+// Comments like '//' and '/*...*/' are skipped.
+
+bool XPMReader::ImplGetString()
+{
+ sal_uInt8 const sID[] = "/* XPM */";
+ sal_uInt8* pString = mpStringBuf;
+
+ mnStringSize = 0;
+ mpStringBuf[0] = 0;
+
+ while( mbStatus && ( mnStatus != XPMFINISHED ) )
+ {
+ if ( mnTempAvail == 0 )
+ {
+ mnTempAvail = mrIStm.ReadBytes( mpTempBuf, XPMTEMPBUFSIZE );
+ if ( mnTempAvail == 0 )
+ break;
+
+ mpTempPtr = mpTempBuf;
+
+ if ( mnIdentifier == XPMIDENTIFIER )
+ {
+ if ( mnTempAvail <= 50 )
+ {
+ mbStatus = false; // file is too short to be a correct XPM format
+ break;
+ }
+ for ( int i = 0; i < 9; i++ ) // searching for "/* XPM */"
+ if ( *mpTempPtr++ != sID[i] )
+ {
+ mbStatus = false;
+ break;
+ }
+ mnTempAvail-=9;
+ mnIdentifier++;
+ }
+ }
+ mcLastByte = mcThisByte;
+ mcThisByte = *mpTempPtr++;
+ mnTempAvail--;
+
+ if ( mnStatus & XPMDOUBLE )
+ {
+ if ( mcThisByte == 0x0a )
+ mnStatus &=~XPMDOUBLE;
+ continue;
+ }
+ if ( mnStatus & XPMREMARK )
+ {
+ if ( ( mcThisByte == '/' ) && ( mcLastByte == '*' ) )
+ mnStatus &=~XPMREMARK;
+ continue;
+ }
+ if ( mnStatus & XPMSTRING ) // characters in string
+ {
+ if ( mcThisByte == '"' )
+ {
+ mnStatus &=~XPMSTRING; // end of parameter by eol
+ break;
+ }
+ if ( mnStringSize >= ( XPMSTRINGBUF - 1 ) )
+ {
+ mbStatus = false;
+ break;
+ }
+ *pString++ = mcThisByte;
+ pString[0] = 0;
+ mnStringSize++;
+ continue;
+ }
+ else
+ { // characters beside string
+ switch ( mcThisByte )
+ {
+ case '*' :
+ if ( mcLastByte == '/' ) mnStatus |= XPMREMARK;
+ break;
+ case '/' :
+ if ( mcLastByte == '/' ) mnStatus |= XPMDOUBLE;
+ break;
+ case '"' : mnStatus |= XPMSTRING;
+ break;
+ case '{' :
+ if ( mnIdentifier == XPMDEFINITION )
+ mnIdentifier++;
+ break;
+ case '}' :
+ if ( mnIdentifier == XPMENDEXT )
+ mnStatus = XPMFINISHED;
+ break;
+ }
+ }
+ }
+ return mbStatus;
+}
+
+
+VCL_DLLPUBLIC bool ImportXPM( SvStream& rStm, Graphic& rGraphic )
+{
+ std::shared_ptr<GraphicReader> pContext = rGraphic.GetReaderContext();
+ rGraphic.SetReaderContext(nullptr);
+ XPMReader* pXPMReader = dynamic_cast<XPMReader*>( pContext.get() );
+ if (!pXPMReader)
+ {
+ pContext = std::make_shared<XPMReader>( rStm );
+ pXPMReader = static_cast<XPMReader*>( pContext.get() );
+ }
+
+ bool bRet = true;
+
+ ReadState eReadState = pXPMReader->ReadXPM( rGraphic );
+
+ if( eReadState == XPMREAD_ERROR )
+ {
+ bRet = false;
+ }
+ else if( eReadState == XPMREAD_NEED_MORE )
+ rGraphic.SetReaderContext( pContext );
+
+ return bRet;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/vcl/source/filter/jpeg/Exif.cxx b/vcl/source/filter/jpeg/Exif.cxx
new file mode 100644
index 000000000..53b55f69a
--- /dev/null
+++ b/vcl/source/filter/jpeg/Exif.cxx
@@ -0,0 +1,288 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include "Exif.hxx"
+#include <memory>
+#include <osl/endian.h>
+#include <tools/stream.hxx>
+
+Exif::Exif() :
+ maOrientation(exif::TOP_LEFT),
+ mbExifPresent(false)
+{}
+
+void Exif::setOrientation(exif::Orientation aOrientation) {
+ maOrientation = aOrientation;
+}
+
+exif::Orientation Exif::convertToOrientation(sal_Int32 value)
+{
+ switch(value) {
+ case 1: return exif::TOP_LEFT;
+ case 2: return exif::TOP_RIGHT;
+ case 3: return exif::BOTTOM_RIGHT;
+ case 4: return exif::BOTTOM_LEFT;
+ case 5: return exif::LEFT_TOP;
+ case 6: return exif::RIGHT_TOP;
+ case 7: return exif::RIGHT_BOTTOM;
+ case 8: return exif::LEFT_BOTTOM;
+ }
+ return exif::TOP_LEFT;
+}
+
+Degree10 Exif::getRotation() const
+{
+ switch(maOrientation) {
+ case exif::TOP_LEFT:
+ return 0_deg10;
+ case exif::BOTTOM_RIGHT:
+ return 1800_deg10;
+ case exif::RIGHT_TOP:
+ return 2700_deg10;
+ case exif::LEFT_BOTTOM:
+ return 900_deg10;
+ default:
+ break;
+ }
+ return 0_deg10;
+}
+
+bool Exif::read(SvStream& rStream)
+{
+ sal_Int32 nStreamPosition = rStream.Tell();
+ bool result = processJpeg(rStream, false);
+ rStream.Seek( nStreamPosition );
+
+ return result;
+}
+
+void Exif::write(SvStream& rStream)
+{
+ sal_Int32 nStreamPosition = rStream.Tell();
+ processJpeg(rStream, true);
+ rStream.Seek( nStreamPosition );
+}
+
+bool Exif::processJpeg(SvStream& rStream, bool bSetValue)
+{
+ sal_uInt16 aMagic16;
+ sal_uInt16 aLength;
+
+ sal_uInt32 aSize = rStream.TellEnd();
+ rStream.Seek(STREAM_SEEK_TO_BEGIN);
+
+ rStream.SetEndian( SvStreamEndian::BIG );
+ rStream.ReadUInt16( aMagic16 );
+
+ // Compare JPEG magic bytes
+ if( 0xFFD8 != aMagic16 )
+ {
+ return false;
+ }
+
+ sal_uInt32 aPreviousPosition = STREAM_SEEK_TO_BEGIN;
+
+ while(true)
+ {
+ sal_uInt8 aMarker = 0xD9;
+ sal_Int32 aCount;
+
+ for (aCount = 0; aCount < 7; aCount++)
+ {
+ rStream.ReadUChar( aMarker );
+ if (aMarker != 0xFF)
+ {
+ break;
+ }
+ if (aCount >= 6)
+ {
+ return false;
+ }
+ }
+
+ rStream.ReadUInt16( aLength );
+
+ if (aLength < 8 || aLength > rStream.remainingSize())
+ {
+ return false;
+ }
+
+ if (aMarker == 0xE1)
+ {
+ return processExif(rStream, aLength, bSetValue);
+ }
+ else if (aMarker == 0xD9)
+ {
+ return false;
+ }
+ else
+ {
+ sal_uInt32 aCurrentPosition = rStream.SeekRel(aLength-1);
+ if (aCurrentPosition == aPreviousPosition || aCurrentPosition > aSize)
+ {
+ return false;
+ }
+ aPreviousPosition = aCurrentPosition;
+ }
+ }
+ return false;
+}
+
+namespace {
+
+sal_uInt16 read16(sal_uInt8 const (& data)[2], bool littleEndian) {
+ if (littleEndian) {
+ return data[0] | (sal_uInt16(data[1]) << 8);
+ } else {
+ return data[1] | (sal_uInt16(data[0]) << 8);
+ }
+}
+
+void write16(sal_uInt16 value, sal_uInt8 (& data)[2], bool littleEndian) {
+ if (littleEndian) {
+ data[0] = value & 0xFF;
+ data[1] = value >> 8;
+ } else {
+ data[1] = value & 0xFF;
+ data[0] = value >> 8;
+ }
+}
+
+void write32(sal_uInt32 value, sal_uInt8 (& data)[4], bool littleEndian) {
+ if (littleEndian) {
+ data[0] = value & 0xFF;
+ data[1] = (value >> 8) & 0xFF;
+ data[2] = (value >> 16) & 0xFF;
+ data[3] = value >> 24;
+ } else {
+ data[3] = value & 0xFF;
+ data[2] = (value >> 8) & 0xFF;
+ data[1] = (value >> 16) & 0xFF;
+ data[0] = value >> 24;
+ }
+}
+
+}
+
+void Exif::processIFD(sal_uInt8* pExifData, sal_uInt16 aLength, sal_uInt16 aOffset, sal_uInt16 aNumberOfTags, bool bSetValue, bool littleEndian)
+{
+ ExifIFD* ifd = nullptr;
+
+ while (aOffset <= aLength - 12 && aNumberOfTags > 0)
+ {
+ ifd = reinterpret_cast<ExifIFD*>(&pExifData[aOffset]);
+ sal_uInt16 tag = read16(ifd->tag, littleEndian);
+
+ if (tag == ORIENTATION)
+ {
+ if(bSetValue)
+ {
+ write16(3, ifd->type, littleEndian);
+ write32(1, ifd->count, littleEndian);
+ write16(
+ maOrientation, reinterpret_cast<sal_uInt8 (&)[2]>(ifd->offset), littleEndian);
+ }
+ else
+ {
+ sal_uInt16 nIfdOffset = read16(
+ reinterpret_cast<sal_uInt8 (&)[2]>(ifd->offset), littleEndian);
+ maOrientation = convertToOrientation(nIfdOffset);
+ }
+ }
+
+ aNumberOfTags--;
+ aOffset += 12;
+ }
+}
+
+bool Exif::processExif(SvStream& rStream, sal_uInt16 aSectionLength, bool bSetValue)
+{
+ sal_uInt32 aMagic32;
+ sal_uInt16 aMagic16;
+
+ rStream.ReadUInt32( aMagic32 );
+ rStream.ReadUInt16( aMagic16 );
+
+ // Compare EXIF magic bytes
+ if( 0x45786966 != aMagic32 || 0x0000 != aMagic16)
+ {
+ return false;
+ }
+
+ sal_uInt16 aLength = aSectionLength - 6; // Length = Section - Header
+
+ std::unique_ptr<sal_uInt8[]> aExifData(new sal_uInt8[aLength]);
+ sal_uInt32 aExifDataBeginPosition = rStream.Tell();
+
+ rStream.ReadBytes(aExifData.get(), aLength);
+
+ // Exif detected
+ mbExifPresent = true;
+
+ TiffHeader* aTiffHeader = reinterpret_cast<TiffHeader*>(&aExifData[0]);
+
+ bool bIntel = aTiffHeader->byteOrder == 0x4949; //little-endian
+ bool bMotorola = aTiffHeader->byteOrder == 0x4D4D; //big-endian
+
+ if (!bIntel && !bMotorola)
+ {
+ return false;
+ }
+
+ bool bSwap = false;
+
+#ifdef OSL_BIGENDIAN
+ if (bIntel)
+ bSwap = true;
+#else
+ if (bMotorola)
+ bSwap = true;
+#endif
+
+ if (bSwap)
+ {
+ aTiffHeader->tagAlign = OSL_SWAPWORD(aTiffHeader->tagAlign);
+ aTiffHeader->offset = OSL_SWAPDWORD(aTiffHeader->offset);
+ }
+
+ if (aTiffHeader->tagAlign != 0x002A) // TIFF tag
+ {
+ return false;
+ }
+
+ sal_uInt16 aOffset = aTiffHeader->offset;
+
+ sal_uInt16 aNumberOfTags = aExifData[aOffset];
+ if (bSwap)
+ {
+ aNumberOfTags = ((aExifData[aOffset] << 8) | aExifData[aOffset+1]);
+ }
+
+ processIFD(aExifData.get(), aLength, aOffset+2, aNumberOfTags, bSetValue, bIntel);
+
+ if (bSetValue)
+ {
+ rStream.Seek(aExifDataBeginPosition);
+ rStream.WriteBytes(aExifData.get(), aLength);
+ }
+
+ return true;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/vcl/source/filter/jpeg/Exif.hxx b/vcl/source/filter/jpeg/Exif.hxx
new file mode 100644
index 000000000..287b7ae43
--- /dev/null
+++ b/vcl/source/filter/jpeg/Exif.hxx
@@ -0,0 +1,83 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#pragma once
+
+#include <tools/stream.hxx>
+#include <tools/degree.hxx>
+
+namespace exif {
+
+enum Orientation {
+ TOP_LEFT = 1,
+ TOP_RIGHT = 2,
+ BOTTOM_RIGHT = 3,
+ BOTTOM_LEFT = 4,
+ LEFT_TOP = 5,
+ RIGHT_TOP = 6,
+ RIGHT_BOTTOM = 7,
+ LEFT_BOTTOM = 8
+};
+};
+
+enum Tag {
+ ORIENTATION = 0x0112
+};
+
+class Exif final
+{
+private:
+ exif::Orientation maOrientation;
+ bool mbExifPresent;
+
+ bool processJpeg(SvStream& rStream, bool bSetValue);
+ bool processExif(SvStream& rStream, sal_uInt16 aLength, bool bSetValue);
+ void processIFD(sal_uInt8* pExifData, sal_uInt16 aLength, sal_uInt16 aOffset, sal_uInt16 aNumberOfTags, bool bSetValue, bool bLittleEndian);
+
+ struct ExifIFD {
+ sal_uInt8 tag[2];
+ sal_uInt8 type[2];
+ sal_uInt8 count[4];
+ sal_uInt8 offset[4];
+ };
+
+ struct TiffHeader {
+ sal_uInt16 byteOrder;
+ sal_uInt16 tagAlign;
+ sal_uInt32 offset;
+ };
+
+ static exif::Orientation convertToOrientation(sal_Int32 value);
+
+public:
+ Exif();
+
+ bool hasExif() const { return mbExifPresent;}
+
+ exif::Orientation getOrientation() const { return maOrientation;}
+ Degree10 getRotation() const;
+
+ void setOrientation(exif::Orientation orientation);
+
+ bool read(SvStream& rStream);
+ void write(SvStream& rStream);
+
+};
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/vcl/source/filter/jpeg/JpegReader.cxx b/vcl/source/filter/jpeg/JpegReader.cxx
new file mode 100644
index 000000000..c68ba88d7
--- /dev/null
+++ b/vcl/source/filter/jpeg/JpegReader.cxx
@@ -0,0 +1,331 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <sal/config.h>
+
+#include "jpeg.h"
+#include <jpeglib.h>
+#include <jerror.h>
+
+#include "JpegReader.hxx"
+#include <vcl/graphicfilter.hxx>
+#include <vcl/outdev.hxx>
+#include <tools/fract.hxx>
+#include <tools/stream.hxx>
+#include <memory>
+
+#define BUFFER_SIZE 4096
+
+extern "C" {
+
+/*
+ * Initialize source --- called by jpeg_read_header
+ * before any data is actually read.
+ */
+static void init_source (j_decompress_ptr cinfo)
+{
+ SourceManagerStruct * source = reinterpret_cast<SourceManagerStruct *>(cinfo->src);
+
+ /* We reset the empty-input-file flag for each image,
+ * but we don't clear the input buffer.
+ * This is correct behavior for reading a series of images from one source.
+ */
+ source->start_of_file = TRUE;
+ source->no_data_available_failures = 0;
+}
+
+}
+
+static tools::Long StreamRead( SvStream* pStream, void* pBuffer, tools::Long nBufferSize )
+{
+ tools::Long nRead = 0;
+
+ if( pStream->GetError() != ERRCODE_IO_PENDING )
+ {
+ sal_uInt64 nInitialPosition = pStream->Tell();
+
+ nRead = static_cast<tools::Long>(pStream->ReadBytes(pBuffer, nBufferSize));
+
+ if( pStream->GetError() == ERRCODE_IO_PENDING )
+ {
+ // in order to search from the old position
+ // we temporarily reset the error
+ pStream->ResetError();
+ pStream->Seek( nInitialPosition );
+ pStream->SetError( ERRCODE_IO_PENDING );
+ }
+ }
+
+ return nRead;
+}
+
+extern "C" {
+
+static boolean fill_input_buffer (j_decompress_ptr cinfo)
+{
+ SourceManagerStruct * source = reinterpret_cast<SourceManagerStruct *>(cinfo->src);
+ size_t nbytes;
+
+ nbytes = StreamRead(source->stream, source->buffer, BUFFER_SIZE);
+
+ if (!nbytes)
+ {
+ source->no_data_available_failures++;
+ if (source->start_of_file) /* Treat empty input file as fatal error */
+ {
+ ERREXIT(cinfo, JERR_INPUT_EMPTY);
+ }
+ WARNMS(cinfo, JWRN_JPEG_EOF);
+ /* Insert a fake EOI marker */
+ source->buffer[0] = JOCTET(0xFF);
+ source->buffer[1] = JOCTET(JPEG_EOI);
+ nbytes = 2;
+ }
+
+ source->pub.next_input_byte = source->buffer;
+ source->pub.bytes_in_buffer = nbytes;
+ source->start_of_file = FALSE;
+
+ return TRUE;
+}
+
+static void skip_input_data (j_decompress_ptr cinfo, long numberOfBytes)
+{
+ SourceManagerStruct * source = reinterpret_cast<SourceManagerStruct *>(cinfo->src);
+
+ /* Just a dumb implementation for now. Could use fseek() except
+ * it doesn't work on pipes. Not clear that being smart is worth
+ * any trouble anyway --- large skips are infrequent.
+ */
+ if (numberOfBytes <= 0)
+ return;
+
+ while (numberOfBytes > static_cast<tools::Long>(source->pub.bytes_in_buffer))
+ {
+ numberOfBytes -= static_cast<tools::Long>(source->pub.bytes_in_buffer);
+ (void) fill_input_buffer(cinfo);
+
+ /* note we assume that fill_input_buffer will never return false,
+ * so suspension need not be handled.
+ */
+ }
+ source->pub.next_input_byte += static_cast<size_t>(numberOfBytes);
+ source->pub.bytes_in_buffer -= static_cast<size_t>(numberOfBytes);
+}
+
+static void term_source (j_decompress_ptr)
+{
+ /* no work necessary here */
+}
+
+}
+
+void jpeg_svstream_src (j_decompress_ptr cinfo, void* input)
+{
+ SourceManagerStruct * source;
+ SvStream* stream = static_cast<SvStream*>(input);
+
+ /* The source object and input buffer are made permanent so that a series
+ * of JPEG images can be read from the same file by calling jpeg_stdio_src
+ * only before the first one. (If we discarded the buffer at the end of
+ * one image, we'd likely lose the start of the next one.)
+ * This makes it unsafe to use this manager and a different source
+ * manager serially with the same JPEG object. Caveat programmer.
+ */
+
+ if (cinfo->src == nullptr)
+ { /* first time for this JPEG object? */
+ cinfo->src = static_cast<jpeg_source_mgr *>(
+ (*cinfo->mem->alloc_small) (reinterpret_cast<j_common_ptr>(cinfo), JPOOL_PERMANENT, sizeof(SourceManagerStruct)));
+ source = reinterpret_cast<SourceManagerStruct *>(cinfo->src);
+ source->buffer = static_cast<JOCTET *>(
+ (*cinfo->mem->alloc_small) (reinterpret_cast<j_common_ptr>(cinfo), JPOOL_PERMANENT, BUFFER_SIZE * sizeof(JOCTET)));
+ }
+
+ source = reinterpret_cast<SourceManagerStruct*>(cinfo->src);
+ source->pub.init_source = init_source;
+ source->pub.fill_input_buffer = fill_input_buffer;
+ source->pub.skip_input_data = skip_input_data;
+ source->pub.resync_to_restart = jpeg_resync_to_restart; /* use default method */
+ source->pub.term_source = term_source;
+ source->stream = stream;
+ source->pub.bytes_in_buffer = 0; /* forces fill_input_buffer on first read */
+ source->pub.next_input_byte = nullptr; /* until buffer loaded */
+}
+
+JPEGReader::JPEGReader( SvStream& rStream, GraphicFilterImportFlags nImportFlags ) :
+ mrStream ( rStream ),
+ mnLastPos ( rStream.Tell() ),
+ mnLastLines ( 0 ),
+ mbSetLogSize ( nImportFlags & GraphicFilterImportFlags::SetLogsizeForJpeg )
+{
+ maUpperName = "SVIJPEG";
+
+ if (!(nImportFlags & GraphicFilterImportFlags::UseExistingBitmap))
+ {
+ mpBitmap.reset(new Bitmap());
+ mpIncompleteAlpha.reset(new Bitmap());
+ }
+}
+
+JPEGReader::~JPEGReader()
+{
+}
+
+bool JPEGReader::CreateBitmap(JPEGCreateBitmapParam const & rParam)
+{
+ if (rParam.nWidth > SAL_MAX_INT32 / 8 || rParam.nHeight > SAL_MAX_INT32 / 8)
+ return false; // avoid overflows later
+
+ if (rParam.nWidth == 0 || rParam.nHeight == 0)
+ return false;
+
+ Size aSize(rParam.nWidth, rParam.nHeight);
+ bool bGray = rParam.bGray;
+
+ mpBitmap.reset(new Bitmap());
+
+ sal_uInt64 nSize = aSize.Width() * aSize.Height();
+
+ if (nSize > SAL_MAX_INT32 / (bGray?1:3))
+ return false;
+
+ if( bGray )
+ {
+ BitmapPalette aGrayPal( 256 );
+
+ for( sal_uInt16 n = 0; n < 256; n++ )
+ {
+ const sal_uInt8 cGray = static_cast<sal_uInt8>(n);
+ aGrayPal[ n ] = BitmapColor( cGray, cGray, cGray );
+ }
+
+ mpBitmap.reset(new Bitmap(aSize, vcl::PixelFormat::N8_BPP, &aGrayPal));
+ }
+ else
+ {
+ mpBitmap.reset(new Bitmap(aSize, vcl::PixelFormat::N24_BPP));
+ }
+
+ if (mbSetLogSize)
+ {
+ unsigned long nUnit = rParam.density_unit;
+
+ if (((1 == nUnit) || (2 == nUnit)) && rParam.X_density && rParam.Y_density )
+ {
+ Fraction aFractX( 1, rParam.X_density );
+ Fraction aFractY( 1, rParam.Y_density );
+ MapMode aMapMode( nUnit == 1 ? MapUnit::MapInch : MapUnit::MapCM, Point(), aFractX, aFractY );
+ Size aPrefSize = OutputDevice::LogicToLogic(aSize, aMapMode, MapMode(MapUnit::Map100thMM));
+
+ mpBitmap->SetPrefSize(aPrefSize);
+ mpBitmap->SetPrefMapMode(MapMode(MapUnit::Map100thMM));
+ }
+ }
+
+ return true;
+}
+
+Graphic JPEGReader::CreateIntermediateGraphic(tools::Long nLines)
+{
+ Graphic aGraphic;
+ const Size aSizePixel(mpBitmap->GetSizePixel());
+
+ if (!mnLastLines)
+ {
+ mpIncompleteAlpha.reset(new Bitmap(aSizePixel, vcl::PixelFormat::N1_BPP));
+ mpIncompleteAlpha->Erase(COL_WHITE);
+ }
+
+ if (nLines && (nLines < aSizePixel.Height()))
+ {
+ const tools::Long nNewLines = nLines - mnLastLines;
+
+ if (nNewLines > 0)
+ {
+ {
+ BitmapScopedWriteAccess pAccess(*mpIncompleteAlpha);
+ pAccess->SetFillColor(COL_BLACK);
+ pAccess->FillRect(tools::Rectangle(Point(0, mnLastLines), Size(pAccess->Width(), nNewLines)));
+ }
+
+ aGraphic = BitmapEx(*mpBitmap, *mpIncompleteAlpha);
+ }
+ else
+ {
+ aGraphic = BitmapEx(*mpBitmap);
+ }
+ }
+ else
+ {
+ aGraphic = BitmapEx(*mpBitmap);
+ }
+
+ mnLastLines = nLines;
+
+ return aGraphic;
+}
+
+ReadState JPEGReader::Read( Graphic& rGraphic, GraphicFilterImportFlags nImportFlags, BitmapScopedWriteAccess* ppAccess )
+{
+ ReadState eReadState;
+ bool bRet = false;
+
+ // seek back to the original position
+ mrStream.Seek( mnLastPos );
+
+ // read the (partial) image
+ tools::Long nLines;
+ ReadJPEG( this, &mrStream, &nLines, nImportFlags, ppAccess );
+
+ auto bUseExistingBitmap = static_cast<bool>(nImportFlags & GraphicFilterImportFlags::UseExistingBitmap);
+ if (bUseExistingBitmap || !mpBitmap->IsEmpty())
+ {
+ if( mrStream.GetError() == ERRCODE_IO_PENDING )
+ {
+ rGraphic = CreateIntermediateGraphic(nLines);
+ }
+ else
+ {
+ if (!bUseExistingBitmap)
+ rGraphic = BitmapEx(*mpBitmap);
+ }
+
+ bRet = true;
+ }
+ else if( mrStream.GetError() == ERRCODE_IO_PENDING )
+ {
+ bRet = true;
+ }
+
+ // Set status ( Pending has priority )
+ if (mrStream.GetError() == ERRCODE_IO_PENDING)
+ {
+ eReadState = JPEGREAD_NEED_MORE;
+ mrStream.ResetError();
+ }
+ else
+ {
+ eReadState = bRet ? JPEGREAD_OK : JPEGREAD_ERROR;
+ }
+
+ return eReadState;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/vcl/source/filter/jpeg/JpegReader.hxx b/vcl/source/filter/jpeg/JpegReader.hxx
new file mode 100644
index 000000000..f9a2eb292
--- /dev/null
+++ b/vcl/source/filter/jpeg/JpegReader.hxx
@@ -0,0 +1,74 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#ifndef INCLUDED_VCL_SOURCE_FILTER_JPEG_JPEGREADER_HXX
+#define INCLUDED_VCL_SOURCE_FILTER_JPEG_JPEGREADER_HXX
+
+#include <vcl/graph.hxx>
+#include <vcl/bitmap.hxx>
+
+#include <bitmap/BitmapWriteAccess.hxx>
+#include <graphic/GraphicReader.hxx>
+
+enum class GraphicFilterImportFlags;
+
+enum ReadState
+{
+ JPEGREAD_OK,
+ JPEGREAD_ERROR,
+ JPEGREAD_NEED_MORE
+};
+
+struct JPEGCreateBitmapParam
+{
+ tools::ULong nWidth;
+ tools::ULong nHeight;
+ tools::ULong density_unit;
+ tools::ULong X_density;
+ tools::ULong Y_density;
+
+ bool bGray;
+};
+
+class JPEGReader : public GraphicReader
+{
+ SvStream& mrStream;
+ std::unique_ptr<Bitmap> mpBitmap;
+ std::unique_ptr<Bitmap> mpIncompleteAlpha;
+
+ tools::Long mnLastPos;
+ tools::Long mnLastLines;
+ bool mbSetLogSize;
+
+ Graphic CreateIntermediateGraphic(tools::Long nLines);
+
+public:
+ JPEGReader( SvStream& rStream, GraphicFilterImportFlags nImportFlags );
+ virtual ~JPEGReader() override;
+
+ ReadState Read(Graphic& rGraphic, GraphicFilterImportFlags nImportFlags, BitmapScopedWriteAccess* ppAccess);
+
+ bool CreateBitmap(JPEGCreateBitmapParam const & param);
+
+ Bitmap& GetBitmap() { return *mpBitmap; }
+};
+
+#endif // INCLUDED_VCL_SOURCE_FILTER_JPEG_JPEGREADER_HXX
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/vcl/source/filter/jpeg/JpegTransform.cxx b/vcl/source/filter/jpeg/JpegTransform.cxx
new file mode 100644
index 000000000..16c0c060b
--- /dev/null
+++ b/vcl/source/filter/jpeg/JpegTransform.cxx
@@ -0,0 +1,42 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <sal/config.h>
+
+#include "jpeg.h"
+
+#include "JpegTransform.hxx"
+
+JpegTransform::JpegTransform(SvStream& rInputStream, SvStream& rOutputStream) :
+ maRotate ( 0 ),
+ mrInputStream ( rInputStream ),
+ mrOutputStream ( rOutputStream )
+{}
+
+void JpegTransform::perform()
+{
+ Transform( &mrInputStream, &mrOutputStream, maRotate );
+}
+
+void JpegTransform::setRotate(Degree10 aRotate)
+{
+ maRotate = aRotate;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/vcl/source/filter/jpeg/JpegTransform.hxx b/vcl/source/filter/jpeg/JpegTransform.hxx
new file mode 100644
index 000000000..2b5c6dfd6
--- /dev/null
+++ b/vcl/source/filter/jpeg/JpegTransform.hxx
@@ -0,0 +1,40 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#ifndef INCLUDED_VCL_SOURCE_FILTER_JPEG_JPEGTRANSFORM_HXX
+#define INCLUDED_VCL_SOURCE_FILTER_JPEG_JPEGTRANSFORM_HXX
+
+#include <tools/stream.hxx>
+
+class JpegTransform final
+{
+ Degree10 maRotate;
+ SvStream& mrInputStream;
+ SvStream& mrOutputStream;
+
+public:
+ JpegTransform(SvStream& rInputStream, SvStream& rOutputStream);
+
+ void setRotate(Degree10 aRotate);
+ void perform();
+};
+
+#endif // INCLUDED_VCL_SOURCE_FILTER_JPEG_JPEGTRANSFORM_HXX
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/vcl/source/filter/jpeg/JpegWriter.cxx b/vcl/source/filter/jpeg/JpegWriter.cxx
new file mode 100644
index 000000000..026ab887b
--- /dev/null
+++ b/vcl/source/filter/jpeg/JpegWriter.cxx
@@ -0,0 +1,265 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <sal/config.h>
+#include <sal/log.hxx>
+
+#include "jpeg.h"
+#include <jpeglib.h>
+#include <jerror.h>
+
+#include "JpegWriter.hxx"
+#include <vcl/BitmapReadAccess.hxx>
+#include <vcl/FilterConfigItem.hxx>
+#include <tools/helpers.hxx>
+#include <tools/stream.hxx>
+
+#define BUFFER_SIZE 4096
+
+namespace {
+
+struct DestinationManagerStruct
+{
+ jpeg_destination_mgr pub; /* public fields */
+ SvStream* stream; /* target stream */
+ JOCTET * buffer; /* start of buffer */
+};
+
+}
+
+extern "C" {
+
+static void init_destination (j_compress_ptr cinfo)
+{
+ DestinationManagerStruct * destination = reinterpret_cast<DestinationManagerStruct *>(cinfo->dest);
+
+ /* Allocate the output buffer -- it will be released when done with image */
+ destination->buffer = static_cast<JOCTET *>(
+ (*cinfo->mem->alloc_small) (reinterpret_cast<j_common_ptr>(cinfo), JPOOL_IMAGE, BUFFER_SIZE * sizeof(JOCTET)));
+
+ destination->pub.next_output_byte = destination->buffer;
+ destination->pub.free_in_buffer = BUFFER_SIZE;
+}
+
+static boolean empty_output_buffer (j_compress_ptr cinfo)
+{
+ DestinationManagerStruct * destination = reinterpret_cast<DestinationManagerStruct *>(cinfo->dest);
+
+ if (destination->stream->WriteBytes(destination->buffer, BUFFER_SIZE) != BUFFER_SIZE)
+ {
+ ERREXIT(cinfo, JERR_FILE_WRITE);
+ }
+
+ destination->pub.next_output_byte = destination->buffer;
+ destination->pub.free_in_buffer = BUFFER_SIZE;
+
+ return TRUE;
+}
+
+static void term_destination (j_compress_ptr cinfo)
+{
+ DestinationManagerStruct * destination = reinterpret_cast<DestinationManagerStruct *>(cinfo->dest);
+ size_t datacount = BUFFER_SIZE - destination->pub.free_in_buffer;
+
+ /* Write any data remaining in the buffer */
+ if (datacount > 0)
+ {
+ if (destination->stream->WriteBytes(destination->buffer, datacount) != datacount)
+ {
+ ERREXIT(cinfo, JERR_FILE_WRITE);
+ }
+ }
+}
+
+}
+
+void jpeg_svstream_dest (j_compress_ptr cinfo, void* output)
+{
+ SvStream* stream = static_cast<SvStream*>(output);
+ DestinationManagerStruct * destination;
+
+ /* The destination object is made permanent so that multiple JPEG images
+ * can be written to the same file without re-executing jpeg_svstream_dest.
+ * This makes it dangerous to use this manager and a different destination
+ * manager serially with the same JPEG object, because their private object
+ * sizes may be different. Caveat programmer.
+ */
+ if (cinfo->dest == nullptr)
+ { /* first time for this JPEG object? */
+ cinfo->dest = static_cast<jpeg_destination_mgr*>(
+ (*cinfo->mem->alloc_small) (reinterpret_cast<j_common_ptr>(cinfo), JPOOL_PERMANENT, sizeof(DestinationManagerStruct)));
+ }
+
+ destination = reinterpret_cast<DestinationManagerStruct *>(cinfo->dest);
+ destination->pub.init_destination = init_destination;
+ destination->pub.empty_output_buffer = empty_output_buffer;
+ destination->pub.term_destination = term_destination;
+ destination->stream = stream;
+}
+
+JPEGWriter::JPEGWriter( SvStream& rStream, const css::uno::Sequence< css::beans::PropertyValue >* pFilterData, bool* pExportWasGrey ) :
+ mrStream ( rStream ),
+ mpBuffer ( nullptr ),
+ mbNative ( false ),
+ mpExpWasGrey ( pExportWasGrey )
+{
+ FilterConfigItem aConfigItem( pFilterData );
+ mbGreys = aConfigItem.ReadInt32( "ColorMode", 0 ) != 0;
+ mnQuality = aConfigItem.ReadInt32( "Quality", 75 );
+ maChromaSubsampling = aConfigItem.ReadInt32( "ChromaSubsamplingMode", 0 );
+
+ if ( pFilterData )
+ {
+ for( const auto& rValue : *pFilterData )
+ {
+ if ( rValue.Name == "StatusIndicator" )
+ {
+ rValue.Value >>= mxStatusIndicator;
+ }
+ }
+ }
+}
+
+void* JPEGWriter::GetScanline( tools::Long nY )
+{
+ void* pScanline = nullptr;
+
+ if( mpReadAccess )
+ {
+ if( mbNative )
+ {
+ pScanline = mpReadAccess->GetScanline( nY );
+ }
+ else if( mpBuffer )
+ {
+ BitmapColor aColor;
+ tools::Long nWidth = mpReadAccess->Width();
+ sal_uInt8* pTmp = mpBuffer;
+
+ if( mpReadAccess->HasPalette() )
+ {
+ Scanline pScanlineRead = mpReadAccess->GetScanline( nY );
+ for( tools::Long nX = 0; nX < nWidth; nX++ )
+ {
+ aColor = mpReadAccess->GetPaletteColor( mpReadAccess->GetIndexFromData( pScanlineRead, nX ) );
+ *pTmp++ = aColor.GetRed();
+ if ( !mbGreys )
+ {
+ *pTmp++ = aColor.GetGreen();
+ *pTmp++ = aColor.GetBlue();
+ }
+ }
+ }
+ else
+ {
+ Scanline pScanlineRead = mpReadAccess->GetScanline( nY );
+ for( tools::Long nX = 0; nX < nWidth; nX++ )
+ {
+ aColor = mpReadAccess->GetPixelFromData( pScanlineRead, nX );
+ *pTmp++ = aColor.GetRed();
+ if ( !mbGreys )
+ {
+ *pTmp++ = aColor.GetGreen();
+ *pTmp++ = aColor.GetBlue();
+ }
+ }
+ }
+
+ pScanline = mpBuffer;
+ }
+ }
+
+ return pScanline;
+}
+
+bool JPEGWriter::Write( const Graphic& rGraphic )
+{
+ bool bRet = false;
+
+ if ( mxStatusIndicator.is() )
+ {
+ mxStatusIndicator->start( OUString(), 100 );
+ }
+
+ // This slightly weird logic is here to match the behaviour in ImpGraphic::ImplGetBitmap
+ // and is necessary to match pre-existing behaviour. We should probably pass down the expected
+ // background color for alpha from the higher layers.
+ Bitmap aGraphicBmp;
+ if (rGraphic.GetType() == GraphicType::Bitmap)
+ aGraphicBmp = rGraphic.GetBitmapEx().GetBitmap(COL_WHITE);
+ else
+ aGraphicBmp = rGraphic.GetBitmapEx().GetBitmap();
+
+ if ( mbGreys )
+ {
+ if ( !aGraphicBmp.Convert( BmpConversion::N8BitGreys ) )
+ aGraphicBmp = rGraphic.GetBitmapEx().GetBitmap();
+ }
+
+ mpReadAccess = Bitmap::ScopedReadAccess(aGraphicBmp);
+ if( mpReadAccess )
+ {
+ if ( !mbGreys ) // bitmap was not explicitly converted into greyscale,
+ { // check if source is greyscale only
+ bool bIsGrey = true;
+
+ tools::Long nWidth = mpReadAccess->Width();
+ for ( tools::Long nY = 0; bIsGrey && ( nY < mpReadAccess->Height() ); nY++ )
+ {
+ BitmapColor aColor;
+ Scanline pScanlineRead = mpReadAccess->GetScanline( nY );
+ for( tools::Long nX = 0; bIsGrey && ( nX < nWidth ); nX++ )
+ {
+ aColor = mpReadAccess->HasPalette() ? mpReadAccess->GetPaletteColor( mpReadAccess->GetIndexFromData( pScanlineRead, nX ) )
+ : mpReadAccess->GetPixelFromData( pScanlineRead, nX );
+ bIsGrey = ( aColor.GetRed() == aColor.GetGreen() ) && ( aColor.GetRed() == aColor.GetBlue() );
+ }
+ }
+ if ( bIsGrey )
+ mbGreys = true;
+ }
+ if( mpExpWasGrey )
+ *mpExpWasGrey = mbGreys;
+
+ if ( mbGreys )
+ mbNative = ( mpReadAccess->GetScanlineFormat() == ScanlineFormat::N8BitPal && aGraphicBmp.HasGreyPalette8Bit());
+ else
+ mbNative = ( mpReadAccess->GetScanlineFormat() == ScanlineFormat::N24BitTcRgb );
+
+ if( !mbNative )
+ mpBuffer = new sal_uInt8[ AlignedWidth4Bytes( mbGreys ? mpReadAccess->Width() * 8L : mpReadAccess->Width() * 24L ) ];
+
+ SAL_INFO("vcl", "\nJPEG Export - DPI X: " << rGraphic.GetPPI().getX() << "\nJPEG Export - DPI Y: " << rGraphic.GetPPI().getY());
+
+ bRet = WriteJPEG( this, &mrStream, mpReadAccess->Width(),
+ mpReadAccess->Height(), rGraphic.GetPPI(), mbGreys,
+ mnQuality, maChromaSubsampling, mxStatusIndicator );
+
+ delete[] mpBuffer;
+ mpBuffer = nullptr;
+
+ mpReadAccess.reset();
+ }
+ if ( mxStatusIndicator.is() )
+ mxStatusIndicator->end();
+
+ return bRet;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/vcl/source/filter/jpeg/JpegWriter.hxx b/vcl/source/filter/jpeg/JpegWriter.hxx
new file mode 100644
index 000000000..76666cfa0
--- /dev/null
+++ b/vcl/source/filter/jpeg/JpegWriter.hxx
@@ -0,0 +1,57 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#ifndef INCLUDED_VCL_SOURCE_FILTER_JPEG_JPEGWRITER_HXX
+#define INCLUDED_VCL_SOURCE_FILTER_JPEG_JPEGWRITER_HXX
+
+#include <vcl/bitmap.hxx>
+#include <vcl/BitmapReadAccess.hxx>
+#include <vcl/graph.hxx>
+
+#include <com/sun/star/uno/Sequence.h>
+#include <com/sun/star/beans/PropertyValue.hpp>
+#include <com/sun/star/task/XStatusIndicator.hpp>
+
+class JPEGWriter final
+{
+ SvStream& mrStream;
+ Bitmap::ScopedReadAccess mpReadAccess;
+ sal_uInt8* mpBuffer;
+ bool mbNative;
+ bool mbGreys;
+ sal_Int32 mnQuality;
+ sal_Int32 maChromaSubsampling;
+
+ bool* mpExpWasGrey;
+
+ css::uno::Reference< css::task::XStatusIndicator > mxStatusIndicator;
+
+public:
+ JPEGWriter( SvStream& rStream,
+ const css::uno::Sequence< css::beans::PropertyValue >* pFilterData,
+ bool* pExportWasGrey );
+
+ void* GetScanline( tools::Long nY );
+ bool Write( const Graphic& rGraphic );
+
+};
+
+#endif // INCLUDED_VCL_SOURCE_FILTER_JPEG_JPEGWRITER_HXX
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/vcl/source/filter/jpeg/jinclude.h b/vcl/source/filter/jpeg/jinclude.h
new file mode 100644
index 000000000..b863b11c4
--- /dev/null
+++ b/vcl/source/filter/jpeg/jinclude.h
@@ -0,0 +1,79 @@
+/*
+ * jinclude.h
+ *
+ * Copyright (C) 1991-1994, Thomas G. Lane.
+ * This file is part of the Independent JPEG Group's software.
+ * For conditions of distribution and use, see the accompanying README file.
+ *
+ * This file exists to provide a single place to fix any problems with
+ * including the wrong system include files. (Common problems are taken
+ * care of by the standard jconfig symbols, but on really weird systems
+ * you may have to edit this file.)
+ *
+ * NOTE: this file is NOT intended to be included by applications using the
+ * JPEG library. Most applications need only include jpeglib.h.
+ */
+
+/* Include auto-config file to find out which system include files we need. */
+
+#include <jconfig.h> /* auto configuration options */
+#define JCONFIG_INCLUDED /* so that jpeglib.h doesn't do it again */
+
+/*
+ * We need the NULL macro and size_t typedef.
+ * On an ANSI-conforming system it is sufficient to include <stddef.h>.
+ * Otherwise, we get them from <stdlib.h> or <stdio.h>; we may have to
+ * pull in <sys/types.h> as well.
+ * Note that the core JPEG library does not require <stdio.h>;
+ * only the default error handler and data source/destination modules do.
+ * But we must pull it in because of the references to FILE in jpeglib.h.
+ * You can remove those references if you want to compile without <stdio.h>.
+ */
+
+#ifdef HAVE_STDDEF_H
+#include <stddef.h>
+#endif
+
+#ifdef HAVE_STDLIB_H
+#include <stdlib.h>
+#endif
+
+#ifdef NEED_SYS_TYPES_H
+#include <sys/types.h>
+#endif
+
+#include <stdio.h>
+
+/*
+ * We need memory copying and zeroing functions, plus strncpy().
+ * ANSI and System V implementations declare these in <string.h>.
+ * BSD doesn't have the mem() functions, but it does have bcopy()/bzero().
+ * Some systems may declare memset and memcpy in <memory.h>.
+ *
+ * NOTE: we assume the size parameters to these functions are of type size_t.
+ * Change the casts in these macros if not!
+ */
+
+#ifdef NEED_BSD_STRINGS
+
+#include <strings.h>
+#define MEMZERO(target, size) bzero((void*)(target), (size_t)(size))
+#define MEMCOPY(dest, src, size) bcopy((const void*)(src), (void*)(dest), (size_t)(size))
+
+#else /* not BSD, assume ANSI/SysV string lib */
+
+#include <string.h>
+#define MEMZERO(target, size) memset((void*)(target), 0, (size_t)(size))
+#define MEMCOPY(dest, src, size) memcpy((void*)(dest), (const void*)(src), (size_t)(size))
+
+#endif
+
+/*
+ * In ANSI C, and indeed any rational implementation, size_t is also the
+ * type returned by sizeof(). However, it seems there are some irrational
+ * implementations out there, in which sizeof() returns an int even though
+ * size_t is defined as long or unsigned long. To ensure consistent results
+ * we always use this SIZEOF() macro in place of using sizeof() directly.
+ */
+
+#define SIZEOF(object) ((size_t)sizeof(object))
diff --git a/vcl/source/filter/jpeg/jpeg.cxx b/vcl/source/filter/jpeg/jpeg.cxx
new file mode 100644
index 000000000..e7158b858
--- /dev/null
+++ b/vcl/source/filter/jpeg/jpeg.cxx
@@ -0,0 +1,62 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include "JpegReader.hxx"
+#include "JpegWriter.hxx"
+#include "jpeg.hxx"
+
+#include <vcl/graphicfilter.hxx>
+
+VCL_DLLPUBLIC bool ImportJPEG( SvStream& rInputStream, Graphic& rGraphic, GraphicFilterImportFlags nImportFlags, BitmapScopedWriteAccess* ppAccess )
+{
+ bool bReturn = true;
+
+ std::shared_ptr<GraphicReader> pContext = rGraphic.GetReaderContext();
+ rGraphic.SetReaderContext(nullptr);
+ JPEGReader* pJPEGReader = dynamic_cast<JPEGReader*>( pContext.get() );
+ if (!pJPEGReader)
+ {
+ pContext = std::make_shared<JPEGReader>( rInputStream, nImportFlags );
+ pJPEGReader = static_cast<JPEGReader*>( pContext.get() );
+ }
+
+ ReadState eReadState = pJPEGReader->Read( rGraphic, nImportFlags, ppAccess );
+
+ if( eReadState == JPEGREAD_ERROR )
+ {
+ bReturn = false;
+ }
+ else if( eReadState == JPEGREAD_NEED_MORE )
+ {
+ rGraphic.SetReaderContext( pContext );
+ }
+
+ return bReturn;
+}
+
+bool ExportJPEG(SvStream& rOutputStream, const Graphic& rGraphic,
+ const css::uno::Sequence<css::beans::PropertyValue>* pFilterData,
+ bool* pExportWasGrey)
+{
+ JPEGWriter aJPEGWriter( rOutputStream, pFilterData, pExportWasGrey );
+ bool bReturn = aJPEGWriter.Write( rGraphic );
+ return bReturn;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/vcl/source/filter/jpeg/jpeg.h b/vcl/source/filter/jpeg/jpeg.h
new file mode 100644
index 000000000..a7ddcffa6
--- /dev/null
+++ b/vcl/source/filter/jpeg/jpeg.h
@@ -0,0 +1,67 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#ifndef INCLUDED_VCL_SOURCE_FILTER_JPEG_JPEG_H
+#define INCLUDED_VCL_SOURCE_FILTER_JPEG_JPEG_H
+
+#include <sal/config.h>
+
+#include <com/sun/star/uno/Reference.hxx>
+#include <basegfx/vector/b2dsize.hxx>
+#include <bitmap/BitmapWriteAccess.hxx>
+
+#include <jpeglib.h>
+
+namespace com::sun::star::task {
+ class XStatusIndicator;
+}
+class JPEGReader;
+class JPEGWriter;
+class Size;
+class SvStream;
+enum class GraphicFilterImportFlags;
+
+void jpeg_svstream_src (j_decompress_ptr cinfo, void* infile);
+
+void jpeg_svstream_dest (j_compress_ptr cinfo, void* outfile);
+
+bool WriteJPEG( JPEGWriter* pJPEGWriter, void* pOutputStream,
+ tools::Long nWidth, tools::Long nHeight, basegfx::B2DSize const & aPPI, bool bGreyScale,
+ tools::Long nQualityPercent, tools::Long aChromaSubsampling,
+ css::uno::Reference<css::task::XStatusIndicator> const & status);
+
+void ReadJPEG( JPEGReader* pJPEGReader, void* pInputStream, tools::Long* pLines,
+ GraphicFilterImportFlags nImportFlags,
+ BitmapScopedWriteAccess* ppAccess );
+
+void Transform(void* pInputStream, void* pOutputStream, Degree10 nAngle);
+
+/* Expanded data source object for stdio input */
+
+struct SourceManagerStruct {
+ jpeg_source_mgr pub; /* public fields */
+ SvStream* stream; /* source stream */
+ JOCTET* buffer; /* start of buffer */
+ boolean start_of_file; /* have we gotten any data yet? */
+ int no_data_available_failures;
+};
+
+#endif
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/vcl/source/filter/jpeg/jpeg.hxx b/vcl/source/filter/jpeg/jpeg.hxx
new file mode 100644
index 000000000..96c9280c2
--- /dev/null
+++ b/vcl/source/filter/jpeg/jpeg.hxx
@@ -0,0 +1,39 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#ifndef INCLUDED_VCL_SOURCE_FILTER_JPEG_JPEG_HXX
+#define INCLUDED_VCL_SOURCE_FILTER_JPEG_JPEG_HXX
+
+#include <vcl/graph.hxx>
+#include <vcl/graphicfilter.hxx>
+
+#include <bitmap/BitmapWriteAccess.hxx>
+
+#include <com/sun/star/uno/Sequence.h>
+
+VCL_DLLPUBLIC bool ImportJPEG( SvStream& rInputStream, Graphic& rGraphic, GraphicFilterImportFlags nImportFlags, BitmapScopedWriteAccess* ppAccess );
+
+bool ExportJPEG(SvStream& rOutputStream,
+ const Graphic& rGraphic,
+ const css::uno::Sequence< css::beans::PropertyValue >* pFilterData,
+ bool* pExportWasGrey);
+
+#endif // INCLUDED_VCL_SOURCE_FILTER_JPEG_JPEG_HXX
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/vcl/source/filter/jpeg/jpegc.cxx b/vcl/source/filter/jpeg/jpegc.cxx
new file mode 100644
index 000000000..8807927a8
--- /dev/null
+++ b/vcl/source/filter/jpeg/jpegc.cxx
@@ -0,0 +1,520 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <sal/config.h>
+#include <sal/log.hxx>
+#include <o3tl/float_int_conversion.hxx>
+#include <o3tl/safeint.hxx>
+
+#include <stdio.h>
+#include <setjmp.h>
+#include <jpeglib.h>
+
+#include <com/sun/star/task/XStatusIndicator.hpp>
+
+extern "C" {
+#include "transupp.h"
+}
+
+#include "jpeg.h"
+#include "JpegReader.hxx"
+#include "JpegWriter.hxx"
+#include <memory>
+#include <unotools/configmgr.hxx>
+#include <vcl/graphicfilter.hxx>
+
+#ifdef _MSC_VER
+#pragma warning(push)
+#pragma warning (disable: 4324) /* disable to __declspec(align()) aligned warning */
+#endif
+
+namespace {
+
+struct ErrorManagerStruct
+{
+ jpeg_error_mgr pub;
+ jmp_buf setjmp_buffer;
+};
+
+}
+
+#ifdef _MSC_VER
+#pragma warning(pop)
+#endif
+
+extern "C" {
+
+static void errorExit (j_common_ptr cinfo)
+{
+ char buffer[JMSG_LENGTH_MAX];
+ (*cinfo->err->format_message) (cinfo, buffer);
+ SAL_WARN("vcl.filter", "fatal failure reading JPEG: " << buffer);
+ ErrorManagerStruct * error = reinterpret_cast<ErrorManagerStruct *>(cinfo->err);
+ longjmp(error->setjmp_buffer, 1);
+}
+
+static void outputMessage (j_common_ptr cinfo)
+{
+ char buffer[JMSG_LENGTH_MAX];
+ (*cinfo->err->format_message) (cinfo, buffer);
+ SAL_WARN("vcl.filter", "failure reading JPEG: " << buffer);
+}
+
+}
+
+static int GetWarningLimit()
+{
+ return utl::ConfigManager::IsFuzzing() ? 5 : 1000;
+}
+
+extern "C" {
+
+static void emitMessage (j_common_ptr cinfo, int msg_level)
+{
+ if (msg_level < 0)
+ {
+ // ofz#3002 try to retain some degree of recoverability up to some
+ // reasonable limit (initially using ImageMagick's current limit of
+ // 1000), then bail.
+ // https://libjpeg-turbo.org/pmwiki/uploads/About/TwoIssueswiththeJPEGStandard.pdf
+ if (++cinfo->err->num_warnings > GetWarningLimit())
+ cinfo->err->error_exit(cinfo);
+ else
+ cinfo->err->output_message(cinfo);
+ }
+ else if (cinfo->err->trace_level >= msg_level)
+ cinfo->err->output_message(cinfo);
+}
+
+}
+
+namespace {
+
+class JpegDecompressOwner
+{
+public:
+ void set(jpeg_decompress_struct *cinfo)
+ {
+ m_cinfo = cinfo;
+ }
+ ~JpegDecompressOwner()
+ {
+ if (m_cinfo != nullptr)
+ {
+ jpeg_destroy_decompress(m_cinfo);
+ }
+ }
+private:
+ jpeg_decompress_struct *m_cinfo = nullptr;
+};
+
+class JpegCompressOwner
+{
+public:
+ void set(jpeg_compress_struct *cinfo)
+ {
+ m_cinfo = cinfo;
+ }
+ ~JpegCompressOwner()
+ {
+ if (m_cinfo != nullptr)
+ {
+ jpeg_destroy_compress(m_cinfo);
+ }
+ }
+private:
+ jpeg_compress_struct *m_cinfo = nullptr;
+};
+
+struct JpegStuff
+{
+ jpeg_decompress_struct cinfo;
+ ErrorManagerStruct jerr;
+ JpegDecompressOwner aOwner;
+ std::unique_ptr<BitmapScopedWriteAccess> pScopedAccess;
+ std::vector<sal_uInt8> pScanLineBuffer;
+ std::vector<sal_uInt8> pCYMKBuffer;
+};
+
+}
+
+static void ReadJPEG(JpegStuff& rContext, JPEGReader* pJPEGReader, void* pInputStream, tools::Long* pLines,
+ GraphicFilterImportFlags nImportFlags,
+ BitmapScopedWriteAccess* ppAccess)
+{
+ if (setjmp(rContext.jerr.setjmp_buffer))
+ {
+ return;
+ }
+
+ rContext.cinfo.err = jpeg_std_error(&rContext.jerr.pub);
+ rContext.jerr.pub.error_exit = errorExit;
+ rContext.jerr.pub.output_message = outputMessage;
+ rContext.jerr.pub.emit_message = emitMessage;
+
+ jpeg_create_decompress(&rContext.cinfo);
+ rContext.aOwner.set(&rContext.cinfo);
+ jpeg_svstream_src(&rContext.cinfo, pInputStream);
+ SourceManagerStruct *source = reinterpret_cast<SourceManagerStruct*>(rContext.cinfo.src);
+ jpeg_read_header(&rContext.cinfo, TRUE);
+
+ rContext.cinfo.scale_num = 1;
+ rContext.cinfo.scale_denom = 1;
+ rContext.cinfo.output_gamma = 1.0;
+ rContext.cinfo.raw_data_out = FALSE;
+ rContext.cinfo.quantize_colors = FALSE;
+
+ jpeg_calc_output_dimensions(&rContext.cinfo);
+
+ tools::Long nWidth = rContext.cinfo.output_width;
+ tools::Long nHeight = rContext.cinfo.output_height;
+
+ tools::Long nResult = 0;
+ if (utl::ConfigManager::IsFuzzing() && (o3tl::checked_multiply(nWidth, nHeight, nResult) || nResult > 4000000))
+ return;
+
+ bool bGray = (rContext.cinfo.output_components == 1);
+
+ JPEGCreateBitmapParam aCreateBitmapParam;
+
+ aCreateBitmapParam.nWidth = nWidth;
+ aCreateBitmapParam.nHeight = nHeight;
+
+ aCreateBitmapParam.density_unit = rContext.cinfo.density_unit;
+ aCreateBitmapParam.X_density = rContext.cinfo.X_density;
+ aCreateBitmapParam.Y_density = rContext.cinfo.Y_density;
+ aCreateBitmapParam.bGray = bGray;
+
+ const auto bOnlyCreateBitmap = static_cast<bool>(nImportFlags & GraphicFilterImportFlags::OnlyCreateBitmap);
+ const auto bUseExistingBitmap = static_cast<bool>(nImportFlags & GraphicFilterImportFlags::UseExistingBitmap);
+ bool bBitmapCreated = bUseExistingBitmap;
+ if (!bBitmapCreated)
+ bBitmapCreated = pJPEGReader->CreateBitmap(aCreateBitmapParam);
+
+ if (bBitmapCreated && !bOnlyCreateBitmap)
+ {
+ if (nImportFlags & GraphicFilterImportFlags::UseExistingBitmap)
+ // ppAccess must be set if this flag is used.
+ assert(ppAccess);
+ else
+ rContext.pScopedAccess.reset(new BitmapScopedWriteAccess(pJPEGReader->GetBitmap()));
+
+ BitmapScopedWriteAccess& pAccess = bUseExistingBitmap ? *ppAccess : *rContext.pScopedAccess;
+
+ if (pAccess)
+ {
+ int nPixelSize = 3;
+ J_COLOR_SPACE best_out_color_space = JCS_RGB;
+ ScanlineFormat eScanlineFormat = ScanlineFormat::N24BitTcRgb;
+ ScanlineFormat eFinalFormat = pAccess->GetScanlineFormat();
+
+ if (bGray)
+ {
+ best_out_color_space = JCS_GRAYSCALE;
+ eScanlineFormat = ScanlineFormat::N8BitPal;
+ nPixelSize = 1;
+ }
+#if defined(JCS_EXTENSIONS)
+ else if (eFinalFormat == ScanlineFormat::N24BitTcBgr)
+ {
+ best_out_color_space = JCS_EXT_BGR;
+ eScanlineFormat = eFinalFormat;
+ nPixelSize = 3;
+ }
+ else if (eFinalFormat == ScanlineFormat::N32BitTcBgra)
+ {
+ best_out_color_space = JCS_EXT_BGRA;
+ eScanlineFormat = eFinalFormat;
+ nPixelSize = 4;
+ }
+ else if (eFinalFormat == ScanlineFormat::N32BitTcRgba)
+ {
+ best_out_color_space = JCS_EXT_RGBA;
+ eScanlineFormat = eFinalFormat;
+ nPixelSize = 4;
+ }
+ else if (eFinalFormat == ScanlineFormat::N32BitTcArgb)
+ {
+ best_out_color_space = JCS_EXT_ARGB;
+ eScanlineFormat = eFinalFormat;
+ nPixelSize = 4;
+ }
+#endif
+ if (rContext.cinfo.jpeg_color_space == JCS_YCCK)
+ rContext.cinfo.out_color_space = JCS_CMYK;
+
+ if (rContext.cinfo.out_color_space != JCS_CMYK)
+ rContext.cinfo.out_color_space = best_out_color_space;
+
+ jpeg_start_decompress(&rContext.cinfo);
+
+ JSAMPLE* aRangeLimit = rContext.cinfo.sample_range_limit;
+
+ rContext.pScanLineBuffer.resize(nWidth * nPixelSize);
+
+ if (rContext.cinfo.out_color_space == JCS_CMYK)
+ {
+ rContext.pCYMKBuffer.resize(nWidth * 4);
+ }
+
+ // tdf#138950 allow up to one short read (no_data_available_failures <= 1) to not trigger cancelling import
+ for (*pLines = 0; *pLines < nHeight && source->no_data_available_failures <= 1; (*pLines)++)
+ {
+ size_t yIndex = *pLines;
+
+ sal_uInt8* p = (rContext.cinfo.out_color_space == JCS_CMYK) ? rContext.pCYMKBuffer.data() : rContext.pScanLineBuffer.data();
+ jpeg_read_scanlines(&rContext.cinfo, reinterpret_cast<JSAMPARRAY>(&p), 1);
+
+ if (rContext.cinfo.out_color_space == JCS_CMYK)
+ {
+ // convert CMYK to RGB
+ Scanline pScanline = pAccess->GetScanline(yIndex);
+ for (tools::Long cmyk = 0, x = 0; cmyk < nWidth * 4; cmyk += 4, ++x)
+ {
+ int color_C = 255 - rContext.pCYMKBuffer[cmyk + 0];
+ int color_M = 255 - rContext.pCYMKBuffer[cmyk + 1];
+ int color_Y = 255 - rContext.pCYMKBuffer[cmyk + 2];
+ int color_K = 255 - rContext.pCYMKBuffer[cmyk + 3];
+
+ sal_uInt8 cRed = aRangeLimit[255L - (color_C + color_K)];
+ sal_uInt8 cGreen = aRangeLimit[255L - (color_M + color_K)];
+ sal_uInt8 cBlue = aRangeLimit[255L - (color_Y + color_K)];
+
+ pAccess->SetPixelOnData(pScanline, x, BitmapColor(cRed, cGreen, cBlue));
+ }
+ }
+ else
+ {
+ pAccess->CopyScanline(yIndex, rContext.pScanLineBuffer.data(), eScanlineFormat, rContext.pScanLineBuffer.size());
+ }
+
+ /* PENDING ??? */
+ if (rContext.cinfo.err->msg_code == 113)
+ break;
+ }
+
+ rContext.pScanLineBuffer.clear();
+ rContext.pCYMKBuffer.clear();
+ }
+ rContext.pScopedAccess.reset();
+ }
+
+ if (bBitmapCreated && !bOnlyCreateBitmap)
+ {
+ jpeg_finish_decompress(&rContext.cinfo);
+ }
+ else
+ {
+ jpeg_abort_decompress(&rContext.cinfo);
+ }
+}
+
+void ReadJPEG( JPEGReader* pJPEGReader, void* pInputStream, tools::Long* pLines,
+ GraphicFilterImportFlags nImportFlags,
+ BitmapScopedWriteAccess* ppAccess )
+{
+ JpegStuff aContext;
+ ReadJPEG(aContext, pJPEGReader, pInputStream, pLines, nImportFlags, ppAccess);
+}
+
+bool WriteJPEG( JPEGWriter* pJPEGWriter, void* pOutputStream,
+ tools::Long nWidth, tools::Long nHeight, basegfx::B2DSize const & rPPI, bool bGreys,
+ tools::Long nQualityPercent, tools::Long aChromaSubsampling,
+ css::uno::Reference<css::task::XStatusIndicator> const & status )
+{
+ jpeg_compress_struct cinfo;
+ ErrorManagerStruct jerr;
+ void* pScanline;
+ tools::Long nY;
+
+ JpegCompressOwner aOwner;
+
+ if ( setjmp( jerr.setjmp_buffer ) )
+ {
+ return false;
+ }
+
+ cinfo.err = jpeg_std_error( &jerr.pub );
+ jerr.pub.error_exit = errorExit;
+ jerr.pub.output_message = outputMessage;
+
+ jpeg_create_compress( &cinfo );
+ aOwner.set(&cinfo);
+ jpeg_svstream_dest( &cinfo, pOutputStream );
+
+ cinfo.image_width = static_cast<JDIMENSION>(nWidth);
+ cinfo.image_height = static_cast<JDIMENSION>(nHeight);
+ if ( bGreys )
+ {
+ cinfo.input_components = 1;
+ cinfo.in_color_space = JCS_GRAYSCALE;
+ }
+ else
+ {
+ cinfo.input_components = 3;
+ cinfo.in_color_space = JCS_RGB;
+ }
+
+ jpeg_set_defaults( &cinfo );
+ jpeg_set_quality( &cinfo, static_cast<int>(nQualityPercent), FALSE );
+
+ if (o3tl::convertsToAtMost(rPPI.getX(), 65535) && o3tl::convertsToAtMost(rPPI.getY(), 65535))
+ {
+ cinfo.density_unit = 1;
+ cinfo.X_density = rPPI.getX();
+ cinfo.Y_density = rPPI.getY();
+ }
+ else
+ {
+ SAL_WARN("vcl.filter", "ignoring too large PPI " << rPPI);
+ }
+
+ if ( ( nWidth > 128 ) || ( nHeight > 128 ) )
+ jpeg_simple_progression( &cinfo );
+
+ if (aChromaSubsampling == 1) // YUV 4:4:4
+ {
+ cinfo.comp_info[0].h_samp_factor = 1;
+ cinfo.comp_info[0].v_samp_factor = 1;
+ }
+ else if (aChromaSubsampling == 2) // YUV 4:2:2
+ {
+ cinfo.comp_info[0].h_samp_factor = 2;
+ cinfo.comp_info[0].v_samp_factor = 1;
+ }
+ else if (aChromaSubsampling == 3) // YUV 4:2:0
+ {
+ cinfo.comp_info[0].h_samp_factor = 2;
+ cinfo.comp_info[0].v_samp_factor = 2;
+ }
+
+ jpeg_start_compress( &cinfo, TRUE );
+
+ for( nY = 0; nY < nHeight; nY++ )
+ {
+ pScanline = pJPEGWriter->GetScanline( nY );
+
+ if( pScanline )
+ {
+ jpeg_write_scanlines( &cinfo, reinterpret_cast<JSAMPARRAY>(&pScanline), 1 );
+ }
+
+ if( status.is() )
+ {
+ status->setValue( nY * 100L / nHeight );
+ }
+ }
+
+ jpeg_finish_compress(&cinfo);
+
+ return true;
+}
+
+void Transform(void* pInputStream, void* pOutputStream, Degree10 nAngle)
+{
+ jpeg_transform_info aTransformOption;
+ JCOPY_OPTION aCopyOption = JCOPYOPT_ALL;
+
+ jpeg_decompress_struct aSourceInfo;
+ jpeg_compress_struct aDestinationInfo;
+ ErrorManagerStruct aSourceError;
+ ErrorManagerStruct aDestinationError;
+
+ jvirt_barray_ptr* aSourceCoefArrays = nullptr;
+ jvirt_barray_ptr* aDestinationCoefArrays = nullptr;
+
+ aTransformOption.force_grayscale = FALSE;
+ aTransformOption.trim = FALSE;
+ aTransformOption.perfect = FALSE;
+ aTransformOption.crop = FALSE;
+
+ // Angle to transform option
+ // 90 Clockwise = 270 Counterclockwise
+ switch (nAngle.get())
+ {
+ case 2700:
+ aTransformOption.transform = JXFORM_ROT_90;
+ break;
+ case 1800:
+ aTransformOption.transform = JXFORM_ROT_180;
+ break;
+ case 900:
+ aTransformOption.transform = JXFORM_ROT_270;
+ break;
+ default:
+ aTransformOption.transform = JXFORM_NONE;
+ }
+
+ // Decompression
+ aSourceInfo.err = jpeg_std_error(&aSourceError.pub);
+ aSourceInfo.err->error_exit = errorExit;
+ aSourceInfo.err->output_message = outputMessage;
+
+ // Compression
+ aDestinationInfo.err = jpeg_std_error(&aDestinationError.pub);
+ aDestinationInfo.err->error_exit = errorExit;
+ aDestinationInfo.err->output_message = outputMessage;
+
+ aDestinationInfo.optimize_coding = TRUE;
+
+ JpegDecompressOwner aDecompressOwner;
+ JpegCompressOwner aCompressOwner;
+
+ if (setjmp(aSourceError.setjmp_buffer))
+ {
+ jpeg_destroy_decompress(&aSourceInfo);
+ jpeg_destroy_compress(&aDestinationInfo);
+ return;
+ }
+ if (setjmp(aDestinationError.setjmp_buffer))
+ {
+ jpeg_destroy_decompress(&aSourceInfo);
+ jpeg_destroy_compress(&aDestinationInfo);
+ return;
+ }
+
+ jpeg_create_decompress(&aSourceInfo);
+ aDecompressOwner.set(&aSourceInfo);
+ jpeg_create_compress(&aDestinationInfo);
+ aCompressOwner.set(&aDestinationInfo);
+
+ jpeg_svstream_src (&aSourceInfo, pInputStream);
+
+ jcopy_markers_setup(&aSourceInfo, aCopyOption);
+ jpeg_read_header(&aSourceInfo, TRUE);
+ jtransform_request_workspace(&aSourceInfo, &aTransformOption);
+
+ aSourceCoefArrays = jpeg_read_coefficients(&aSourceInfo);
+ jpeg_copy_critical_parameters(&aSourceInfo, &aDestinationInfo);
+
+ aDestinationCoefArrays = jtransform_adjust_parameters(&aSourceInfo, &aDestinationInfo, aSourceCoefArrays, &aTransformOption);
+ jpeg_svstream_dest (&aDestinationInfo, pOutputStream);
+
+ // Compute optimal Huffman coding tables instead of precomputed tables
+ aDestinationInfo.optimize_coding = TRUE;
+ jpeg_write_coefficients(&aDestinationInfo, aDestinationCoefArrays);
+ jcopy_markers_execute(&aSourceInfo, &aDestinationInfo, aCopyOption);
+ jtransform_execute_transformation(&aSourceInfo, &aDestinationInfo, aSourceCoefArrays, &aTransformOption);
+
+ jpeg_finish_compress(&aDestinationInfo);
+
+ jpeg_finish_decompress(&aSourceInfo);
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/vcl/source/filter/jpeg/jpegcomp.h b/vcl/source/filter/jpeg/jpegcomp.h
new file mode 100644
index 000000000..9b3e36775
--- /dev/null
+++ b/vcl/source/filter/jpeg/jpegcomp.h
@@ -0,0 +1,18 @@
+/*
+ * jpegcomp.h
+ *
+ * Copyright (C) 2010, D. R. Commander
+ * For conditions of distribution and use, see the accompanying README file.
+ *
+ * JPEG compatibility macros
+ * These declarations are considered internal to the JPEG library; most
+ * applications using the library shouldn't need to include this file.
+ */
+
+#if JPEG_LIB_VERSION >= 70
+#define min_DCT_h_scaled_size_ min_DCT_h_scaled_size
+#define min_DCT_v_scaled_size_ min_DCT_v_scaled_size
+#else
+#define min_DCT_h_scaled_size_ min_DCT_scaled_size
+#define min_DCT_v_scaled_size_ min_DCT_scaled_size
+#endif
diff --git a/vcl/source/filter/jpeg/transupp.c b/vcl/source/filter/jpeg/transupp.c
new file mode 100644
index 000000000..d26cb9510
--- /dev/null
+++ b/vcl/source/filter/jpeg/transupp.c
@@ -0,0 +1,1570 @@
+/*
+ * transupp.c
+ *
+ * This file was part of the Independent JPEG Group's software:
+ * Copyright (C) 1997-2011, Thomas G. Lane, Guido Vollbeding.
+ * Modifications:
+ * Copyright (C) 2010, D. R. Commander.
+ * For conditions of distribution and use, see the accompanying README file.
+ *
+ * This file contains image transformation routines and other utility code
+ * used by the jpegtran sample application. These are NOT part of the core
+ * JPEG library. But we keep these routines separate from jpegtran.c to
+ * ease the task of maintaining jpegtran-like programs that have other user
+ * interfaces.
+ */
+
+#include <sal/config.h>
+
+#include "jinclude.h"
+#include <jerror.h>
+#include <jpeglib.h>
+#include "transupp.h" /* My own external interface */
+#include "jpegcomp.h"
+
+/* Definition of jdiv_round_up is copied here from jutils.c in jpeg-8c.tar.gz,
+ just as the rest of this file appears to be copied here from transupp.c in
+ jpeg-8c.tar.gz: */
+static long
+jdiv_round_up (long a, long b)
+/* Compute a/b rounded up to next integer, ie, ceil(a/b) */
+/* Assumes a >= 0, b > 0 */
+{
+ return (a + b - 1) / b;
+}
+
+#if JPEG_LIB_VERSION >= 70
+#define dstinfo_min_DCT_h_scaled_size dstinfo->min_DCT_h_scaled_size
+#define dstinfo_min_DCT_v_scaled_size dstinfo->min_DCT_v_scaled_size
+#else
+#define dstinfo_min_DCT_h_scaled_size DCTSIZE
+#define dstinfo_min_DCT_v_scaled_size DCTSIZE
+#endif
+
+
+#if TRANSFORMS_SUPPORTED
+
+/*
+ * Lossless image transformation routines. These routines work on DCT
+ * coefficient arrays and thus do not require any lossy decompression
+ * or recompression of the image.
+ * Thanks to Guido Vollbeding for the initial design and code of this feature,
+ * and to Ben Jackson for introducing the cropping feature.
+ *
+ * Horizontal flipping is done in-place, using a single top-to-bottom
+ * pass through the virtual source array. It will thus be much the
+ * fastest option for images larger than main memory.
+ *
+ * The other routines require a set of destination virtual arrays, so they
+ * need twice as much memory as jpegtran normally does. The destination
+ * arrays are always written in normal scan order (top to bottom) because
+ * the virtual array manager expects this. The source arrays will be scanned
+ * in the corresponding order, which means multiple passes through the source
+ * arrays for most of the transforms. That could result in much thrashing
+ * if the image is larger than main memory.
+ *
+ * If cropping or trimming is involved, the destination arrays may be smaller
+ * than the source arrays. Note it is not possible to do horizontal flip
+ * in-place when a nonzero Y crop offset is specified, since we'd have to move
+ * data from one block row to another but the virtual array manager doesn't
+ * guarantee we can touch more than one row at a time. So in that case,
+ * we have to use a separate destination array.
+ *
+ * Some notes about the operating environment of the individual transform
+ * routines:
+ * 1. Both the source and destination virtual arrays are allocated from the
+ * source JPEG object, and therefore should be manipulated by calling the
+ * source's memory manager.
+ * 2. The destination's component count should be used. It may be smaller
+ * than the source's when forcing to grayscale.
+ * 3. Likewise the destination's sampling factors should be used. When
+ * forcing to grayscale the destination's sampling factors will be all 1,
+ * and we may as well take that as the effective iMCU size.
+ * 4. When "trim" is in effect, the destination's dimensions will be the
+ * trimmed values but the source's will be untrimmed.
+ * 5. When "crop" is in effect, the destination's dimensions will be the
+ * cropped values but the source's will be uncropped. Each transform
+ * routine is responsible for picking up source data starting at the
+ * correct X and Y offset for the crop region. (The X and Y offsets
+ * passed to the transform routines are measured in iMCU blocks of the
+ * destination.)
+ * 6. All the routines assume that the source and destination buffers are
+ * padded out to a full iMCU boundary. This is true, although for the
+ * source buffer it is an undocumented property of jdcoefct.c.
+ */
+
+static void lcl_jcopy_block_row (JBLOCKROW input_row, JBLOCKROW output_row, JDIMENSION num_blocks)
+/* Copy a row of coefficient blocks from one place to another. */
+{
+#ifdef FMEMCOPY
+ FMEMCOPY(output_row, input_row, num_blocks * (DCTSIZE2 * SIZEOF(JCOEF)));
+#else
+ JCOEFPTR inptr, outptr;
+ long count;
+
+ inptr = (JCOEFPTR) input_row;
+ outptr = (JCOEFPTR) output_row;
+ for (count = (long) num_blocks * DCTSIZE2; count > 0; count--) {
+ *outptr++ = *inptr++;
+ }
+#endif
+}
+
+LOCAL(void)
+do_crop (j_decompress_ptr srcinfo, j_compress_ptr dstinfo,
+ JDIMENSION x_crop_offset, JDIMENSION y_crop_offset,
+ jvirt_barray_ptr *src_coef_arrays,
+ jvirt_barray_ptr *dst_coef_arrays)
+/* Crop. This is only used when no rotate/flip is requested with the crop. */
+{
+ JDIMENSION dst_blk_y, x_crop_blocks, y_crop_blocks;
+ int ci, offset_y;
+ JBLOCKARRAY src_buffer, dst_buffer;
+ jpeg_component_info *compptr;
+
+ /* We simply have to copy the right amount of data (the destination's
+ * image size) starting at the given X and Y offsets in the source.
+ */
+ for (ci = 0; ci < dstinfo->num_components; ci++) {
+ compptr = dstinfo->comp_info + ci;
+ x_crop_blocks = x_crop_offset * compptr->h_samp_factor;
+ y_crop_blocks = y_crop_offset * compptr->v_samp_factor;
+ for (dst_blk_y = 0; dst_blk_y < compptr->height_in_blocks;
+ dst_blk_y += compptr->v_samp_factor) {
+ dst_buffer = (*srcinfo->mem->access_virt_barray)
+ ((j_common_ptr) srcinfo, dst_coef_arrays[ci], dst_blk_y,
+ (JDIMENSION) compptr->v_samp_factor, TRUE);
+ src_buffer = (*srcinfo->mem->access_virt_barray)
+ ((j_common_ptr) srcinfo, src_coef_arrays[ci],
+ dst_blk_y + y_crop_blocks,
+ (JDIMENSION) compptr->v_samp_factor, FALSE);
+ for (offset_y = 0; offset_y < compptr->v_samp_factor; offset_y++) {
+ lcl_jcopy_block_row(src_buffer[offset_y] + x_crop_blocks,
+ dst_buffer[offset_y],
+ compptr->width_in_blocks);
+ }
+ }
+ }
+}
+
+
+LOCAL(void)
+do_flip_h_no_crop (j_decompress_ptr srcinfo, j_compress_ptr dstinfo,
+ JDIMENSION x_crop_offset,
+ jvirt_barray_ptr *src_coef_arrays)
+/* Horizontal flip; done in-place, so no separate dest array is required.
+ * NB: this only works when y_crop_offset is zero.
+ */
+{
+ JDIMENSION MCU_cols, comp_width, blk_x, blk_y, x_crop_blocks;
+ int ci, k, offset_y;
+ JBLOCKARRAY buffer;
+ JCOEFPTR ptr1, ptr2;
+ JCOEF temp1, temp2;
+ jpeg_component_info *compptr;
+
+ /* Horizontal mirroring of DCT blocks is accomplished by swapping
+ * pairs of blocks in-place. Within a DCT block, we perform horizontal
+ * mirroring by changing the signs of odd-numbered columns.
+ * Partial iMCUs at the right edge are left untouched.
+ */
+ MCU_cols = srcinfo->output_width /
+ (dstinfo->max_h_samp_factor * dstinfo_min_DCT_h_scaled_size);
+
+ for (ci = 0; ci < dstinfo->num_components; ci++) {
+ compptr = dstinfo->comp_info + ci;
+ comp_width = MCU_cols * compptr->h_samp_factor;
+ x_crop_blocks = x_crop_offset * compptr->h_samp_factor;
+ for (blk_y = 0; blk_y < compptr->height_in_blocks;
+ blk_y += compptr->v_samp_factor) {
+ buffer = (*srcinfo->mem->access_virt_barray)
+ ((j_common_ptr) srcinfo, src_coef_arrays[ci], blk_y,
+ (JDIMENSION) compptr->v_samp_factor, TRUE);
+ for (offset_y = 0; offset_y < compptr->v_samp_factor; offset_y++) {
+ /* Do the mirroring */
+ for (blk_x = 0; blk_x * 2 < comp_width; blk_x++) {
+ ptr1 = buffer[offset_y][blk_x];
+ ptr2 = buffer[offset_y][comp_width - blk_x - 1];
+ /* this unrolled loop doesn't need to know which row it's on... */
+ for (k = 0; k < DCTSIZE2; k += 2) {
+ temp1 = *ptr1; /* swap even column */
+ temp2 = *ptr2;
+ *ptr1++ = temp2;
+ *ptr2++ = temp1;
+ temp1 = *ptr1; /* swap odd column with sign change */
+ temp2 = *ptr2;
+ *ptr1++ = -temp2;
+ *ptr2++ = -temp1;
+ }
+ }
+ if (x_crop_blocks > 0) {
+ /* Now left-justify the portion of the data to be kept.
+ * We can't use a single lcl_jcopy_block_row() call because that routine
+ * depends on memcpy(), whose behavior is unspecified for overlapping
+ * source and destination areas. Sigh.
+ */
+ for (blk_x = 0; blk_x < compptr->width_in_blocks; blk_x++) {
+ lcl_jcopy_block_row(buffer[offset_y] + blk_x + x_crop_blocks,
+ buffer[offset_y] + blk_x,
+ (JDIMENSION) 1);
+ }
+ }
+ }
+ }
+ }
+}
+
+
+LOCAL(void)
+do_flip_h (j_decompress_ptr srcinfo, j_compress_ptr dstinfo,
+ JDIMENSION x_crop_offset, JDIMENSION y_crop_offset,
+ jvirt_barray_ptr *src_coef_arrays,
+ jvirt_barray_ptr *dst_coef_arrays)
+/* Horizontal flip in general cropping case */
+{
+ JDIMENSION MCU_cols, comp_width, dst_blk_x, dst_blk_y;
+ JDIMENSION x_crop_blocks, y_crop_blocks;
+ int ci, k, offset_y;
+ JBLOCKARRAY src_buffer, dst_buffer;
+ JBLOCKROW src_row_ptr, dst_row_ptr;
+ JCOEFPTR src_ptr, dst_ptr;
+ jpeg_component_info *compptr;
+
+ /* Here we must output into a separate array because we can't touch
+ * different rows of a single virtual array simultaneously. Otherwise,
+ * this is essentially the same as the routine above.
+ */
+ MCU_cols = srcinfo->output_width /
+ (dstinfo->max_h_samp_factor * dstinfo_min_DCT_h_scaled_size);
+
+ for (ci = 0; ci < dstinfo->num_components; ci++) {
+ compptr = dstinfo->comp_info + ci;
+ comp_width = MCU_cols * compptr->h_samp_factor;
+ x_crop_blocks = x_crop_offset * compptr->h_samp_factor;
+ y_crop_blocks = y_crop_offset * compptr->v_samp_factor;
+ for (dst_blk_y = 0; dst_blk_y < compptr->height_in_blocks;
+ dst_blk_y += compptr->v_samp_factor) {
+ dst_buffer = (*srcinfo->mem->access_virt_barray)
+ ((j_common_ptr) srcinfo, dst_coef_arrays[ci], dst_blk_y,
+ (JDIMENSION) compptr->v_samp_factor, TRUE);
+ src_buffer = (*srcinfo->mem->access_virt_barray)
+ ((j_common_ptr) srcinfo, src_coef_arrays[ci],
+ dst_blk_y + y_crop_blocks,
+ (JDIMENSION) compptr->v_samp_factor, FALSE);
+ for (offset_y = 0; offset_y < compptr->v_samp_factor; offset_y++) {
+ dst_row_ptr = dst_buffer[offset_y];
+ src_row_ptr = src_buffer[offset_y];
+ for (dst_blk_x = 0; dst_blk_x < compptr->width_in_blocks; dst_blk_x++) {
+ if (x_crop_blocks + dst_blk_x < comp_width) {
+ /* Do the mirrorable blocks */
+ dst_ptr = dst_row_ptr[dst_blk_x];
+ src_ptr = src_row_ptr[comp_width - x_crop_blocks - dst_blk_x - 1];
+ /* this unrolled loop doesn't need to know which row it's on... */
+ for (k = 0; k < DCTSIZE2; k += 2) {
+ *dst_ptr++ = *src_ptr++; /* copy even column */
+ *dst_ptr++ = - *src_ptr++; /* copy odd column with sign change */
+ }
+ } else {
+ /* Copy last partial block(s) verbatim */
+ lcl_jcopy_block_row(src_row_ptr + dst_blk_x + x_crop_blocks,
+ dst_row_ptr + dst_blk_x,
+ (JDIMENSION) 1);
+ }
+ }
+ }
+ }
+ }
+}
+
+
+LOCAL(void)
+do_flip_v (j_decompress_ptr srcinfo, j_compress_ptr dstinfo,
+ JDIMENSION x_crop_offset, JDIMENSION y_crop_offset,
+ jvirt_barray_ptr *src_coef_arrays,
+ jvirt_barray_ptr *dst_coef_arrays)
+/* Vertical flip */
+{
+ JDIMENSION MCU_rows, comp_height, dst_blk_x, dst_blk_y;
+ JDIMENSION x_crop_blocks, y_crop_blocks;
+ int ci, i, j, offset_y;
+ JBLOCKARRAY src_buffer, dst_buffer;
+ JBLOCKROW src_row_ptr, dst_row_ptr;
+ JCOEFPTR src_ptr, dst_ptr;
+ jpeg_component_info *compptr;
+
+ /* We output into a separate array because we can't touch different
+ * rows of the source virtual array simultaneously. Otherwise, this
+ * is a pretty straightforward analog of horizontal flip.
+ * Within a DCT block, vertical mirroring is done by changing the signs
+ * of odd-numbered rows.
+ * Partial iMCUs at the bottom edge are copied verbatim.
+ */
+ MCU_rows = srcinfo->output_height /
+ (dstinfo->max_v_samp_factor * dstinfo_min_DCT_v_scaled_size);
+
+ for (ci = 0; ci < dstinfo->num_components; ci++) {
+ compptr = dstinfo->comp_info + ci;
+ comp_height = MCU_rows * compptr->v_samp_factor;
+ x_crop_blocks = x_crop_offset * compptr->h_samp_factor;
+ y_crop_blocks = y_crop_offset * compptr->v_samp_factor;
+ for (dst_blk_y = 0; dst_blk_y < compptr->height_in_blocks;
+ dst_blk_y += compptr->v_samp_factor) {
+ dst_buffer = (*srcinfo->mem->access_virt_barray)
+ ((j_common_ptr) srcinfo, dst_coef_arrays[ci], dst_blk_y,
+ (JDIMENSION) compptr->v_samp_factor, TRUE);
+ if (y_crop_blocks + dst_blk_y < comp_height) {
+ /* Row is within the mirrorable area. */
+ src_buffer = (*srcinfo->mem->access_virt_barray)
+ ((j_common_ptr) srcinfo, src_coef_arrays[ci],
+ comp_height - y_crop_blocks - dst_blk_y -
+ (JDIMENSION) compptr->v_samp_factor,
+ (JDIMENSION) compptr->v_samp_factor, FALSE);
+ } else {
+ /* Bottom-edge blocks will be copied verbatim. */
+ src_buffer = (*srcinfo->mem->access_virt_barray)
+ ((j_common_ptr) srcinfo, src_coef_arrays[ci],
+ dst_blk_y + y_crop_blocks,
+ (JDIMENSION) compptr->v_samp_factor, FALSE);
+ }
+ for (offset_y = 0; offset_y < compptr->v_samp_factor; offset_y++) {
+ if (y_crop_blocks + dst_blk_y < comp_height) {
+ /* Row is within the mirrorable area. */
+ dst_row_ptr = dst_buffer[offset_y];
+ src_row_ptr = src_buffer[compptr->v_samp_factor - offset_y - 1];
+ src_row_ptr += x_crop_blocks;
+ for (dst_blk_x = 0; dst_blk_x < compptr->width_in_blocks;
+ dst_blk_x++) {
+ dst_ptr = dst_row_ptr[dst_blk_x];
+ src_ptr = src_row_ptr[dst_blk_x];
+ for (i = 0; i < DCTSIZE; i += 2) {
+ /* copy even row */
+ for (j = 0; j < DCTSIZE; j++)
+ *dst_ptr++ = *src_ptr++;
+ /* copy odd row with sign change */
+ for (j = 0; j < DCTSIZE; j++)
+ *dst_ptr++ = - *src_ptr++;
+ }
+ }
+ } else {
+ /* Just copy row verbatim. */
+ lcl_jcopy_block_row(src_buffer[offset_y] + x_crop_blocks,
+ dst_buffer[offset_y],
+ compptr->width_in_blocks);
+ }
+ }
+ }
+ }
+}
+
+
+LOCAL(void)
+do_transpose (j_decompress_ptr srcinfo, j_compress_ptr dstinfo,
+ JDIMENSION x_crop_offset, JDIMENSION y_crop_offset,
+ jvirt_barray_ptr *src_coef_arrays,
+ jvirt_barray_ptr *dst_coef_arrays)
+/* Transpose source into destination */
+{
+ JDIMENSION dst_blk_x, dst_blk_y, x_crop_blocks, y_crop_blocks;
+ int ci, i, j, offset_x, offset_y;
+ JBLOCKARRAY src_buffer, dst_buffer;
+ JCOEFPTR src_ptr, dst_ptr;
+ jpeg_component_info *compptr;
+
+ /* Transposing pixels within a block just requires transposing the
+ * DCT coefficients.
+ * Partial iMCUs at the edges require no special treatment; we simply
+ * process all the available DCT blocks for every component.
+ */
+ for (ci = 0; ci < dstinfo->num_components; ci++) {
+ compptr = dstinfo->comp_info + ci;
+ x_crop_blocks = x_crop_offset * compptr->h_samp_factor;
+ y_crop_blocks = y_crop_offset * compptr->v_samp_factor;
+ for (dst_blk_y = 0; dst_blk_y < compptr->height_in_blocks;
+ dst_blk_y += compptr->v_samp_factor) {
+ dst_buffer = (*srcinfo->mem->access_virt_barray)
+ ((j_common_ptr) srcinfo, dst_coef_arrays[ci], dst_blk_y,
+ (JDIMENSION) compptr->v_samp_factor, TRUE);
+ for (offset_y = 0; offset_y < compptr->v_samp_factor; offset_y++) {
+ for (dst_blk_x = 0; dst_blk_x < compptr->width_in_blocks;
+ dst_blk_x += compptr->h_samp_factor) {
+ src_buffer = (*srcinfo->mem->access_virt_barray)
+ ((j_common_ptr) srcinfo, src_coef_arrays[ci],
+ dst_blk_x + x_crop_blocks,
+ (JDIMENSION) compptr->h_samp_factor, FALSE);
+ for (offset_x = 0; offset_x < compptr->h_samp_factor; offset_x++) {
+ dst_ptr = dst_buffer[offset_y][dst_blk_x + offset_x];
+ src_ptr = src_buffer[offset_x][dst_blk_y + offset_y + y_crop_blocks];
+ for (i = 0; i < DCTSIZE; i++)
+ for (j = 0; j < DCTSIZE; j++)
+ dst_ptr[j*DCTSIZE+i] = src_ptr[i*DCTSIZE+j];
+ }
+ }
+ }
+ }
+ }
+}
+
+
+LOCAL(void)
+do_rot_90 (j_decompress_ptr srcinfo, j_compress_ptr dstinfo,
+ JDIMENSION x_crop_offset, JDIMENSION y_crop_offset,
+ jvirt_barray_ptr *src_coef_arrays,
+ jvirt_barray_ptr *dst_coef_arrays)
+/* 90 degree rotation is equivalent to
+ * 1. Transposing the image;
+ * 2. Horizontal mirroring.
+ * These two steps are merged into a single processing routine.
+ */
+{
+ JDIMENSION MCU_cols, comp_width, dst_blk_x, dst_blk_y;
+ JDIMENSION x_crop_blocks, y_crop_blocks;
+ int ci, i, j, offset_x, offset_y;
+ JBLOCKARRAY src_buffer, dst_buffer;
+ JCOEFPTR src_ptr, dst_ptr;
+ jpeg_component_info *compptr;
+
+ /* Because of the horizontal mirror step, we can't process partial iMCUs
+ * at the (output) right edge properly. They just get transposed and
+ * not mirrored.
+ */
+ MCU_cols = srcinfo->output_height /
+ (dstinfo->max_h_samp_factor * dstinfo_min_DCT_h_scaled_size);
+
+ for (ci = 0; ci < dstinfo->num_components; ci++) {
+ compptr = dstinfo->comp_info + ci;
+ comp_width = MCU_cols * compptr->h_samp_factor;
+ x_crop_blocks = x_crop_offset * compptr->h_samp_factor;
+ y_crop_blocks = y_crop_offset * compptr->v_samp_factor;
+ for (dst_blk_y = 0; dst_blk_y < compptr->height_in_blocks;
+ dst_blk_y += compptr->v_samp_factor) {
+ dst_buffer = (*srcinfo->mem->access_virt_barray)
+ ((j_common_ptr) srcinfo, dst_coef_arrays[ci], dst_blk_y,
+ (JDIMENSION) compptr->v_samp_factor, TRUE);
+ for (offset_y = 0; offset_y < compptr->v_samp_factor; offset_y++) {
+ for (dst_blk_x = 0; dst_blk_x < compptr->width_in_blocks;
+ dst_blk_x += compptr->h_samp_factor) {
+ if (x_crop_blocks + dst_blk_x < comp_width) {
+ /* Block is within the mirrorable area. */
+ src_buffer = (*srcinfo->mem->access_virt_barray)
+ ((j_common_ptr) srcinfo, src_coef_arrays[ci],
+ comp_width - x_crop_blocks - dst_blk_x -
+ (JDIMENSION) compptr->h_samp_factor,
+ (JDIMENSION) compptr->h_samp_factor, FALSE);
+ } else {
+ /* Edge blocks are transposed but not mirrored. */
+ src_buffer = (*srcinfo->mem->access_virt_barray)
+ ((j_common_ptr) srcinfo, src_coef_arrays[ci],
+ dst_blk_x + x_crop_blocks,
+ (JDIMENSION) compptr->h_samp_factor, FALSE);
+ }
+ for (offset_x = 0; offset_x < compptr->h_samp_factor; offset_x++) {
+ dst_ptr = dst_buffer[offset_y][dst_blk_x + offset_x];
+ if (x_crop_blocks + dst_blk_x < comp_width) {
+ /* Block is within the mirrorable area. */
+ src_ptr = src_buffer[compptr->h_samp_factor - offset_x - 1]
+ [dst_blk_y + offset_y + y_crop_blocks];
+ for (i = 0; i < DCTSIZE; i++) {
+ for (j = 0; j < DCTSIZE; j++)
+ dst_ptr[j*DCTSIZE+i] = src_ptr[i*DCTSIZE+j];
+ i++;
+ for (j = 0; j < DCTSIZE; j++)
+ dst_ptr[j*DCTSIZE+i] = -src_ptr[i*DCTSIZE+j];
+ }
+ } else {
+ /* Edge blocks are transposed but not mirrored. */
+ src_ptr = src_buffer[offset_x]
+ [dst_blk_y + offset_y + y_crop_blocks];
+ for (i = 0; i < DCTSIZE; i++)
+ for (j = 0; j < DCTSIZE; j++)
+ dst_ptr[j*DCTSIZE+i] = src_ptr[i*DCTSIZE+j];
+ }
+ }
+ }
+ }
+ }
+ }
+}
+
+
+LOCAL(void)
+do_rot_270 (j_decompress_ptr srcinfo, j_compress_ptr dstinfo,
+ JDIMENSION x_crop_offset, JDIMENSION y_crop_offset,
+ jvirt_barray_ptr *src_coef_arrays,
+ jvirt_barray_ptr *dst_coef_arrays)
+/* 270 degree rotation is equivalent to
+ * 1. Horizontal mirroring;
+ * 2. Transposing the image.
+ * These two steps are merged into a single processing routine.
+ */
+{
+ JDIMENSION MCU_rows, comp_height, dst_blk_x, dst_blk_y;
+ JDIMENSION x_crop_blocks, y_crop_blocks;
+ int ci, i, j, offset_x, offset_y;
+ JBLOCKARRAY src_buffer, dst_buffer;
+ JCOEFPTR src_ptr, dst_ptr;
+ jpeg_component_info *compptr;
+
+ /* Because of the horizontal mirror step, we can't process partial iMCUs
+ * at the (output) bottom edge properly. They just get transposed and
+ * not mirrored.
+ */
+ MCU_rows = srcinfo->output_width /
+ (dstinfo->max_v_samp_factor * dstinfo_min_DCT_v_scaled_size);
+
+ for (ci = 0; ci < dstinfo->num_components; ci++) {
+ compptr = dstinfo->comp_info + ci;
+ comp_height = MCU_rows * compptr->v_samp_factor;
+ x_crop_blocks = x_crop_offset * compptr->h_samp_factor;
+ y_crop_blocks = y_crop_offset * compptr->v_samp_factor;
+ for (dst_blk_y = 0; dst_blk_y < compptr->height_in_blocks;
+ dst_blk_y += compptr->v_samp_factor) {
+ dst_buffer = (*srcinfo->mem->access_virt_barray)
+ ((j_common_ptr) srcinfo, dst_coef_arrays[ci], dst_blk_y,
+ (JDIMENSION) compptr->v_samp_factor, TRUE);
+ for (offset_y = 0; offset_y < compptr->v_samp_factor; offset_y++) {
+ for (dst_blk_x = 0; dst_blk_x < compptr->width_in_blocks;
+ dst_blk_x += compptr->h_samp_factor) {
+ src_buffer = (*srcinfo->mem->access_virt_barray)
+ ((j_common_ptr) srcinfo, src_coef_arrays[ci],
+ dst_blk_x + x_crop_blocks,
+ (JDIMENSION) compptr->h_samp_factor, FALSE);
+ for (offset_x = 0; offset_x < compptr->h_samp_factor; offset_x++) {
+ dst_ptr = dst_buffer[offset_y][dst_blk_x + offset_x];
+ if (y_crop_blocks + dst_blk_y < comp_height) {
+ /* Block is within the mirrorable area. */
+ src_ptr = src_buffer[offset_x]
+ [comp_height - y_crop_blocks - dst_blk_y - offset_y - 1];
+ for (i = 0; i < DCTSIZE; i++) {
+ for (j = 0; j < DCTSIZE; j++) {
+ dst_ptr[j*DCTSIZE+i] = src_ptr[i*DCTSIZE+j];
+ j++;
+ dst_ptr[j*DCTSIZE+i] = -src_ptr[i*DCTSIZE+j];
+ }
+ }
+ } else {
+ /* Edge blocks are transposed but not mirrored. */
+ src_ptr = src_buffer[offset_x]
+ [dst_blk_y + offset_y + y_crop_blocks];
+ for (i = 0; i < DCTSIZE; i++)
+ for (j = 0; j < DCTSIZE; j++)
+ dst_ptr[j*DCTSIZE+i] = src_ptr[i*DCTSIZE+j];
+ }
+ }
+ }
+ }
+ }
+ }
+}
+
+
+LOCAL(void)
+do_rot_180 (j_decompress_ptr srcinfo, j_compress_ptr dstinfo,
+ JDIMENSION x_crop_offset, JDIMENSION y_crop_offset,
+ jvirt_barray_ptr *src_coef_arrays,
+ jvirt_barray_ptr *dst_coef_arrays)
+/* 180 degree rotation is equivalent to
+ * 1. Vertical mirroring;
+ * 2. Horizontal mirroring.
+ * These two steps are merged into a single processing routine.
+ */
+{
+ JDIMENSION MCU_cols, MCU_rows, comp_width, comp_height, dst_blk_x, dst_blk_y;
+ JDIMENSION x_crop_blocks, y_crop_blocks;
+ int ci, i, j, offset_y;
+ JBLOCKARRAY src_buffer, dst_buffer;
+ JBLOCKROW src_row_ptr, dst_row_ptr;
+ JCOEFPTR src_ptr, dst_ptr;
+ jpeg_component_info *compptr;
+
+ MCU_cols = srcinfo->output_width /
+ (dstinfo->max_h_samp_factor * dstinfo_min_DCT_h_scaled_size);
+ MCU_rows = srcinfo->output_height /
+ (dstinfo->max_v_samp_factor * dstinfo_min_DCT_v_scaled_size);
+
+ for (ci = 0; ci < dstinfo->num_components; ci++) {
+ compptr = dstinfo->comp_info + ci;
+ comp_width = MCU_cols * compptr->h_samp_factor;
+ comp_height = MCU_rows * compptr->v_samp_factor;
+ x_crop_blocks = x_crop_offset * compptr->h_samp_factor;
+ y_crop_blocks = y_crop_offset * compptr->v_samp_factor;
+ for (dst_blk_y = 0; dst_blk_y < compptr->height_in_blocks;
+ dst_blk_y += compptr->v_samp_factor) {
+ dst_buffer = (*srcinfo->mem->access_virt_barray)
+ ((j_common_ptr) srcinfo, dst_coef_arrays[ci], dst_blk_y,
+ (JDIMENSION) compptr->v_samp_factor, TRUE);
+ if (y_crop_blocks + dst_blk_y < comp_height) {
+ /* Row is within the vertically mirrorable area. */
+ src_buffer = (*srcinfo->mem->access_virt_barray)
+ ((j_common_ptr) srcinfo, src_coef_arrays[ci],
+ comp_height - y_crop_blocks - dst_blk_y -
+ (JDIMENSION) compptr->v_samp_factor,
+ (JDIMENSION) compptr->v_samp_factor, FALSE);
+ } else {
+ /* Bottom-edge rows are only mirrored horizontally. */
+ src_buffer = (*srcinfo->mem->access_virt_barray)
+ ((j_common_ptr) srcinfo, src_coef_arrays[ci],
+ dst_blk_y + y_crop_blocks,
+ (JDIMENSION) compptr->v_samp_factor, FALSE);
+ }
+ for (offset_y = 0; offset_y < compptr->v_samp_factor; offset_y++) {
+ dst_row_ptr = dst_buffer[offset_y];
+ if (y_crop_blocks + dst_blk_y < comp_height) {
+ /* Row is within the mirrorable area. */
+ src_row_ptr = src_buffer[compptr->v_samp_factor - offset_y - 1];
+ for (dst_blk_x = 0; dst_blk_x < compptr->width_in_blocks; dst_blk_x++) {
+ dst_ptr = dst_row_ptr[dst_blk_x];
+ if (x_crop_blocks + dst_blk_x < comp_width) {
+ /* Process the blocks that can be mirrored both ways. */
+ src_ptr = src_row_ptr[comp_width - x_crop_blocks - dst_blk_x - 1];
+ for (i = 0; i < DCTSIZE; i += 2) {
+ /* For even row, negate every odd column. */
+ for (j = 0; j < DCTSIZE; j += 2) {
+ *dst_ptr++ = *src_ptr++;
+ *dst_ptr++ = - *src_ptr++;
+ }
+ /* For odd row, negate every even column. */
+ for (j = 0; j < DCTSIZE; j += 2) {
+ *dst_ptr++ = - *src_ptr++;
+ *dst_ptr++ = *src_ptr++;
+ }
+ }
+ } else {
+ /* Any remaining right-edge blocks are only mirrored vertically. */
+ src_ptr = src_row_ptr[x_crop_blocks + dst_blk_x];
+ for (i = 0; i < DCTSIZE; i += 2) {
+ for (j = 0; j < DCTSIZE; j++)
+ *dst_ptr++ = *src_ptr++;
+ for (j = 0; j < DCTSIZE; j++)
+ *dst_ptr++ = - *src_ptr++;
+ }
+ }
+ }
+ } else {
+ /* Remaining rows are just mirrored horizontally. */
+ src_row_ptr = src_buffer[offset_y];
+ for (dst_blk_x = 0; dst_blk_x < compptr->width_in_blocks; dst_blk_x++) {
+ if (x_crop_blocks + dst_blk_x < comp_width) {
+ /* Process the blocks that can be mirrored. */
+ dst_ptr = dst_row_ptr[dst_blk_x];
+ src_ptr = src_row_ptr[comp_width - x_crop_blocks - dst_blk_x - 1];
+ for (i = 0; i < DCTSIZE2; i += 2) {
+ *dst_ptr++ = *src_ptr++;
+ *dst_ptr++ = - *src_ptr++;
+ }
+ } else {
+ /* Any remaining right-edge blocks are only copied. */
+ lcl_jcopy_block_row(src_row_ptr + dst_blk_x + x_crop_blocks,
+ dst_row_ptr + dst_blk_x,
+ (JDIMENSION) 1);
+ }
+ }
+ }
+ }
+ }
+ }
+}
+
+
+LOCAL(void)
+do_transverse (j_decompress_ptr srcinfo, j_compress_ptr dstinfo,
+ JDIMENSION x_crop_offset, JDIMENSION y_crop_offset,
+ jvirt_barray_ptr *src_coef_arrays,
+ jvirt_barray_ptr *dst_coef_arrays)
+/* Transverse transpose is equivalent to
+ * 1. 180 degree rotation;
+ * 2. Transposition;
+ * or
+ * 1. Horizontal mirroring;
+ * 2. Transposition;
+ * 3. Horizontal mirroring.
+ * These steps are merged into a single processing routine.
+ */
+{
+ JDIMENSION MCU_cols, MCU_rows, comp_width, comp_height, dst_blk_x, dst_blk_y;
+ JDIMENSION x_crop_blocks, y_crop_blocks;
+ int ci, i, j, offset_x, offset_y;
+ JBLOCKARRAY src_buffer, dst_buffer;
+ JCOEFPTR src_ptr, dst_ptr;
+ jpeg_component_info *compptr;
+
+ MCU_cols = srcinfo->output_height /
+ (dstinfo->max_h_samp_factor * dstinfo_min_DCT_h_scaled_size);
+ MCU_rows = srcinfo->output_width /
+ (dstinfo->max_v_samp_factor * dstinfo_min_DCT_v_scaled_size);
+
+ for (ci = 0; ci < dstinfo->num_components; ci++) {
+ compptr = dstinfo->comp_info + ci;
+ comp_width = MCU_cols * compptr->h_samp_factor;
+ comp_height = MCU_rows * compptr->v_samp_factor;
+ x_crop_blocks = x_crop_offset * compptr->h_samp_factor;
+ y_crop_blocks = y_crop_offset * compptr->v_samp_factor;
+ for (dst_blk_y = 0; dst_blk_y < compptr->height_in_blocks;
+ dst_blk_y += compptr->v_samp_factor) {
+ dst_buffer = (*srcinfo->mem->access_virt_barray)
+ ((j_common_ptr) srcinfo, dst_coef_arrays[ci], dst_blk_y,
+ (JDIMENSION) compptr->v_samp_factor, TRUE);
+ for (offset_y = 0; offset_y < compptr->v_samp_factor; offset_y++) {
+ for (dst_blk_x = 0; dst_blk_x < compptr->width_in_blocks;
+ dst_blk_x += compptr->h_samp_factor) {
+ if (x_crop_blocks + dst_blk_x < comp_width) {
+ /* Block is within the mirrorable area. */
+ src_buffer = (*srcinfo->mem->access_virt_barray)
+ ((j_common_ptr) srcinfo, src_coef_arrays[ci],
+ comp_width - x_crop_blocks - dst_blk_x -
+ (JDIMENSION) compptr->h_samp_factor,
+ (JDIMENSION) compptr->h_samp_factor, FALSE);
+ } else {
+ src_buffer = (*srcinfo->mem->access_virt_barray)
+ ((j_common_ptr) srcinfo, src_coef_arrays[ci],
+ dst_blk_x + x_crop_blocks,
+ (JDIMENSION) compptr->h_samp_factor, FALSE);
+ }
+ for (offset_x = 0; offset_x < compptr->h_samp_factor; offset_x++) {
+ dst_ptr = dst_buffer[offset_y][dst_blk_x + offset_x];
+ if (y_crop_blocks + dst_blk_y < comp_height) {
+ if (x_crop_blocks + dst_blk_x < comp_width) {
+ /* Block is within the mirrorable area. */
+ src_ptr = src_buffer[compptr->h_samp_factor - offset_x - 1]
+ [comp_height - y_crop_blocks - dst_blk_y - offset_y - 1];
+ for (i = 0; i < DCTSIZE; i++) {
+ for (j = 0; j < DCTSIZE; j++) {
+ dst_ptr[j*DCTSIZE+i] = src_ptr[i*DCTSIZE+j];
+ j++;
+ dst_ptr[j*DCTSIZE+i] = -src_ptr[i*DCTSIZE+j];
+ }
+ i++;
+ for (j = 0; j < DCTSIZE; j++) {
+ dst_ptr[j*DCTSIZE+i] = -src_ptr[i*DCTSIZE+j];
+ j++;
+ dst_ptr[j*DCTSIZE+i] = src_ptr[i*DCTSIZE+j];
+ }
+ }
+ } else {
+ /* Right-edge blocks are mirrored in y only */
+ src_ptr = src_buffer[offset_x]
+ [comp_height - y_crop_blocks - dst_blk_y - offset_y - 1];
+ for (i = 0; i < DCTSIZE; i++) {
+ for (j = 0; j < DCTSIZE; j++) {
+ dst_ptr[j*DCTSIZE+i] = src_ptr[i*DCTSIZE+j];
+ j++;
+ dst_ptr[j*DCTSIZE+i] = -src_ptr[i*DCTSIZE+j];
+ }
+ }
+ }
+ } else {
+ if (x_crop_blocks + dst_blk_x < comp_width) {
+ /* Bottom-edge blocks are mirrored in x only */
+ src_ptr = src_buffer[compptr->h_samp_factor - offset_x - 1]
+ [dst_blk_y + offset_y + y_crop_blocks];
+ for (i = 0; i < DCTSIZE; i++) {
+ for (j = 0; j < DCTSIZE; j++)
+ dst_ptr[j*DCTSIZE+i] = src_ptr[i*DCTSIZE+j];
+ i++;
+ for (j = 0; j < DCTSIZE; j++)
+ dst_ptr[j*DCTSIZE+i] = -src_ptr[i*DCTSIZE+j];
+ }
+ } else {
+ /* At lower right corner, just transpose, no mirroring */
+ src_ptr = src_buffer[offset_x]
+ [dst_blk_y + offset_y + y_crop_blocks];
+ for (i = 0; i < DCTSIZE; i++)
+ for (j = 0; j < DCTSIZE; j++)
+ dst_ptr[j*DCTSIZE+i] = src_ptr[i*DCTSIZE+j];
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+}
+
+
+/* Trim off any partial iMCUs on the indicated destination edge */
+
+LOCAL(void)
+trim_right_edge (jpeg_transform_info *info, JDIMENSION full_width)
+{
+ JDIMENSION MCU_cols;
+
+ MCU_cols = info->output_width / info->iMCU_sample_width;
+ if (MCU_cols > 0 && info->x_crop_offset + MCU_cols ==
+ full_width / info->iMCU_sample_width)
+ info->output_width = MCU_cols * info->iMCU_sample_width;
+}
+
+LOCAL(void)
+trim_bottom_edge (jpeg_transform_info *info, JDIMENSION full_height)
+{
+ JDIMENSION MCU_rows;
+
+ MCU_rows = info->output_height / info->iMCU_sample_height;
+ if (MCU_rows > 0 && info->y_crop_offset + MCU_rows ==
+ full_height / info->iMCU_sample_height)
+ info->output_height = MCU_rows * info->iMCU_sample_height;
+}
+
+
+/* Request any required workspace.
+ *
+ * This routine figures out the size that the output image will be
+ * (which implies that all the transform parameters must be set before
+ * it is called).
+ *
+ * We allocate the workspace virtual arrays from the source decompression
+ * object, so that all the arrays (both the original data and the workspace)
+ * will be taken into account while making memory management decisions.
+ * Hence, this routine must be called after jpeg_read_header (which reads
+ * the image dimensions) and before jpeg_read_coefficients (which realizes
+ * the source's virtual arrays).
+ *
+ * This function returns FALSE right away if -perfect is given
+ * and transformation is not perfect. Otherwise returns TRUE.
+ */
+
+GLOBAL(boolean)
+jtransform_request_workspace (j_decompress_ptr srcinfo,
+ jpeg_transform_info *info)
+{
+ jvirt_barray_ptr *coef_arrays;
+ boolean need_workspace, transpose_it;
+ jpeg_component_info *compptr;
+ JDIMENSION xoffset, yoffset;
+ JDIMENSION width_in_iMCUs, height_in_iMCUs;
+ JDIMENSION width_in_blocks, height_in_blocks;
+ int ci, h_samp_factor, v_samp_factor;
+
+ /* Determine number of components in output image */
+ if (info->force_grayscale &&
+ srcinfo->jpeg_color_space == JCS_YCbCr &&
+ srcinfo->num_components == 3)
+ /* We'll only process the first component */
+ info->num_components = 1;
+ else
+ /* Process all the components */
+ info->num_components = srcinfo->num_components;
+
+ /* Compute output image dimensions and related values. */
+#if JPEG_LIB_VERSION >= 80
+ jpeg_core_output_dimensions(srcinfo);
+#else
+ srcinfo->output_width = srcinfo->image_width;
+ srcinfo->output_height = srcinfo->image_height;
+#endif
+
+ /* Return right away if -perfect is given and transformation is not perfect.
+ */
+ if (info->perfect) {
+ if (info->num_components == 1) {
+ if (!jtransform_perfect_transform(srcinfo->output_width,
+ srcinfo->output_height,
+ srcinfo->min_DCT_h_scaled_size_,
+ srcinfo->min_DCT_v_scaled_size_,
+ info->transform))
+ return FALSE;
+ } else {
+ if (!jtransform_perfect_transform(srcinfo->output_width,
+ srcinfo->output_height,
+ srcinfo->max_h_samp_factor * srcinfo->min_DCT_h_scaled_size_,
+ srcinfo->max_v_samp_factor * srcinfo->min_DCT_v_scaled_size_,
+ info->transform))
+ return FALSE;
+ }
+ }
+
+ /* If there is only one output component, force the iMCU size to be 1;
+ * else use the source iMCU size. (This allows us to do the right thing
+ * when reducing color to grayscale, and also provides a handy way of
+ * cleaning up "funny" grayscale images whose sampling factors are not 1x1.)
+ */
+ switch (info->transform) {
+ case JXFORM_TRANSPOSE:
+ case JXFORM_TRANSVERSE:
+ case JXFORM_ROT_90:
+ case JXFORM_ROT_270:
+ info->output_width = srcinfo->output_height;
+ info->output_height = srcinfo->output_width;
+ if (info->num_components == 1) {
+ info->iMCU_sample_width = srcinfo->min_DCT_v_scaled_size_;
+ info->iMCU_sample_height = srcinfo->min_DCT_h_scaled_size_;
+ } else {
+ info->iMCU_sample_width =
+ srcinfo->max_v_samp_factor * srcinfo->min_DCT_v_scaled_size_;
+ info->iMCU_sample_height =
+ srcinfo->max_h_samp_factor * srcinfo->min_DCT_h_scaled_size_;
+ }
+ break;
+ default:
+ info->output_width = srcinfo->output_width;
+ info->output_height = srcinfo->output_height;
+ if (info->num_components == 1) {
+ info->iMCU_sample_width = srcinfo->min_DCT_h_scaled_size_;
+ info->iMCU_sample_height = srcinfo->min_DCT_v_scaled_size_;
+ } else {
+ info->iMCU_sample_width =
+ srcinfo->max_h_samp_factor * srcinfo->min_DCT_h_scaled_size_;
+ info->iMCU_sample_height =
+ srcinfo->max_v_samp_factor * srcinfo->min_DCT_v_scaled_size_;
+ }
+ break;
+ }
+
+ /* If cropping has been requested, compute the crop area's position and
+ * dimensions, ensuring that its upper left corner falls at an iMCU boundary.
+ */
+ if (info->crop) {
+ /* Insert default values for unset crop parameters */
+ if (info->crop_xoffset_set == JCROP_UNSET)
+ info->crop_xoffset = 0; /* default to +0 */
+ if (info->crop_yoffset_set == JCROP_UNSET)
+ info->crop_yoffset = 0; /* default to +0 */
+ if (info->crop_xoffset >= info->output_width ||
+ info->crop_yoffset >= info->output_height)
+ ERREXIT(srcinfo, JERR_CONVERSION_NOTIMPL);
+ if (info->crop_width_set == JCROP_UNSET)
+ info->crop_width = info->output_width - info->crop_xoffset;
+ if (info->crop_height_set == JCROP_UNSET)
+ info->crop_height = info->output_height - info->crop_yoffset;
+ /* Ensure parameters are valid */
+ if (info->crop_width <= 0 || info->crop_width > info->output_width ||
+ info->crop_height <= 0 || info->crop_height > info->output_height ||
+ info->crop_xoffset > info->output_width - info->crop_width ||
+ info->crop_yoffset > info->output_height - info->crop_height)
+ ERREXIT(srcinfo, JERR_CONVERSION_NOTIMPL);
+ /* Convert negative crop offsets into regular offsets */
+ if (info->crop_xoffset_set == JCROP_NEG)
+ xoffset = info->output_width - info->crop_width - info->crop_xoffset;
+ else
+ xoffset = info->crop_xoffset;
+ if (info->crop_yoffset_set == JCROP_NEG)
+ yoffset = info->output_height - info->crop_height - info->crop_yoffset;
+ else
+ yoffset = info->crop_yoffset;
+ /* Now adjust so that upper left corner falls at an iMCU boundary */
+ if (info->crop_width_set == JCROP_FORCE)
+ info->output_width = info->crop_width;
+ else
+ info->output_width =
+ info->crop_width + (xoffset % info->iMCU_sample_width);
+ if (info->crop_height_set == JCROP_FORCE)
+ info->output_height = info->crop_height;
+ else
+ info->output_height =
+ info->crop_height + (yoffset % info->iMCU_sample_height);
+ /* Save x/y offsets measured in iMCUs */
+ info->x_crop_offset = xoffset / info->iMCU_sample_width;
+ info->y_crop_offset = yoffset / info->iMCU_sample_height;
+ } else {
+ info->x_crop_offset = 0;
+ info->y_crop_offset = 0;
+ }
+
+ /* Figure out whether we need workspace arrays,
+ * and if so whether they are transposed relative to the source.
+ */
+ need_workspace = FALSE;
+ transpose_it = FALSE;
+ switch (info->transform) {
+ case JXFORM_NONE:
+ if (info->x_crop_offset != 0 || info->y_crop_offset != 0)
+ need_workspace = TRUE;
+ /* No workspace needed if neither cropping nor transforming */
+ break;
+ case JXFORM_FLIP_H:
+ if (info->trim)
+ trim_right_edge(info, srcinfo->output_width);
+ if (info->y_crop_offset != 0 || info->slow_hflip)
+ need_workspace = TRUE;
+ /* do_flip_h_no_crop doesn't need a workspace array */
+ break;
+ case JXFORM_FLIP_V:
+ if (info->trim)
+ trim_bottom_edge(info, srcinfo->output_height);
+ /* Need workspace arrays having same dimensions as source image. */
+ need_workspace = TRUE;
+ break;
+ case JXFORM_TRANSPOSE:
+ /* transpose does NOT have to trim anything */
+ /* Need workspace arrays having transposed dimensions. */
+ need_workspace = TRUE;
+ transpose_it = TRUE;
+ break;
+ case JXFORM_TRANSVERSE:
+ if (info->trim) {
+ trim_right_edge(info, srcinfo->output_height);
+ trim_bottom_edge(info, srcinfo->output_width);
+ }
+ /* Need workspace arrays having transposed dimensions. */
+ need_workspace = TRUE;
+ transpose_it = TRUE;
+ break;
+ case JXFORM_ROT_90:
+ if (info->trim)
+ trim_right_edge(info, srcinfo->output_height);
+ /* Need workspace arrays having transposed dimensions. */
+ need_workspace = TRUE;
+ transpose_it = TRUE;
+ break;
+ case JXFORM_ROT_180:
+ if (info->trim) {
+ trim_right_edge(info, srcinfo->output_width);
+ trim_bottom_edge(info, srcinfo->output_height);
+ }
+ /* Need workspace arrays having same dimensions as source image. */
+ need_workspace = TRUE;
+ break;
+ case JXFORM_ROT_270:
+ if (info->trim)
+ trim_bottom_edge(info, srcinfo->output_width);
+ /* Need workspace arrays having transposed dimensions. */
+ need_workspace = TRUE;
+ transpose_it = TRUE;
+ break;
+ }
+
+ /* Allocate workspace if needed.
+ * Note that we allocate arrays padded out to the next iMCU boundary,
+ * so that transform routines need not worry about missing edge blocks.
+ */
+ if (need_workspace) {
+ coef_arrays = (jvirt_barray_ptr *)
+ (*srcinfo->mem->alloc_small) ((j_common_ptr) srcinfo, JPOOL_IMAGE,
+ SIZEOF(jvirt_barray_ptr) * info->num_components);
+ width_in_iMCUs = (JDIMENSION)
+ jdiv_round_up((long) info->output_width,
+ (long) info->iMCU_sample_width);
+ height_in_iMCUs = (JDIMENSION)
+ jdiv_round_up((long) info->output_height,
+ (long) info->iMCU_sample_height);
+ for (ci = 0; ci < info->num_components; ci++) {
+ compptr = srcinfo->comp_info + ci;
+ if (info->num_components == 1) {
+ /* we're going to force samp factors to 1x1 in this case */
+ h_samp_factor = v_samp_factor = 1;
+ } else if (transpose_it) {
+ h_samp_factor = compptr->v_samp_factor;
+ v_samp_factor = compptr->h_samp_factor;
+ } else {
+ h_samp_factor = compptr->h_samp_factor;
+ v_samp_factor = compptr->v_samp_factor;
+ }
+ width_in_blocks = width_in_iMCUs * h_samp_factor;
+ height_in_blocks = height_in_iMCUs * v_samp_factor;
+ coef_arrays[ci] = (*srcinfo->mem->request_virt_barray)
+ ((j_common_ptr) srcinfo, JPOOL_IMAGE, FALSE,
+ width_in_blocks, height_in_blocks, (JDIMENSION) v_samp_factor);
+ }
+ info->workspace_coef_arrays = coef_arrays;
+ } else
+ info->workspace_coef_arrays = NULL;
+
+ return TRUE;
+}
+
+
+/* Transpose destination image parameters */
+
+LOCAL(void)
+transpose_critical_parameters (j_compress_ptr dstinfo)
+{
+ int tblno, i, j, ci, itemp;
+ jpeg_component_info *compptr;
+ JQUANT_TBL *qtblptr;
+ JDIMENSION jtemp;
+ UINT16 qtemp;
+
+ /* Transpose image dimensions */
+ jtemp = dstinfo->image_width;
+ dstinfo->image_width = dstinfo->image_height;
+ dstinfo->image_height = jtemp;
+#if JPEG_LIB_VERSION >= 70
+ itemp = dstinfo->min_DCT_h_scaled_size;
+ dstinfo->min_DCT_h_scaled_size = dstinfo->min_DCT_v_scaled_size;
+ dstinfo->min_DCT_v_scaled_size = itemp;
+#endif
+
+ /* Transpose sampling factors */
+ for (ci = 0; ci < dstinfo->num_components; ci++) {
+ compptr = dstinfo->comp_info + ci;
+ itemp = compptr->h_samp_factor;
+ compptr->h_samp_factor = compptr->v_samp_factor;
+ compptr->v_samp_factor = itemp;
+ }
+
+ /* Transpose quantization tables */
+ for (tblno = 0; tblno < NUM_QUANT_TBLS; tblno++) {
+ qtblptr = dstinfo->quant_tbl_ptrs[tblno];
+ if (qtblptr != NULL) {
+ for (i = 0; i < DCTSIZE; i++) {
+ for (j = 0; j < i; j++) {
+ qtemp = qtblptr->quantval[i*DCTSIZE+j];
+ qtblptr->quantval[i*DCTSIZE+j] = qtblptr->quantval[j*DCTSIZE+i];
+ qtblptr->quantval[j*DCTSIZE+i] = qtemp;
+ }
+ }
+ }
+ }
+}
+
+
+/* Adjust Exif image parameters.
+ *
+ * We try to adjust the Tags ExifImageWidth and ExifImageHeight if possible.
+ */
+
+#if JPEG_LIB_VERSION >= 70
+LOCAL(void)
+adjust_exif_parameters (JOCTET FAR * data, unsigned int length,
+ JDIMENSION new_width, JDIMENSION new_height)
+{
+ boolean is_motorola; /* Flag for byte order */
+ unsigned int number_of_tags, tagnum;
+ unsigned int firstoffset, offset;
+ JDIMENSION new_value;
+
+ if (length < 12) return; /* Length of an IFD entry */
+
+ /* Discover byte order */
+ if (GETJOCTET(data[0]) == 0x49 && GETJOCTET(data[1]) == 0x49)
+ is_motorola = FALSE;
+ else if (GETJOCTET(data[0]) == 0x4D && GETJOCTET(data[1]) == 0x4D)
+ is_motorola = TRUE;
+ else
+ return;
+
+ /* Check Tag Mark */
+ if (is_motorola) {
+ if (GETJOCTET(data[2]) != 0) return;
+ if (GETJOCTET(data[3]) != 0x2A) return;
+ } else {
+ if (GETJOCTET(data[3]) != 0) return;
+ if (GETJOCTET(data[2]) != 0x2A) return;
+ }
+
+ /* Get first IFD offset (offset to IFD0) */
+ if (is_motorola) {
+ if (GETJOCTET(data[4]) != 0) return;
+ if (GETJOCTET(data[5]) != 0) return;
+ firstoffset = GETJOCTET(data[6]);
+ firstoffset <<= 8;
+ firstoffset += GETJOCTET(data[7]);
+ } else {
+ if (GETJOCTET(data[7]) != 0) return;
+ if (GETJOCTET(data[6]) != 0) return;
+ firstoffset = GETJOCTET(data[5]);
+ firstoffset <<= 8;
+ firstoffset += GETJOCTET(data[4]);
+ }
+ if (firstoffset > length - 2) return; /* check end of data segment */
+
+ /* Get the number of directory entries contained in this IFD */
+ if (is_motorola) {
+ number_of_tags = GETJOCTET(data[firstoffset]);
+ number_of_tags <<= 8;
+ number_of_tags += GETJOCTET(data[firstoffset+1]);
+ } else {
+ number_of_tags = GETJOCTET(data[firstoffset+1]);
+ number_of_tags <<= 8;
+ number_of_tags += GETJOCTET(data[firstoffset]);
+ }
+ if (number_of_tags == 0) return;
+ firstoffset += 2;
+
+ /* Search for ExifSubIFD offset Tag in IFD0 */
+ for (;;) {
+ if (firstoffset > length - 12) return; /* check end of data segment */
+ /* Get Tag number */
+ if (is_motorola) {
+ tagnum = GETJOCTET(data[firstoffset]);
+ tagnum <<= 8;
+ tagnum += GETJOCTET(data[firstoffset+1]);
+ } else {
+ tagnum = GETJOCTET(data[firstoffset+1]);
+ tagnum <<= 8;
+ tagnum += GETJOCTET(data[firstoffset]);
+ }
+ if (tagnum == 0x8769) break; /* found ExifSubIFD offset Tag */
+ if (--number_of_tags == 0) return;
+ firstoffset += 12;
+ }
+
+ /* Get the ExifSubIFD offset */
+ if (is_motorola) {
+ if (GETJOCTET(data[firstoffset+8]) != 0) return;
+ if (GETJOCTET(data[firstoffset+9]) != 0) return;
+ offset = GETJOCTET(data[firstoffset+10]);
+ offset <<= 8;
+ offset += GETJOCTET(data[firstoffset+11]);
+ } else {
+ if (GETJOCTET(data[firstoffset+11]) != 0) return;
+ if (GETJOCTET(data[firstoffset+10]) != 0) return;
+ offset = GETJOCTET(data[firstoffset+9]);
+ offset <<= 8;
+ offset += GETJOCTET(data[firstoffset+8]);
+ }
+ if (offset > length - 2) return; /* check end of data segment */
+
+ /* Get the number of directory entries contained in this SubIFD */
+ if (is_motorola) {
+ number_of_tags = GETJOCTET(data[offset]);
+ number_of_tags <<= 8;
+ number_of_tags += GETJOCTET(data[offset+1]);
+ } else {
+ number_of_tags = GETJOCTET(data[offset+1]);
+ number_of_tags <<= 8;
+ number_of_tags += GETJOCTET(data[offset]);
+ }
+ if (number_of_tags < 2) return;
+ offset += 2;
+
+ /* Search for ExifImageWidth and ExifImageHeight Tags in this SubIFD */
+ do {
+ if (offset > length - 12) return; /* check end of data segment */
+ /* Get Tag number */
+ if (is_motorola) {
+ tagnum = GETJOCTET(data[offset]);
+ tagnum <<= 8;
+ tagnum += GETJOCTET(data[offset+1]);
+ } else {
+ tagnum = GETJOCTET(data[offset+1]);
+ tagnum <<= 8;
+ tagnum += GETJOCTET(data[offset]);
+ }
+ if (tagnum == 0xA002 || tagnum == 0xA003) {
+ if (tagnum == 0xA002)
+ new_value = new_width; /* ExifImageWidth Tag */
+ else
+ new_value = new_height; /* ExifImageHeight Tag */
+ if (is_motorola) {
+ data[offset+2] = 0; /* Format = unsigned long (4 octets) */
+ data[offset+3] = 4;
+ data[offset+4] = 0; /* Number Of Components = 1 */
+ data[offset+5] = 0;
+ data[offset+6] = 0;
+ data[offset+7] = 1;
+ data[offset+8] = 0;
+ data[offset+9] = 0;
+ data[offset+10] = (JOCTET)((new_value >> 8) & 0xFF);
+ data[offset+11] = (JOCTET)(new_value & 0xFF);
+ } else {
+ data[offset+2] = 4; /* Format = unsigned long (4 octets) */
+ data[offset+3] = 0;
+ data[offset+4] = 1; /* Number Of Components = 1 */
+ data[offset+5] = 0;
+ data[offset+6] = 0;
+ data[offset+7] = 0;
+ data[offset+8] = (JOCTET)(new_value & 0xFF);
+ data[offset+9] = (JOCTET)((new_value >> 8) & 0xFF);
+ data[offset+10] = 0;
+ data[offset+11] = 0;
+ }
+ }
+ offset += 12;
+ } while (--number_of_tags);
+}
+#endif
+
+
+/* Adjust output image parameters as needed.
+ *
+ * This must be called after jpeg_copy_critical_parameters()
+ * and before jpeg_write_coefficients().
+ *
+ * The return value is the set of virtual coefficient arrays to be written
+ * (either the ones allocated by jtransform_request_workspace, or the
+ * original source data arrays). The caller will need to pass this value
+ * to jpeg_write_coefficients().
+ */
+
+GLOBAL(jvirt_barray_ptr *)
+jtransform_adjust_parameters (j_decompress_ptr srcinfo,
+ j_compress_ptr dstinfo,
+ jvirt_barray_ptr *src_coef_arrays,
+ jpeg_transform_info *info)
+{
+ /* If force-to-grayscale is requested, adjust destination parameters */
+ if (info->force_grayscale) {
+ /* First, ensure we have YCbCr or grayscale data, and that the source's
+ * Y channel is full resolution. (No reasonable person would make Y
+ * be less than full resolution, so actually copying with that case
+ * isn't worth extra code space. But we check it to avoid crashing.)
+ */
+ if (((dstinfo->jpeg_color_space == JCS_YCbCr &&
+ dstinfo->num_components == 3) ||
+ (dstinfo->jpeg_color_space == JCS_GRAYSCALE &&
+ dstinfo->num_components == 1)) &&
+ srcinfo->comp_info[0].h_samp_factor == srcinfo->max_h_samp_factor &&
+ srcinfo->comp_info[0].v_samp_factor == srcinfo->max_v_samp_factor) {
+ /* We use jpeg_set_colorspace to make sure subsidiary settings get fixed
+ * properly. Among other things, it sets the target h_samp_factor &
+ * v_samp_factor to 1, which typically won't match the source.
+ * We have to preserve the source's quantization table number, however.
+ */
+ int sv_quant_tbl_no = dstinfo->comp_info[0].quant_tbl_no;
+ jpeg_set_colorspace(dstinfo, JCS_GRAYSCALE);
+ dstinfo->comp_info[0].quant_tbl_no = sv_quant_tbl_no;
+ } else {
+ /* Sorry, can't do it */
+ ERREXIT(dstinfo, JERR_CONVERSION_NOTIMPL);
+ }
+ } else if (info->num_components == 1) {
+ /* For a single-component source, we force the destination sampling factors
+ * to 1x1, with or without force_grayscale. This is useful because some
+ * decoders choke on grayscale images with other sampling factors.
+ */
+ dstinfo->comp_info[0].h_samp_factor = 1;
+ dstinfo->comp_info[0].v_samp_factor = 1;
+ }
+
+ /* Correct the destination's image dimensions as necessary
+ * for rotate/flip, resize, and crop operations.
+ */
+#if JPEG_LIB_VERSION >= 70
+ dstinfo->jpeg_width = info->output_width;
+ dstinfo->jpeg_height = info->output_height;
+#endif
+
+ /* Transpose destination image parameters */
+ switch (info->transform) {
+ case JXFORM_TRANSPOSE:
+ case JXFORM_TRANSVERSE:
+ case JXFORM_ROT_90:
+ case JXFORM_ROT_270:
+#if JPEG_LIB_VERSION < 70
+ dstinfo->image_width = info->output_height;
+ dstinfo->image_height = info->output_width;
+#endif
+ transpose_critical_parameters(dstinfo);
+ break;
+ default:
+#if JPEG_LIB_VERSION < 70
+ dstinfo->image_width = info->output_width;
+ dstinfo->image_height = info->output_height;
+#endif
+ break;
+ }
+
+ /* Adjust Exif properties */
+ if (srcinfo->marker_list != NULL &&
+ srcinfo->marker_list->marker == JPEG_APP0+1 &&
+ srcinfo->marker_list->data_length >= 6 &&
+ GETJOCTET(srcinfo->marker_list->data[0]) == 0x45 &&
+ GETJOCTET(srcinfo->marker_list->data[1]) == 0x78 &&
+ GETJOCTET(srcinfo->marker_list->data[2]) == 0x69 &&
+ GETJOCTET(srcinfo->marker_list->data[3]) == 0x66 &&
+ GETJOCTET(srcinfo->marker_list->data[4]) == 0 &&
+ GETJOCTET(srcinfo->marker_list->data[5]) == 0) {
+ /* Suppress output of JFIF marker */
+ dstinfo->write_JFIF_header = FALSE;
+#if JPEG_LIB_VERSION >= 70
+ /* Adjust Exif image parameters */
+ if (dstinfo->jpeg_width != srcinfo->image_width ||
+ dstinfo->jpeg_height != srcinfo->image_height)
+ /* Align data segment to start of TIFF structure for parsing */
+ adjust_exif_parameters(srcinfo->marker_list->data + 6,
+ srcinfo->marker_list->data_length - 6,
+ dstinfo->jpeg_width, dstinfo->jpeg_height);
+#endif
+ }
+
+ /* Return the appropriate output data set */
+ if (info->workspace_coef_arrays != NULL)
+ return info->workspace_coef_arrays;
+ return src_coef_arrays;
+}
+
+
+/* Execute the actual transformation, if any.
+ *
+ * This must be called *after* jpeg_write_coefficients, because it depends
+ * on jpeg_write_coefficients to have computed subsidiary values such as
+ * the per-component width and height fields in the destination object.
+ *
+ * Note that some transformations will modify the source data arrays!
+ */
+
+GLOBAL(void)
+jtransform_execute_transform (j_decompress_ptr srcinfo,
+ j_compress_ptr dstinfo,
+ jvirt_barray_ptr *src_coef_arrays,
+ jpeg_transform_info *info)
+{
+ jvirt_barray_ptr *dst_coef_arrays = info->workspace_coef_arrays;
+
+ /* Note: conditions tested here should match those in switch statement
+ * in jtransform_request_workspace()
+ */
+ switch (info->transform) {
+ case JXFORM_NONE:
+ if (info->x_crop_offset != 0 || info->y_crop_offset != 0)
+ do_crop(srcinfo, dstinfo, info->x_crop_offset, info->y_crop_offset,
+ src_coef_arrays, dst_coef_arrays);
+ break;
+ case JXFORM_FLIP_H:
+ if (info->y_crop_offset != 0 || info->slow_hflip)
+ do_flip_h(srcinfo, dstinfo, info->x_crop_offset, info->y_crop_offset,
+ src_coef_arrays, dst_coef_arrays);
+ else
+ do_flip_h_no_crop(srcinfo, dstinfo, info->x_crop_offset,
+ src_coef_arrays);
+ break;
+ case JXFORM_FLIP_V:
+ do_flip_v(srcinfo, dstinfo, info->x_crop_offset, info->y_crop_offset,
+ src_coef_arrays, dst_coef_arrays);
+ break;
+ case JXFORM_TRANSPOSE:
+ do_transpose(srcinfo, dstinfo, info->x_crop_offset, info->y_crop_offset,
+ src_coef_arrays, dst_coef_arrays);
+ break;
+ case JXFORM_TRANSVERSE:
+ do_transverse(srcinfo, dstinfo, info->x_crop_offset, info->y_crop_offset,
+ src_coef_arrays, dst_coef_arrays);
+ break;
+ case JXFORM_ROT_90:
+ do_rot_90(srcinfo, dstinfo, info->x_crop_offset, info->y_crop_offset,
+ src_coef_arrays, dst_coef_arrays);
+ break;
+ case JXFORM_ROT_180:
+ do_rot_180(srcinfo, dstinfo, info->x_crop_offset, info->y_crop_offset,
+ src_coef_arrays, dst_coef_arrays);
+ break;
+ case JXFORM_ROT_270:
+ do_rot_270(srcinfo, dstinfo, info->x_crop_offset, info->y_crop_offset,
+ src_coef_arrays, dst_coef_arrays);
+ break;
+ }
+}
+
+/* jtransform_perfect_transform
+ *
+ * Determine whether lossless transformation is perfectly
+ * possible for a specified image and transformation.
+ *
+ * Inputs:
+ * image_width, image_height: source image dimensions.
+ * MCU_width, MCU_height: pixel dimensions of MCU.
+ * transform: transformation identifier.
+ * Parameter sources from initialized jpeg_struct
+ * (after reading source header):
+ * image_width = cinfo.image_width
+ * image_height = cinfo.image_height
+ * MCU_width = cinfo.max_h_samp_factor * cinfo.block_size
+ * MCU_height = cinfo.max_v_samp_factor * cinfo.block_size
+ * Result:
+ * TRUE = perfect transformation possible
+ * FALSE = perfect transformation not possible
+ * (may use custom action then)
+ */
+
+GLOBAL(boolean)
+jtransform_perfect_transform(JDIMENSION image_width, JDIMENSION image_height,
+ int MCU_width, int MCU_height,
+ JXFORM_CODE transform)
+{
+ boolean result = TRUE; /* initialize TRUE */
+
+ switch (transform) {
+ case JXFORM_FLIP_H:
+ case JXFORM_ROT_270:
+ if (image_width % (JDIMENSION) MCU_width)
+ result = FALSE;
+ break;
+ case JXFORM_FLIP_V:
+ case JXFORM_ROT_90:
+ if (image_height % (JDIMENSION) MCU_height)
+ result = FALSE;
+ break;
+ case JXFORM_TRANSVERSE:
+ case JXFORM_ROT_180:
+ if (image_width % (JDIMENSION) MCU_width)
+ result = FALSE;
+ if (image_height % (JDIMENSION) MCU_height)
+ result = FALSE;
+ break;
+ default:
+ break;
+ }
+
+ return result;
+}
+
+#endif /* TRANSFORMS_SUPPORTED */
+
+
+/* Setup decompression object to save desired markers in memory.
+ * This must be called before jpeg_read_header() to have the desired effect.
+ */
+
+GLOBAL(void)
+jcopy_markers_setup (j_decompress_ptr srcinfo, JCOPY_OPTION option)
+{
+#ifdef SAVE_MARKERS_SUPPORTED
+ int m;
+
+ /* Save comments except under NONE option */
+ if (option != JCOPYOPT_NONE) {
+ jpeg_save_markers(srcinfo, JPEG_COM, 0xFFFF);
+ }
+ /* Save all types of APPn markers iff ALL option */
+ if (option == JCOPYOPT_ALL) {
+ for (m = 0; m < 16; m++)
+ jpeg_save_markers(srcinfo, JPEG_APP0 + m, 0xFFFF);
+ }
+#else
+ (void) srcinfo; (void) option;
+#endif /* SAVE_MARKERS_SUPPORTED */
+}
+
+/* Copy markers saved in the given source object to the destination object.
+ * This should be called just after jpeg_start_compress() or
+ * jpeg_write_coefficients().
+ * Note that those routines will have written the SOI, and also the
+ * JFIF APP0 or Adobe APP14 markers if selected.
+ */
+
+GLOBAL(void)
+jcopy_markers_execute (j_decompress_ptr srcinfo, j_compress_ptr dstinfo,
+ JCOPY_OPTION option)
+{
+ jpeg_saved_marker_ptr marker;
+
+ /* In the current implementation, we don't actually need to examine the
+ * option flag here; we just copy everything that got saved.
+ * But to avoid confusion, we do not output JFIF and Adobe APP14 markers
+ * if the encoder library already wrote one.
+ */
+ (void)option;
+
+ for (marker = srcinfo->marker_list; marker != NULL; marker = marker->next) {
+ if (dstinfo->write_JFIF_header &&
+ marker->marker == JPEG_APP0 &&
+ marker->data_length >= 5 &&
+ GETJOCTET(marker->data[0]) == 0x4A &&
+ GETJOCTET(marker->data[1]) == 0x46 &&
+ GETJOCTET(marker->data[2]) == 0x49 &&
+ GETJOCTET(marker->data[3]) == 0x46 &&
+ GETJOCTET(marker->data[4]) == 0)
+ continue; /* reject duplicate JFIF */
+ if (dstinfo->write_Adobe_marker &&
+ marker->marker == JPEG_APP0+14 &&
+ marker->data_length >= 5 &&
+ GETJOCTET(marker->data[0]) == 0x41 &&
+ GETJOCTET(marker->data[1]) == 0x64 &&
+ GETJOCTET(marker->data[2]) == 0x6F &&
+ GETJOCTET(marker->data[3]) == 0x62 &&
+ GETJOCTET(marker->data[4]) == 0x65)
+ continue; /* reject duplicate Adobe */
+#ifdef NEED_FAR_POINTERS
+ /* We could use jpeg_write_marker if the data weren't FAR... */
+ {
+ unsigned int i;
+ jpeg_write_m_header(dstinfo, marker->marker, marker->data_length);
+ for (i = 0; i < marker->data_length; i++)
+ jpeg_write_m_byte(dstinfo, marker->data[i]);
+ }
+#else
+ jpeg_write_marker(dstinfo, marker->marker,
+ marker->data, marker->data_length);
+#endif
+ }
+}
diff --git a/vcl/source/filter/jpeg/transupp.h b/vcl/source/filter/jpeg/transupp.h
new file mode 100644
index 000000000..a5d403335
--- /dev/null
+++ b/vcl/source/filter/jpeg/transupp.h
@@ -0,0 +1,212 @@
+/*
+ * transupp.h
+ *
+ * Copyright (C) 1997-2011, Thomas G. Lane, Guido Vollbeding.
+ * This file is part of the Independent JPEG Group's software.
+ * For conditions of distribution and use, see the accompanying README file.
+ *
+ * This file contains declarations for image transformation routines and
+ * other utility code used by the jpegtran sample application. These are
+ * NOT part of the core JPEG library. But we keep these routines separate
+ * from jpegtran.c to ease the task of maintaining jpegtran-like programs
+ * that have other user interfaces.
+ *
+ * NOTE: all the routines declared here have very specific requirements
+ * about when they are to be executed during the reading and writing of the
+ * source and destination files. See the comments in transupp.c, or see
+ * jpegtran.c for an example of correct usage.
+ */
+
+/* If you happen not to want the image transform support, disable it here */
+#ifndef TRANSFORMS_SUPPORTED
+#define TRANSFORMS_SUPPORTED 1 /* 0 disables transform code */
+#endif
+
+/*
+ * Although rotating and flipping data expressed as DCT coefficients is not
+ * hard, there is an asymmetry in the JPEG format specification for images
+ * whose dimensions aren't multiples of the iMCU size. The right and bottom
+ * image edges are padded out to the next iMCU boundary with junk data; but
+ * no padding is possible at the top and left edges. If we were to flip
+ * the whole image including the pad data, then pad garbage would become
+ * visible at the top and/or left, and real pixels would disappear into the
+ * pad margins --- perhaps permanently, since encoders & decoders may not
+ * bother to preserve DCT blocks that appear to be completely outside the
+ * nominal image area. So, we have to exclude any partial iMCUs from the
+ * basic transformation.
+ *
+ * Transpose is the only transformation that can handle partial iMCUs at the
+ * right and bottom edges completely cleanly. flip_h can flip partial iMCUs
+ * at the bottom, but leaves any partial iMCUs at the right edge untouched.
+ * Similarly flip_v leaves any partial iMCUs at the bottom edge untouched.
+ * The other transforms are defined as combinations of these basic transforms
+ * and process edge blocks in a way that preserves the equivalence.
+ *
+ * The "trim" option causes untransformable partial iMCUs to be dropped;
+ * this is not strictly lossless, but it usually gives the best-looking
+ * result for odd-size images. Note that when this option is active,
+ * the expected mathematical equivalences between the transforms may not hold.
+ * (For example, -rot 270 -trim trims only the bottom edge, but -rot 90 -trim
+ * followed by -rot 180 -trim trims both edges.)
+ *
+ * We also offer a lossless-crop option, which discards data outside a given
+ * image region but losslessly preserves what is inside. Like the rotate and
+ * flip transforms, lossless crop is restricted by the JPEG format: the upper
+ * left corner of the selected region must fall on an iMCU boundary. If this
+ * does not hold for the given crop parameters, we silently move the upper left
+ * corner up and/or left to make it so, simultaneously increasing the region
+ * dimensions to keep the lower right crop corner unchanged. (Thus, the
+ * output image covers at least the requested region, but may cover more.)
+ * The adjustment of the region dimensions may be optionally disabled.
+ *
+ * We also provide a lossless-resize option, which is kind of a lossless-crop
+ * operation in the DCT coefficient block domain - it discards higher-order
+ * coefficients and losslessly preserves lower-order coefficients of a
+ * sub-block.
+ *
+ * Rotate/flip transform, resize, and crop can be requested together in a
+ * single invocation. The crop is applied last --- that is, the crop region
+ * is specified in terms of the destination image after transform/resize.
+ *
+ * We also offer a "force to grayscale" option, which simply discards the
+ * chrominance channels of a YCbCr image. This is lossless in the sense that
+ * the luminance channel is preserved exactly. It's not the same kind of
+ * thing as the rotate/flip transformations, but it's convenient to handle it
+ * as part of this package, mainly because the transformation routines have to
+ * be aware of the option to know how many components to work on.
+ */
+
+/* Short forms of external names for systems with brain-damaged linkers. */
+
+#ifdef NEED_SHORT_EXTERNAL_NAMES
+#define jtransform_parse_crop_spec jTrParCrop
+#define jtransform_request_workspace jTrRequest
+#define jtransform_adjust_parameters jTrAdjust
+#define jtransform_execute_transform jTrExec
+#define jtransform_perfect_transform jTrPerfect
+#define jcopy_markers_setup jCMrkSetup
+#define jcopy_markers_execute jCMrkExec
+#endif /* NEED_SHORT_EXTERNAL_NAMES */
+
+/*
+ * Codes for supported types of image transformations.
+ */
+
+typedef enum {
+ JXFORM_NONE, /* no transformation */
+ JXFORM_FLIP_H, /* horizontal flip */
+ JXFORM_FLIP_V, /* vertical flip */
+ JXFORM_TRANSPOSE, /* transpose across UL-to-LR axis */
+ JXFORM_TRANSVERSE, /* transpose across UR-to-LL axis */
+ JXFORM_ROT_90, /* 90-degree clockwise rotation */
+ JXFORM_ROT_180, /* 180-degree rotation */
+ JXFORM_ROT_270 /* 270-degree clockwise (or 90 ccw) */
+} JXFORM_CODE;
+
+/*
+ * Codes for crop parameters, which can individually be unspecified,
+ * positive or negative for xoffset or yoffset,
+ * positive or forced for width or height.
+ */
+
+typedef enum {
+ JCROP_UNSET,
+ JCROP_POS,
+ JCROP_NEG,
+ JCROP_FORCE
+} JCROP_CODE;
+
+/*
+ * Transform parameters struct.
+ * NB: application must not change any elements of this struct after
+ * calling jtransform_request_workspace.
+ */
+
+typedef struct {
+ /* Options: set by caller */
+ JXFORM_CODE transform; /* image transform operator */
+ boolean perfect; /* if TRUE, fail if partial MCUs are requested */
+ boolean trim; /* if TRUE, trim partial MCUs as needed */
+ boolean force_grayscale; /* if TRUE, convert color image to grayscale */
+ boolean crop; /* if TRUE, crop source image */
+ boolean slow_hflip; /* For best performance, the JXFORM_FLIP_H transform
+ normally modifies the source coefficients in place.
+ Setting this to TRUE will instead use a slower,
+ double-buffered algorithm, which leaves the source
+ coefficients intact (necessary if other transformed
+ images must be generated from the same set of
+ coefficients. */
+
+ /* Crop parameters: application need not set these unless crop is TRUE.
+ * These can be filled in by jtransform_parse_crop_spec().
+ */
+ JDIMENSION crop_width; /* Width of selected region */
+ JCROP_CODE crop_width_set; /* (forced disables adjustment) */
+ JDIMENSION crop_height; /* Height of selected region */
+ JCROP_CODE crop_height_set; /* (forced disables adjustment) */
+ JDIMENSION crop_xoffset; /* X offset of selected region */
+ JCROP_CODE crop_xoffset_set; /* (negative measures from right edge) */
+ JDIMENSION crop_yoffset; /* Y offset of selected region */
+ JCROP_CODE crop_yoffset_set; /* (negative measures from bottom edge) */
+
+ /* Internal workspace: caller should not touch these */
+ int num_components; /* # of components in workspace */
+ jvirt_barray_ptr * workspace_coef_arrays; /* workspace for transformations */
+ JDIMENSION output_width; /* cropped destination dimensions */
+ JDIMENSION output_height;
+ JDIMENSION x_crop_offset; /* destination crop offsets measured in iMCUs */
+ JDIMENSION y_crop_offset;
+ int iMCU_sample_width; /* destination iMCU size */
+ int iMCU_sample_height;
+} jpeg_transform_info;
+
+#if TRANSFORMS_SUPPORTED
+
+/* Request any required workspace */
+EXTERN(boolean) jtransform_request_workspace
+ (j_decompress_ptr srcinfo, jpeg_transform_info *info);
+/* Adjust output image parameters */
+EXTERN(jvirt_barray_ptr *) jtransform_adjust_parameters
+ (j_decompress_ptr srcinfo, j_compress_ptr dstinfo,
+ jvirt_barray_ptr *src_coef_arrays,
+ jpeg_transform_info *info);
+/* Execute the actual transformation, if any */
+EXTERN(void) jtransform_execute_transform
+ (j_decompress_ptr srcinfo, j_compress_ptr dstinfo,
+ jvirt_barray_ptr *src_coef_arrays,
+ jpeg_transform_info *info);
+/* Determine whether lossless transformation is perfectly
+ * possible for a specified image and transformation.
+ */
+EXTERN(boolean) jtransform_perfect_transform
+ (JDIMENSION image_width, JDIMENSION image_height,
+ int MCU_width, int MCU_height,
+ JXFORM_CODE transform);
+
+/* jtransform_execute_transform used to be called
+ * jtransform_execute_transformation, but some compilers complain about
+ * routine names that long. This macro is here to avoid breaking any
+ * old source code that uses the original name...
+ */
+#define jtransform_execute_transformation jtransform_execute_transform
+
+#endif /* TRANSFORMS_SUPPORTED */
+
+/*
+ * Support for copying optional markers from source to destination file.
+ */
+
+typedef enum {
+ JCOPYOPT_NONE, /* copy no optional markers */
+ JCOPYOPT_COMMENTS, /* copy only comment (COM) markers */
+ JCOPYOPT_ALL /* copy all optional markers */
+} JCOPY_OPTION;
+
+
+/* Setup decompression object to save desired markers in memory */
+EXTERN(void) jcopy_markers_setup
+ (j_decompress_ptr srcinfo, JCOPY_OPTION option);
+/* Copy markers saved in the given source object to the destination object */
+EXTERN(void) jcopy_markers_execute
+ (j_decompress_ptr srcinfo, j_compress_ptr dstinfo,
+ JCOPY_OPTION option);
diff --git a/vcl/source/filter/png/PngImageReader.cxx b/vcl/source/filter/png/PngImageReader.cxx
new file mode 100644
index 000000000..6a3053a72
--- /dev/null
+++ b/vcl/source/filter/png/PngImageReader.cxx
@@ -0,0 +1,532 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ */
+
+#include <vcl/filter/PngImageReader.hxx>
+#include <png.h>
+#include <rtl/crc.h>
+#include <tools/stream.hxx>
+#include <vcl/bitmap.hxx>
+#include <vcl/alpha.hxx>
+#include <vcl/BitmapTools.hxx>
+#include <unotools/configmgr.hxx>
+
+#include <bitmap/BitmapWriteAccess.hxx>
+#include <svdata.hxx>
+#include <salinst.hxx>
+
+#include "png.hxx"
+
+namespace
+{
+void lclReadStream(png_structp pPng, png_bytep pOutBytes, png_size_t nBytesToRead)
+{
+ png_voidp pIO = png_get_io_ptr(pPng);
+
+ if (pIO == nullptr)
+ return;
+
+ SvStream* pStream = static_cast<SvStream*>(pIO);
+
+ sal_Size nBytesRead = pStream->ReadBytes(pOutBytes, nBytesToRead);
+
+ if (nBytesRead != nBytesToRead)
+ {
+ if (!nBytesRead)
+ png_error(pPng, "Error reading");
+ else
+ {
+ // Make sure to not reuse old data (could cause infinite loop).
+ memset(pOutBytes + nBytesRead, 0, nBytesToRead - nBytesRead);
+ png_warning(pPng, "Short read");
+ }
+ }
+}
+
+constexpr int PNG_SIGNATURE_SIZE = 8;
+
+bool isPng(SvStream& rStream)
+{
+ // Check signature bytes
+ sal_uInt8 aHeader[PNG_SIGNATURE_SIZE];
+ if (rStream.ReadBytes(aHeader, PNG_SIGNATURE_SIZE) != PNG_SIGNATURE_SIZE)
+ return false;
+ return png_sig_cmp(aHeader, 0, PNG_SIGNATURE_SIZE) == 0;
+}
+
+struct PngDestructor
+{
+ ~PngDestructor() { png_destroy_read_struct(&pPng, &pInfo, nullptr); }
+ png_structp pPng;
+ png_infop pInfo;
+};
+
+#if defined __GNUC__ && __GNUC__ == 8 && !defined __clang__
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Wclobbered"
+#endif
+bool reader(SvStream& rStream, BitmapEx& rBitmapEx,
+ GraphicFilterImportFlags nImportFlags = GraphicFilterImportFlags::NONE,
+ BitmapScopedWriteAccess* pAccess = nullptr,
+ AlphaScopedWriteAccess* pAlphaAccess = nullptr)
+{
+ if (!isPng(rStream))
+ return false;
+
+ png_structp pPng = png_create_read_struct(PNG_LIBPNG_VER_STRING, nullptr, nullptr, nullptr);
+ if (!pPng)
+ return false;
+
+ png_infop pInfo = png_create_info_struct(pPng);
+ if (!pInfo)
+ {
+ png_destroy_read_struct(&pPng, nullptr, nullptr);
+ return false;
+ }
+
+ PngDestructor pngDestructor = { pPng, pInfo };
+
+ // All variables holding resources need to be declared here in order to be
+ // properly cleaned up in case of an error, otherwise libpng's longjmp()
+ // jumps over the destructor calls.
+ Bitmap aBitmap;
+ AlphaMask aBitmapAlpha;
+ Size prefSize;
+ BitmapScopedWriteAccess pWriteAccessInstance;
+ AlphaScopedWriteAccess pWriteAccessAlphaInstance;
+ const bool bFuzzing = utl::ConfigManager::IsFuzzing();
+ const bool bSupportsBitmap32 = bFuzzing || ImplGetSVData()->mpDefInst->supportsBitmap32();
+ const bool bOnlyCreateBitmap
+ = static_cast<bool>(nImportFlags & GraphicFilterImportFlags::OnlyCreateBitmap);
+ const bool bUseExistingBitmap
+ = static_cast<bool>(nImportFlags & GraphicFilterImportFlags::UseExistingBitmap);
+
+ if (setjmp(png_jmpbuf(pPng)))
+ {
+ if (!bUseExistingBitmap)
+ {
+ // Set the bitmap if it contains something, even on failure. This allows
+ // reading images that are only partially broken.
+ pWriteAccessInstance.reset();
+ pWriteAccessAlphaInstance.reset();
+ if (!aBitmap.IsEmpty() && !aBitmapAlpha.IsEmpty())
+ rBitmapEx = BitmapEx(aBitmap, aBitmapAlpha);
+ else if (!aBitmap.IsEmpty())
+ rBitmapEx = BitmapEx(aBitmap);
+ if (!rBitmapEx.IsEmpty() && !prefSize.IsEmpty())
+ {
+ rBitmapEx.SetPrefMapMode(MapMode(MapUnit::Map100thMM));
+ rBitmapEx.SetPrefSize(prefSize);
+ }
+ }
+ return false;
+ }
+
+ png_set_option(pPng, PNG_MAXIMUM_INFLATE_WINDOW, PNG_OPTION_ON);
+
+ png_set_read_fn(pPng, &rStream, lclReadStream);
+
+ if (!bFuzzing)
+ png_set_crc_action(pPng, PNG_CRC_ERROR_QUIT, PNG_CRC_WARN_DISCARD);
+ else
+ png_set_crc_action(pPng, PNG_CRC_QUIET_USE, PNG_CRC_QUIET_USE);
+
+ png_set_sig_bytes(pPng, PNG_SIGNATURE_SIZE);
+
+ png_read_info(pPng, pInfo);
+
+ png_uint_32 width = 0;
+ png_uint_32 height = 0;
+ int bitDepth = 0;
+ int colorType = -1;
+ int interlace = -1;
+
+ png_uint_32 returnValue = png_get_IHDR(pPng, pInfo, &width, &height, &bitDepth, &colorType,
+ &interlace, nullptr, nullptr);
+
+ if (returnValue != 1)
+ return false;
+
+ if (colorType == PNG_COLOR_TYPE_PALETTE)
+ png_set_palette_to_rgb(pPng);
+
+ if (colorType == PNG_COLOR_TYPE_GRAY)
+ png_set_expand_gray_1_2_4_to_8(pPng);
+
+ if (png_get_valid(pPng, pInfo, PNG_INFO_tRNS))
+ png_set_tRNS_to_alpha(pPng);
+
+ if (bitDepth == 16)
+ png_set_scale_16(pPng);
+
+ if (bitDepth < 8)
+ png_set_packing(pPng);
+
+ // Convert gray+alpha to RGBA, keep gray as gray.
+ if (colorType == PNG_COLOR_TYPE_GRAY_ALPHA
+ || (colorType == PNG_COLOR_TYPE_GRAY && png_get_valid(pPng, pInfo, PNG_INFO_tRNS)))
+ {
+ png_set_gray_to_rgb(pPng);
+ }
+
+ // Sets the filler byte - if RGB it converts to RGBA
+ // png_set_filler(pPng, 0xFF, PNG_FILLER_AFTER);
+
+ int nNumberOfPasses = png_set_interlace_handling(pPng);
+
+ png_read_update_info(pPng, pInfo);
+ returnValue = png_get_IHDR(pPng, pInfo, &width, &height, &bitDepth, &colorType, nullptr,
+ nullptr, nullptr);
+
+ if (returnValue != 1)
+ return false;
+
+ if (bitDepth != 8
+ || (colorType != PNG_COLOR_TYPE_RGB && colorType != PNG_COLOR_TYPE_RGB_ALPHA
+ && colorType != PNG_COLOR_TYPE_GRAY))
+ {
+ return false;
+ }
+
+ png_uint_32 res_x = 0;
+ png_uint_32 res_y = 0;
+ int unit_type = 0;
+ if (png_get_pHYs(pPng, pInfo, &res_x, &res_y, &unit_type) != 0
+ && unit_type == PNG_RESOLUTION_METER && res_x && res_y)
+ {
+ // convert into MapUnit::Map100thMM
+ prefSize = Size(static_cast<sal_Int32>((100000.0 * width) / res_x),
+ static_cast<sal_Int32>((100000.0 * height) / res_y));
+ }
+
+ if (!bUseExistingBitmap)
+ {
+ switch (colorType)
+ {
+ case PNG_COLOR_TYPE_RGB:
+ aBitmap = Bitmap(Size(width, height), vcl::PixelFormat::N24_BPP);
+ break;
+ case PNG_COLOR_TYPE_RGBA:
+ if (bSupportsBitmap32)
+ aBitmap = Bitmap(Size(width, height), vcl::PixelFormat::N32_BPP);
+ else
+ {
+ aBitmap = Bitmap(Size(width, height), vcl::PixelFormat::N24_BPP);
+ aBitmapAlpha = AlphaMask(Size(width, height), nullptr);
+ }
+ break;
+ case PNG_COLOR_TYPE_GRAY:
+ aBitmap = Bitmap(Size(width, height), vcl::PixelFormat::N8_BPP,
+ &Bitmap::GetGreyPalette(256));
+ break;
+ default:
+ abort();
+ }
+
+ if (bOnlyCreateBitmap)
+ {
+ if (!aBitmapAlpha.IsEmpty())
+ rBitmapEx = BitmapEx(aBitmap, aBitmapAlpha);
+ else
+ rBitmapEx = BitmapEx(aBitmap);
+ if (!prefSize.IsEmpty())
+ {
+ rBitmapEx.SetPrefMapMode(MapMode(MapUnit::Map100thMM));
+ rBitmapEx.SetPrefSize(prefSize);
+ }
+ return true;
+ }
+
+ pWriteAccessInstance = BitmapScopedWriteAccess(aBitmap);
+ if (!pWriteAccessInstance)
+ return false;
+ if (!aBitmapAlpha.IsEmpty())
+ {
+ pWriteAccessAlphaInstance = AlphaScopedWriteAccess(aBitmapAlpha);
+ if (!pWriteAccessAlphaInstance)
+ return false;
+ }
+ }
+ BitmapScopedWriteAccess& pWriteAccess = pAccess ? *pAccess : pWriteAccessInstance;
+ AlphaScopedWriteAccess& pWriteAccessAlpha
+ = pAlphaAccess ? *pAlphaAccess : pWriteAccessAlphaInstance;
+
+ if (colorType == PNG_COLOR_TYPE_RGB)
+ {
+ ScanlineFormat eFormat = pWriteAccess->GetScanlineFormat();
+ if (eFormat == ScanlineFormat::N24BitTcBgr)
+ png_set_bgr(pPng);
+
+ for (int pass = 0; pass < nNumberOfPasses; pass++)
+ {
+ for (png_uint_32 y = 0; y < height; y++)
+ {
+ Scanline pScanline = pWriteAccess->GetScanline(y);
+ png_read_row(pPng, pScanline, nullptr);
+ }
+ }
+ }
+ else if (colorType == PNG_COLOR_TYPE_RGB_ALPHA)
+ {
+ size_t aRowSizeBytes = png_get_rowbytes(pPng, pInfo);
+
+ if (bSupportsBitmap32)
+ {
+ ScanlineFormat eFormat = pWriteAccess->GetScanlineFormat();
+ if (eFormat == ScanlineFormat::N32BitTcAbgr || eFormat == ScanlineFormat::N32BitTcBgra)
+ png_set_bgr(pPng);
+
+ for (int pass = 0; pass < nNumberOfPasses; pass++)
+ {
+ for (png_uint_32 y = 0; y < height; y++)
+ {
+ Scanline pScanline = pWriteAccess->GetScanline(y);
+ png_read_row(pPng, pScanline, nullptr);
+ }
+ }
+#if !ENABLE_WASM_STRIP_PREMULTIPLY
+ const vcl::bitmap::lookup_table& premultiply = vcl::bitmap::get_premultiply_table();
+#endif
+ if (eFormat == ScanlineFormat::N32BitTcAbgr || eFormat == ScanlineFormat::N32BitTcArgb)
+ { // alpha first and premultiply
+ for (png_uint_32 y = 0; y < height; y++)
+ {
+ Scanline pScanline = pWriteAccess->GetScanline(y);
+ for (size_t i = 0; i < aRowSizeBytes; i += 4)
+ {
+ const sal_uInt8 alpha = pScanline[i + 3];
+#if ENABLE_WASM_STRIP_PREMULTIPLY
+ pScanline[i + 3] = vcl::bitmap::premultiply(alpha, pScanline[i + 2]);
+ pScanline[i + 2] = vcl::bitmap::premultiply(alpha, pScanline[i + 1]);
+ pScanline[i + 1] = vcl::bitmap::premultiply(alpha, pScanline[i]);
+#else
+ pScanline[i + 3] = premultiply[alpha][pScanline[i + 2]];
+ pScanline[i + 2] = premultiply[alpha][pScanline[i + 1]];
+ pScanline[i + 1] = premultiply[alpha][pScanline[i]];
+#endif
+ pScanline[i] = alpha;
+ }
+ }
+ }
+ else
+ { // keep alpha last, only premultiply
+ for (png_uint_32 y = 0; y < height; y++)
+ {
+ Scanline pScanline = pWriteAccess->GetScanline(y);
+ for (size_t i = 0; i < aRowSizeBytes; i += 4)
+ {
+ const sal_uInt8 alpha = pScanline[i + 3];
+#if ENABLE_WASM_STRIP_PREMULTIPLY
+ pScanline[i] = vcl::bitmap::premultiply(alpha, pScanline[i]);
+ pScanline[i + 1] = vcl::bitmap::premultiply(alpha, pScanline[i + 1]);
+ pScanline[i + 2] = vcl::bitmap::premultiply(alpha, pScanline[i + 2]);
+#else
+ pScanline[i] = premultiply[alpha][pScanline[i]];
+ pScanline[i + 1] = premultiply[alpha][pScanline[i + 1]];
+ pScanline[i + 2] = premultiply[alpha][pScanline[i + 2]];
+#endif
+ }
+ }
+ }
+ }
+ else
+ {
+ ScanlineFormat eFormat = pWriteAccess->GetScanlineFormat();
+ if (eFormat == ScanlineFormat::N24BitTcBgr)
+ png_set_bgr(pPng);
+
+ if (nNumberOfPasses == 1)
+ {
+ // optimise the common case, where we can use a buffer of only a single row
+ std::vector<png_byte> aRow(aRowSizeBytes, 0);
+ for (png_uint_32 y = 0; y < height; y++)
+ {
+ Scanline pScanline = pWriteAccess->GetScanline(y);
+ Scanline pScanAlpha = pWriteAccessAlpha->GetScanline(y);
+ png_bytep pRow = aRow.data();
+ png_read_row(pPng, pRow, nullptr);
+ size_t iAlpha = 0;
+ size_t iColor = 0;
+ for (size_t i = 0; i < aRowSizeBytes; i += 4)
+ {
+ pScanline[iColor++] = pRow[i + 0];
+ pScanline[iColor++] = pRow[i + 1];
+ pScanline[iColor++] = pRow[i + 2];
+ pScanAlpha[iAlpha++] = 0xFF - pRow[i + 3];
+ }
+ }
+ }
+ else
+ {
+ std::vector<std::vector<png_byte>> aRows(height);
+ for (auto& rRow : aRows)
+ rRow.resize(aRowSizeBytes, 0);
+ for (int pass = 0; pass < nNumberOfPasses; pass++)
+ {
+ for (png_uint_32 y = 0; y < height; y++)
+ {
+ Scanline pScanline = pWriteAccess->GetScanline(y);
+ Scanline pScanAlpha = pWriteAccessAlpha->GetScanline(y);
+ png_bytep pRow = aRows[y].data();
+ png_read_row(pPng, pRow, nullptr);
+ size_t iAlpha = 0;
+ size_t iColor = 0;
+ for (size_t i = 0; i < aRowSizeBytes; i += 4)
+ {
+ pScanline[iColor++] = pRow[i + 0];
+ pScanline[iColor++] = pRow[i + 1];
+ pScanline[iColor++] = pRow[i + 2];
+ pScanAlpha[iAlpha++] = 0xFF - pRow[i + 3];
+ }
+ }
+ }
+ }
+ }
+ }
+ else if (colorType == PNG_COLOR_TYPE_GRAY)
+ {
+ for (int pass = 0; pass < nNumberOfPasses; pass++)
+ {
+ for (png_uint_32 y = 0; y < height; y++)
+ {
+ Scanline pScanline = pWriteAccess->GetScanline(y);
+ png_read_row(pPng, pScanline, nullptr);
+ }
+ }
+ }
+
+ png_read_end(pPng, pInfo);
+
+ if (!bUseExistingBitmap)
+ {
+ pWriteAccess.reset();
+ pWriteAccessAlpha.reset();
+ if (!aBitmapAlpha.IsEmpty())
+ rBitmapEx = BitmapEx(aBitmap, aBitmapAlpha);
+ else
+ rBitmapEx = BitmapEx(aBitmap);
+ if (!prefSize.IsEmpty())
+ {
+ rBitmapEx.SetPrefMapMode(MapMode(MapUnit::Map100thMM));
+ rBitmapEx.SetPrefSize(prefSize);
+ }
+ }
+
+ return true;
+}
+
+std::unique_ptr<sal_uInt8[]> getMsGifChunk(SvStream& rStream, sal_Int32* chunkSize)
+{
+ if (chunkSize)
+ *chunkSize = 0;
+ if (!isPng(rStream))
+ return nullptr;
+ // It's easier to read manually the contents and find the chunk than
+ // try to get it using libpng.
+ // https://en.wikipedia.org/wiki/Portable_Network_Graphics#File_format
+ // Each chunk is: 4 bytes length, 4 bytes type, <length> bytes, 4 bytes crc
+ bool ignoreCrc = utl::ConfigManager::IsFuzzing();
+ for (;;)
+ {
+ sal_uInt32 length(0), type(0), crc(0);
+ rStream.ReadUInt32(length);
+ rStream.ReadUInt32(type);
+ if (!rStream.good())
+ return nullptr;
+ constexpr sal_uInt32 PNGCHUNK_msOG = 0x6d734f47; // Microsoft Office Animated GIF
+ constexpr sal_uInt64 MSGifHeaderSize = 11; // "MSOFFICE9.0"
+ if (type == PNGCHUNK_msOG && length > MSGifHeaderSize)
+ {
+ // calculate chunktype CRC (swap it back to original byte order)
+ sal_uInt32 typeForCrc = type;
+#if defined(__LITTLEENDIAN) || defined(OSL_LITENDIAN)
+ typeForCrc = OSL_SWAPDWORD(typeForCrc);
+#endif
+ sal_uInt32 computedCrc = rtl_crc32(0, &typeForCrc, 4);
+ const sal_uInt64 pos = rStream.Tell();
+ if (pos + length >= rStream.TellEnd())
+ return nullptr; // broken PNG
+
+ char msHeader[MSGifHeaderSize];
+ if (rStream.ReadBytes(msHeader, MSGifHeaderSize) != MSGifHeaderSize)
+ return nullptr;
+ computedCrc = rtl_crc32(computedCrc, msHeader, MSGifHeaderSize);
+ length -= MSGifHeaderSize;
+
+ std::unique_ptr<sal_uInt8[]> chunk(new sal_uInt8[length]);
+ if (rStream.ReadBytes(chunk.get(), length) != length)
+ return nullptr;
+ computedCrc = rtl_crc32(computedCrc, chunk.get(), length);
+ rStream.ReadUInt32(crc);
+ if (!ignoreCrc && crc != computedCrc)
+ continue; // invalid chunk, ignore
+ if (chunkSize)
+ *chunkSize = length;
+ return chunk;
+ }
+ if (rStream.remainingSize() < length)
+ return nullptr;
+ rStream.SeekRel(length);
+ rStream.ReadUInt32(crc);
+ constexpr sal_uInt32 PNGCHUNK_IEND = 0x49454e44;
+ if (type == PNGCHUNK_IEND)
+ return nullptr;
+ }
+}
+#if defined __GNUC__ && __GNUC__ == 8 && !defined __clang__
+#pragma GCC diagnostic pop
+#endif
+
+} // anonymous namespace
+
+namespace vcl
+{
+PngImageReader::PngImageReader(SvStream& rStream)
+ : mrStream(rStream)
+{
+}
+
+bool PngImageReader::read(BitmapEx& rBitmapEx) { return reader(mrStream, rBitmapEx); }
+
+BitmapEx PngImageReader::read()
+{
+ BitmapEx bitmap;
+ read(bitmap);
+ return bitmap;
+}
+
+std::unique_ptr<sal_uInt8[]> PngImageReader::getMicrosoftGifChunk(SvStream& rStream,
+ sal_Int32* chunkSize)
+{
+ sal_uInt64 originalPosition = rStream.Tell();
+ SvStreamEndian originalEndian = rStream.GetEndian();
+ rStream.SetEndian(SvStreamEndian::BIG);
+ std::unique_ptr<sal_uInt8[]> chunk = getMsGifChunk(rStream, chunkSize);
+ rStream.SetEndian(originalEndian);
+ rStream.Seek(originalPosition);
+ return chunk;
+}
+
+bool ImportPNG(SvStream& rInputStream, Graphic& rGraphic, GraphicFilterImportFlags nImportFlags,
+ BitmapScopedWriteAccess* pAccess, AlphaScopedWriteAccess* pAlphaAccess)
+{
+ // Creating empty bitmaps should be practically a no-op, and thus thread-safe.
+ BitmapEx bitmap;
+ if (reader(rInputStream, bitmap, nImportFlags, pAccess, pAlphaAccess))
+ {
+ if (!(nImportFlags & GraphicFilterImportFlags::UseExistingBitmap))
+ rGraphic = bitmap;
+ return true;
+ }
+ return false;
+}
+
+} // namespace vcl
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/vcl/source/filter/png/png.hxx b/vcl/source/filter/png/png.hxx
new file mode 100644
index 000000000..b3a1b7b17
--- /dev/null
+++ b/vcl/source/filter/png/png.hxx
@@ -0,0 +1,33 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#pragma once
+
+#include <vcl/graph.hxx>
+#include <vcl/graphicfilter.hxx>
+
+#include <bitmap/BitmapWriteAccess.hxx>
+
+namespace vcl
+{
+bool ImportPNG(SvStream& rInputStream, Graphic& rGraphic, GraphicFilterImportFlags nImportFlags,
+ BitmapScopedWriteAccess* pAccess, AlphaScopedWriteAccess* pAlphaAccess);
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/vcl/source/filter/png/pngwrite.cxx b/vcl/source/filter/png/pngwrite.cxx
new file mode 100644
index 000000000..865fe38eb
--- /dev/null
+++ b/vcl/source/filter/png/pngwrite.cxx
@@ -0,0 +1,660 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <vcl/pngwrite.hxx>
+#include <vcl/bitmapex.hxx>
+
+#include <com/sun/star/beans/PropertyValue.hpp>
+#include <com/sun/star/uno/Sequence.hxx>
+
+#include <limits>
+#include <rtl/crc.h>
+#include <tools/solar.h>
+#include <tools/zcodec.hxx>
+#include <tools/stream.hxx>
+#include <vcl/BitmapReadAccess.hxx>
+#include <vcl/alpha.hxx>
+#include <osl/endian.h>
+#include <memory>
+#include <vcl/BitmapTools.hxx>
+
+#define PNG_DEF_COMPRESSION 6
+
+#define PNGCHUNK_IHDR 0x49484452
+#define PNGCHUNK_PLTE 0x504c5445
+#define PNGCHUNK_IDAT 0x49444154
+#define PNGCHUNK_IEND 0x49454e44
+#define PNGCHUNK_pHYs 0x70485973
+
+namespace vcl
+{
+class PNGWriterImpl
+{
+public:
+ PNGWriterImpl(const BitmapEx& BmpEx,
+ const css::uno::Sequence<css::beans::PropertyValue>* pFilterData);
+
+ bool Write(SvStream& rOutStream);
+
+ std::vector<vcl::PNGWriter::ChunkData>& GetChunks() { return maChunkSeq; }
+
+private:
+ std::vector<vcl::PNGWriter::ChunkData> maChunkSeq;
+
+ sal_Int32 mnCompLevel;
+ sal_Int32 mnInterlaced;
+ sal_uInt32 mnMaxChunkSize;
+ bool mbStatus;
+
+ Bitmap::ScopedReadAccess mpAccess;
+ BitmapReadAccess* mpMaskAccess;
+ ZCodec mpZCodec;
+
+ std::unique_ptr<sal_uInt8[]>
+ mpDeflateInBuf; // as big as the size of a scanline + alphachannel + 1
+ std::unique_ptr<sal_uInt8[]> mpPreviousScan; // as big as mpDeflateInBuf
+ std::unique_ptr<sal_uInt8[]> mpCurrentScan;
+ sal_uLong mnDeflateInSize;
+
+ sal_uLong mnWidth;
+ sal_uLong mnHeight;
+ sal_uInt8 mnBitsPerPixel;
+ sal_uInt8 mnFilterType; // 0 or 4;
+ sal_uLong mnBBP; // bytes per pixel ( needed for filtering )
+ bool mbTrueAlpha;
+
+ void ImplWritepHYs(const BitmapEx& rBitmapEx);
+ void ImplWriteIDAT();
+ sal_uLong ImplGetFilter(sal_uLong nY, sal_uLong nXStart = 0, sal_uLong nXAdd = 1);
+ void ImplClearFirstScanline();
+ bool ImplWriteHeader();
+ void ImplWritePalette();
+ void ImplOpenChunk(sal_uLong nChunkType);
+ void ImplWriteChunk(sal_uInt8 nNumb);
+ void ImplWriteChunk(sal_uInt32 nNumb);
+ void ImplWriteChunk(unsigned char const* pSource, sal_uInt32 nDatSize);
+};
+
+PNGWriterImpl::PNGWriterImpl(const BitmapEx& rBitmapEx,
+ const css::uno::Sequence<css::beans::PropertyValue>* pFilterData)
+ : mnCompLevel(PNG_DEF_COMPRESSION)
+ , mnInterlaced(0)
+ , mnMaxChunkSize(0)
+ , mbStatus(true)
+ , mpMaskAccess(nullptr)
+ , mnDeflateInSize(0)
+ , mnWidth(0)
+ , mnHeight(0)
+ , mnBitsPerPixel(0)
+ , mnFilterType(0)
+ , mnBBP(0)
+ , mbTrueAlpha(false)
+{
+ if (rBitmapEx.IsEmpty())
+ return;
+
+ BitmapEx aBitmapEx;
+
+ if (rBitmapEx.GetBitmap().getPixelFormat() == vcl::PixelFormat::N32_BPP)
+ {
+ if (!vcl::bitmap::convertBitmap32To24Plus8(rBitmapEx, aBitmapEx))
+ return;
+ }
+ else
+ {
+ aBitmapEx = rBitmapEx;
+ }
+
+ Bitmap aBmp(aBitmapEx.GetBitmap());
+
+ mnMaxChunkSize = std::numeric_limits<sal_uInt32>::max();
+ bool bTranslucent = true;
+
+ if (pFilterData)
+ {
+ for (const auto& rPropVal : *pFilterData)
+ {
+ if (rPropVal.Name == "Compression")
+ rPropVal.Value >>= mnCompLevel;
+ else if (rPropVal.Name == "Interlaced")
+ rPropVal.Value >>= mnInterlaced;
+ else if (rPropVal.Name == "Translucent")
+ {
+ tools::Long nTmp = 0;
+ rPropVal.Value >>= nTmp;
+ if (!nTmp)
+ bTranslucent = false;
+ }
+ else if (rPropVal.Name == "MaxChunkSize")
+ {
+ sal_Int32 nVal = 0;
+ if (rPropVal.Value >>= nVal)
+ mnMaxChunkSize = static_cast<sal_uInt32>(nVal);
+ }
+ }
+ }
+ mnBitsPerPixel = sal_uInt8(vcl::pixelFormatBitCount(aBmp.getPixelFormat()));
+
+ if (aBitmapEx.IsAlpha() && bTranslucent)
+ {
+ if (mnBitsPerPixel <= 8)
+ {
+ aBmp.Convert(BmpConversion::N24Bit);
+ mnBitsPerPixel = 24;
+ }
+
+ mpAccess = Bitmap::ScopedReadAccess(aBmp); // true RGB with alphachannel
+ if (mpAccess)
+ {
+ mbTrueAlpha = true;
+ AlphaMask aMask(aBitmapEx.GetAlpha());
+ mpMaskAccess = aMask.AcquireReadAccess();
+ if (mpMaskAccess)
+ {
+ if (ImplWriteHeader())
+ {
+ ImplWritepHYs(aBitmapEx);
+ ImplWriteIDAT();
+ }
+ aMask.ReleaseAccess(mpMaskAccess);
+ mpMaskAccess = nullptr;
+ }
+ else
+ {
+ mbStatus = false;
+ }
+ mpAccess.reset();
+ }
+ else
+ {
+ mbStatus = false;
+ }
+ }
+ else
+ {
+ mpAccess = Bitmap::ScopedReadAccess(aBmp); // palette + RGB without alphachannel
+ if (mpAccess)
+ {
+ if (ImplWriteHeader())
+ {
+ ImplWritepHYs(aBitmapEx);
+ if (mpAccess->HasPalette())
+ ImplWritePalette();
+
+ ImplWriteIDAT();
+ }
+ mpAccess.reset();
+ }
+ else
+ {
+ mbStatus = false;
+ }
+ }
+
+ if (mbStatus)
+ {
+ ImplOpenChunk(PNGCHUNK_IEND); // create an IEND chunk
+ }
+}
+
+bool PNGWriterImpl::Write(SvStream& rOStm)
+{
+ /* png signature is always an array of 8 bytes */
+ SvStreamEndian nOldMode = rOStm.GetEndian();
+ rOStm.SetEndian(SvStreamEndian::BIG);
+ rOStm.WriteUInt32(0x89504e47);
+ rOStm.WriteUInt32(0x0d0a1a0a);
+
+ for (auto const& chunk : maChunkSeq)
+ {
+ sal_uInt32 nType = chunk.nType;
+#if defined(__LITTLEENDIAN) || defined(OSL_LITENDIAN)
+ nType = OSL_SWAPDWORD(nType);
+#endif
+ sal_uInt32 nCRC = rtl_crc32(0, &nType, 4);
+ sal_uInt32 nDataSize = chunk.aData.size();
+ if (nDataSize)
+ nCRC = rtl_crc32(nCRC, chunk.aData.data(), nDataSize);
+ rOStm.WriteUInt32(nDataSize);
+ rOStm.WriteUInt32(chunk.nType);
+ if (nDataSize)
+ rOStm.WriteBytes(chunk.aData.data(), nDataSize);
+ rOStm.WriteUInt32(nCRC);
+ }
+ rOStm.SetEndian(nOldMode);
+ return mbStatus;
+}
+
+bool PNGWriterImpl::ImplWriteHeader()
+{
+ ImplOpenChunk(PNGCHUNK_IHDR);
+ mnWidth = mpAccess->Width();
+ ImplWriteChunk(sal_uInt32(mnWidth));
+ mnHeight = mpAccess->Height();
+ ImplWriteChunk(sal_uInt32(mnHeight));
+
+ if (mnWidth && mnHeight && mnBitsPerPixel && mbStatus)
+ {
+ sal_uInt8 nBitDepth = mnBitsPerPixel;
+ if (mnBitsPerPixel <= 8)
+ mnFilterType = 0;
+ else
+ mnFilterType = 4;
+
+ sal_uInt8 nColorType = 2; // colortype:
+
+ // bit 0 -> palette is used
+ if (mpAccess->HasPalette()) // bit 1 -> color is used
+ nColorType |= 1; // bit 2 -> alpha channel is used
+ else
+ nBitDepth /= 3;
+
+ if (mpMaskAccess)
+ nColorType |= 4;
+
+ ImplWriteChunk(nBitDepth);
+ ImplWriteChunk(nColorType); // colortype
+ ImplWriteChunk(static_cast<sal_uInt8>(0)); // compression type
+ ImplWriteChunk(static_cast<sal_uInt8>(0)); // filter type - is not supported in this version
+ ImplWriteChunk(static_cast<sal_uInt8>(mnInterlaced)); // interlace type
+ }
+ else
+ {
+ mbStatus = false;
+ }
+ return mbStatus;
+}
+
+void PNGWriterImpl::ImplWritePalette()
+{
+ const sal_uLong nCount = mpAccess->GetPaletteEntryCount();
+ std::unique_ptr<sal_uInt8[]> pTempBuf(new sal_uInt8[nCount * 3]);
+ sal_uInt8* pTmp = pTempBuf.get();
+
+ ImplOpenChunk(PNGCHUNK_PLTE);
+
+ for (sal_uLong i = 0; i < nCount; i++)
+ {
+ const BitmapColor& rColor = mpAccess->GetPaletteColor(i);
+ *pTmp++ = rColor.GetRed();
+ *pTmp++ = rColor.GetGreen();
+ *pTmp++ = rColor.GetBlue();
+ }
+ ImplWriteChunk(pTempBuf.get(), nCount * 3);
+}
+
+void PNGWriterImpl::ImplWritepHYs(const BitmapEx& rBmpEx)
+{
+ if (rBmpEx.GetPrefMapMode().GetMapUnit() != MapUnit::Map100thMM)
+ return;
+
+ Size aPrefSize(rBmpEx.GetPrefSize());
+
+ if (aPrefSize.Width() && aPrefSize.Height() && mnWidth && mnHeight)
+ {
+ ImplOpenChunk(PNGCHUNK_pHYs);
+ sal_uInt32 nPrefSizeX = static_cast<sal_uInt32>(
+ 100000.0 / (static_cast<double>(aPrefSize.Width()) / mnWidth) + 0.5);
+ sal_uInt32 nPrefSizeY = static_cast<sal_uInt32>(
+ 100000.0 / (static_cast<double>(aPrefSize.Height()) / mnHeight) + 0.5);
+ ImplWriteChunk(nPrefSizeX);
+ ImplWriteChunk(nPrefSizeY);
+ ImplWriteChunk(sal_uInt8(1)); // nMapUnit
+ }
+}
+
+void PNGWriterImpl::ImplWriteIDAT()
+{
+ mnDeflateInSize = mnBitsPerPixel;
+
+ if (mpMaskAccess)
+ mnDeflateInSize += 8;
+
+ mnBBP = (mnDeflateInSize + 7) >> 3;
+
+ mnDeflateInSize = mnBBP * mnWidth + 1;
+
+ mpDeflateInBuf.reset(new sal_uInt8[mnDeflateInSize]);
+
+ if (mnFilterType) // using filter type 4 we need memory for the scanline 3 times
+ {
+ mpPreviousScan.reset(new sal_uInt8[mnDeflateInSize]);
+ mpCurrentScan.reset(new sal_uInt8[mnDeflateInSize]);
+ ImplClearFirstScanline();
+ }
+ mpZCodec.BeginCompression(mnCompLevel);
+ SvMemoryStream aOStm;
+ if (mnInterlaced == 0)
+ {
+ for (sal_uLong nY = 0; nY < mnHeight; nY++)
+ {
+ mpZCodec.Write(aOStm, mpDeflateInBuf.get(), ImplGetFilter(nY));
+ }
+ }
+ else
+ {
+ // interlace mode
+ sal_uLong nY;
+ for (nY = 0; nY < mnHeight; nY += 8) // pass 1
+ {
+ mpZCodec.Write(aOStm, mpDeflateInBuf.get(), ImplGetFilter(nY, 0, 8));
+ }
+ ImplClearFirstScanline();
+
+ for (nY = 0; nY < mnHeight; nY += 8) // pass 2
+ {
+ mpZCodec.Write(aOStm, mpDeflateInBuf.get(), ImplGetFilter(nY, 4, 8));
+ }
+ ImplClearFirstScanline();
+
+ if (mnHeight >= 5) // pass 3
+ {
+ for (nY = 4; nY < mnHeight; nY += 8)
+ {
+ mpZCodec.Write(aOStm, mpDeflateInBuf.get(), ImplGetFilter(nY, 0, 4));
+ }
+ ImplClearFirstScanline();
+ }
+
+ for (nY = 0; nY < mnHeight; nY += 4) // pass 4
+ {
+ mpZCodec.Write(aOStm, mpDeflateInBuf.get(), ImplGetFilter(nY, 2, 4));
+ }
+ ImplClearFirstScanline();
+
+ if (mnHeight >= 3) // pass 5
+ {
+ for (nY = 2; nY < mnHeight; nY += 4)
+ {
+ mpZCodec.Write(aOStm, mpDeflateInBuf.get(), ImplGetFilter(nY, 0, 2));
+ }
+ ImplClearFirstScanline();
+ }
+
+ for (nY = 0; nY < mnHeight; nY += 2) // pass 6
+ {
+ mpZCodec.Write(aOStm, mpDeflateInBuf.get(), ImplGetFilter(nY, 1, 2));
+ }
+ ImplClearFirstScanline();
+
+ if (mnHeight >= 2) // pass 7
+ {
+ for (nY = 1; nY < mnHeight; nY += 2)
+ {
+ mpZCodec.Write(aOStm, mpDeflateInBuf.get(), ImplGetFilter(nY));
+ }
+ }
+ }
+ mpZCodec.EndCompression();
+
+ if (mnFilterType) // using filter type 4 we need memory for the scanline 3 times
+ {
+ mpCurrentScan.reset();
+ mpPreviousScan.reset();
+ }
+ mpDeflateInBuf.reset();
+
+ sal_uInt32 nIDATSize = aOStm.Tell();
+ sal_uInt32 nBytes, nBytesToWrite = nIDATSize;
+ while (nBytesToWrite)
+ {
+ nBytes = nBytesToWrite <= mnMaxChunkSize ? nBytesToWrite : mnMaxChunkSize;
+ ImplOpenChunk(PNGCHUNK_IDAT);
+ ImplWriteChunk(
+ const_cast<unsigned char*>(static_cast<unsigned char const*>(aOStm.GetData()))
+ + (nIDATSize - nBytesToWrite),
+ nBytes);
+ nBytesToWrite -= nBytes;
+ }
+}
+
+// ImplGetFilter writes the complete Scanline (nY) - in interlace mode the parameter nXStart and nXAdd
+// appends to the currently used pass
+// the complete size of scanline will be returned - in interlace mode zero is possible!
+
+sal_uLong PNGWriterImpl::ImplGetFilter(sal_uLong nY, sal_uLong nXStart, sal_uLong nXAdd)
+{
+ sal_uInt8* pDest;
+
+ if (mnFilterType)
+ pDest = mpCurrentScan.get();
+ else
+ pDest = mpDeflateInBuf.get();
+
+ if (nXStart < mnWidth)
+ {
+ *pDest++ = mnFilterType; // in this version the filter type is either 0 or 4
+
+ if (mpAccess
+ ->HasPalette()) // alphachannel is not allowed by pictures including palette entries
+ {
+ switch (mnBitsPerPixel)
+ {
+ case 1:
+ {
+ Scanline pScanline = mpAccess->GetScanline(nY);
+ sal_uLong nX, nXIndex;
+ for (nX = nXStart, nXIndex = 0; nX < mnWidth; nX += nXAdd, nXIndex++)
+ {
+ sal_uLong nShift = (nXIndex & 7) ^ 7;
+ if (nShift == 7)
+ *pDest = mpAccess->GetIndexFromData(pScanline, nX) << nShift;
+ else if (nShift == 0)
+ *pDest++ |= mpAccess->GetIndexFromData(pScanline, nX) << nShift;
+ else
+ *pDest |= mpAccess->GetIndexFromData(pScanline, nX) << nShift;
+ }
+ if ((nXIndex & 7) != 0)
+ pDest++; // byte is not completely used, so the bufferpointer is to correct
+ }
+ break;
+
+ case 4:
+ {
+ Scanline pScanline = mpAccess->GetScanline(nY);
+ sal_uLong nX, nXIndex;
+ for (nX = nXStart, nXIndex = 0; nX < mnWidth; nX += nXAdd, nXIndex++)
+ {
+ if (nXIndex & 1)
+ *pDest++ |= mpAccess->GetIndexFromData(pScanline, nX);
+ else
+ *pDest = mpAccess->GetIndexFromData(pScanline, nX) << 4;
+ }
+ if (nXIndex & 1)
+ pDest++;
+ }
+ break;
+
+ case 8:
+ {
+ Scanline pScanline = mpAccess->GetScanline(nY);
+ for (sal_uLong nX = nXStart; nX < mnWidth; nX += nXAdd)
+ {
+ *pDest++ = mpAccess->GetIndexFromData(pScanline, nX);
+ }
+ }
+ break;
+
+ default:
+ mbStatus = false;
+ break;
+ }
+ }
+ else
+ {
+ if (mpMaskAccess) // mpMaskAccess != NULL -> alphachannel is to create
+ {
+ if (mbTrueAlpha)
+ {
+ Scanline pScanline = mpAccess->GetScanline(nY);
+ Scanline pScanlineMask = mpMaskAccess->GetScanline(nY);
+ for (sal_uLong nX = nXStart; nX < mnWidth; nX += nXAdd)
+ {
+ const BitmapColor& rColor = mpAccess->GetPixelFromData(pScanline, nX);
+ *pDest++ = rColor.GetRed();
+ *pDest++ = rColor.GetGreen();
+ *pDest++ = rColor.GetBlue();
+ *pDest++ = 255 - mpMaskAccess->GetIndexFromData(pScanlineMask, nX);
+ }
+ }
+ else
+ {
+ const BitmapColor aTrans(mpMaskAccess->GetBestMatchingColor(COL_WHITE));
+ Scanline pScanline = mpAccess->GetScanline(nY);
+ Scanline pScanlineMask = mpMaskAccess->GetScanline(nY);
+
+ for (sal_uLong nX = nXStart; nX < mnWidth; nX += nXAdd)
+ {
+ const BitmapColor& rColor = mpAccess->GetPixelFromData(pScanline, nX);
+ *pDest++ = rColor.GetRed();
+ *pDest++ = rColor.GetGreen();
+ *pDest++ = rColor.GetBlue();
+
+ if (mpMaskAccess->GetPixelFromData(pScanlineMask, nX) == aTrans)
+ *pDest++ = 0;
+ else
+ *pDest++ = 0xff;
+ }
+ }
+ }
+ else
+ {
+ Scanline pScanline = mpAccess->GetScanline(nY);
+ for (sal_uLong nX = nXStart; nX < mnWidth; nX += nXAdd)
+ {
+ const BitmapColor& rColor = mpAccess->GetPixelFromData(pScanline, nX);
+ *pDest++ = rColor.GetRed();
+ *pDest++ = rColor.GetGreen();
+ *pDest++ = rColor.GetBlue();
+ }
+ }
+ }
+ }
+ // filter type4 ( PAETH ) will be used only for 24bit graphics
+ if (mnFilterType)
+ {
+ mnDeflateInSize = pDest - mpCurrentScan.get();
+ pDest = mpDeflateInBuf.get();
+ *pDest++ = 4; // filter type
+
+ sal_uInt8* p1 = mpCurrentScan.get() + 1; // Current Pixel
+ sal_uInt8* p2 = p1 - mnBBP; // left pixel
+ sal_uInt8* p3 = mpPreviousScan.get(); // upper pixel
+ sal_uInt8* p4 = p3 - mnBBP; // upperleft Pixel;
+
+ while (pDest < mpDeflateInBuf.get() + mnDeflateInSize)
+ {
+ sal_uLong nb = *p3++;
+ sal_uLong na, nc;
+ if (p2 >= mpCurrentScan.get() + 1)
+ {
+ na = *p2;
+ nc = *p4;
+ }
+ else
+ {
+ na = nc = 0;
+ }
+
+ tools::Long np = na + nb - nc;
+ tools::Long npa = np - na;
+ tools::Long npb = np - nb;
+ tools::Long npc = np - nc;
+
+ if (npa < 0)
+ npa = -npa;
+ if (npb < 0)
+ npb = -npb;
+ if (npc < 0)
+ npc = -npc;
+
+ if (npa <= npb && npa <= npc)
+ *pDest++ = *p1++ - static_cast<sal_uInt8>(na);
+ else if (npb <= npc)
+ *pDest++ = *p1++ - static_cast<sal_uInt8>(nb);
+ else
+ *pDest++ = *p1++ - static_cast<sal_uInt8>(nc);
+
+ p4++;
+ p2++;
+ }
+ for (tools::Long i = 0; i < static_cast<tools::Long>(mnDeflateInSize - 1); i++)
+ {
+ mpPreviousScan[i] = mpCurrentScan[i + 1];
+ }
+ }
+ else
+ {
+ mnDeflateInSize = pDest - mpDeflateInBuf.get();
+ }
+ return mnDeflateInSize;
+}
+
+void PNGWriterImpl::ImplClearFirstScanline()
+{
+ if (mnFilterType)
+ memset(mpPreviousScan.get(), 0, mnDeflateInSize);
+}
+
+void PNGWriterImpl::ImplOpenChunk(sal_uLong nChunkType)
+{
+ maChunkSeq.emplace_back();
+ maChunkSeq.back().nType = nChunkType;
+}
+
+void PNGWriterImpl::ImplWriteChunk(sal_uInt8 nSource)
+{
+ maChunkSeq.back().aData.push_back(nSource);
+}
+
+void PNGWriterImpl::ImplWriteChunk(sal_uInt32 nSource)
+{
+ vcl::PNGWriter::ChunkData& rChunkData = maChunkSeq.back();
+ rChunkData.aData.push_back(static_cast<sal_uInt8>(nSource >> 24));
+ rChunkData.aData.push_back(static_cast<sal_uInt8>(nSource >> 16));
+ rChunkData.aData.push_back(static_cast<sal_uInt8>(nSource >> 8));
+ rChunkData.aData.push_back(static_cast<sal_uInt8>(nSource));
+}
+
+void PNGWriterImpl::ImplWriteChunk(unsigned char const* pSource, sal_uInt32 nDatSize)
+{
+ if (nDatSize)
+ {
+ vcl::PNGWriter::ChunkData& rChunkData = maChunkSeq.back();
+ sal_uInt32 nSize = rChunkData.aData.size();
+ rChunkData.aData.resize(nSize + nDatSize);
+ memcpy(&rChunkData.aData[nSize], pSource, nDatSize);
+ }
+}
+
+PNGWriter::PNGWriter(const BitmapEx& rBmpEx,
+ const css::uno::Sequence<css::beans::PropertyValue>* pFilterData)
+ : mpImpl(new vcl::PNGWriterImpl(rBmpEx, pFilterData))
+{
+}
+
+PNGWriter::~PNGWriter() {}
+
+bool PNGWriter::Write(SvStream& rStream) { return mpImpl->Write(rStream); }
+
+std::vector<vcl::PNGWriter::ChunkData>& PNGWriter::GetChunks() { return mpImpl->GetChunks(); }
+
+} // namespace vcl
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/vcl/source/filter/svm/SvmConverter.cxx b/vcl/source/filter/svm/SvmConverter.cxx
new file mode 100644
index 000000000..2e7ed8f35
--- /dev/null
+++ b/vcl/source/filter/svm/SvmConverter.cxx
@@ -0,0 +1,1290 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <sal/log.hxx>
+#include <osl/diagnose.h>
+#include <osl/thread.h>
+#include <tools/fract.hxx>
+#include <tools/stream.hxx>
+#include <o3tl/safeint.hxx>
+
+#include <vcl/TypeSerializer.hxx>
+#include <vcl/dibtools.hxx>
+#include <vcl/filter/SvmReader.hxx>
+#include <vcl/lineinfo.hxx>
+#include <vcl/metaact.hxx>
+#include <vcl/virdev.hxx>
+
+#include "SvmConverter.hxx"
+
+#include <boost/rational.hpp>
+#include <algorithm>
+#include <memory>
+#include <stack>
+#include <string.h>
+
+// Inlines
+static void ImplReadRect( SvStream& rIStm, tools::Rectangle& rRect )
+{
+ Point aTL;
+ Point aBR;
+
+ TypeSerializer aSerializer(rIStm);
+ aSerializer.readPoint(aTL);
+ aSerializer.readPoint(aBR);
+
+ rRect = tools::Rectangle( aTL, aBR );
+}
+
+static bool ImplReadPoly(SvStream& rIStm, tools::Polygon& rPoly)
+{
+ TypeSerializer aSerializer(rIStm);
+
+ sal_Int32 nSize32(0);
+ rIStm.ReadInt32(nSize32);
+ sal_uInt16 nSize = nSize32;
+
+ const size_t nMaxPossiblePoints = rIStm.remainingSize() / 2 * sizeof(sal_Int32);
+ if (nSize > nMaxPossiblePoints)
+ {
+ SAL_WARN("vcl.gdi", "svm record claims to have: " << nSize << " points, but only " << nMaxPossiblePoints << " possible");
+ return false;
+ }
+
+ rPoly = tools::Polygon(nSize);
+
+ for (sal_uInt16 i = 0; i < nSize && rIStm.good(); ++i)
+ {
+ aSerializer.readPoint(rPoly[i]);
+ }
+ return rIStm.good();
+}
+
+static bool ImplReadPolyPoly(SvStream& rIStm, tools::PolyPolygon& rPolyPoly)
+{
+ bool bSuccess = true;
+
+ tools::Polygon aPoly;
+ sal_Int32 nPolyCount32(0);
+ rIStm.ReadInt32(nPolyCount32);
+ sal_uInt16 nPolyCount = static_cast<sal_uInt16>(nPolyCount32);
+
+ for (sal_uInt16 i = 0; i < nPolyCount && rIStm.good(); ++i)
+ {
+ if (!ImplReadPoly(rIStm, aPoly))
+ {
+ bSuccess = false;
+ break;
+ }
+ rPolyPoly.Insert(aPoly);
+ }
+
+ return bSuccess && rIStm.good();
+}
+
+static void ImplReadColor( SvStream& rIStm, Color& rColor )
+{
+ sal_Int16 nVal(0);
+
+ rIStm.ReadInt16( nVal ); rColor.SetRed( sal::static_int_cast<sal_uInt8>(static_cast<sal_uInt16>(nVal) >> 8) );
+ rIStm.ReadInt16( nVal ); rColor.SetGreen( sal::static_int_cast<sal_uInt8>(static_cast<sal_uInt16>(nVal) >> 8) );
+ rIStm.ReadInt16( nVal ); rColor.SetBlue( sal::static_int_cast<sal_uInt8>(static_cast<sal_uInt16>(nVal) >> 8) );
+}
+
+static bool ImplReadMapMode(SvStream& rIStm, MapMode& rMapMode)
+{
+ sal_Int16 nUnit(0);
+ rIStm.ReadInt16(nUnit);
+
+ Point aOrg;
+ TypeSerializer aSerializer(rIStm);
+ aSerializer.readPoint(aOrg);
+
+ sal_Int32 nXNum(0), nXDenom(0), nYNum(0), nYDenom(0);
+ rIStm.ReadInt32(nXNum).ReadInt32(nXDenom).ReadInt32(nYNum).ReadInt32(nYDenom);
+
+ if (!rIStm.good() || nXDenom <= 0 || nYDenom <= 0 || nXNum <= 0 || nYNum <= 0)
+ {
+ SAL_WARN("vcl.gdi", "Parsing error: invalid mapmode fraction");
+ return false;
+ }
+
+ if (nUnit < sal_Int16(MapUnit::Map100thMM) || nUnit > sal_Int16(MapUnit::LAST))
+ {
+ SAL_WARN("vcl.gdi", "Parsing error: invalid mapmode");
+ return false;
+ }
+
+ rMapMode = MapMode(static_cast<MapUnit>(nUnit), aOrg, Fraction(nXNum, nXDenom), Fraction(nYNum, nYDenom));
+
+ return true;
+}
+
+static void ImplReadUnicodeComment( sal_uInt32 nStrmPos, SvStream& rIStm, OUString& rString )
+{
+ sal_uInt64 nOld = rIStm.Tell();
+ if ( nStrmPos )
+ {
+ sal_uInt16 nType;
+ sal_uInt32 nActionSize;
+ std::size_t nStringLen;
+
+ rIStm.Seek( nStrmPos );
+ rIStm .ReadUInt16( nType )
+ .ReadUInt32( nActionSize );
+
+ nStringLen = (nActionSize - 4) >> 1;
+
+ if ( nStringLen && ( nType == GDI_UNICODE_COMMENT ) )
+ rString = read_uInt16s_ToOUString(rIStm, nStringLen);
+ }
+ rIStm.Seek( nOld );
+}
+
+static void ImplSkipActions(SvStream& rIStm, sal_uLong nSkipCount)
+{
+ sal_Int32 nActionSize;
+ sal_Int16 nType;
+ for (sal_uLong i = 0; i < nSkipCount; ++i)
+ {
+ rIStm.ReadInt16(nType).ReadInt32(nActionSize);
+ if (!rIStm.good() || nActionSize < 4)
+ break;
+ rIStm.SeekRel(nActionSize - 4);
+ }
+}
+
+static void ImplReadExtendedPolyPolygonAction(SvStream& rIStm, tools::PolyPolygon& rPolyPoly)
+{
+ TypeSerializer aSerializer(rIStm);
+
+ rPolyPoly.Clear();
+ sal_uInt16 nPolygonCount(0);
+ rIStm.ReadUInt16( nPolygonCount );
+
+ if (!nPolygonCount)
+ return;
+
+ const size_t nMinRecordSize = sizeof(sal_uInt16);
+ const size_t nMaxRecords = rIStm.remainingSize() / nMinRecordSize;
+ if (nPolygonCount > nMaxRecords)
+ {
+ SAL_WARN("vcl.gdi", "Parsing error: " << nMaxRecords <<
+ " max possible entries, but " << nPolygonCount << " claimed, truncating");
+ nPolygonCount = nMaxRecords;
+ }
+
+ for(sal_uInt16 a(0); a < nPolygonCount; a++)
+ {
+ sal_uInt16 nPointCount(0);
+ rIStm.ReadUInt16(nPointCount);
+
+ const size_t nMinPolygonSize = sizeof(sal_Int32) * 2;
+ const size_t nMaxPolygons = rIStm.remainingSize() / nMinPolygonSize;
+ if (nPointCount > nMaxPolygons)
+ {
+ SAL_WARN("vcl.gdi", "Parsing error: " << nMaxPolygons <<
+ " max possible entries, but " << nPointCount << " claimed, truncating");
+ nPointCount = nMaxPolygons;
+ }
+
+ tools::Polygon aCandidate(nPointCount);
+
+ if (nPointCount)
+ {
+ for(sal_uInt16 b(0); b < nPointCount; b++)
+ {
+ aSerializer.readPoint(aCandidate[b]);
+ }
+
+ sal_uInt8 bHasFlags(int(false));
+ rIStm.ReadUChar( bHasFlags );
+
+ if(bHasFlags)
+ {
+ sal_uInt8 aPolyFlags(0);
+
+ for(sal_uInt16 c(0); c < nPointCount; c++)
+ {
+ rIStm.ReadUChar( aPolyFlags );
+ aCandidate.SetFlags(c, static_cast<PolyFlags>(aPolyFlags));
+ }
+ }
+ }
+
+ rPolyPoly.Insert(aCandidate);
+ }
+}
+
+SVMConverter::SVMConverter( SvStream& rStm, GDIMetaFile& rMtf )
+{
+ if( !rStm.GetError() )
+ {
+ ImplConvertFromSVM1( rStm, rMtf );
+ }
+}
+
+namespace
+{
+ sal_Int32 SkipActions(sal_Int32 i, sal_Int32 nFollowingActionCount, sal_Int32 nActions)
+ {
+ sal_Int32 remainingActions = nActions - i;
+ if (nFollowingActionCount < 0)
+ nFollowingActionCount = remainingActions;
+ return std::min(remainingActions, nFollowingActionCount);
+ }
+
+ void ClampRange(const OUString& rStr, sal_Int32& rIndex, sal_Int32& rLength,
+ std::vector<sal_Int32>* pDXAry = nullptr)
+ {
+ const sal_Int32 nStrLength = rStr.getLength();
+
+ if (rIndex < 0 || rIndex > nStrLength)
+ {
+ SAL_WARN("vcl.gdi", "inconsistent offset");
+ rIndex = nStrLength;
+ }
+
+ if (rLength < 0 || rLength > nStrLength - rIndex)
+ {
+ SAL_WARN("vcl.gdi", "inconsistent len");
+ rLength = nStrLength - rIndex;
+ }
+
+ if (pDXAry && pDXAry->size() > o3tl::make_unsigned(rLength))
+ pDXAry->resize(rLength);
+ }
+}
+
+#define LF_FACESIZE 32
+
+void static lcl_error( SvStream& rIStm, const SvStreamEndian& nOldFormat, sal_uInt64 nPos)
+{
+ rIStm.SetError(SVSTREAM_FILEFORMAT_ERROR);
+ rIStm.SetEndian(nOldFormat);
+ rIStm.Seek(nPos);
+ return;
+}
+void SVMConverter::ImplConvertFromSVM1( SvStream& rIStm, GDIMetaFile& rMtf )
+{
+ const sal_uInt64 nPos = rIStm.Tell();
+ const SvStreamEndian nOldFormat = rIStm.GetEndian();
+
+ rIStm.SetEndian( SvStreamEndian::LITTLE );
+
+ char aCode[ 5 ];
+ Size aPrefSz;
+
+ // read header
+ rIStm.ReadBytes(aCode, sizeof(aCode)); // Identifier
+ sal_Int16 nSize(0);
+ rIStm.ReadInt16( nSize ); // Size
+ sal_Int16 nVersion(0);
+ rIStm.ReadInt16( nVersion ); // Version
+ sal_Int32 nTmp32(0);
+ rIStm.ReadInt32( nTmp32 );
+ if (nTmp32 < 0)
+ {
+ SAL_WARN("vcl.gdi", "svm: value for width should be positive");
+ lcl_error(rIStm, nOldFormat, nPos);
+ return;
+ }
+ aPrefSz.setWidth( nTmp32 ); // PrefSize.Width()
+ rIStm.ReadInt32( nTmp32 );
+ if (nTmp32 < 0)
+ {
+ SAL_WARN("vcl.gdi", "svm: value for height should be positive");
+ lcl_error(rIStm, nOldFormat, nPos);
+ return;
+ }
+ aPrefSz.setHeight( nTmp32 ); // PrefSize.Height()
+
+ // check header-magic and version
+ if( rIStm.GetError()
+ || ( nVersion != 200 )
+ || ( memcmp( aCode, "SVGDI", sizeof( aCode ) ) != 0 ) )
+ {
+ SAL_WARN("vcl.gdi", "svm: wrong check for header-magic and version");
+ lcl_error(rIStm, nOldFormat, nPos);
+ return;
+ }
+
+ LineInfo aLineInfo( LineStyle::NONE, 0 );
+ std::stack<LineInfo, std::vector<LineInfo>> aLIStack;
+ ScopedVclPtrInstance< VirtualDevice > aFontVDev;
+ rtl_TextEncoding eActualCharSet = osl_getThreadTextEncoding();
+ bool bFatLine = false;
+
+ tools::Polygon aActionPoly;
+ tools::Rectangle aRect;
+ Point aPt, aPt1;
+ Size aSz;
+ Color aActionColor;
+
+ sal_uInt32 nUnicodeCommentStreamPos = 0;
+ sal_Int32 nUnicodeCommentActionNumber = 0;
+
+ rMtf.SetPrefSize(aPrefSz);
+
+ MapMode aMapMode;
+ if (ImplReadMapMode(rIStm, aMapMode)) // MapMode
+ rMtf.SetPrefMapMode(aMapMode);
+
+ sal_Int32 nActions(0);
+ rIStm.ReadInt32(nActions); // Action count
+ if (nActions < 0)
+ {
+ SAL_WARN("vcl.gdi", "svm claims negative action count (" << nActions << ")");
+ nActions = 0;
+ }
+
+ const size_t nMinActionSize = sizeof(sal_uInt16) + sizeof(sal_Int32);
+ const size_t nMaxPossibleActions = rIStm.remainingSize() / nMinActionSize;
+ if (o3tl::make_unsigned(nActions) > nMaxPossibleActions)
+ {
+ SAL_WARN("vcl.gdi", "svm claims more actions (" << nActions << ") than stream could provide, truncating");
+ nActions = nMaxPossibleActions;
+ }
+
+ size_t nLastPolygonAction(0);
+
+ TypeSerializer aSerializer(rIStm);
+
+ for (sal_Int32 i = 0; i < nActions && rIStm.good(); ++i)
+ {
+ sal_Int16 nType(0);
+ rIStm.ReadInt16(nType);
+ sal_Int32 nActBegin = rIStm.Tell();
+ sal_Int32 nActionSize(0);
+ rIStm.ReadInt32(nActionSize);
+
+ SAL_WARN_IF( ( nType > 33 ) && ( nType < 1024 ), "vcl.gdi", "Unknown GDIMetaAction while converting!" );
+
+ switch( nType )
+ {
+ case GDI_PIXEL_ACTION:
+ {
+ aSerializer.readPoint(aPt);
+ ImplReadColor( rIStm, aActionColor );
+ rMtf.AddAction( new MetaPixelAction( aPt, aActionColor ) );
+ }
+ break;
+
+ case GDI_POINT_ACTION:
+ {
+ aSerializer.readPoint(aPt);
+ rMtf.AddAction( new MetaPointAction( aPt ) );
+ }
+ break;
+
+ case GDI_LINE_ACTION:
+ {
+ aSerializer.readPoint(aPt);
+ aSerializer.readPoint(aPt1);
+ rMtf.AddAction( new MetaLineAction( aPt, aPt1, aLineInfo ) );
+ }
+ break;
+
+ case GDI_LINEJOIN_ACTION :
+ {
+ sal_Int16 nLineJoin(0);
+ rIStm.ReadInt16( nLineJoin );
+ aLineInfo.SetLineJoin(static_cast<basegfx::B2DLineJoin>(nLineJoin));
+ }
+ break;
+
+ case GDI_LINECAP_ACTION :
+ {
+ sal_Int16 nLineCap(0);
+ rIStm.ReadInt16( nLineCap );
+ aLineInfo.SetLineCap(static_cast<css::drawing::LineCap>(nLineCap));
+ }
+ break;
+
+ case GDI_LINEDASHDOT_ACTION :
+ {
+ sal_Int16 a(0);
+ sal_Int32 b(0);
+
+ rIStm.ReadInt16( a ); aLineInfo.SetDashCount(a);
+ rIStm.ReadInt32( b ); aLineInfo.SetDashLen(b);
+ rIStm.ReadInt16( a ); aLineInfo.SetDotCount(a);
+ rIStm.ReadInt32( b ); aLineInfo.SetDotLen(b);
+ rIStm.ReadInt32( b ); aLineInfo.SetDistance(b);
+
+ if(((aLineInfo.GetDashCount() && aLineInfo.GetDashLen())
+ || (aLineInfo.GetDotCount() && aLineInfo.GetDotLen()))
+ && aLineInfo.GetDistance())
+ {
+ aLineInfo.SetStyle(LineStyle::Dash);
+ }
+ }
+ break;
+
+ case GDI_EXTENDEDPOLYGON_ACTION :
+ {
+ // read the tools::PolyPolygon in every case
+ tools::PolyPolygon aInputPolyPolygon;
+ ImplReadExtendedPolyPolygonAction(rIStm, aInputPolyPolygon);
+
+ // now check if it can be set somewhere
+ if(nLastPolygonAction < rMtf.GetActionSize())
+ {
+ MetaPolyLineAction* pPolyLineAction = dynamic_cast< MetaPolyLineAction* >(rMtf.GetAction(nLastPolygonAction));
+
+ if(pPolyLineAction)
+ {
+ // replace MetaPolyLineAction when we have a single polygon. Do not rely on the
+ // same point count; the originally written GDI_POLYLINE_ACTION may have been
+ // Subdivided for better quality for older usages
+ if(1 == aInputPolyPolygon.Count())
+ {
+ rMtf.ReplaceAction(
+ new MetaPolyLineAction(
+ aInputPolyPolygon.GetObject(0),
+ pPolyLineAction->GetLineInfo()),
+ nLastPolygonAction);
+ }
+ }
+ else
+ {
+ MetaPolyPolygonAction* pPolyPolygonAction = dynamic_cast< MetaPolyPolygonAction* >(rMtf.GetAction(nLastPolygonAction));
+
+ if(pPolyPolygonAction)
+ {
+ // replace MetaPolyPolygonAction when we have a curved polygon. Do rely on the
+ // same sub-polygon count
+ if(pPolyPolygonAction->GetPolyPolygon().Count() == aInputPolyPolygon.Count())
+ {
+ rMtf.ReplaceAction(
+ new MetaPolyPolygonAction(
+ aInputPolyPolygon),
+ nLastPolygonAction);
+ }
+ }
+ else
+ {
+ MetaPolygonAction* pPolygonAction = dynamic_cast< MetaPolygonAction* >(rMtf.GetAction(nLastPolygonAction));
+
+ if(pPolygonAction)
+ {
+ // replace MetaPolygonAction
+ if(1 == aInputPolyPolygon.Count())
+ {
+ rMtf.ReplaceAction(
+ new MetaPolygonAction(
+ aInputPolyPolygon.GetObject(0)),
+ nLastPolygonAction);
+ }
+ }
+ }
+ }
+ }
+ }
+ break;
+
+ case GDI_RECT_ACTION:
+ {
+ ImplReadRect( rIStm, aRect );
+ sal_Int32 nTmp(0), nTmp1(0);
+ rIStm.ReadInt32( nTmp ).ReadInt32( nTmp1 );
+
+ if( nTmp || nTmp1 )
+ rMtf.AddAction( new MetaRoundRectAction( aRect, nTmp, nTmp1 ) );
+ else
+ {
+ rMtf.AddAction( new MetaRectAction( aRect ) );
+
+ if( bFatLine )
+ rMtf.AddAction( new MetaPolyLineAction( aRect, aLineInfo ) );
+ }
+ }
+ break;
+
+ case GDI_ELLIPSE_ACTION:
+ {
+ ImplReadRect( rIStm, aRect );
+
+ if( bFatLine )
+ {
+ const tools::Polygon aPoly( aRect.Center(), aRect.GetWidth() >> 1, aRect.GetHeight() >> 1 );
+
+ rMtf.AddAction( new MetaPushAction( vcl::PushFlags::LINECOLOR ) );
+ rMtf.AddAction( new MetaLineColorAction( COL_TRANSPARENT, false ) );
+ rMtf.AddAction( new MetaPolygonAction( aPoly ) );
+ rMtf.AddAction( new MetaPopAction() );
+ rMtf.AddAction( new MetaPolyLineAction( aPoly, aLineInfo ) );
+ }
+ else
+ rMtf.AddAction( new MetaEllipseAction( aRect ) );
+ }
+ break;
+
+ case GDI_ARC_ACTION:
+ {
+ ImplReadRect( rIStm, aRect );
+ aSerializer.readPoint(aPt);
+ aSerializer.readPoint(aPt1);
+
+ if( bFatLine )
+ {
+ const tools::Polygon aPoly( aRect, aPt, aPt1, PolyStyle::Arc );
+
+ rMtf.AddAction( new MetaPushAction( vcl::PushFlags::LINECOLOR ) );
+ rMtf.AddAction( new MetaLineColorAction( COL_TRANSPARENT, false ) );
+ rMtf.AddAction( new MetaPolygonAction( aPoly ) );
+ rMtf.AddAction( new MetaPopAction() );
+ rMtf.AddAction( new MetaPolyLineAction( aPoly, aLineInfo ) );
+ }
+ else
+ rMtf.AddAction( new MetaArcAction( aRect, aPt, aPt1 ) );
+ }
+ break;
+
+ case GDI_PIE_ACTION:
+ {
+ ImplReadRect( rIStm, aRect );
+ aSerializer.readPoint(aPt);
+ aSerializer.readPoint(aPt1);
+
+ if( bFatLine )
+ {
+ const tools::Polygon aPoly( aRect, aPt, aPt1, PolyStyle::Pie );
+
+ rMtf.AddAction( new MetaPushAction( vcl::PushFlags::LINECOLOR ) );
+ rMtf.AddAction( new MetaLineColorAction( COL_TRANSPARENT, false ) );
+ rMtf.AddAction( new MetaPolygonAction( aPoly ) );
+ rMtf.AddAction( new MetaPopAction() );
+ rMtf.AddAction( new MetaPolyLineAction( aPoly, aLineInfo ) );
+ }
+ else
+ rMtf.AddAction( new MetaPieAction( aRect, aPt, aPt1 ) );
+ }
+ break;
+
+ case GDI_INVERTRECT_ACTION:
+ case GDI_HIGHLIGHTRECT_ACTION:
+ {
+ ImplReadRect( rIStm, aRect );
+ rMtf.AddAction( new MetaPushAction( vcl::PushFlags::RASTEROP ) );
+ rMtf.AddAction( new MetaRasterOpAction( RasterOp::Invert ) );
+ rMtf.AddAction( new MetaRectAction( aRect ) );
+ rMtf.AddAction( new MetaPopAction() );
+ }
+ break;
+
+ case GDI_POLYLINE_ACTION:
+ {
+ if (ImplReadPoly(rIStm, aActionPoly))
+ {
+ nLastPolygonAction = rMtf.GetActionSize();
+
+ if( bFatLine )
+ rMtf.AddAction( new MetaPolyLineAction( aActionPoly, aLineInfo ) );
+ else
+ rMtf.AddAction( new MetaPolyLineAction( aActionPoly ) );
+ }
+ }
+ break;
+
+ case GDI_POLYGON_ACTION:
+ {
+ if (ImplReadPoly(rIStm, aActionPoly))
+ {
+ if( bFatLine )
+ {
+ rMtf.AddAction( new MetaPushAction( vcl::PushFlags::LINECOLOR ) );
+ rMtf.AddAction( new MetaLineColorAction( COL_TRANSPARENT, false ) );
+ rMtf.AddAction( new MetaPolygonAction( aActionPoly ) );
+ rMtf.AddAction( new MetaPopAction() );
+ rMtf.AddAction( new MetaPolyLineAction( aActionPoly, aLineInfo ) );
+ }
+ else
+ {
+ nLastPolygonAction = rMtf.GetActionSize();
+ rMtf.AddAction( new MetaPolygonAction( aActionPoly ) );
+ }
+ }
+ }
+ break;
+
+ case GDI_POLYPOLYGON_ACTION:
+ {
+ tools::PolyPolygon aPolyPoly;
+
+ if (ImplReadPolyPoly(rIStm, aPolyPoly))
+ {
+ if( bFatLine )
+ {
+ rMtf.AddAction( new MetaPushAction( vcl::PushFlags::LINECOLOR ) );
+ rMtf.AddAction( new MetaLineColorAction( COL_TRANSPARENT, false ) );
+ rMtf.AddAction( new MetaPolyPolygonAction( aPolyPoly ) );
+ rMtf.AddAction( new MetaPopAction() );
+
+ for( sal_uInt16 nPoly = 0, nCount = aPolyPoly.Count(); nPoly < nCount; nPoly++ )
+ rMtf.AddAction( new MetaPolyLineAction( aPolyPoly[ nPoly ], aLineInfo ) );
+ }
+ else
+ {
+ nLastPolygonAction = rMtf.GetActionSize();
+ rMtf.AddAction( new MetaPolyPolygonAction( aPolyPoly ) );
+ }
+ }
+ }
+ break;
+
+ case GDI_FONT_ACTION:
+ {
+ vcl::Font aFont;
+ char aName[LF_FACESIZE+1];
+
+ ImplReadColor( rIStm, aActionColor ); aFont.SetColor( aActionColor );
+ ImplReadColor( rIStm, aActionColor ); aFont.SetFillColor( aActionColor );
+ size_t nRet = rIStm.ReadBytes(aName, LF_FACESIZE);
+ aName[nRet] = 0;
+ aFont.SetFamilyName( OUString( aName, strlen(aName), rIStm.GetStreamCharSet() ) );
+
+ sal_Int32 nWidth(0), nHeight(0);
+ rIStm.ReadInt32(nWidth).ReadInt32(nHeight);
+ sal_Int16 nCharOrient(0), nLineOrient(0);
+ rIStm.ReadInt16(nCharOrient).ReadInt16(nLineOrient);
+ sal_Int16 nCharSet(0), nFamily(0), nPitch(0), nAlign(0), nWeight(0), nUnderline(0), nStrikeout(0);
+ rIStm.ReadInt16(nCharSet).ReadInt16(nFamily).ReadInt16(nPitch).ReadInt16(nAlign).ReadInt16(nWeight).ReadInt16(nUnderline).ReadInt16(nStrikeout);
+ bool bItalic(false), bOutline(false), bShadow(false), bTransparent(false);
+ rIStm.ReadCharAsBool(bItalic).ReadCharAsBool(bOutline).ReadCharAsBool(bShadow).ReadCharAsBool(bTransparent);
+
+ aFont.SetFontSize( Size( nWidth, nHeight ) );
+ aFont.SetCharSet( static_cast<rtl_TextEncoding>(nCharSet) );
+ aFont.SetFamily( static_cast<FontFamily>(nFamily & SAL_MAX_ENUM) );
+ aFont.SetPitch( static_cast<FontPitch>(nPitch & SAL_MAX_ENUM) );
+ aFont.SetAlignment( static_cast<TextAlign>(nAlign & SAL_MAX_ENUM) );
+ aFont.SetWeight( ( nWeight == 1 ) ? WEIGHT_LIGHT : ( nWeight == 2 ) ? WEIGHT_NORMAL :
+ ( nWeight == 3 ) ? WEIGHT_BOLD : WEIGHT_DONTKNOW );
+ aFont.SetUnderline( static_cast<FontLineStyle>(nUnderline & SAL_MAX_ENUM) );
+ aFont.SetStrikeout( static_cast<FontStrikeout>(nStrikeout & SAL_MAX_ENUM) );
+ aFont.SetItalic( bItalic ? ITALIC_NORMAL : ITALIC_NONE );
+ aFont.SetOutline( bOutline );
+ aFont.SetShadow( bShadow );
+ aFont.SetOrientation( Degree10(nLineOrient) );
+ aFont.SetTransparent( bTransparent );
+
+ eActualCharSet = aFont.GetCharSet();
+ if ( eActualCharSet == RTL_TEXTENCODING_DONTKNOW )
+ eActualCharSet = osl_getThreadTextEncoding();
+
+ rMtf.AddAction( new MetaFontAction( aFont ) );
+ rMtf.AddAction( new MetaTextAlignAction( aFont.GetAlignment() ) );
+ rMtf.AddAction( new MetaTextColorAction( aFont.GetColor() ) );
+ rMtf.AddAction( new MetaTextFillColorAction( aFont.GetFillColor(), !aFont.IsTransparent() ) );
+
+ // #106172# Track font relevant data in shadow VDev
+ aFontVDev->SetFont( aFont );
+ }
+ break;
+
+ case GDI_TEXT_ACTION:
+ {
+ sal_Int32 nIndex(0), nLen(0), nTmp(0);
+ aSerializer.readPoint(aPt);
+ rIStm.ReadInt32( nIndex ).ReadInt32( nLen ).ReadInt32( nTmp );
+ if (nTmp > 0)
+ {
+ OString aByteStr = read_uInt8s_ToOString(rIStm, nTmp);
+ sal_uInt8 nTerminator = 0;
+ rIStm.ReadUChar( nTerminator );
+ SAL_WARN_IF( nTerminator != 0, "vcl.gdi", "expected string to be NULL terminated" );
+
+ OUString aStr(OStringToOUString(aByteStr, eActualCharSet));
+ if ( nUnicodeCommentActionNumber == i )
+ ImplReadUnicodeComment( nUnicodeCommentStreamPos, rIStm, aStr );
+ ClampRange(aStr, nIndex, nLen);
+ rMtf.AddAction( new MetaTextAction( aPt, aStr, nIndex, nLen ) );
+ }
+
+ if (nActionSize < 24)
+ rIStm.SetError(SVSTREAM_FILEFORMAT_ERROR);
+ else
+ rIStm.Seek(nActBegin + nActionSize);
+ }
+ break;
+
+ case GDI_TEXTARRAY_ACTION:
+ {
+ sal_Int32 nIndex(0), nLen(0), nAryLen(0), nTmp(0);
+ aSerializer.readPoint(aPt);
+ rIStm.ReadInt32( nIndex ).ReadInt32( nLen ).ReadInt32( nTmp ).ReadInt32( nAryLen );
+ if (nTmp > 0)
+ {
+ OString aByteStr = read_uInt8s_ToOString(rIStm, nTmp);
+ sal_uInt8 nTerminator = 0;
+ rIStm.ReadUChar( nTerminator );
+ SAL_WARN_IF( nTerminator != 0, "vcl.gdi", "expected string to be NULL terminated" );
+
+ OUString aStr(OStringToOUString(aByteStr, eActualCharSet));
+
+ std::vector<sal_Int32> aDXAry;
+ if (nAryLen > 0)
+ {
+ const size_t nMinRecordSize = sizeof(sal_Int32);
+ const size_t nMaxRecords = rIStm.remainingSize() / nMinRecordSize;
+ if (o3tl::make_unsigned(nAryLen) > nMaxRecords)
+ {
+ SAL_WARN("vcl.gdi", "Parsing error: " << nMaxRecords <<
+ " max possible entries, but " << nAryLen << " claimed, truncating");
+ nAryLen = nMaxRecords;
+ }
+
+ sal_Int32 nStrLen( aStr.getLength() );
+
+ sal_Int32 nDXAryLen = std::max(nAryLen, nStrLen);
+
+ if (nDXAryLen < nLen)
+ {
+ //MetaTextArrayAction ctor expects pDXAry to be >= nLen if set, so if this can't
+ //be achieved, don't read it, it's utterly broken.
+ SAL_WARN("vcl.gdi", "dxary too short, discarding completely");
+ rIStm.SeekRel(sizeof(sal_Int32) * nDXAryLen);
+ nLen = 0;
+ nIndex = 0;
+ }
+ else
+ {
+ aDXAry.resize(nDXAryLen);
+
+ for (sal_Int32 j = 0; j < nAryLen; ++j)
+ {
+ rIStm.ReadInt32( nTmp );
+ aDXAry[ j ] = nTmp;
+ }
+
+ // #106172# Add last DX array elem, if missing
+ if( nAryLen != nStrLen )
+ {
+ if (nAryLen+1 == nStrLen && nIndex >= 0)
+ {
+ std::vector<sal_Int32> aTmpAry;
+
+ aFontVDev->GetTextArray( aStr, &aTmpAry, nIndex, nLen );
+
+ if (aTmpAry.size() < o3tl::make_unsigned(nStrLen))
+ SAL_WARN("vcl.gdi", "TextArray too short to recover missing element");
+ else
+ {
+ // now, the difference between the
+ // last and the second last DX array
+ // is the advancement for the last
+ // glyph. Thus, to complete our meta
+ // action's DX array, just add that
+ // difference to last elem and store
+ // in very last.
+ if( nStrLen > 1 )
+ aDXAry[ nStrLen-1 ] = aDXAry[ nStrLen-2 ] + aTmpAry[ nStrLen-1 ] - aTmpAry[ nStrLen-2 ];
+ else
+ aDXAry[ nStrLen-1 ] = aTmpAry[ nStrLen-1 ]; // len=1: 0th position taken to be 0
+ }
+ }
+#ifdef DBG_UTIL
+ else
+ OSL_FAIL("More than one DX array element missing on SVM import");
+#endif
+ }
+ }
+ }
+ if ( nUnicodeCommentActionNumber == i )
+ ImplReadUnicodeComment( nUnicodeCommentStreamPos, rIStm, aStr );
+ ClampRange(aStr, nIndex, nLen, &aDXAry);
+ rMtf.AddAction( new MetaTextArrayAction( aPt, aStr, aDXAry, nIndex, nLen ) );
+ }
+
+ if (nActionSize < 24)
+ rIStm.SetError(SVSTREAM_FILEFORMAT_ERROR);
+ else
+ rIStm.Seek(nActBegin + nActionSize);
+ }
+ break;
+
+ case GDI_STRETCHTEXT_ACTION:
+ {
+ sal_Int32 nIndex(0), nLen(0), nWidth(0), nTmp(0);
+
+ aSerializer.readPoint(aPt);
+ rIStm.ReadInt32( nIndex ).ReadInt32( nLen ).ReadInt32( nTmp ).ReadInt32( nWidth );
+ if (nTmp > 0)
+ {
+ OString aByteStr = read_uInt8s_ToOString(rIStm, nTmp);
+ sal_uInt8 nTerminator = 0;
+ rIStm.ReadUChar( nTerminator );
+ SAL_WARN_IF( nTerminator != 0, "vcl.gdi", "expected string to be NULL terminated" );
+
+ OUString aStr(OStringToOUString(aByteStr, eActualCharSet));
+ if ( nUnicodeCommentActionNumber == i )
+ ImplReadUnicodeComment( nUnicodeCommentStreamPos, rIStm, aStr );
+ ClampRange(aStr, nIndex, nLen);
+ rMtf.AddAction( new MetaStretchTextAction( aPt, nWidth, aStr, nIndex, nLen ) );
+ }
+
+ if (nActionSize < 28)
+ rIStm.SetError(SVSTREAM_FILEFORMAT_ERROR);
+ else
+ rIStm.Seek(nActBegin + nActionSize);
+ }
+ break;
+
+ case GDI_BITMAP_ACTION:
+ {
+ Bitmap aBmp;
+
+ aSerializer.readPoint(aPt);
+ ReadDIB(aBmp, rIStm, true);
+ rMtf.AddAction( new MetaBmpAction( aPt, aBmp ) );
+ }
+ break;
+
+ case GDI_BITMAPSCALE_ACTION:
+ {
+ Bitmap aBmp;
+
+ aSerializer.readPoint(aPt);
+ aSerializer.readSize(aSz);
+ ReadDIB(aBmp, rIStm, true);
+ rMtf.AddAction( new MetaBmpScaleAction( aPt, aSz, aBmp ) );
+ }
+ break;
+
+ case GDI_BITMAPSCALEPART_ACTION:
+ {
+ Bitmap aBmp;
+ Size aSz2;
+
+ aSerializer.readPoint(aPt);
+ aSerializer.readSize(aSz);
+ aSerializer.readPoint(aPt1);
+ aSerializer.readSize(aSz2);
+ ReadDIB(aBmp, rIStm, true);
+ rMtf.AddAction( new MetaBmpScalePartAction( aPt, aSz, aPt1, aSz2, aBmp ) );
+ }
+ break;
+
+ case GDI_PEN_ACTION:
+ {
+ ImplReadColor( rIStm, aActionColor );
+
+ sal_Int32 nPenWidth(0);
+ sal_Int16 nPenStyle(0);
+ rIStm.ReadInt32( nPenWidth ).ReadInt16( nPenStyle );
+
+ aLineInfo.SetStyle( nPenStyle ? LineStyle::Solid : LineStyle::NONE );
+ aLineInfo.SetWidth( nPenWidth );
+ bFatLine = nPenStyle && !aLineInfo.IsDefault();
+
+ rMtf.AddAction( new MetaLineColorAction( aActionColor, nPenStyle != 0 ) );
+ }
+ break;
+
+ case GDI_FILLBRUSH_ACTION:
+ {
+ ImplReadColor( rIStm, aActionColor );
+ rIStm.SeekRel( 6 );
+ sal_Int16 nBrushStyle(0);
+ rIStm.ReadInt16( nBrushStyle );
+ rMtf.AddAction( new MetaFillColorAction( aActionColor, nBrushStyle != 0 ) );
+ rIStm.SeekRel( 2 );
+ }
+ break;
+
+ case GDI_MAPMODE_ACTION:
+ {
+ if (ImplReadMapMode(rIStm, aMapMode))
+ {
+ rMtf.AddAction(new MetaMapModeAction(aMapMode));
+
+ // #106172# Track font relevant data in shadow VDev
+ aFontVDev->SetMapMode(aMapMode);
+ };
+ }
+ break;
+
+ case GDI_CLIPREGION_ACTION:
+ {
+ vcl::Region aRegion;
+ bool bClip = false;
+
+ sal_Int16 nRegType(0);
+ sal_Int16 bIntersect(0);
+ rIStm.ReadInt16( nRegType ).ReadInt16( bIntersect );
+ ImplReadRect( rIStm, aRect );
+
+ switch( nRegType )
+ {
+ case 0:
+ break;
+
+ case 1:
+ {
+ tools::Rectangle aRegRect;
+
+ ImplReadRect( rIStm, aRegRect );
+ aRegion = vcl::Region( aRegRect );
+ bClip = true;
+ }
+ break;
+
+ case 2:
+ {
+ if (ImplReadPoly(rIStm, aActionPoly))
+ {
+ aRegion = vcl::Region( aActionPoly );
+ bClip = true;
+ }
+ }
+ break;
+
+ case 3:
+ {
+ bool bSuccess = true;
+ tools::PolyPolygon aPolyPoly;
+ sal_Int32 nPolyCount32(0);
+ rIStm.ReadInt32(nPolyCount32);
+ sal_uInt16 nPolyCount(nPolyCount32);
+
+ for (sal_uInt16 j = 0; j < nPolyCount && rIStm.good(); ++j)
+ {
+ if (!ImplReadPoly(rIStm, aActionPoly))
+ {
+ bSuccess = false;
+ break;
+ }
+ aPolyPoly.Insert(aActionPoly);
+ }
+
+ if (bSuccess)
+ {
+ aRegion = vcl::Region( aPolyPoly );
+ bClip = true;
+ }
+ }
+ break;
+ }
+
+ if( bIntersect )
+ aRegion.Intersect( aRect );
+
+ rMtf.AddAction( new MetaClipRegionAction( aRegion, bClip ) );
+ }
+ break;
+
+ case GDI_MOVECLIPREGION_ACTION:
+ {
+ sal_Int32 nTmp(0), nTmp1(0);
+ rIStm.ReadInt32( nTmp ).ReadInt32( nTmp1 );
+ rMtf.AddAction( new MetaMoveClipRegionAction( nTmp, nTmp1 ) );
+ }
+ break;
+
+ case GDI_ISECTCLIPREGION_ACTION:
+ {
+ ImplReadRect( rIStm, aRect );
+ rMtf.AddAction( new MetaISectRectClipRegionAction( aRect ) );
+ }
+ break;
+
+ case GDI_RASTEROP_ACTION:
+ {
+ RasterOp eRasterOp;
+
+ sal_Int16 nRasterOp(0);
+ rIStm.ReadInt16( nRasterOp );
+
+ switch( nRasterOp )
+ {
+ case 1:
+ eRasterOp = RasterOp::Invert;
+ break;
+
+ case 4:
+ case 5:
+ eRasterOp = RasterOp::Xor;
+ break;
+
+ default:
+ eRasterOp = RasterOp::OverPaint;
+ break;
+ }
+
+ rMtf.AddAction( new MetaRasterOpAction( eRasterOp ) );
+ }
+ break;
+
+ case GDI_PUSH_ACTION:
+ {
+ aLIStack.push(aLineInfo);
+ rMtf.AddAction( new MetaPushAction( vcl::PushFlags::ALL ) );
+
+ // #106172# Track font relevant data in shadow VDev
+ aFontVDev->Push();
+ }
+ break;
+
+ case GDI_POP_ACTION:
+ {
+
+ std::optional<LineInfo> xLineInfo;
+ if (!aLIStack.empty())
+ {
+ xLineInfo = std::move(aLIStack.top());
+ aLIStack.pop();
+ }
+
+ // restore line info
+ if (xLineInfo)
+ {
+ aLineInfo = *xLineInfo;
+ xLineInfo.reset();
+ bFatLine = ( LineStyle::NONE != aLineInfo.GetStyle() ) && !aLineInfo.IsDefault();
+ }
+
+ rMtf.AddAction( new MetaPopAction() );
+
+ // #106172# Track font relevant data in shadow VDev
+ aFontVDev->Pop();
+ }
+ break;
+
+ case GDI_GRADIENT_ACTION:
+ {
+ ImplReadRect( rIStm, aRect );
+
+ sal_Int16 nStyle(0);
+ rIStm.ReadInt16( nStyle );
+
+ Color aStartCol, aEndCol;
+ ImplReadColor( rIStm, aStartCol );
+ ImplReadColor( rIStm, aEndCol );
+
+ sal_Int16 nAngle(0), nBorder(0), nOfsX(0), nOfsY(0), nIntensityStart(0), nIntensityEnd(0);
+ rIStm.ReadInt16( nAngle ).ReadInt16( nBorder ).ReadInt16( nOfsX ).ReadInt16( nOfsY ).ReadInt16( nIntensityStart ).ReadInt16( nIntensityEnd );
+
+ Gradient aGrad( static_cast<GradientStyle>(nStyle), aStartCol, aEndCol );
+
+ aGrad.SetAngle( Degree10(nAngle) );
+ aGrad.SetBorder( nBorder );
+ aGrad.SetOfsX( nOfsX );
+ aGrad.SetOfsY( nOfsY );
+ aGrad.SetStartIntensity( nIntensityStart );
+ aGrad.SetEndIntensity( nIntensityEnd );
+ rMtf.AddAction( new MetaGradientAction( aRect, aGrad ) );
+ }
+ break;
+
+ case GDI_TRANSPARENT_COMMENT:
+ {
+ tools::PolyPolygon aPolyPoly;
+ sal_Int32 nFollowingActionCount(0);
+ sal_Int16 nTrans(0);
+
+ ReadPolyPolygon( rIStm, aPolyPoly );
+ rIStm.ReadInt16( nTrans ).ReadInt32( nFollowingActionCount );
+ ImplSkipActions( rIStm, nFollowingActionCount );
+ rMtf.AddAction( new MetaTransparentAction( aPolyPoly, nTrans ) );
+
+ i = SkipActions(i, nFollowingActionCount, nActions);
+ }
+ break;
+
+ case GDI_FLOATTRANSPARENT_COMMENT:
+ {
+ GDIMetaFile aMtf;
+ Point aPos;
+ Size aSize;
+ Gradient aGradient;
+ sal_Int32 nFollowingActionCount(0);
+
+ SvmReader aReader( rIStm );
+ aReader.Read( aMtf );
+ aSerializer.readPoint(aPos);
+ aSerializer.readSize(aSize);
+ aSerializer.readGradient(aGradient);
+ rIStm.ReadInt32( nFollowingActionCount );
+ ImplSkipActions( rIStm, nFollowingActionCount );
+ rMtf.AddAction( new MetaFloatTransparentAction( aMtf, aPos, aSize, aGradient ) );
+
+ i = SkipActions(i, nFollowingActionCount, nActions);
+ }
+ break;
+
+ case GDI_HATCH_COMMENT:
+ {
+ tools::PolyPolygon aPolyPoly;
+ Hatch aHatch;
+ sal_Int32 nFollowingActionCount(0);
+
+ ReadPolyPolygon( rIStm, aPolyPoly );
+ ReadHatch( rIStm, aHatch );
+ rIStm.ReadInt32( nFollowingActionCount );
+ ImplSkipActions( rIStm, nFollowingActionCount );
+ rMtf.AddAction( new MetaHatchAction( aPolyPoly, aHatch ) );
+
+ i = SkipActions(i, nFollowingActionCount, nActions);
+ }
+ break;
+
+ case GDI_REFPOINT_COMMENT:
+ {
+ Point aRefPoint;
+ bool bSet(false);
+ sal_Int32 nFollowingActionCount(0);
+
+ aSerializer.readPoint(aRefPoint);
+ rIStm.ReadCharAsBool( bSet ).ReadInt32( nFollowingActionCount );
+ ImplSkipActions( rIStm, nFollowingActionCount );
+ rMtf.AddAction( new MetaRefPointAction( aRefPoint, bSet ) );
+
+ i = SkipActions(i, nFollowingActionCount, nActions);
+
+ // #106172# Track font relevant data in shadow VDev
+ if( bSet )
+ aFontVDev->SetRefPoint( aRefPoint );
+ else
+ aFontVDev->SetRefPoint();
+ }
+ break;
+
+ case GDI_TEXTLINECOLOR_COMMENT:
+ {
+ Color aColor;
+ bool bSet(false);
+ sal_Int32 nFollowingActionCount(0);
+
+ aSerializer.readColor(aColor);
+ rIStm.ReadCharAsBool( bSet ).ReadInt32( nFollowingActionCount );
+ ImplSkipActions( rIStm, nFollowingActionCount );
+ rMtf.AddAction( new MetaTextLineColorAction( aColor, bSet ) );
+
+ i = SkipActions(i, nFollowingActionCount, nActions);
+ }
+ break;
+
+ case GDI_TEXTLINE_COMMENT:
+ {
+ Point aStartPt;
+ sal_Int32 nWidth(0);
+ sal_uInt32 nStrikeout(0);
+ sal_uInt32 nUnderline(0);
+ sal_Int32 nFollowingActionCount(0);
+
+ aSerializer.readPoint(aStartPt);
+ rIStm.ReadInt32(nWidth ).ReadUInt32(nStrikeout).ReadUInt32(nUnderline).ReadInt32(nFollowingActionCount);
+ ImplSkipActions(rIStm, nFollowingActionCount);
+ rMtf.AddAction( new MetaTextLineAction( aStartPt, nWidth,
+ static_cast<FontStrikeout>(nStrikeout & SAL_MAX_ENUM),
+ static_cast<FontLineStyle>(nUnderline & SAL_MAX_ENUM),
+ LINESTYLE_NONE ) );
+
+ i = SkipActions(i, nFollowingActionCount, nActions);
+ }
+ break;
+
+ case GDI_GRADIENTEX_COMMENT:
+ {
+ tools::PolyPolygon aPolyPoly;
+ Gradient aGradient;
+ sal_Int32 nFollowingActionCount(0);
+
+ ReadPolyPolygon( rIStm, aPolyPoly );
+ aSerializer.readGradient(aGradient);
+ rIStm.ReadInt32( nFollowingActionCount );
+ ImplSkipActions( rIStm, nFollowingActionCount );
+ rMtf.AddAction( new MetaGradientExAction( aPolyPoly, aGradient ) );
+
+ i = SkipActions(i, nFollowingActionCount, nActions);
+ }
+ break;
+
+ case GDI_COMMENT_COMMENT:
+ {
+ std::vector<sal_uInt8> aData;
+
+ OString aComment = read_uInt16_lenPrefixed_uInt8s_ToOString(rIStm);
+ sal_Int32 nValue(0);
+ sal_uInt32 nDataSize(0);
+ rIStm.ReadInt32(nValue).ReadUInt32(nDataSize);
+
+ if (nDataSize)
+ {
+ const size_t nMaxPossibleData = rIStm.remainingSize();
+ if (nDataSize > nMaxPossibleActions)
+ {
+ SAL_WARN("vcl.gdi", "svm record claims to have: " << nDataSize << " data, but only " << nMaxPossibleData << " possible");
+ nDataSize = nMaxPossibleActions;
+ }
+ aData.resize(nDataSize);
+ nDataSize = rIStm.ReadBytes(aData.data(), nDataSize);
+ }
+
+ sal_Int32 nFollowingActionCount(0);
+ rIStm.ReadInt32(nFollowingActionCount);
+ ImplSkipActions( rIStm, nFollowingActionCount );
+ rMtf.AddAction(new MetaCommentAction(aComment, nValue, aData.data(), nDataSize));
+
+ i = SkipActions(i, nFollowingActionCount, nActions);
+ }
+ break;
+
+ case GDI_UNICODE_COMMENT:
+ {
+ nUnicodeCommentActionNumber = i + 1;
+ nUnicodeCommentStreamPos = rIStm.Tell() - 6;
+ if (nActionSize < 4)
+ rIStm.SetError(SVSTREAM_FILEFORMAT_ERROR);
+ else
+ rIStm.SeekRel(nActionSize - 4);
+ }
+ break;
+
+ default:
+ if (nActionSize < 4)
+ rIStm.SetError(SVSTREAM_FILEFORMAT_ERROR);
+ else
+ rIStm.SeekRel(nActionSize - 4);
+ break;
+ }
+ }
+
+ rIStm.SetEndian( nOldFormat );
+}
+
+bool TestImportSVM(SvStream& rStream)
+{
+ GDIMetaFile aGDIMetaFile;
+ SvmReader aReader(rStream);
+ aReader.Read(aGDIMetaFile);
+ ScopedVclPtrInstance<VirtualDevice> aVDev;
+ aVDev->SetTextRenderModeForResolutionIndependentLayout(true);
+ try
+ {
+ aGDIMetaFile.Play(*aVDev);
+ }
+ catch (const boost::bad_rational&)
+ {
+ return false;
+ }
+ return true;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/vcl/source/filter/svm/SvmConverter.hxx b/vcl/source/filter/svm/SvmConverter.hxx
new file mode 100644
index 000000000..23185dc04
--- /dev/null
+++ b/vcl/source/filter/svm/SvmConverter.hxx
@@ -0,0 +1,92 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#ifndef INCLUDED_VCL_INC_SVMCONVERTER_HXX
+#define INCLUDED_VCL_INC_SVMCONVERTER_HXX
+
+#include <vcl/dllapi.h>
+#include <vcl/gdimtf.hxx>
+
+#define GDI_PIXEL_ACTION 1
+#define GDI_POINT_ACTION 2
+#define GDI_LINE_ACTION 3
+#define GDI_RECT_ACTION 4
+#define GDI_ELLIPSE_ACTION 5
+#define GDI_ARC_ACTION 6
+#define GDI_PIE_ACTION 7
+#define GDI_INVERTRECT_ACTION 8
+#define GDI_HIGHLIGHTRECT_ACTION 9
+#define GDI_POLYLINE_ACTION 10
+#define GDI_POLYGON_ACTION 11
+#define GDI_POLYPOLYGON_ACTION 12
+#define GDI_TEXT_ACTION 13
+#define GDI_TEXTARRAY_ACTION 14
+#define GDI_STRETCHTEXT_ACTION 15
+#define GDI_BITMAP_ACTION 17
+#define GDI_BITMAPSCALE_ACTION 18
+#define GDI_PEN_ACTION 19
+#define GDI_FONT_ACTION 20
+#define GDI_FILLBRUSH_ACTION 22
+#define GDI_MAPMODE_ACTION 23
+#define GDI_CLIPREGION_ACTION 24
+#define GDI_RASTEROP_ACTION 25
+#define GDI_PUSH_ACTION 26
+#define GDI_POP_ACTION 27
+#define GDI_MOVECLIPREGION_ACTION 28
+#define GDI_ISECTCLIPREGION_ACTION 29
+#define GDI_BITMAPSCALEPART_ACTION 32
+#define GDI_GRADIENT_ACTION 33
+
+#define GDI_TRANSPARENT_COMMENT 1024
+#define GDI_HATCH_COMMENT 1025
+#define GDI_REFPOINT_COMMENT 1026
+#define GDI_TEXTLINECOLOR_COMMENT 1027
+#define GDI_TEXTLINE_COMMENT 1028
+#define GDI_FLOATTRANSPARENT_COMMENT 1029
+#define GDI_GRADIENTEX_COMMENT 1030
+#define GDI_COMMENT_COMMENT 1031
+#define GDI_UNICODE_COMMENT 1032
+
+#define GDI_LINEJOIN_ACTION 1033
+#define GDI_EXTENDEDPOLYGON_ACTION 1034
+#define GDI_LINEDASHDOT_ACTION 1035
+
+#define GDI_LINECAP_ACTION 1036
+
+/**
+ * Converts old SVGDI aka SVM1 format data to current VCLMTF aka SVM2 format metafile data.
+ */
+class SVMConverter
+{
+private:
+ static void ImplConvertFromSVM1( SvStream& rIStm, GDIMetaFile& rMtf );
+
+public:
+ SVMConverter( SvStream& rIStm, GDIMetaFile& rMtf );
+
+private:
+ SVMConverter( const SVMConverter& ) = delete;
+ SVMConverter& operator=( const SVMConverter& ) = delete;
+};
+
+extern "C" SAL_DLLPUBLIC_EXPORT bool TestImportSVM(SvStream& rStream);
+
+#endif // INCLUDED_VCL_INC_SVMCONVERTER_HXX
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/vcl/source/filter/svm/SvmReader.cxx b/vcl/source/filter/svm/SvmReader.cxx
new file mode 100644
index 000000000..f02451ea3
--- /dev/null
+++ b/vcl/source/filter/svm/SvmReader.cxx
@@ -0,0 +1,1438 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <sal/log.hxx>
+#include <osl/thread.h>
+#include <tools/stream.hxx>
+#include <tools/vcompat.hxx>
+
+#include <vcl/filter/SvmReader.hxx>
+#include <vcl/TypeSerializer.hxx>
+#include <vcl/dibtools.hxx>
+#include <vcl/gdimtf.hxx>
+#include <vcl/metaact.hxx>
+
+#include "SvmConverter.hxx"
+
+namespace
+{
+class DepthGuard
+{
+private:
+ ImplMetaReadData& m_rData;
+ rtl_TextEncoding m_eOrigCharSet;
+
+public:
+ DepthGuard(ImplMetaReadData& rData, SvStream const& rIStm)
+ : m_rData(rData)
+ , m_eOrigCharSet(m_rData.meActualCharSet)
+ {
+ ++m_rData.mnParseDepth;
+ m_rData.meActualCharSet = rIStm.GetStreamCharSet();
+ }
+ bool TooDeep() const { return m_rData.mnParseDepth > 1024; }
+ ~DepthGuard()
+ {
+ --m_rData.mnParseDepth;
+ m_rData.meActualCharSet = m_eOrigCharSet;
+ }
+};
+}
+
+SvmReader::SvmReader(SvStream& rIStm)
+ : mrStream(rIStm)
+{
+}
+
+SvStream& SvmReader::Read(GDIMetaFile& rMetaFile, ImplMetaReadData* pData)
+{
+ if (mrStream.GetError())
+ {
+ SAL_WARN("vcl.gdi", "Stream error: " << mrStream.GetError());
+ return mrStream;
+ }
+
+ sal_uInt64 nStmPos = mrStream.Tell();
+ SvStreamEndian nOldFormat = mrStream.GetEndian();
+
+ mrStream.SetEndian(SvStreamEndian::LITTLE);
+
+ try
+ {
+ char aId[7];
+ aId[0] = 0;
+ aId[6] = 0;
+ mrStream.ReadBytes(aId, 6);
+ if (mrStream.good() && !strcmp(aId, "VCLMTF"))
+ {
+ // new format
+ sal_uInt32 nStmCompressMode = 0;
+ sal_uInt32 nCount = 0;
+ std::unique_ptr<VersionCompatRead> pCompat(new VersionCompatRead(mrStream));
+
+ mrStream.ReadUInt32(nStmCompressMode);
+ TypeSerializer aSerializer(mrStream);
+ MapMode aMapMode;
+ aSerializer.readMapMode(aMapMode);
+ rMetaFile.SetPrefMapMode(aMapMode);
+ Size aSize;
+ aSerializer.readSize(aSize);
+ rMetaFile.SetPrefSize(aSize);
+ mrStream.ReadUInt32(nCount);
+
+ pCompat.reset(); // destructor writes stuff into the header
+
+ std::unique_ptr<ImplMetaReadData> xReadData;
+ if (!pData)
+ {
+ xReadData.reset(new ImplMetaReadData);
+ pData = xReadData.get();
+ }
+ DepthGuard aDepthGuard(*pData, mrStream);
+
+ if (aDepthGuard.TooDeep())
+ throw std::runtime_error("too much recursion");
+
+ for (sal_uInt32 nAction = 0; (nAction < nCount) && !mrStream.eof(); nAction++)
+ {
+ rtl::Reference<MetaAction> pAction = MetaActionHandler(pData);
+ if (pAction)
+ {
+ if (pAction->GetType() == MetaActionType::COMMENT)
+ {
+ MetaCommentAction* pCommentAct
+ = static_cast<MetaCommentAction*>(pAction.get());
+
+ if (pCommentAct->GetComment() == "EMF_PLUS")
+ rMetaFile.UseCanvas(true);
+ }
+ rMetaFile.AddAction(pAction);
+ }
+ }
+ }
+ else
+ {
+ mrStream.Seek(nStmPos);
+ SVMConverter(mrStream, rMetaFile);
+ }
+ }
+ catch (...)
+ {
+ SAL_WARN("vcl", "GDIMetaFile exception during load");
+ mrStream.SetError(SVSTREAM_FILEFORMAT_ERROR);
+ };
+
+ // check for errors
+ if (mrStream.GetError())
+ {
+ rMetaFile.Clear();
+ mrStream.Seek(nStmPos);
+ }
+
+ mrStream.SetEndian(nOldFormat);
+ return mrStream;
+}
+
+rtl::Reference<MetaAction> SvmReader::MetaActionHandler(ImplMetaReadData* pData)
+{
+ rtl::Reference<MetaAction> pAction;
+ sal_uInt16 nTmp = 0;
+ mrStream.ReadUInt16(nTmp);
+ MetaActionType nType = static_cast<MetaActionType>(nTmp);
+
+ switch (nType)
+ {
+ case MetaActionType::NONE:
+ return DefaultHandler();
+ case MetaActionType::PIXEL:
+ return PixelHandler();
+ case MetaActionType::POINT:
+ return PointHandler();
+ case MetaActionType::LINE:
+ return LineHandler();
+ case MetaActionType::RECT:
+ return RectHandler();
+ case MetaActionType::ROUNDRECT:
+ return RoundRectHandler();
+ case MetaActionType::ELLIPSE:
+ return EllipseHandler();
+ case MetaActionType::ARC:
+ return ArcHandler();
+ case MetaActionType::PIE:
+ return PieHandler();
+ case MetaActionType::CHORD:
+ return ChordHandler();
+ case MetaActionType::POLYLINE:
+ return PolyLineHandler();
+ case MetaActionType::POLYGON:
+ return PolygonHandler();
+ case MetaActionType::POLYPOLYGON:
+ return PolyPolygonHandler();
+ case MetaActionType::TEXT:
+ return TextHandler(pData);
+ case MetaActionType::TEXTARRAY:
+ return TextArrayHandler(pData);
+ case MetaActionType::STRETCHTEXT:
+ return StretchTextHandler(pData);
+ case MetaActionType::TEXTRECT:
+ return TextRectHandler(pData);
+ case MetaActionType::TEXTLINE:
+ return TextLineHandler();
+ case MetaActionType::BMP:
+ return BmpHandler();
+ case MetaActionType::BMPSCALE:
+ return BmpScaleHandler();
+ case MetaActionType::BMPSCALEPART:
+ return BmpScalePartHandler();
+ case MetaActionType::BMPEX:
+ return BmpExHandler();
+ case MetaActionType::BMPEXSCALE:
+ return BmpExScaleHandler();
+ case MetaActionType::BMPEXSCALEPART:
+ return BmpExScalePartHandler();
+ case MetaActionType::MASK:
+ return MaskHandler();
+ case MetaActionType::MASKSCALE:
+ return MaskScaleHandler();
+ case MetaActionType::MASKSCALEPART:
+ return MaskScalePartHandler();
+ case MetaActionType::GRADIENT:
+ return GradientHandler();
+ case MetaActionType::GRADIENTEX:
+ return GradientExHandler();
+ case MetaActionType::HATCH:
+ return HatchHandler();
+ case MetaActionType::WALLPAPER:
+ return WallpaperHandler();
+ case MetaActionType::CLIPREGION:
+ return ClipRegionHandler();
+ case MetaActionType::ISECTRECTCLIPREGION:
+ return ISectRectClipRegionHandler();
+ case MetaActionType::ISECTREGIONCLIPREGION:
+ return ISectRegionClipRegionHandler();
+ case MetaActionType::MOVECLIPREGION:
+ return MoveClipRegionHandler();
+ case MetaActionType::LINECOLOR:
+ return LineColorHandler();
+ case MetaActionType::FILLCOLOR:
+ return FillColorHandler();
+ case MetaActionType::TEXTCOLOR:
+ return TextColorHandler();
+ case MetaActionType::TEXTFILLCOLOR:
+ return TextFillColorHandler();
+ case MetaActionType::TEXTLINECOLOR:
+ return TextLineColorHandler();
+ case MetaActionType::OVERLINECOLOR:
+ return OverlineColorHandler();
+ case MetaActionType::TEXTALIGN:
+ return TextAlignHandler();
+ case MetaActionType::MAPMODE:
+ return MapModeHandler();
+ case MetaActionType::FONT:
+ return FontHandler(pData);
+ case MetaActionType::PUSH:
+ return PushHandler();
+ case MetaActionType::POP:
+ return PopHandler();
+ case MetaActionType::RASTEROP:
+ return RasterOpHandler();
+ case MetaActionType::Transparent:
+ return TransparentHandler();
+ case MetaActionType::FLOATTRANSPARENT:
+ return FloatTransparentHandler(pData);
+ case MetaActionType::EPS:
+ return EPSHandler();
+ case MetaActionType::REFPOINT:
+ return RefPointHandler();
+ case MetaActionType::COMMENT:
+ return CommentHandler();
+ case MetaActionType::LAYOUTMODE:
+ return LayoutModeHandler();
+ case MetaActionType::TEXTLANGUAGE:
+ return TextLanguageHandler();
+
+ default:
+ {
+ VersionCompatRead aCompat(mrStream);
+ }
+ break;
+ }
+
+ return pAction;
+}
+
+void SvmReader::ReadColor(Color& rColor)
+{
+ sal_uInt32 nTmp(0);
+ mrStream.ReadUInt32(nTmp);
+ rColor = ::Color(ColorTransparency, nTmp);
+}
+
+rtl::Reference<MetaAction> SvmReader::LineColorHandler()
+{
+ rtl::Reference<MetaLineColorAction> pAction(new MetaLineColorAction);
+
+ VersionCompatRead aCompat(mrStream);
+ Color aColor;
+ ReadColor(aColor);
+ bool aBool(false);
+ mrStream.ReadCharAsBool(aBool);
+
+ pAction->SetSetting(aBool);
+ pAction->SetColor(aColor);
+
+ return pAction;
+}
+
+rtl::Reference<MetaAction> SvmReader::FillColorHandler()
+{
+ rtl::Reference<MetaFillColorAction> pAction(new MetaFillColorAction);
+
+ VersionCompatRead aCompat(mrStream);
+
+ Color aColor;
+ ReadColor(aColor);
+ bool aBool(false);
+ mrStream.ReadCharAsBool(aBool);
+
+ pAction->SetColor(aColor);
+ pAction->SetSetting(aBool);
+
+ return pAction;
+}
+
+rtl::Reference<MetaAction> SvmReader::RectHandler()
+{
+ rtl::Reference<MetaRectAction> pAction(new MetaRectAction);
+
+ VersionCompatRead aCompat(mrStream);
+ TypeSerializer aSerializer(mrStream);
+
+ tools::Rectangle aRectangle;
+ aSerializer.readRectangle(aRectangle);
+ pAction->SetRect(aRectangle);
+
+ return pAction;
+}
+
+rtl::Reference<MetaAction> SvmReader::PointHandler()
+{
+ rtl::Reference<MetaPointAction> pAction(new MetaPointAction);
+
+ VersionCompatRead aCompat(mrStream);
+ TypeSerializer aSerializer(mrStream);
+
+ Point aPoint;
+ aSerializer.readPoint(aPoint);
+ pAction->SetPoint(aPoint);
+
+ return pAction;
+}
+
+rtl::Reference<MetaAction> SvmReader::PixelHandler()
+{
+ rtl::Reference<MetaPixelAction> pAction(new MetaPixelAction);
+
+ VersionCompatRead aCompat(mrStream);
+ TypeSerializer aSerializer(mrStream);
+
+ Point aPoint;
+ aSerializer.readPoint(aPoint);
+ Color aColor;
+ ReadColor(aColor);
+
+ pAction->SetPoint(aPoint);
+ pAction->SetColor(aColor);
+
+ return pAction;
+}
+
+rtl::Reference<MetaAction> SvmReader::LineHandler()
+{
+ rtl::Reference<MetaLineAction> pAction(new MetaLineAction);
+
+ VersionCompatRead aCompat(mrStream);
+
+ // Version 1
+ TypeSerializer aSerializer(mrStream);
+ Point aPoint;
+ Point aEndPoint;
+ aSerializer.readPoint(aPoint);
+ aSerializer.readPoint(aEndPoint);
+
+ pAction->SetStartPoint(aPoint);
+ pAction->SetEndPoint(aEndPoint);
+
+ // Version 2
+ if (aCompat.GetVersion() >= 2)
+ {
+ LineInfo aLineInfo;
+ ReadLineInfo(mrStream, aLineInfo);
+ pAction->SetLineInfo(aLineInfo);
+ }
+
+ return pAction;
+}
+
+rtl::Reference<MetaAction> SvmReader::RoundRectHandler()
+{
+ rtl::Reference<MetaRoundRectAction> pAction(new MetaRoundRectAction);
+
+ VersionCompatRead aCompat(mrStream);
+ TypeSerializer aSerializer(mrStream);
+
+ tools::Rectangle aRectangle;
+ aSerializer.readRectangle(aRectangle);
+ sal_uInt32 HorzRound(0);
+ sal_uInt32 VertRound(0);
+ mrStream.ReadUInt32(HorzRound).ReadUInt32(VertRound);
+
+ pAction->SetRect(aRectangle);
+ pAction->SetHorzRound(HorzRound);
+ pAction->SetVertRound(VertRound);
+
+ return pAction;
+}
+
+rtl::Reference<MetaAction> SvmReader::EllipseHandler()
+{
+ rtl::Reference<MetaEllipseAction> pAction(new MetaEllipseAction);
+
+ VersionCompatRead aCompat(mrStream);
+ TypeSerializer aSerializer(mrStream);
+
+ tools::Rectangle aRectangle;
+ aSerializer.readRectangle(aRectangle);
+
+ pAction->SetRect(aRectangle);
+
+ return pAction;
+}
+
+rtl::Reference<MetaAction> SvmReader::ArcHandler()
+{
+ rtl::Reference<MetaArcAction> pAction(new MetaArcAction);
+
+ VersionCompatRead aCompat(mrStream);
+ TypeSerializer aSerializer(mrStream);
+
+ tools::Rectangle aRectangle;
+ aSerializer.readRectangle(aRectangle);
+ Point aPoint;
+ aSerializer.readPoint(aPoint);
+ Point aEndPoint;
+ aSerializer.readPoint(aEndPoint);
+
+ pAction->SetRect(aRectangle);
+ pAction->SetStartPoint(aPoint);
+ pAction->SetEndPoint(aEndPoint);
+
+ return pAction;
+}
+
+rtl::Reference<MetaAction> SvmReader::PieHandler()
+{
+ rtl::Reference<MetaPieAction> pAction(new MetaPieAction);
+
+ VersionCompatRead aCompat(mrStream);
+ TypeSerializer aSerializer(mrStream);
+
+ tools::Rectangle aRectangle;
+ aSerializer.readRectangle(aRectangle);
+ Point aPoint;
+ aSerializer.readPoint(aPoint);
+ Point aEndPoint;
+ aSerializer.readPoint(aEndPoint);
+
+ pAction->SetRect(aRectangle);
+ pAction->SetStartPoint(aPoint);
+ pAction->SetEndPoint(aEndPoint);
+
+ return pAction;
+}
+
+rtl::Reference<MetaAction> SvmReader::ChordHandler()
+{
+ rtl::Reference<MetaChordAction> pAction(new MetaChordAction);
+
+ VersionCompatRead aCompat(mrStream);
+ TypeSerializer aSerializer(mrStream);
+
+ tools::Rectangle aRectangle;
+ aSerializer.readRectangle(aRectangle);
+ Point aPoint;
+ aSerializer.readPoint(aPoint);
+ Point aEndPoint;
+ aSerializer.readPoint(aEndPoint);
+
+ pAction->SetRect(aRectangle);
+ pAction->SetStartPoint(aPoint);
+ pAction->SetEndPoint(aEndPoint);
+
+ return pAction;
+}
+
+rtl::Reference<MetaAction> SvmReader::PolyLineHandler()
+{
+ rtl::Reference<MetaPolyLineAction> pAction(new MetaPolyLineAction);
+
+ VersionCompatRead aCompat(mrStream);
+
+ // Version 1
+ tools::Polygon aPolygon;
+ ReadPolygon(mrStream, aPolygon);
+
+ // Version 2
+ if (aCompat.GetVersion() >= 2)
+ {
+ LineInfo aLineInfo;
+ ReadLineInfo(mrStream, aLineInfo);
+ pAction->SetLineInfo(aLineInfo);
+ }
+ if (aCompat.GetVersion() >= 3)
+ {
+ sal_uInt8 bHasPolyFlags(0);
+ mrStream.ReadUChar(bHasPolyFlags);
+ if (bHasPolyFlags)
+ aPolygon.Read(mrStream);
+ }
+ pAction->SetPolygon(aPolygon);
+
+ return pAction;
+}
+
+rtl::Reference<MetaAction> SvmReader::PolygonHandler()
+{
+ rtl::Reference<MetaPolygonAction> pAction(new MetaPolygonAction);
+
+ VersionCompatRead aCompat(mrStream);
+
+ tools::Polygon aPolygon;
+ ReadPolygon(mrStream, aPolygon); // Version 1
+
+ if (aCompat.GetVersion() >= 2) // Version 2
+ {
+ sal_uInt8 bHasPolyFlags(0);
+ mrStream.ReadUChar(bHasPolyFlags);
+ if (bHasPolyFlags)
+ aPolygon.Read(mrStream);
+ }
+
+ pAction->SetPolygon(aPolygon);
+
+ return pAction;
+}
+
+rtl::Reference<MetaAction> SvmReader::PolyPolygonHandler()
+{
+ rtl::Reference<MetaPolyPolygonAction> pAction(new MetaPolyPolygonAction);
+
+ VersionCompatRead aCompat(mrStream);
+ tools::PolyPolygon aPolyPolygon;
+ ReadPolyPolygon(mrStream, aPolyPolygon); // Version 1
+
+ if (aCompat.GetVersion() < 2) // Version 2
+ {
+ pAction->SetPolyPolygon(aPolyPolygon);
+ return pAction;
+ }
+
+ sal_uInt16 nNumberOfComplexPolygons(0);
+ mrStream.ReadUInt16(nNumberOfComplexPolygons);
+ const size_t nMinRecordSize = sizeof(sal_uInt16);
+ const size_t nMaxRecords = mrStream.remainingSize() / nMinRecordSize;
+ if (nNumberOfComplexPolygons > nMaxRecords)
+ {
+ SAL_WARN("vcl.gdi", "Parsing error: " << nMaxRecords << " max possible entries, but "
+ << nNumberOfComplexPolygons
+ << " claimed, truncating");
+ nNumberOfComplexPolygons = nMaxRecords;
+ }
+ for (sal_uInt16 i = 0; i < nNumberOfComplexPolygons; ++i)
+ {
+ sal_uInt16 nIndex(0);
+ mrStream.ReadUInt16(nIndex);
+ tools::Polygon aPoly;
+ aPoly.Read(mrStream);
+ if (nIndex >= aPolyPolygon.Count())
+ {
+ SAL_WARN("vcl.gdi", "svm contains polygon index " << nIndex
+ << " outside possible range "
+ << aPolyPolygon.Count());
+ continue;
+ }
+ aPolyPolygon.Replace(aPoly, nIndex);
+ }
+
+ pAction->SetPolyPolygon(aPolyPolygon);
+
+ return pAction;
+}
+
+rtl::Reference<MetaAction> SvmReader::TextHandler(const ImplMetaReadData* pData)
+{
+ rtl::Reference<MetaTextAction> pAction(new MetaTextAction);
+
+ VersionCompatRead aCompat(mrStream);
+ TypeSerializer aSerializer(mrStream);
+
+ Point aPoint;
+ aSerializer.readPoint(aPoint);
+ OUString aStr = mrStream.ReadUniOrByteString(pData->meActualCharSet);
+ sal_uInt16 nTmpIndex(0);
+ mrStream.ReadUInt16(nTmpIndex);
+ sal_uInt16 nTmpLen(0);
+ mrStream.ReadUInt16(nTmpLen);
+
+ pAction->SetPoint(aPoint);
+
+ if (aCompat.GetVersion() >= 2) // Version 2
+ aStr = read_uInt16_lenPrefixed_uInt16s_ToOUString(mrStream);
+
+ if (nTmpIndex > aStr.getLength())
+ {
+ SAL_WARN("vcl.gdi", "inconsistent offset");
+ nTmpIndex = aStr.getLength();
+ }
+
+ if (nTmpLen > aStr.getLength() - nTmpIndex)
+ {
+ SAL_WARN("vcl.gdi", "inconsistent len");
+ nTmpLen = aStr.getLength() - nTmpIndex;
+ }
+
+ pAction->SetIndex(nTmpIndex);
+ pAction->SetLen(nTmpLen);
+
+ pAction->SetText(aStr);
+
+ return pAction;
+}
+
+rtl::Reference<MetaAction> SvmReader::TextArrayHandler(const ImplMetaReadData* pData)
+{
+ rtl::Reference<MetaTextArrayAction> pAction(new MetaTextArrayAction);
+
+ std::vector<sal_Int32> aArray;
+
+ VersionCompatRead aCompat(mrStream);
+ TypeSerializer aSerializer(mrStream);
+
+ Point aPoint;
+ aSerializer.readPoint(aPoint);
+ pAction->SetPoint(aPoint);
+
+ OUString aStr = mrStream.ReadUniOrByteString(pData->meActualCharSet);
+ pAction->SetText(aStr);
+
+ sal_uInt16 nTmpIndex(0);
+ mrStream.ReadUInt16(nTmpIndex);
+
+ sal_uInt16 nTmpLen(0);
+ mrStream.ReadUInt16(nTmpLen);
+
+ sal_Int32 nAryLen(0);
+ mrStream.ReadInt32(nAryLen);
+
+ if (nTmpLen > aStr.getLength() - nTmpIndex)
+ {
+ SAL_WARN("vcl.gdi", "inconsistent offset and len");
+ pAction->SetIndex(0);
+ pAction->SetLen(aStr.getLength());
+ return pAction;
+ }
+
+ pAction->SetIndex(nTmpIndex);
+ pAction->SetLen(nTmpLen);
+
+ if (nAryLen)
+ {
+ // #i9762#, #106172# Ensure that DX array is at least mnLen entries long
+ if (nTmpLen >= nAryLen)
+ {
+ try
+ {
+ aArray.resize(nTmpLen);
+ sal_Int32 i;
+ sal_Int32 val(0);
+ for (i = 0; i < nAryLen; i++)
+ {
+ mrStream.ReadInt32(val);
+ aArray[i] = val;
+ }
+ // #106172# setup remainder
+ for (; i < nTmpLen; i++)
+ aArray[i] = 0;
+ }
+ catch (std::bad_alloc&)
+ {
+ }
+ }
+ else
+ {
+ return pAction;
+ }
+ }
+
+ if (aCompat.GetVersion() >= 2) // Version 2
+ {
+ aStr = read_uInt16_lenPrefixed_uInt16s_ToOUString(mrStream);
+ pAction->SetText(aStr);
+
+ if (nTmpLen > aStr.getLength() - nTmpIndex)
+ {
+ SAL_WARN("vcl.gdi", "inconsistent offset and len");
+ pAction->SetIndex(0);
+ pAction->SetLen(aStr.getLength());
+ aArray.clear();
+ }
+ }
+
+ if (!aArray.empty())
+ pAction->SetDXArray(std::move(aArray));
+
+ return pAction;
+}
+
+rtl::Reference<MetaAction> SvmReader::StretchTextHandler(const ImplMetaReadData* pData)
+{
+ rtl::Reference<MetaStretchTextAction> pAction(new MetaStretchTextAction);
+
+ VersionCompatRead aCompat(mrStream);
+ TypeSerializer aSerializer(mrStream);
+
+ Point aPoint;
+ aSerializer.readPoint(aPoint);
+ OUString aStr = mrStream.ReadUniOrByteString(pData->meActualCharSet);
+ sal_uInt32 nTmpWidth(0);
+ mrStream.ReadUInt32(nTmpWidth);
+ sal_uInt16 nTmpIndex(0);
+ mrStream.ReadUInt16(nTmpIndex);
+ sal_uInt16 nTmpLen(0);
+ mrStream.ReadUInt16(nTmpLen);
+
+ pAction->SetPoint(aPoint);
+ pAction->SetWidth(nTmpWidth);
+
+ if (aCompat.GetVersion() >= 2) // Version 2
+ aStr = read_uInt16_lenPrefixed_uInt16s_ToOUString(mrStream);
+
+ if (nTmpIndex > aStr.getLength())
+ {
+ SAL_WARN("vcl.gdi", "inconsistent offset");
+ nTmpIndex = aStr.getLength();
+ }
+
+ if (nTmpLen > aStr.getLength() - nTmpIndex)
+ {
+ SAL_WARN("vcl.gdi", "inconsistent len");
+ nTmpLen = aStr.getLength() - nTmpIndex;
+ }
+
+ pAction->SetIndex(nTmpIndex);
+ pAction->SetLen(nTmpLen);
+
+ pAction->SetText(aStr);
+
+ return pAction;
+}
+
+rtl::Reference<MetaAction> SvmReader::TextRectHandler(const ImplMetaReadData* pData)
+{
+ rtl::Reference<MetaTextRectAction> pAction(new MetaTextRectAction);
+
+ VersionCompatRead aCompat(mrStream);
+ TypeSerializer aSerializer(mrStream);
+
+ tools::Rectangle aRect;
+ aSerializer.readRectangle(aRect);
+ OUString aStr = mrStream.ReadUniOrByteString(pData->meActualCharSet);
+ sal_uInt16 nTmp(0);
+ mrStream.ReadUInt16(nTmp);
+
+ pAction->SetRect(aRect);
+ pAction->SetStyle(static_cast<DrawTextFlags>(nTmp));
+
+ if (aCompat.GetVersion() >= 2) // Version 2
+ aStr = read_uInt16_lenPrefixed_uInt16s_ToOUString(mrStream);
+
+ pAction->SetText(aStr);
+
+ return pAction;
+}
+
+rtl::Reference<MetaAction> SvmReader::TextLineHandler()
+{
+ rtl::Reference<MetaTextLineAction> pAction(new MetaTextLineAction);
+
+ VersionCompatRead aCompat(mrStream);
+ TypeSerializer aSerializer(mrStream);
+
+ Point aPos;
+ aSerializer.readPoint(aPos);
+ sal_Int32 nTempWidth(0);
+ mrStream.ReadInt32(nTempWidth);
+
+ pAction->SetStartPoint(aPos);
+ pAction->SetWidth(nTempWidth);
+
+ sal_uInt32 nTempStrikeout(0);
+ mrStream.ReadUInt32(nTempStrikeout);
+ sal_uInt32 nTempUnderline(0);
+ mrStream.ReadUInt32(nTempUnderline);
+
+ pAction->SetStrikeout(static_cast<FontStrikeout>(nTempStrikeout & SAL_MAX_ENUM));
+ pAction->SetUnderline(static_cast<FontLineStyle>(nTempUnderline & SAL_MAX_ENUM));
+
+ if (aCompat.GetVersion() >= 2)
+ {
+ sal_uInt32 nTempOverline(0);
+ mrStream.ReadUInt32(nTempOverline);
+ pAction->SetOverline(static_cast<FontLineStyle>(nTempOverline & SAL_MAX_ENUM));
+ }
+
+ return pAction;
+}
+
+rtl::Reference<MetaAction> SvmReader::BmpHandler()
+{
+ rtl::Reference<MetaBmpAction> pAction(new MetaBmpAction);
+
+ VersionCompatRead aCompat(mrStream);
+ Bitmap aBmp;
+ ReadDIB(aBmp, mrStream, true);
+ TypeSerializer aSerializer(mrStream);
+ Point aPoint;
+ aSerializer.readPoint(aPoint);
+
+ pAction->SetBitmap(aBmp);
+ pAction->SetPoint(aPoint);
+
+ return pAction;
+}
+
+namespace
+{
+void sanitizeNegativeSizeDimensions(Size& rSize)
+{
+ if (rSize.Width() < 0)
+ {
+ SAL_WARN("vcl.gdi", "sanitizeNegativeSizeDimensions: negative width");
+ rSize.setWidth(0);
+ }
+
+ if (rSize.Height() < 0)
+ {
+ SAL_WARN("vcl.gdi", "sanitizeNegativeSizeDimensions: negative height");
+ rSize.setHeight(0);
+ }
+}
+}
+
+rtl::Reference<MetaAction> SvmReader::BmpScaleHandler()
+{
+ rtl::Reference<MetaBmpScaleAction> pAction(new MetaBmpScaleAction);
+
+ VersionCompatRead aCompat(mrStream);
+ Bitmap aBmp;
+ ReadDIB(aBmp, mrStream, true);
+ TypeSerializer aSerializer(mrStream);
+ Point aPoint;
+ aSerializer.readPoint(aPoint);
+
+ Size aSz;
+ aSerializer.readSize(aSz);
+ sanitizeNegativeSizeDimensions(aSz);
+
+ pAction->SetBitmap(aBmp);
+ pAction->SetPoint(aPoint);
+ pAction->SetSize(aSz);
+
+ return pAction;
+}
+
+rtl::Reference<MetaAction> SvmReader::BmpScalePartHandler()
+{
+ rtl::Reference<MetaBmpScalePartAction> pAction(new MetaBmpScalePartAction);
+
+ VersionCompatRead aCompat(mrStream);
+ Bitmap aBmp;
+ ReadDIB(aBmp, mrStream, true);
+ TypeSerializer aSerializer(mrStream);
+ Point aDestPoint;
+ aSerializer.readPoint(aDestPoint);
+ Size aDestSize;
+ aSerializer.readSize(aDestSize);
+ Point aSrcPoint;
+ aSerializer.readPoint(aSrcPoint);
+ Size aSrcSize;
+ aSerializer.readSize(aSrcSize);
+
+ pAction->SetBitmap(aBmp);
+ pAction->SetDestPoint(aDestPoint);
+ pAction->SetDestSize(aDestSize);
+ pAction->SetSrcPoint(aSrcPoint);
+ pAction->SetSrcSize(aSrcSize);
+
+ return pAction;
+}
+
+rtl::Reference<MetaAction> SvmReader::BmpExHandler()
+{
+ rtl::Reference<MetaBmpExAction> pAction(new MetaBmpExAction);
+
+ VersionCompatRead aCompat(mrStream);
+ BitmapEx aBmpEx;
+ ReadDIBBitmapEx(aBmpEx, mrStream);
+ TypeSerializer aSerializer(mrStream);
+ Point aPoint;
+ aSerializer.readPoint(aPoint);
+
+ pAction->SetPoint(aPoint);
+ pAction->SetBitmapEx(aBmpEx);
+
+ return pAction;
+}
+
+rtl::Reference<MetaAction> SvmReader::BmpExScaleHandler()
+{
+ rtl::Reference<MetaBmpExScaleAction> pAction(new MetaBmpExScaleAction);
+
+ VersionCompatRead aCompat(mrStream);
+ BitmapEx aBmpEx;
+ ReadDIBBitmapEx(aBmpEx, mrStream);
+ TypeSerializer aSerializer(mrStream);
+ Point aPoint;
+ aSerializer.readPoint(aPoint);
+
+ Size aSize;
+ aSerializer.readSize(aSize);
+ sanitizeNegativeSizeDimensions(aSize);
+
+ pAction->SetBitmapEx(aBmpEx);
+ pAction->SetPoint(aPoint);
+ pAction->SetSize(aSize);
+
+ return pAction;
+}
+
+rtl::Reference<MetaAction> SvmReader::BmpExScalePartHandler()
+{
+ rtl::Reference<MetaBmpExScalePartAction> pAction(new MetaBmpExScalePartAction);
+
+ VersionCompatRead aCompat(mrStream);
+ BitmapEx aBmpEx;
+ ReadDIBBitmapEx(aBmpEx, mrStream);
+ TypeSerializer aSerializer(mrStream);
+ Point aDstPoint;
+ aSerializer.readPoint(aDstPoint);
+ Size aDstSize;
+ aSerializer.readSize(aDstSize);
+ Point aSrcPoint;
+ aSerializer.readPoint(aSrcPoint);
+ Size aSrcSize;
+ aSerializer.readSize(aSrcSize);
+
+ pAction->SetBitmapEx(aBmpEx);
+ pAction->SetDestPoint(aDstPoint);
+ pAction->SetDestSize(aDstSize);
+ pAction->SetSrcPoint(aSrcPoint);
+ pAction->SetSrcSize(aSrcSize);
+
+ return pAction;
+}
+
+rtl::Reference<MetaAction> SvmReader::MaskHandler()
+{
+ rtl::Reference<MetaMaskAction> pAction(new MetaMaskAction);
+
+ VersionCompatRead aCompat(mrStream);
+ Bitmap aBmp;
+ ReadDIB(aBmp, mrStream, true);
+ TypeSerializer aSerializer(mrStream);
+ Point aPoint;
+ aSerializer.readPoint(aPoint);
+
+ pAction->SetBitmap(aBmp);
+ pAction->SetPoint(aPoint);
+
+ return pAction;
+}
+
+rtl::Reference<MetaAction> SvmReader::MaskScaleHandler()
+{
+ rtl::Reference<MetaMaskScaleAction> pAction(new MetaMaskScaleAction);
+
+ VersionCompatRead aCompat(mrStream);
+ Bitmap aBmp;
+ ReadDIB(aBmp, mrStream, true);
+ TypeSerializer aSerializer(mrStream);
+ Point aPoint;
+ aSerializer.readPoint(aPoint);
+ Size aSize;
+ aSerializer.readSize(aSize);
+
+ pAction->SetBitmap(aBmp);
+ pAction->SetPoint(aPoint);
+ pAction->SetSize(aSize);
+
+ return pAction;
+}
+
+rtl::Reference<MetaAction> SvmReader::MaskScalePartHandler()
+{
+ rtl::Reference<MetaMaskScalePartAction> pAction(new MetaMaskScalePartAction);
+
+ VersionCompatRead aCompat(mrStream);
+ Bitmap aBmp;
+ ReadDIB(aBmp, mrStream, true);
+ Color aColor;
+ ReadColor(aColor);
+ TypeSerializer aSerializer(mrStream);
+ Point aDstPt;
+ aSerializer.readPoint(aDstPt);
+ Size aDstSz;
+ aSerializer.readSize(aDstSz);
+ Point aSrcPt;
+ aSerializer.readPoint(aSrcPt);
+ Size aSrcSz;
+ aSerializer.readSize(aSrcSz);
+
+ pAction->SetBitmap(aBmp);
+ pAction->SetColor(aColor);
+ pAction->SetDestPoint(aDstPt);
+ pAction->SetDestSize(aDstSz);
+ pAction->SetSrcPoint(aSrcPt);
+ pAction->SetSrcSize(aSrcSz);
+
+ return pAction;
+}
+
+rtl::Reference<MetaAction> SvmReader::GradientHandler()
+{
+ rtl::Reference<MetaGradientAction> pAction(new MetaGradientAction);
+
+ VersionCompatRead aCompat(mrStream);
+ TypeSerializer aSerializer(mrStream);
+
+ tools::Rectangle aRect;
+ aSerializer.readRectangle(aRect);
+ Gradient aGradient;
+ aSerializer.readGradient(aGradient);
+
+ pAction->SetRect(aRect);
+ pAction->SetGradient(aGradient);
+
+ return pAction;
+}
+
+rtl::Reference<MetaAction> SvmReader::GradientExHandler()
+{
+ rtl::Reference<MetaGradientExAction> pAction(new MetaGradientExAction);
+
+ VersionCompatRead aCompat(mrStream);
+ tools::PolyPolygon aPolyPoly;
+ ReadPolyPolygon(mrStream, aPolyPoly);
+ TypeSerializer aSerializer(mrStream);
+ Gradient aGradient;
+ aSerializer.readGradient(aGradient);
+
+ pAction->SetGradient(aGradient);
+ pAction->SetPolyPolygon(aPolyPoly);
+
+ return pAction;
+}
+
+rtl::Reference<MetaAction> SvmReader::HatchHandler()
+{
+ rtl::Reference<MetaHatchAction> pAction(new MetaHatchAction);
+
+ VersionCompatRead aCompat(mrStream);
+ tools::PolyPolygon aPolyPoly;
+ ReadPolyPolygon(mrStream, aPolyPoly);
+ Hatch aHatch;
+ ReadHatch(mrStream, aHatch);
+
+ pAction->SetPolyPolygon(aPolyPoly);
+ pAction->SetHatch(aHatch);
+
+ return pAction;
+}
+
+rtl::Reference<MetaAction> SvmReader::WallpaperHandler()
+{
+ rtl::Reference<MetaWallpaperAction> pAction(new MetaWallpaperAction);
+
+ VersionCompatRead aCompat(mrStream);
+ Wallpaper aWallpaper;
+ ReadWallpaper(mrStream, aWallpaper);
+
+ pAction->SetWallpaper(aWallpaper);
+
+ return pAction;
+}
+
+rtl::Reference<MetaAction> SvmReader::ClipRegionHandler()
+{
+ rtl::Reference<MetaClipRegionAction> pAction(new MetaClipRegionAction);
+
+ VersionCompatRead aCompat(mrStream);
+ vcl::Region aRegion;
+ ReadRegion(mrStream, aRegion);
+ bool aClip(false);
+ mrStream.ReadCharAsBool(aClip);
+
+ pAction->SetRegion(aRegion);
+ pAction->SetClipping(aClip);
+
+ return pAction;
+}
+
+rtl::Reference<MetaAction> SvmReader::ISectRectClipRegionHandler()
+{
+ rtl::Reference<MetaISectRectClipRegionAction> pAction(new MetaISectRectClipRegionAction);
+
+ VersionCompatRead aCompat(mrStream);
+ TypeSerializer aSerializer(mrStream);
+ tools::Rectangle aRect;
+ aSerializer.readRectangle(aRect);
+
+ pAction->SetRect(aRect);
+
+ return pAction;
+}
+
+rtl::Reference<MetaAction> SvmReader::ISectRegionClipRegionHandler()
+{
+ rtl::Reference<MetaISectRegionClipRegionAction> pAction(new MetaISectRegionClipRegionAction);
+
+ VersionCompatRead aCompat(mrStream);
+ vcl::Region aRegion;
+ ReadRegion(mrStream, aRegion);
+ pAction->SetRegion(aRegion);
+
+ return pAction;
+}
+
+rtl::Reference<MetaAction> SvmReader::MoveClipRegionHandler()
+{
+ rtl::Reference<MetaMoveClipRegionAction> pAction(new MetaMoveClipRegionAction);
+
+ VersionCompatRead aCompat(mrStream);
+ sal_Int32 nTmpHM(0), nTmpVM(0);
+ mrStream.ReadInt32(nTmpHM).ReadInt32(nTmpVM);
+
+ pAction->SetHorzMove(nTmpHM);
+ pAction->SetVertMove(nTmpVM);
+
+ return pAction;
+}
+
+rtl::Reference<MetaAction> SvmReader::TextColorHandler()
+{
+ rtl::Reference<MetaTextColorAction> pAction(new MetaTextColorAction);
+
+ VersionCompatRead aCompat(mrStream);
+ Color aColor;
+ ReadColor(aColor);
+
+ pAction->SetColor(aColor);
+
+ return pAction;
+}
+
+rtl::Reference<MetaAction> SvmReader::TextFillColorHandler()
+{
+ rtl::Reference<MetaTextFillColorAction> pAction(new MetaTextFillColorAction);
+
+ VersionCompatRead aCompat(mrStream);
+ Color aColor;
+ ReadColor(aColor);
+ bool bSet(false);
+ mrStream.ReadCharAsBool(bSet);
+
+ pAction->SetColor(aColor);
+ pAction->SetSetting(bSet);
+
+ return pAction;
+}
+
+rtl::Reference<MetaAction> SvmReader::TextLineColorHandler()
+{
+ rtl::Reference<MetaTextLineColorAction> pAction(new MetaTextLineColorAction);
+
+ VersionCompatRead aCompat(mrStream);
+ Color aColor;
+ ReadColor(aColor);
+ bool bSet(false);
+ mrStream.ReadCharAsBool(bSet);
+
+ pAction->SetColor(aColor);
+ pAction->SetSetting(bSet);
+
+ return pAction;
+}
+
+rtl::Reference<MetaAction> SvmReader::OverlineColorHandler()
+{
+ rtl::Reference<MetaOverlineColorAction> pAction(new MetaOverlineColorAction);
+
+ VersionCompatRead aCompat(mrStream);
+ Color aColor;
+ ReadColor(aColor);
+ bool bSet(false);
+ mrStream.ReadCharAsBool(bSet);
+
+ pAction->SetColor(aColor);
+ pAction->SetSetting(bSet);
+
+ return pAction;
+}
+
+rtl::Reference<MetaAction> SvmReader::TextAlignHandler()
+{
+ rtl::Reference<MetaTextAlignAction> pAction(new MetaTextAlignAction);
+
+ VersionCompatRead aCompat(mrStream);
+ sal_uInt16 nTmp16(0);
+ mrStream.ReadUInt16(nTmp16);
+
+ pAction->SetTextAlign(static_cast<TextAlign>(nTmp16));
+
+ return pAction;
+}
+
+rtl::Reference<MetaAction> SvmReader::MapModeHandler()
+{
+ rtl::Reference<MetaMapModeAction> pAction(new MetaMapModeAction);
+
+ VersionCompatRead aCompat(mrStream);
+ TypeSerializer aSerializer(mrStream);
+ MapMode aMapMode;
+ aSerializer.readMapMode(aMapMode);
+
+ pAction->SetMapMode(aMapMode);
+
+ return pAction;
+}
+
+rtl::Reference<MetaAction> SvmReader::FontHandler(ImplMetaReadData* pData)
+{
+ rtl::Reference<MetaFontAction> pAction(new MetaFontAction);
+
+ VersionCompatRead aCompat(mrStream);
+ vcl::Font aFont;
+ ReadFont(mrStream, aFont);
+ pData->meActualCharSet = aFont.GetCharSet();
+ if (pData->meActualCharSet == RTL_TEXTENCODING_DONTKNOW)
+ pData->meActualCharSet = osl_getThreadTextEncoding();
+
+ pAction->SetFont(aFont);
+
+ return pAction;
+}
+
+rtl::Reference<MetaAction> SvmReader::PushHandler()
+{
+ rtl::Reference<MetaPushAction> pAction(new MetaPushAction);
+
+ VersionCompatRead aCompat(mrStream);
+ sal_uInt16 nTmp(0);
+ mrStream.ReadUInt16(nTmp);
+
+ pAction->SetPushFlags(static_cast<vcl::PushFlags>(nTmp));
+
+ return pAction;
+}
+
+rtl::Reference<MetaAction> SvmReader::PopHandler()
+{
+ rtl::Reference<MetaPopAction> pAction(new MetaPopAction);
+
+ VersionCompatRead aCompat(mrStream);
+
+ return pAction;
+}
+
+rtl::Reference<MetaAction> SvmReader::RasterOpHandler()
+{
+ rtl::Reference<MetaRasterOpAction> pAction(new MetaRasterOpAction);
+
+ sal_uInt16 nTmp16(0);
+
+ VersionCompatRead aCompat(mrStream);
+ mrStream.ReadUInt16(nTmp16);
+
+ pAction->SetRasterOp(static_cast<RasterOp>(nTmp16));
+
+ return pAction;
+}
+
+rtl::Reference<MetaAction> SvmReader::TransparentHandler()
+{
+ rtl::Reference<MetaTransparentAction> pAction(new MetaTransparentAction);
+
+ VersionCompatRead aCompat(mrStream);
+ tools::PolyPolygon aPolyPoly;
+ ReadPolyPolygon(mrStream, aPolyPoly);
+ sal_uInt16 nTransPercent(0);
+ mrStream.ReadUInt16(nTransPercent);
+
+ pAction->SetPolyPolygon(aPolyPoly);
+ pAction->SetTransparence(nTransPercent);
+
+ return pAction;
+}
+
+rtl::Reference<MetaAction> SvmReader::FloatTransparentHandler(ImplMetaReadData* pData)
+{
+ rtl::Reference<MetaFloatTransparentAction> pAction(new MetaFloatTransparentAction);
+
+ VersionCompatRead aCompat(mrStream);
+ GDIMetaFile aMtf;
+ SvmReader aReader(mrStream);
+ aReader.Read(aMtf, pData);
+ TypeSerializer aSerializer(mrStream);
+ Point aPoint;
+ aSerializer.readPoint(aPoint);
+
+ Size aSize;
+ aSerializer.readSize(aSize);
+ sanitizeNegativeSizeDimensions(aSize);
+
+ Gradient aGradient;
+ aSerializer.readGradient(aGradient);
+
+ pAction->SetGDIMetaFile(aMtf);
+ pAction->SetPoint(aPoint);
+ pAction->SetSize(aSize);
+ pAction->SetGradient(aGradient);
+
+ return pAction;
+}
+
+rtl::Reference<MetaAction> SvmReader::EPSHandler()
+{
+ rtl::Reference<MetaEPSAction> pAction(new MetaEPSAction);
+
+ VersionCompatRead aCompat(mrStream);
+ TypeSerializer aSerializer(mrStream);
+ GfxLink aGfxLink;
+ aSerializer.readGfxLink(aGfxLink);
+ Point aPoint;
+ aSerializer.readPoint(aPoint);
+ Size aSize;
+ aSerializer.readSize(aSize);
+ GDIMetaFile aSubst;
+ Read(aSubst);
+
+ pAction->SetLink(aGfxLink);
+ pAction->SetPoint(aPoint);
+ pAction->SetSize(aSize);
+ pAction->SetSubstitute(aSubst);
+
+ return pAction;
+}
+
+rtl::Reference<MetaAction> SvmReader::RefPointHandler()
+{
+ rtl::Reference<MetaRefPointAction> pAction(new MetaRefPointAction);
+
+ VersionCompatRead aCompat(mrStream);
+ TypeSerializer aSerializer(mrStream);
+
+ Point aRefPoint;
+ aSerializer.readPoint(aRefPoint);
+ bool bSet(false);
+ mrStream.ReadCharAsBool(bSet);
+
+ pAction->SetRefPoint(aRefPoint);
+ pAction->SetSetting(bSet);
+
+ return pAction;
+}
+
+rtl::Reference<MetaAction> SvmReader::CommentHandler()
+{
+ rtl::Reference<MetaCommentAction> pAction(new MetaCommentAction);
+
+ VersionCompatRead aCompat(mrStream);
+ OString aComment;
+ aComment = read_uInt16_lenPrefixed_uInt8s_ToOString(mrStream);
+ sal_Int32 nValue(0);
+ sal_uInt32 nDataSize(0);
+ mrStream.ReadInt32(nValue).ReadUInt32(nDataSize);
+
+ if (nDataSize > mrStream.remainingSize())
+ {
+ SAL_WARN("vcl.gdi", "Parsing error: " << mrStream.remainingSize() << " available data, but "
+ << nDataSize << " claimed, truncating");
+ nDataSize = mrStream.remainingSize();
+ }
+
+ SAL_INFO("vcl.gdi", "MetaCommentAction::Read " << aComment);
+
+ std::unique_ptr<sal_uInt8[]> pData;
+ pData.reset();
+
+ if (nDataSize)
+ {
+ pData.reset(new sal_uInt8[nDataSize]);
+ mrStream.ReadBytes(pData.get(), nDataSize);
+ }
+
+ pAction->SetComment(aComment);
+ pAction->SetDataSize(nDataSize);
+ pAction->SetValue(nValue);
+ pAction->SetData(pData.get(), nDataSize);
+
+ return pAction;
+}
+
+rtl::Reference<MetaAction> SvmReader::LayoutModeHandler()
+{
+ rtl::Reference<MetaLayoutModeAction> pAction(new MetaLayoutModeAction);
+
+ VersionCompatRead aCompat(mrStream);
+ sal_uInt32 tmp(0);
+ mrStream.ReadUInt32(tmp);
+
+ pAction->SetLayoutMode(static_cast<vcl::text::ComplexTextLayoutFlags>(tmp));
+
+ return pAction;
+}
+
+rtl::Reference<MetaAction> SvmReader::TextLanguageHandler()
+{
+ rtl::Reference<MetaTextLanguageAction> pAction(new MetaTextLanguageAction);
+
+ VersionCompatRead aCompat(mrStream);
+ sal_uInt16 nTmp = 0;
+ mrStream.ReadUInt16(nTmp);
+
+ pAction->SetTextLanguage(static_cast<LanguageType>(nTmp));
+
+ return pAction;
+}
+
+rtl::Reference<MetaAction> SvmReader::DefaultHandler()
+{
+ return rtl::Reference<MetaAction>(new MetaAction);
+}
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/vcl/source/filter/svm/SvmWriter.cxx b/vcl/source/filter/svm/SvmWriter.cxx
new file mode 100644
index 000000000..118db1325
--- /dev/null
+++ b/vcl/source/filter/svm/SvmWriter.cxx
@@ -0,0 +1,1421 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <vcl/filter/SvmWriter.hxx>
+#include <vcl/TypeSerializer.hxx>
+#include <vcl/dibtools.hxx>
+
+#include <tools/vcompat.hxx>
+
+#include <osl/thread.h>
+
+SvmWriter::SvmWriter(SvStream& rIStm)
+ : mrStream(rIStm)
+{
+}
+
+void SvmWriter::WriteColor(::Color aColor)
+{
+ mrStream.WriteUInt32(static_cast<sal_uInt32>(aColor));
+}
+
+SvStream& SvmWriter::Write(const GDIMetaFile& rMetaFile)
+{
+ const SvStreamCompressFlags nStmCompressMode = mrStream.GetCompressMode();
+ SvStreamEndian nOldFormat = mrStream.GetEndian();
+
+ mrStream.SetEndian(SvStreamEndian::LITTLE);
+ mrStream.WriteBytes("VCLMTF", 6);
+
+ {
+ VersionCompatWrite aCompat(mrStream, 1);
+
+ mrStream.WriteUInt32(static_cast<sal_uInt32>(nStmCompressMode));
+ TypeSerializer aSerializer(mrStream);
+ aSerializer.writeMapMode(rMetaFile.GetPrefMapMode());
+ aSerializer.writeSize(rMetaFile.GetPrefSize());
+ mrStream.WriteUInt32(rMetaFile.GetActionSize());
+ } // VersionCompatWrite dtor writes stuff into the header
+
+ ImplMetaWriteData aWriteData;
+
+ aWriteData.meActualCharSet = mrStream.GetStreamCharSet();
+
+ MetaAction* pAct = const_cast<GDIMetaFile&>(rMetaFile).FirstAction();
+ while (pAct)
+ {
+ MetaActionHandler(pAct, &aWriteData);
+ pAct = const_cast<GDIMetaFile&>(rMetaFile).NextAction();
+ }
+
+ mrStream.SetEndian(nOldFormat);
+
+ return mrStream;
+}
+
+BitmapChecksum SvmWriter::GetChecksum(const GDIMetaFile& rMetaFile)
+{
+ SvMemoryStream aMemStm(65535, 65535);
+ ImplMetaWriteData aWriteData;
+ SVBT16 aBT16;
+ SVBT32 aBT32;
+ BitmapChecksumOctetArray aBCOA;
+ BitmapChecksum nCrc = 0;
+
+ aWriteData.meActualCharSet = aMemStm.GetStreamCharSet();
+
+ for (size_t i = 0, nObjCount = rMetaFile.GetActionSize(); i < nObjCount; i++)
+ {
+ MetaAction* pAction = rMetaFile.GetAction(i);
+
+ switch (pAction->GetType())
+ {
+ case MetaActionType::BMP:
+ {
+ MetaBmpAction* pAct = static_cast<MetaBmpAction*>(pAction);
+
+ ShortToSVBT16(static_cast<sal_uInt16>(pAct->GetType()), aBT16);
+ nCrc = vcl_get_checksum(nCrc, aBT16, 2);
+
+ BCToBCOA(pAct->GetBitmap().GetChecksum(), aBCOA);
+ nCrc = vcl_get_checksum(nCrc, aBCOA, BITMAP_CHECKSUM_SIZE);
+
+ Int32ToSVBT32(pAct->GetPoint().X(), aBT32);
+ nCrc = vcl_get_checksum(nCrc, aBT32, 4);
+
+ Int32ToSVBT32(pAct->GetPoint().Y(), aBT32);
+ nCrc = vcl_get_checksum(nCrc, aBT32, 4);
+ }
+ break;
+
+ case MetaActionType::BMPSCALE:
+ {
+ MetaBmpScaleAction* pAct = static_cast<MetaBmpScaleAction*>(pAction);
+
+ ShortToSVBT16(static_cast<sal_uInt16>(pAct->GetType()), aBT16);
+ nCrc = vcl_get_checksum(nCrc, aBT16, 2);
+
+ BCToBCOA(pAct->GetBitmap().GetChecksum(), aBCOA);
+ nCrc = vcl_get_checksum(nCrc, aBCOA, BITMAP_CHECKSUM_SIZE);
+
+ Int32ToSVBT32(pAct->GetPoint().X(), aBT32);
+ nCrc = vcl_get_checksum(nCrc, aBT32, 4);
+
+ Int32ToSVBT32(pAct->GetPoint().Y(), aBT32);
+ nCrc = vcl_get_checksum(nCrc, aBT32, 4);
+
+ Int32ToSVBT32(pAct->GetSize().Width(), aBT32);
+ nCrc = vcl_get_checksum(nCrc, aBT32, 4);
+
+ Int32ToSVBT32(pAct->GetSize().Height(), aBT32);
+ nCrc = vcl_get_checksum(nCrc, aBT32, 4);
+ }
+ break;
+
+ case MetaActionType::BMPSCALEPART:
+ {
+ MetaBmpScalePartAction* pAct = static_cast<MetaBmpScalePartAction*>(pAction);
+
+ ShortToSVBT16(static_cast<sal_uInt16>(pAct->GetType()), aBT16);
+ nCrc = vcl_get_checksum(nCrc, aBT16, 2);
+
+ BCToBCOA(pAct->GetBitmap().GetChecksum(), aBCOA);
+ nCrc = vcl_get_checksum(nCrc, aBCOA, BITMAP_CHECKSUM_SIZE);
+
+ Int32ToSVBT32(pAct->GetDestPoint().X(), aBT32);
+ nCrc = vcl_get_checksum(nCrc, aBT32, 4);
+
+ Int32ToSVBT32(pAct->GetDestPoint().Y(), aBT32);
+ nCrc = vcl_get_checksum(nCrc, aBT32, 4);
+
+ Int32ToSVBT32(pAct->GetDestSize().Width(), aBT32);
+ nCrc = vcl_get_checksum(nCrc, aBT32, 4);
+
+ Int32ToSVBT32(pAct->GetDestSize().Height(), aBT32);
+ nCrc = vcl_get_checksum(nCrc, aBT32, 4);
+
+ Int32ToSVBT32(pAct->GetSrcPoint().X(), aBT32);
+ nCrc = vcl_get_checksum(nCrc, aBT32, 4);
+
+ Int32ToSVBT32(pAct->GetSrcPoint().Y(), aBT32);
+ nCrc = vcl_get_checksum(nCrc, aBT32, 4);
+
+ Int32ToSVBT32(pAct->GetSrcSize().Width(), aBT32);
+ nCrc = vcl_get_checksum(nCrc, aBT32, 4);
+
+ Int32ToSVBT32(pAct->GetSrcSize().Height(), aBT32);
+ nCrc = vcl_get_checksum(nCrc, aBT32, 4);
+ }
+ break;
+
+ case MetaActionType::BMPEX:
+ {
+ MetaBmpExAction* pAct = static_cast<MetaBmpExAction*>(pAction);
+
+ ShortToSVBT16(static_cast<sal_uInt16>(pAct->GetType()), aBT16);
+ nCrc = vcl_get_checksum(nCrc, aBT16, 2);
+
+ BCToBCOA(pAct->GetBitmapEx().GetChecksum(), aBCOA);
+ nCrc = vcl_get_checksum(nCrc, aBCOA, BITMAP_CHECKSUM_SIZE);
+
+ Int32ToSVBT32(pAct->GetPoint().X(), aBT32);
+ nCrc = vcl_get_checksum(nCrc, aBT32, 4);
+
+ Int32ToSVBT32(pAct->GetPoint().Y(), aBT32);
+ nCrc = vcl_get_checksum(nCrc, aBT32, 4);
+ }
+ break;
+
+ case MetaActionType::BMPEXSCALE:
+ {
+ MetaBmpExScaleAction* pAct = static_cast<MetaBmpExScaleAction*>(pAction);
+
+ ShortToSVBT16(static_cast<sal_uInt16>(pAct->GetType()), aBT16);
+ nCrc = vcl_get_checksum(nCrc, aBT16, 2);
+
+ BCToBCOA(pAct->GetBitmapEx().GetChecksum(), aBCOA);
+ nCrc = vcl_get_checksum(nCrc, aBCOA, BITMAP_CHECKSUM_SIZE);
+
+ Int32ToSVBT32(pAct->GetPoint().X(), aBT32);
+ nCrc = vcl_get_checksum(nCrc, aBT32, 4);
+
+ Int32ToSVBT32(pAct->GetPoint().Y(), aBT32);
+ nCrc = vcl_get_checksum(nCrc, aBT32, 4);
+
+ Int32ToSVBT32(pAct->GetSize().Width(), aBT32);
+ nCrc = vcl_get_checksum(nCrc, aBT32, 4);
+
+ Int32ToSVBT32(pAct->GetSize().Height(), aBT32);
+ nCrc = vcl_get_checksum(nCrc, aBT32, 4);
+ }
+ break;
+
+ case MetaActionType::BMPEXSCALEPART:
+ {
+ MetaBmpExScalePartAction* pAct = static_cast<MetaBmpExScalePartAction*>(pAction);
+
+ ShortToSVBT16(static_cast<sal_uInt16>(pAct->GetType()), aBT16);
+ nCrc = vcl_get_checksum(nCrc, aBT16, 2);
+
+ BCToBCOA(pAct->GetBitmapEx().GetChecksum(), aBCOA);
+ nCrc = vcl_get_checksum(nCrc, aBCOA, BITMAP_CHECKSUM_SIZE);
+
+ Int32ToSVBT32(pAct->GetDestPoint().X(), aBT32);
+ nCrc = vcl_get_checksum(nCrc, aBT32, 4);
+
+ Int32ToSVBT32(pAct->GetDestPoint().Y(), aBT32);
+ nCrc = vcl_get_checksum(nCrc, aBT32, 4);
+
+ Int32ToSVBT32(pAct->GetDestSize().Width(), aBT32);
+ nCrc = vcl_get_checksum(nCrc, aBT32, 4);
+
+ Int32ToSVBT32(pAct->GetDestSize().Height(), aBT32);
+ nCrc = vcl_get_checksum(nCrc, aBT32, 4);
+
+ Int32ToSVBT32(pAct->GetSrcPoint().X(), aBT32);
+ nCrc = vcl_get_checksum(nCrc, aBT32, 4);
+
+ Int32ToSVBT32(pAct->GetSrcPoint().Y(), aBT32);
+ nCrc = vcl_get_checksum(nCrc, aBT32, 4);
+
+ Int32ToSVBT32(pAct->GetSrcSize().Width(), aBT32);
+ nCrc = vcl_get_checksum(nCrc, aBT32, 4);
+
+ Int32ToSVBT32(pAct->GetSrcSize().Height(), aBT32);
+ nCrc = vcl_get_checksum(nCrc, aBT32, 4);
+ }
+ break;
+
+ case MetaActionType::MASK:
+ {
+ MetaMaskAction* pAct = static_cast<MetaMaskAction*>(pAction);
+
+ ShortToSVBT16(static_cast<sal_uInt16>(pAct->GetType()), aBT16);
+ nCrc = vcl_get_checksum(nCrc, aBT16, 2);
+
+ BCToBCOA(pAct->GetBitmap().GetChecksum(), aBCOA);
+ nCrc = vcl_get_checksum(nCrc, aBCOA, BITMAP_CHECKSUM_SIZE);
+
+ UInt32ToSVBT32(sal_uInt32(pAct->GetColor()), aBT32);
+ nCrc = vcl_get_checksum(nCrc, aBT32, 4);
+
+ Int32ToSVBT32(pAct->GetPoint().X(), aBT32);
+ nCrc = vcl_get_checksum(nCrc, aBT32, 4);
+
+ Int32ToSVBT32(pAct->GetPoint().Y(), aBT32);
+ nCrc = vcl_get_checksum(nCrc, aBT32, 4);
+ }
+ break;
+
+ case MetaActionType::MASKSCALE:
+ {
+ MetaMaskScaleAction* pAct = static_cast<MetaMaskScaleAction*>(pAction);
+
+ ShortToSVBT16(static_cast<sal_uInt16>(pAct->GetType()), aBT16);
+ nCrc = vcl_get_checksum(nCrc, aBT16, 2);
+
+ BCToBCOA(pAct->GetBitmap().GetChecksum(), aBCOA);
+ nCrc = vcl_get_checksum(nCrc, aBCOA, BITMAP_CHECKSUM_SIZE);
+
+ UInt32ToSVBT32(sal_uInt32(pAct->GetColor()), aBT32);
+ nCrc = vcl_get_checksum(nCrc, aBT32, 4);
+
+ Int32ToSVBT32(pAct->GetPoint().X(), aBT32);
+ nCrc = vcl_get_checksum(nCrc, aBT32, 4);
+
+ Int32ToSVBT32(pAct->GetPoint().Y(), aBT32);
+ nCrc = vcl_get_checksum(nCrc, aBT32, 4);
+
+ Int32ToSVBT32(pAct->GetSize().Width(), aBT32);
+ nCrc = vcl_get_checksum(nCrc, aBT32, 4);
+
+ Int32ToSVBT32(pAct->GetSize().Height(), aBT32);
+ nCrc = vcl_get_checksum(nCrc, aBT32, 4);
+ }
+ break;
+
+ case MetaActionType::MASKSCALEPART:
+ {
+ MetaMaskScalePartAction* pAct = static_cast<MetaMaskScalePartAction*>(pAction);
+
+ ShortToSVBT16(static_cast<sal_uInt16>(pAct->GetType()), aBT16);
+ nCrc = vcl_get_checksum(nCrc, aBT16, 2);
+
+ BCToBCOA(pAct->GetBitmap().GetChecksum(), aBCOA);
+ nCrc = vcl_get_checksum(nCrc, aBCOA, BITMAP_CHECKSUM_SIZE);
+
+ UInt32ToSVBT32(sal_uInt32(pAct->GetColor()), aBT32);
+ nCrc = vcl_get_checksum(nCrc, aBT32, 4);
+
+ Int32ToSVBT32(pAct->GetDestPoint().X(), aBT32);
+ nCrc = vcl_get_checksum(nCrc, aBT32, 4);
+
+ Int32ToSVBT32(pAct->GetDestPoint().Y(), aBT32);
+ nCrc = vcl_get_checksum(nCrc, aBT32, 4);
+
+ Int32ToSVBT32(pAct->GetDestSize().Width(), aBT32);
+ nCrc = vcl_get_checksum(nCrc, aBT32, 4);
+
+ Int32ToSVBT32(pAct->GetDestSize().Height(), aBT32);
+ nCrc = vcl_get_checksum(nCrc, aBT32, 4);
+
+ Int32ToSVBT32(pAct->GetSrcPoint().X(), aBT32);
+ nCrc = vcl_get_checksum(nCrc, aBT32, 4);
+
+ Int32ToSVBT32(pAct->GetSrcPoint().Y(), aBT32);
+ nCrc = vcl_get_checksum(nCrc, aBT32, 4);
+
+ Int32ToSVBT32(pAct->GetSrcSize().Width(), aBT32);
+ nCrc = vcl_get_checksum(nCrc, aBT32, 4);
+
+ Int32ToSVBT32(pAct->GetSrcSize().Height(), aBT32);
+ nCrc = vcl_get_checksum(nCrc, aBT32, 4);
+ }
+ break;
+
+ case MetaActionType::EPS:
+ {
+ MetaEPSAction* pAct = static_cast<MetaEPSAction*>(pAction);
+ nCrc = vcl_get_checksum(nCrc, pAct->GetLink().GetData(),
+ pAct->GetLink().GetDataSize());
+ }
+ break;
+
+ case MetaActionType::CLIPREGION:
+ {
+ MetaClipRegionAction& rAct = static_cast<MetaClipRegionAction&>(*pAction);
+ const vcl::Region& rRegion = rAct.GetRegion();
+
+ if (rRegion.HasPolyPolygonOrB2DPolyPolygon())
+ {
+ // It has shown that this is a possible bottleneck for checksum calculation.
+ // In worst case a very expensive RegionHandle representation gets created.
+ // In this case it's cheaper to use the PolyPolygon
+ const basegfx::B2DPolyPolygon aPolyPolygon(rRegion.GetAsB2DPolyPolygon());
+ SVBT64 aSVBT64;
+
+ for (auto const& rPolygon : aPolyPolygon)
+ {
+ const sal_uInt32 nPointCount(rPolygon.count());
+ const bool bControl(rPolygon.areControlPointsUsed());
+
+ for (sal_uInt32 b(0); b < nPointCount; b++)
+ {
+ const basegfx::B2DPoint aPoint(rPolygon.getB2DPoint(b));
+
+ DoubleToSVBT64(aPoint.getX(), aSVBT64);
+ nCrc = vcl_get_checksum(nCrc, aSVBT64, 8);
+ DoubleToSVBT64(aPoint.getY(), aSVBT64);
+ nCrc = vcl_get_checksum(nCrc, aSVBT64, 8);
+
+ if (bControl)
+ {
+ if (rPolygon.isPrevControlPointUsed(b))
+ {
+ const basegfx::B2DPoint aCtrl(rPolygon.getPrevControlPoint(b));
+
+ DoubleToSVBT64(aCtrl.getX(), aSVBT64);
+ nCrc = vcl_get_checksum(nCrc, aSVBT64, 8);
+ DoubleToSVBT64(aCtrl.getY(), aSVBT64);
+ nCrc = vcl_get_checksum(nCrc, aSVBT64, 8);
+ }
+
+ if (rPolygon.isNextControlPointUsed(b))
+ {
+ const basegfx::B2DPoint aCtrl(rPolygon.getNextControlPoint(b));
+
+ DoubleToSVBT64(aCtrl.getX(), aSVBT64);
+ nCrc = vcl_get_checksum(nCrc, aSVBT64, 8);
+ DoubleToSVBT64(aCtrl.getY(), aSVBT64);
+ nCrc = vcl_get_checksum(nCrc, aSVBT64, 8);
+ }
+ }
+ }
+ }
+
+ sal_uInt8 tmp = static_cast<sal_uInt8>(rAct.IsClipping());
+ nCrc = vcl_get_checksum(nCrc, &tmp, 1);
+ }
+ else
+ {
+ SvmWriter aWriter(aMemStm);
+ aWriter.MetaActionHandler(pAction, &aWriteData);
+ nCrc = vcl_get_checksum(nCrc, aMemStm.GetData(), aMemStm.Tell());
+ aMemStm.Seek(0);
+ }
+ }
+ break;
+
+ default:
+ {
+ SvmWriter aWriter(aMemStm);
+ aWriter.MetaActionHandler(pAction, &aWriteData);
+ nCrc = vcl_get_checksum(nCrc, aMemStm.GetData(), aMemStm.Tell());
+ aMemStm.Seek(0);
+ }
+ break;
+ }
+ }
+
+ return nCrc;
+}
+
+void SvmWriter::MetaActionHandler(MetaAction* pAction, ImplMetaWriteData* pData)
+{
+ MetaActionType nType = pAction->GetType();
+
+ switch (nType)
+ {
+ case MetaActionType::NONE:
+ {
+ ActionHandler(pAction);
+ }
+ break;
+
+ case MetaActionType::PIXEL:
+ {
+ auto* pMetaAction = static_cast<MetaPixelAction*>(pAction);
+ PixelHandler(pMetaAction);
+ }
+ break;
+
+ case MetaActionType::POINT:
+ {
+ auto pMetaAction = static_cast<MetaPointAction*>(pAction);
+ PointHandler(pMetaAction);
+ }
+ break;
+
+ case MetaActionType::LINE:
+ {
+ auto* pMetaAction = static_cast<MetaLineAction*>(pAction);
+ LineHandler(pMetaAction);
+ }
+ break;
+
+ case MetaActionType::RECT:
+ {
+ auto* pMetaAction = static_cast<MetaRectAction*>(pAction);
+ RectHandler(pMetaAction);
+ }
+ break;
+
+ case MetaActionType::ROUNDRECT:
+ {
+ auto* pMetaAction = static_cast<MetaRoundRectAction*>(pAction);
+ RoundRectHandler(pMetaAction);
+ }
+ break;
+
+ case MetaActionType::ELLIPSE:
+ {
+ auto* pMetaAction = static_cast<MetaEllipseAction*>(pAction);
+ EllipseHandler(pMetaAction);
+ }
+ break;
+
+ case MetaActionType::ARC:
+ {
+ auto* pMetaAction = static_cast<MetaArcAction*>(pAction);
+ ArcHandler(pMetaAction);
+ }
+ break;
+
+ case MetaActionType::PIE:
+ {
+ auto* pMetaAction = static_cast<MetaPieAction*>(pAction);
+ PieHandler(pMetaAction);
+ }
+ break;
+
+ case MetaActionType::CHORD:
+ {
+ auto* pMetaAction = static_cast<MetaChordAction*>(pAction);
+ ChordHandler(pMetaAction);
+ }
+ break;
+
+ case MetaActionType::POLYLINE:
+ {
+ auto* pMetaAction = static_cast<MetaPolyLineAction*>(pAction);
+ PolyLineHandler(pMetaAction);
+ }
+ break;
+
+ case MetaActionType::POLYGON:
+ {
+ auto* pMetaAction = static_cast<MetaPolygonAction*>(pAction);
+ PolygonHandler(pMetaAction);
+ }
+ break;
+
+ case MetaActionType::POLYPOLYGON:
+ {
+ auto* pMetaAction = static_cast<MetaPolyPolygonAction*>(pAction);
+ PolyPolygonHandler(pMetaAction);
+ }
+ break;
+
+ case MetaActionType::TEXT:
+ {
+ auto* pMetaAction = static_cast<MetaTextAction*>(pAction);
+ TextHandler(pMetaAction, pData);
+ }
+ break;
+
+ case MetaActionType::TEXTARRAY:
+ {
+ auto* pMetaAction = static_cast<MetaTextArrayAction*>(pAction);
+ TextArrayHandler(pMetaAction, pData);
+ }
+ break;
+
+ case MetaActionType::STRETCHTEXT:
+ {
+ auto* pMetaAction = static_cast<MetaStretchTextAction*>(pAction);
+ StretchTextHandler(pMetaAction, pData);
+ }
+ break;
+
+ case MetaActionType::TEXTRECT:
+ {
+ auto* pMetaAction = static_cast<MetaTextRectAction*>(pAction);
+ TextRectHandler(pMetaAction, pData);
+ }
+ break;
+
+ case MetaActionType::TEXTLINE:
+ {
+ auto* pMetaAction = static_cast<MetaTextLineAction*>(pAction);
+ TextLineHandler(pMetaAction);
+ }
+ break;
+
+ case MetaActionType::BMP:
+ {
+ auto* pMetaAction = static_cast<MetaBmpAction*>(pAction);
+ BmpHandler(pMetaAction);
+ }
+ break;
+
+ case MetaActionType::BMPSCALE:
+ {
+ auto* pMetaAction = static_cast<MetaBmpScaleAction*>(pAction);
+ BmpScaleHandler(pMetaAction);
+ }
+ break;
+
+ case MetaActionType::BMPSCALEPART:
+ {
+ auto* pMetaAction = static_cast<MetaBmpScalePartAction*>(pAction);
+ BmpScalePartHandler(pMetaAction);
+ }
+ break;
+
+ case MetaActionType::BMPEX:
+ {
+ auto* pMetaAction = static_cast<MetaBmpExAction*>(pAction);
+ BmpExHandler(pMetaAction);
+ }
+ break;
+
+ case MetaActionType::BMPEXSCALE:
+ {
+ auto* pMetaAction = static_cast<MetaBmpExScaleAction*>(pAction);
+ BmpExScaleHandler(pMetaAction);
+ }
+ break;
+
+ case MetaActionType::BMPEXSCALEPART:
+ {
+ auto* pMetaAction = static_cast<MetaBmpExScalePartAction*>(pAction);
+ BmpExScalePartHandler(pMetaAction);
+ }
+ break;
+
+ case MetaActionType::MASK:
+ {
+ auto* pMetaAction = static_cast<MetaMaskAction*>(pAction);
+ MaskHandler(pMetaAction);
+ }
+ break;
+
+ case MetaActionType::MASKSCALE:
+ {
+ auto* pMetaAction = static_cast<MetaMaskScaleAction*>(pAction);
+ MaskScaleHandler(pMetaAction);
+ }
+ break;
+
+ case MetaActionType::MASKSCALEPART:
+ {
+ auto* pMetaAction = static_cast<MetaMaskScalePartAction*>(pAction);
+ MaskScalePartHandler(pMetaAction);
+ }
+ break;
+
+ case MetaActionType::GRADIENT:
+ {
+ auto* pMetaAction = static_cast<MetaGradientAction*>(pAction);
+ GradientHandler(pMetaAction);
+ }
+ break;
+
+ case MetaActionType::GRADIENTEX:
+ {
+ auto* pMetaAction = static_cast<MetaGradientExAction*>(pAction);
+ GradientExHandler(pMetaAction);
+ }
+ break;
+
+ case MetaActionType::HATCH:
+ {
+ auto* pMetaAction = static_cast<MetaHatchAction*>(pAction);
+ HatchHandler(pMetaAction);
+ }
+ break;
+
+ case MetaActionType::WALLPAPER:
+ {
+ auto* pMetaAction = static_cast<MetaWallpaperAction*>(pAction);
+ WallpaperHandler(pMetaAction);
+ }
+ break;
+
+ case MetaActionType::CLIPREGION:
+ {
+ auto* pMetaAction = static_cast<MetaClipRegionAction*>(pAction);
+ ClipRegionHandler(pMetaAction);
+ }
+ break;
+
+ case MetaActionType::ISECTRECTCLIPREGION:
+ {
+ auto* pMetaAction = static_cast<MetaISectRectClipRegionAction*>(pAction);
+ ISectRectClipRegionHandler(pMetaAction);
+ }
+ break;
+
+ case MetaActionType::ISECTREGIONCLIPREGION:
+ {
+ auto* pMetaAction = static_cast<MetaISectRegionClipRegionAction*>(pAction);
+ ISectRegionClipRegionHandler(pMetaAction);
+ }
+ break;
+
+ case MetaActionType::MOVECLIPREGION:
+ {
+ auto* pMetaAction = static_cast<MetaMoveClipRegionAction*>(pAction);
+ MoveClipRegionHandler(pMetaAction);
+ }
+ break;
+
+ case MetaActionType::LINECOLOR:
+ {
+ auto* pMetaAction = static_cast<MetaLineColorAction*>(pAction);
+ LineColorHandler(pMetaAction);
+ }
+ break;
+
+ case MetaActionType::FILLCOLOR:
+ {
+ auto* pMetaAction = static_cast<MetaFillColorAction*>(pAction);
+ FillColorHandler(pMetaAction);
+ }
+ break;
+
+ case MetaActionType::TEXTCOLOR:
+ {
+ auto* pMetaAction = static_cast<MetaTextColorAction*>(pAction);
+ TextColorHandler(pMetaAction);
+ }
+ break;
+
+ case MetaActionType::TEXTFILLCOLOR:
+ {
+ auto* pMetaAction = static_cast<MetaTextFillColorAction*>(pAction);
+ TextFillColorHandler(pMetaAction);
+ }
+ break;
+
+ case MetaActionType::TEXTLINECOLOR:
+ {
+ auto* pMetaAction = static_cast<MetaTextLineColorAction*>(pAction);
+ TextLineColorHandler(pMetaAction);
+ }
+ break;
+
+ case MetaActionType::OVERLINECOLOR:
+ {
+ auto* pMetaAction = static_cast<MetaOverlineColorAction*>(pAction);
+ OverlineColorHandler(pMetaAction);
+ }
+ break;
+
+ case MetaActionType::TEXTALIGN:
+ {
+ auto* pMetaAction = static_cast<MetaTextAlignAction*>(pAction);
+ TextAlignHandler(pMetaAction);
+ }
+ break;
+
+ case MetaActionType::MAPMODE:
+ {
+ auto* pMetaAction = static_cast<MetaMapModeAction*>(pAction);
+ MapModeHandler(pMetaAction);
+ }
+ break;
+
+ case MetaActionType::FONT:
+ {
+ auto* pMetaAction = static_cast<MetaFontAction*>(pAction);
+ FontHandler(pMetaAction, pData);
+ }
+ break;
+
+ case MetaActionType::PUSH:
+ {
+ auto* pMetaAction = static_cast<MetaPushAction*>(pAction);
+ PushHandler(pMetaAction);
+ }
+ break;
+
+ case MetaActionType::POP:
+ {
+ auto* pMetaAction = static_cast<MetaPopAction*>(pAction);
+ PopHandler(pMetaAction);
+ }
+ break;
+
+ case MetaActionType::RASTEROP:
+ {
+ auto* pMetaAction = static_cast<MetaRasterOpAction*>(pAction);
+ RasterOpHandler(pMetaAction);
+ }
+ break;
+
+ case MetaActionType::Transparent:
+ {
+ auto* pMetaAction = static_cast<MetaTransparentAction*>(pAction);
+ TransparentHandler(pMetaAction);
+ }
+ break;
+
+ case MetaActionType::FLOATTRANSPARENT:
+ {
+ auto* pMetaAction = static_cast<MetaFloatTransparentAction*>(pAction);
+ FloatTransparentHandler(pMetaAction);
+ }
+ break;
+
+ case MetaActionType::EPS:
+ {
+ auto* pMetaAction = static_cast<MetaEPSAction*>(pAction);
+ EPSHandler(pMetaAction);
+ }
+ break;
+
+ case MetaActionType::REFPOINT:
+ {
+ auto* pMetaAction = static_cast<MetaRefPointAction*>(pAction);
+ RefPointHandler(pMetaAction);
+ }
+ break;
+
+ case MetaActionType::COMMENT:
+ {
+ auto* pMetaAction = static_cast<MetaCommentAction*>(pAction);
+ CommentHandler(pMetaAction);
+ }
+ break;
+
+ case MetaActionType::LAYOUTMODE:
+ {
+ auto* pMetaAction = static_cast<MetaLayoutModeAction*>(pAction);
+ LayoutModeHandler(pMetaAction);
+ }
+ break;
+
+ case MetaActionType::TEXTLANGUAGE:
+ {
+ auto* pMetaAction = static_cast<MetaTextLanguageAction*>(pAction);
+ TextLanguageHandler(pMetaAction);
+ }
+ break;
+ }
+}
+
+void SvmWriter::ActionHandler(const MetaAction* pAction)
+{
+ mrStream.WriteUInt16(static_cast<sal_uInt16>(pAction->GetType()));
+}
+
+void SvmWriter::PixelHandler(const MetaPixelAction* pAction)
+{
+ mrStream.WriteUInt16(static_cast<sal_uInt16>(pAction->GetType()));
+ VersionCompatWrite aCompat(mrStream, 1);
+ TypeSerializer aSerializer(mrStream);
+ aSerializer.writePoint(pAction->GetPoint());
+ WriteColor(pAction->GetColor());
+}
+
+void SvmWriter::PointHandler(const MetaPointAction* pAction)
+{
+ mrStream.WriteUInt16(static_cast<sal_uInt16>(pAction->GetType()));
+ VersionCompatWrite aCompat(mrStream, 1);
+ TypeSerializer aSerializer(mrStream);
+ aSerializer.writePoint(pAction->GetPoint());
+}
+
+void SvmWriter::LineHandler(const MetaLineAction* pAction)
+{
+ mrStream.WriteUInt16(static_cast<sal_uInt16>(pAction->GetType()));
+
+ VersionCompatWrite aCompat(mrStream, 2);
+
+ // Version 1
+ TypeSerializer aSerializer(mrStream);
+ aSerializer.writePoint(pAction->GetStartPoint());
+ aSerializer.writePoint(pAction->GetEndPoint());
+ // Version 2
+ WriteLineInfo(mrStream, pAction->GetLineInfo());
+}
+
+void SvmWriter::RectHandler(const MetaRectAction* pAction)
+{
+ mrStream.WriteUInt16(static_cast<sal_uInt16>(pAction->GetType()));
+
+ VersionCompatWrite aCompat(mrStream, 1);
+ TypeSerializer aSerializer(mrStream);
+ aSerializer.writeRectangle(pAction->GetRect());
+}
+
+void SvmWriter::RoundRectHandler(const MetaRoundRectAction* pAction)
+{
+ mrStream.WriteUInt16(static_cast<sal_uInt16>(pAction->GetType()));
+
+ VersionCompatWrite aCompat(mrStream, 1);
+ TypeSerializer aSerializer(mrStream);
+ aSerializer.writeRectangle(pAction->GetRect());
+ mrStream.WriteUInt32(pAction->GetHorzRound()).WriteUInt32(pAction->GetVertRound());
+}
+
+void SvmWriter::EllipseHandler(const MetaEllipseAction* pAction)
+{
+ mrStream.WriteUInt16(static_cast<sal_uInt16>(pAction->GetType()));
+
+ VersionCompatWrite aCompat(mrStream, 1);
+ TypeSerializer aSerializer(mrStream);
+ aSerializer.writeRectangle(pAction->GetRect());
+}
+
+void SvmWriter::ArcHandler(const MetaArcAction* pAction)
+{
+ mrStream.WriteUInt16(static_cast<sal_uInt16>(pAction->GetType()));
+
+ VersionCompatWrite aCompat(mrStream, 1);
+ TypeSerializer aSerializer(mrStream);
+ aSerializer.writeRectangle(pAction->GetRect());
+ aSerializer.writePoint(pAction->GetStartPoint());
+ aSerializer.writePoint(pAction->GetEndPoint());
+}
+
+void SvmWriter::PieHandler(const MetaPieAction* pAction)
+{
+ mrStream.WriteUInt16(static_cast<sal_uInt16>(pAction->GetType()));
+
+ VersionCompatWrite aCompat(mrStream, 1);
+ TypeSerializer aSerializer(mrStream);
+ aSerializer.writeRectangle(pAction->GetRect());
+ aSerializer.writePoint(pAction->GetStartPoint());
+ aSerializer.writePoint(pAction->GetEndPoint());
+}
+
+void SvmWriter::ChordHandler(const MetaChordAction* pAction)
+{
+ mrStream.WriteUInt16(static_cast<sal_uInt16>(pAction->GetType()));
+
+ VersionCompatWrite aCompat(mrStream, 1);
+ TypeSerializer aSerializer(mrStream);
+ aSerializer.writeRectangle(pAction->GetRect());
+ aSerializer.writePoint(pAction->GetStartPoint());
+ aSerializer.writePoint(pAction->GetEndPoint());
+}
+
+void SvmWriter::PolyLineHandler(const MetaPolyLineAction* pAction)
+{
+ mrStream.WriteUInt16(static_cast<sal_uInt16>(pAction->GetType()));
+
+ VersionCompatWrite aCompat(mrStream, 3);
+
+ tools::Polygon aSimplePoly;
+ pAction->GetPolygon().AdaptiveSubdivide(aSimplePoly);
+
+ WritePolygon(mrStream, aSimplePoly); // Version 1
+ WriteLineInfo(mrStream, pAction->GetLineInfo()); // Version 2
+
+ bool bHasPolyFlags = pAction->GetPolygon().HasFlags(); // Version 3
+ mrStream.WriteBool(bHasPolyFlags);
+ if (bHasPolyFlags)
+ pAction->GetPolygon().Write(mrStream);
+}
+
+void SvmWriter::PolygonHandler(const MetaPolygonAction* pAction)
+{
+ mrStream.WriteUInt16(static_cast<sal_uInt16>(pAction->GetType()));
+
+ VersionCompatWrite aCompat(mrStream, 2);
+
+ tools::Polygon aSimplePoly; // Version 1
+ pAction->GetPolygon().AdaptiveSubdivide(aSimplePoly);
+ WritePolygon(mrStream, aSimplePoly);
+
+ bool bHasPolyFlags = pAction->GetPolygon().HasFlags(); // Version 2
+ mrStream.WriteBool(bHasPolyFlags);
+ if (bHasPolyFlags)
+ pAction->GetPolygon().Write(mrStream);
+}
+
+void SvmWriter::PolyPolygonHandler(const MetaPolyPolygonAction* pAction)
+{
+ mrStream.WriteUInt16(static_cast<sal_uInt16>(pAction->GetType()));
+
+ VersionCompatWrite aCompat(mrStream, 2);
+
+ sal_uInt16 nNumberOfComplexPolygons = 0;
+ sal_uInt16 i, nPolyCount = pAction->GetPolyPolygon().Count();
+
+ tools::Polygon aSimplePoly; // Version 1
+ mrStream.WriteUInt16(nPolyCount);
+ for (i = 0; i < nPolyCount; i++)
+ {
+ const tools::Polygon& rPoly = pAction->GetPolyPolygon().GetObject(i);
+ if (rPoly.HasFlags())
+ nNumberOfComplexPolygons++;
+ rPoly.AdaptiveSubdivide(aSimplePoly);
+ WritePolygon(mrStream, aSimplePoly);
+ }
+
+ mrStream.WriteUInt16(nNumberOfComplexPolygons); // Version 2
+ for (i = 0; nNumberOfComplexPolygons && (i < nPolyCount); i++)
+ {
+ const tools::Polygon& rPoly = pAction->GetPolyPolygon().GetObject(i);
+ if (rPoly.HasFlags())
+ {
+ mrStream.WriteUInt16(i);
+ rPoly.Write(mrStream);
+
+ nNumberOfComplexPolygons--;
+ }
+ }
+}
+
+void SvmWriter::TextHandler(const MetaTextAction* pAction, const ImplMetaWriteData* pData)
+{
+ mrStream.WriteUInt16(static_cast<sal_uInt16>(pAction->GetType()));
+
+ VersionCompatWrite aCompat(mrStream, 2);
+ TypeSerializer aSerializer(mrStream);
+ aSerializer.writePoint(pAction->GetPoint());
+ mrStream.WriteUniOrByteString(pAction->GetText(), pData->meActualCharSet);
+ mrStream.WriteUInt16(pAction->GetIndex());
+ mrStream.WriteUInt16(pAction->GetLen());
+
+ write_uInt16_lenPrefixed_uInt16s_FromOUString(mrStream, pAction->GetText()); // version 2
+}
+
+void SvmWriter::TextArrayHandler(const MetaTextArrayAction* pAction, const ImplMetaWriteData* pData)
+{
+ mrStream.WriteUInt16(static_cast<sal_uInt16>(pAction->GetType()));
+
+ const std::vector<sal_Int32>& rDXArray = pAction->GetDXArray();
+
+ const sal_Int32 nAryLen = !rDXArray.empty() ? pAction->GetLen() : 0;
+
+ VersionCompatWrite aCompat(mrStream, 2);
+ TypeSerializer aSerializer(mrStream);
+ aSerializer.writePoint(pAction->GetPoint());
+ mrStream.WriteUniOrByteString(pAction->GetText(), pData->meActualCharSet);
+ mrStream.WriteUInt16(pAction->GetIndex());
+ mrStream.WriteUInt16(pAction->GetLen());
+ mrStream.WriteInt32(nAryLen);
+
+ for (sal_Int32 i = 0; i < nAryLen; ++i)
+ mrStream.WriteInt32(rDXArray[i]);
+
+ write_uInt16_lenPrefixed_uInt16s_FromOUString(mrStream, pAction->GetText()); // version 2
+}
+
+void SvmWriter::StretchTextHandler(const MetaStretchTextAction* pAction,
+ const ImplMetaWriteData* pData)
+{
+ mrStream.WriteUInt16(static_cast<sal_uInt16>(pAction->GetType()));
+
+ VersionCompatWrite aCompat(mrStream, 2);
+ TypeSerializer aSerializer(mrStream);
+ aSerializer.writePoint(pAction->GetPoint());
+ mrStream.WriteUniOrByteString(pAction->GetText(), pData->meActualCharSet);
+ mrStream.WriteUInt32(pAction->GetWidth());
+ mrStream.WriteUInt16(pAction->GetIndex());
+ mrStream.WriteUInt16(pAction->GetLen());
+
+ write_uInt16_lenPrefixed_uInt16s_FromOUString(mrStream, pAction->GetText()); // version 2
+}
+
+void SvmWriter::TextRectHandler(const MetaTextRectAction* pAction, const ImplMetaWriteData* pData)
+{
+ mrStream.WriteUInt16(static_cast<sal_uInt16>(pAction->GetType()));
+
+ VersionCompatWrite aCompat(mrStream, 2);
+ TypeSerializer aSerializer(mrStream);
+ aSerializer.writeRectangle(pAction->GetRect());
+ mrStream.WriteUniOrByteString(pAction->GetText(), pData->meActualCharSet);
+ mrStream.WriteUInt16(static_cast<sal_uInt16>(pAction->GetStyle()));
+
+ write_uInt16_lenPrefixed_uInt16s_FromOUString(mrStream, pAction->GetText()); // version 2
+}
+
+void SvmWriter::TextLineHandler(const MetaTextLineAction* pAction)
+{
+ mrStream.WriteUInt16(static_cast<sal_uInt16>(pAction->GetType()));
+
+ VersionCompatWrite aCompat(mrStream, 2);
+
+ TypeSerializer aSerializer(mrStream);
+ aSerializer.writePoint(pAction->GetStartPoint());
+
+ mrStream.WriteInt32(pAction->GetWidth());
+ mrStream.WriteUInt32(pAction->GetStrikeout());
+ mrStream.WriteUInt32(pAction->GetUnderline());
+ // new in version 2
+ mrStream.WriteUInt32(pAction->GetOverline());
+}
+
+void SvmWriter::BmpHandler(const MetaBmpAction* pAction)
+{
+ if (!pAction->GetBitmap().IsEmpty())
+ {
+ mrStream.WriteUInt16(static_cast<sal_uInt16>(pAction->GetType()));
+ VersionCompatWrite aCompat(mrStream, 1);
+ WriteDIB(pAction->GetBitmap(), mrStream, false, true);
+ TypeSerializer aSerializer(mrStream);
+ aSerializer.writePoint(pAction->GetPoint());
+ }
+}
+
+void SvmWriter::BmpScaleHandler(const MetaBmpScaleAction* pAction)
+{
+ if (!pAction->GetBitmap().IsEmpty())
+ {
+ mrStream.WriteUInt16(static_cast<sal_uInt16>(pAction->GetType()));
+ VersionCompatWrite aCompat(mrStream, 1);
+ WriteDIB(pAction->GetBitmap(), mrStream, false, true);
+ TypeSerializer aSerializer(mrStream);
+ aSerializer.writePoint(pAction->GetPoint());
+ aSerializer.writeSize(pAction->GetSize());
+ }
+}
+
+void SvmWriter::BmpScalePartHandler(const MetaBmpScalePartAction* pAction)
+{
+ if (!pAction->GetBitmap().IsEmpty())
+ {
+ mrStream.WriteUInt16(static_cast<sal_uInt16>(pAction->GetType()));
+ VersionCompatWrite aCompat(mrStream, 1);
+ WriteDIB(pAction->GetBitmap(), mrStream, false, true);
+ TypeSerializer aSerializer(mrStream);
+ aSerializer.writePoint(pAction->GetDestPoint());
+ aSerializer.writeSize(pAction->GetDestSize());
+ aSerializer.writePoint(pAction->GetSrcPoint());
+ aSerializer.writeSize(pAction->GetSrcSize());
+ }
+}
+
+void SvmWriter::BmpExHandler(const MetaBmpExAction* pAction)
+{
+ if (!pAction->GetBitmapEx().GetBitmap().IsEmpty())
+ {
+ mrStream.WriteUInt16(static_cast<sal_uInt16>(pAction->GetType()));
+ VersionCompatWrite aCompat(mrStream, 1);
+ WriteDIBBitmapEx(pAction->GetBitmapEx(), mrStream);
+ TypeSerializer aSerializer(mrStream);
+ aSerializer.writePoint(pAction->GetPoint());
+ }
+}
+
+void SvmWriter::BmpExScaleHandler(const MetaBmpExScaleAction* pAction)
+{
+ if (!pAction->GetBitmapEx().GetBitmap().IsEmpty())
+ {
+ mrStream.WriteUInt16(static_cast<sal_uInt16>(pAction->GetType()));
+ VersionCompatWrite aCompat(mrStream, 1);
+ WriteDIBBitmapEx(pAction->GetBitmapEx(), mrStream);
+ TypeSerializer aSerializer(mrStream);
+ aSerializer.writePoint(pAction->GetPoint());
+ aSerializer.writeSize(pAction->GetSize());
+ }
+}
+
+void SvmWriter::BmpExScalePartHandler(const MetaBmpExScalePartAction* pAction)
+{
+ if (!pAction->GetBitmapEx().GetBitmap().IsEmpty())
+ {
+ mrStream.WriteUInt16(static_cast<sal_uInt16>(pAction->GetType()));
+ VersionCompatWrite aCompat(mrStream, 1);
+ WriteDIBBitmapEx(pAction->GetBitmapEx(), mrStream);
+ TypeSerializer aSerializer(mrStream);
+ aSerializer.writePoint(pAction->GetDestPoint());
+ aSerializer.writeSize(pAction->GetDestSize());
+ aSerializer.writePoint(pAction->GetSrcPoint());
+ aSerializer.writeSize(pAction->GetSrcSize());
+ }
+}
+
+void SvmWriter::MaskHandler(const MetaMaskAction* pAction)
+{
+ if (!pAction->GetBitmap().IsEmpty())
+ {
+ mrStream.WriteUInt16(static_cast<sal_uInt16>(pAction->GetType()));
+ VersionCompatWrite aCompat(mrStream, 1);
+ WriteDIB(pAction->GetBitmap(), mrStream, false, true);
+ TypeSerializer aSerializer(mrStream);
+ aSerializer.writePoint(pAction->GetPoint());
+ }
+}
+
+void SvmWriter::MaskScaleHandler(const MetaMaskScaleAction* pAction)
+{
+ if (!pAction->GetBitmap().IsEmpty())
+ {
+ mrStream.WriteUInt16(static_cast<sal_uInt16>(pAction->GetType()));
+ VersionCompatWrite aCompat(mrStream, 1);
+ WriteDIB(pAction->GetBitmap(), mrStream, false, true);
+ TypeSerializer aSerializer(mrStream);
+ aSerializer.writePoint(pAction->GetPoint());
+ aSerializer.writeSize(pAction->GetSize());
+ }
+}
+
+void SvmWriter::MaskScalePartHandler(const MetaMaskScalePartAction* pAction)
+{
+ if (!pAction->GetBitmap().IsEmpty())
+ {
+ mrStream.WriteUInt16(static_cast<sal_uInt16>(pAction->GetType()));
+ VersionCompatWrite aCompat(mrStream, 1);
+ WriteDIB(pAction->GetBitmap(), mrStream, false, true);
+ WriteColor(pAction->GetColor());
+ TypeSerializer aSerializer(mrStream);
+ aSerializer.writePoint(pAction->GetDestPoint());
+ aSerializer.writeSize(pAction->GetDestSize());
+ aSerializer.writePoint(pAction->GetSrcPoint());
+ aSerializer.writeSize(pAction->GetSrcSize());
+ }
+}
+
+void SvmWriter::GradientHandler(const MetaGradientAction* pAction)
+{
+ mrStream.WriteUInt16(static_cast<sal_uInt16>(pAction->GetType()));
+ VersionCompatWrite aCompat(mrStream, 1);
+ TypeSerializer aSerializer(mrStream);
+ aSerializer.writeRectangle(pAction->GetRect());
+ aSerializer.writeGradient(pAction->GetGradient());
+}
+
+void SvmWriter::GradientExHandler(const MetaGradientExAction* pAction)
+{
+ mrStream.WriteUInt16(static_cast<sal_uInt16>(pAction->GetType()));
+ VersionCompatWrite aCompat(mrStream, 1);
+
+ // #i105373# see comment at MetaTransparentAction::Write
+ tools::PolyPolygon aNoCurvePolyPolygon;
+ pAction->GetPolyPolygon().AdaptiveSubdivide(aNoCurvePolyPolygon);
+
+ WritePolyPolygon(mrStream, aNoCurvePolyPolygon);
+ TypeSerializer aSerializer(mrStream);
+ aSerializer.writeGradient(pAction->GetGradient());
+}
+
+void SvmWriter::HatchHandler(const MetaHatchAction* pAction)
+{
+ mrStream.WriteUInt16(static_cast<sal_uInt16>(pAction->GetType()));
+ VersionCompatWrite aCompat(mrStream, 1);
+
+ // #i105373# see comment at MetaTransparentAction::Write
+ tools::PolyPolygon aNoCurvePolyPolygon;
+ pAction->GetPolyPolygon().AdaptiveSubdivide(aNoCurvePolyPolygon);
+
+ WritePolyPolygon(mrStream, aNoCurvePolyPolygon);
+ WriteHatch(mrStream, pAction->GetHatch());
+}
+
+void SvmWriter::WallpaperHandler(const MetaWallpaperAction* pAction)
+{
+ mrStream.WriteUInt16(static_cast<sal_uInt16>(pAction->GetType()));
+ VersionCompatWrite aCompat(mrStream, 1);
+
+ WriteWallpaper(mrStream, pAction->GetWallpaper());
+}
+
+void SvmWriter::ClipRegionHandler(const MetaClipRegionAction* pAction)
+{
+ mrStream.WriteUInt16(static_cast<sal_uInt16>(pAction->GetType()));
+ VersionCompatWrite aCompat(mrStream, 1);
+ WriteRegion(mrStream, pAction->GetRegion());
+ mrStream.WriteBool(pAction->IsClipping());
+}
+
+void SvmWriter::ISectRectClipRegionHandler(const MetaISectRectClipRegionAction* pAction)
+{
+ mrStream.WriteUInt16(static_cast<sal_uInt16>(pAction->GetType()));
+ VersionCompatWrite aCompat(mrStream, 1);
+ TypeSerializer aSerializer(mrStream);
+ aSerializer.writeRectangle(pAction->GetRect());
+}
+
+void SvmWriter::ISectRegionClipRegionHandler(const MetaISectRegionClipRegionAction* pAction)
+{
+ mrStream.WriteUInt16(static_cast<sal_uInt16>(pAction->GetType()));
+ VersionCompatWrite aCompat(mrStream, 1);
+ WriteRegion(mrStream, pAction->GetRegion());
+}
+
+void SvmWriter::MoveClipRegionHandler(const MetaMoveClipRegionAction* pAction)
+{
+ mrStream.WriteUInt16(static_cast<sal_uInt16>(pAction->GetType()));
+ VersionCompatWrite aCompat(mrStream, 1);
+ mrStream.WriteInt32(pAction->GetHorzMove()).WriteInt32(pAction->GetVertMove());
+}
+
+void SvmWriter::LineColorHandler(const MetaLineColorAction* pAction)
+{
+ mrStream.WriteUInt16(static_cast<sal_uInt16>(pAction->GetType()));
+ VersionCompatWrite aCompat(mrStream, 1);
+ WriteColor(pAction->GetColor());
+ mrStream.WriteBool(pAction->IsSetting());
+}
+
+void SvmWriter::FillColorHandler(const MetaFillColorAction* pAction)
+{
+ mrStream.WriteUInt16(static_cast<sal_uInt16>(pAction->GetType()));
+ VersionCompatWrite aCompat(mrStream, 1);
+ WriteColor(pAction->GetColor());
+ mrStream.WriteBool(pAction->IsSetting());
+}
+
+void SvmWriter::TextColorHandler(const MetaTextColorAction* pAction)
+{
+ mrStream.WriteUInt16(static_cast<sal_uInt16>(pAction->GetType()));
+ VersionCompatWrite aCompat(mrStream, 1);
+ WriteColor(pAction->GetColor());
+}
+
+void SvmWriter::TextFillColorHandler(const MetaTextFillColorAction* pAction)
+{
+ mrStream.WriteUInt16(static_cast<sal_uInt16>(pAction->GetType()));
+ VersionCompatWrite aCompat(mrStream, 1);
+ WriteColor(pAction->GetColor());
+ mrStream.WriteBool(pAction->IsSetting());
+}
+
+void SvmWriter::TextLineColorHandler(const MetaTextLineColorAction* pAction)
+{
+ mrStream.WriteUInt16(static_cast<sal_uInt16>(pAction->GetType()));
+ VersionCompatWrite aCompat(mrStream, 1);
+ WriteColor(pAction->GetColor());
+ mrStream.WriteBool(pAction->IsSetting());
+}
+
+void SvmWriter::OverlineColorHandler(const MetaOverlineColorAction* pAction)
+{
+ mrStream.WriteUInt16(static_cast<sal_uInt16>(pAction->GetType()));
+ VersionCompatWrite aCompat(mrStream, 1);
+ WriteColor(pAction->GetColor());
+ mrStream.WriteBool(pAction->IsSetting());
+}
+
+void SvmWriter::TextAlignHandler(const MetaTextAlignAction* pAction)
+{
+ mrStream.WriteUInt16(static_cast<sal_uInt16>(pAction->GetType()));
+ VersionCompatWrite aCompat(mrStream, 1);
+ mrStream.WriteUInt16(static_cast<sal_uInt16>(pAction->GetTextAlign()));
+}
+
+void SvmWriter::MapModeHandler(const MetaMapModeAction* pAction)
+{
+ mrStream.WriteUInt16(static_cast<sal_uInt16>(pAction->GetType()));
+ VersionCompatWrite aCompat(mrStream, 1);
+ TypeSerializer aSerializer(mrStream);
+ aSerializer.writeMapMode(pAction->GetMapMode());
+}
+
+void SvmWriter::FontHandler(const MetaFontAction* pAction, ImplMetaWriteData* pData)
+{
+ mrStream.WriteUInt16(static_cast<sal_uInt16>(pAction->GetType()));
+ VersionCompatWrite aCompat(mrStream, 1);
+ WriteFont(mrStream, pAction->GetFont());
+ pData->meActualCharSet = pAction->GetFont().GetCharSet();
+ if (pData->meActualCharSet == RTL_TEXTENCODING_DONTKNOW)
+ pData->meActualCharSet = osl_getThreadTextEncoding();
+}
+
+void SvmWriter::PushHandler(const MetaPushAction* pAction)
+{
+ mrStream.WriteUInt16(static_cast<sal_uInt16>(pAction->GetType()));
+ VersionCompatWrite aCompat(mrStream, 1);
+ mrStream.WriteUInt16(static_cast<sal_uInt16>(pAction->GetFlags()));
+}
+
+void SvmWriter::PopHandler(const MetaPopAction* pAction)
+{
+ mrStream.WriteUInt16(static_cast<sal_uInt16>(pAction->GetType()));
+ VersionCompatWrite aCompat(mrStream, 1);
+}
+
+void SvmWriter::RasterOpHandler(const MetaRasterOpAction* pAction)
+{
+ mrStream.WriteUInt16(static_cast<sal_uInt16>(pAction->GetType()));
+ VersionCompatWrite aCompat(mrStream, 1);
+ mrStream.WriteUInt16(static_cast<sal_uInt16>(pAction->GetRasterOp()));
+}
+
+void SvmWriter::TransparentHandler(const MetaTransparentAction* pAction)
+{
+ mrStream.WriteUInt16(static_cast<sal_uInt16>(pAction->GetType()));
+ VersionCompatWrite aCompat(mrStream, 1);
+
+ // #i105373# The tools::PolyPolygon in this action may be a curve; this
+ // was ignored until now what is an error. To make older office
+ // versions work with MetaFiles, i opt for applying AdaptiveSubdivide
+ // to the PolyPolygon.
+ // The alternative would be to really write the curve information
+ // like in MetaPolyPolygonAction::Write (where someone extended it
+ // correctly, but not here :-( ).
+ // The golden solution would be to combine both, but i think it's
+ // not necessary; a good subdivision will be sufficient.
+ tools::PolyPolygon aNoCurvePolyPolygon;
+ pAction->GetPolyPolygon().AdaptiveSubdivide(aNoCurvePolyPolygon);
+
+ WritePolyPolygon(mrStream, aNoCurvePolyPolygon);
+ mrStream.WriteUInt16(pAction->GetTransparence());
+}
+
+void SvmWriter::FloatTransparentHandler(const MetaFloatTransparentAction* pAction)
+{
+ mrStream.WriteUInt16(static_cast<sal_uInt16>(pAction->GetType()));
+ VersionCompatWrite aCompat(mrStream, 1);
+
+ SvmWriter aWriter(mrStream);
+ GDIMetaFile aMtf = pAction->GetGDIMetaFile();
+ aWriter.Write(aMtf);
+ TypeSerializer aSerializer(mrStream);
+ aSerializer.writePoint(pAction->GetPoint());
+ aSerializer.writeSize(pAction->GetSize());
+ aSerializer.writeGradient(pAction->GetGradient());
+}
+
+void SvmWriter::EPSHandler(const MetaEPSAction* pAction)
+{
+ mrStream.WriteUInt16(static_cast<sal_uInt16>(pAction->GetType()));
+ VersionCompatWrite aCompat(mrStream, 1);
+
+ TypeSerializer aSerializer(mrStream);
+ aSerializer.writeGfxLink(pAction->GetLink());
+ aSerializer.writePoint(pAction->GetPoint());
+ aSerializer.writeSize(pAction->GetSize());
+
+ SvmWriter aWriter(mrStream);
+ GDIMetaFile aMtf = pAction->GetSubstitute();
+ aWriter.Write(aMtf);
+}
+
+void SvmWriter::RefPointHandler(const MetaRefPointAction* pAction)
+{
+ mrStream.WriteUInt16(static_cast<sal_uInt16>(pAction->GetType()));
+ VersionCompatWrite aCompat(mrStream, 1);
+
+ TypeSerializer aSerializer(mrStream);
+ aSerializer.writePoint(pAction->GetRefPoint());
+ mrStream.WriteBool(pAction->IsSetting());
+}
+
+void SvmWriter::CommentHandler(const MetaCommentAction* pAction)
+{
+ mrStream.WriteUInt16(static_cast<sal_uInt16>(pAction->GetType()));
+ VersionCompatWrite aCompat(mrStream, 1);
+ write_uInt16_lenPrefixed_uInt8s_FromOString(mrStream, pAction->GetComment());
+ mrStream.WriteInt32(pAction->GetValue()).WriteUInt32(pAction->GetDataSize());
+
+ if (pAction->GetDataSize())
+ mrStream.WriteBytes(pAction->GetData(), pAction->GetDataSize());
+}
+
+void SvmWriter::LayoutModeHandler(const MetaLayoutModeAction* pAction)
+{
+ mrStream.WriteUInt16(static_cast<sal_uInt16>(pAction->GetType()));
+ VersionCompatWrite aCompat(mrStream, 1);
+ mrStream.WriteUInt32(static_cast<sal_uInt32>(pAction->GetLayoutMode()));
+}
+
+void SvmWriter::TextLanguageHandler(const MetaTextLanguageAction* pAction)
+{
+ mrStream.WriteUInt16(static_cast<sal_uInt16>(pAction->GetType()));
+ VersionCompatWrite aCompat(mrStream, 1);
+ mrStream.WriteUInt16(static_cast<sal_uInt16>(pAction->GetTextLanguage()));
+}
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/vcl/source/filter/webp/reader.cxx b/vcl/source/filter/webp/reader.cxx
new file mode 100644
index 000000000..3c0b1399c
--- /dev/null
+++ b/vcl/source/filter/webp/reader.cxx
@@ -0,0 +1,318 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <memory>
+#include <vcl/graph.hxx>
+#include <tools/stream.hxx>
+#include <filter/WebpReader.hxx>
+#include <bitmap/BitmapWriteAccess.hxx>
+#include <salinst.hxx>
+#include <sal/log.hxx>
+#include <unotools/configmgr.hxx>
+#include <svdata.hxx>
+#include <comphelper/scopeguard.hxx>
+
+#include <webp/decode.h>
+
+static bool readWebpInfo(SvStream& stream, std::vector<uint8_t>& data,
+ WebPBitstreamFeatures& features)
+{
+ for (;;)
+ {
+ // Read 4096 (more) bytes.
+ size_t lastSize = data.size();
+ data.resize(data.size() + 4096);
+ sal_Size nBytesRead = stream.ReadBytes(data.data() + lastSize, 4096);
+ if (nBytesRead <= 0)
+ return false;
+ data.resize(lastSize + nBytesRead);
+ int status = WebPGetFeatures(data.data(), data.size(), &features);
+ if (status == VP8_STATUS_OK)
+ break;
+ if (status == VP8_STATUS_NOT_ENOUGH_DATA)
+ continue; // Try again with 4096 more bytes read.
+ return false;
+ }
+ return true;
+}
+
+static bool readWebp(SvStream& stream, Graphic& graphic)
+{
+ WebPDecoderConfig config;
+ if (!WebPInitDecoderConfig(&config))
+ {
+ SAL_WARN("vcl.filter.webp", "WebPInitDecoderConfig() failed");
+ return false;
+ }
+ comphelper::ScopeGuard freeBuffer([&config]() { WebPFreeDecBuffer(&config.output); });
+ std::vector<uint8_t> data;
+ if (!readWebpInfo(stream, data, config.input))
+ return false;
+ // Here various parts of 'config' can be altered if wanted.
+ const int& width = config.input.width;
+ const int& height = config.input.height;
+ const int& has_alpha = config.input.has_alpha;
+
+ if (width > SAL_MAX_INT32 / 8 || height > SAL_MAX_INT32 / 8)
+ return false; // avoid overflows later
+
+ const bool bFuzzing = utl::ConfigManager::IsFuzzing();
+ const bool bSupportsBitmap32 = bFuzzing || ImplGetSVData()->mpDefInst->supportsBitmap32();
+
+ Bitmap bitmap;
+ AlphaMask bitmapAlpha;
+ if (bSupportsBitmap32 && has_alpha)
+ {
+ bitmap = Bitmap(Size(width, height), vcl::PixelFormat::N32_BPP);
+ }
+ else
+ {
+ bitmap = Bitmap(Size(width, height), vcl::PixelFormat::N24_BPP);
+ if (has_alpha)
+ bitmapAlpha = AlphaMask(Size(width, height));
+ }
+
+ BitmapScopedWriteAccess access(bitmap);
+ if (!access)
+ return false;
+ // If data cannot be read directly into the bitmap, read data first to this buffer and then convert.
+ std::vector<uint8_t> tmpRgbaData;
+ enum class PixelMode
+ {
+ DirectRead, // read data directly to the bitmap
+ Split, // read to tmp buffer and split to rgb and alpha
+ SetPixel // read to tmp buffer and use setPixel()
+ };
+ PixelMode pixelMode = PixelMode::SetPixel;
+
+ config.output.width = width;
+ config.output.height = height;
+ config.output.is_external_memory = 1;
+ if (bSupportsBitmap32 && has_alpha)
+ {
+ switch (RemoveScanline(access->GetScanlineFormat()))
+ {
+ // Our bitmap32 code expects premultiplied.
+ case ScanlineFormat::N32BitTcRgba:
+ config.output.colorspace = MODE_rgbA;
+ pixelMode = PixelMode::DirectRead;
+ break;
+ case ScanlineFormat::N32BitTcBgra:
+ config.output.colorspace = MODE_bgrA;
+ pixelMode = PixelMode::DirectRead;
+ break;
+ case ScanlineFormat::N32BitTcArgb:
+ config.output.colorspace = MODE_Argb;
+ pixelMode = PixelMode::DirectRead;
+ break;
+ default:
+ config.output.colorspace = MODE_RGBA;
+ pixelMode = PixelMode::SetPixel;
+ break;
+ }
+ }
+ else
+ {
+ if (has_alpha)
+ {
+ switch (RemoveScanline(access->GetScanlineFormat()))
+ {
+ case ScanlineFormat::N24BitTcRgb:
+ config.output.colorspace = MODE_RGBA;
+ pixelMode = PixelMode::Split;
+ break;
+ case ScanlineFormat::N24BitTcBgr:
+ config.output.colorspace = MODE_BGRA;
+ pixelMode = PixelMode::Split;
+ break;
+ default:
+ config.output.colorspace = MODE_RGBA;
+ pixelMode = PixelMode::SetPixel;
+ break;
+ }
+ }
+ else
+ {
+ switch (RemoveScanline(access->GetScanlineFormat()))
+ {
+ case ScanlineFormat::N24BitTcRgb:
+ config.output.colorspace = MODE_RGB;
+ pixelMode = PixelMode::DirectRead;
+ break;
+ case ScanlineFormat::N24BitTcBgr:
+ config.output.colorspace = MODE_BGR;
+ pixelMode = PixelMode::DirectRead;
+ break;
+ default:
+ config.output.colorspace = MODE_RGBA;
+ pixelMode = PixelMode::SetPixel;
+ break;
+ }
+ }
+ }
+ if (pixelMode == PixelMode::DirectRead)
+ {
+ config.output.u.RGBA.rgba = access->GetBuffer();
+ config.output.u.RGBA.stride = access->GetScanlineSize();
+ config.output.u.RGBA.size = access->GetScanlineSize() * access->Height();
+ }
+ else
+ {
+ tmpRgbaData.resize(width * height * 4);
+ config.output.u.RGBA.rgba = tmpRgbaData.data();
+ config.output.u.RGBA.stride = width * 4;
+ config.output.u.RGBA.size = tmpRgbaData.size();
+ }
+
+ std::unique_ptr<WebPIDecoder, decltype(&WebPIDelete)> decoder(WebPIDecode(nullptr, 0, &config),
+ WebPIDelete);
+
+ bool success = true;
+ for (;;)
+ {
+ // During first iteration, use data read while reading the header.
+ int status = WebPIAppend(decoder.get(), data.data(), data.size());
+ if (status == VP8_STATUS_OK)
+ break;
+ if (status != VP8_STATUS_SUSPENDED)
+ {
+ // An error, still try to return at least a partially read bitmap,
+ // even if returning an error flag.
+ success = false;
+ break;
+ }
+ // If more data is needed, reading 4096 bytes more and repeat.
+ data.resize(4096);
+ sal_Size nBytesRead = stream.ReadBytes(data.data(), 4096);
+ if (nBytesRead <= 0)
+ {
+ // Truncated file, again try to return at least something.
+ success = false;
+ break;
+ }
+ data.resize(nBytesRead);
+ }
+
+ switch (pixelMode)
+ {
+ case PixelMode::DirectRead:
+ {
+ // Adjust for IsBottomUp() if necessary.
+ if (access->IsBottomUp())
+ {
+ std::vector<char> tmp;
+ const sal_uInt32 lineSize = access->GetScanlineSize();
+ tmp.resize(lineSize);
+ for (tools::Long y = 0; y < access->Height() / 2; ++y)
+ {
+ tools::Long otherY = access->Height() - 1 - y;
+ memcpy(tmp.data(), access->GetScanline(y), lineSize);
+ memcpy(access->GetScanline(y), access->GetScanline(otherY), lineSize);
+ memcpy(access->GetScanline(otherY), tmp.data(), lineSize);
+ }
+ }
+ break;
+ }
+ case PixelMode::Split:
+ {
+ // Split to normal and alpha bitmaps.
+ AlphaScopedWriteAccess accessAlpha(bitmapAlpha);
+ for (tools::Long y = 0; y < access->Height(); ++y)
+ {
+ const unsigned char* src = tmpRgbaData.data() + width * 4 * y;
+ unsigned char* dstB = access->GetScanline(y);
+ unsigned char* dstA = accessAlpha->GetScanline(y);
+ for (tools::Long x = 0; x < access->Width(); ++x)
+ {
+ memcpy(dstB, src, 3);
+ *dstA = 255 - *(src + 3);
+ src += 4;
+ dstB += 3;
+ dstA += 1;
+ }
+ }
+ break;
+ }
+ case PixelMode::SetPixel:
+ {
+ for (tools::Long y = 0; y < access->Height(); ++y)
+ {
+ const unsigned char* src = tmpRgbaData.data() + width * 4 * y;
+ for (tools::Long x = 0; x < access->Width(); ++x)
+ {
+ sal_uInt8 r = src[0];
+ sal_uInt8 g = src[1];
+ sal_uInt8 b = src[2];
+ sal_uInt8 a = src[3];
+ access->SetPixel(y, x, Color(ColorAlpha, a, r, g, b));
+ src += 4;
+ }
+ }
+ if (!bitmapAlpha.IsEmpty())
+ {
+ AlphaScopedWriteAccess accessAlpha(bitmapAlpha);
+ for (tools::Long y = 0; y < accessAlpha->Height(); ++y)
+ {
+ const unsigned char* src = tmpRgbaData.data() + width * 4 * y;
+ for (tools::Long x = 0; x < accessAlpha->Width(); ++x)
+ {
+ sal_uInt8 a = src[3];
+ accessAlpha->SetPixelIndex(y, x, 255 - a);
+ src += 4;
+ }
+ }
+ }
+ break;
+ }
+ }
+
+ access.reset(); // Flush BitmapScopedWriteAccess.
+ if (bSupportsBitmap32 && has_alpha)
+ graphic = BitmapEx(bitmap);
+ else
+ {
+ if (has_alpha)
+ graphic = BitmapEx(bitmap, bitmapAlpha);
+ else
+ graphic = BitmapEx(bitmap);
+ }
+ return success;
+}
+
+bool ImportWebpGraphic(SvStream& rStream, Graphic& rGraphic)
+{
+ bool bRetValue = readWebp(rStream, rGraphic);
+ if (!bRetValue)
+ rStream.SetError(SVSTREAM_FILEFORMAT_ERROR);
+ return bRetValue;
+}
+
+bool ReadWebpInfo(SvStream& stream, Size& pixelSize, sal_uInt16& bitsPerPixel, bool& hasAlpha)
+{
+ std::vector<uint8_t> data;
+ WebPBitstreamFeatures features;
+ if (!readWebpInfo(stream, data, features))
+ return false;
+ pixelSize = Size(features.width, features.height);
+ bitsPerPixel = features.has_alpha ? 32 : 24;
+ hasAlpha = features.has_alpha;
+ return true;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/vcl/source/filter/webp/writer.cxx b/vcl/source/filter/webp/writer.cxx
new file mode 100644
index 000000000..f29dad007
--- /dev/null
+++ b/vcl/source/filter/webp/writer.cxx
@@ -0,0 +1,205 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <vcl/graph.hxx>
+#include <tools/stream.hxx>
+#include <filter/WebpWriter.hxx>
+#include <vcl/BitmapReadAccess.hxx>
+#include <comphelper/scopeguard.hxx>
+#include <sal/log.hxx>
+#include <o3tl/string_view.hxx>
+
+#include <webp/encode.h>
+
+static int writerFunction(const uint8_t* data, size_t size, const WebPPicture* picture)
+{
+ SvStream* stream = static_cast<SvStream*>(picture->custom_ptr);
+ return stream->WriteBytes(data, size) == size ? 1 : 0;
+}
+
+static WebPPreset presetToValue(std::u16string_view preset)
+{
+ if (o3tl::equalsIgnoreAsciiCase(preset, u"picture"))
+ return WEBP_PRESET_PICTURE;
+ if (o3tl::equalsIgnoreAsciiCase(preset, u"photo"))
+ return WEBP_PRESET_PHOTO;
+ if (o3tl::equalsIgnoreAsciiCase(preset, u"drawing"))
+ return WEBP_PRESET_DRAWING;
+ if (o3tl::equalsIgnoreAsciiCase(preset, u"icon"))
+ return WEBP_PRESET_ICON;
+ if (o3tl::equalsIgnoreAsciiCase(preset, u"text"))
+ return WEBP_PRESET_TEXT;
+ return WEBP_PRESET_DEFAULT;
+}
+
+static bool writeWebp(SvStream& rStream, const BitmapEx& bitmapEx, bool lossless,
+ std::u16string_view preset, int quality)
+{
+ WebPConfig config;
+ if (!WebPConfigInit(&config))
+ {
+ SAL_WARN("vcl.filter.webp", "WebPConfigInit() failed");
+ return false;
+ }
+ if (lossless)
+ {
+ if (!WebPConfigLosslessPreset(&config, 6))
+ {
+ SAL_WARN("vcl.filter.webp", "WebPConfigLosslessPreset() failed");
+ return false;
+ }
+ }
+ else
+ {
+ if (!WebPConfigPreset(&config, presetToValue(preset), quality))
+ {
+ SAL_WARN("vcl.filter.webp", "WebPConfigPreset() failed");
+ return false;
+ }
+ }
+ // Here various parts of 'config' can be altered if wanted.
+ assert(WebPValidateConfig(&config));
+
+ const int width = bitmapEx.GetSizePixel().Width();
+ const int height = bitmapEx.GetSizePixel().Height();
+
+ WebPPicture picture;
+ if (!WebPPictureInit(&picture))
+ {
+ SAL_WARN("vcl.filter.webp", "WebPPictureInit() failed");
+ return false;
+ }
+ picture.width = width;
+ picture.height = height;
+ picture.use_argb = lossless ? 1 : 0; // libwebp recommends argb only for lossless
+ comphelper::ScopeGuard freePicture([&picture]() { WebPPictureFree(&picture); });
+
+ // Apparently libwebp needs the entire image data at once in WebPPicture,
+ // so allocate it and copy there.
+ Bitmap bitmap(bitmapEx.GetBitmap());
+ AlphaMask bitmapAlpha;
+ if (bitmapEx.IsAlpha())
+ bitmapAlpha = bitmapEx.GetAlpha();
+ Bitmap::ScopedReadAccess access(bitmap);
+ AlphaMask::ScopedReadAccess accessAlpha(bitmapAlpha);
+ bool dataDone = false;
+ if (!access->IsBottomUp() && bitmapAlpha.IsEmpty())
+ {
+ // Try to directly copy the bitmap data.
+ switch (access->GetScanlineFormat())
+ {
+ case ScanlineFormat::N24BitTcRgb:
+ if (!WebPPictureImportRGB(&picture, access->GetBuffer(), access->GetScanlineSize()))
+ {
+ SAL_WARN("vcl.filter.webp", "WebPPictureImportRGB() failed");
+ return false;
+ }
+ dataDone = true;
+ break;
+ case ScanlineFormat::N24BitTcBgr:
+ if (!WebPPictureImportBGR(&picture, access->GetBuffer(), access->GetScanlineSize()))
+ {
+ SAL_WARN("vcl.filter.webp", "WebPPictureImportBGR() failed");
+ return false;
+ }
+ dataDone = true;
+ break;
+ // Our argb formats are premultiplied, so can't read directly using libwebp functions.
+ default:
+ break;
+ }
+ }
+ if (!dataDone)
+ {
+ // It would be simpler to convert the bitmap to 32bpp, but our 32bpp support is broken
+ // (it's unspecified whether it's premultiplied or not, for example). So handle this manually.
+ // Special handling for some common cases, generic otherwise.
+ if (!WebPPictureAlloc(&picture))
+ {
+ SAL_WARN("vcl.filter.webp", "WebPPictureAlloc() failed");
+ return false;
+ }
+ std::vector<uint8_t> data;
+ const int bpp = 4;
+ data.resize(width * height * bpp);
+ if (!bitmapAlpha.IsEmpty())
+ {
+ for (tools::Long y = 0; y < access->Height(); ++y)
+ {
+ unsigned char* dst = data.data() + width * bpp * y;
+ const sal_uInt8* srcB = access->GetScanline(y);
+ const sal_uInt8* srcA = accessAlpha->GetScanline(y);
+ for (tools::Long x = 0; x < access->Width(); ++x)
+ {
+ BitmapColor color = access->GetPixelFromData(srcB, x);
+ BitmapColor transparency = accessAlpha->GetPixelFromData(srcA, x);
+ color.SetAlpha(255 - transparency.GetIndex());
+ dst[0] = color.GetRed();
+ dst[1] = color.GetGreen();
+ dst[2] = color.GetBlue();
+ dst[3] = color.GetAlpha();
+ dst += bpp;
+ }
+ }
+ }
+ else
+ {
+ for (tools::Long y = 0; y < access->Height(); ++y)
+ {
+ unsigned char* dst = data.data() + width * bpp * y;
+ const sal_uInt8* src = access->GetScanline(y);
+ for (tools::Long x = 0; x < access->Width(); ++x)
+ {
+ Color color = access->GetPixelFromData(src, x);
+ dst[0] = color.GetRed();
+ dst[1] = color.GetGreen();
+ dst[2] = color.GetBlue();
+ dst[3] = color.GetAlpha();
+ dst += bpp;
+ }
+ }
+ }
+ // And now import from the temporary data. Use the import function rather
+ // than writing the data directly to avoid the need to write the data
+ // in the exact format WebPPicture wants (YUV, etc.).
+ if (!WebPPictureImportRGBA(&picture, data.data(), width * bpp))
+ {
+ SAL_WARN("vcl.filter.webp", "WebPPictureImportRGBA() failed");
+ return false;
+ }
+ }
+
+ picture.writer = writerFunction;
+ picture.custom_ptr = &rStream;
+ return WebPEncode(&config, &picture);
+}
+
+bool ExportWebpGraphic(SvStream& rStream, const Graphic& rGraphic,
+ FilterConfigItem* pFilterConfigItem)
+{
+ BitmapEx bitmap = rGraphic.GetBitmapEx();
+ // If lossless, neither presets nor quality matter.
+ bool lossless = pFilterConfigItem->ReadBool("Lossless", true);
+ // Preset is WebPPreset values.
+ const OUString preset = pFilterConfigItem->ReadString("Preset", "");
+ int quality = pFilterConfigItem->ReadInt32("Quality", 75);
+ return writeWebp(rStream, bitmap, lossless, preset, quality);
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/vcl/source/filter/wmf/emfwr.cxx b/vcl/source/filter/wmf/emfwr.cxx
new file mode 100644
index 000000000..b2782847b
--- /dev/null
+++ b/vcl/source/filter/wmf/emfwr.cxx
@@ -0,0 +1,1511 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <sal/config.h>
+#include <sal/log.hxx>
+
+#include <algorithm>
+
+#include "emfwr.hxx"
+#include <tools/helpers.hxx>
+#include <tools/fract.hxx>
+#include <tools/stream.hxx>
+#include <basegfx/polygon/b2dpolygon.hxx>
+#include <basegfx/polygon/b2dpolypolygon.hxx>
+#include <vcl/lineinfo.hxx>
+#include <vcl/dibtools.hxx>
+#include <vcl/metaact.hxx>
+#include <memory>
+
+#define WIN_EMR_POLYGON 3
+#define WIN_EMR_POLYLINE 4
+#define WIN_EMR_POLYBEZIERTO 5
+#define WIN_EMR_POLYLINETO 6
+#define WIN_EMR_POLYPOLYGON 8
+#define WIN_EMR_SETWINDOWEXTEX 9
+#define WIN_EMR_SETWINDOWORGEX 10
+#define WIN_EMR_SETVIEWPORTEXTEX 11
+#define WIN_EMR_SETVIEWPORTORGEX 12
+#define WIN_EMR_EOF 14
+#define WIN_EMR_SETPIXELV 15
+#define WIN_EMR_SETMAPMODE 17
+#define WIN_EMR_SETBKMODE 18
+#define WIN_EMR_SETROP2 20
+#define WIN_EMR_SETTEXTALIGN 22
+#define WIN_EMR_SETTEXTCOLOR 24
+#define WIN_EMR_MOVETOEX 27
+#define WIN_EMR_INTERSECTCLIPRECT 30
+#define WIN_EMR_SAVEDC 33
+#define WIN_EMR_RESTOREDC 34
+#define WIN_EMR_SELECTOBJECT 37
+#define WIN_EMR_CREATEPEN 38
+#define WIN_EMR_CREATEBRUSHINDIRECT 39
+#define WIN_EMR_DELETEOBJECT 40
+#define WIN_EMR_ELLIPSE 42
+#define WIN_EMR_RECTANGLE 43
+#define WIN_EMR_ROUNDRECT 44
+#define WIN_EMR_LINETO 54
+#define WIN_EMR_BEGINPATH 59
+#define WIN_EMR_ENDPATH 60
+#define WIN_EMR_CLOSEFIGURE 61
+#define WIN_EMR_FILLPATH 62
+#define WIN_EMR_STROKEPATH 64
+
+#define WIN_EMR_GDICOMMENT 70
+#define WIN_EMR_STRETCHDIBITS 81
+#define WIN_EMR_EXTCREATEFONTINDIRECTW 82
+#define WIN_EMR_EXTTEXTOUTW 84
+
+#define WIN_SRCCOPY 0x00CC0020L
+#define WIN_SRCPAINT 0x00EE0086L
+#define WIN_SRCAND 0x008800C6L
+#define WIN_SRCINVERT 0x00660046L
+#define WIN_EMR_COMMENT_EMFPLUS 0x2B464D45L
+
+#define HANDLE_INVALID 0xffffffff
+#define MAXHANDLES 65000
+
+#define LINE_SELECT 0x00000001
+#define FILL_SELECT 0x00000002
+#define TEXT_SELECT 0x00000004
+
+/* Text Alignment Options */
+#define TA_RIGHT 2
+
+#define TA_TOP 0
+#define TA_BOTTOM 8
+#define TA_BASELINE 24
+#define TA_RTLREADING 256
+
+#define MM_ANISOTROPIC 8
+
+enum class EmfPlusRecordType
+{
+ Header = 0x4001,
+ EndOfFile = 0x4002,
+ GetDC = 0x4004,
+ FillPolygon = 0x400C,
+ SetAntiAliasMode = 0x401E,
+ SetInterpolationMode = 0x4021,
+ SetPixelOffsetMode = 0x4022,
+ SetCompositingQuality = 0x4024
+};
+
+void EMFWriter::ImplBeginCommentRecord( sal_Int32 nCommentType )
+{
+ ImplBeginRecord( WIN_EMR_GDICOMMENT );
+ m_rStm.SeekRel( 4 );
+ m_rStm.WriteInt32( nCommentType );
+}
+
+void EMFWriter::ImplEndCommentRecord()
+{
+ if( mbRecordOpen )
+ {
+ sal_Int32 nActPos = m_rStm.Tell();
+ m_rStm.Seek( mnRecordPos + 8 );
+ m_rStm.WriteUInt32( nActPos - mnRecordPos - 0xc );
+ m_rStm.Seek( nActPos );
+ }
+ ImplEndRecord();
+}
+
+void EMFWriter::ImplBeginPlusRecord( EmfPlusRecordType nType, sal_uInt16 nFlags )
+{
+ SAL_WARN_IF( mbRecordPlusOpen, "vcl", "Another EMF+ record is already opened!" );
+
+ if( !mbRecordPlusOpen )
+ {
+ mbRecordPlusOpen = true;
+ mnRecordPlusPos = m_rStm.Tell();
+
+ m_rStm.WriteUInt16( static_cast<sal_uInt16>(nType) ).WriteUInt16( nFlags );
+ m_rStm.SeekRel( 8 );
+ }
+}
+
+void EMFWriter::ImplEndPlusRecord()
+{
+ SAL_WARN_IF( !mbRecordPlusOpen, "vcl", "EMF+ Record was not opened!" );
+
+ if( mbRecordPlusOpen )
+ {
+ sal_Int32 nActPos = m_rStm.Tell();
+ sal_Int32 nSize = nActPos - mnRecordPlusPos;
+ m_rStm.Seek( mnRecordPlusPos + 4 );
+ m_rStm.WriteUInt32( nSize ) // Size
+ .WriteUInt32( nSize - 0xc ); // Data Size
+ m_rStm.Seek( nActPos );
+ mbRecordPlusOpen = false;
+ }
+}
+
+void EMFWriter::ImplPlusRecord( EmfPlusRecordType nType, sal_uInt16 nFlags )
+{
+ ImplBeginPlusRecord( nType, nFlags );
+ ImplEndPlusRecord();
+}
+
+void EMFWriter::WriteEMFPlusHeader( const Size &rMtfSizePix, const Size &rMtfSizeLog )
+{
+ ImplBeginCommentRecord( WIN_EMR_COMMENT_EMFPLUS );
+
+ sal_Int32 nDPIX = rMtfSizePix.Width()*25;
+ sal_Int32 nDivX = rMtfSizeLog.Width()/100;
+ if (nDivX)
+ nDPIX /= nDivX; // DPI X
+
+ sal_Int32 nDPIY = rMtfSizePix.Height()*25;
+ sal_Int32 nDivY = rMtfSizeLog.Height()/100;
+ if (nDivY)
+ nDPIY /= nDivY; // DPI Y
+
+ m_rStm.WriteInt16( sal_Int16(EmfPlusRecordType::Header) );
+ m_rStm.WriteInt16( 0x01 ) // Flags - Dual Mode // TODO: Check this
+ .WriteInt32( 0x1C ) // Size
+ .WriteInt32( 0x10 ) // Data Size
+ .WriteInt32( 0xdbc01002 ) // (lower 12bits) 1-> v1 2-> v1.1 // TODO: Check this
+ .WriteInt32( 0x01 ) // Video display
+ .WriteInt32( nDPIX )
+ .WriteInt32( nDPIY );
+ ImplEndCommentRecord();
+
+ // Write more properties
+ ImplBeginCommentRecord( WIN_EMR_COMMENT_EMFPLUS );
+ ImplPlusRecord( EmfPlusRecordType::SetPixelOffsetMode, 0x0 );
+ ImplPlusRecord( EmfPlusRecordType::SetAntiAliasMode, 0x09 ); // TODO: Check actual values for AntiAlias
+ ImplPlusRecord( EmfPlusRecordType::SetCompositingQuality, 0x0100 ); // Default Quality
+ ImplPlusRecord( EmfPlusRecordType::SetInterpolationMode, 0x00 ); // Default
+ ImplPlusRecord( EmfPlusRecordType::GetDC, 0x00 );
+ ImplEndCommentRecord();
+}
+
+void EMFWriter::ImplWritePlusEOF()
+{
+ ImplBeginCommentRecord( WIN_EMR_COMMENT_EMFPLUS );
+ ImplPlusRecord( EmfPlusRecordType::EndOfFile, 0x0 );
+ ImplEndCommentRecord();
+}
+
+void EMFWriter::ImplWritePlusColor( const Color& rColor, sal_uInt32 nTrans )
+{
+ sal_uInt32 nAlpha = ((100-nTrans)*0xFF)/100;
+ sal_uInt32 nCol = rColor.GetBlue();
+
+ nCol |= static_cast<sal_uInt32>(rColor.GetGreen()) << 8;
+ nCol |= static_cast<sal_uInt32>(rColor.GetRed()) << 16;
+ nCol |= ( nAlpha << 24 );
+ m_rStm.WriteUInt32( nCol );
+}
+
+void EMFWriter::ImplWritePlusPoint( const Point& rPoint )
+{
+ // Convert to pixels
+ const Point aPoint(maVDev->LogicToPixel( rPoint, maDestMapMode ));
+ m_rStm.WriteUInt16( aPoint.X() ).WriteUInt16( aPoint.Y() );
+}
+
+void EMFWriter::ImplWritePlusFillPolygonRecord( const tools::Polygon& rPoly, sal_uInt32 nTrans )
+{
+ ImplBeginCommentRecord( WIN_EMR_COMMENT_EMFPLUS );
+ if( rPoly.GetSize() )
+ {
+ ImplBeginPlusRecord( EmfPlusRecordType::FillPolygon, 0xC000 ); // Sets the color as well
+ ImplWritePlusColor( maVDev->GetFillColor(), nTrans );
+ m_rStm.WriteUInt32( rPoly.GetSize() );
+ for( sal_uInt16 i = 0; i < rPoly.GetSize(); i++ )
+ ImplWritePlusPoint( rPoly[ i ] );
+ ImplEndPlusRecord();
+ }
+ ImplEndCommentRecord();
+}
+
+bool EMFWriter::WriteEMF(const GDIMetaFile& rMtf)
+{
+ const sal_uInt64 nHeaderPos = m_rStm.Tell();
+
+ maVDev->EnableOutput( false );
+ maVDev->SetMapMode( rMtf.GetPrefMapMode() );
+ // don't work with pixel as destination map mode -> higher resolution preferable
+ maDestMapMode.SetMapUnit( MapUnit::Map100thMM );
+ mHandlesUsed = std::vector<bool>(MAXHANDLES, false);
+ mnHandleCount = mnRecordCount = mnRecordPos = mnRecordPlusPos = 0;
+ mbRecordOpen = mbRecordPlusOpen = false;
+ mbLineChanged = mbFillChanged = mbTextChanged = false;
+ mnLineHandle = mnFillHandle = mnTextHandle = HANDLE_INVALID;
+ mnHorTextAlign = 0;
+
+ const Size aMtfSizePix( maVDev->LogicToPixel( rMtf.GetPrefSize(), rMtf.GetPrefMapMode() ) );
+ const Size aMtfSizeLog( OutputDevice::LogicToLogic(rMtf.GetPrefSize(), rMtf.GetPrefMapMode(), MapMode(MapUnit::Map100thMM)) );
+
+ // seek over header
+ // use [MS-EMF 2.2.11] HeaderExtension2 Object, otherwise resulting EMF cannot be converted with GetWinMetaFileBits()
+ m_rStm.SeekRel( 108 );
+
+ // Write EMF+ Header
+ WriteEMFPlusHeader( aMtfSizePix, aMtfSizeLog );
+
+ // write initial values
+
+ // set 100th mm map mode in EMF
+ ImplBeginRecord( WIN_EMR_SETMAPMODE );
+ m_rStm.WriteInt32( MM_ANISOTROPIC );
+ ImplEndRecord();
+
+ ImplBeginRecord( WIN_EMR_SETVIEWPORTEXTEX );
+ m_rStm.WriteInt32( maVDev->GetDPIX() ).WriteInt32( maVDev->GetDPIY() );
+ ImplEndRecord();
+
+ ImplBeginRecord( WIN_EMR_SETWINDOWEXTEX );
+ m_rStm.WriteInt32( 2540 ).WriteInt32( 2540 );
+ ImplEndRecord();
+
+ ImplBeginRecord( WIN_EMR_SETVIEWPORTORGEX );
+ m_rStm.WriteInt32( 0 ).WriteInt32( 0 );
+ ImplEndRecord();
+
+ ImplBeginRecord( WIN_EMR_SETWINDOWORGEX );
+ m_rStm.WriteInt32( 0 ).WriteInt32( 0 );
+ ImplEndRecord();
+
+ ImplWriteRasterOp( RasterOp::OverPaint );
+
+ ImplBeginRecord( WIN_EMR_SETBKMODE );
+ m_rStm.WriteUInt32( 1 ); // TRANSPARENT
+ ImplEndRecord();
+
+ // write emf data
+ ImplWrite( rMtf );
+
+ ImplWritePlusEOF();
+
+ ImplBeginRecord( WIN_EMR_EOF );
+ m_rStm.WriteUInt32( 0 ) // nPalEntries
+ .WriteUInt32( 0x10 ) // offPalEntries
+ .WriteUInt32( 0x14 ); // nSizeLast
+ ImplEndRecord();
+
+ // write header
+ const sal_uInt64 nEndPos = m_rStm.Tell(); m_rStm.Seek( nHeaderPos );
+
+ m_rStm.WriteUInt32( 0x00000001 ).WriteUInt32( 108 ) //use [MS-EMF 2.2.11] HeaderExtension2 Object
+ .WriteInt32( 0 ).WriteInt32( 0 ).WriteInt32( aMtfSizePix.Width() - 1 ).WriteInt32( aMtfSizePix.Height() - 1 )
+ .WriteInt32( 0 ).WriteInt32( 0 ).WriteInt32( aMtfSizeLog.Width() - 1 ).WriteInt32( aMtfSizeLog.Height() - 1 )
+ .WriteUInt32( 0x464d4520 ).WriteUInt32( 0x10000 ).WriteUInt32( nEndPos - nHeaderPos )
+ .WriteUInt32( mnRecordCount ).WriteUInt16( mnHandleCount + 1 ).WriteUInt16( 0 ).WriteUInt32( 0 ).WriteUInt32( 0 ).WriteUInt32( 0 )
+ .WriteInt32( aMtfSizePix.Width() ).WriteInt32( aMtfSizePix.Height() )
+ .WriteInt32( aMtfSizeLog.Width() / 100 ).WriteInt32( aMtfSizeLog.Height() / 100 )
+ .WriteUInt32( 0 ).WriteUInt32( 0 ).WriteUInt32( 0 )
+ .WriteInt32( aMtfSizeLog.Width() * 10 ).WriteInt32( aMtfSizeLog.Height() * 10 ); //use [MS-EMF 2.2.11] HeaderExtension2 Object
+
+ m_rStm.Seek( nEndPos );
+ mHandlesUsed.clear();
+
+ return( m_rStm.GetError() == ERRCODE_NONE );
+}
+
+sal_uLong EMFWriter::ImplAcquireHandle()
+{
+ sal_uLong nHandle = HANDLE_INVALID;
+
+ for( sal_uLong i = 0; i < mHandlesUsed.size() && ( HANDLE_INVALID == nHandle ); i++ )
+ {
+ if( !mHandlesUsed[ i ] )
+ {
+ mHandlesUsed[ i ] = true;
+
+ if( ( nHandle = i ) == mnHandleCount )
+ mnHandleCount++;
+ }
+ }
+
+ SAL_WARN_IF( nHandle == HANDLE_INVALID, "vcl", "No more handles available" );
+ return( nHandle != HANDLE_INVALID ? nHandle + 1 : HANDLE_INVALID );
+}
+
+void EMFWriter::ImplReleaseHandle( sal_uLong nHandle )
+{
+ SAL_WARN_IF( !nHandle || ( nHandle >= mHandlesUsed.size() ), "vcl", "Handle out of range" );
+ mHandlesUsed[ nHandle - 1 ] = false;
+}
+
+void EMFWriter::ImplBeginRecord( sal_uInt32 nType )
+{
+ SAL_WARN_IF( mbRecordOpen, "vcl", "Another record is already opened!" );
+
+ if( !mbRecordOpen )
+ {
+ mbRecordOpen = true;
+ mnRecordPos = m_rStm.Tell();
+
+ m_rStm.WriteUInt32( nType );
+ m_rStm.SeekRel( 4 );
+ }
+}
+
+void EMFWriter::ImplEndRecord()
+{
+ SAL_WARN_IF( !mbRecordOpen, "vcl", "Record was not opened!" );
+
+ if( !mbRecordOpen )
+ return;
+
+ sal_Int32 nFillBytes, nActPos = m_rStm.Tell();
+ m_rStm.Seek( mnRecordPos + 4 );
+ nFillBytes = nActPos - mnRecordPos;
+ nFillBytes += 3; // each record has to be dword aligned
+ nFillBytes ^= 3;
+ nFillBytes &= 3;
+ m_rStm.WriteUInt32( ( nActPos - mnRecordPos ) + nFillBytes );
+ m_rStm.Seek( nActPos );
+ while( nFillBytes-- )
+ m_rStm.WriteUChar( 0 );
+ mnRecordCount++;
+ mbRecordOpen = false;
+
+}
+
+bool EMFWriter::ImplPrepareHandleSelect( sal_uInt32& rHandle, sal_uLong nSelectType )
+{
+ if( rHandle != HANDLE_INVALID )
+ {
+ sal_uInt32 nStockObject = 0x80000000;
+
+ if( LINE_SELECT == nSelectType )
+ nStockObject |= 0x00000007;
+ else if( FILL_SELECT == nSelectType )
+ nStockObject |= 0x00000001;
+ else if( TEXT_SELECT == nSelectType )
+ nStockObject |= 0x0000000a;
+
+ // select stock object first
+ ImplBeginRecord( WIN_EMR_SELECTOBJECT );
+ m_rStm.WriteUInt32( nStockObject );
+ ImplEndRecord();
+
+ // destroy handle of created object
+ ImplBeginRecord( WIN_EMR_DELETEOBJECT );
+ m_rStm.WriteUInt32( rHandle );
+ ImplEndRecord();
+
+ // mark handle as free
+ ImplReleaseHandle( rHandle );
+ }
+
+ rHandle = ImplAcquireHandle();
+
+ return( HANDLE_INVALID != rHandle );
+}
+
+void EMFWriter::ImplCheckLineAttr()
+{
+ if( !(mbLineChanged && ImplPrepareHandleSelect( mnLineHandle, LINE_SELECT )) )
+ return;
+
+ sal_uInt32 nStyle = maVDev->IsLineColor() ? 0 : 5;
+
+ ImplBeginRecord( WIN_EMR_CREATEPEN );
+ m_rStm.WriteUInt32( mnLineHandle ).WriteUInt32( nStyle ).WriteUInt32( 0/*nWidth*/ ).WriteUInt32( 0/*nHeight*/ );
+ ImplWriteColor( maVDev->GetLineColor() );
+ ImplEndRecord();
+
+ ImplBeginRecord( WIN_EMR_SELECTOBJECT );
+ m_rStm.WriteUInt32( mnLineHandle );
+ ImplEndRecord();
+}
+
+void EMFWriter::ImplCheckFillAttr()
+{
+ if( !(mbFillChanged && ImplPrepareHandleSelect( mnFillHandle, FILL_SELECT )) )
+ return;
+
+ sal_uInt32 nStyle = maVDev->IsFillColor() ? 0 : 1;
+
+ ImplBeginRecord( WIN_EMR_CREATEBRUSHINDIRECT );
+ m_rStm.WriteUInt32( mnFillHandle ).WriteUInt32( nStyle );
+ ImplWriteColor( maVDev->GetFillColor() );
+ m_rStm.WriteUInt32( 0/*nPatternStyle*/ );
+ ImplEndRecord();
+
+ ImplBeginRecord( WIN_EMR_SELECTOBJECT );
+ m_rStm.WriteUInt32( mnFillHandle );
+ ImplEndRecord();
+}
+
+void EMFWriter::ImplCheckTextAttr()
+{
+ if( !(mbTextChanged && ImplPrepareHandleSelect( mnTextHandle, TEXT_SELECT )) )
+ return;
+
+ const vcl::Font& rFont = maVDev->GetFont();
+ const OUString& aFontName( rFont.GetFamilyName() );
+ sal_Int32 nWeight;
+ sal_uInt16 i;
+ sal_uInt8 nPitchAndFamily;
+
+ // tdf#127471 adapt nFontWidth from NormedFontScaling to
+ // Windows-like notation if used for text scaling
+ const tools::Long nFontHeight(rFont.GetFontSize().Height());
+ tools::Long nFontWidth(rFont.GetFontSize().Width());
+
+#ifndef _WIN32
+ const bool bFontScaledHorizontally(nFontWidth != 0 && nFontWidth != nFontHeight);
+
+ if(bFontScaledHorizontally)
+ {
+ // tdf#127471 nFontWidth is the non-Windows NormedFontScaling, need to convert to
+ // Windows-like notation with pre-multiplied AvgFontWidth since EMF/WMF are Windows
+ // specific formats.
+ const tools::Long nAverageFontWidth(rFont.GetOrCalculateAverageFontWidth());
+
+ if(nFontHeight > 0)
+ {
+ const double fScaleFactor(static_cast<double>(nAverageFontWidth) / static_cast<double>(nFontHeight));
+ nFontWidth = static_cast<tools::Long>(static_cast<double>(nFontWidth) * fScaleFactor);
+ }
+ }
+#endif
+
+ ImplBeginRecord( WIN_EMR_EXTCREATEFONTINDIRECTW );
+ m_rStm.WriteUInt32( mnTextHandle );
+ ImplWriteExtent( -nFontHeight );
+ ImplWriteExtent( nFontWidth );
+ m_rStm.WriteInt32( rFont.GetOrientation().get() ).WriteInt32( rFont.GetOrientation().get() );
+
+ switch( rFont.GetWeight() )
+ {
+ case WEIGHT_THIN: nWeight = 100; break;
+ case WEIGHT_ULTRALIGHT: nWeight = 200; break;
+ case WEIGHT_LIGHT: nWeight = 300; break;
+ case WEIGHT_SEMILIGHT: nWeight = 300; break;
+ case WEIGHT_NORMAL: nWeight = 400; break;
+ case WEIGHT_MEDIUM: nWeight = 500; break;
+ case WEIGHT_SEMIBOLD: nWeight = 600; break;
+ case WEIGHT_BOLD: nWeight = 700; break;
+ case WEIGHT_ULTRABOLD: nWeight = 800; break;
+ case WEIGHT_BLACK: nWeight = 900; break;
+ default: nWeight = 0; break;
+ }
+
+ m_rStm.WriteInt32( nWeight );
+ m_rStm.WriteUChar( ( ITALIC_NONE == rFont.GetItalic() ) ? 0 : 1 );
+ m_rStm.WriteUChar( ( LINESTYLE_NONE == rFont.GetUnderline() ) ? 0 : 1 );
+ m_rStm.WriteUChar( ( STRIKEOUT_NONE == rFont.GetStrikeout() ) ? 0 : 1 );
+ m_rStm.WriteUChar( ( RTL_TEXTENCODING_SYMBOL == rFont.GetCharSet() ) ? 2 : 0 );
+ m_rStm.WriteUChar( 0 ).WriteUChar( 0 ).WriteUChar( 0 );
+
+ switch( rFont.GetPitch() )
+ {
+ case PITCH_FIXED: nPitchAndFamily = 0x01; break;
+ case PITCH_VARIABLE: nPitchAndFamily = 0x02; break;
+ default: nPitchAndFamily = 0x00; break;
+ }
+
+ switch( rFont.GetFamilyType() )
+ {
+ case FAMILY_DECORATIVE: nPitchAndFamily |= 0x50; break;
+ case FAMILY_MODERN: nPitchAndFamily |= 0x30; break;
+ case FAMILY_ROMAN: nPitchAndFamily |= 0x10; break;
+ case FAMILY_SCRIPT: nPitchAndFamily |= 0x40; break;
+ case FAMILY_SWISS: nPitchAndFamily |= 0x20; break;
+ default: break;
+ }
+
+ m_rStm.WriteUChar( nPitchAndFamily );
+
+ for( i = 0; i < 32; i++ )
+ m_rStm.WriteUInt16( ( i < aFontName.getLength() ) ? aFontName[ i ] : 0 );
+
+ // dummy elfFullName
+ for( i = 0; i < 64; i++ )
+ m_rStm.WriteUInt16( 0 );
+
+ // dummy elfStyle
+ for( i = 0; i < 32; i++ )
+ m_rStm.WriteUInt16( 0 );
+
+ // dummy elfVersion, elfStyleSize, elfMatch, elfReserved
+ m_rStm.WriteUInt32( 0 ).WriteUInt32( 0 ).WriteUInt32( 0 ).WriteUInt32( 0 ) ;
+
+ // dummy elfVendorId
+ m_rStm.WriteUInt32( 0 );
+
+ // dummy elfCulture
+ m_rStm.WriteUInt32( 0 );
+
+ // dummy elfPanose
+ m_rStm.WriteUChar( 0 ).WriteUChar( 0 ).WriteUChar( 0 ).WriteUChar( 0 ).WriteUChar( 0 ).WriteUChar( 0 ).WriteUChar( 0 ).WriteUChar( 0 ).WriteUChar( 0 ).WriteUChar( 0 );
+
+ // fill record to get a record size divideable by 4
+ m_rStm.WriteUInt16( 0 );
+
+ ImplEndRecord();
+
+ // TextAlign
+ sal_uInt32 nTextAlign;
+
+ switch( rFont.GetAlignment() )
+ {
+ case ALIGN_TOP: nTextAlign = TA_TOP; break;
+ case ALIGN_BOTTOM: nTextAlign = TA_BOTTOM; break;
+ default: nTextAlign = TA_BASELINE; break;
+ }
+ nTextAlign |= mnHorTextAlign;
+
+ ImplBeginRecord( WIN_EMR_SETTEXTALIGN );
+ m_rStm.WriteUInt32( nTextAlign );
+ ImplEndRecord();
+
+ // Text color
+ ImplBeginRecord( WIN_EMR_SETTEXTCOLOR );
+ ImplWriteColor( maVDev->GetTextColor() );
+ ImplEndRecord();
+
+ ImplBeginRecord( WIN_EMR_SELECTOBJECT );
+ m_rStm.WriteUInt32( mnTextHandle );
+ ImplEndRecord();
+
+}
+
+void EMFWriter::ImplWriteColor( const Color& rColor )
+{
+ sal_uInt32 nCol = rColor.GetRed();
+
+ nCol |= static_cast<sal_uInt32>(rColor.GetGreen()) << 8;
+ nCol |= static_cast<sal_uInt32>(rColor.GetBlue()) << 16;
+
+ m_rStm.WriteUInt32( nCol );
+}
+
+void EMFWriter::ImplWriteRasterOp( RasterOp eRop )
+{
+ sal_uInt32 nROP2;
+
+ switch( eRop )
+ {
+ case RasterOp::Invert: nROP2 = 6; break;
+ case RasterOp::Xor: nROP2 = 7; break;
+ default: nROP2 = 13;break;
+ }
+
+ ImplBeginRecord( WIN_EMR_SETROP2 );
+ m_rStm.WriteUInt32( nROP2 );
+ ImplEndRecord();
+}
+
+void EMFWriter::ImplWriteExtent( tools::Long nExtent )
+{
+ nExtent = OutputDevice::LogicToLogic( Size( nExtent, 0 ), maVDev->GetMapMode(), maDestMapMode ).Width();
+ m_rStm.WriteInt32( nExtent );
+}
+
+void EMFWriter::ImplWritePoint( const Point& rPoint )
+{
+ const Point aPoint( OutputDevice::LogicToLogic( rPoint, maVDev->GetMapMode(), maDestMapMode ));
+ m_rStm.WriteInt32( aPoint.X() ).WriteInt32( aPoint.Y() );
+}
+
+void EMFWriter::ImplWriteSize( const Size& rSize)
+{
+ const Size aSize( OutputDevice::LogicToLogic( rSize, maVDev->GetMapMode(), maDestMapMode ));
+ m_rStm.WriteInt32( aSize.Width() ).WriteInt32( aSize.Height() );
+}
+
+void EMFWriter::ImplWriteRect( const tools::Rectangle& rRect )
+{
+ const tools::Rectangle aRect( OutputDevice::LogicToLogic ( rRect, maVDev->GetMapMode(), maDestMapMode ));
+ auto right = aRect.IsWidthEmpty() ? aRect.Left() : aRect.Right();
+ auto bottom = aRect.IsHeightEmpty() ? aRect.Top() : aRect.Bottom();
+ m_rStm
+ .WriteInt32( aRect.Left() )
+ .WriteInt32( aRect.Top() )
+ .WriteInt32( right )
+ .WriteInt32( bottom );
+}
+
+void EMFWriter::ImplWritePolygonRecord( const tools::Polygon& rPoly, bool bClose )
+{
+ if( !rPoly.GetSize() )
+ return;
+
+ if( rPoly.HasFlags() )
+ ImplWritePath( rPoly, bClose );
+ else
+ {
+ if( bClose )
+ ImplCheckFillAttr();
+
+ ImplCheckLineAttr();
+
+ ImplBeginRecord( bClose ? WIN_EMR_POLYGON : WIN_EMR_POLYLINE );
+ ImplWriteRect( rPoly.GetBoundRect() );
+ m_rStm.WriteUInt32( rPoly.GetSize() );
+
+ for( sal_uInt16 i = 0; i < rPoly.GetSize(); i++ )
+ ImplWritePoint( rPoly[ i ] );
+
+ ImplEndRecord();
+ }
+}
+
+void EMFWriter::ImplWritePolyPolygonRecord( const tools::PolyPolygon& rPolyPoly )
+{
+ sal_uInt16 n, i, nPolyCount = rPolyPoly.Count();
+
+ if( !nPolyCount )
+ return;
+
+ if( 1 == nPolyCount )
+ ImplWritePolygonRecord( rPolyPoly[ 0 ], true );
+ else
+ {
+ bool bHasFlags = false;
+ sal_uInt32 nTotalPoints = 0;
+
+ for( i = 0; i < nPolyCount; i++ )
+ {
+ nTotalPoints += rPolyPoly[ i ].GetSize();
+ if ( rPolyPoly[ i ].HasFlags() )
+ bHasFlags = true;
+ }
+ if( nTotalPoints )
+ {
+ if ( bHasFlags )
+ ImplWritePath( rPolyPoly, true );
+ else
+ {
+ ImplCheckFillAttr();
+ ImplCheckLineAttr();
+
+ ImplBeginRecord( WIN_EMR_POLYPOLYGON );
+ ImplWriteRect( rPolyPoly.GetBoundRect() );
+ m_rStm.WriteUInt32( nPolyCount ).WriteUInt32( nTotalPoints );
+
+ for( i = 0; i < nPolyCount; i++ )
+ m_rStm.WriteUInt32( rPolyPoly[ i ].GetSize() );
+
+ for( i = 0; i < nPolyCount; i++ )
+ {
+ const tools::Polygon& rPoly = rPolyPoly[ i ];
+
+ for( n = 0; n < rPoly.GetSize(); n++ )
+ ImplWritePoint( rPoly[ n ] );
+ }
+ ImplEndRecord();
+ }
+ }
+ }
+}
+
+void EMFWriter::ImplWritePath( const tools::PolyPolygon& rPolyPoly, bool bClosed )
+{
+ if ( bClosed )
+ ImplCheckFillAttr();
+ ImplCheckLineAttr();
+
+ ImplBeginRecord( WIN_EMR_BEGINPATH );
+ ImplEndRecord();
+
+ sal_uInt16 i, n, o, nPolyCount = rPolyPoly.Count();
+ for ( i = 0; i < nPolyCount; i++ )
+ {
+ n = 0;
+ const tools::Polygon& rPoly = rPolyPoly[ i ];
+ while ( n < rPoly.GetSize() )
+ {
+ if( n == 0 )
+ {
+ ImplBeginRecord( WIN_EMR_MOVETOEX );
+ ImplWritePoint( rPoly[ 0 ] );
+ ImplEndRecord();
+ n++;
+ continue;
+ }
+
+ sal_uInt16 nBezPoints = 0;
+
+ while ( ( ( nBezPoints + n + 2 ) < rPoly.GetSize() ) && ( rPoly.GetFlags( nBezPoints + n ) == PolyFlags::Control ) )
+ nBezPoints += 3;
+
+ if ( nBezPoints )
+ {
+ ImplBeginRecord( WIN_EMR_POLYBEZIERTO );
+ tools::Polygon aNewPoly( nBezPoints + 1 );
+ aNewPoly[ 0 ] = rPoly[ n - 1 ];
+ for ( o = 0; o < nBezPoints; o++ )
+ aNewPoly[ o + 1 ] = rPoly[ n + o ];
+ ImplWriteRect( aNewPoly.GetBoundRect() );
+ m_rStm.WriteUInt32( nBezPoints );
+ for( o = 1; o < aNewPoly.GetSize(); o++ )
+ ImplWritePoint( aNewPoly[ o ] );
+ ImplEndRecord();
+ n = n + nBezPoints;
+ }
+ else
+ {
+ sal_uInt16 nPoints = 1;
+ while( ( nPoints + n ) < rPoly.GetSize() && ( rPoly.GetFlags( nPoints + n ) != PolyFlags::Control ) )
+ nPoints++;
+
+ if ( nPoints > 1 )
+ {
+ ImplBeginRecord( WIN_EMR_POLYLINETO );
+ tools::Polygon aNewPoly( nPoints + 1 );
+ aNewPoly[ 0 ] = rPoly[ n - 1];
+ for ( o = 1; o <= nPoints; o++ )
+ aNewPoly[ o ] = rPoly[ n - 1 + o ];
+ ImplWriteRect( aNewPoly.GetBoundRect() );
+ m_rStm.WriteUInt32( nPoints );
+ for( o = 1; o < aNewPoly.GetSize(); o++ )
+ ImplWritePoint( aNewPoly[ o ] );
+ ImplEndRecord();
+ }
+ else
+ {
+ ImplBeginRecord( WIN_EMR_LINETO );
+ ImplWritePoint( rPoly[ n ] );
+ ImplEndRecord();
+ }
+ n = n + nPoints;
+ }
+ if ( bClosed && ( n == rPoly.GetSize() ) )
+ {
+ ImplBeginRecord( WIN_EMR_CLOSEFIGURE );
+ ImplEndRecord();
+ }
+ }
+ }
+ ImplBeginRecord( WIN_EMR_ENDPATH );
+ ImplEndRecord();
+ ImplBeginRecord( bClosed ? WIN_EMR_FILLPATH : WIN_EMR_STROKEPATH );
+ ImplWriteRect( rPolyPoly.GetBoundRect() );
+ ImplEndRecord();
+}
+
+void EMFWriter::ImplWriteBmpRecord( const Bitmap& rBmp, const Point& rPt,
+ const Size& rSz, sal_uInt32 nROP )
+{
+ if( rBmp.IsEmpty() )
+ return;
+
+ SvMemoryStream aMemStm( 65535, 65535 );
+ const Size aBmpSizePixel( rBmp.GetSizePixel() );
+
+ ImplBeginRecord( WIN_EMR_STRETCHDIBITS );
+ ImplWriteRect( tools::Rectangle( rPt, rSz ) );
+ ImplWritePoint( rPt );
+ m_rStm.WriteInt32( 0 ).WriteInt32( 0 ).WriteInt32( aBmpSizePixel.Width() ).WriteInt32( aBmpSizePixel.Height() );
+
+ // write offset positions and sizes later
+ const sal_uInt64 nOffPos = m_rStm.Tell();
+ m_rStm.SeekRel( 16 );
+
+ m_rStm.WriteUInt32( 0 ).WriteInt32( ( RasterOp::Xor == maVDev->GetRasterOp() && WIN_SRCCOPY == nROP ) ? WIN_SRCINVERT : nROP );
+ ImplWriteSize( rSz );
+
+ WriteDIB(rBmp, aMemStm, true, false);
+
+ sal_uInt32 nDIBSize = aMemStm.Tell(), nHeaderSize, nCompression, nColsUsed, nPalCount, nImageSize;
+ sal_uInt16 nBitCount;
+
+ // get DIB parameters
+ aMemStm.Seek( 0 );
+ aMemStm.ReadUInt32( nHeaderSize );
+ aMemStm.SeekRel( 10 );
+ aMemStm.ReadUInt16( nBitCount ).ReadUInt32( nCompression ).ReadUInt32( nImageSize );
+ aMemStm.SeekRel( 8 );
+ aMemStm.ReadUInt32( nColsUsed );
+
+ if (nBitCount <= 8)
+ {
+ if (nColsUsed)
+ nPalCount = nColsUsed;
+ else
+ nPalCount = 1 << static_cast<sal_uInt32>(nBitCount);
+ }
+ else
+ {
+ if (nCompression == BITFIELDS)
+ nPalCount = 3;
+ else
+ nPalCount = 0;
+ }
+
+ sal_uInt32 nPalSize = nPalCount * 4;
+
+ m_rStm.WriteBytes( aMemStm.GetData(), nDIBSize );
+
+ const sal_uInt64 nEndPos = m_rStm.Tell();
+ m_rStm.Seek( nOffPos );
+ m_rStm.WriteUInt32( 80 ).WriteUInt32( nHeaderSize + nPalSize );
+ m_rStm.WriteUInt32( 80 + nHeaderSize + nPalSize ).WriteUInt32( nImageSize );
+ m_rStm.Seek( nEndPos );
+
+ ImplEndRecord();
+
+}
+
+void EMFWriter::ImplWriteTextRecord( const Point& rPos, const OUString& rText, o3tl::span<const sal_Int32> pDXArray, sal_uInt32 nWidth )
+{
+ sal_Int32 nLen = rText.getLength(), i;
+
+ if( !nLen )
+ return;
+
+ sal_uInt32 nNormWidth;
+ std::vector<sal_Int32> aOwnArray;
+ o3tl::span<const sal_Int32> pDX;
+
+ // get text sizes
+ if( !pDXArray.empty() )
+ {
+ nNormWidth = maVDev->GetTextWidth( rText );
+ pDX = pDXArray;
+ }
+ else
+ {
+ nNormWidth = maVDev->GetTextArray( rText, &aOwnArray );
+ pDX = aOwnArray;
+ }
+
+ if( nLen > 1 )
+ {
+ nNormWidth = pDX[ nLen - 2 ] + maVDev->GetTextWidth( OUString(rText[ nLen - 1 ]) );
+
+ if( nWidth && nNormWidth && ( nWidth != nNormWidth ) )
+ {
+ if (!pDXArray.empty())
+ {
+ aOwnArray.insert(aOwnArray.begin(), pDXArray.begin(), pDXArray.end());
+ pDX = aOwnArray;
+ }
+ const double fFactor = static_cast<double>(nWidth) / nNormWidth;
+
+ for( i = 0; i < ( nLen - 1 ); i++ )
+ aOwnArray[ i ] = FRound( aOwnArray[ i ] * fFactor );
+ }
+ }
+
+ // write text record
+ ImplBeginRecord( WIN_EMR_EXTTEXTOUTW );
+
+ ImplWriteRect( tools::Rectangle( rPos, Size( nNormWidth, maVDev->GetTextHeight() ) ) );
+ m_rStm.WriteUInt32( 1 );
+ m_rStm.WriteInt32( 0 ).WriteInt32( 0 );
+ ImplWritePoint( rPos );
+ m_rStm.WriteUInt32( nLen ).WriteUInt32( 76 ).WriteUInt32( 2 );
+ m_rStm.WriteInt32( 0 ).WriteInt32( 0 ).WriteInt32( 0 ).WriteInt32( 0 );
+ m_rStm.WriteUInt32( 76 + ( nLen << 1 ) + ( (nLen & 1 ) ? 2 : 0 ) );
+
+ // write text
+ for( i = 0; i < nLen; i++ )
+ m_rStm.WriteUInt16( rText[ i ] );
+
+ // padding word
+ if( nLen & 1 )
+ m_rStm.WriteUInt16( 0 );
+
+ // write DX array
+ ImplWriteExtent( pDX[ 0 ] );
+
+ if( nLen > 1 )
+ {
+ for( i = 1; i < ( nLen - 1 ); i++ )
+ ImplWriteExtent( pDX[ i ] - pDX[ i - 1 ] );
+
+ ImplWriteExtent( pDX[ nLen - 2 ] / ( nLen - 1 ) );
+ }
+
+ ImplEndRecord();
+
+}
+
+void EMFWriter::Impl_handleLineInfoPolyPolygons(const LineInfo& rInfo, const basegfx::B2DPolygon& rLinePolygon)
+{
+ if(!rLinePolygon.count())
+ return;
+
+ basegfx::B2DPolyPolygon aLinePolyPolygon(rLinePolygon);
+ basegfx::B2DPolyPolygon aFillPolyPolygon;
+
+ rInfo.applyToB2DPolyPolygon(aLinePolyPolygon, aFillPolyPolygon);
+
+ if(aLinePolyPolygon.count())
+ {
+ for(auto const& rB2DPolygon : std::as_const(aLinePolyPolygon))
+ {
+ ImplWritePolygonRecord( tools::Polygon(rB2DPolygon), false );
+ }
+ }
+
+ if(!aFillPolyPolygon.count())
+ return;
+
+ const Color aOldLineColor(maVDev->GetLineColor());
+ const Color aOldFillColor(maVDev->GetFillColor());
+
+ maVDev->SetLineColor();
+ maVDev->SetFillColor(aOldLineColor);
+
+ for(auto const& rB2DPolygon : std::as_const(aFillPolyPolygon))
+ {
+ ImplWritePolyPolygonRecord(tools::PolyPolygon( tools::Polygon(rB2DPolygon) ));
+ }
+
+ maVDev->SetLineColor(aOldLineColor);
+ maVDev->SetFillColor(aOldFillColor);
+}
+
+void EMFWriter::ImplWrite( const GDIMetaFile& rMtf )
+{
+ for( size_t j = 0, nActionCount = rMtf.GetActionSize(); j < nActionCount; j++ )
+ {
+ const MetaAction* pAction = rMtf.GetAction( j );
+ const MetaActionType nType = pAction->GetType();
+
+ switch( nType )
+ {
+ case MetaActionType::PIXEL:
+ {
+ const MetaPixelAction* pA = static_cast<const MetaPixelAction*>(pAction);
+
+ ImplCheckLineAttr();
+ ImplBeginRecord( WIN_EMR_SETPIXELV );
+ ImplWritePoint( pA->GetPoint() );
+ ImplWriteColor( pA->GetColor() );
+ ImplEndRecord();
+ }
+ break;
+
+ case MetaActionType::POINT:
+ {
+ if( maVDev->IsLineColor() )
+ {
+ const MetaPointAction* pA = static_cast<const MetaPointAction*>(pAction);
+
+ ImplCheckLineAttr();
+ ImplBeginRecord( WIN_EMR_SETPIXELV );
+ ImplWritePoint( pA->GetPoint() );
+ ImplWriteColor( maVDev->GetLineColor() );
+ ImplEndRecord();
+ }
+ }
+ break;
+
+ case MetaActionType::LINE:
+ {
+ if( maVDev->IsLineColor() )
+ {
+ const MetaLineAction* pA = static_cast<const MetaLineAction*>(pAction);
+
+ if(pA->GetLineInfo().IsDefault())
+ {
+ ImplCheckLineAttr();
+
+ ImplBeginRecord( WIN_EMR_MOVETOEX );
+ ImplWritePoint( pA->GetStartPoint() );
+ ImplEndRecord();
+
+ ImplBeginRecord( WIN_EMR_LINETO );
+ ImplWritePoint( pA->GetEndPoint() );
+ ImplEndRecord();
+
+ ImplBeginRecord( WIN_EMR_SETPIXELV );
+ ImplWritePoint( pA->GetEndPoint() );
+ ImplWriteColor( maVDev->GetLineColor() );
+ ImplEndRecord();
+ }
+ else
+ {
+ // LineInfo used; handle Dash/Dot and fat lines
+ basegfx::B2DPolygon aPolygon;
+ aPolygon.append(basegfx::B2DPoint(pA->GetStartPoint().X(), pA->GetStartPoint().Y()));
+ aPolygon.append(basegfx::B2DPoint(pA->GetEndPoint().X(), pA->GetEndPoint().Y()));
+ Impl_handleLineInfoPolyPolygons(pA->GetLineInfo(), aPolygon);
+ }
+ }
+ }
+ break;
+
+ case MetaActionType::RECT:
+ {
+ if( maVDev->IsLineColor() || maVDev->IsFillColor() )
+ {
+ const MetaRectAction* pA = static_cast<const MetaRectAction*>(pAction);
+
+ ImplCheckFillAttr();
+ ImplCheckLineAttr();
+
+ ImplBeginRecord( WIN_EMR_RECTANGLE );
+ ImplWriteRect( pA->GetRect() );
+ ImplEndRecord();
+ }
+ }
+ break;
+
+ case MetaActionType::ROUNDRECT:
+ {
+ if( maVDev->IsLineColor() || maVDev->IsFillColor() )
+ {
+ const MetaRoundRectAction* pA = static_cast<const MetaRoundRectAction*>(pAction);
+
+ ImplCheckFillAttr();
+ ImplCheckLineAttr();
+
+ ImplBeginRecord( WIN_EMR_ROUNDRECT );
+ ImplWriteRect( pA->GetRect() );
+ ImplWriteSize( Size( pA->GetHorzRound(), pA->GetVertRound() ) );
+ ImplEndRecord();
+ }
+ }
+ break;
+
+ case MetaActionType::ELLIPSE:
+ {
+ if( maVDev->IsLineColor() || maVDev->IsFillColor() )
+ {
+ const MetaEllipseAction* pA = static_cast<const MetaEllipseAction*>(pAction);
+
+ ImplCheckFillAttr();
+ ImplCheckLineAttr();
+
+ ImplBeginRecord( WIN_EMR_ELLIPSE );
+ ImplWriteRect( pA->GetRect() );
+ ImplEndRecord();
+ }
+ }
+ break;
+
+ case MetaActionType::ARC:
+ case MetaActionType::PIE:
+ case MetaActionType::CHORD:
+ case MetaActionType::POLYGON:
+ {
+ if( maVDev->IsLineColor() || maVDev->IsFillColor() )
+ {
+ tools::Polygon aPoly;
+
+ switch( nType )
+ {
+ case MetaActionType::ARC:
+ {
+ const MetaArcAction* pA = static_cast<const MetaArcAction*>(pAction);
+ aPoly = tools::Polygon( pA->GetRect(), pA->GetStartPoint(), pA->GetEndPoint(), PolyStyle::Arc );
+ }
+ break;
+
+ case MetaActionType::PIE:
+ {
+ const MetaPieAction* pA = static_cast<const MetaPieAction*>(pAction);
+ aPoly = tools::Polygon( pA->GetRect(), pA->GetStartPoint(), pA->GetEndPoint(), PolyStyle::Pie );
+ }
+ break;
+
+ case MetaActionType::CHORD:
+ {
+ const MetaChordAction* pA = static_cast<const MetaChordAction*>(pAction);
+ aPoly = tools::Polygon( pA->GetRect(), pA->GetStartPoint(), pA->GetEndPoint(), PolyStyle::Chord );
+ }
+ break;
+
+ case MetaActionType::POLYGON:
+ aPoly = static_cast<const MetaPolygonAction*>(pAction)->GetPolygon();
+ break;
+ default: break;
+ }
+
+ ImplWritePolygonRecord( aPoly, nType != MetaActionType::ARC );
+ }
+ }
+ break;
+
+ case MetaActionType::POLYLINE:
+ {
+ if( maVDev->IsLineColor() )
+ {
+ const MetaPolyLineAction* pA = static_cast<const MetaPolyLineAction*>(pAction);
+ const tools::Polygon& rPoly = pA->GetPolygon();
+
+ if( rPoly.GetSize() )
+ {
+ if(pA->GetLineInfo().IsDefault())
+ {
+ ImplWritePolygonRecord( rPoly, false );
+ }
+ else
+ {
+ // LineInfo used; handle Dash/Dot and fat lines
+ Impl_handleLineInfoPolyPolygons(pA->GetLineInfo(), rPoly.getB2DPolygon());
+ }
+ }
+ }
+ }
+ break;
+
+ case MetaActionType::POLYPOLYGON:
+ {
+ if( maVDev->IsLineColor() || maVDev->IsFillColor() )
+ ImplWritePolyPolygonRecord( static_cast<const MetaPolyPolygonAction*>(pAction)->GetPolyPolygon() );
+ }
+ break;
+
+ case MetaActionType::GRADIENT:
+ {
+ const MetaGradientAction* pA = static_cast<const MetaGradientAction*>(pAction);
+ GDIMetaFile aTmpMtf;
+
+ Gradient aGradient = pA->GetGradient();
+ aGradient.AddGradientActions( pA->GetRect(), aTmpMtf );
+ ImplWrite( aTmpMtf );
+ }
+ break;
+
+ case MetaActionType::HATCH:
+ {
+ const MetaHatchAction* pA = static_cast<const MetaHatchAction*>(pAction);
+ GDIMetaFile aTmpMtf;
+
+ maVDev->AddHatchActions( pA->GetPolyPolygon(), pA->GetHatch(), aTmpMtf );
+ ImplWrite( aTmpMtf );
+ }
+ break;
+
+ case MetaActionType::Transparent:
+ {
+ const tools::PolyPolygon& rPolyPoly = static_cast<const MetaTransparentAction*>(pAction)->GetPolyPolygon();
+ if( rPolyPoly.Count() )
+ ImplWritePlusFillPolygonRecord( rPolyPoly[0], static_cast<const MetaTransparentAction*>(pAction)->GetTransparence() );
+ ImplCheckFillAttr();
+ ImplCheckLineAttr();
+ ImplWritePolyPolygonRecord( rPolyPoly );
+
+ ImplBeginCommentRecord( WIN_EMR_COMMENT_EMFPLUS );
+ ImplPlusRecord( EmfPlusRecordType::GetDC, 0x00 );
+ ImplEndCommentRecord();
+ }
+ break;
+
+ case MetaActionType::FLOATTRANSPARENT:
+ {
+ const MetaFloatTransparentAction* pA = static_cast<const MetaFloatTransparentAction*>(pAction);
+
+ GDIMetaFile aTmpMtf( pA->GetGDIMetaFile() );
+ Point aSrcPt( aTmpMtf.GetPrefMapMode().GetOrigin() );
+ const Size aSrcSize( aTmpMtf.GetPrefSize() );
+ const Point aDestPt( pA->GetPoint() );
+ const Size aDestSize( pA->GetSize() );
+ const double fScaleX = aSrcSize.Width() ? static_cast<double>(aDestSize.Width()) / aSrcSize.Width() : 1.0;
+ const double fScaleY = aSrcSize.Height() ? static_cast<double>(aDestSize.Height()) / aSrcSize.Height() : 1.0;
+ tools::Long nMoveX, nMoveY;
+
+ if( fScaleX != 1.0 || fScaleY != 1.0 )
+ {
+ aTmpMtf.Scale( fScaleX, fScaleY );
+ aSrcPt.setX( FRound( aSrcPt.X() * fScaleX ) );
+ aSrcPt.setY( FRound( aSrcPt.Y() * fScaleY ) );
+ }
+
+ nMoveX = aDestPt.X() - aSrcPt.X();
+ nMoveY = aDestPt.Y() - aSrcPt.Y();
+
+ if( nMoveX || nMoveY )
+ aTmpMtf.Move( nMoveX, nMoveY );
+
+ ImplCheckFillAttr();
+ ImplCheckLineAttr();
+ ImplCheckTextAttr();
+ ImplWrite( aTmpMtf );
+ }
+ break;
+
+ case MetaActionType::EPS:
+ {
+ const MetaEPSAction* pA = static_cast<const MetaEPSAction*>(pAction);
+ const GDIMetaFile& aSubstitute( pA->GetSubstitute() );
+
+ for( size_t i = 0, nCount = aSubstitute.GetActionSize(); i < nCount; i++ )
+ {
+ const MetaAction* pSubstAct = aSubstitute.GetAction( i );
+ if( pSubstAct->GetType() == MetaActionType::BMPSCALE )
+ {
+ maVDev->Push();
+ ImplBeginRecord( WIN_EMR_SAVEDC );
+ ImplEndRecord();
+
+ MapMode aMapMode( aSubstitute.GetPrefMapMode() );
+ Size aOutSize( OutputDevice::LogicToLogic( pA->GetSize(), maVDev->GetMapMode(), aMapMode ) );
+ aMapMode.SetScaleX( Fraction( aOutSize.Width(), aSubstitute.GetPrefSize().Width() ) );
+ aMapMode.SetScaleY( Fraction( aOutSize.Height(), aSubstitute.GetPrefSize().Height() ) );
+ aMapMode.SetOrigin( OutputDevice::LogicToLogic( pA->GetPoint(), maVDev->GetMapMode(), aMapMode ) );
+ maVDev->SetMapMode( aMapMode );
+ ImplWrite( aSubstitute );
+
+ maVDev->Pop();
+ ImplBeginRecord( WIN_EMR_RESTOREDC );
+ m_rStm.WriteInt32( -1 );
+ ImplEndRecord();
+ break;
+ }
+ }
+ }
+ break;
+
+ case MetaActionType::BMP:
+ {
+ const MetaBmpAction* pA = static_cast<const MetaBmpAction *>(pAction);
+ ImplWriteBmpRecord( pA->GetBitmap(), pA->GetPoint(), maVDev->PixelToLogic( pA->GetBitmap().GetSizePixel() ), WIN_SRCCOPY );
+ }
+ break;
+
+ case MetaActionType::BMPSCALE:
+ {
+ const MetaBmpScaleAction* pA = static_cast<const MetaBmpScaleAction*>(pAction);
+ ImplWriteBmpRecord( pA->GetBitmap(), pA->GetPoint(), pA->GetSize(), WIN_SRCCOPY );
+ }
+ break;
+
+ case MetaActionType::BMPSCALEPART:
+ {
+ const MetaBmpScalePartAction* pA = static_cast<const MetaBmpScalePartAction*>(pAction);
+ Bitmap aTmp( pA->GetBitmap() );
+
+ if( aTmp.Crop( tools::Rectangle( pA->GetSrcPoint(), pA->GetSrcSize() ) ) )
+ ImplWriteBmpRecord( aTmp, pA->GetDestPoint(), pA->GetDestSize(), WIN_SRCCOPY );
+ }
+ break;
+
+ case MetaActionType::BMPEX:
+ {
+ const MetaBmpExAction* pA = static_cast<const MetaBmpExAction *>(pAction);
+ Bitmap aBmp( pA->GetBitmapEx().GetBitmap() );
+ Bitmap aMsk( pA->GetBitmapEx().GetAlpha() );
+
+ if( !aMsk.IsEmpty() )
+ {
+ aBmp.Replace( aMsk, COL_WHITE );
+ aMsk.Invert();
+ ImplWriteBmpRecord( aMsk, pA->GetPoint(), maVDev->PixelToLogic( aMsk.GetSizePixel() ), WIN_SRCPAINT );
+ ImplWriteBmpRecord( aBmp, pA->GetPoint(), maVDev->PixelToLogic( aBmp.GetSizePixel() ), WIN_SRCAND );
+ }
+ else
+ ImplWriteBmpRecord( aBmp, pA->GetPoint(), aBmp.GetSizePixel(), WIN_SRCCOPY );
+ }
+ break;
+
+ case MetaActionType::BMPEXSCALE:
+ {
+ const MetaBmpExScaleAction* pA = static_cast<const MetaBmpExScaleAction*>(pAction);
+ Bitmap aBmp( pA->GetBitmapEx().GetBitmap() );
+ Bitmap aMsk( pA->GetBitmapEx().GetAlpha() );
+
+ if( !aMsk.IsEmpty() )
+ {
+ aBmp.Replace( aMsk, COL_WHITE );
+ aMsk.Invert();
+ ImplWriteBmpRecord( aMsk, pA->GetPoint(), pA->GetSize(), WIN_SRCPAINT );
+ ImplWriteBmpRecord( aBmp, pA->GetPoint(), pA->GetSize(), WIN_SRCAND );
+ }
+ else
+ ImplWriteBmpRecord( aBmp, pA->GetPoint(), pA->GetSize(), WIN_SRCCOPY );
+ }
+ break;
+
+ case MetaActionType::BMPEXSCALEPART:
+ {
+ const MetaBmpExScalePartAction* pA = static_cast<const MetaBmpExScalePartAction*>(pAction);
+ BitmapEx aBmpEx( pA->GetBitmapEx() );
+ aBmpEx.Crop( tools::Rectangle( pA->GetSrcPoint(), pA->GetSrcSize() ) );
+ Bitmap aBmp( aBmpEx.GetBitmap() );
+ Bitmap aMsk( aBmpEx.GetAlpha() );
+
+ if( !aMsk.IsEmpty() )
+ {
+ aBmp.Replace( aMsk, COL_WHITE );
+ aMsk.Invert();
+ ImplWriteBmpRecord( aMsk, pA->GetDestPoint(), pA->GetDestSize(), WIN_SRCPAINT );
+ ImplWriteBmpRecord( aBmp, pA->GetDestPoint(), pA->GetDestSize(), WIN_SRCAND );
+ }
+ else
+ ImplWriteBmpRecord( aBmp, pA->GetDestPoint(), pA->GetDestSize(), WIN_SRCCOPY );
+ }
+ break;
+
+ case MetaActionType::TEXT:
+ {
+ const MetaTextAction* pA = static_cast<const MetaTextAction*>(pAction);
+ const OUString aText = pA->GetText().copy( pA->GetIndex(), std::min(pA->GetText().getLength() - pA->GetIndex(), pA->GetLen()) );
+
+ ImplCheckTextAttr();
+ ImplWriteTextRecord( pA->GetPoint(), aText, {}, 0 );
+ }
+ break;
+
+ case MetaActionType::TEXTRECT:
+ {
+ const MetaTextRectAction* pA = static_cast<const MetaTextRectAction*>(pAction);
+ const OUString& aText( pA->GetText() );
+
+ ImplCheckTextAttr();
+ ImplWriteTextRecord( pA->GetRect().TopLeft(), aText, {}, 0 );
+ }
+ break;
+
+ case MetaActionType::TEXTARRAY:
+ {
+ const MetaTextArrayAction* pA = static_cast<const MetaTextArrayAction*>(pAction);
+ const OUString aText = pA->GetText().copy( pA->GetIndex(), std::min(pA->GetText().getLength() - pA->GetIndex(), pA->GetLen()) );
+
+ ImplCheckTextAttr();
+ ImplWriteTextRecord( pA->GetPoint(), aText, pA->GetDXArray(), 0 );
+ }
+ break;
+
+ case MetaActionType::STRETCHTEXT:
+ {
+ const MetaStretchTextAction* pA = static_cast<const MetaStretchTextAction*>(pAction);
+ const OUString aText = pA->GetText().copy( pA->GetIndex(), std::min(pA->GetText().getLength() - pA->GetIndex(), pA->GetLen()) );
+
+ ImplCheckTextAttr();
+ ImplWriteTextRecord( pA->GetPoint(), aText, {}, pA->GetWidth() );
+ }
+ break;
+
+ case MetaActionType::LINECOLOR:
+ {
+ const_cast<MetaAction*>(pAction)->Execute( maVDev );
+ mbLineChanged = true;
+ }
+ break;
+
+ case MetaActionType::FILLCOLOR:
+ {
+ const_cast<MetaAction*>(pAction)->Execute( maVDev );
+ mbFillChanged = true;
+ }
+ break;
+
+ case MetaActionType::TEXTCOLOR:
+ case MetaActionType::TEXTLINECOLOR:
+ case MetaActionType::TEXTFILLCOLOR:
+ case MetaActionType::TEXTALIGN:
+ case MetaActionType::FONT:
+ {
+ const_cast<MetaAction*>(pAction)->Execute( maVDev );
+ mbTextChanged = true;
+ }
+ break;
+
+ case MetaActionType::ISECTRECTCLIPREGION:
+ {
+ const_cast<MetaAction*>(pAction)->Execute( maVDev );
+
+ ImplBeginRecord( WIN_EMR_INTERSECTCLIPRECT );
+ ImplWriteRect( static_cast<const MetaISectRectClipRegionAction*>(pAction)->GetRect() );
+ ImplEndRecord();
+ }
+ break;
+
+ case MetaActionType::CLIPREGION:
+ case MetaActionType::ISECTREGIONCLIPREGION:
+ case MetaActionType::MOVECLIPREGION:
+ {
+ const_cast<MetaAction*>(pAction)->Execute( maVDev );
+ }
+ break;
+
+ case MetaActionType::REFPOINT:
+ case MetaActionType::MAPMODE:
+ const_cast<MetaAction*>(pAction)->Execute( maVDev );
+ break;
+
+ case MetaActionType::PUSH:
+ {
+ const_cast<MetaAction*>(pAction)->Execute( maVDev );
+
+ ImplBeginRecord( WIN_EMR_SAVEDC );
+ ImplEndRecord();
+ }
+ break;
+
+ case MetaActionType::POP:
+ {
+ const_cast<MetaAction*>(pAction)->Execute( maVDev );
+
+ ImplBeginRecord( WIN_EMR_RESTOREDC );
+ m_rStm.WriteInt32( -1 );
+ ImplEndRecord();
+
+ ImplWriteRasterOp( maVDev->GetRasterOp() );
+ mbLineChanged = mbFillChanged = mbTextChanged = true;
+ }
+ break;
+
+ case MetaActionType::RASTEROP:
+ {
+ const_cast<MetaAction*>(pAction)->Execute( maVDev );
+ ImplWriteRasterOp( static_cast<const MetaRasterOpAction*>(pAction)->GetRasterOp() );
+ }
+ break;
+
+ case MetaActionType::LAYOUTMODE:
+ {
+ vcl::text::ComplexTextLayoutFlags nLayoutMode = static_cast<const MetaLayoutModeAction*>(pAction)->GetLayoutMode();
+ mnHorTextAlign = 0;
+ if ((nLayoutMode & vcl::text::ComplexTextLayoutFlags::BiDiRtl) != vcl::text::ComplexTextLayoutFlags::Default)
+ {
+ mnHorTextAlign = TA_RIGHT | TA_RTLREADING;
+ }
+ if ((nLayoutMode & vcl::text::ComplexTextLayoutFlags::TextOriginRight) != vcl::text::ComplexTextLayoutFlags::Default)
+ mnHorTextAlign |= TA_RIGHT;
+ else if ((nLayoutMode & vcl::text::ComplexTextLayoutFlags::TextOriginLeft) != vcl::text::ComplexTextLayoutFlags::Default)
+ mnHorTextAlign &= ~TA_RIGHT;
+ break;
+ }
+
+ case MetaActionType::COMMENT:
+ {
+ MetaCommentAction const*const pCommentAction(
+ static_cast<MetaCommentAction const*>(pAction));
+ if (pCommentAction->GetComment() == "EMF_PLUS")
+ {
+ ImplBeginCommentRecord(WIN_EMR_COMMENT_EMFPLUS);
+ m_rStm.WriteBytes(pCommentAction->GetData(),
+ pCommentAction->GetDataSize());
+ ImplEndCommentRecord();
+ }
+ }
+ break;
+
+ case MetaActionType::MASK:
+ case MetaActionType::MASKSCALE:
+ case MetaActionType::MASKSCALEPART:
+ case MetaActionType::WALLPAPER:
+ case MetaActionType::TEXTLINE:
+ case MetaActionType::GRADIENTEX:
+ // Explicitly ignored cases
+ break;
+
+ default:
+ // TODO: Implement more cases as necessary. Let's not bother with a warning.
+ break;
+ }
+ }
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/vcl/source/filter/wmf/emfwr.hxx b/vcl/source/filter/wmf/emfwr.hxx
new file mode 100644
index 000000000..502ae5d28
--- /dev/null
+++ b/vcl/source/filter/wmf/emfwr.hxx
@@ -0,0 +1,114 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#ifndef INCLUDED_VCL_SOURCE_FILTER_WMF_EMFWR_HXX
+#define INCLUDED_VCL_SOURCE_FILTER_WMF_EMFWR_HXX
+
+#include <vcl/gdimtf.hxx>
+#include <vcl/virdev.hxx>
+
+class LineInfo;
+namespace basegfx { class B2DPolygon; }
+enum class EmfPlusRecordType;
+
+class EMFWriter
+{
+private:
+
+ ScopedVclPtr<VirtualDevice> maVDev;
+ MapMode maDestMapMode;
+ SvStream& m_rStm;
+ std::vector<bool> mHandlesUsed;
+ sal_uLong mnHandleCount;
+ sal_uLong mnRecordCount;
+ sal_uLong mnRecordPos;
+ sal_uLong mnRecordPlusPos;
+ bool mbRecordOpen;
+ bool mbRecordPlusOpen;
+ bool mbLineChanged;
+ sal_uInt32 mnLineHandle;
+ bool mbFillChanged;
+ sal_uInt32 mnFillHandle;
+ bool mbTextChanged;
+ sal_uInt32 mnTextHandle;
+ sal_uInt32 mnHorTextAlign;
+
+ void ImplBeginRecord( sal_uInt32 nType );
+ void ImplEndRecord();
+ void ImplBeginPlusRecord( EmfPlusRecordType nType, sal_uInt16 nFlags );
+ void ImplEndPlusRecord();
+ void ImplPlusRecord( EmfPlusRecordType nType, sal_uInt16 nFlags );
+ void ImplBeginCommentRecord( sal_Int32 nCommentType );
+ void ImplEndCommentRecord();
+
+ sal_uLong ImplAcquireHandle();
+ void ImplReleaseHandle( sal_uLong nHandle );
+
+ bool ImplPrepareHandleSelect( sal_uInt32& rHandle, sal_uLong nSelectType );
+ void ImplCheckLineAttr();
+ void ImplCheckFillAttr();
+ void ImplCheckTextAttr();
+
+ void ImplWriteColor( const Color& rColor );
+ void ImplWriteRasterOp( RasterOp eRop );
+ void ImplWriteExtent( tools::Long nExtent );
+ void ImplWritePoint( const Point& rPoint );
+ void ImplWriteSize( const Size& rSize);
+ void ImplWriteRect( const tools::Rectangle& rRect );
+ void ImplWritePath( const tools::PolyPolygon& rPolyPoly, bool bClose );
+ void ImplWritePolygonRecord( const tools::Polygon& rPoly, bool bClose );
+ void ImplWritePolyPolygonRecord( const tools::PolyPolygon& rPolyPoly );
+ void ImplWriteBmpRecord( const Bitmap& rBmp, const Point& rPt, const Size& rSz, sal_uInt32 nROP );
+ void ImplWriteTextRecord( const Point& rPos, const OUString& rText, o3tl::span<const sal_Int32> pDXArray, sal_uInt32 nWidth );
+
+ void Impl_handleLineInfoPolyPolygons(const LineInfo& rInfo, const basegfx::B2DPolygon& rLinePolygon);
+ void ImplWrite( const GDIMetaFile& rMtf );
+ void WriteEMFPlusHeader( const Size &rMtfSizePix, const Size &rMtfSizeLog );
+ void ImplWritePlusEOF();
+ void ImplWritePlusFillPolygonRecord( const tools::Polygon& rPoly, sal_uInt32 nTrans );
+ void ImplWritePlusColor( const Color& rColor, sal_uInt32 nTrans );
+ void ImplWritePlusPoint( const Point& rPoint );
+
+public:
+
+ explicit EMFWriter(SvStream &rStream)
+ : maVDev( VclPtr<VirtualDevice>::Create() )
+ , m_rStm(rStream)
+ , mnHandleCount(0)
+ , mnRecordCount(0)
+ , mnRecordPos(0)
+ , mnRecordPlusPos(0)
+ , mbRecordOpen(false)
+ , mbRecordPlusOpen(false)
+ , mbLineChanged(false)
+ , mnLineHandle(0)
+ , mbFillChanged(false)
+ , mnFillHandle(0)
+ , mbTextChanged(false)
+ , mnTextHandle(0)
+ , mnHorTextAlign(0)
+ {
+ }
+
+ bool WriteEMF(const GDIMetaFile& rMtf);
+};
+
+#endif // INCLUDED_VCL_SOURCE_FILTER_WMF_EMFWR_HXX
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/vcl/source/filter/wmf/wmf.cxx b/vcl/source/filter/wmf/wmf.cxx
new file mode 100644
index 000000000..bf91502b4
--- /dev/null
+++ b/vcl/source/filter/wmf/wmf.cxx
@@ -0,0 +1,134 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include "emfwr.hxx"
+#include "wmfwr.hxx"
+#include <vcl/wmf.hxx>
+#include <vcl/gdimetafiletools.hxx>
+#include <vcl/graph.hxx>
+
+using namespace com::sun::star;
+
+bool ReadWindowMetafile( SvStream& rStream, GDIMetaFile& rMTF )
+{
+ // tdf#111484 Use new method to import Metafile. Take current StreamPos
+ // into account (used by SwWW8ImplReader::ReadGrafFile and by
+ // SwWw6ReadMetaStream, so do *not* ignore. OTOH XclImpDrawing::ReadWmf
+ // is nice enough to copy to an own MemStream to avoid that indirect
+ // parameter passing...)
+ const sal_uInt32 nStreamStart(rStream.Tell());
+ const sal_uInt32 nStreamEnd(rStream.TellEnd());
+
+ if (nStreamStart >= nStreamEnd)
+ {
+ return false;
+ }
+
+ // Read binary data to mem array
+ const sal_uInt32 nStreamLength(nStreamEnd - nStreamStart);
+ auto rData = std::make_unique<std::vector<sal_uInt8>>(nStreamLength);
+ rStream.ReadBytes(rData->data(), rData->size());
+ BinaryDataContainer aDataContainer(std::move(rData));
+ rStream.Seek(nStreamStart);
+
+ if (rStream.good())
+ {
+ // Throw into VectorGraphicData to get the import. Do not care
+ // too much for type, this will be checked there. Also no path
+ // needed, it is a temporary object
+ auto aVectorGraphicDataPtr =
+ std::make_shared<VectorGraphicData>(aDataContainer, VectorGraphicDataType::Emf);
+
+ // create a Graphic and grep Metafile from it
+ const Graphic aGraphic(aVectorGraphicDataPtr);
+
+ // get the Metafile from it, done
+ rMTF = aGraphic.GetGDIMetaFile();
+ return true;
+ }
+
+ return rStream.good();
+}
+
+bool ConvertGDIMetaFileToWMF( const GDIMetaFile & rMTF, SvStream & rTargetStream,
+ FilterConfigItem const * pConfigItem, bool bPlaceable)
+{
+ WMFWriter aWMFWriter;
+ GDIMetaFile aGdiMetaFile(rMTF);
+
+ if(usesClipActions(aGdiMetaFile))
+ {
+ // #i121267# It is necessary to prepare the metafile since the export does *not* support
+ // clip regions. This tooling method clips the geometry content of the metafile internally
+ // against its own clip regions, so that the export is safe to ignore clip regions
+ clipMetafileContentAgainstOwnRegions(aGdiMetaFile);
+ }
+
+ bool bRet = aWMFWriter.WriteWMF(aGdiMetaFile, rTargetStream, pConfigItem, bPlaceable);
+ return bRet;
+}
+
+bool ConvertGraphicToWMF(const Graphic& rGraphic, SvStream& rTargetStream,
+ FilterConfigItem const* pConfigItem, bool bPlaceable)
+{
+ GfxLink aLink = rGraphic.GetGfxLink();
+ if (aLink.GetType() == GfxLinkType::NativeWmf && aLink.GetData() && aLink.GetDataSize())
+ {
+ if(!aLink.IsEMF()) // If WMF, just write directly.
+ return rTargetStream.WriteBytes(aLink.GetData(), aLink.GetDataSize()) == aLink.GetDataSize();
+
+ // This may be an EMF+ file. In EmfReader::ReadEnhWMF() we normally drop non-EMF commands
+ // when reading EMF+, so converting that to WMF is better done by re-parsing with EMF+ disabled.
+ auto & rDataContainer = aLink.getDataContainer();
+ auto aVectorGraphicData
+ = std::make_shared<VectorGraphicData>(rDataContainer, VectorGraphicDataType::Emf);
+ aVectorGraphicData->setEnableEMFPlus(false);
+ Graphic aGraphic(aVectorGraphicData);
+ bool bRet = ConvertGDIMetaFileToWMF(aGraphic.GetGDIMetaFile(), rTargetStream, pConfigItem,
+ bPlaceable);
+ return bRet;
+ }
+
+ bool bRet = ConvertGDIMetaFileToWMF(rGraphic.GetGDIMetaFile(), rTargetStream, pConfigItem,
+ bPlaceable);
+ return bRet;
+}
+
+bool ConvertGDIMetaFileToEMF(const GDIMetaFile & rMTF, SvStream & rTargetStream)
+{
+ EMFWriter aEMFWriter(rTargetStream);
+ GDIMetaFile aGdiMetaFile(rMTF);
+
+ if(usesClipActions(aGdiMetaFile))
+ {
+ // #i121267# It is necessary to prepare the metafile since the export does *not* support
+ // clip regions. This tooling method clips the geometry content of the metafile internally
+ // against its own clip regions, so that the export is safe to ignore clip regions
+ clipMetafileContentAgainstOwnRegions(aGdiMetaFile);
+ }
+
+ return aEMFWriter.WriteEMF(aGdiMetaFile);
+}
+
+bool WriteWindowMetafileBits( SvStream& rStream, const GDIMetaFile& rMTF )
+{
+ return WMFWriter().WriteWMF( rMTF, rStream, nullptr, false );
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/vcl/source/filter/wmf/wmfexternal.cxx b/vcl/source/filter/wmf/wmfexternal.cxx
new file mode 100644
index 000000000..c5616beac
--- /dev/null
+++ b/vcl/source/filter/wmf/wmfexternal.cxx
@@ -0,0 +1,74 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <sal/config.h>
+
+#include <comphelper/propertyvalue.hxx>
+#include <vcl/wmfexternal.hxx>
+#include <com/sun/star/beans/PropertyValue.hpp>
+
+// formally known as WMF_EXTERNALHEADER
+WmfExternal::WmfExternal()
+ : xExt(0)
+ , yExt(0)
+ , mapMode(0)
+{
+}
+
+css::uno::Sequence<css::beans::PropertyValue> WmfExternal::getSequence() const
+{
+ if (0 != xExt || 0 != yExt || 0 != mapMode)
+ {
+ return { comphelper::makePropertyValue("Width", static_cast<sal_Int16>(xExt)),
+ comphelper::makePropertyValue("Height", static_cast<sal_Int16>(yExt)),
+ comphelper::makePropertyValue("MapMode", static_cast<sal_Int16>(mapMode)) };
+ }
+
+ return {};
+}
+
+bool WmfExternal::setSequence(const css::uno::Sequence<css::beans::PropertyValue>& rSequence)
+{
+ bool bRetval(false);
+
+ for (const auto& rPropVal : rSequence)
+ {
+ const OUString aName(rPropVal.Name);
+
+ if (aName == "Width")
+ {
+ rPropVal.Value >>= xExt;
+ bRetval = true;
+ }
+ else if (aName == "Height")
+ {
+ rPropVal.Value >>= yExt;
+ bRetval = true;
+ }
+ else if (aName == "MapMode")
+ {
+ rPropVal.Value >>= mapMode;
+ bRetval = true;
+ }
+ }
+
+ return bRetval;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/vcl/source/filter/wmf/wmfwr.cxx b/vcl/source/filter/wmf/wmfwr.cxx
new file mode 100644
index 000000000..22230ba46
--- /dev/null
+++ b/vcl/source/filter/wmf/wmfwr.cxx
@@ -0,0 +1,1904 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <sal/config.h>
+#include <osl/diagnose.h>
+
+#include <algorithm>
+
+#include "wmfwr.hxx"
+#include "emfwr.hxx"
+#include <rtl/crc.h>
+#include <rtl/tencinfo.h>
+#include <tools/bigint.hxx>
+#include <tools/helpers.hxx>
+#include <tools/tenccvt.hxx>
+#include <tools/fract.hxx>
+#include <tools/stream.hxx>
+#include <vcl/dibtools.hxx>
+#include <vcl/metaact.hxx>
+#include <vcl/FilterConfigItem.hxx>
+#include <basegfx/polygon/b2dpolygon.hxx>
+#include <basegfx/polygon/b2dpolypolygon.hxx>
+#include <memory>
+#include <vcl/fontcharmap.hxx>
+#include <comphelper/sequenceashashmap.hxx>
+
+// MS Windows defines
+
+#define W_META_SETBKMODE 0x0102
+#define W_META_SETROP2 0x0104
+#define W_META_SETSTRETCHBLTMODE 0x0107
+#define W_META_SETTEXTCOLOR 0x0209
+#define W_META_SETWINDOWORG 0x020B
+#define W_META_SETWINDOWEXT 0x020C
+#define W_META_LINETO 0x0213
+#define W_META_MOVETO 0x0214
+#define W_META_INTERSECTCLIPRECT 0x0416
+#define W_META_ARC 0x0817
+#define W_META_ELLIPSE 0x0418
+#define W_META_PIE 0x081A
+#define W_META_RECTANGLE 0x041B
+#define W_META_ROUNDRECT 0x061C
+#define W_META_SAVEDC 0x001E
+#define W_META_SETPIXEL 0x041F
+#define W_META_TEXTOUT 0x0521
+#define W_META_POLYGON 0x0324
+#define W_META_POLYLINE 0x0325
+#define W_META_ESCAPE 0x0626
+#define W_META_RESTOREDC 0x0127
+#define W_META_SELECTOBJECT 0x012D
+#define W_META_SETTEXTALIGN 0x012E
+#define W_META_CHORD 0x0830
+#define W_META_EXTTEXTOUT 0x0a32
+#define W_META_POLYPOLYGON 0x0538
+#define W_META_STRETCHDIB 0x0f43
+#define W_META_DELETEOBJECT 0x01f0
+#define W_META_CREATEPENINDIRECT 0x02FA
+#define W_META_CREATEFONTINDIRECT 0x02FB
+#define W_META_CREATEBRUSHINDIRECT 0x02FC
+
+#define W_TRANSPARENT 1
+#define W_OPAQUE 2
+
+#define W_R2_NOT 6
+#define W_R2_XORPEN 7
+#define W_R2_COPYPEN 13
+
+#define W_TA_NOUPDATECP 0x0000
+#define W_TA_LEFT 0x0000
+#define W_TA_RIGHT 0x0002
+#define W_TA_TOP 0x0000
+#define W_TA_BOTTOM 0x0008
+#define W_TA_BASELINE 0x0018
+#define W_TA_RTLREADING 0x0100
+
+#define W_SRCCOPY 0x00CC0020L
+#define W_SRCPAINT 0x00EE0086L
+#define W_SRCAND 0x008800C6L
+#define W_SRCINVERT 0x00660046L
+#define W_DSTINVERT 0x00550009L
+
+#define W_PS_SOLID 0
+#define W_PS_DASH 1
+#define W_PS_DOT 2
+#define W_PS_DASHDOT 3
+#define W_PS_DASHDOTDOT 4
+#define W_PS_NULL 5
+
+#define W_LF_FACESIZE 32
+
+#define W_ANSI_CHARSET 0
+
+#define W_DEFAULT_PITCH 0x00
+#define W_FIXED_PITCH 0x01
+#define W_VARIABLE_PITCH 0x02
+
+#define W_FF_DONTCARE 0x00
+#define W_FF_ROMAN 0x10
+#define W_FF_SWISS 0x20
+#define W_FF_MODERN 0x30
+#define W_FF_SCRIPT 0x40
+#define W_FF_DECORATIVE 0x50
+
+#define W_FW_DONTCARE 0
+#define W_FW_THIN 100
+#define W_FW_LIGHT 300
+#define W_FW_NORMAL 400
+#define W_FW_MEDIUM 500
+#define W_FW_SEMIBOLD 600
+#define W_FW_BOLD 700
+#define W_FW_ULTRALIGHT 200
+#define W_FW_ULTRABOLD 800
+#define W_FW_BLACK 900
+
+#define W_BS_SOLID 0
+#define W_BS_HOLLOW 1
+
+#define W_MFCOMMENT 15
+
+#define PRIVATE_ESCAPE_UNICODE 2
+
+WMFWriter::WMFWriter()
+ : bStatus(false)
+ , nLastPercent(0)
+ , pWMF(nullptr)
+ , pVirDev(nullptr)
+ , nMetafileHeaderPos(0)
+ , nMaxRecordSize(0)
+ , nActRecordPos(0)
+ , eSrcRasterOp(RasterOp::OverPaint)
+ , eSrcTextAlign(ALIGN_BASELINE)
+ , pAttrStack(nullptr)
+ , eSrcHorTextAlign(W_TA_LEFT)
+ , eDstROP2(RasterOp::OverPaint)
+ , eDstTextAlign(ALIGN_BASELINE)
+ , eDstHorTextAlign(W_TA_LEFT)
+ , bHandleAllocated{}
+ , nDstPenHandle(0)
+ , nDstFontHandle(0)
+ , nDstBrushHandle(0)
+ , nNumberOfActions(0)
+ , nNumberOfBitmaps(0)
+ , nWrittenActions(0)
+ , nWrittenBitmaps(0)
+ , nActBitmapPercent(0)
+ , bEmbedEMF(false)
+{
+}
+
+void WMFWriter::MayCallback()
+{
+ if ( !xStatusIndicator.is() )
+ return;
+
+ sal_uLong nPercent;
+
+ // we simply assume that 16386 actions match to a bitmap
+ // (normally a metafile either contains only actions or some bitmaps and
+ // almost no actions. In which case the ratio is less important)
+
+ nPercent=((nWrittenBitmaps<<14)+(nActBitmapPercent<<14)/100+nWrittenActions)
+ *100
+ /((nNumberOfBitmaps<<14)+nNumberOfActions);
+
+ if ( nPercent >= nLastPercent + 3 )
+ {
+ nLastPercent = nPercent;
+ if( nPercent <= 100 )
+ xStatusIndicator->setValue( nPercent );
+ }
+}
+
+void WMFWriter::CountActionsAndBitmaps( const GDIMetaFile & rMTF )
+{
+ size_t nAction, nActionCount;
+
+ nActionCount = rMTF.GetActionSize();
+
+ for ( nAction=0; nAction < nActionCount; nAction++ )
+ {
+ MetaAction* pMA = rMTF.GetAction( nAction );
+
+ switch( pMA->GetType() )
+ {
+ case MetaActionType::BMP:
+ case MetaActionType::BMPSCALE:
+ case MetaActionType::BMPSCALEPART:
+ case MetaActionType::BMPEX:
+ case MetaActionType::BMPEXSCALE:
+ case MetaActionType::BMPEXSCALEPART:
+ nNumberOfBitmaps++;
+ break;
+ default: break;
+ }
+ nNumberOfActions++;
+ }
+}
+
+void WMFWriter::WritePointXY(const Point & rPoint)
+{
+ Point aPt( OutputDevice::LogicToLogic(rPoint,aSrcMapMode,aTargetMapMode) );
+ pWMF->WriteInt16( aPt.X() ).WriteInt16( aPt.Y() );
+}
+
+void WMFWriter::WritePointYX(const Point & rPoint)
+{
+ Point aPt( OutputDevice::LogicToLogic(rPoint,aSrcMapMode,aTargetMapMode) );
+ pWMF->WriteInt16( aPt.Y() ).WriteInt16( aPt.X() );
+}
+
+sal_Int32 WMFWriter::ScaleWidth( sal_Int32 nDX )
+{
+ Size aSz( OutputDevice::LogicToLogic(Size(nDX,0),aSrcMapMode,aTargetMapMode) );
+ return aSz.Width();
+}
+
+void WMFWriter::WriteSize(const Size & rSize)
+{
+ Size aSz( OutputDevice::LogicToLogic(rSize,aSrcMapMode,aTargetMapMode) );
+ pWMF->WriteInt16( aSz.Width() ).WriteInt16( aSz.Height() );
+}
+
+void WMFWriter::WriteHeightWidth(const Size & rSize)
+{
+ Size aSz( OutputDevice::LogicToLogic(rSize,aSrcMapMode,aTargetMapMode) );
+ pWMF->WriteInt16( aSz.Height() ).WriteInt16( aSz.Width() );
+}
+
+void WMFWriter::WriteRectangle(const tools::Rectangle & rRect)
+{
+ WritePointYX(Point(rRect.Right()+1,rRect.Bottom()+1));
+ WritePointYX(rRect.TopLeft());
+}
+
+void WMFWriter::WriteColor(const Color & rColor)
+{
+ pWMF->WriteUChar( rColor.GetRed() ).WriteUChar( rColor.GetGreen() ).WriteUChar( rColor.GetBlue() ).WriteUChar( 0 );
+}
+
+void WMFWriter::WriteRecordHeader(sal_uInt32 nSizeWords, sal_uInt16 nType)
+{
+ nActRecordPos=pWMF->Tell();
+ if (nSizeWords>nMaxRecordSize) nMaxRecordSize=nSizeWords;
+ pWMF->WriteUInt32( nSizeWords ).WriteUInt16( nType );
+}
+
+void WMFWriter::UpdateRecordHeader()
+{
+ sal_uLong nPos;
+ sal_uInt32 nSize;
+
+ nPos=pWMF->Tell(); nSize=nPos-nActRecordPos;
+ if ((nSize & 1)!=0) {
+ pWMF->WriteUChar( 0 );
+ nPos++; nSize++;
+ }
+ nSize/=2;
+ if (nSize>nMaxRecordSize) nMaxRecordSize=nSize;
+ pWMF->Seek(nActRecordPos);
+ pWMF->WriteUInt32( nSize );
+ pWMF->Seek(nPos);
+}
+
+void WMFWriter::WMFRecord_Arc(const tools::Rectangle & rRect, const Point & rStartPt, const Point & rEndPt)
+{
+ WriteRecordHeader(0x0000000b,W_META_ARC);
+ WritePointYX(rEndPt);
+ WritePointYX(rStartPt);
+ WriteRectangle(rRect);
+}
+
+void WMFWriter::WMFRecord_Chord(const tools::Rectangle & rRect, const Point & rStartPt, const Point & rEndPt)
+{
+ WriteRecordHeader(0x0000000b,W_META_CHORD);
+ WritePointYX(rEndPt);
+ WritePointYX(rStartPt);
+ WriteRectangle(rRect);
+}
+
+void WMFWriter::WMFRecord_CreateBrushIndirect(const Color& rColor)
+{
+ WriteRecordHeader(0x00000007,W_META_CREATEBRUSHINDIRECT);
+
+ if( rColor==COL_TRANSPARENT )
+ pWMF->WriteUInt16( W_BS_HOLLOW );
+ else
+ pWMF->WriteUInt16( W_BS_SOLID );
+
+ WriteColor( rColor );
+ pWMF->WriteUInt16( 0 );
+}
+
+void WMFWriter::WMFRecord_CreateFontIndirect(const vcl::Font & rFont)
+{
+ sal_uInt16 nWeight,i;
+ sal_uInt8 nPitchFamily;
+
+ WriteRecordHeader(0x00000000,W_META_CREATEFONTINDIRECT);
+ WriteHeightWidth(Size(rFont.GetFontSize().Width(),-rFont.GetFontSize().Height()));
+ pWMF->WriteInt16( rFont.GetOrientation().get() ).WriteInt16( rFont.GetOrientation().get() );
+
+ switch (rFont.GetWeight()) {
+ case WEIGHT_THIN: nWeight=W_FW_THIN; break;
+ case WEIGHT_ULTRALIGHT: nWeight=W_FW_ULTRALIGHT; break;
+ case WEIGHT_LIGHT: nWeight=W_FW_LIGHT; break;
+ case WEIGHT_SEMILIGHT: nWeight=W_FW_LIGHT; break;
+ case WEIGHT_NORMAL: nWeight=W_FW_NORMAL; break;
+ case WEIGHT_MEDIUM: nWeight=W_FW_MEDIUM; break;
+ case WEIGHT_SEMIBOLD: nWeight=W_FW_SEMIBOLD; break;
+ case WEIGHT_BOLD: nWeight=W_FW_BOLD; break;
+ case WEIGHT_ULTRABOLD: nWeight=W_FW_ULTRABOLD; break;
+ case WEIGHT_BLACK: nWeight=W_FW_BLACK; break;
+ default: nWeight=W_FW_DONTCARE;
+ }
+ pWMF->WriteUInt16( nWeight );
+
+ if (rFont.GetItalic()==ITALIC_NONE) pWMF->WriteUChar( 0 ); else pWMF->WriteUChar( 1 );
+ if (rFont.GetUnderline()==LINESTYLE_NONE) pWMF->WriteUChar( 0 ); else pWMF->WriteUChar( 1 );
+ if (rFont.GetStrikeout()==STRIKEOUT_NONE) pWMF->WriteUChar( 0 ); else pWMF->WriteUChar( 1 );
+
+ rtl_TextEncoding eFontNameEncoding = rFont.GetCharSet();
+ sal_uInt8 nCharSet = rtl_getBestWindowsCharsetFromTextEncoding( eFontNameEncoding );
+ if ( eFontNameEncoding == RTL_TEXTENCODING_SYMBOL )
+ eFontNameEncoding = RTL_TEXTENCODING_MS_1252;
+ if ( nCharSet == 1 )
+ nCharSet = W_ANSI_CHARSET;
+ pWMF->WriteUChar( nCharSet );
+
+ pWMF->WriteUChar( 0 ).WriteUChar( 0 ).WriteUChar( 0 );
+
+ switch (rFont.GetPitch()) {
+ case PITCH_FIXED: nPitchFamily=W_FIXED_PITCH; break;
+ case PITCH_VARIABLE: nPitchFamily=W_VARIABLE_PITCH; break;
+ default: nPitchFamily=W_DEFAULT_PITCH;
+ }
+ switch (rFont.GetFamilyType()) {
+ case FAMILY_DECORATIVE: nPitchFamily|=W_FF_DECORATIVE; break;
+ case FAMILY_MODERN: nPitchFamily|=W_FF_MODERN; break;
+ case FAMILY_ROMAN: nPitchFamily|=W_FF_ROMAN; break;
+ case FAMILY_SCRIPT: nPitchFamily|=W_FF_SCRIPT; break;
+ case FAMILY_SWISS: nPitchFamily|=W_FF_SWISS; break;
+ default: nPitchFamily|=W_FF_DONTCARE;
+ }
+ pWMF->WriteUChar( nPitchFamily );
+
+ OString aFontName(OUStringToOString(rFont.GetFamilyName(), eFontNameEncoding));
+ for ( i = 0; i < W_LF_FACESIZE; i++ )
+ {
+ char nChar = ( i < aFontName.getLength() ) ? aFontName[i] : 0;
+ pWMF->WriteChar( nChar );
+ }
+ UpdateRecordHeader();
+}
+
+void WMFWriter::WMFRecord_CreatePenIndirect(const Color& rColor, const LineInfo& rLineInfo )
+{
+ WriteRecordHeader(0x00000008,W_META_CREATEPENINDIRECT);
+ sal_uInt16 nStyle = rColor == COL_TRANSPARENT ? W_PS_NULL : W_PS_SOLID;
+ switch( rLineInfo.GetStyle() )
+ {
+ case LineStyle::Dash :
+ {
+ if ( rLineInfo.GetDotCount() )
+ {
+ if ( !rLineInfo.GetDashCount() )
+ nStyle = W_PS_DOT;
+ else
+ {
+ if ( rLineInfo.GetDotCount() == 1 )
+ nStyle = W_PS_DASHDOT;
+ else
+ nStyle = W_PS_DASHDOTDOT;
+ }
+ }
+ else
+ nStyle = W_PS_DASH;
+ }
+ break;
+ case LineStyle::NONE :
+ nStyle = W_PS_NULL;
+ break;
+ default:
+ break;
+ }
+ pWMF->WriteUInt16( nStyle );
+
+ WriteSize( Size( rLineInfo.GetWidth(), 0 ) );
+ WriteColor( rColor );
+}
+
+void WMFWriter::WMFRecord_DeleteObject(sal_uInt16 nObjectHandle)
+{
+ WriteRecordHeader(0x00000004,W_META_DELETEOBJECT);
+ pWMF->WriteUInt16( nObjectHandle );
+}
+
+void WMFWriter::WMFRecord_Ellipse(const tools::Rectangle & rRect)
+{
+ WriteRecordHeader(0x00000007,W_META_ELLIPSE);
+ WriteRectangle(rRect);
+}
+
+void WMFWriter::WMFRecord_Escape( sal_uInt32 nEsc, sal_uInt32 nLen, const sal_Int8* pData )
+{
+#ifdef OSL_BIGENDIAN
+ sal_uInt32 nTmp = OSL_SWAPDWORD( nEsc );
+ sal_uInt32 nCheckSum = rtl_crc32( 0, &nTmp, 4 );
+#else
+ sal_uInt32 nCheckSum = rtl_crc32( 0, &nEsc, 4 );
+#endif
+ if ( nLen )
+ nCheckSum = rtl_crc32( nCheckSum, pData, nLen );
+
+ WriteRecordHeader( 3 + 9 + ( ( nLen + 1 ) >> 1 ), W_META_ESCAPE );
+ pWMF->WriteUInt16( W_MFCOMMENT )
+ .WriteUInt16( nLen + 14 ) // we will always have a fourteen byte escape header:
+ .WriteUInt16( 0x4f4f ) // OO
+ .WriteUInt32( 0xa2c2a ) // evil magic number
+ .WriteUInt32( nCheckSum ) // crc32 checksum about nEsc & pData
+ .WriteUInt32( nEsc ); // escape number
+ pWMF->WriteBytes( pData, nLen );
+ if ( nLen & 1 )
+ pWMF->WriteUChar( 0 ); // pad byte
+}
+
+/* if return value is true, then a complete unicode string and also a polygon replacement has been written,
+ so there is no more action necessary
+*/
+bool WMFWriter::WMFRecord_Escape_Unicode( const Point& rPoint, const OUString& rUniStr, o3tl::span<const sal_Int32> pDXAry )
+{
+ bool bEscapeUsed = false;
+
+ sal_uInt32 i, nStringLen = rUniStr.getLength();
+ if ( nStringLen )
+ {
+ // first we will check if a comment is necessary
+ if ( aSrcFont.GetCharSet() != RTL_TEXTENCODING_SYMBOL ) // symbol is always byte character, so there is no unicode loss
+ {
+ const sal_Unicode* pBuf = rUniStr.getStr();
+ const rtl_TextEncoding aTextEncodingOrg = aSrcFont.GetCharSet();
+ OString aByteStr(OUStringToOString(rUniStr, aTextEncodingOrg));
+ OUString aUniStr2(OStringToOUString(aByteStr, aTextEncodingOrg));
+ const sal_Unicode* pConversion = aUniStr2.getStr(); // this is the unicode array after bytestring <-> unistring conversion
+ for ( i = 0; i < nStringLen; i++ )
+ {
+ if ( *pBuf++ != *pConversion++ )
+ break;
+ }
+
+ if ( i != nStringLen ) // after conversion the characters are not original,
+ { // try again, with determining a better charset from unicode char
+ pBuf = rUniStr.getStr();
+ const sal_Unicode* pCheckChar = pBuf;
+ rtl_TextEncoding aTextEncoding = getBestMSEncodingByChar(*pCheckChar); // try the first character
+ if (aTextEncoding == RTL_TEXTENCODING_DONTKNOW) {
+ aTextEncoding = aTextEncodingOrg;
+ }
+ for ( i = 1; i < nStringLen; i++)
+ {
+ if (aTextEncoding != aTextEncodingOrg) // found something
+ break;
+ pCheckChar++;
+ aTextEncoding = getBestMSEncodingByChar(*pCheckChar); // try the next character
+ if (aTextEncoding == RTL_TEXTENCODING_DONTKNOW) {
+ aTextEncoding = aTextEncodingOrg;
+ }
+ }
+
+ aByteStr = OUStringToOString(rUniStr, aTextEncoding);
+ aUniStr2 = OStringToOUString(aByteStr, aTextEncoding);
+ pConversion = aUniStr2.getStr(); // this is the unicode array after bytestring <-> unistring conversion
+ for ( i = 0; i < nStringLen; i++ )
+ {
+ if ( *pBuf++ != *pConversion++ )
+ break;
+ }
+ if (i == nStringLen)
+ {
+ aSrcFont.SetCharSet (aTextEncoding);
+ SetAllAttr();
+ }
+ }
+
+ if ( ( i != nStringLen ) || IsStarSymbol( aSrcFont.GetFamilyName() ) ) // after conversion the characters are not original, so we
+ { // will store the unicode string and a polypoly replacement
+ Color aOldFillColor( aSrcFillColor );
+ Color aOldLineColor( aSrcLineColor );
+ aSrcLineInfo = LineInfo();
+ aSrcFillColor = aSrcTextColor;
+ aSrcLineColor = COL_TRANSPARENT;
+ SetLineAndFillAttr();
+ pVirDev->SetFont( aSrcFont );
+ std::vector<tools::PolyPolygon> aPolyPolyVec;
+ if ( pVirDev->GetTextOutlines( aPolyPolyVec, rUniStr ) )
+ {
+ sal_uInt32 nDXCount = !pDXAry.empty() ? nStringLen : 0;
+ sal_uInt32 nSkipActions = aPolyPolyVec.size();
+ sal_Int32 nStrmLen = 8 +
+ + sizeof( nStringLen ) + ( nStringLen * 2 )
+ + sizeof( nDXCount ) + ( nDXCount * 4 )
+ + sizeof( nSkipActions );
+
+ SvMemoryStream aMemoryStream( nStrmLen );
+ Point aPt( OutputDevice::LogicToLogic( rPoint, aSrcMapMode, aTargetMapMode ) );
+ aMemoryStream.WriteInt32( aPt.X() )
+ .WriteInt32( aPt.Y() )
+ .WriteUInt32( nStringLen );
+ for ( i = 0; i < nStringLen; i++ )
+ aMemoryStream.WriteUInt16( rUniStr[ i ] );
+ aMemoryStream.WriteUInt32( nDXCount );
+ for ( i = 0; i < nDXCount; i++ )
+ aMemoryStream.WriteInt32( pDXAry[ i ] );
+ aMemoryStream.WriteUInt32( nSkipActions );
+ WMFRecord_Escape( PRIVATE_ESCAPE_UNICODE, nStrmLen, static_cast<const sal_Int8*>(aMemoryStream.GetData()) );
+
+ for ( const auto& rPolyPoly : aPolyPolyVec )
+ {
+ tools::PolyPolygon aPolyPoly( rPolyPoly );
+ aPolyPoly.Move( rPoint.X(), rPoint.Y() );
+ WMFRecord_PolyPolygon( aPolyPoly );
+ }
+ aSrcFillColor = aOldFillColor;
+ aSrcLineColor = aOldLineColor;
+ bEscapeUsed = true;
+ }
+ }
+ }
+ }
+ return bEscapeUsed;
+}
+
+void WMFWriter::WMFRecord_ExtTextOut( const Point& rPoint,
+ const OUString& rString,
+ o3tl::span<const sal_Int32> pDXAry )
+{
+ sal_Int32 nOriginalTextLen = rString.getLength();
+
+ if ( (nOriginalTextLen <= 1) || pDXAry.empty() )
+ {
+ WMFRecord_TextOut(rPoint, rString);
+ return;
+ }
+ rtl_TextEncoding eChrSet = aSrcFont.GetCharSet();
+ OString aByteString(OUStringToOString(rString, eChrSet));
+ TrueExtTextOut(rPoint, rString, aByteString, pDXAry);
+}
+
+void WMFWriter::TrueExtTextOut( const Point& rPoint, const OUString& rString,
+ const OString& rByteString, o3tl::span<const sal_Int32> pDXAry )
+{
+ WriteRecordHeader( 0, W_META_EXTTEXTOUT );
+ WritePointYX( rPoint );
+ sal_uInt16 nNewTextLen = static_cast<sal_uInt16>(rByteString.getLength());
+ pWMF->WriteUInt16( nNewTextLen ).WriteUInt16( 0 );
+ write_uInt8s_FromOString(*pWMF, rByteString, nNewTextLen);
+ if ( nNewTextLen & 1 )
+ pWMF->WriteUChar( 0 );
+
+ sal_Int32 nOriginalTextLen = rString.getLength();
+ std::unique_ptr<sal_Int16[]> pConvertedDXAry(new sal_Int16[ nOriginalTextLen ]);
+ sal_Int32 j = 0;
+ pConvertedDXAry[ j++ ] = static_cast<sal_Int16>(ScaleWidth( pDXAry[ 0 ] ));
+ for (sal_Int32 i = 1; i < ( nOriginalTextLen - 1 ); ++i)
+ pConvertedDXAry[ j++ ] = static_cast<sal_Int16>(ScaleWidth( pDXAry[ i ] - pDXAry[ i - 1 ] ));
+ pConvertedDXAry[ j ] = static_cast<sal_Int16>(ScaleWidth( pDXAry[ nOriginalTextLen - 2 ] / ( nOriginalTextLen - 1 ) ));
+
+ for (sal_Int32 i = 0; i < nOriginalTextLen; ++i)
+ {
+ sal_Int16 nDx = pConvertedDXAry[ i ];
+ pWMF->WriteInt16( nDx );
+ if ( nOriginalTextLen < nNewTextLen )
+ {
+ sal_Unicode nUniChar = rString[i];
+ OString aTemp(&nUniChar, 1, aSrcFont.GetCharSet());
+ j = aTemp.getLength();
+ while ( --j > 0 )
+ pWMF->WriteUInt16( 0 );
+ }
+ }
+ pConvertedDXAry.reset();
+ UpdateRecordHeader();
+}
+
+void WMFWriter::WMFRecord_LineTo(const Point & rPoint)
+{
+ WriteRecordHeader(0x00000005,W_META_LINETO);
+ WritePointYX(rPoint);
+}
+
+void WMFWriter::WMFRecord_MoveTo(const Point & rPoint)
+{
+ WriteRecordHeader(0x00000005,W_META_MOVETO);
+ WritePointYX(rPoint);
+}
+
+void WMFWriter::WMFRecord_Pie(const tools::Rectangle & rRect, const Point & rStartPt, const Point & rEndPt)
+{
+ WriteRecordHeader(0x0000000b,W_META_PIE);
+ WritePointYX(rEndPt);
+ WritePointYX(rStartPt);
+ WriteRectangle(rRect);
+}
+
+void WMFWriter::WMFRecord_Polygon(const tools::Polygon & rPoly)
+{
+ tools::Polygon aSimplePoly;
+ if ( rPoly.HasFlags() )
+ rPoly.AdaptiveSubdivide( aSimplePoly );
+ else
+ aSimplePoly = rPoly;
+ const sal_uInt16 nSize = aSimplePoly.GetSize();
+ WriteRecordHeader(static_cast<sal_uInt32>(nSize)*2+4,W_META_POLYGON);
+ pWMF->WriteUInt16( nSize );
+ for (sal_uInt16 i=0; i<nSize; ++i)
+ WritePointXY(aSimplePoly.GetPoint(i));
+}
+
+void WMFWriter::WMFRecord_PolyLine(const tools::Polygon & rPoly)
+{
+ tools::Polygon aSimplePoly;
+ if ( rPoly.HasFlags() )
+ rPoly.AdaptiveSubdivide( aSimplePoly );
+ else
+ aSimplePoly = rPoly;
+ const sal_uInt16 nSize = aSimplePoly.GetSize();
+ WriteRecordHeader(static_cast<sal_uInt32>(nSize)*2+4,W_META_POLYLINE);
+ pWMF->WriteUInt16( nSize );
+ for (sal_uInt16 i=0; i<nSize; ++i)
+ WritePointXY(aSimplePoly.GetPoint(i));
+}
+
+void WMFWriter::WMFRecord_PolyPolygon(const tools::PolyPolygon & rPolyPoly)
+{
+ const tools::Polygon * pPoly;
+ sal_uInt16 nCount,nSize,i,j;
+
+ nCount=rPolyPoly.Count();
+ tools::PolyPolygon aSimplePolyPoly( rPolyPoly );
+ for ( i = 0; i < nCount; i++ )
+ {
+ if ( aSimplePolyPoly[ i ].HasFlags() )
+ {
+ tools::Polygon aSimplePoly;
+ aSimplePolyPoly[ i ].AdaptiveSubdivide( aSimplePoly );
+ aSimplePolyPoly[ i ] = aSimplePoly;
+ }
+ }
+ WriteRecordHeader(0,W_META_POLYPOLYGON);
+ pWMF->WriteUInt16( nCount );
+ for (i=0; i<nCount; i++) pWMF->WriteUInt16( aSimplePolyPoly.GetObject(i).GetSize() );
+ for (i=0; i<nCount; i++) {
+ pPoly=&(aSimplePolyPoly.GetObject(i));
+ nSize=pPoly->GetSize();
+ for (j=0; j<nSize; j++) WritePointXY(pPoly->GetPoint(j));
+ }
+ UpdateRecordHeader();
+}
+
+void WMFWriter::WMFRecord_Rectangle(const tools::Rectangle & rRect)
+{
+ WriteRecordHeader( 0x00000007,W_META_RECTANGLE );
+ WriteRectangle( rRect );
+}
+
+void WMFWriter::WMFRecord_RestoreDC()
+{
+ WriteRecordHeader(0x00000004,W_META_RESTOREDC);
+ pWMF->WriteInt16( -1 );
+}
+
+void WMFWriter::WMFRecord_RoundRect(const tools::Rectangle & rRect, tools::Long nHorzRound, tools::Long nVertRound)
+{
+ WriteRecordHeader(0x00000009,W_META_ROUNDRECT);
+ WriteHeightWidth(Size(nHorzRound,nVertRound));
+ WriteRectangle(rRect);
+}
+
+void WMFWriter::WMFRecord_SaveDC()
+{
+ WriteRecordHeader(0x00000003,W_META_SAVEDC);
+}
+
+void WMFWriter::WMFRecord_SelectObject(sal_uInt16 nObjectHandle)
+{
+ WriteRecordHeader(0x00000004,W_META_SELECTOBJECT);
+ pWMF->WriteUInt16( nObjectHandle );
+}
+
+void WMFWriter::WMFRecord_SetBkMode(bool bTransparent)
+{
+ WriteRecordHeader(0x00000004,W_META_SETBKMODE);
+ if (bTransparent) pWMF->WriteUInt16( W_TRANSPARENT );
+ else pWMF->WriteUInt16( W_OPAQUE );
+}
+
+void WMFWriter::WMFRecord_SetStretchBltMode()
+{
+ WriteRecordHeader( 0x00000004, W_META_SETSTRETCHBLTMODE );
+ pWMF->WriteUInt16( 3 ); // STRETCH_DELETESCANS
+}
+
+void WMFWriter::WMFRecord_SetPixel(const Point & rPoint, const Color & rColor)
+{
+ WriteRecordHeader(0x00000007,W_META_SETPIXEL);
+ WriteColor(rColor);
+ WritePointYX(rPoint);
+}
+
+void WMFWriter::WMFRecord_SetROP2(RasterOp eROP)
+{
+ sal_uInt16 nROP2;
+
+ switch (eROP) {
+ case RasterOp::Invert: nROP2=W_R2_NOT; break;
+ case RasterOp::Xor: nROP2=W_R2_XORPEN; break;
+ default: nROP2=W_R2_COPYPEN;
+ }
+ WriteRecordHeader(0x00000004,W_META_SETROP2);
+ pWMF->WriteUInt16( nROP2 );
+}
+
+void WMFWriter::WMFRecord_SetTextAlign(TextAlign eFontAlign, sal_uInt16 eHorTextAlign)
+{
+ sal_uInt16 nAlign;
+
+ switch (eFontAlign) {
+ case ALIGN_TOP: nAlign=W_TA_TOP; break;
+ case ALIGN_BOTTOM: nAlign=W_TA_BOTTOM; break;
+ default: nAlign=W_TA_BASELINE;
+ }
+ nAlign|=eHorTextAlign;
+ nAlign|=W_TA_NOUPDATECP;
+
+ WriteRecordHeader(0x00000004,W_META_SETTEXTALIGN);
+ pWMF->WriteUInt16( nAlign );
+}
+
+void WMFWriter::WMFRecord_SetTextColor(const Color & rColor)
+{
+ WriteRecordHeader(0x00000005,W_META_SETTEXTCOLOR);
+ WriteColor(rColor);
+}
+
+void WMFWriter::WMFRecord_SetWindowExt(const Size & rSize)
+{
+ WriteRecordHeader(0x00000005,W_META_SETWINDOWEXT);
+ WriteHeightWidth(rSize);
+}
+
+void WMFWriter::WMFRecord_SetWindowOrg(const Point & rPoint)
+{
+ WriteRecordHeader(0x00000005,W_META_SETWINDOWORG);
+ WritePointYX(rPoint);
+}
+
+void WMFWriter::WMFRecord_StretchDIB( const Point & rPoint, const Size & rSize,
+ const Bitmap & rBitmap, sal_uInt32 nROP )
+{
+ sal_uLong nPosAnf,nPosEnd;
+
+ nActBitmapPercent=50;
+ MayCallback();
+
+ WriteRecordHeader(0x00000000,W_META_STRETCHDIB);
+
+ // The sequence in the metafile should be:
+ // some parameters (length 22), then the bitmap without FILEHEADER.
+ // As *pWMF << rBitmap generates a FILEHEADER of size 14,
+ // we first write the bitmap at the right position
+ // and overwrite later the FILEHEADER with the parameters.
+ nPosAnf=pWMF->Tell(); // remember position, where parameters should be stored
+ pWMF->WriteInt32( 0 ).WriteInt32( 0 ); // replenish 8 bytes (these 8 bytes +
+ // 14 bytes superfluous FILEHEADER
+ // = 22 bytes parameter)
+
+ // write bitmap
+ WriteDIB(rBitmap, *pWMF, false, true);
+
+ // write the parameters:
+ nPosEnd=pWMF->Tell();
+ pWMF->Seek(nPosAnf);
+
+ // determine raster-op, if nothing was passed
+ if( !nROP )
+ {
+ switch( eSrcRasterOp )
+ {
+ case RasterOp::Invert: nROP = W_DSTINVERT; break;
+ case RasterOp::Xor: nROP = W_SRCINVERT; break;
+ default: nROP = W_SRCCOPY;
+ }
+ }
+
+ pWMF->WriteUInt32( nROP ).
+ WriteInt16( 0 ).
+ WriteInt16( rBitmap.GetSizePixel().Height() ).
+ WriteInt16( rBitmap.GetSizePixel().Width() ).
+ WriteInt16( 0 ).
+ WriteInt16( 0 );
+
+ WriteHeightWidth(rSize);
+ WritePointYX(rPoint);
+ pWMF->Seek(nPosEnd);
+
+ UpdateRecordHeader();
+
+ nWrittenBitmaps++;
+ nActBitmapPercent=0;
+}
+
+void WMFWriter::WMFRecord_TextOut(const Point & rPoint, std::u16string_view rStr)
+{
+ rtl_TextEncoding eChrSet = aSrcFont.GetCharSet();
+ OString aString(OUStringToOString(rStr, eChrSet));
+ TrueTextOut(rPoint, aString);
+}
+
+void WMFWriter::TrueTextOut(const Point & rPoint, const OString& rString)
+{
+ WriteRecordHeader(0,W_META_TEXTOUT);
+
+ write_uInt16_lenPrefixed_uInt8s_FromOString(*pWMF, rString);
+ sal_Int32 nLen = rString.getLength();
+ if ((nLen&1)!=0) pWMF->WriteUChar( 0 );
+ WritePointYX(rPoint);
+ UpdateRecordHeader();
+}
+
+void WMFWriter::WMFRecord_IntersectClipRect( const tools::Rectangle& rRect )
+{
+ WriteRecordHeader( 0x00000007, W_META_INTERSECTCLIPRECT );
+ WriteRectangle(rRect);
+}
+
+sal_uInt16 WMFWriter::AllocHandle()
+{
+ sal_uInt16 i;
+
+ for (i=0; i<MAXOBJECTHANDLES; i++) {
+ if (!bHandleAllocated[i]) {
+ bHandleAllocated[i]=true;
+ return i;
+ }
+ }
+ bStatus=false;
+ return 0xffff;
+}
+
+void WMFWriter::FreeHandle(sal_uInt16 nObjectHandle)
+{
+ if (nObjectHandle<MAXOBJECTHANDLES) bHandleAllocated[nObjectHandle]=false;
+}
+
+void WMFWriter::CreateSelectDeletePen( const Color& rColor, const LineInfo& rLineInfo )
+{
+ sal_uInt16 nOldHandle;
+
+ nOldHandle=nDstPenHandle;
+ nDstPenHandle=AllocHandle();
+ WMFRecord_CreatePenIndirect( rColor, rLineInfo );
+ WMFRecord_SelectObject(nDstPenHandle);
+ if (nOldHandle<MAXOBJECTHANDLES) {
+ WMFRecord_DeleteObject(nOldHandle);
+ FreeHandle(nOldHandle);
+ }
+}
+
+void WMFWriter::CreateSelectDeleteFont(const vcl::Font & rFont)
+{
+ sal_uInt16 nOldHandle;
+
+ nOldHandle=nDstFontHandle;
+ nDstFontHandle=AllocHandle();
+ WMFRecord_CreateFontIndirect(rFont);
+ WMFRecord_SelectObject(nDstFontHandle);
+ if (nOldHandle<MAXOBJECTHANDLES) {
+ WMFRecord_DeleteObject(nOldHandle);
+ FreeHandle(nOldHandle);
+ }
+}
+
+void WMFWriter::CreateSelectDeleteBrush(const Color& rColor)
+{
+ sal_uInt16 nOldHandle;
+
+ nOldHandle=nDstBrushHandle;
+ nDstBrushHandle=AllocHandle();
+ WMFRecord_CreateBrushIndirect(rColor);
+ WMFRecord_SelectObject(nDstBrushHandle);
+ if (nOldHandle<MAXOBJECTHANDLES) {
+ WMFRecord_DeleteObject(nOldHandle);
+ FreeHandle(nOldHandle);
+ }
+}
+
+void WMFWriter::SetLineAndFillAttr()
+{
+ if ( eDstROP2 != eSrcRasterOp )
+ {
+ eDstROP2=eSrcRasterOp;
+ WMFRecord_SetROP2(eDstROP2);
+ }
+ if ( ( aDstLineColor != aSrcLineColor ) || ( aDstLineInfo != aSrcLineInfo ) )
+ {
+ aDstLineColor = aSrcLineColor;
+ aDstLineInfo = aSrcLineInfo;
+ CreateSelectDeletePen( aDstLineColor, aDstLineInfo );
+ }
+ if ( aDstFillColor != aSrcFillColor )
+ {
+ aDstFillColor = aSrcFillColor;
+ CreateSelectDeleteBrush( aDstFillColor );
+ }
+}
+
+void WMFWriter::SetAllAttr()
+{
+ SetLineAndFillAttr();
+ if ( aDstTextColor != aSrcTextColor )
+ {
+ aDstTextColor = aSrcTextColor;
+ WMFRecord_SetTextColor(aDstTextColor);
+ }
+ if ( eDstTextAlign != eSrcTextAlign || eDstHorTextAlign != eSrcHorTextAlign )
+ {
+ eDstTextAlign = eSrcTextAlign;
+ eDstHorTextAlign = eSrcHorTextAlign;
+ WMFRecord_SetTextAlign( eDstTextAlign, eDstHorTextAlign );
+ }
+ if ( aDstFont == aSrcFont )
+ return;
+
+ pVirDev->SetFont(aSrcFont);
+ if ( aDstFont.GetFamilyName() != aSrcFont.GetFamilyName() )
+ {
+ FontCharMapRef xFontCharMap;
+ if ( pVirDev->GetFontCharMap( xFontCharMap ) )
+ {
+ if ( ( xFontCharMap->GetFirstChar() & 0xff00 ) == 0xf000 )
+ aSrcFont.SetCharSet( RTL_TEXTENCODING_SYMBOL );
+ else if ( aSrcFont.GetCharSet() == RTL_TEXTENCODING_SYMBOL )
+ aSrcFont.SetCharSet( RTL_TEXTENCODING_MS_1252 );
+ }
+ }
+
+ aDstFont = aSrcFont;
+ CreateSelectDeleteFont(aDstFont);
+}
+
+void WMFWriter::HandleLineInfoPolyPolygons(const LineInfo& rInfo, const basegfx::B2DPolygon& rLinePolygon)
+{
+ if(!rLinePolygon.count())
+ return;
+
+ basegfx::B2DPolyPolygon aLinePolyPolygon(rLinePolygon);
+ basegfx::B2DPolyPolygon aFillPolyPolygon;
+
+ rInfo.applyToB2DPolyPolygon(aLinePolyPolygon, aFillPolyPolygon);
+
+ if(aLinePolyPolygon.count())
+ {
+ aSrcLineInfo = rInfo;
+ SetLineAndFillAttr();
+
+ for(auto const& rB2DPolygon : std::as_const(aLinePolyPolygon))
+ {
+ WMFRecord_PolyLine( tools::Polygon(rB2DPolygon) );
+ }
+ }
+
+ if(!aFillPolyPolygon.count())
+ return;
+
+ const Color aOldLineColor(aSrcLineColor);
+ const Color aOldFillColor(aSrcFillColor);
+
+ aSrcLineColor = COL_TRANSPARENT;
+ aSrcFillColor = aOldLineColor;
+ SetLineAndFillAttr();
+
+ for(auto const& rB2DPolygon : std::as_const(aFillPolyPolygon))
+ {
+ WMFRecord_Polygon( tools::Polygon(rB2DPolygon) );
+ }
+
+ aSrcLineColor = aOldLineColor;
+ aSrcFillColor = aOldFillColor;
+ SetLineAndFillAttr();
+}
+
+void WMFWriter::WriteRecords( const GDIMetaFile & rMTF )
+{
+ if( !bStatus )
+ return;
+
+ size_t nACount = rMTF.GetActionSize();
+
+ WMFRecord_SetStretchBltMode();
+
+ for( size_t nA = 0; nA < nACount; nA++ )
+ {
+ MetaAction* pMA = rMTF.GetAction( nA );
+
+ switch( pMA->GetType() )
+ {
+ case MetaActionType::PIXEL:
+ {
+ const MetaPixelAction* pA = static_cast<const MetaPixelAction *>(pMA);
+ aSrcLineInfo = LineInfo();
+ SetLineAndFillAttr();
+ WMFRecord_SetPixel( pA->GetPoint(), pA->GetColor() );
+ }
+ break;
+
+ case MetaActionType::POINT:
+ {
+ const MetaPointAction* pA = static_cast<const MetaPointAction*>(pMA);
+ const Point& rPt = pA->GetPoint();
+ aSrcLineInfo = LineInfo();
+ SetLineAndFillAttr();
+ WMFRecord_MoveTo( rPt);
+ WMFRecord_LineTo( rPt );
+ }
+ break;
+
+ case MetaActionType::LINE:
+ {
+ const MetaLineAction* pA = static_cast<const MetaLineAction *>(pMA);
+ if(pA->GetLineInfo().IsDefault())
+ {
+ aSrcLineInfo = pA->GetLineInfo();
+ SetLineAndFillAttr();
+ WMFRecord_MoveTo( pA->GetStartPoint() );
+ WMFRecord_LineTo( pA->GetEndPoint() );
+ }
+ else
+ {
+ // LineInfo used; handle Dash/Dot and fat lines
+ basegfx::B2DPolygon aPolygon;
+ aPolygon.append(basegfx::B2DPoint(pA->GetStartPoint().X(), pA->GetStartPoint().Y()));
+ aPolygon.append(basegfx::B2DPoint(pA->GetEndPoint().X(), pA->GetEndPoint().Y()));
+ HandleLineInfoPolyPolygons(pA->GetLineInfo(), aPolygon);
+ }
+ }
+ break;
+
+ case MetaActionType::RECT:
+ {
+ const MetaRectAction* pA = static_cast<const MetaRectAction*>(pMA);
+ aSrcLineInfo = LineInfo();
+ SetLineAndFillAttr();
+ WMFRecord_Rectangle( pA->GetRect() );
+ }
+ break;
+
+ case MetaActionType::ROUNDRECT:
+ {
+ const MetaRoundRectAction* pA = static_cast<const MetaRoundRectAction*>(pMA);
+ aSrcLineInfo = LineInfo();
+ SetLineAndFillAttr();
+ WMFRecord_RoundRect( pA->GetRect(), pA->GetHorzRound(), pA->GetVertRound() );
+ }
+ break;
+
+ case MetaActionType::ELLIPSE:
+ {
+ const MetaEllipseAction* pA = static_cast<const MetaEllipseAction*>(pMA);
+ aSrcLineInfo = LineInfo();
+ SetLineAndFillAttr();
+ WMFRecord_Ellipse( pA->GetRect() );
+ }
+ break;
+
+ case MetaActionType::ARC:
+ {
+ const MetaArcAction* pA = static_cast<const MetaArcAction*>(pMA);
+ aSrcLineInfo = LineInfo();
+ SetLineAndFillAttr();
+ WMFRecord_Arc( pA->GetRect(),pA->GetStartPoint(),pA->GetEndPoint() );
+ }
+ break;
+
+ case MetaActionType::PIE:
+ {
+ const MetaPieAction* pA = static_cast<const MetaPieAction*>(pMA);
+ aSrcLineInfo = LineInfo();
+ SetLineAndFillAttr();
+ WMFRecord_Pie( pA->GetRect(), pA->GetStartPoint(), pA->GetEndPoint() );
+ }
+ break;
+
+ case MetaActionType::CHORD:
+ {
+ const MetaChordAction* pA = static_cast<const MetaChordAction*>(pMA);
+ aSrcLineInfo = LineInfo();
+ SetLineAndFillAttr();
+ WMFRecord_Chord( pA->GetRect(), pA->GetStartPoint(), pA->GetEndPoint() );
+ }
+ break;
+
+ case MetaActionType::POLYLINE:
+ {
+ const MetaPolyLineAction* pA = static_cast<const MetaPolyLineAction*>(pMA);
+ const tools::Polygon& rPoly = pA->GetPolygon();
+
+ if( rPoly.GetSize() )
+ {
+ if(pA->GetLineInfo().IsDefault())
+ {
+ aSrcLineInfo = pA->GetLineInfo();
+ SetLineAndFillAttr();
+ WMFRecord_PolyLine( rPoly );
+ }
+ else
+ {
+ // LineInfo used; handle Dash/Dot and fat lines
+ HandleLineInfoPolyPolygons(pA->GetLineInfo(), rPoly.getB2DPolygon());
+ }
+ }
+ }
+ break;
+
+ case MetaActionType::POLYGON:
+ {
+ const MetaPolygonAction* pA = static_cast<const MetaPolygonAction*>(pMA);
+ aSrcLineInfo = LineInfo();
+ SetLineAndFillAttr();
+ WMFRecord_Polygon( pA->GetPolygon() );
+ }
+ break;
+
+ case MetaActionType::POLYPOLYGON:
+ {
+ const MetaPolyPolygonAction* pA = static_cast<const MetaPolyPolygonAction*>(pMA);
+ aSrcLineInfo = LineInfo();
+ SetLineAndFillAttr();
+ WMFRecord_PolyPolygon( pA->GetPolyPolygon() );
+ }
+ break;
+
+ case MetaActionType::TEXTRECT:
+ {
+ const MetaTextRectAction * pA = static_cast<const MetaTextRectAction*>(pMA);
+ OUString aTemp( pA->GetText() );
+ aSrcLineInfo = LineInfo();
+ SetAllAttr();
+
+ Point aPos( pA->GetRect().TopLeft() );
+ if ( !WMFRecord_Escape_Unicode( aPos, aTemp, {} ) )
+ WMFRecord_TextOut( aPos, aTemp );
+ }
+ break;
+
+ case MetaActionType::TEXT:
+ {
+ const MetaTextAction * pA = static_cast<const MetaTextAction*>(pMA);
+ OUString aTemp = pA->GetText().copy( pA->GetIndex(), std::min<sal_Int32>(pA->GetText().getLength() - pA->GetIndex(), pA->GetLen()) );
+ aSrcLineInfo = LineInfo();
+ SetAllAttr();
+ if ( !WMFRecord_Escape_Unicode( pA->GetPoint(), aTemp, {} ) )
+ WMFRecord_TextOut( pA->GetPoint(), aTemp );
+ }
+ break;
+
+ case MetaActionType::TEXTARRAY:
+ {
+ const MetaTextArrayAction* pA = static_cast<const MetaTextArrayAction*>(pMA);
+
+ OUString aTemp = pA->GetText().copy( pA->GetIndex(), std::min<sal_Int32>(pA->GetText().getLength() - pA->GetIndex(), pA->GetLen()) );
+ aSrcLineInfo = LineInfo();
+ SetAllAttr();
+ if ( !WMFRecord_Escape_Unicode( pA->GetPoint(), aTemp, pA->GetDXArray() ) )
+ WMFRecord_ExtTextOut( pA->GetPoint(), aTemp, pA->GetDXArray() );
+ }
+ break;
+
+ case MetaActionType::STRETCHTEXT:
+ {
+ const MetaStretchTextAction* pA = static_cast<const MetaStretchTextAction *>(pMA);
+ OUString aTemp = pA->GetText().copy( pA->GetIndex(), std::min<sal_Int32>(pA->GetText().getLength() - pA->GetIndex(), pA->GetLen()) );
+
+ pVirDev->SetFont( aSrcFont );
+ const sal_Int32 nLen = aTemp.getLength();
+ std::vector<sal_Int32> aDXAry;
+ const sal_Int32 nNormSize = pVirDev->GetTextArray( aTemp, nLen ? &aDXAry : nullptr );
+ if (nLen && nNormSize == 0)
+ {
+ OSL_FAIL("Impossible div by 0 action: MetaStretchTextAction!");
+ }
+ else
+ {
+ for ( sal_Int32 i = 0; i < ( nLen - 1 ); i++ )
+ aDXAry[ i ] = aDXAry[ i ] * static_cast<sal_Int32>(pA->GetWidth()) / nNormSize;
+ if ( ( nLen <= 1 ) || ( static_cast<sal_Int32>(pA->GetWidth()) == nNormSize ) )
+ aDXAry.clear();
+ aSrcLineInfo = LineInfo();
+ SetAllAttr();
+ if ( !WMFRecord_Escape_Unicode( pA->GetPoint(), aTemp, aDXAry ) )
+ WMFRecord_ExtTextOut( pA->GetPoint(), aTemp, aDXAry );
+ }
+ }
+ break;
+
+ case MetaActionType::BMP:
+ {
+ const MetaBmpAction* pA = static_cast<const MetaBmpAction *>(pMA);
+ WMFRecord_StretchDIB( pA->GetPoint(), pA->GetBitmap().GetSizePixel(), pA->GetBitmap() );
+ }
+ break;
+
+ case MetaActionType::BMPSCALE:
+ {
+ const MetaBmpScaleAction* pA = static_cast<const MetaBmpScaleAction*>(pMA);
+ WMFRecord_StretchDIB( pA->GetPoint(), pA->GetSize(), pA->GetBitmap() );
+ }
+ break;
+
+ case MetaActionType::BMPSCALEPART:
+ {
+ const MetaBmpScalePartAction* pA = static_cast<const MetaBmpScalePartAction*>(pMA);
+ Bitmap aTmp( pA->GetBitmap() );
+
+ if( aTmp.Crop( tools::Rectangle( pA->GetSrcPoint(), pA->GetSrcSize() ) ) )
+ WMFRecord_StretchDIB( pA->GetDestPoint(), pA->GetDestSize(), aTmp );
+ }
+ break;
+
+ case MetaActionType::BMPEX:
+ {
+ const MetaBmpExAction* pA = static_cast<const MetaBmpExAction *>(pMA);
+ Bitmap aBmp( pA->GetBitmapEx().GetBitmap() );
+ Bitmap aMsk( pA->GetBitmapEx().GetAlpha() );
+
+ if( !aMsk.IsEmpty() )
+ {
+ aBmp.Replace( aMsk, COL_WHITE );
+ aMsk.Invert();
+ WMFRecord_StretchDIB( pA->GetPoint(), aMsk.GetSizePixel(), aBmp, W_SRCPAINT );
+ WMFRecord_StretchDIB( pA->GetPoint(), aBmp.GetSizePixel(), aBmp, W_SRCAND );
+ }
+ else
+ WMFRecord_StretchDIB( pA->GetPoint(), aBmp.GetSizePixel(), aBmp );
+ }
+ break;
+
+ case MetaActionType::BMPEXSCALE:
+ {
+ const MetaBmpExScaleAction* pA = static_cast<const MetaBmpExScaleAction*>(pMA);
+ Bitmap aBmp( pA->GetBitmapEx().GetBitmap() );
+ Bitmap aMsk( pA->GetBitmapEx().GetAlpha() );
+
+ if( !aMsk.IsEmpty() )
+ {
+ aBmp.Replace( aMsk, COL_WHITE );
+ aMsk.Invert();
+ WMFRecord_StretchDIB( pA->GetPoint(), pA->GetSize(), aMsk, W_SRCPAINT );
+ WMFRecord_StretchDIB( pA->GetPoint(), pA->GetSize(), aBmp, W_SRCAND );
+ }
+ else
+ WMFRecord_StretchDIB( pA->GetPoint(), pA->GetSize(), aBmp );
+ }
+ break;
+
+ case MetaActionType::BMPEXSCALEPART:
+ {
+ const MetaBmpExScalePartAction* pA = static_cast<const MetaBmpExScalePartAction*>(pMA);
+ BitmapEx aBmpEx( pA->GetBitmapEx() );
+ aBmpEx.Crop( tools::Rectangle( pA->GetSrcPoint(), pA->GetSrcSize() ) );
+ Bitmap aBmp( aBmpEx.GetBitmap() );
+ Bitmap aMsk( aBmpEx.GetAlpha() );
+
+ if( !aMsk.IsEmpty() )
+ {
+ aBmp.Replace( aMsk, COL_WHITE );
+ aMsk.Invert();
+ WMFRecord_StretchDIB( pA->GetDestPoint(), pA->GetDestSize(), aMsk, W_SRCPAINT );
+ WMFRecord_StretchDIB( pA->GetDestPoint(), pA->GetDestSize(), aBmp, W_SRCAND );
+ }
+ else
+ WMFRecord_StretchDIB( pA->GetDestPoint(), pA->GetDestSize(), aBmp );
+ }
+ break;
+
+ case MetaActionType::GRADIENT:
+ {
+ const MetaGradientAction* pA = static_cast<const MetaGradientAction*>(pMA);
+ GDIMetaFile aTmpMtf;
+
+ Gradient aGradient = pA->GetGradient();
+ aGradient.AddGradientActions( pA->GetRect(), aTmpMtf );
+ WriteRecords( aTmpMtf );
+ }
+ break;
+
+ case MetaActionType::HATCH:
+ {
+ const MetaHatchAction* pA = static_cast<const MetaHatchAction*>(pMA);
+ GDIMetaFile aTmpMtf;
+
+ pVirDev->AddHatchActions( pA->GetPolyPolygon(), pA->GetHatch(), aTmpMtf );
+ WriteRecords( aTmpMtf );
+ }
+ break;
+
+ case MetaActionType::WALLPAPER:
+ {
+ const MetaWallpaperAction* pA = static_cast<const MetaWallpaperAction*>(pMA);
+ const Color& rColor = pA->GetWallpaper().GetColor();
+ const Color aOldLineColor( aSrcLineColor );
+ const Color aOldFillColor( aSrcFillColor );
+
+ aSrcLineColor = rColor;
+ aSrcFillColor = rColor;
+ aSrcLineInfo = LineInfo();
+ SetLineAndFillAttr();
+ WMFRecord_Rectangle( pA->GetRect() );
+ aSrcLineColor = aOldLineColor;
+ aSrcFillColor = aOldFillColor;
+ }
+ break;
+
+ case MetaActionType::ISECTRECTCLIPREGION:
+ {
+ const MetaISectRectClipRegionAction* pA = static_cast<const MetaISectRectClipRegionAction*>(pMA);
+ WMFRecord_IntersectClipRect( pA->GetRect() );
+ }
+ break;
+
+ case MetaActionType::LINECOLOR:
+ {
+ const MetaLineColorAction* pA = static_cast<const MetaLineColorAction*>(pMA);
+
+ if( pA->IsSetting() )
+ aSrcLineColor = pA->GetColor();
+ else
+ aSrcLineColor = COL_TRANSPARENT;
+ }
+ break;
+
+ case MetaActionType::FILLCOLOR:
+ {
+ const MetaFillColorAction* pA = static_cast<const MetaFillColorAction*>(pMA);
+
+ if( pA->IsSetting() )
+ aSrcFillColor = pA->GetColor();
+ else
+ aSrcFillColor = COL_TRANSPARENT;
+ }
+ break;
+
+ case MetaActionType::TEXTCOLOR:
+ {
+ const MetaTextColorAction* pA = static_cast<const MetaTextColorAction*>(pMA);
+ aSrcTextColor = pA->GetColor();
+ }
+ break;
+
+ case MetaActionType::TEXTFILLCOLOR:
+ {
+ const MetaTextFillColorAction* pA = static_cast<const MetaTextFillColorAction*>(pMA);
+ if( pA->IsSetting() )
+ aSrcFont.SetFillColor( pA->GetColor() );
+ else
+ aSrcFont.SetFillColor( COL_TRANSPARENT );
+ }
+ break;
+
+ case MetaActionType::TEXTALIGN:
+ {
+ const MetaTextAlignAction* pA = static_cast<const MetaTextAlignAction*>(pMA);
+ eSrcTextAlign = pA->GetTextAlign();
+ }
+ break;
+
+ case MetaActionType::MAPMODE:
+ {
+ const MetaMapModeAction* pA = static_cast<const MetaMapModeAction*>(pMA);
+
+ if (aSrcMapMode!=pA->GetMapMode())
+ {
+ if( pA->GetMapMode().GetMapUnit() == MapUnit::MapRelative )
+ {
+ const MapMode& aMM = pA->GetMapMode();
+ Fraction aScaleX = aMM.GetScaleX();
+ Fraction aScaleY = aMM.GetScaleY();
+
+ Point aOrigin = aSrcMapMode.GetOrigin();
+ BigInt aX( aOrigin.X() );
+ aX *= BigInt( aScaleX.GetDenominator() );
+ if( aOrigin.X() >= 0 )
+ if( aScaleX.GetNumerator() >= 0 )
+ aX += BigInt( aScaleX.GetNumerator()/2 );
+ else
+ aX -= BigInt( (aScaleX.GetNumerator()+1)/2 );
+ else
+ if( aScaleX.GetNumerator() >= 0 )
+ aX -= BigInt( (aScaleX.GetNumerator()-1)/2 );
+ else
+ aX += BigInt( aScaleX.GetNumerator()/2 );
+ aX /= BigInt( aScaleX.GetNumerator() );
+ aOrigin.setX( static_cast<tools::Long>(aX) + aMM.GetOrigin().X() );
+ BigInt aY( aOrigin.Y() );
+ aY *= BigInt( aScaleY.GetDenominator() );
+ if( aOrigin.Y() >= 0 )
+ if( aScaleY.GetNumerator() >= 0 )
+ aY += BigInt( aScaleY.GetNumerator()/2 );
+ else
+ aY -= BigInt( (aScaleY.GetNumerator()+1)/2 );
+ else
+ if( aScaleY.GetNumerator() >= 0 )
+ aY -= BigInt( (aScaleY.GetNumerator()-1)/2 );
+ else
+ aY += BigInt( aScaleY.GetNumerator()/2 );
+ aY /= BigInt( aScaleY.GetNumerator() );
+ aOrigin.setY( static_cast<tools::Long>(aY) + aMM.GetOrigin().Y() );
+ aSrcMapMode.SetOrigin( aOrigin );
+
+ aScaleX *= aSrcMapMode.GetScaleX();
+ aScaleY *= aSrcMapMode.GetScaleY();
+ aSrcMapMode.SetScaleX( aScaleX );
+ aSrcMapMode.SetScaleY( aScaleY );
+ }
+ else
+ aSrcMapMode=pA->GetMapMode();
+ }
+ }
+ break;
+
+ case MetaActionType::FONT:
+ {
+ const MetaFontAction* pA = static_cast<const MetaFontAction*>(pMA);
+ aSrcFont = pA->GetFont();
+
+ if ( (aSrcFont.GetCharSet() == RTL_TEXTENCODING_DONTKNOW)
+ || (aSrcFont.GetCharSet() == RTL_TEXTENCODING_UNICODE) )
+ {
+ aSrcFont.SetCharSet( RTL_TEXTENCODING_MS_1252 );
+ }
+ eSrcTextAlign = aSrcFont.GetAlignment();
+ aSrcTextColor = aSrcFont.GetColor();
+ aSrcFont.SetAlignment( ALIGN_BASELINE );
+ aSrcFont.SetColor( COL_WHITE );
+ }
+ break;
+
+ case MetaActionType::PUSH:
+ {
+ const MetaPushAction* pA = static_cast<const MetaPushAction*>(pMA);
+
+ WMFWriterAttrStackMember* pAt = new WMFWriterAttrStackMember;
+ pAt->nFlags = pA->GetFlags();
+ pAt->aClipRegion = aSrcClipRegion;
+ pAt->aLineColor=aSrcLineColor;
+ pAt->aFillColor=aSrcFillColor;
+ pAt->eRasterOp=eSrcRasterOp;
+ pAt->aFont=aSrcFont;
+ pAt->eTextAlign=eSrcTextAlign;
+ pAt->aTextColor=aSrcTextColor;
+ pAt->aMapMode=aSrcMapMode;
+ pAt->aLineInfo=aDstLineInfo;
+ pAt->pSucc=pAttrStack;
+ pAttrStack=pAt;
+
+ SetAllAttr(); // update ( now all source attributes are equal to the destination attributes )
+ WMFRecord_SaveDC();
+
+ }
+ break;
+
+ case MetaActionType::POP:
+ {
+ WMFWriterAttrStackMember * pAt=pAttrStack;
+
+ if( pAt )
+ {
+ aDstLineInfo = pAt->aLineInfo;
+ aDstLineColor = pAt->aLineColor;
+ if ( pAt->nFlags & vcl::PushFlags::LINECOLOR )
+ aSrcLineColor = pAt->aLineColor;
+ aDstFillColor = pAt->aFillColor;
+ if ( pAt->nFlags & vcl::PushFlags::FILLCOLOR )
+ aSrcFillColor = pAt->aFillColor;
+ eDstROP2 = pAt->eRasterOp;
+ if ( pAt->nFlags & vcl::PushFlags::RASTEROP )
+ eSrcRasterOp = pAt->eRasterOp;
+ aDstFont = pAt->aFont;
+ if ( pAt->nFlags & vcl::PushFlags::FONT )
+ aSrcFont = pAt->aFont;
+ eDstTextAlign = pAt->eTextAlign;
+ if ( pAt->nFlags & ( vcl::PushFlags::FONT | vcl::PushFlags::TEXTALIGN ) )
+ eSrcTextAlign = pAt->eTextAlign;
+ aDstTextColor = pAt->aTextColor;
+ if ( pAt->nFlags & ( vcl::PushFlags::FONT | vcl::PushFlags::TEXTCOLOR ) )
+ aSrcTextColor = pAt->aTextColor;
+ if ( pAt->nFlags & vcl::PushFlags::MAPMODE )
+ aSrcMapMode = pAt->aMapMode;
+ aDstClipRegion = pAt->aClipRegion;
+ if ( pAt->nFlags & vcl::PushFlags::CLIPREGION )
+ aSrcClipRegion = pAt->aClipRegion;
+
+ WMFRecord_RestoreDC();
+ pAttrStack = pAt->pSucc;
+ delete pAt;
+ }
+ }
+ break;
+
+ case MetaActionType::EPS :
+ {
+ const MetaEPSAction* pA = static_cast<const MetaEPSAction*>(pMA);
+ const GDIMetaFile& aGDIMetaFile( pA->GetSubstitute() );
+
+ size_t nCount = aGDIMetaFile.GetActionSize();
+ for ( size_t i = 0; i < nCount; i++ )
+ {
+ const MetaAction* pMetaAct = aGDIMetaFile.GetAction( i );
+ if ( pMetaAct->GetType() == MetaActionType::BMPSCALE )
+ {
+ const MetaBmpScaleAction* pBmpScaleAction = static_cast<const MetaBmpScaleAction*>(pMetaAct);
+ WMFRecord_StretchDIB( pA->GetPoint(), pA->GetSize(), pBmpScaleAction->GetBitmap() );
+ break;
+ }
+ }
+ }
+ break;
+
+ case MetaActionType::RASTEROP:
+ {
+ const MetaRasterOpAction* pA = static_cast<const MetaRasterOpAction*>(pMA);
+ eSrcRasterOp=pA->GetRasterOp();
+ }
+ break;
+
+ case MetaActionType::Transparent:
+ {
+ aSrcLineInfo = LineInfo();
+ SetLineAndFillAttr();
+ WMFRecord_PolyPolygon( static_cast<const MetaTransparentAction*>(pMA)->GetPolyPolygon() );
+ }
+ break;
+
+ case MetaActionType::FLOATTRANSPARENT:
+ {
+ const MetaFloatTransparentAction* pA = static_cast<const MetaFloatTransparentAction*>(pMA);
+
+ GDIMetaFile aTmpMtf( pA->GetGDIMetaFile() );
+ Point aSrcPt( aTmpMtf.GetPrefMapMode().GetOrigin() );
+ const Size aSrcSize( aTmpMtf.GetPrefSize() );
+ const Point aDestPt( pA->GetPoint() );
+ const Size aDestSize( pA->GetSize() );
+ const double fScaleX = aSrcSize.Width() ? static_cast<double>(aDestSize.Width()) / aSrcSize.Width() : 1.0;
+ const double fScaleY = aSrcSize.Height() ? static_cast<double>(aDestSize.Height()) / aSrcSize.Height() : 1.0;
+ tools::Long nMoveX, nMoveY;
+
+ aSrcLineInfo = LineInfo();
+ SetAllAttr();
+
+ if( fScaleX != 1.0 || fScaleY != 1.0 )
+ {
+ aTmpMtf.Scale( fScaleX, fScaleY );
+ aSrcPt.setX( FRound( aSrcPt.X() * fScaleX ) );
+ aSrcPt.setY( FRound( aSrcPt.Y() * fScaleY ) );
+ }
+
+ nMoveX = aDestPt.X() - aSrcPt.X();
+ nMoveY = aDestPt.Y() - aSrcPt.Y();
+
+ if( nMoveX || nMoveY )
+ aTmpMtf.Move( nMoveX, nMoveY );
+
+ WriteRecords( aTmpMtf );
+ }
+ break;
+
+ case MetaActionType::LAYOUTMODE:
+ {
+ vcl::text::ComplexTextLayoutFlags nLayoutMode = static_cast<const MetaLayoutModeAction*>(pMA)->GetLayoutMode();
+ eSrcHorTextAlign = 0; // TA_LEFT
+ if ((nLayoutMode & vcl::text::ComplexTextLayoutFlags::BiDiRtl) != vcl::text::ComplexTextLayoutFlags::Default)
+ {
+ eSrcHorTextAlign = W_TA_RIGHT | W_TA_RTLREADING;
+ }
+ if ((nLayoutMode & vcl::text::ComplexTextLayoutFlags::TextOriginRight) != vcl::text::ComplexTextLayoutFlags::Default)
+ eSrcHorTextAlign |= W_TA_RIGHT;
+ else if ((nLayoutMode & vcl::text::ComplexTextLayoutFlags::TextOriginLeft) != vcl::text::ComplexTextLayoutFlags::Default)
+ eSrcHorTextAlign &= ~W_TA_RIGHT;
+ break;
+ }
+
+ case MetaActionType::CLIPREGION:
+ case MetaActionType::TEXTLANGUAGE:
+ case MetaActionType::COMMENT:
+ // Explicitly ignored cases
+ break;
+
+ default:
+ // TODO: Implement more cases as necessary. Let's not bother with a warning.
+ break;
+ }
+
+ nWrittenActions++;
+ MayCallback();
+
+ if (pWMF->GetError())
+ bStatus=false;
+
+ if(!bStatus)
+ break;
+ }
+}
+
+void WMFWriter::WriteHeader( bool bPlaceable )
+{
+ if( bPlaceable )
+ {
+ sal_uInt16 nCheckSum, nValue;
+ Size aSize( OutputDevice::LogicToLogic(Size(1,1),MapMode(MapUnit::MapInch), aTargetMapMode) );
+ sal_uInt16 nUnitsPerInch = static_cast<sal_uInt16>( ( aSize.Width() + aSize.Height() ) >> 1 );
+
+ nCheckSum=0;
+ nValue=0xcdd7; nCheckSum^=nValue; pWMF->WriteUInt16( nValue );
+ nValue=0x9ac6; nCheckSum^=nValue; pWMF->WriteUInt16( nValue );
+ nValue=0x0000; nCheckSum^=nValue; pWMF->WriteUInt16( nValue );
+ nValue=0x0000; nCheckSum^=nValue; pWMF->WriteUInt16( nValue );
+ nValue=0x0000; nCheckSum^=nValue; pWMF->WriteUInt16( nValue );
+ nValue=static_cast<sal_uInt16>(aTargetSize.Width()); nCheckSum^=nValue; pWMF->WriteUInt16( nValue );
+ nValue=static_cast<sal_uInt16>(aTargetSize.Height()); nCheckSum^=nValue; pWMF->WriteUInt16( nValue );
+ nValue=nUnitsPerInch; nCheckSum^=nValue; pWMF->WriteUInt16( nValue );
+ nValue=0x0000; nCheckSum^=nValue; pWMF->WriteUInt16( nValue );
+ nValue=0x0000; nCheckSum^=nValue; pWMF->WriteUInt16( nValue );
+ pWMF->WriteUInt16( nCheckSum );
+ }
+
+ nMetafileHeaderPos=pWMF->Tell();
+ pWMF->WriteUInt16( 0x0001 ) // type: file
+ .WriteUInt16( 0x0009 ) // header length in words
+ .WriteUInt16( 0x0300 ) // Version as BCD number
+ .WriteUInt32( 0x00000000 ) // file length (without 1st header), is later corrected by UpdateHeader()
+ .WriteUInt16( MAXOBJECTHANDLES ) // maximum number of simultaneous objects
+ .WriteUInt32( 0x00000000 ) // maximum record length, is later corrected by UpdateHeader()
+ .WriteUInt16( 0x0000 ); // reserved
+}
+
+void WMFWriter::UpdateHeader()
+{
+ sal_uLong nPos;
+ sal_uInt32 nFileSize;
+
+ nPos=pWMF->Tell(); // endposition = total size of file
+ nFileSize=nPos-nMetafileHeaderPos; // subtract size of 1st header
+ if ((nFileSize&1)!=0) { // if needed round to words
+ pWMF->WriteUChar( 0 );
+ nPos++;
+ nFileSize++;
+ }
+ nFileSize>>=1; // convert to number of words
+ pWMF->Seek(nMetafileHeaderPos+6); // to filesize entry in second header
+ pWMF->WriteUInt32( nFileSize ); // rectify file size
+ pWMF->SeekRel(2); // to max-record-length-entry in second header
+ pWMF->WriteUInt32( nMaxRecordSize ); // and rectify
+ pWMF->Seek(nPos);
+}
+
+bool WMFWriter::WriteWMF( const GDIMetaFile& rMTF, SvStream& rTargetStream,
+ FilterConfigItem const * pFConfigItem, bool bPlaceable )
+{
+ WMFWriterAttrStackMember * pAt;
+
+ bEmbedEMF = true;
+ bStatus=true;
+ pVirDev = VclPtr<VirtualDevice>::Create();
+
+ if (pFConfigItem)
+ {
+ xStatusIndicator = pFConfigItem->GetStatusIndicator();
+ if ( xStatusIndicator.is() )
+ {
+ xStatusIndicator->start( OUString(), 100 );
+ }
+
+ comphelper::SequenceAsHashMap aMap(pFConfigItem->GetFilterData());
+ auto it = aMap.find("EmbedEMF");
+ if (it != aMap.end())
+ {
+ it->second >>= bEmbedEMF;
+ }
+ }
+ nLastPercent=0;
+
+ pWMF=&rTargetStream;
+ pWMF->SetEndian(SvStreamEndian::LITTLE);
+
+ nMaxRecordSize=0;
+
+ aSrcMapMode=rMTF.GetPrefMapMode();
+
+ if( bPlaceable )
+ {
+ aTargetMapMode = aSrcMapMode;
+ aTargetSize = rMTF.GetPrefSize();
+ sal_uInt16 nTargetDivisor = CalcSaveTargetMapMode(aTargetMapMode, aTargetSize);
+ aTargetSize.setWidth( aTargetSize.Width() / nTargetDivisor );
+ aTargetSize.setHeight( aTargetSize.Height() / nTargetDivisor );
+ }
+ else
+ {
+ aTargetMapMode = MapMode( MapUnit::MapInch );
+
+ const tools::Long nUnit = pVirDev->LogicToPixel( Size( 1, 1 ), aTargetMapMode ).Width();
+ const Fraction aFrac( 1, nUnit );
+
+ aTargetMapMode.SetScaleX( aFrac );
+ aTargetMapMode.SetScaleY( aFrac );
+ aTargetSize = OutputDevice::LogicToLogic( rMTF.GetPrefSize(), aSrcMapMode, aTargetMapMode );
+ }
+
+ pVirDev->SetMapMode( aTargetMapMode );
+
+ pAttrStack=nullptr;
+
+ for (bool & rn : bHandleAllocated)
+ rn=false;
+
+ nDstPenHandle=0xffff;
+ nDstFontHandle=0xffff;
+ nDstBrushHandle=0xffff;
+
+ nNumberOfActions=0;
+ nNumberOfBitmaps=0;
+ nWrittenActions=0;
+ nWrittenBitmaps=0;
+ nActBitmapPercent=0;
+
+ CountActionsAndBitmaps(rMTF);
+
+ WriteHeader(bPlaceable);
+ if( bEmbedEMF )
+ WriteEmbeddedEMF( rMTF );
+ WMFRecord_SetWindowOrg(Point(0,0));
+ WMFRecord_SetWindowExt(rMTF.GetPrefSize());
+ WMFRecord_SetBkMode( true );
+
+ eDstROP2 = eSrcRasterOp = RasterOp::OverPaint;
+ WMFRecord_SetROP2(eDstROP2);
+
+ aDstLineInfo = LineInfo();
+ aDstLineColor = aSrcLineColor = COL_BLACK;
+ CreateSelectDeletePen( aDstLineColor, aDstLineInfo );
+
+ aDstFillColor = aSrcFillColor = COL_WHITE;
+ CreateSelectDeleteBrush( aDstFillColor );
+
+ aDstClipRegion = aSrcClipRegion = vcl::Region();
+
+ vcl::Font aFont;
+ aFont.SetCharSet( GetExtendedTextEncoding( RTL_TEXTENCODING_MS_1252 ) );
+ aFont.SetColor( COL_WHITE );
+ aFont.SetAlignment( ALIGN_BASELINE );
+ aDstFont = aSrcFont = aFont;
+ CreateSelectDeleteFont(aDstFont);
+
+ eDstTextAlign = eSrcTextAlign = ALIGN_BASELINE;
+ eDstHorTextAlign = eSrcHorTextAlign = W_TA_LEFT;
+ WMFRecord_SetTextAlign( eDstTextAlign, eDstHorTextAlign );
+
+ aDstTextColor = aSrcTextColor = COL_WHITE;
+ WMFRecord_SetTextColor(aDstTextColor);
+
+ // Write records
+ WriteRecords(rMTF);
+
+ WriteRecordHeader(0x00000003,0x0000); // end of file
+ UpdateHeader();
+
+ while(pAttrStack)
+ {
+ pAt=pAttrStack;
+ pAttrStack=pAt->pSucc;
+ delete pAt;
+ }
+
+ pVirDev.disposeAndClear();
+
+ if ( xStatusIndicator.is() )
+ xStatusIndicator->end();
+
+ return bStatus;
+}
+
+sal_uInt16 WMFWriter::CalcSaveTargetMapMode(MapMode& rMapMode,
+ const Size& rPrefSize)
+{
+ Fraction aDivFrac(2, 1);
+ sal_uInt16 nDivisor = 1;
+
+ Size aSize = OutputDevice::LogicToLogic( rPrefSize, aSrcMapMode, rMapMode );
+
+ while( nDivisor <= 64 && (aSize.Width() > 32767 || aSize.Height() > 32767) )
+ {
+ Fraction aFrac = rMapMode.GetScaleX();
+
+ aFrac *= aDivFrac;
+ rMapMode.SetScaleX(aFrac);
+ aFrac = rMapMode.GetScaleY();
+ aFrac *= aDivFrac;
+ rMapMode.SetScaleY(aFrac);
+ nDivisor <<= 1;
+ aSize = OutputDevice::LogicToLogic( rPrefSize, aSrcMapMode, rMapMode );
+ }
+
+ return nDivisor;
+}
+
+void WMFWriter::WriteEmbeddedEMF( const GDIMetaFile& rMTF )
+{
+ SvMemoryStream aStream;
+ EMFWriter aEMFWriter(aStream);
+
+ if( !aEMFWriter.WriteEMF( rMTF ) )
+ return;
+
+ sal_uInt64 const nTotalSize = aStream.Tell();
+ if( nTotalSize > SAL_MAX_UINT32 )
+ return;
+ aStream.Seek( 0 );
+ sal_uInt32 nRemainingSize = static_cast< sal_uInt32 >( nTotalSize );
+ sal_uInt32 nRecCounts = ( (nTotalSize - 1) / 0x2000 ) + 1;
+ sal_uInt16 nCheckSum = 0, nWord;
+
+ sal_uInt32 nPos = 0;
+
+ while( nPos + 1 < nTotalSize )
+ {
+ aStream.ReadUInt16( nWord );
+ nCheckSum ^= nWord;
+ nPos += 2;
+ }
+
+ nCheckSum = static_cast< sal_uInt16 >( nCheckSum * -1 );
+
+ aStream.Seek( 0 );
+ while( nRemainingSize > 0 )
+ {
+ sal_uInt32 nCurSize;
+ if( nRemainingSize > 0x2000 )
+ {
+ nCurSize = 0x2000;
+ nRemainingSize -= 0x2000;
+ }
+ else
+ {
+ nCurSize = nRemainingSize;
+ nRemainingSize = 0;
+ }
+ WriteEMFRecord( aStream,
+ nCurSize,
+ nRemainingSize,
+ nTotalSize,
+ nRecCounts,
+ nCheckSum );
+ nCheckSum = 0;
+ }
+
+}
+
+void WMFWriter::WriteEMFRecord( SvMemoryStream& rStream, sal_uInt32 nCurSize, sal_uInt32 nRemainingSize,
+ sal_uInt32 nTotalSize, sal_uInt32 nRecCounts, sal_uInt16 nCheckSum )
+{
+ // according to http://msdn.microsoft.com/en-us/library/dd366152%28PROT.13%29.aspx
+ WriteRecordHeader( 0, W_META_ESCAPE );
+ pWMF->WriteUInt16( W_MFCOMMENT ) // same as META_ESCAPE_ENHANCED_METAFILE
+ .WriteUInt16( nCurSize + 34 ) // we will always have a 34 byte escape header:
+ .WriteUInt32( 0x43464D57 ) // WMFC
+ .WriteUInt32( 0x00000001 ) // Comment type
+ .WriteUInt32( 0x00010000 ) // version
+ .WriteUInt16( nCheckSum ) // check sum
+ .WriteUInt32( 0 ) // flags = 0
+ .WriteUInt32( nRecCounts ) // total number of records
+ .WriteUInt32( nCurSize ) // size of this record's data
+ .WriteUInt32( nRemainingSize ) // remaining size of data in following records, missing in MSDN documentation
+ .WriteUInt32( nTotalSize ); // total size of EMF stream
+
+ pWMF->WriteBytes(static_cast<const char*>(rStream.GetData()) + rStream.Tell(), nCurSize);
+ rStream.SeekRel( nCurSize );
+ UpdateRecordHeader();
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/vcl/source/filter/wmf/wmfwr.hxx b/vcl/source/filter/wmf/wmfwr.hxx
new file mode 100644
index 000000000..7b0ce679e
--- /dev/null
+++ b/vcl/source/filter/wmf/wmfwr.hxx
@@ -0,0 +1,205 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#ifndef INCLUDED_VCL_SOURCE_FILTER_WMF_WMFWR_HXX
+#define INCLUDED_VCL_SOURCE_FILTER_WMF_WMFWR_HXX
+
+#include <vcl/gdimtf.hxx>
+#include <vcl/lineinfo.hxx>
+#include <vcl/virdev.hxx>
+#include <vcl/FilterConfigItem.hxx>
+#include <com/sun/star/task/XStatusIndicator.hpp>
+#include <tools/stream.hxx>
+
+#define MAXOBJECTHANDLES 16
+
+struct WMFWriterAttrStackMember
+{
+ struct WMFWriterAttrStackMember * pSucc;
+ Color aLineColor;
+ Color aFillColor;
+ Color aTextColor;
+ LineInfo aLineInfo;
+ TextAlign eTextAlign;
+ RasterOp eRasterOp;
+ vcl::Font aFont;
+ MapMode aMapMode;
+ vcl::Region aClipRegion;
+ vcl::PushFlags nFlags;
+};
+
+class StarSymbolToMSMultiFont;
+class LineInfo;
+namespace basegfx { class B2DPolygon; }
+
+class WMFWriter
+{
+private:
+
+ bool bStatus;
+
+ sal_uLong nLastPercent; // with which number pCallback was called last time.
+
+ css::uno::Reference< css::task::XStatusIndicator > xStatusIndicator;
+
+ SvStream* pWMF;
+ VclPtr<VirtualDevice> pVirDev;
+ MapMode aTargetMapMode;
+ Size aTargetSize;
+
+ sal_uLong nMetafileHeaderPos;
+ sal_uInt32 nMaxRecordSize; // in words
+ sal_uLong nActRecordPos;
+
+ // actual attribute in source metafile:
+ Color aSrcLineColor;
+ Color aSrcFillColor;
+ Color aSrcTextColor;
+ LineInfo aSrcLineInfo;
+ RasterOp eSrcRasterOp;
+ TextAlign eSrcTextAlign;
+ vcl::Font aSrcFont;
+ MapMode aSrcMapMode;
+ vcl::Region aSrcClipRegion;
+ WMFWriterAttrStackMember * pAttrStack;
+
+ sal_uInt16 eSrcHorTextAlign;
+
+ // actual attribute in destination metafile:
+ Color aDstLineColor;
+ Color aDstFillColor;
+ Color aDstTextColor;
+ LineInfo aDstLineInfo;
+ RasterOp eDstROP2;
+ TextAlign eDstTextAlign;
+ vcl::Font aDstFont;
+
+ sal_uInt16 eDstHorTextAlign;
+
+ vcl::Region aDstClipRegion; // ???: not taken into account at the moment
+ bool bHandleAllocated[MAXOBJECTHANDLES]; // which handles have been assigned
+ sal_uInt16 nDstPenHandle,nDstFontHandle,nDstBrushHandle; // which handles are owned by
+ // Selected-Objects
+ // 0xFFFF = none:
+
+ // to prevent we have to compare all attributes at each operation:
+
+ sal_uLong nNumberOfActions; // number of actions in the GDIMetafile
+ sal_uLong nNumberOfBitmaps; // number of bitmaps
+ sal_uLong nWrittenActions; // number of processed actions while writing the directory
+ sal_uLong nWrittenBitmaps; // number of bitmaps written
+ sal_uLong nActBitmapPercent; // percentage of next bitmap written.
+
+ bool bEmbedEMF; // optionally embed EMF data into WMF
+
+ void MayCallback();
+ // this function calculates percentage using the above 5 parameters
+ // and triggers a callback if needed. Puts bStatus to FALSE if the
+ // users wants to abort.
+
+ void CountActionsAndBitmaps(const GDIMetaFile & rMTF);
+ // Counts bitmaps and actions (nNumberOfActions and nNumberOfBitmaps should
+ // be initialised to 0 at start, as this method is recursive)
+
+ void WritePointXY(const Point & rPoint);
+ void WritePointYX(const Point & rPoint);
+ sal_Int32 ScaleWidth( sal_Int32 nDX );
+ void WriteSize(const Size & rSize);
+ void WriteHeightWidth(const Size & rSize);
+ void WriteRectangle(const tools::Rectangle & rRect);
+ void WriteColor(const Color & rColor);
+
+ void WriteRecordHeader(sal_uInt32 nSizeWords, sal_uInt16 nType);
+ // nSizeWords is the size of the all records in number of words.
+ // If nSizeWords is unknown, then use 0 (see UpdateRecordHeader())
+
+ void UpdateRecordHeader();
+ // returns the size of the record after writing the parameters, if
+ // nSizeWords was unknown upon calling WriteRecordHeader(..)
+ // if needed it inserts a BYTE 0 to make number of bytes even
+
+ void WMFRecord_Arc(const tools::Rectangle& rRect, const Point& rStartPt, const Point& rEndPt);
+ void WMFRecord_Chord(const tools::Rectangle& rRect, const Point& rStartPt, const Point& rEndPt);
+ void WMFRecord_CreateBrushIndirect(const Color& rColor);
+ void WMFRecord_CreateFontIndirect(const vcl::Font& rFont);
+ void WMFRecord_CreatePenIndirect(const Color& rColor, const LineInfo& rLineInfo );
+ void WMFRecord_DeleteObject(sal_uInt16 nObjectHandle);
+ void WMFRecord_Ellipse(const tools::Rectangle& rRect);
+ void WMFRecord_Escape( sal_uInt32 nEsc, sal_uInt32 nLen, const sal_Int8* pData );
+ bool WMFRecord_Escape_Unicode( const Point& rPoint, const OUString& rStr, o3tl::span<const sal_Int32> pDXAry );
+ void WMFRecord_ExtTextOut(const Point& rPoint, const OUString& rString, o3tl::span<const sal_Int32> pDXAry);
+
+ void TrueExtTextOut(const Point& rPoint, const OUString& rString,
+ const OString& rByteString, o3tl::span<const sal_Int32> pDXAry);
+ void TrueTextOut(const Point& rPoint, const OString& rString);
+ void WMFRecord_LineTo(const Point & rPoint);
+ void WMFRecord_MoveTo(const Point & rPoint);
+ void WMFRecord_Pie(const tools::Rectangle & rRect, const Point & rStartPt, const Point & rEndPt);
+ void WMFRecord_Polygon(const tools::Polygon & rPoly);
+ void WMFRecord_PolyLine(const tools::Polygon & rPoly);
+ void WMFRecord_PolyPolygon(const tools::PolyPolygon & rPolyPoly);
+ void WMFRecord_Rectangle(const tools::Rectangle & rRect);
+ void WMFRecord_RestoreDC();
+ void WMFRecord_RoundRect(const tools::Rectangle & rRect, tools::Long nHorzRound, tools::Long nVertRound);
+ void WMFRecord_SaveDC();
+ void WMFRecord_SelectObject(sal_uInt16 nObjectHandle);
+ void WMFRecord_SetBkMode(bool bTransparent);
+ void WMFRecord_SetStretchBltMode();
+ void WMFRecord_SetPixel(const Point & rPoint, const Color & rColor);
+ void WMFRecord_SetROP2(RasterOp eROP);
+ void WMFRecord_SetTextAlign(TextAlign eFontAlign, sal_uInt16 eHorTextAlign);
+ void WMFRecord_SetTextColor(const Color & rColor);
+ void WMFRecord_SetWindowExt(const Size & rSize);
+ void WMFRecord_SetWindowOrg(const Point & rPoint);
+ void WMFRecord_StretchDIB(const Point & rPoint, const Size & rSize, const Bitmap & rBitmap, sal_uInt32 nROP = 0 );
+ void WMFRecord_TextOut(const Point & rPoint, std::u16string_view rString);
+ void WMFRecord_IntersectClipRect( const tools::Rectangle& rRect);
+
+ sal_uInt16 AllocHandle();
+ void FreeHandle(sal_uInt16 nObjectHandle);
+ void CreateSelectDeletePen( const Color& rColor, const LineInfo& rLineInfo );
+ void CreateSelectDeleteFont(const vcl::Font & rFont);
+ void CreateSelectDeleteBrush(const Color& rColor);
+
+ void SetLineAndFillAttr();
+ void SetAllAttr();
+
+ void HandleLineInfoPolyPolygons(const LineInfo& rInfo, const basegfx::B2DPolygon& rLinePolygon);
+ void WriteRecords(const GDIMetaFile & rMTF);
+
+ void WriteHeader(bool bPlaceable);
+ void UpdateHeader();
+
+ void WriteEmbeddedEMF( const GDIMetaFile& rMTF );
+ void WriteEMFRecord( SvMemoryStream& rStream, sal_uInt32 nCurSize,
+ sal_uInt32 nRemainingSize,
+ sal_uInt32 nTotalSize,
+ sal_uInt32 nRecCounts,
+ sal_uInt16 nCheckSum );
+
+ sal_uInt16 CalcSaveTargetMapMode(MapMode& rMapMode, const Size& rPrefSize);
+
+public:
+ WMFWriter();
+ bool WriteWMF(const GDIMetaFile & rMTF, SvStream & rTargetStream, FilterConfigItem const * pFilterConfigItem, bool bPlaceable);
+};
+
+#endif
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */