diff options
author | Tomaž Vajngerl <tomaz.vajngerl@collabora.co.uk> | 2021-02-15 00:08:02 +0900 |
---|---|---|
committer | Tomaž Vajngerl <quikee@gmail.com> | 2021-02-17 04:02:41 +0100 |
commit | 509814d936461cb7690862eac8d6c88e9f412362 (patch) | |
tree | 4552c1b720aa49d595689b54be92e493df47fade /vcl | |
parent | 1093c21ed9736368ecfb8f5c7935db31256671f0 (diff) |
Move EPS reader and writer from filter module into VCL
Change-Id: I1646f72d6a1db176e4520f8f64321646a26f054e
Reviewed-on: https://gerrit.libreoffice.org/c/core/+/111016
Tested-by: Jenkins
Reviewed-by: Tomaž Vajngerl <quikee@gmail.com>
Diffstat (limited to 'vcl')
19 files changed, 4318 insertions, 28 deletions
diff --git a/vcl/CppunitTest_vcl_filters_test.mk b/vcl/CppunitTest_vcl_filters_test.mk index 87a5da8e9284..a84aec4c22ec 100644 --- a/vcl/CppunitTest_vcl_filters_test.mk +++ b/vcl/CppunitTest_vcl_filters_test.mk @@ -10,6 +10,7 @@ $(eval $(call gb_CppunitTest_CppunitTest,vcl_filters_test)) $(eval $(call gb_CppunitTest_add_exception_objects,vcl_filters_test, \ + vcl/qa/cppunit/graphicfilter/filters-eps-test \ vcl/qa/cppunit/graphicfilter/filters-met-test \ vcl/qa/cppunit/graphicfilter/filters-pcx-test \ vcl/qa/cppunit/graphicfilter/filters-pict-test \ diff --git a/vcl/Library_vcl.mk b/vcl/Library_vcl.mk index 08815ddc95e1..14decf45643f 100644 --- a/vcl/Library_vcl.mk +++ b/vcl/Library_vcl.mk @@ -427,6 +427,7 @@ $(eval $(call gb_Library_add_exception_objects,vcl,\ vcl/source/components/dtranscomp \ vcl/source/components/factory \ vcl/source/components/fontident \ + vcl/source/filter/eps/eps \ vcl/source/filter/etiff/etiff \ vcl/source/filter/FilterConfigCache \ vcl/source/filter/FilterConfigItem \ @@ -435,6 +436,7 @@ $(eval $(call gb_Library_add_exception_objects,vcl,\ vcl/source/filter/GraphicNativeTransform \ vcl/source/filter/GraphicNativeMetadata \ vcl/source/filter/GraphicFormatDetector \ + vcl/source/filter/ieps/ieps \ vcl/source/filter/igif/decode \ vcl/source/filter/igif/gifread \ vcl/source/filter/imet/ios2met \ diff --git a/vcl/inc/filter/EpsReader.hxx b/vcl/inc/filter/EpsReader.hxx new file mode 100644 index 000000000000..8cc945ee8e74 --- /dev/null +++ b/vcl/inc/filter/EpsReader.hxx @@ -0,0 +1,26 @@ +/* -*- 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> + +VCL_DLLPUBLIC bool ImportEpsGraphic(SvStream& rStream, Graphic& rGraphic); + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/vcl/inc/filter/EpsWriter.hxx b/vcl/inc/filter/EpsWriter.hxx new file mode 100644 index 000000000000..8cb002a57c66 --- /dev/null +++ b/vcl/inc/filter/EpsWriter.hxx @@ -0,0 +1,28 @@ +/* -*- 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/FilterConfigItem.hxx> + +VCL_DLLPUBLIC bool ExportEpsGraphic(SvStream& rStream, Graphic& rGraphic, + FilterConfigItem* pFilterConfigItem); + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/vcl/inc/strings.hrc b/vcl/inc/strings.hrc index 56437a7325e8..11ba428d5798 100644 --- a/vcl/inc/strings.hrc +++ b/vcl/inc/strings.hrc @@ -137,6 +137,8 @@ #define STR_SEPARATOR NC_("STR_SEPARATOR", "Separator") +#define KEY_VERSION_CHECK NC_("KEY_VERSION_CHECK", "Warning: Not all of the imported EPS graphics could be saved at level1\nas some are at a higher level!") + #endif // INCLUDED_VCL_INC_STRINGS_HRC /* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/vcl/qa/cppunit/graphicfilter/data/eps/fail/.gitignore b/vcl/qa/cppunit/graphicfilter/data/eps/fail/.gitignore new file mode 100644 index 000000000000..e69de29bb2d1 --- /dev/null +++ b/vcl/qa/cppunit/graphicfilter/data/eps/fail/.gitignore diff --git a/vcl/qa/cppunit/graphicfilter/data/eps/fail/CVE-2009-4195-1.eps b/vcl/qa/cppunit/graphicfilter/data/eps/fail/CVE-2009-4195-1.eps Binary files differnew file mode 100644 index 000000000000..5ae189f15105 --- /dev/null +++ b/vcl/qa/cppunit/graphicfilter/data/eps/fail/CVE-2009-4195-1.eps diff --git a/vcl/qa/cppunit/graphicfilter/data/eps/fail/short-1.eps b/vcl/qa/cppunit/graphicfilter/data/eps/fail/short-1.eps Binary files differnew file mode 100644 index 000000000000..4b38b782f6fd --- /dev/null +++ b/vcl/qa/cppunit/graphicfilter/data/eps/fail/short-1.eps diff --git a/vcl/qa/cppunit/graphicfilter/data/eps/indeterminate/.gitignore b/vcl/qa/cppunit/graphicfilter/data/eps/indeterminate/.gitignore new file mode 100644 index 000000000000..b2a2eb0476ab --- /dev/null +++ b/vcl/qa/cppunit/graphicfilter/data/eps/indeterminate/.gitignore @@ -0,0 +1 @@ +*.eps-* diff --git a/vcl/qa/cppunit/graphicfilter/data/eps/pass/.gitignore b/vcl/qa/cppunit/graphicfilter/data/eps/pass/.gitignore new file mode 100644 index 000000000000..e69de29bb2d1 --- /dev/null +++ b/vcl/qa/cppunit/graphicfilter/data/eps/pass/.gitignore diff --git a/vcl/qa/cppunit/graphicfilter/data/eps/pass/CVE-2013-4979-1.eps b/vcl/qa/cppunit/graphicfilter/data/eps/pass/CVE-2013-4979-1.eps Binary files differnew file mode 100644 index 000000000000..ae6c6aad7e1e --- /dev/null +++ b/vcl/qa/cppunit/graphicfilter/data/eps/pass/CVE-2013-4979-1.eps diff --git a/vcl/qa/cppunit/graphicfilter/data/eps/pass/fdo13433-4.eps b/vcl/qa/cppunit/graphicfilter/data/eps/pass/fdo13433-4.eps new file mode 100644 index 000000000000..6ca427f86f4f --- /dev/null +++ b/vcl/qa/cppunit/graphicfilter/data/eps/pass/fdo13433-4.eps @@ -0,0 +1,667 @@ +%!PS-Adobe-3.0 EPSF-3.0 +%%Title: /home/amg/newtest05.eps +%%Creator: matplotlib version 0.98.0, http://matplotlib.sourceforge.net/ +%%CreationDate: Sat Jul 19 09:49:37 2008 +%%Orientation: portrait +%%BoundingBox: 13 175 598 616 +%%EndComments +%%BeginProlog +/mpldict 7 dict def +mpldict begin +/m { moveto } bind def +/l { lineto } bind def +/r { rlineto } bind def +/c { curveto } bind def +/cl { closepath } bind def +/box { +m +1 index 0 r +0 exch r +neg 0 r +cl +} bind def +/clipbox { +box +clip +newpath +} bind def +end +%%EndProlog +mpldict begin +13.5 175.5 translate +585 441 0 0 clipbox +1.000 setlinewidth +0 setlinejoin +2 setlinecap +[] 0 setdash +1.000 setgray +gsave +0 0 m +585 0 l +585 441 l +0 441 l +0 0 l +gsave +fill +grestore +stroke +grestore +0.000 setgray +gsave +73.125 44.1 m +526.5 44.1 l +526.5 396.9 l +73.125 396.9 l +73.125 44.1 l +gsave +1.000 setgray +fill +grestore +stroke +grestore +0.500 setlinewidth +0 setlinecap +gsave +453.4 352.8 73.12 44.1 clipbox +/o { +gsave +newpath +translate +0 -3 m +0.795609 -3 1.55874 -2.6839 2.12132 -2.12132 c +2.6839 -1.55874 3 -0.795609 3 0 c +3 0.795609 2.6839 1.55874 2.12132 2.12132 c +1.55874 2.6839 0.795609 3 0 3 c +-0.795609 3 -1.55874 2.6839 -2.12132 2.12132 c +-2.6839 1.55874 -3 0.795609 -3 0 c +-3 -0.795609 -2.6839 -1.55874 -2.12132 -2.12132 c +-1.55874 -2.6839 -0.795609 -3 0 -3 c +cl +gsave +1.000 0.000 0.000 setrgbcolor +fill +grestore +stroke +grestore +} bind def +73.1 92.5 o +77.7 206 o +82.2 204 o +86.7 293 o +91.3 189 o +95.8 276 o +100 250 o +105 226 o +109 240 o +114 329 o +118 250 o +123 226 o +128 99 o +132 195 o +137 241 o +141 168 o +146 166 o +150 301 o +155 294 o +159 223 o +164 220 o +168 184 o +173 205 o +177 242 o +182 320 o +186 239 o +191 306 o +196 272 o +200 96.1 o +205 293 o +209 265 o +214 233 o +218 231 o +223 138 o +227 204 o +232 278 o +236 271 o +241 165 o +245 199 o +250 239 o +254 234 o +259 306 o +264 304 o +268 246 o +273 172 o +277 180 o +282 246 o +286 146 o +291 97.8 o +295 241 o +300 141 o +304 242 o +309 177 o +313 189 o +318 142 o +322 216 o +327 273 o +332 261 o +336 173 o +341 223 o +345 282 o +350 285 o +354 90.1 o +359 241 o +363 344 o +368 187 o +372 172 o +377 224 o +381 300 o +386 237 o +390 107 o +395 249 o +400 263 o +404 146 o +409 162 o +413 228 o +418 252 o +422 166 o +427 255 o +431 92.7 o +436 277 o +440 204 o +445 226 o +449 356 o +454 277 o +458 247 o +463 244 o +468 272 o +472 286 o +477 259 o +481 332 o +486 138 o +490 212 o +495 203 o +499 163 o +504 374 o +508 371 o +513 223 o +517 126 o +522 205 o +grestore +gsave +/o { +gsave +newpath +translate +0 0 m +0 4 l +stroke +grestore +} bind def +73.1 44.1 o +grestore +gsave +/o { +gsave +newpath +translate +0 0 m +0 -4 l +stroke +grestore +} bind def +73.1 397 o +grestore +gsave +/Helvetica-Narrow findfont +12.0 scalefont +setfont +70.389000 31.664000 translate +0.000000 rotate +0.000000 0.000000 m /zero glyphshow +grestore + gsave +/o { +gsave +newpath +translate +0 0 m +0 4 l +stroke +grestore +} bind def +164 44.1 o +grestore +gsave +/o { +gsave +newpath +translate +0 0 m +0 -4 l +stroke +grestore +} bind def +164 397 o +grestore +gsave +/Helvetica-Narrow findfont +12.0 scalefont +setfont +158.328000 31.436000 translate +0.000000 rotate +0.000000 0.000000 m /two glyphshow +5.472000 0.000000 m /zero glyphshow +grestore + gsave +/o { +gsave +newpath +translate +0 0 m +0 4 l +stroke +grestore +} bind def +254 44.1 o +grestore +gsave +/o { +gsave +newpath +translate +0 0 m +0 -4 l +stroke +grestore +} bind def +254 397 o +grestore +gsave +/Helvetica-Narrow findfont +12.0 scalefont +setfont +249.003000 31.436000 translate +0.000000 rotate +0.000000 0.000000 m /four glyphshow +5.472000 0.000000 m /zero glyphshow +grestore + gsave +/o { +gsave +newpath +translate +0 0 m +0 4 l +stroke +grestore +} bind def +345 44.1 o +grestore +gsave +/o { +gsave +newpath +translate +0 0 m +0 -4 l +stroke +grestore +} bind def +345 397 o +grestore +gsave +/Helvetica-Narrow findfont +12.0 scalefont +setfont +339.678000 31.664000 translate +0.000000 rotate +0.000000 0.000000 m /six glyphshow +5.472000 0.000000 m /zero glyphshow +grestore + gsave +/o { +gsave +newpath +translate +0 0 m +0 4 l +stroke +grestore +} bind def +436 44.1 o +grestore +gsave +/o { +gsave +newpath +translate +0 0 m +0 -4 l +stroke +grestore +} bind def +436 397 o +grestore +gsave +/Helvetica-Narrow findfont +12.0 scalefont +setfont +430.353000 31.664000 translate +0.000000 rotate +0.000000 0.000000 m /eight glyphshow +5.472000 0.000000 m /zero glyphshow +grestore + gsave +/o { +gsave +newpath +translate +0 0 m +0 4 l +stroke +grestore +} bind def +526 44.1 o +grestore +gsave +/o { +gsave +newpath +translate +0 0 m +0 -4 l +stroke +grestore +} bind def +526 397 o +grestore +gsave +/Helvetica-Narrow findfont +12.0 scalefont +setfont +518.292000 31.436000 translate +0.000000 rotate +0.000000 0.000000 m /one glyphshow +5.472000 0.000000 m /zero glyphshow +10.944000 0.000000 m /zero glyphshow +grestore + 1.000 setlinewidth +gsave +gsave +/Helvetica-Narrow findfont +12.0 scalefont +setfont +296.531 17.82 moveto +0.0 rotate +(X) +0.000 0.000 0.000 setrgbcolor +show +grestore +stroke +grestore +0.500 setlinewidth +gsave +/o { +gsave +newpath +translate +0 0 m +4 0 l +stroke +grestore +} bind def +73.1 44.1 o +grestore +gsave +/o { +gsave +newpath +translate +0 0 m +-4 0 l +stroke +grestore +} bind def +526 44.1 o +grestore +gsave +/Helvetica-Narrow findfont +12.0 scalefont +setfont +57.905000 39.882000 translate +0.000000 rotate +0.000000 0.000000 m /minus glyphshow +5.748000 0.000000 m /three glyphshow +grestore + gsave +/o { +gsave +newpath +translate +0 0 m +4 0 l +stroke +grestore +} bind def +73.1 103 o +grestore +gsave +/o { +gsave +newpath +translate +0 0 m +-4 0 l +stroke +grestore +} bind def +526 103 o +grestore +gsave +/Helvetica-Narrow findfont +12.0 scalefont +setfont +57.905000 98.682000 translate +0.000000 rotate +0.000000 0.000000 m /minus glyphshow +5.748000 0.000000 m /two glyphshow +grestore + gsave +/o { +gsave +newpath +translate +0 0 m +4 0 l +stroke +grestore +} bind def +73.1 162 o +grestore +gsave +/o { +gsave +newpath +translate +0 0 m +-4 0 l +stroke +grestore +} bind def +526 162 o +grestore +gsave +/Helvetica-Narrow findfont +12.0 scalefont +setfont +57.905000 157.482000 translate +0.000000 rotate +0.000000 0.000000 m /minus glyphshow +5.748000 0.000000 m /one glyphshow +grestore + gsave +/o { +gsave +newpath +translate +0 0 m +4 0 l +stroke +grestore +} bind def +73.1 221 o +grestore +gsave +/o { +gsave +newpath +translate +0 0 m +-4 0 l +stroke +grestore +} bind def +526 221 o +grestore +gsave +/Helvetica-Narrow findfont +12.0 scalefont +setfont +63.653000 216.282000 translate +0.000000 rotate +0.000000 0.000000 m /zero glyphshow +grestore + gsave +/o { +gsave +newpath +translate +0 0 m +4 0 l +stroke +grestore +} bind def +73.1 279 o +grestore +gsave +/o { +gsave +newpath +translate +0 0 m +-4 0 l +stroke +grestore +} bind def +526 279 o +grestore +gsave +/Helvetica-Narrow findfont +12.0 scalefont +setfont +63.653000 275.082000 translate +0.000000 rotate +0.000000 0.000000 m /one glyphshow +grestore + gsave +/o { +gsave +newpath +translate +0 0 m +4 0 l +stroke +grestore +} bind def +73.1 338 o +grestore +gsave +/o { +gsave +newpath +translate +0 0 m +-4 0 l +stroke +grestore +} bind def +526 338 o +grestore +gsave +/Helvetica-Narrow findfont +12.0 scalefont +setfont +63.653000 333.882000 translate +0.000000 rotate +0.000000 0.000000 m /two glyphshow +grestore + gsave +/o { +gsave +newpath +translate +0 0 m +4 0 l +stroke +grestore +} bind def +73.1 397 o +grestore +gsave +/o { +gsave +newpath +translate +0 0 m +-4 0 l +stroke +grestore +} bind def +526 397 o +grestore +gsave +/Helvetica-Narrow findfont +12.0 scalefont +setfont +63.653000 392.682000 translate +0.000000 rotate +0.000000 0.000000 m /three glyphshow +grestore + 1.000 setlinewidth +gsave +gsave +/Helvetica-Narrow findfont +12.0 scalefont +setfont +52.905 217.218 moveto +90.0 rotate +(Y) +0.000 0.000 0.000 setrgbcolor +show +grestore +stroke +grestore +2 setlinecap +gsave +73.125 44.1 m +526.5 44.1 l +526.5 396.9 l +73.125 396.9 l +73.125 44.1 l +stroke +grestore +0 setlinecap +gsave +gsave +/Helvetica-Narrow findfont +14.0 scalefont +setfont +277.798 404.798 moveto +0.0 rotate +(Test plot) +0.000 0.000 0.000 setrgbcolor +show +grestore +stroke +grestore + +end +showpage diff --git a/vcl/qa/cppunit/graphicfilter/filters-eps-test.cxx b/vcl/qa/cppunit/graphicfilter/filters-eps-test.cxx new file mode 100644 index 000000000000..34d7bab5d43d --- /dev/null +++ b/vcl/qa/cppunit/graphicfilter/filters-eps-test.cxx @@ -0,0 +1,61 @@ +/* -*- 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 <unotest/filters-test.hxx> +#include <test/bootstrapfixture.hxx> +#include <vcl/FilterConfigItem.hxx> +#include <tools/stream.hxx> +#include <vcl/graph.hxx> +#include <filter/EpsReader.hxx> + +using namespace css; + +/* Implementation of Filters test */ + +class EpsFilterTest + : public test::FiltersTest + , public test::BootstrapFixture +{ +public: + EpsFilterTest() : BootstrapFixture(true, false) {} + + virtual bool load(const OUString &, + const OUString &rURL, const OUString &, + SfxFilterFlags, SotClipboardFormatId, unsigned int) override; + + /** + * Ensure CVEs remain unbroken + */ + void testCVEs(); + + CPPUNIT_TEST_SUITE(EpsFilterTest); + CPPUNIT_TEST(testCVEs); + CPPUNIT_TEST_SUITE_END(); +}; + +bool EpsFilterTest::load(const OUString &, + const OUString &rURL, const OUString &, + SfxFilterFlags, SotClipboardFormatId, unsigned int) +{ + SvFileStream aFileStream(rURL, StreamMode::READ); + Graphic aGraphic; + return ImportEpsGraphic(aFileStream, aGraphic); +} + +void EpsFilterTest::testCVEs() +{ +#ifndef DISABLE_CVE_TESTS + testDir(OUString(), + m_directories.getURLFromSrc(u"/vcl/qa/cppunit/graphicfilter/data/eps/")); +#endif +} + +CPPUNIT_TEST_SUITE_REGISTRATION(EpsFilterTest); + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/vcl/source/filter/FilterConfigCache.cxx b/vcl/source/filter/FilterConfigCache.cxx index 2263c36b6dbe..43655cd23a59 100644 --- a/vcl/source/filter/FilterConfigCache.cxx +++ b/vcl/source/filter/FilterConfigCache.cxx @@ -49,7 +49,8 @@ const char* FilterConfigCache::FilterConfigCacheEntry::InternalPixelFilterNameLi const char* FilterConfigCache::FilterConfigCacheEntry::InternalVectorFilterNameList[] = { IMP_SVMETAFILE, IMP_WMF, IMP_EMF, IMP_SVG, IMP_PDF, - EXP_SVMETAFILE, EXP_WMF, EXP_EMF, EXP_SVG, EXP_PDF, nullptr + EXP_SVMETAFILE, EXP_WMF, EXP_EMF, EXP_SVG, EXP_PDF, + IMP_EPS, EXP_EPS, nullptr }; const char* FilterConfigCache::FilterConfigCacheEntry::ExternalPixelFilterNameList[] = @@ -221,8 +222,8 @@ const char* FilterConfigCache::InternalFilterListForSvxLight[] = "bmp","1","SVBMP", "bmp","2","SVBMP", "dxf","1","idx", - "eps","1","ips", - "eps","2","eps", + "eps","1","SVIEPS", + "eps","2","SVEEPS", "gif","1","SVIGIF", "gif","2","egi", "jpg","1","SVIJPEG", diff --git a/vcl/source/filter/eps/eps.cxx b/vcl/source/filter/eps/eps.cxx new file mode 100644 index 000000000000..1f8719bea423 --- /dev/null +++ b/vcl/source/filter/eps/eps.cxx @@ -0,0 +1,2675 @@ +/* -*- 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 <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; + sal_uInt8 nNextChrSetId; // first unused ChrSet-Id + + 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 const & 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, const tools::Long* pDXArry, sal_Int32 nWidth, VirtualDevice const & rVDev ); + void ImplSetAttrForText( const Point & rPoint ); + void ImplWriteCharacter( char ); + void ImplWriteString( const OString&, VirtualDevice const & rVDev, const tools::Long* 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) + , pVDev() + , nBoundingX2(0) + , nBoundingY2(0) + , pGDIStack(nullptr) + , mnCursorPos(0) + , aColor() + , bLineColor(false) + , aLineColor() + , bFillColor(false) + , aFillColor() + , aTextColor() + , bTextFillColor(false) + , aTextFillColor() + , aBackgroundColor() + , eTextAlign() + , fLineWidth(0) + , fMiterLimit(0) + , eLineCap() + , eJoinType() + , aDashArray() + , maFont() + , maLastFont() + , nNextChrSetId(0) + , pPrefix(nullptr) + , nDataSize(0) + , nClearCode(0) + , nEOICode(0) + , nTableSize(0) + , nCodeSize(0) + , nOffset(0) + , dwShift(0) + , xStatusIndicator() +{ +} + +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; + + nNextChrSetId = 1; + + 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( "%%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, nullptr, 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, nullptr, 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.GetMask() ); + 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.GetMask() ); + 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.GetMask() ); + 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.IsTransparent() ) + { + if ( aWallpaper.IsGradient() ) + { + + // gradient action + + } + Bitmap aMask( aBitmapEx.GetMask() ); + 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, 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() ); + l_pVDev->AddGradientActions( rPolyPoly.GetBoundRect(), rGradient, 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, const tools::Long* pDXArry, bool bStretch ) +{ + sal_Int32 nLen = rString.getLength(); + if ( !nLen ) + return; + + if ( pDXArry ) + { + 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, const tools::Long* 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::BITMASK); + 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 = nullptr; // 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 const & 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 = 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 ) +{ + SvtGraphicStroke::DashArray l_aDashArray; + if ( rLineInfo.GetStyle() == LineStyle::Dash ) + l_aDashArray.push_back( 2 ); + 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, 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, 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/graphicfilter.cxx b/vcl/source/filter/graphicfilter.cxx index e80291e6a3e0..0917a69705a1 100644 --- a/vcl/source/filter/graphicfilter.cxx +++ b/vcl/source/filter/graphicfilter.cxx @@ -54,6 +54,8 @@ #include <filter/MetReader.hxx> #include <filter/RasReader.hxx> #include <filter/PcxReader.hxx> +#include <filter/EpsReader.hxx> +#include <filter/EpsWriter.hxx> #include <osl/module.hxx> #include <com/sun/star/uno/Reference.h> #include <com/sun/star/awt/Size.hpp> @@ -648,7 +650,6 @@ extern "C" bool icdGraphicImport( SvStream& rStream, Graphic& rGraphic, FilterCo extern "C" bool idxGraphicImport( SvStream& rStream, Graphic& rGraphic, FilterConfigItem* pConfigItem ); extern "C" bool ipbGraphicImport( SvStream& rStream, Graphic& rGraphic, FilterConfigItem* pConfigItem ); extern "C" bool ipdGraphicImport( SvStream& rStream, Graphic& rGraphic, FilterConfigItem* pConfigItem ); -extern "C" bool ipsGraphicImport( SvStream& rStream, Graphic& rGraphic, FilterConfigItem* pConfigItem ); #endif @@ -665,8 +666,6 @@ PFilterCall ImpFilterLibCacheEntry::GetImportFunction() mpfnImport = reinterpret_cast<PFilterCall>(maLibrary.getFunctionSymbol("ipbGraphicImport")); else if (maFormatName == "ipd") mpfnImport = reinterpret_cast<PFilterCall>(maLibrary.getFunctionSymbol("ipdGraphicImport")); - else if (maFormatName == "ips") - mpfnImport = reinterpret_cast<PFilterCall>(maLibrary.getFunctionSymbol("ipsGraphicImport")); #else if (maFormatName == "icd") mpfnImport = icdGraphicImport; @@ -676,8 +675,6 @@ PFilterCall ImpFilterLibCacheEntry::GetImportFunction() mpfnImport = ipbGraphicImport; else if (maFormatName == "ipd") mpfnImport = ipdGraphicImport; - else if (maFormatName == "ips") - mpfnImport = ipsGraphicImport; #endif } @@ -1158,7 +1155,6 @@ void GraphicFilter::MakeGraphicsAvailableThreaded(std::vector<Graphic*>& graphic } } - Graphic GraphicFilter::ImportUnloadedGraphic(SvStream& rIStream, sal_uInt64 sizeLimit, const Size* pSizeHint) { @@ -1407,7 +1403,7 @@ void GraphicFilter::preload() sal_Int32 nTokenCount = comphelper::string::getTokenCount(aFilterPath, ';'); ImpFilterLibCache& rCache = Cache::get(); static const std::initializer_list<std::u16string_view> aFilterNames = { - u"icd", u"idx", u"ipb", u"ipd", u"ips", + u"icd", u"idx", u"ipb", u"ipd" }; // Load library for each filter. @@ -1737,6 +1733,14 @@ ErrCode GraphicFilter::readPCX(SvStream & rStream, Graphic & rGraphic) 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::ImportGraphic( Graphic& rGraphic, const OUString& rPath, SvStream& rIStream, sal_uInt16 nFormat, sal_uInt16* pDeterminedFormat, GraphicFilterImportFlags nImportFlags, const css::uno::Sequence< css::beans::PropertyValue >* /*pFilterData*/, @@ -1870,6 +1874,10 @@ ErrCode GraphicFilter::ImportGraphic( Graphic& rGraphic, const OUString& rPath, { nStatus = readPCX(rIStream, rGraphic); } + else if (aFilterName.equalsIgnoreAsciiCase(IMP_EPS)) + { + nStatus = readEPS(rIStream, rGraphic); + } else nStatus = ERRCODE_GRFILTER_FILTERERROR; } @@ -1981,7 +1989,6 @@ ErrCode GraphicFilter::ExportGraphic( const Graphic& rGraphic, const INetURLObje #ifdef DISABLE_DYNLOADING extern "C" bool egiGraphicExport( SvStream& rStream, Graphic& rGraphic, FilterConfigItem* pConfigItem ); -extern "C" bool epsGraphicExport( SvStream& rStream, Graphic& rGraphic, FilterConfigItem* pConfigItem ); #endif @@ -2178,6 +2185,14 @@ ErrCode GraphicFilter::ExportGraphic( const Graphic& rGraphic, const OUString& r 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 ); @@ -2311,16 +2326,12 @@ ErrCode GraphicFilter::ExportGraphic( const Graphic& rGraphic, const OUString& r PFilterCall pFunc = nullptr; if (aExternalFilterName == "egi") pFunc = reinterpret_cast<PFilterCall>(aLibrary.getFunctionSymbol("egiGraphicExport")); - else if (aExternalFilterName == "eps") - pFunc = reinterpret_cast<PFilterCall>(aLibrary.getFunctionSymbol("epsGraphicExport")); // Execute dialog in DLL #else --nIdx; // Just one iteration PFilterCall pFunc = NULL; if (aExternalFilterName == "egi") pFunc = egiGraphicExport; - else if (aExternalFilterName == "eps") - pFunc = epsGraphicExport; #endif if( pFunc ) { diff --git a/vcl/source/filter/ieps/ieps.cxx b/vcl/source/filter/ieps/ieps.cxx new file mode 100644 index 000000000000..946fbf30b3d7 --- /dev/null +++ b/vcl/source/filter/ieps/ieps.cxx @@ -0,0 +1,824 @@ +/* -*- 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( 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::unique_ptr<sal_uInt8[]> pHeader( new sal_uInt8[ 22 ] ); + rStream.Seek( nPSStreamPos ); + rStream.ReadBytes(pHeader.get(), 22); // check PostScript header + bool bOk = ImplSearchEntry(pHeader.get(), 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/workben/epsfuzzer.cxx b/vcl/workben/epsfuzzer.cxx index effb05193aa1..1de2c63afe7c 100644 --- a/vcl/workben/epsfuzzer.cxx +++ b/vcl/workben/epsfuzzer.cxx @@ -10,6 +10,7 @@ #include <tools/stream.hxx> #include <vcl/FilterConfigItem.hxx> #include "commonfuzzer.hxx" +#include <filter/EpsReader.hxx> #include <config_features.h> #include <osl/detail/component-mapping.h> @@ -48,8 +49,6 @@ extern "C" void* lo_get_custom_widget_func(const char*) return nullptr; } -extern "C" bool ipsGraphicImport(SvStream& rStream, Graphic& rGraphic, FilterConfigItem* pConfigItem); - extern "C" int LLVMFuzzerInitialize(int *argc, char ***argv) { TypicalFuzzerInitialize(argc, argv); @@ -60,7 +59,7 @@ extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) { SvMemoryStream aStream(const_cast<uint8_t*>(data), size, StreamMode::READ); Graphic aGraphic; - (void)ipsGraphicImport(aStream, aGraphic, nullptr); + (void)ImportEpsGraphic(aStream, aGraphic); return 0; } diff --git a/vcl/workben/fftester.cxx b/vcl/workben/fftester.cxx index 893565e457d3..c6c2754d4dc6 100644 --- a/vcl/workben/fftester.cxx +++ b/vcl/workben/fftester.cxx @@ -48,6 +48,7 @@ #include <filter/MetReader.hxx> #include <filter/RasReader.hxx> #include <filter/PcxReader.hxx> +#include <filter/EpsReader.hxx> #include <osl/file.hxx> #include <osl/module.hxx> #include <tools/stream.hxx> @@ -230,18 +231,9 @@ SAL_IMPLEMENT_MAIN_WITH_ARGS(argc, argv) } else if (strcmp(argv[2], "eps") == 0) { - static PFilterCall pfnImport(nullptr); - if (!pfnImport) - { - osl::Module aLibrary; - aLibrary.loadRelative(&thisModule, "libgielo.so"); - pfnImport = reinterpret_cast<PFilterCall>( - aLibrary.getFunctionSymbol("ipsGraphicImport")); - aLibrary.release(); - } Graphic aGraphic; SvFileStream aFileStream(out, StreamMode::READ); - ret = static_cast<int>((*pfnImport)(aFileStream, aGraphic, nullptr)); + ret = static_cast<int>(ImportEpsGraphic(aFileStream, aGraphic)); } else if (strcmp(argv[2], "pct") == 0) { |