summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Repository.mk1
-rw-r--r--desktop/source/lib/init.cxx1
-rw-r--r--filter/Configuration_filter.mk4
-rw-r--r--filter/Library_flash.mk56
-rw-r--r--filter/Module_filter.mk1
-rw-r--r--filter/UIConfig_filter.mk1
-rw-r--r--filter/qa/unit/data/filter-dialogs-test.txt1
-rw-r--r--filter/source/config/cache/typedetection.cxx1
-rw-r--r--filter/source/config/fragments/filters/draw_flash_Export.xcu30
-rw-r--r--filter/source/config/fragments/filters/impress_flash_Export.xcu30
-rw-r--r--filter/source/config/fragments/types/graphic_SWF.xcu29
-rw-r--r--filter/source/flash/flash.component28
-rw-r--r--filter/source/flash/impswfdialog.cxx78
-rw-r--r--filter/source/flash/impswfdialog.hxx53
-rw-r--r--filter/source/flash/swfdialog.cxx159
-rw-r--r--filter/source/flash/swfdialog.hxx74
-rw-r--r--filter/source/flash/swfexporter.cxx729
-rw-r--r--filter/source/flash/swfexporter.hxx148
-rw-r--r--filter/source/flash/swffilter.cxx518
-rw-r--r--filter/source/flash/swfuno.cxx69
-rw-r--r--filter/source/flash/swfuno.hxx73
-rw-r--r--filter/source/flash/swfwriter.cxx401
-rw-r--r--filter/source/flash/swfwriter.hxx421
-rw-r--r--filter/source/flash/swfwriter1.cxx1960
-rw-r--r--filter/source/flash/swfwriter2.cxx575
-rw-r--r--filter/uiconfig/ui/impswfdialog.ui277
-rw-r--r--include/sal/log-areas.dox1
-rw-r--r--include/vcl/gdimetafiletools.hxx4
-rw-r--r--include/vcl/gradient.hxx1
-rw-r--r--scp2/source/graphicfilter/module_graphicfilter.ulf6
-rw-r--r--sfx2/source/dialog/filtergrouping.cxx13
-rw-r--r--solenv/inc/mime.types3
-rw-r--r--sysui/desktop/apparmor/program.soffice.bin2
-rw-r--r--vcl/source/gdi/gradient.cxx31
34 files changed, 5777 insertions, 2 deletions
diff --git a/Repository.mk b/Repository.mk
index c3c3378ecdea..c148cf315d6c 100644
--- a/Repository.mk
+++ b/Repository.mk
@@ -282,6 +282,7 @@ $(eval $(call gb_Helper_register_plugins_for_install,OOOLIBS,calc, \
$(eval $(call gb_Helper_register_libraries_for_install,OOOLIBS,graphicfilter, \
svgfilter \
+ flash \
wpftdraw \
graphicfilter \
))
diff --git a/desktop/source/lib/init.cxx b/desktop/source/lib/init.cxx
index 967264222b95..7d13a476a3f1 100644
--- a/desktop/source/lib/init.cxx
+++ b/desktop/source/lib/init.cxx
@@ -380,6 +380,7 @@ constexpr ExtensionMap aImpressExtensionMap[] =
{ "svg", u"impress_svg_Export"_ustr },
{ "xhtml", u"XHTML Impress File"_ustr },
{ "png", u"impress_png_Export"_ustr },
+ { "swf", u"impress_flash_Export"_ustr },
{ "bmp", u"impress_bmp_Export"_ustr },
{ "gif", u"impress_gif_Export"_ustr },
{ "tif", u"impress_tif_Export"_ustr },
diff --git a/filter/Configuration_filter.mk b/filter/Configuration_filter.mk
index 29d503c0cc33..6407f162c906 100644
--- a/filter/Configuration_filter.mk
+++ b/filter/Configuration_filter.mk
@@ -702,6 +702,7 @@ $(eval $(call filter_Configuration_add_types,fcfg_langpack,fcfg_drawgraphics_typ
eps_Encapsulated_PostScript \
gif_Graphics_Interchange \
graphic_HTML \
+ graphic_SWF \
jpg_JPEG \
met_OS2_Metafile \
mov_MOV \
@@ -767,6 +768,7 @@ $(eval $(call filter_Configuration_add_filters,fcfg_langpack,fcfg_drawgraphics_f
draw_emf_Export \
draw_emz_Export \
draw_eps_Export \
+ draw_flash_Export \
draw_gif_Export \
draw_html_Export \
draw_jpg_Export \
@@ -787,6 +789,7 @@ $(eval $(call filter_Configuration_add_types,fcfg_langpack,fcfg_impressgraphics_
eps_Encapsulated_PostScript \
gif_Graphics_Interchange \
graphic_HTML \
+ graphic_SWF \
impress_CGM_Computer_Graphics_Metafile \
jpg_JPEG \
met_OS2_Metafile \
@@ -812,6 +815,7 @@ $(eval $(call filter_Configuration_add_filters,fcfg_langpack,fcfg_impressgraphic
impress_bmp_Export \
impress_emf_Export \
impress_eps_Export \
+ impress_flash_Export \
impress_gif_Export \
impress_html_Export \
impress_jpg_Export \
diff --git a/filter/Library_flash.mk b/filter/Library_flash.mk
new file mode 100644
index 000000000000..2ad3c5538198
--- /dev/null
+++ b/filter/Library_flash.mk
@@ -0,0 +1,56 @@
+# -*- Mode: makefile-gmake; tab-width: 4; indent-tabs-mode: t -*-
+#
+# 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 .
+#
+
+$(eval $(call gb_Library_Library,flash))
+
+$(eval $(call gb_Library_set_componentfile,flash,filter/source/flash/flash,services))
+
+$(eval $(call gb_Library_use_sdk_api,flash))
+
+$(eval $(call gb_Library_use_libraries,flash,\
+ svt \
+ vcl \
+ utl \
+ tl \
+ i18nlangtag \
+ comphelper \
+ basegfx \
+ cppuhelper \
+ cppu \
+ sal \
+ salhelper \
+))
+
+$(eval $(call gb_Library_use_externals,flash,\
+ boost_headers \
+ zlib \
+))
+
+$(eval $(call gb_Library_add_exception_objects,flash,\
+ filter/source/flash/impswfdialog \
+ filter/source/flash/swfdialog \
+ filter/source/flash/swfexporter \
+ filter/source/flash/swffilter \
+ filter/source/flash/swfuno \
+ filter/source/flash/swfwriter \
+ filter/source/flash/swfwriter1 \
+ filter/source/flash/swfwriter2 \
+))
+
+# vim: set noet sw=4 ts=4:
diff --git a/filter/Module_filter.mk b/filter/Module_filter.mk
index c28c72705cec..23fec0d58a01 100644
--- a/filter/Module_filter.mk
+++ b/filter/Module_filter.mk
@@ -22,6 +22,7 @@ $(eval $(call gb_Module_add_targets,filter,\
Configuration_filter \
CustomTarget_svg \
Library_filterconfig \
+ Library_flash \
Library_icg \
Library_msfilter \
Library_odfflatxml \
diff --git a/filter/UIConfig_filter.mk b/filter/UIConfig_filter.mk
index 7d34128f56e0..34421120a05a 100644
--- a/filter/UIConfig_filter.mk
+++ b/filter/UIConfig_filter.mk
@@ -10,6 +10,7 @@
$(eval $(call gb_UIConfig_UIConfig,filter))
$(eval $(call gb_UIConfig_add_uifiles,filter,\
+ filter/uiconfig/ui/impswfdialog \
filter/uiconfig/ui/pdfgeneralpage \
filter/uiconfig/ui/pdflinkspage \
filter/uiconfig/ui/pdfoptionsdialog \
diff --git a/filter/qa/unit/data/filter-dialogs-test.txt b/filter/qa/unit/data/filter-dialogs-test.txt
index 22792ad28e6a..1f1715a194f8 100644
--- a/filter/qa/unit/data/filter-dialogs-test.txt
+++ b/filter/qa/unit/data/filter-dialogs-test.txt
@@ -44,6 +44,7 @@ filter/ui/pdflinkspage.ui
filter/ui/pdfsignpage.ui
filter/ui/xmlfiltertabpagegeneral.ui
filter/ui/xmlfiltertabpagetransformation.ui
+filter/ui/impswfdialog.ui
filter/ui/testxmlfilter.ui
filter/ui/warnpdfdialog.ui
filter/ui/xmlfiltersettings.ui
diff --git a/filter/source/config/cache/typedetection.cxx b/filter/source/config/cache/typedetection.cxx
index 66706a027259..b45aee107e39 100644
--- a/filter/source/config/cache/typedetection.cxx
+++ b/filter/source/config/cache/typedetection.cxx
@@ -284,6 +284,7 @@ int getFlatTypeRank(std::u16string_view rType)
// Export only
"writer_layout_dump_xml",
"writer_indexing_export",
+ "graphic_SWF",
"graphic_HTML",
// Internal use only
diff --git a/filter/source/config/fragments/filters/draw_flash_Export.xcu b/filter/source/config/fragments/filters/draw_flash_Export.xcu
new file mode 100644
index 000000000000..36e094c9bec9
--- /dev/null
+++ b/filter/source/config/fragments/filters/draw_flash_Export.xcu
@@ -0,0 +1,30 @@
+<!--
+ * 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 .
+-->
+ <node oor:name="draw_flash_Export" oor:op="replace">
+ <prop oor:name="Flags"><value>EXPORT ALIEN 3RDPARTYFILTER</value></prop>
+ <prop oor:name="UIComponent"><value>com.sun.star.Impress.FlashExportDialog</value></prop>
+ <prop oor:name="FilterService"><value>com.sun.star.comp.Impress.FlashExportFilter</value></prop>
+ <prop oor:name="UserData"><value></value></prop>
+ <prop oor:name="UIName">
+ <value xml:lang="en-US">Macromedia Flash (SWF)</value>
+ </prop>
+ <prop oor:name="FileFormatVersion"><value>0</value></prop>
+ <prop oor:name="Type"><value>graphic_SWF</value></prop>
+ <prop oor:name="TemplateName"/>
+ <prop oor:name="DocumentService"><value>com.sun.star.drawing.DrawingDocument</value></prop>
+ </node>
diff --git a/filter/source/config/fragments/filters/impress_flash_Export.xcu b/filter/source/config/fragments/filters/impress_flash_Export.xcu
new file mode 100644
index 000000000000..0cb961f7d287
--- /dev/null
+++ b/filter/source/config/fragments/filters/impress_flash_Export.xcu
@@ -0,0 +1,30 @@
+<!--
+ * 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 .
+-->
+ <node oor:name="impress_flash_Export" oor:op="replace">
+ <prop oor:name="Flags"><value>EXPORT ALIEN 3RDPARTYFILTER</value></prop>
+ <prop oor:name="UIComponent"><value>com.sun.star.Impress.FlashExportDialog</value></prop>
+ <prop oor:name="FilterService"><value>com.sun.star.comp.Impress.FlashExportFilter</value></prop>
+ <prop oor:name="UserData"><value></value></prop>
+ <prop oor:name="UIName">
+ <value xml:lang="en-US">Macromedia Flash (SWF)</value>
+ </prop>
+ <prop oor:name="FileFormatVersion"><value>0</value></prop>
+ <prop oor:name="Type"><value>graphic_SWF</value></prop>
+ <prop oor:name="TemplateName"/>
+ <prop oor:name="DocumentService"><value>com.sun.star.presentation.PresentationDocument</value></prop>
+ </node>
diff --git a/filter/source/config/fragments/types/graphic_SWF.xcu b/filter/source/config/fragments/types/graphic_SWF.xcu
new file mode 100644
index 000000000000..66f503d08623
--- /dev/null
+++ b/filter/source/config/fragments/types/graphic_SWF.xcu
@@ -0,0 +1,29 @@
+<!--
+ * 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 .
+-->
+ <node oor:name="graphic_SWF" oor:op="replace" >
+ <prop oor:name="DetectService"/>
+ <prop oor:name="URLPattern"/>
+ <prop oor:name="Extensions"><value>swf</value></prop>
+ <prop oor:name="MediaType"/>
+ <prop oor:name="Preferred"><value>false</value></prop>
+ <prop oor:name="PreferredFilter"/>
+ <prop oor:name="UIName">
+ <value>Macromedia Flash (SWF)</value>
+ </prop>
+ <prop oor:name="ClipboardFormat"/>
+ </node>
diff --git a/filter/source/flash/flash.component b/filter/source/flash/flash.component
new file mode 100644
index 000000000000..879466efb301
--- /dev/null
+++ b/filter/source/flash/flash.component
@@ -0,0 +1,28 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ * 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 .
+ -->
+
+<component loader="com.sun.star.loader.SharedLibrary" environment="@CPPU_ENV@"
+ prefix="flash" xmlns="http://openoffice.org/2010/uno-components">
+ <implementation name="com.sun.star.Impress.FlashExportDialog">
+ <service name="com.sun.star.Impress.FlashExportDialog"/>
+ </implementation>
+ <implementation name="com.sun.star.comp.Impress.FlashExportFilter">
+ <service name="com.sun.star.document.ExportFilter"/>
+ </implementation>
+</component>
diff --git a/filter/source/flash/impswfdialog.cxx b/filter/source/flash/impswfdialog.cxx
new file mode 100644
index 000000000000..5e50c2c4f1b6
--- /dev/null
+++ b/filter/source/flash/impswfdialog.cxx
@@ -0,0 +1,78 @@
+/* -*- 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 "impswfdialog.hxx"
+#include <tools/solar.h>
+
+using namespace com::sun::star::uno;
+using namespace com::sun::star::beans;
+
+ImpSWFDialog::ImpSWFDialog(weld::Window* pParent, Sequence<PropertyValue>& rFilterData)
+ : GenericDialogController(pParent, "filter/ui/impswfdialog.ui", "ImpSWFDialog")
+ , maConfigItem(u"Office.Common/Filter/Flash/Export/", &rFilterData)
+ , mxNumFldQuality(m_xBuilder->weld_spin_button("quality"))
+ , mxCheckExportAll(m_xBuilder->weld_check_button("exportall"))
+ , mxCheckExportBackgrounds(m_xBuilder->weld_check_button("exportbackgrounds"))
+ , mxCheckExportBackgroundObjects(m_xBuilder->weld_check_button("exportbackgroundobjects"))
+ , mxCheckExportSlideContents(m_xBuilder->weld_check_button("exportslidecontents"))
+ , mxCheckExportSound(m_xBuilder->weld_check_button("exportsound"))
+ , mxCheckExportOLEAsJPEG(m_xBuilder->weld_check_button("exportoleasjpeg"))
+ , mxCheckExportMultipleFiles(m_xBuilder->weld_check_button("exportmultiplefiles"))
+{
+ const sal_Int32 nCompressMode = maConfigItem.ReadInt32("CompressMode", 75);
+ mxNumFldQuality->set_value(nCompressMode);
+
+ mxCheckExportAll->set_active(true);
+ mxCheckExportSlideContents->set_active(true);
+ mxCheckExportSound->set_active(true);
+
+ mxCheckExportAll->connect_toggled(LINK(this, ImpSWFDialog, OnToggleCheckbox));
+
+ mxCheckExportBackgrounds->set_sensitive(false);
+ mxCheckExportBackgroundObjects->set_sensitive(false);
+ mxCheckExportSlideContents->set_sensitive(false);
+}
+
+ImpSWFDialog::~ImpSWFDialog() {}
+
+Sequence<PropertyValue> ImpSWFDialog::GetFilterData()
+{
+ sal_Int32 nCompressMode = static_cast<sal_Int32>(mxNumFldQuality->get_value());
+ maConfigItem.WriteInt32("CompressMode", nCompressMode);
+ maConfigItem.WriteBool("ExportAll", mxCheckExportAll->get_active());
+ maConfigItem.WriteBool("ExportBackgrounds", mxCheckExportBackgrounds->get_active());
+ maConfigItem.WriteBool("ExportBackgroundObjects", mxCheckExportBackgroundObjects->get_active());
+ maConfigItem.WriteBool("ExportSlideContents", mxCheckExportSlideContents->get_active());
+ maConfigItem.WriteBool("ExportSound", mxCheckExportSound->get_active());
+ maConfigItem.WriteBool("ExportOLEAsJPEG", mxCheckExportOLEAsJPEG->get_active());
+ maConfigItem.WriteBool("ExportMultipleFiles", mxCheckExportMultipleFiles->get_active());
+
+ Sequence<PropertyValue> aRet(maConfigItem.GetFilterData());
+
+ return aRet;
+}
+
+IMPL_LINK_NOARG(ImpSWFDialog, OnToggleCheckbox, weld::Toggleable&, void)
+{
+ mxCheckExportBackgrounds->set_sensitive(!mxCheckExportBackgrounds->get_sensitive());
+ mxCheckExportBackgroundObjects->set_sensitive(!mxCheckExportBackgroundObjects->get_sensitive());
+ mxCheckExportSlideContents->set_sensitive(!mxCheckExportSlideContents->get_sensitive());
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/filter/source/flash/impswfdialog.hxx b/filter/source/flash/impswfdialog.hxx
new file mode 100644
index 000000000000..b48cbaf5d980
--- /dev/null
+++ b/filter/source/flash/impswfdialog.hxx
@@ -0,0 +1,53 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#ifndef INCLUDED_FILTER_SOURCE_FLASH_IMPSWFDIALOG_HXX
+#define INCLUDED_FILTER_SOURCE_FLASH_IMPSWFDIALOG_HXX
+
+#include <com/sun/star/uno/Sequence.h>
+#include <com/sun/star/beans/PropertyValue.hpp>
+#include <vcl/FilterConfigItem.hxx>
+#include <vcl/weld.hxx>
+
+class ImpSWFDialog : public weld::GenericDialogController
+{
+private:
+ FilterConfigItem maConfigItem;
+
+ std::unique_ptr<weld::SpinButton> mxNumFldQuality;
+ std::unique_ptr<weld::CheckButton> mxCheckExportAll;
+ std::unique_ptr<weld::CheckButton> mxCheckExportBackgrounds;
+ std::unique_ptr<weld::CheckButton> mxCheckExportBackgroundObjects;
+ std::unique_ptr<weld::CheckButton> mxCheckExportSlideContents;
+ std::unique_ptr<weld::CheckButton> mxCheckExportSound;
+ std::unique_ptr<weld::CheckButton> mxCheckExportOLEAsJPEG;
+ std::unique_ptr<weld::CheckButton> mxCheckExportMultipleFiles;
+
+ DECL_LINK(OnToggleCheckbox, weld::Toggleable&, void);
+
+public:
+ ImpSWFDialog(weld::Window* pParent, css::uno::Sequence<css::beans::PropertyValue>& rFilterData);
+ virtual ~ImpSWFDialog() override;
+
+ css::uno::Sequence<css::beans::PropertyValue> GetFilterData();
+};
+
+#endif // INCLUDED_FILTER_SOURCE_FLASH_IMPSWFDIALOG_HXX
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/filter/source/flash/swfdialog.cxx b/filter/source/flash/swfdialog.cxx
new file mode 100644
index 000000000000..11518d3f5f35
--- /dev/null
+++ b/filter/source/flash/swfdialog.cxx
@@ -0,0 +1,159 @@
+/* -*- 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 "swfdialog.hxx"
+#include "swfuno.hxx"
+#include "impswfdialog.hxx"
+#include <comphelper/processfactory.hxx>
+#include <cppuhelper/queryinterface.hxx>
+#include <com/sun/star/view/XRenderable.hpp>
+#include <vcl/svapp.hxx>
+
+using namespace ::com::sun::star;
+using namespace ::com::sun::star::uno;
+using namespace ::com::sun::star::lang;
+using namespace ::com::sun::star::beans;
+using namespace ::com::sun::star::view;
+using namespace ::com::sun::star::document;
+
+#define SERVICE_NAME "com.sun.star.Impress.FlashExportDialog"
+
+OUString SWFDialog_getImplementationName() { return SERVICE_NAME; }
+
+Sequence<OUString> SWFDialog_getSupportedServiceNames()
+{
+ Sequence<OUString> aRet{ SERVICE_NAME };
+ return aRet;
+}
+
+Reference<XInterface> SWFDialog_createInstance(const Reference<XMultiServiceFactory>& rSMgr)
+{
+ return static_cast<cppu::OWeakObject*>(new SWFDialog(comphelper::getComponentContext(rSMgr)));
+}
+
+#undef SERVICE_NAME
+
+SWFDialog::SWFDialog(const Reference<XComponentContext>& rxContext)
+ : SWFDialog_Base(rxContext)
+{
+}
+
+SWFDialog::~SWFDialog() {}
+
+OUString SAL_CALL SWFDialog::getImplementationName() { return SWFDialog_getImplementationName(); }
+
+Sequence<OUString> SAL_CALL SWFDialog::getSupportedServiceNames()
+{
+ return SWFDialog_getSupportedServiceNames();
+}
+
+std::unique_ptr<weld::DialogController>
+SWFDialog::createDialog(const css::uno::Reference<css::awt::XWindow>& rParent)
+{
+ std::unique_ptr<weld::DialogController> xRet;
+
+ if (mxSrcDoc.is())
+ {
+ /* TODO: From the controller we may get information what page is visible and what shapes
+ are selected, if we optionally want to limit output to that
+ Any aSelection;
+
+ try
+ {
+ Reference< XController > xController( Reference< XModel >( mxSrcDoc, UNO_QUERY )->getCurrentController() );
+
+ if( xController.is() )
+ {
+ Reference< XSelectionSupplier > xView( xController, UNO_QUERY );
+
+ if( xView.is() )
+ xView->getSelection() >>= aSelection;
+ }
+ }
+ catch( RuntimeException )
+ {
+ }
+*/
+ xRet.reset(new ImpSWFDialog(Application::GetFrameWeld(rParent), maFilterData));
+ }
+
+ return xRet;
+}
+
+void SWFDialog::executedDialog(sal_Int16 nExecutionResult)
+{
+ if (nExecutionResult && m_xDialog)
+ maFilterData = static_cast<ImpSWFDialog*>(m_xDialog.get())->GetFilterData();
+
+ destroyDialog();
+}
+
+Reference<XPropertySetInfo> SAL_CALL SWFDialog::getPropertySetInfo()
+{
+ Reference<XPropertySetInfo> xInfo(createPropertySetInfo(getInfoHelper()));
+ return xInfo;
+}
+
+::cppu::IPropertyArrayHelper& SWFDialog::getInfoHelper() { return *getArrayHelper(); }
+
+::cppu::IPropertyArrayHelper* SWFDialog::createArrayHelper() const
+{
+ Sequence<Property> aProps;
+ describeProperties(aProps);
+ return new ::cppu::OPropertyArrayHelper(aProps);
+}
+
+Sequence<PropertyValue> SAL_CALL SWFDialog::getPropertyValues()
+{
+ sal_Int32 i, nCount;
+
+ for (i = 0, nCount = maMediaDescriptor.getLength(); i < nCount; i++)
+ {
+ if (maMediaDescriptor[i].Name == "FilterData")
+ break;
+ }
+
+ if (i == nCount)
+ maMediaDescriptor.realloc(++nCount);
+
+ auto pMediaDescriptor = maMediaDescriptor.getArray();
+
+ pMediaDescriptor[i].Name = "FilterData";
+ pMediaDescriptor[i].Value <<= maFilterData;
+
+ return maMediaDescriptor;
+}
+
+void SAL_CALL SWFDialog::setPropertyValues(const Sequence<PropertyValue>& rProps)
+{
+ maMediaDescriptor = rProps;
+
+ for (sal_Int32 i = 0, nCount = maMediaDescriptor.getLength(); i < nCount; i++)
+ {
+ if (maMediaDescriptor[i].Name == "FilterData")
+ {
+ maMediaDescriptor[i].Value >>= maFilterData;
+ break;
+ }
+ }
+}
+
+void SAL_CALL SWFDialog::setSourceDocument(const Reference<XComponent>& xDoc) { mxSrcDoc = xDoc; }
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/filter/source/flash/swfdialog.hxx b/filter/source/flash/swfdialog.hxx
new file mode 100644
index 000000000000..04bea8cc2f32
--- /dev/null
+++ b/filter/source/flash/swfdialog.hxx
@@ -0,0 +1,74 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#ifndef INCLUDED_FILTER_SOURCE_FLASH_SWFDIALOG_HXX
+#define INCLUDED_FILTER_SOURCE_FLASH_SWFDIALOG_HXX
+
+#include <com/sun/star/beans/XPropertyAccess.hpp>
+#include <com/sun/star/document/XExporter.hpp>
+
+#include <comphelper/proparrhlp.hxx>
+#include <svtools/genericunodialog.hxx>
+
+namespace vcl
+{
+class Window;
+}
+
+typedef cppu::ImplInheritanceHelper<::svt::OGenericUnoDialog, css::beans::XPropertyAccess,
+ css::document::XExporter>
+ SWFDialog_Base;
+class SWFDialog final : public ::comphelper::OPropertyArrayUsageHelper<SWFDialog>,
+ public SWFDialog_Base
+{
+private:
+ css::uno::Sequence<css::beans::PropertyValue> maMediaDescriptor;
+ css::uno::Sequence<css::beans::PropertyValue> maFilterData;
+ css::uno::Reference<css::lang::XComponent> mxSrcDoc;
+
+ // OGenericUnoDialog
+ virtual OUString SAL_CALL getImplementationName() override;
+ virtual css::uno::Sequence<OUString> SAL_CALL getSupportedServiceNames() override;
+ virtual std::unique_ptr<weld::DialogController>
+ createDialog(const css::uno::Reference<css::awt::XWindow>& rParent) override;
+ virtual void executedDialog(sal_Int16 nExecutionResult) override;
+ virtual css::uno::Reference<css::beans::XPropertySetInfo>
+ SAL_CALL getPropertySetInfo() override;
+ virtual ::cppu::IPropertyArrayHelper& SAL_CALL getInfoHelper() override;
+ virtual ::cppu::IPropertyArrayHelper* createArrayHelper() const override;
+
+ // XPropertyAccess
+ using ::cppu::OPropertySetHelper::getPropertyValues;
+ using ::cppu::OPropertySetHelper::setPropertyValues;
+ virtual css::uno::Sequence<css::beans::PropertyValue> SAL_CALL getPropertyValues() override;
+ virtual void SAL_CALL
+ setPropertyValues(const css::uno::Sequence<css::beans::PropertyValue>& aProps) override;
+
+ // XExporter
+ virtual void SAL_CALL
+ setSourceDocument(const css::uno::Reference<css::lang::XComponent>& xDoc) override;
+
+public:
+ explicit SWFDialog(const css::uno::Reference<css::uno::XComponentContext>& rxContext);
+ virtual ~SWFDialog() override;
+};
+
+#endif // INCLUDED_FILTER_SOURCE_FLASH_SWFDIALOG_HXX
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/filter/source/flash/swfexporter.cxx b/filter/source/flash/swfexporter.cxx
new file mode 100644
index 000000000000..a0f1fd7207d2
--- /dev/null
+++ b/filter/source/flash/swfexporter.cxx
@@ -0,0 +1,729 @@
+/* -*- 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 <com/sun/star/awt/Rectangle.hpp>
+#include <com/sun/star/beans/PropertyValue.hpp>
+#include <com/sun/star/beans/XPropertySet.hpp>
+#include <com/sun/star/drawing/GraphicExportFilter.hpp>
+#include <com/sun/star/drawing/XMasterPageTarget.hpp>
+#include <com/sun/star/drawing/XDrawPagesSupplier.hpp>
+#include <com/sun/star/container/XIndexAccess.hpp>
+#include <com/sun/star/task/XStatusIndicatorFactory.hpp>
+#include <com/sun/star/lang/XServiceInfo.hpp>
+#include <vcl/gdimtf.hxx>
+#include <unotools/tempfile.hxx>
+#include <osl/diagnose.h>
+#include <vcl/metaact.hxx>
+#include <vcl/graphicfilter.hxx>
+#include <vcl/gdimetafiletools.hxx>
+#include <memory>
+#include <vcl/filter/SvmWriter.hxx>
+#include <vcl/filter/SvmReader.hxx>
+
+#include "swfexporter.hxx"
+#include "swfwriter.hxx"
+
+using namespace ::com::sun::star::uno;
+using namespace ::com::sun::star::drawing;
+using namespace ::com::sun::star::task;
+using namespace ::swf;
+
+using com::sun::star::io::XOutputStream;
+using com::sun::star::beans::PropertyValue;
+using com::sun::star::container::XIndexAccess;
+using com::sun::star::beans::XPropertySet;
+using com::sun::star::lang::XComponent;
+using com::sun::star::lang::XServiceInfo;
+
+PageInfo::PageInfo()
+ : mnBackgroundID(0)
+ , mnObjectsID(0)
+ , mnForegroundID(0)
+{
+}
+
+FlashExporter::FlashExporter(const Reference<XComponentContext>& rxContext,
+
+ // #i56084# variables for selection export
+ const Reference<XShapes>& rxSelectedShapes,
+ const Reference<XDrawPage>& rxSelectedDrawPage,
+
+ sal_Int32 nJPEGCompressMode, bool bExportOLEAsJPEG)
+ : mxContext(rxContext)
+ // #i56084# variables for selection export
+ , mxSelectedShapes(rxSelectedShapes)
+ , mxSelectedDrawPage(rxSelectedDrawPage)
+ , mbExportSelection(false)
+
+ , mnDocWidth(0)
+ , mnDocHeight(0)
+ , mnJPEGcompressMode(nJPEGCompressMode)
+ , mbExportOLEAsJPEG(bExportOLEAsJPEG)
+ , mbPresentation(true)
+ , mnPageNumber(-1)
+{
+ if (mxSelectedDrawPage.is() && mxSelectedShapes.is() && mxSelectedShapes->getCount())
+ {
+ // #i56084# determine export selection
+ mbExportSelection = true;
+ }
+}
+
+FlashExporter::~FlashExporter() { Flush(); }
+
+void FlashExporter::Flush()
+{
+ mpWriter.reset();
+ maPagesMap.clear();
+}
+
+const sal_uInt16 cBackgroundDepth = 2;
+const sal_uInt16 cBackgroundObjectsDepth = 3;
+const sal_uInt16 cPageObjectsDepth = 4;
+const sal_uInt16 cWaitButtonDepth = 10;
+
+bool FlashExporter::exportAll(const Reference<XComponent>& xDoc,
+ Reference<XOutputStream> const& xOutputStream,
+ Reference<XStatusIndicator> const& xStatusIndicator)
+{
+ Reference<XServiceInfo> xDocServInfo(xDoc, UNO_QUERY);
+ if (xDocServInfo.is())
+ mbPresentation
+ = xDocServInfo->supportsService("com.sun.star.presentation.PresentationDocument");
+
+ Reference<XDrawPagesSupplier> xDrawPagesSupplier(xDoc, UNO_QUERY);
+ if (!xDrawPagesSupplier.is())
+ return false;
+
+ Reference<XIndexAccess> xDrawPages = xDrawPagesSupplier->getDrawPages();
+ if (!xDrawPages.is())
+ return false;
+
+ Reference<XDrawPage> xDrawPage;
+
+ // #i56084# set xDrawPage directly when exporting selection
+ if (mbExportSelection)
+ {
+ xDrawPage = mxSelectedDrawPage;
+ }
+ else
+ {
+ xDrawPages->getByIndex(0) >>= xDrawPage;
+ }
+
+ Reference<XPropertySet> xProp(xDrawPage, UNO_QUERY);
+ try
+ {
+ xProp->getPropertyValue("Width") >>= mnDocWidth;
+ xProp->getPropertyValue("Height") >>= mnDocHeight;
+
+ sal_Int32 nOutputWidth = 14400;
+ sal_Int32 nOutputHeight = (nOutputWidth * mnDocHeight) / mnDocWidth;
+ mpWriter.reset(
+ new Writer(nOutputWidth, nOutputHeight, mnDocWidth, mnDocHeight, mnJPEGcompressMode));
+ }
+ catch (const Exception&)
+ {
+ OSL_ASSERT(false);
+ return false; // no writer, no cookies
+ }
+
+ // #i56084# nPageCount is 1 when exporting selection
+ const sal_Int32 nPageCount = mbExportSelection ? 1 : xDrawPages->getCount();
+
+ if (xStatusIndicator.is())
+ {
+ xStatusIndicator->start("Macromedia Flash (SWF)", nPageCount);
+ }
+
+ for (sal_Int32 nPage = 0; nPage < nPageCount; nPage++)
+ {
+ // #i56084# keep PageNumber? We could determine the PageNumber of the single to-be-exported page
+ // when exporting the selection, but this is only used for swf internal, so no need to do so (AFAIK)
+ mnPageNumber = nPage + 1;
+
+ if (xStatusIndicator.is())
+ xStatusIndicator->setValue(nPage);
+
+ // #i56084# get current xDrawPage when not exporting selection; else already set above
+ if (!mbExportSelection)
+ {
+ xDrawPages->getByIndex(nPage) >>= xDrawPage;
+ }
+
+ if (!xDrawPage.is())
+ continue;
+
+ Reference<XPropertySet> xPropSet(xDrawPage, UNO_QUERY);
+ if (mbPresentation)
+ {
+ bool bVisible = false;
+ xPropSet->getPropertyValue("Visible") >>= bVisible;
+ if (!bVisible)
+ continue;
+ }
+
+ // #i56084# no background when exporting selection
+ if (!mbExportSelection)
+ {
+ exportBackgrounds(xDrawPage, nPage, false);
+ exportBackgrounds(xDrawPage, nPage, true);
+ }
+
+ maPagesMap[nPage].mnForegroundID = mpWriter->startSprite();
+
+ // #i56084# directly export selection in export selection mode
+ if (mbExportSelection)
+ {
+ exportShapes(mxSelectedShapes, false, false);
+ }
+ else
+ {
+ exportDrawPageContents(xDrawPage, false, false);
+ }
+
+ mpWriter->endSprite();
+
+ // AS: If the background is different than the previous slide,
+ // we have to remove the old one and place the new one.
+ if (nPage)
+ {
+ if (maPagesMap[nPage].mnBackgroundID != maPagesMap[nPage - 1].mnBackgroundID)
+ {
+ mpWriter->removeShape(cBackgroundDepth);
+ mpWriter->placeShape(maPagesMap[nPage].mnBackgroundID, cBackgroundDepth, 0, 0);
+ }
+
+ if (maPagesMap[nPage].mnObjectsID != maPagesMap[nPage - 1].mnObjectsID)
+ {
+ mpWriter->removeShape(cBackgroundObjectsDepth);
+ mpWriter->placeShape(maPagesMap[nPage].mnObjectsID, cBackgroundObjectsDepth, 0, 0);
+ }
+
+ // AS: Remove the Foreground of the previous slide.
+ mpWriter->removeShape(cPageObjectsDepth);
+ }
+ else
+ {
+ mpWriter->placeShape(maPagesMap[nPage].mnBackgroundID, cBackgroundDepth, 0, 0);
+ mpWriter->placeShape(maPagesMap[nPage].mnObjectsID, cBackgroundObjectsDepth, 0, 0);
+ }
+
+ mpWriter->placeShape(maPagesMap[nPage].mnForegroundID, cPageObjectsDepth, 0, 0);
+
+ mpWriter->waitOnClick(cWaitButtonDepth);
+ mpWriter->showFrame();
+ }
+
+ mpWriter->removeShape(cBackgroundDepth);
+ mpWriter->removeShape(cBackgroundObjectsDepth);
+ mpWriter->removeShape(cPageObjectsDepth);
+ mpWriter->gotoFrame(0);
+ mpWriter->showFrame();
+
+ mpWriter->storeTo(xOutputStream);
+
+ return true;
+}
+
+bool FlashExporter::exportSlides(const Reference<XDrawPage>& xDrawPage,
+ Reference<XOutputStream> const& xOutputStream)
+{
+ Reference<XPropertySet> xPropSet(xDrawPage, UNO_QUERY);
+ if (!xDrawPage.is() || !xPropSet.is())
+ return false;
+
+ try
+ {
+ if (!mpWriter)
+ {
+ xPropSet->getPropertyValue("Width") >>= mnDocWidth;
+ xPropSet->getPropertyValue("Height") >>= mnDocHeight;
+
+ mpWriter.reset(new Writer(14400, 10800, mnDocWidth, mnDocHeight, mnJPEGcompressMode));
+ }
+
+ if (mbPresentation)
+ {
+ bool bVisible = false;
+ xPropSet->getPropertyValue("Visible") >>= bVisible;
+ if (!bVisible)
+ return false;
+ }
+ }
+ catch (const Exception&)
+ {
+ OSL_ASSERT(false);
+ }
+
+ exportDrawPageContents(xDrawPage, true, false);
+
+ mpWriter->storeTo(xOutputStream);
+
+ return true;
+}
+
+sal_uInt16 FlashExporter::exportBackgrounds(const Reference<XDrawPage>& xDrawPage,
+ Reference<XOutputStream> const& xOutputStream,
+ sal_uInt16 nPage, bool bExportObjects)
+{
+ Reference<XPropertySet> xPropSet(xDrawPage, UNO_QUERY);
+ if (!xDrawPage.is() || !xPropSet.is())
+ return 0;
+
+ if (!mpWriter)
+ {
+ xPropSet->getPropertyValue("Width") >>= mnDocWidth;
+ xPropSet->getPropertyValue("Height") >>= mnDocHeight;
+
+ mpWriter.reset(new Writer(14400, 10800, mnDocWidth, mnDocHeight, mnJPEGcompressMode));
+ }
+
+ sal_uInt16 ret = exportBackgrounds(xDrawPage, nPage, bExportObjects);
+
+ if (ret != nPage)
+ return ret;
+
+ if (bExportObjects)
+ mpWriter->placeShape(maPagesMap[nPage].mnObjectsID, uInt16_(1), 0, 0);
+ else
+ mpWriter->placeShape(maPagesMap[nPage].mnBackgroundID, uInt16_(0), 0, 0);
+
+ mpWriter->storeTo(xOutputStream);
+
+ return nPage;
+}
+
+sal_uInt16 FlashExporter::exportBackgrounds(Reference<XDrawPage> const& xDrawPage, sal_uInt16 nPage,
+ bool bExportObjects)
+{
+ Reference<XPropertySet> xPropSet(xDrawPage, UNO_QUERY);
+ if (!xDrawPage.is() || !xPropSet.is())
+ return 0;
+
+ bool bBackgroundVisible = true;
+ bool bBackgroundObjectsVisible = true;
+
+ if (mbPresentation)
+ {
+ xPropSet->getPropertyValue("IsBackgroundVisible") >>= bBackgroundVisible;
+ xPropSet->getPropertyValue("IsBackgroundObjectsVisible") >>= bBackgroundObjectsVisible;
+ }
+
+ if (bExportObjects)
+ {
+ if (bBackgroundObjectsVisible)
+ {
+ Reference<XMasterPageTarget> xMasterPageTarget(xDrawPage, UNO_QUERY);
+ if (!xMasterPageTarget.is())
+ {
+ maPagesMap[nPage].mnObjectsID = 0xffff;
+ return 0xffff;
+ }
+ Reference<XDrawPage> aTemp = xMasterPageTarget->getMasterPage();
+ sal_uInt16 ret = exportMasterPageObjects(nPage, aTemp);
+ if (ret != nPage)
+ return ret;
+ }
+ else
+ {
+ maPagesMap[nPage].mnObjectsID = 0xffff;
+ return 0xffff;
+ }
+ }
+ else
+ {
+ if (bBackgroundVisible)
+ {
+ sal_uInt16 ret = exportDrawPageBackground(nPage, xDrawPage);
+
+ if (ret != nPage)
+ return ret;
+ }
+ else
+ {
+ maPagesMap[nPage].mnBackgroundID = 0xffff;
+ return 0xffff;
+ }
+ }
+
+ return nPage;
+}
+
+static sal_Int32 nPlaceDepth;
+// AS: A Slide can have a private background or use its masterpage's background.
+// We use the checksums on the metafiles to tell if backgrounds are the same and
+// should be reused. The return value indicates which slide's background to use.
+// If the return value != nPage, then there is no background (if == -1) or the
+// background has already been exported.
+sal_uInt16 FlashExporter::exportDrawPageBackground(sal_uInt16 nPage,
+ Reference<XDrawPage> const& xPage)
+{
+ sal_uInt16 rBackgroundID;
+
+ GDIMetaFile aMtfPrivate, aMtfMaster;
+ Reference<XComponent> xComponent(xPage, UNO_QUERY);
+
+ Reference<XMasterPageTarget> xMasterPageTarget(xPage, UNO_QUERY);
+ if (!xMasterPageTarget.is())
+ return 0xffff;
+
+ Reference<XDrawPage> xMasterPage = xMasterPageTarget->getMasterPage();
+ if (!xMasterPage.is())
+ return 0xffff;
+
+ Reference<XComponent> xCompMaster(xMasterPage, UNO_QUERY);
+
+ getMetaFile(xCompMaster, aMtfMaster, true);
+ getMetaFile(xComponent, aMtfPrivate, true);
+
+ BitmapChecksum masterchecksum = SvmWriter::GetChecksum(aMtfMaster);
+ BitmapChecksum privatechecksum = SvmWriter::GetChecksum(aMtfPrivate);
+
+ // AS: If the slide has its own background
+ if (privatechecksum)
+ {
+ ChecksumCache::iterator it = gPrivateCache.find(privatechecksum);
+
+ // AS: and we've previously encountered this background, just return
+ // the previous index.
+ if (gPrivateCache.end() != it)
+ {
+ maPagesMap[nPage].mnBackgroundID = maPagesMap[it->second].mnBackgroundID;
+ return it->second;
+ }
+ else
+ {
+ // AS: Otherwise, cache this checksum.
+ gPrivateCache[privatechecksum] = nPage;
+
+ rBackgroundID = mpWriter->defineShape(aMtfPrivate);
+
+ maPagesMap[nPage].mnBackgroundID = rBackgroundID;
+ return nPage;
+ }
+ }
+
+ // AS: Ok, no private background. Use the master page's.
+ // AS: Have we already exported this master page?
+ ChecksumCache::iterator it = gMasterCache.find(masterchecksum);
+
+ if (gMasterCache.end() != it)
+ {
+ maPagesMap[nPage].mnBackgroundID = maPagesMap[it->second].mnBackgroundID;
+
+ return it->second; // AS: Yes, so don't export it again.
+ }
+
+ gMasterCache[masterchecksum] = nPage;
+
+ rBackgroundID = mpWriter->defineShape(aMtfMaster);
+
+ maPagesMap[nPage].mnBackgroundID = rBackgroundID;
+
+ return nPage;
+}
+
+sal_uInt16 FlashExporter::exportMasterPageObjects(sal_uInt16 nPage,
+ Reference<XDrawPage> const& xMasterPage)
+{
+ BitmapChecksum shapesum = ActionSummer(xMasterPage);
+
+ ChecksumCache::iterator it = gObjectCache.find(shapesum);
+
+ if (gObjectCache.end() != it)
+ {
+ maPagesMap[nPage].mnObjectsID = maPagesMap[it->second].mnObjectsID;
+
+ return it->second; // AS: Yes, so don't export it again.
+ }
+
+ gObjectCache[shapesum] = nPage;
+
+ sal_uInt16 rObjectsID = mpWriter->startSprite();
+ exportDrawPageContents(xMasterPage, false, true);
+ mpWriter->endSprite();
+
+ maPagesMap[nPage].mnObjectsID = rObjectsID;
+
+ return nPage;
+}
+
+/** export's the definition of the shapes inside this drawing page and adds the
+ shape infos to the current PageInfo */
+void FlashExporter::exportDrawPageContents(const Reference<XDrawPage>& xPage, bool bStream,
+ bool bMaster)
+{
+ exportShapes(xPage, bStream, bMaster);
+}
+
+/** export's the definition of the shapes inside this XShapes container and adds the
+ shape infos to the current PageInfo */
+void FlashExporter::exportShapes(const Reference<XShapes>& xShapes, bool bStream, bool bMaster)
+{
+ OSL_ENSURE((xShapes->getCount() <= 0xffff),
+ "overflow in FlashExporter::exportDrawPageContents()");
+
+ sal_uInt16 nShapeCount
+ = static_cast<sal_uInt16>(std::min(xShapes->getCount(), sal_Int32(0xffff)));
+ sal_uInt16 nShape;
+
+ Reference<XShape> xShape;
+
+ for (nShape = 0; nShape < nShapeCount; nShape++)
+ {
+ xShapes->getByIndex(nShape) >>= xShape;
+
+ if (xShape.is())
+ {
+ Reference<XShapes> xShapes2(xShape, UNO_QUERY);
+ if (xShapes2.is() && xShape->getShapeType() == "com.sun.star.drawing.GroupShape")
+ // export the contents of group shapes, but we only ever stream at the top
+ // recursive level anyway, so pass false for streaming.
+ exportShapes(xShapes2, false, bMaster);
+ else
+ exportShape(xShape, bMaster);
+ }
+
+ if (bStream)
+ mpWriter->showFrame();
+ }
+}
+
+/** export this shape definition and adds it's info to the current PageInfo */
+void FlashExporter::exportShape(const Reference<XShape>& xShape, bool bMaster)
+{
+ Reference<XPropertySet> xPropSet(xShape, UNO_QUERY);
+ if (!xPropSet.is())
+ return;
+
+ if (mbPresentation)
+ {
+ try
+ {
+ // skip empty presentation objects
+ bool bEmpty = false;
+ xPropSet->getPropertyValue("IsEmptyPresentationObject") >>= bEmpty;
+ if (bEmpty)
+ return;
+
+ // don't export presentation placeholders on masterpage
+ // they can be non empty when user edits the default texts
+ if (bMaster)
+ {
+ OUString aShapeType(xShape->getShapeType());
+ if (aShapeType == "com.sun.star.presentation.TitleTextShape"
+ || aShapeType == "com.sun.star.presentation.OutlinerShape"
+ || aShapeType == "com.sun.star.presentation.HeaderShape"
+ || aShapeType == "com.sun.star.presentation.FooterShape"
+ || aShapeType == "com.sun.star.presentation.SlideNumberShape"
+ || aShapeType == "com.sun.star.presentation.DateTimeShape")
+ return;
+ }
+ }
+ catch (const Exception&)
+ {
+ // TODO: If we are exporting a draw, this property is not available
+ }
+ }
+
+ try
+ {
+ css::awt::Rectangle aBoundRect;
+ xPropSet->getPropertyValue("BoundRect") >>= aBoundRect;
+
+ std::unique_ptr<ShapeInfo> pShapeInfo(new ShapeInfo());
+ pShapeInfo->mnX = aBoundRect.X;
+ pShapeInfo->mnY = aBoundRect.Y;
+ pShapeInfo->mnWidth = aBoundRect.Width;
+ pShapeInfo->mnHeight = aBoundRect.Height;
+
+ GDIMetaFile aMtf;
+ Reference<XComponent> xComponent(xShape, UNO_QUERY);
+
+ bool bIsOleObject = xShape->getShapeType() == "com.sun.star.presentation.OLE2Shape"
+ || xShape->getShapeType() == "com.sun.star.drawing.OLE2Shape";
+
+ getMetaFile(xComponent, aMtf);
+
+ // AS: If it's an OLE object, then export a JPEG if the user requested.
+ // In this case, we use the bounding rect info generated in the first getMetaFile
+ // call, and then clear the metafile and add a BMP action. This may be turned into
+ // a JPEG, depending on what gives the best compression.
+ if (bIsOleObject && mbExportOLEAsJPEG)
+ getMetaFile(xComponent, aMtf, false, true);
+
+ sal_uInt16 nID;
+ BitmapChecksum checksum = SvmWriter::GetChecksum(aMtf);
+
+ ChecksumCache::iterator it = gMetafileCache.find(checksum);
+
+ if (gMetafileCache.end() != it)
+ nID = it->second;
+ else
+ {
+ nID = mpWriter->defineShape(aMtf);
+ gMetafileCache[checksum] = nID;
+ }
+
+ if (!nID)
+ return;
+
+ pShapeInfo->mnID = nID;
+
+ // pPageInfo->addShape( pShapeInfo );
+
+ mpWriter->placeShape(pShapeInfo->mnID, uInt16_(nPlaceDepth++), pShapeInfo->mnX,
+ pShapeInfo->mnY);
+ }
+ catch (const Exception&)
+ {
+ OSL_ASSERT(false);
+ }
+}
+
+bool FlashExporter::getMetaFile(Reference<XComponent> const& xComponent, GDIMetaFile& rMtf,
+ bool bOnlyBackground /* = false */,
+ bool bExportAsJPEG /* = false */)
+{
+ if (!mxGraphicExporter.is())
+ mxGraphicExporter = GraphicExportFilter::create(mxContext);
+
+ utl::TempFileNamed aFile;
+ aFile.EnableKillingFile();
+
+ Sequence<PropertyValue> aFilterData(bExportAsJPEG ? 3 : 2);
+
+ auto pFilterData = aFilterData.getArray();
+
+ pFilterData[0].Name = "Version";
+ pFilterData[0].Value <<= sal_Int32(6000);
+ pFilterData[1].Name = "PageNumber";
+ pFilterData[1].Value <<= mnPageNumber;
+
+ if (bExportAsJPEG)
+ {
+ pFilterData[2].Name = "Translucent";
+ pFilterData[2].Value <<= true;
+ }
+
+ Sequence<PropertyValue> aDescriptor(bOnlyBackground ? 4 : 3);
+
+ auto pDescriptor = aDescriptor.getArray();
+
+ pDescriptor[0].Name = "FilterName";
+
+ // AS: If we've been asked to export as an image, then use the BMP filter.
+ // Otherwise, use SVM. This is useful for things that don't convert well as
+ // metafiles, like the occasional OLE object.
+ pDescriptor[0].Value <<= bExportAsJPEG ? OUString("PNG") : OUString("SVM");
+
+ pDescriptor[1].Name = "URL";
+ pDescriptor[1].Value <<= aFile.GetURL();
+ pDescriptor[2].Name = "FilterData";
+ pDescriptor[2].Value <<= aFilterData;
+ if (bOnlyBackground)
+ {
+ pDescriptor[3].Name = "ExportOnlyBackground";
+ pDescriptor[3].Value <<= bOnlyBackground;
+ }
+ mxGraphicExporter->setSourceDocument(xComponent);
+ mxGraphicExporter->filter(aDescriptor);
+
+ if (bExportAsJPEG)
+ {
+ Graphic aGraphic;
+ GraphicFilter aFilter(false);
+
+ aFilter.ImportGraphic(aGraphic, aFile.GetURL(), *aFile.GetStream(StreamMode::READ));
+ BitmapEx rBitmapEx(aGraphic.GetBitmapEx().GetBitmap(), Color(255, 255, 255));
+
+ tools::Rectangle clipRect;
+ for (size_t i = 0, nCount = rMtf.GetActionSize(); i < nCount; i++)
+ {
+ const MetaAction* pAction = rMtf.GetAction(i);
+ if (pAction->GetType() == MetaActionType::ISECTRECTCLIPREGION)
+ {
+ const MetaISectRectClipRegionAction* pA
+ = static_cast<const MetaISectRectClipRegionAction*>(pAction);
+ clipRect = pA->GetRect();
+ break;
+ }
+ }
+ MetaBmpExScaleAction* pmetaAct
+ = new MetaBmpExScaleAction(Point(clipRect.Left(), clipRect.Top()),
+ Size(clipRect.GetWidth(), clipRect.GetHeight()), rBitmapEx);
+
+ rMtf.Clear();
+ rMtf.AddAction(pmetaAct);
+ }
+ else
+ {
+ SvmReader aReader(*aFile.GetStream(StreamMode::READ));
+ aReader.Read(rMtf);
+
+ if (usesClipActions(rMtf))
+ {
+ // #i121267# It is necessary to prepare the metafile since the export does *not* support
+ // clip regions. This tooling method clips the geometry content of the metafile internally
+ // against its own clip regions, so that the export is safe to ignore clip regions
+ clipMetafileContentAgainstOwnRegions(rMtf);
+ }
+ }
+
+ return rMtf.GetActionSize() != 0;
+}
+
+BitmapChecksum FlashExporter::ActionSummer(Reference<XShape> const& xShape)
+{
+ Reference<XShapes> xShapes(xShape, UNO_QUERY);
+
+ if (xShapes.is())
+ {
+ return ActionSummer(xShapes);
+ }
+ else
+ {
+ Reference<XComponent> xComponentShape(xShape, UNO_QUERY);
+
+ GDIMetaFile aMtf;
+ getMetaFile(xComponentShape, aMtf);
+
+ return SvmWriter::GetChecksum(aMtf);
+ }
+}
+
+BitmapChecksum FlashExporter::ActionSummer(Reference<XShapes> const& xShapes)
+{
+ sal_uInt32 nShapeCount = xShapes->getCount();
+ BitmapChecksum shapecount = 0;
+
+ Reference<XShape> xShape2;
+
+ for (sal_uInt32 nShape = 0; nShape < nShapeCount; nShape++)
+ {
+ xShapes->getByIndex(nShape) >>= xShape2;
+
+ shapecount += ActionSummer(xShape2);
+ }
+
+ return shapecount;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/filter/source/flash/swfexporter.hxx b/filter/source/flash/swfexporter.hxx
new file mode 100644
index 000000000000..ffe17fbb8c76
--- /dev/null
+++ b/filter/source/flash/swfexporter.hxx
@@ -0,0 +1,148 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+#ifndef INCLUDED_FILTER_SOURCE_FLASH_SWFEXPORTER_HXX
+#define INCLUDED_FILTER_SOURCE_FLASH_SWFEXPORTER_HXX
+
+#include <com/sun/star/lang/XComponent.hpp>
+#include <com/sun/star/uno/XComponentContext.hpp>
+#include <com/sun/star/task/XStatusIndicator.hpp>
+#include <com/sun/star/drawing/XDrawPage.hpp>
+#include <com/sun/star/drawing/XGraphicExportFilter.hpp>
+#include <com/sun/star/io/XOutputStream.hpp>
+#include <vcl/checksum.hxx>
+
+#include <map>
+#include <memory>
+
+typedef ::std::map<BitmapChecksum, sal_uInt16> ChecksumCache;
+
+class GDIMetaFile;
+
+namespace swf
+{
+class Writer;
+
+class ShapeInfo
+{
+public:
+ sal_uInt16 mnID; // the character id for the sprite definition of this shape
+
+ sal_Int32 mnX;
+ sal_Int32 mnY;
+
+ sal_Int32 mnWidth;
+ sal_Int32 mnHeight;
+
+ ShapeInfo()
+ : mnID(0)
+ , mnX(0)
+ , mnY(0)
+ , mnWidth(0)
+ , mnHeight(0)
+ {
+ }
+};
+
+struct PageInfo
+{
+ sal_uInt16 mnBackgroundID;
+ sal_uInt16 mnObjectsID;
+ sal_uInt16 mnForegroundID;
+
+ PageInfo();
+};
+
+class FlashExporter
+{
+public:
+ FlashExporter(const css::uno::Reference<css::uno::XComponentContext>& rxContext,
+
+ // #i56084# variables for selection export
+ const css::uno::Reference<css::drawing::XShapes>& rxSelectedShapes,
+ const css::uno::Reference<css::drawing::XDrawPage>& rxSelectedDrawPage,
+
+ sal_Int32 nJPEGCompressMode, bool bExportOLEAsJPEG);
+ ~FlashExporter();
+
+ void Flush();
+
+ bool exportAll(const css::uno::Reference<css::lang::XComponent>& xDoc,
+ css::uno::Reference<css::io::XOutputStream> const& xOutputStream,
+ css::uno::Reference<css::task::XStatusIndicator> const& xStatusIndicator);
+ bool exportSlides(const css::uno::Reference<css::drawing::XDrawPage>& xDrawPage,
+ css::uno::Reference<css::io::XOutputStream> const& xOutputStream);
+ sal_uInt16 exportBackgrounds(const css::uno::Reference<css::drawing::XDrawPage>& xDrawPage,
+ css::uno::Reference<css::io::XOutputStream> const& xOutputStream,
+ sal_uInt16 nPage, bool bExportObjects);
+ sal_uInt16 exportBackgrounds(css::uno::Reference<css::drawing::XDrawPage> const& xDrawPage,
+ sal_uInt16 nPage, bool bExportObjects);
+
+ ChecksumCache gMasterCache;
+ ChecksumCache gPrivateCache;
+ ChecksumCache gObjectCache;
+ ChecksumCache gMetafileCache;
+
+private:
+ css::uno::Reference<css::uno::XComponentContext> mxContext;
+
+ // #i56084# variables for selection export
+ const css::uno::Reference<css::drawing::XShapes> mxSelectedShapes;
+ const css::uno::Reference<css::drawing::XDrawPage> mxSelectedDrawPage;
+ bool mbExportSelection;
+
+ css::uno::Reference<css::drawing::XGraphicExportFilter> mxGraphicExporter;
+
+ ::std::map<sal_uInt32, PageInfo> maPagesMap;
+
+ sal_uInt16 exportDrawPageBackground(sal_uInt16 nPage,
+ css::uno::Reference<css::drawing::XDrawPage> const& xPage);
+ sal_uInt16
+ exportMasterPageObjects(sal_uInt16 nPage,
+ css::uno::Reference<css::drawing::XDrawPage> const& xMasterPage);
+
+ void exportDrawPageContents(const css::uno::Reference<css::drawing::XDrawPage>& xPage,
+ bool bStream, bool bMaster);
+ void exportShapes(const css::uno::Reference<css::drawing::XShapes>& xShapes, bool bStream,
+ bool bMaster);
+ void exportShape(const css::uno::Reference<css::drawing::XShape>& xShape, bool bMaster);
+
+ BitmapChecksum ActionSummer(css::uno::Reference<css::drawing::XShape> const& xShape);
+ BitmapChecksum ActionSummer(css::uno::Reference<css::drawing::XShapes> const& xShapes);
+
+ bool getMetaFile(css::uno::Reference<css::lang::XComponent> const& xComponent,
+ GDIMetaFile& rMtf, bool bOnlyBackground = false, bool bExportAsJPEG = false);
+
+ std::unique_ptr<Writer> mpWriter;
+
+ sal_Int32 mnDocWidth;
+ sal_Int32 mnDocHeight;
+
+ sal_Int32 mnJPEGcompressMode;
+
+ bool mbExportOLEAsJPEG;
+
+ bool mbPresentation;
+
+ sal_Int32 mnPageNumber;
+};
+}
+
+#endif
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/filter/source/flash/swffilter.cxx b/filter/source/flash/swffilter.cxx
new file mode 100644
index 000000000000..bdbaec86ce4a
--- /dev/null
+++ b/filter/source/flash/swffilter.cxx
@@ -0,0 +1,518 @@
+/* -*- 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 <com/sun/star/frame/Desktop.hpp>
+#include <com/sun/star/frame/XStorable.hpp>
+#include <com/sun/star/document/XFilter.hpp>
+#include <com/sun/star/document/XExporter.hpp>
+#include <com/sun/star/lang/XInitialization.hpp>
+#include <com/sun/star/lang/XServiceInfo.hpp>
+#include <com/sun/star/drawing/XDrawPagesSupplier.hpp>
+#include <com/sun/star/drawing/XDrawView.hpp>
+#include <com/sun/star/container/XIndexAccess.hpp>
+#include <com/sun/star/task/XStatusIndicatorFactory.hpp>
+#include <com/sun/star/io/IOException.hpp>
+#include <com/sun/star/io/XOutputStream.hpp>
+
+#include <com/sun/star/drawing/XDrawPage.hpp>
+#include <com/sun/star/drawing/XShapes.hpp>
+#include <com/sun/star/frame/XController.hpp>
+#include <com/sun/star/view/XSelectionSupplier.hpp>
+
+#include <cppuhelper/implbase.hxx>
+#include <cppuhelper/supportsservice.hxx>
+#include <comphelper/processfactory.hxx>
+#include <osl/file.hxx>
+
+#include "swfexporter.hxx"
+#include "swfuno.hxx"
+
+#include <string.h>
+
+using namespace ::com::sun::star::uno;
+using namespace ::com::sun::star::frame;
+using namespace ::com::sun::star::lang;
+using namespace ::com::sun::star::drawing;
+using namespace ::com::sun::star::task;
+using namespace ::com::sun::star::view;
+
+using ::com::sun::star::lang::XComponent;
+using ::com::sun::star::beans::PropertyValue;
+using ::com::sun::star::io::XOutputStream;
+using ::com::sun::star::container::XIndexAccess;
+
+namespace swf
+{
+namespace
+{
+class OslOutputStreamWrapper : public ::cppu::WeakImplHelper<css::io::XOutputStream>
+{
+ osl::File maFile;
+
+public:
+ explicit OslOutputStreamWrapper(const OUString& rFileName)
+ : maFile(rFileName)
+ {
+ osl_removeFile(rFileName.pData);
+ (void)maFile.open(osl_File_OpenFlag_Create | osl_File_OpenFlag_Write);
+ }
+
+ // css::io::XOutputStream
+ virtual void SAL_CALL writeBytes(const css::uno::Sequence<sal_Int8>& aData) override;
+ virtual void SAL_CALL flush() override;
+ virtual void SAL_CALL closeOutput() override;
+};
+}
+
+void SAL_CALL OslOutputStreamWrapper::writeBytes(const css::uno::Sequence<sal_Int8>& aData)
+{
+ sal_uInt64 uBytesToWrite = aData.getLength();
+ sal_uInt64 uBytesWritten = 0;
+
+ sal_Int8 const* pBuffer = aData.getConstArray();
+
+ while (uBytesToWrite)
+ {
+ osl::File::RC eRC = maFile.write(pBuffer, uBytesToWrite, uBytesWritten);
+
+ switch (eRC)
+ {
+ case osl::File::E_INVAL: // the format of the parameters was not valid
+ case osl::File::E_FBIG: // File too large
+
+ case osl::File::E_AGAIN: // Operation would block
+ case osl::File::E_BADF: // Bad file
+ case osl::File::E_FAULT: // Bad address
+ case osl::File::E_INTR: // function call was interrupted
+ case osl::File::E_IO: // I/O error
+ case osl::File::E_NOLCK: // No record locks available
+ case osl::File::E_NOLINK: // Link has been severed
+ case osl::File::E_NOSPC: // No space left on device
+ case osl::File::E_NXIO: // No such device or address
+ throw css::io::IOException(); // TODO: Better error handling
+ default:
+ break;
+ }
+
+ uBytesToWrite -= uBytesWritten;
+ pBuffer += uBytesWritten;
+ }
+}
+
+void SAL_CALL OslOutputStreamWrapper::flush() {}
+
+void SAL_CALL OslOutputStreamWrapper::closeOutput()
+{
+ osl::File::RC eRC = maFile.close();
+
+ switch (eRC)
+ {
+ case osl::File::E_INVAL: // the format of the parameters was not valid
+
+ case osl::File::E_BADF: // Bad file
+ case osl::File::E_INTR: // function call was interrupted
+ case osl::File::E_NOLINK: // Link has been severed
+ case osl::File::E_NOSPC: // No space left on device
+ case osl::File::E_IO: // I/O error
+ throw css::io::IOException(); // TODO: Better error handling
+ default:
+ break;
+ }
+}
+
+namespace
+{
+class FlashExportFilter
+ : public cppu::WeakImplHelper<css::document::XFilter, css::document::XExporter,
+ css::lang::XInitialization, css::lang::XServiceInfo>
+{
+ Reference<XComponent> mxDoc;
+ Reference<XComponentContext> mxContext;
+ Reference<XStatusIndicator> mxStatusIndicator;
+
+ // #i56084# variables for selection export
+ Reference<XShapes> mxSelectedShapes;
+ Reference<XDrawPage> mxSelectedDrawPage;
+ bool mbExportSelection;
+
+public:
+ explicit FlashExportFilter(const Reference<XComponentContext>& rxContext);
+
+ // XFilter
+ virtual sal_Bool SAL_CALL filter(const Sequence<PropertyValue>& aDescriptor) override;
+
+ void ExportAsMultipleFiles(const Sequence<PropertyValue>& aDescriptor);
+ void ExportAsSingleFile(const Sequence<PropertyValue>& aDescriptor);
+
+ virtual void SAL_CALL cancel() override;
+
+ // XExporter
+ virtual void SAL_CALL setSourceDocument(const Reference<XComponent>& xDoc) override;
+
+ // XInitialization
+ virtual void SAL_CALL initialize(const Sequence<Any>& aArguments) override;
+
+ // XServiceInfo
+ virtual OUString SAL_CALL getImplementationName() override;
+ virtual sal_Bool SAL_CALL supportsService(const OUString& ServiceName) override;
+ virtual Sequence<OUString> SAL_CALL getSupportedServiceNames() override;
+};
+}
+
+FlashExportFilter::FlashExportFilter(const Reference<XComponentContext>& rxContext)
+ : mxDoc()
+ , mxContext(rxContext)
+ , mxStatusIndicator()
+ , mxSelectedShapes()
+ , mxSelectedDrawPage()
+ , mbExportSelection(false)
+{
+}
+
+static OUString exportBackground(FlashExporter& aFlashExporter,
+ const Reference<XDrawPage>& xDrawPage,
+ const std::u16string_view& sPath, sal_uInt32 nPage,
+ const char* suffix)
+{
+ OUString filename
+ = "slide" + OUString::number(nPage + 1) + OUString::createFromAscii(suffix) + ".swf";
+ OUString fullpath = OUString::Concat(sPath) + "/" + filename;
+
+ // AS: If suffix is "o" then the last parameter is true (for exporting objects).
+ sal_uInt16 nCached
+ = aFlashExporter.exportBackgrounds(xDrawPage, new OslOutputStreamWrapper(fullpath),
+ sal::static_int_cast<sal_uInt16>(nPage), *suffix == 'o');
+ aFlashExporter.Flush();
+
+ if (nCached != nPage)
+ {
+ osl_removeFile(fullpath.pData);
+ if (0xffff == nCached)
+ return "NULL";
+ else
+ return "slide" + OUString::number(nCached + 1) + OUString::createFromAscii(suffix)
+ + ".swf";
+ }
+
+ return filename;
+}
+
+template <typename TYPE>
+static TYPE findPropertyValue(const Sequence<PropertyValue>& aPropertySequence, const char* name,
+ TYPE def)
+{
+ TYPE temp = TYPE();
+
+ sal_Int32 nLength = aPropertySequence.getLength();
+ const PropertyValue* pValue = aPropertySequence.getConstArray();
+
+ for (sal_Int32 i = 0; i < nLength; i++)
+ {
+ if (pValue[i].Name.equalsAsciiL(name, strlen(name)))
+ {
+ pValue[i].Value >>= temp;
+ return temp;
+ }
+ }
+
+ return def;
+}
+
+sal_Bool SAL_CALL
+FlashExportFilter::filter(const css::uno::Sequence<css::beans::PropertyValue>& aDescriptor)
+{
+ mxStatusIndicator = findPropertyValue<Reference<XStatusIndicator>>(
+ aDescriptor, "StatusIndicator", mxStatusIndicator);
+
+ Sequence<PropertyValue> aFilterData;
+ aFilterData
+ = findPropertyValue<Sequence<PropertyValue>>(aDescriptor, "FilterData", aFilterData);
+
+ // #i56084# check if selection shall be exported only; if yes, get the selected page and the selection itself
+ if (findPropertyValue<bool>(aDescriptor, "SelectionOnly", false))
+ {
+ Reference<XDesktop2> xDesktop(Desktop::create(mxContext));
+
+ if (xDesktop.is())
+ {
+ Reference<XFrame> xFrame(xDesktop->getCurrentFrame());
+
+ if (xFrame.is())
+ {
+ Reference<XController> xController(xFrame->getController());
+
+ if (xController.is())
+ {
+ Reference<XDrawView> xDrawView(xController, UNO_QUERY);
+
+ if (xDrawView.is())
+ {
+ mxSelectedDrawPage = xDrawView->getCurrentPage();
+ }
+
+ if (mxSelectedDrawPage.is())
+ {
+ Reference<XSelectionSupplier> xSelection(xController, UNO_QUERY);
+
+ if (xSelection.is())
+ {
+ xSelection->getSelection() >>= mxSelectedShapes;
+ }
+ }
+ }
+ }
+ }
+ }
+
+ if (mxSelectedDrawPage.is() && mxSelectedShapes.is() && mxSelectedShapes->getCount())
+ {
+ // #i56084# to export selection we need the selected page and the selected shapes.
+ // There must be shapes selected, else fallback to regular export (export all)
+ mbExportSelection = true;
+ }
+
+ // #i56084# no multiple files (suppress) when selection since selection can only export a single page
+ if (!mbExportSelection && findPropertyValue<bool>(aFilterData, "ExportMultipleFiles", false))
+ {
+ ExportAsMultipleFiles(aDescriptor);
+ }
+ else
+ {
+ ExportAsSingleFile(aDescriptor);
+ }
+
+ if (mxStatusIndicator.is())
+ mxStatusIndicator->end();
+
+ return true;
+}
+
+// AS: When exporting as multiple files, each background, object layer, and slide gets its own
+// file. Additionally, a file called BackgroundConfig.txt is generated, indicating which
+// background and objects (if any) go with each slide. The files are named slideNb.swf,
+// slideNo.swf, and slideNp.swf, where N is the slide number, and b=background, o=objects, and
+// p=slide contents. Note that under normal circumstances, there will be very few b and o files.
+
+// AS: HACK! Right now, I create a directory as a sibling to the swf file selected in the Export
+// dialog. This directory is called presentation.sxi-swf-files. The name of the swf file selected
+// in the Export dialog has no impact on this. All files created are placed in this directory.
+void FlashExportFilter::ExportAsMultipleFiles(const Sequence<PropertyValue>& aDescriptor)
+{
+ Reference<XDrawPagesSupplier> xDrawPagesSupplier(mxDoc, UNO_QUERY);
+ if (!xDrawPagesSupplier.is())
+ return;
+
+ Reference<XIndexAccess> xDrawPages = xDrawPagesSupplier->getDrawPages();
+ if (!xDrawPages.is())
+ return;
+
+ Reference<XDesktop2> rDesktop = Desktop::create(mxContext);
+
+ Reference<XStorable> xStorable(rDesktop->getCurrentComponent(), UNO_QUERY);
+ if (!xStorable.is())
+ return;
+
+ Reference<XDrawPage> xDrawPage;
+
+ Reference<XFrame> rFrame = rDesktop->getCurrentFrame();
+ Reference<XDrawView> rDrawView(rFrame->getController(), UNO_QUERY);
+
+ Reference<XDrawPage> rCurrentPage = rDrawView->getCurrentPage();
+
+ Sequence<PropertyValue> aFilterData;
+
+ aFilterData
+ = findPropertyValue<Sequence<PropertyValue>>(aDescriptor, "FilterData", aFilterData);
+
+ //AS: Do a bunch of path mangling to figure out where to put the files.
+
+ OUString sOriginalPath = findPropertyValue<OUString>(aDescriptor, "URL", OUString());
+
+ // AS: sPath is the parent directory, where everything else exists (like the sxi,
+ // the -swf-files folder, the -audio files, etc.
+ sal_Int32 lastslash = sOriginalPath.lastIndexOf('/');
+ OUString sPath(sOriginalPath.copy(0, lastslash));
+
+ OUString sPresentation(xStorable->getLocation());
+
+ lastslash = sPresentation.lastIndexOf('/') + 1;
+ sal_Int32 lastdot = sPresentation.lastIndexOf('.');
+
+ // AS: The name of the presentation, without 3 character extension.
+ OUString sPresentationName;
+ if (lastdot < 0) // fdo#71309 in case file has no name
+ sPresentationName = sPresentation.copy(lastslash);
+ else
+ sPresentationName = sPresentation.copy(lastslash, lastdot - lastslash);
+
+ OUString fullpath, swfdirpath, backgroundfilename, objectsfilename;
+
+ swfdirpath = sPath + "/" + sPresentationName + ".sxi-swf-files";
+
+ osl_createDirectory(swfdirpath.pData);
+
+ fullpath = swfdirpath + "/backgroundconfig.txt";
+
+ oslFileHandle aBackgroundConfig(nullptr);
+
+ // AS: Only export the background config if we're exporting all of the pages, otherwise we'll
+ // screw it up.
+ bool bExportAll = findPropertyValue<bool>(aFilterData, "ExportAll", true);
+ if (bExportAll)
+ {
+ osl_removeFile(fullpath.pData);
+ osl_openFile(fullpath.pData, &aBackgroundConfig,
+ osl_File_OpenFlag_Create | osl_File_OpenFlag_Write);
+
+ sal_uInt64 bytesWritten;
+ osl_writeFile(aBackgroundConfig, "slides=", strlen("slides="), &bytesWritten);
+ }
+
+ // TODO: check for errors
+
+ FlashExporter aFlashExporter(mxContext, mxSelectedShapes, mxSelectedDrawPage,
+ findPropertyValue<sal_Int32>(aFilterData, "CompressMode", 75),
+ findPropertyValue<bool>(aFilterData, "ExportOLEAsJPEG", false));
+
+ const sal_Int32 nPageCount = xDrawPages->getCount();
+ if (mxStatusIndicator.is())
+ mxStatusIndicator->start("Saving :", nPageCount);
+
+ for (sal_Int32 nPage = 0; nPage < nPageCount; nPage++)
+ {
+ if (mxStatusIndicator.is())
+ mxStatusIndicator->setValue(nPage);
+ xDrawPages->getByIndex(nPage) >>= xDrawPage;
+
+ // AS: If we're only exporting the current page, then skip the rest.
+ if (!bExportAll && xDrawPage != rCurrentPage)
+ continue;
+
+ // AS: Export the background, the background objects, and then the slide contents.
+ if (bExportAll || findPropertyValue<bool>(aFilterData, "ExportBackgrounds", true))
+ {
+ backgroundfilename
+ = exportBackground(aFlashExporter, xDrawPage, swfdirpath, nPage, "b");
+ }
+
+ if (bExportAll || findPropertyValue<bool>(aFilterData, "ExportBackgroundObjects", true))
+ {
+ objectsfilename = exportBackground(aFlashExporter, xDrawPage, swfdirpath, nPage, "o");
+ }
+
+ if (bExportAll || findPropertyValue<bool>(aFilterData, "ExportSlideContents", true))
+ {
+ fullpath = swfdirpath + "/slide" + OUString::number(nPage + 1) + "p.swf";
+
+ bool ret = aFlashExporter.exportSlides(xDrawPage, new OslOutputStreamWrapper(fullpath));
+ aFlashExporter.Flush();
+
+ if (!ret)
+ osl_removeFile(fullpath.pData);
+ }
+
+ // AS: Write out to the background config what backgrounds and objects this
+ // slide used.
+ if (bExportAll)
+ {
+ OUString temp = backgroundfilename + "|" + objectsfilename;
+ OString ASCIItemp(temp.getStr(), temp.getLength(), RTL_TEXTENCODING_ASCII_US);
+
+ sal_uInt64 bytesWritten;
+ osl_writeFile(aBackgroundConfig, ASCIItemp.getStr(), ASCIItemp.getLength(),
+ &bytesWritten);
+
+ if (nPage < nPageCount - 1)
+ osl_writeFile(aBackgroundConfig, "|", 1, &bytesWritten);
+ }
+ }
+
+ if (bExportAll)
+ osl_closeFile(aBackgroundConfig);
+}
+
+void FlashExportFilter::ExportAsSingleFile(const Sequence<PropertyValue>& aDescriptor)
+{
+ Reference<XOutputStream> xOutputStream
+ = findPropertyValue<Reference<XOutputStream>>(aDescriptor, "OutputStream", nullptr);
+ Sequence<PropertyValue> aFilterData;
+
+ if (!xOutputStream.is())
+ {
+ OSL_ASSERT(false);
+ return;
+ }
+
+ FlashExporter aFlashExporter(mxContext, mxSelectedShapes, mxSelectedDrawPage,
+ findPropertyValue<sal_Int32>(aFilterData, "CompressMode", 75),
+ findPropertyValue<bool>(aFilterData, "ExportOLEAsJPEG", false));
+
+ aFlashExporter.exportAll(mxDoc, xOutputStream, mxStatusIndicator);
+}
+
+void SAL_CALL FlashExportFilter::cancel() {}
+
+// XExporter
+void SAL_CALL
+FlashExportFilter::setSourceDocument(const css::uno::Reference<css::lang::XComponent>& xDoc)
+{
+ mxDoc = xDoc;
+}
+
+// XInitialization
+void SAL_CALL
+FlashExportFilter::initialize(const css::uno::Sequence<css::uno::Any>& /* aArguments */)
+{
+}
+
+OUString FlashExportFilter_getImplementationName()
+{
+ return "com.sun.star.comp.Impress.FlashExportFilter";
+}
+
+Sequence<OUString> FlashExportFilter_getSupportedServiceNames()
+{
+ Sequence<OUString> aRet{ "com.sun.star.document.ExportFilter" };
+ return aRet;
+}
+
+Reference<XInterface> FlashExportFilter_createInstance(const Reference<XMultiServiceFactory>& rSMgr)
+{
+ return static_cast<cppu::OWeakObject*>(
+ new FlashExportFilter(comphelper::getComponentContext(rSMgr)));
+}
+
+// XServiceInfo
+OUString SAL_CALL FlashExportFilter::getImplementationName()
+{
+ return FlashExportFilter_getImplementationName();
+}
+
+sal_Bool SAL_CALL FlashExportFilter::supportsService(const OUString& rServiceName)
+{
+ return cppu::supportsService(this, rServiceName);
+}
+
+css::uno::Sequence<OUString> SAL_CALL FlashExportFilter::getSupportedServiceNames()
+{
+ return FlashExportFilter_getSupportedServiceNames();
+}
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/filter/source/flash/swfuno.cxx b/filter/source/flash/swfuno.cxx
new file mode 100644
index 000000000000..70532b38469f
--- /dev/null
+++ b/filter/source/flash/swfuno.cxx
@@ -0,0 +1,69 @@
+/* -*- 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 <cppuhelper/factory.hxx>
+#include <com/sun/star/lang/XSingleServiceFactory.hpp>
+
+#include "swfuno.hxx"
+
+using namespace ::cppu;
+using namespace ::com::sun::star::uno;
+using namespace ::com::sun::star::lang;
+using namespace ::com::sun::star::registry;
+
+using namespace ::swf;
+
+extern "C" {
+SAL_DLLPUBLIC_EXPORT void* flash_component_getFactory(const char* pImplName, void* pServiceManager,
+ void* /* pRegistryKey */)
+{
+ void* pRet = nullptr;
+
+ if (pServiceManager)
+ {
+ Reference<XSingleServiceFactory> xFactory;
+
+ OUString implName = OUString::createFromAscii(pImplName);
+ if (implName == FlashExportFilter_getImplementationName())
+ {
+ xFactory = createSingleFactory(static_cast<XMultiServiceFactory*>(pServiceManager),
+ OUString::createFromAscii(pImplName),
+ FlashExportFilter_createInstance,
+ FlashExportFilter_getSupportedServiceNames());
+ }
+ else if (implName == SWFDialog_getImplementationName())
+ {
+ xFactory = createSingleFactory(static_cast<XMultiServiceFactory*>(pServiceManager),
+ OUString::createFromAscii(pImplName),
+ SWFDialog_createInstance,
+ SWFDialog_getSupportedServiceNames());
+ }
+
+ if (xFactory.is())
+ {
+ xFactory->acquire();
+ pRet = xFactory.get();
+ }
+ }
+
+ return pRet;
+}
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/filter/source/flash/swfuno.hxx b/filter/source/flash/swfuno.hxx
new file mode 100644
index 000000000000..0db437675445
--- /dev/null
+++ b/filter/source/flash/swfuno.hxx
@@ -0,0 +1,73 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#ifndef INCLUDED_FILTER_SOURCE_FLASH_SWFUNO_HXX
+#define INCLUDED_FILTER_SOURCE_FLASH_SWFUNO_HXX
+
+#include <sal/config.h>
+
+#include <com/sun/star/uno/Reference.hxx>
+#include <com/sun/star/uno/Sequence.hxx>
+#include <com/sun/star/lang/XMultiServiceFactory.hpp>
+#include <rtl/ustring.hxx>
+
+namespace com
+{
+namespace sun
+{
+namespace star
+{
+namespace lang
+{
+class XMultiSerivceFactory;
+}
+namespace uno
+{
+class XInterface;
+}
+}
+}
+}
+
+namespace swf
+{
+/// @throws css::uno::RuntimeException
+OUString FlashExportFilter_getImplementationName();
+
+/// @throws css::uno::RuntimeException
+css::uno::Sequence<OUString> FlashExportFilter_getSupportedServiceNames();
+
+/// @throws css::uno::Exception
+css::uno::Reference<css::uno::XInterface>
+FlashExportFilter_createInstance(css::uno::Reference<css::lang::XMultiServiceFactory> const& rSMgr);
+}
+
+/// @throws css::uno::RuntimeException
+OUString SWFDialog_getImplementationName();
+
+/// @throws css::uno::RuntimeException
+css::uno::Sequence<OUString> SWFDialog_getSupportedServiceNames();
+
+/// @throws css::uno::Exception
+css::uno::Reference<css::uno::XInterface>
+SWFDialog_createInstance(css::uno::Reference<css::lang::XMultiServiceFactory> const& rSMgr);
+
+#endif
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/filter/source/flash/swfwriter.cxx b/filter/source/flash/swfwriter.cxx
new file mode 100644
index 000000000000..81edf16a2f89
--- /dev/null
+++ b/filter/source/flash/swfwriter.cxx
@@ -0,0 +1,401 @@
+/* -*- 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 "swfwriter.hxx"
+#include <vcl/virdev.hxx>
+#include <vcl/gdimtf.hxx>
+#include <basegfx/matrix/b2dhommatrixtools.hxx>
+#include <tools/debug.hxx>
+
+using namespace ::swf;
+using namespace ::com::sun::star::uno;
+using namespace ::com::sun::star::io;
+
+static MapMode aTWIPSMode(MapUnit::MapTwip);
+static MapMode a100thmmMode(MapUnit::Map100thMM);
+
+static sal_Int32 map100thmm(sal_Int32 n100thMM)
+{
+ Point aPoint(n100thMM, n100thMM);
+ sal_Int32 nX = OutputDevice::LogicToLogic(aPoint, a100thmmMode, aTWIPSMode).X();
+ return nX;
+}
+
+Writer::Writer(sal_Int32 nTWIPWidthOutput, sal_Int32 nTWIPHeightOutput, sal_Int32 nDocWidth,
+ sal_Int32 nDocHeight, sal_Int32 nJPEGcompressMode)
+ : mnDocWidth(map100thmm(nDocWidth))
+ , mnDocHeight(map100thmm(nDocHeight))
+ , mnDocXScale(static_cast<double>(nTWIPWidthOutput) / mnDocWidth)
+ , mnDocYScale(static_cast<double>(nTWIPHeightOutput) / mnDocHeight)
+ , mpClipPolyPolygon(nullptr)
+ , mnNextId(1)
+ , mnFrames(0)
+ , mnGlobalTransparency(0)
+ , mnJPEGCompressMode(nJPEGcompressMode)
+{
+ mpVDev->EnableOutput(false);
+
+ maMovieTempFile.EnableKillingFile();
+ maFontsTempFile.EnableKillingFile();
+
+ mpMovieStream = maMovieTempFile.GetStream(StreamMode::WRITE | StreamMode::TRUNC);
+ mpFontsStream = maFontsTempFile.GetStream(StreamMode::WRITE | StreamMode::TRUNC);
+
+ // define an invisible button with the size of a page
+ tools::Rectangle aRect(0, 0, static_cast<long>(mnDocWidth * mnDocXScale),
+ static_cast<long>(mnDocHeight * mnDocYScale));
+ tools::Polygon aPoly(aRect);
+ FillStyle aFill(COL_WHITE);
+ sal_uInt16 nWhiteBackgroundShapeId = defineShape(aPoly, aFill);
+
+ ::basegfx::B2DHomMatrix m; // #i73264#
+ mnPageButtonId = createID();
+ startTag(TAG_DEFINEBUTTON);
+ mpTag->addUI16(mnPageButtonId); // character id for button
+
+ // button records
+ mpTag->addUI8(0x08); // only hit state
+ mpTag->addUI16(nWhiteBackgroundShapeId); // shape id of background rectangle
+ mpTag->addUI16(0); // depth for button DANGER!
+ mpTag->addMatrix(m); // identity matrix
+ mpTag->addUI8(0); // empty color transform
+
+ // mpTag->addUI8( 0 ); // end of button records
+
+ // action records
+ mpTag->addUI8(0x06); // ActionPlay
+ mpTag->addUI8(0); // end of action records
+
+ endTag();
+
+ // place a shape that clips shapes depth 2-3 to document boundaries
+ // placeShape( mnWhiteBackgroundShapeId, 1, 0, 0, 4 );
+}
+
+Writer::~Writer() { mpVDev.disposeAndClear(); }
+
+static void ImplCopySvStreamToXOutputStream(SvStream& rIn, Reference<XOutputStream> const& xOut)
+{
+ sal_uInt32 nBufferSize = 64 * 1024;
+
+ sal_uInt32 nSize = rIn.TellEnd();
+ rIn.Seek(STREAM_SEEK_TO_BEGIN);
+
+ Sequence<sal_Int8> aBuffer(std::min(nBufferSize, nSize));
+
+ while (nSize)
+ {
+ if (nSize < nBufferSize)
+ {
+ nBufferSize = nSize;
+ aBuffer.realloc(nSize);
+ }
+
+ sal_uInt32 nRead = rIn.ReadBytes(aBuffer.getArray(), nBufferSize);
+ DBG_ASSERT(nRead == nBufferSize, "ImplCopySvStreamToXOutputStream failed!");
+ xOut->writeBytes(aBuffer);
+
+ if (nRead == 0)
+ break;
+
+ nSize -= nRead;
+ }
+}
+
+void Writer::storeTo(Reference<XOutputStream> const& xOutStream)
+{
+ for (auto& font : maFonts)
+ {
+ font->write(*mpFontsStream);
+ font.reset();
+ }
+ maFonts.clear();
+
+ // Endtag
+ mpMovieStream->WriteUInt16(0);
+
+ Tag aHeader(0xff);
+
+ aHeader.addUI8('F');
+ aHeader.addUI8('W');
+ aHeader.addUI8('S');
+ aHeader.addUI8(5);
+
+ sal_uInt32 nSizePos = aHeader.Tell();
+
+ aHeader.WriteUInt32(0);
+
+ tools::Rectangle aDocRect(0, 0, static_cast<long>(mnDocWidth * mnDocXScale),
+ static_cast<long>(mnDocHeight * mnDocYScale));
+
+ aHeader.addRect(aDocRect);
+
+ // frame delay in 8.8 fixed number of frames per second
+ aHeader.addUI8(0);
+ aHeader.addUI8(12);
+
+ aHeader.addUI16(uInt16_(mnFrames));
+
+ const sal_uInt32 nSize = aHeader.Tell() + mpFontsStream->Tell() + mpMovieStream->Tell();
+
+ aHeader.Seek(nSizePos);
+ aHeader.WriteUInt32(nSize);
+
+ ImplCopySvStreamToXOutputStream(aHeader, xOutStream);
+ ImplCopySvStreamToXOutputStream(*mpFontsStream, xOutStream);
+ ImplCopySvStreamToXOutputStream(*mpMovieStream, xOutStream);
+}
+
+sal_uInt16 Writer::startSprite()
+{
+ sal_uInt16 nShapeId = createID();
+ mvSpriteStack.push(mpSprite.release());
+ mpSprite.reset(new Sprite(nShapeId));
+ return nShapeId;
+}
+
+void Writer::endSprite()
+{
+ if (!mpSprite)
+ return;
+
+ startTag(TAG_END);
+ endTag();
+
+ mpSprite->write(*mpMovieStream);
+ mpSprite.reset();
+
+ if (!mvSpriteStack.empty())
+ {
+ mpSprite.reset(mvSpriteStack.top());
+ mvSpriteStack.pop();
+ }
+}
+
+void Writer::placeShape(sal_uInt16 nID, sal_uInt16 nDepth, sal_Int32 x, sal_Int32 y)
+{
+ startTag(TAG_PLACEOBJECT2);
+
+ BitStream aBits;
+
+ aBits.writeUB(sal_uInt32(0), 1); // Has Clip Actions?
+ aBits.writeUB(0, 1); // reserved
+ aBits.writeUB(sal_uInt32(0), 1); // has a name
+ aBits.writeUB(0, 1); // no ratio
+ aBits.writeUB(0, 1); // no color transform
+ aBits.writeUB(1, 1); // has a matrix
+ aBits.writeUB(1, 1); // places a character
+ aBits.writeUB(0, 1); // does not define a character to be moved
+
+ mpTag->addBits(aBits);
+ mpTag->addUI16(nDepth); // depth
+ mpTag->addUI16(nID); // character Id
+
+ // #i73264#
+ const basegfx::B2DHomMatrix aMatrix(basegfx::utils::createTranslateB2DHomMatrix(
+ Int16_(static_cast<long>(map100thmm(x) * mnDocXScale)),
+ Int16_(static_cast<long>(map100thmm(y) * mnDocYScale))));
+ mpTag->addMatrix(aMatrix); // transformation matrix
+
+ endTag();
+}
+
+void Writer::removeShape(sal_uInt16 nDepth)
+{
+ startTag(TAG_REMOVEOBJECT2);
+ mpTag->addUI16(nDepth); // depth
+ endTag();
+}
+
+void Writer::startTag(sal_uInt8 nTagId)
+{
+ DBG_ASSERT(mpTag == nullptr, "Last tag was not ended");
+
+ mpTag.reset(new Tag(nTagId));
+}
+
+void Writer::endTag()
+{
+ sal_uInt8 nTag = mpTag->getTagId();
+
+ if (mpSprite
+ && ((nTag == TAG_END) || (nTag == TAG_SHOWFRAME) || (nTag == TAG_DOACTION)
+ || (nTag == TAG_STARTSOUND) || (nTag == TAG_PLACEOBJECT) || (nTag == TAG_PLACEOBJECT2)
+ || (nTag == TAG_REMOVEOBJECT2) || (nTag == TAG_FRAMELABEL)))
+ {
+ mpSprite->addTag(std::move(mpTag));
+ }
+ else
+ {
+ mpTag->write(*mpMovieStream);
+ mpTag.reset();
+ }
+}
+
+void Writer::showFrame()
+{
+ startTag(TAG_SHOWFRAME);
+ endTag();
+
+ if (nullptr == mpSprite)
+ mnFrames++;
+}
+
+sal_uInt16 Writer::defineShape(const GDIMetaFile& rMtf)
+{
+ mpVDev->SetMapMode(rMtf.GetPrefMapMode());
+ Impl_writeActions(rMtf);
+
+ sal_uInt16 nId = 0;
+ if (maShapeIds.empty())
+ return nId;
+
+ {
+ nId = startSprite();
+ sal_uInt16 iDepth = 1;
+ for (auto const& shape : maShapeIds)
+ {
+ placeShape(shape, iDepth++, 0, 0);
+ }
+ endSprite();
+ }
+
+ maShapeIds.clear();
+
+ return nId;
+}
+
+sal_uInt16 Writer::defineShape(const tools::Polygon& rPoly, const FillStyle& rFillStyle)
+{
+ const tools::PolyPolygon aPolyPoly(rPoly);
+ return defineShape(aPolyPoly, rFillStyle);
+}
+
+sal_uInt16 Writer::defineShape(const tools::PolyPolygon& rPolyPoly, const FillStyle& rFillStyle)
+{
+ sal_uInt16 nShapeId = createID();
+
+ // start a DefineShape3 tag
+ startTag(TAG_DEFINESHAPE3);
+
+ mpTag->addUI16(nShapeId);
+ mpTag->addRect(rPolyPoly.GetBoundRect());
+
+ // FILLSTYLEARRAY
+ mpTag->addUI8(1); // FillStyleCount
+
+ // FILLSTYLE
+ rFillStyle.addTo(mpTag.get());
+
+ // LINESTYLEARRAY
+ mpTag->addUI8(0); // LineStyleCount
+
+ // Number of fill and line index bits to 1
+ mpTag->addUI8(0x11);
+
+ BitStream aBits;
+
+ const sal_uInt16 nCount = rPolyPoly.Count();
+ sal_uInt16 i;
+ for (i = 0; i < nCount; i++)
+ {
+ const tools::Polygon& rPoly = rPolyPoly[i];
+ if (rPoly.GetSize())
+ Impl_addPolygon(aBits, rPoly, true);
+ }
+
+ Impl_addEndShapeRecord(aBits);
+
+ mpTag->addBits(aBits);
+ endTag();
+
+ return nShapeId;
+}
+
+sal_uInt16 Writer::defineShape(const tools::PolyPolygon& rPolyPoly, sal_uInt16 nLineWidth,
+ const Color& rLineColor)
+{
+ sal_uInt16 nShapeId = createID();
+
+ // start a DefineShape3 tag
+ startTag(TAG_DEFINESHAPE3);
+
+ mpTag->addUI16(nShapeId);
+ mpTag->addRect(rPolyPoly.GetBoundRect());
+
+ // FILLSTYLEARRAY
+ mpTag->addUI8(0); // FillStyleCount
+
+ // LINESTYLEARRAY
+ mpTag->addUI8(1); // LineStyleCount
+
+ // LINESTYLE
+ mpTag->addUI16(nLineWidth); // Width of line in twips
+ mpTag->addRGBA(rLineColor); // Color
+
+ // Number of fill and line index bits to 1
+ mpTag->addUI8(0x11);
+
+ BitStream aBits;
+
+ const sal_uInt16 nCount = rPolyPoly.Count();
+ sal_uInt16 i;
+ for (i = 0; i < nCount; i++)
+ {
+ const tools::Polygon& rPoly = rPolyPoly[i];
+ if (rPoly.GetSize())
+ Impl_addPolygon(aBits, rPoly, false);
+ }
+
+ Impl_addEndShapeRecord(aBits);
+
+ mpTag->addBits(aBits);
+ endTag();
+
+ return nShapeId;
+}
+
+void Writer::stop()
+{
+ startTag(TAG_DOACTION);
+ mpTag->addUI8(0x07);
+ mpTag->addUI8(0);
+ endTag();
+}
+
+void Writer::waitOnClick(sal_uInt16 nDepth)
+{
+ placeShape(uInt16_(mnPageButtonId), nDepth, 0, 0);
+ stop();
+ showFrame();
+ removeShape(nDepth);
+}
+
+/** inserts a doaction tag with an ActionGotoFrame */
+void Writer::gotoFrame(sal_uInt16 nFrame)
+{
+ startTag(TAG_DOACTION);
+ mpTag->addUI8(0x81);
+ mpTag->addUI16(2);
+ mpTag->addUI16(nFrame);
+ mpTag->addUI8(0);
+ endTag();
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/filter/source/flash/swfwriter.hxx b/filter/source/flash/swfwriter.hxx
new file mode 100644
index 000000000000..3afef0862bdf
--- /dev/null
+++ b/filter/source/flash/swfwriter.hxx
@@ -0,0 +1,421 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#ifndef INCLUDED_FILTER_SOURCE_FLASH_SWFWRITER_HXX
+#define INCLUDED_FILTER_SOURCE_FLASH_SWFWRITER_HXX
+
+#include <com/sun/star/io/XOutputStream.hpp>
+#include <com/sun/star/i18n/XBreakIterator.hpp>
+#include <vcl/checksum.hxx>
+#include <vcl/font.hxx>
+#include <vcl/gradient.hxx>
+#include <vcl/vclptr.hxx>
+#include <unotools/tempfile.hxx>
+#include <tools/color.hxx>
+#include <tools/gen.hxx>
+#include <tools/stream.hxx>
+#include <basegfx/matrix/b2dhommatrix.hxx>
+#include <osl/diagnose.h>
+#include <vcl/kernarray.hxx>
+
+#include <vector>
+#include <stack>
+#include <map>
+
+class GDIMetaFile;
+class BitmapEx;
+class Gradient;
+class SvtGraphicFill;
+class SvtGraphicStroke;
+class LineInfo;
+namespace basegfx
+{
+class B2DPolygon;
+}
+namespace tools
+{
+class Polygon;
+class PolyPolygon;
+}
+
+inline sal_uInt16 uInt16_(sal_Int32 nValue)
+{
+ OSL_ENSURE((nValue >= 0) && (static_cast<sal_uInt32>(nValue) <= 0xffff),
+ "overflow while converting sal_Int32 to sal_uInt16");
+ return static_cast<sal_uInt16>(nValue);
+}
+
+inline sal_Int16 Int16_(sal_Int32 nValue)
+{
+ OSL_ENSURE((nValue >= -32768) && (nValue <= 32767),
+ "overflow while converting sal_Int32 to sal_Int16");
+ return static_cast<sal_Int16>(nValue);
+}
+
+class VirtualDevice;
+
+sal_uInt16 getMaxBitsSigned(sal_Int32 nValue);
+
+namespace swf
+{
+const sal_uInt8 TAG_END = 0;
+const sal_uInt8 TAG_SHOWFRAME = 1;
+
+const sal_uInt8 TAG_DEFINEBUTTON = 7;
+
+const sal_uInt8 TAG_BACKGROUNDCOLOR = 9;
+
+const sal_uInt8 TAG_DOACTION = 12;
+const sal_uInt8 TAG_STARTSOUND = 15;
+
+const sal_uInt8 TAG_SOUNDSTREAMBLOCK = 19;
+const sal_uInt8 TAG_SOUNDSTREAMHEAD = 18;
+const sal_uInt8 TAG_SOUNDSTREAMHEAD2 = 45;
+
+const sal_uInt8 TAG_JPEGTABLES = 8;
+const sal_uInt8 TAG_DEFINEBITS = 6;
+const sal_uInt8 TAG_DEFINEBITSLOSSLESS = 20;
+const sal_uInt8 TAG_DEFINEBITSJPEG2 = 21;
+const sal_uInt8 TAG_DEFINEBITSJPEG3 = 35;
+const sal_uInt8 TAG_DEFINEBITSLOSSLESS2 = 36;
+const sal_uInt8 TAG_DEFINEEDITTEXT = 37;
+const sal_uInt8 TAG_PLACEOBJECT = 4;
+const sal_uInt8 TAG_PLACEOBJECT2 = 26;
+const sal_uInt8 TAG_REMOVEOBJECT2 = 28;
+
+const sal_uInt8 TAG_DEFINEFONT = 10;
+const sal_uInt8 TAG_DEFINETEXT = 11;
+const sal_uInt8 TAG_DEFINESHAPE3 = 32;
+const sal_uInt8 TAG_DEFINESPRITE = 39;
+
+const sal_uInt8 TAG_FRAMELABEL = 43;
+
+const sal_uInt8 TAG_HEADER = 0xff;
+
+/** converts a double to a 16.16 flash fixed value */
+sal_uInt32 getFixed(double fValue);
+
+typedef ::std::map<BitmapChecksum, sal_uInt16> ChecksumCache;
+
+/** container class to create bit structures */
+class BitStream
+{
+public:
+ BitStream();
+
+ void writeUB(sal_uInt32 nValue, sal_uInt16 nBits);
+ void writeSB(sal_Int32 nValue, sal_uInt16 nBits);
+ void writeFB(sal_uInt32 nValue, sal_uInt16 nBits);
+
+ void pad();
+ void writeTo(SvStream& out);
+
+ sal_uInt32 getOffset() const;
+
+private:
+ std::vector<sal_uInt8> maData;
+ sal_uInt8 mnBitPos;
+ sal_uInt8 mnCurrentByte;
+};
+
+/** this class collects all used glyphs for a given fonts and maps
+ characters to glyph ids.
+*/
+class FlashFont
+{
+public:
+ FlashFont(const vcl::Font& rFont, sal_uInt16 nId);
+ ~FlashFont();
+
+ sal_uInt16 getGlyph(sal_uInt16 nChar, VirtualDevice* pVDev);
+
+ void write(SvStream& out);
+
+ sal_uInt16 getID() const { return mnId; }
+ const vcl::Font& getFont() const { return maFont; }
+
+private:
+ const vcl::Font maFont;
+ std::map<sal_uInt16, sal_uInt16> maGlyphIndex;
+ sal_uInt16 mnNextIndex;
+ sal_uInt16 mnId;
+ BitStream maGlyphData;
+ std::vector<sal_uInt16> maGlyphOffsets;
+};
+
+/** this class helps creating flash tags */
+class Tag : public SvMemoryStream
+{
+public:
+ explicit Tag(sal_uInt8 nTagId);
+
+ sal_uInt8 getTagId() const { return mnTagId; }
+
+ void write(SvStream& out);
+
+ void addUI32(sal_uInt32 nValue);
+ void addUI16(sal_uInt16 nValue);
+ void addUI8(sal_uInt8 nValue);
+ void addBits(BitStream& rIn);
+
+ void addRGBA(const Color& rColor);
+ void addRGB(const Color& rColor);
+ void addRect(const tools::Rectangle& rRect);
+ void addMatrix(const ::basegfx::B2DHomMatrix& rMatrix); // #i73264#
+ void addStream(SvStream& rIn);
+
+ static void writeMatrix(SvStream& rOut, const ::basegfx::B2DHomMatrix& rMatrix); // #i73264#
+ static void writeRect(SvStream& rOut, const tools::Rectangle& rRect);
+
+private:
+ sal_uInt8 mnTagId;
+};
+
+/** this class helps to define flash sprites */
+class Sprite
+{
+public:
+ explicit Sprite(sal_uInt16 nId);
+ ~Sprite();
+
+ void write(SvStream& out);
+ void addTag(std::unique_ptr<Tag> pNewTag);
+
+private:
+ std::vector<std::unique_ptr<Tag>> maTags;
+ sal_uInt16 mnId;
+ sal_uInt32 mnFrames;
+};
+
+/** this class stores a flash fill style for shapes */
+class FillStyle
+{
+public:
+ enum FillStyleType
+ {
+ solid = 0x00,
+ linear_gradient = 0x10,
+ radial_gradient = 0x12,
+ tiled_bitmap = 0x40,
+ clipped_bitmap = 0x41
+ };
+
+ /** this c'tor creates a solid fill style */
+ explicit FillStyle(const Color& rSolidColor);
+
+ /** this c'tor creates a linear or radial gradient fill style */
+ FillStyle(const tools::Rectangle& rBoundRect, const Gradient& rGradient);
+
+ /** this c'tor creates a tiled or clipped bitmap fill style */
+ FillStyle(sal_uInt16 nBitmapId, bool bClipped,
+ const ::basegfx::B2DHomMatrix& rMatrix); // #i73264#
+
+ void addTo(Tag* pTag) const;
+
+private:
+ void Impl_addGradient(Tag* pTag) const;
+
+ FillStyleType meType;
+ ::basegfx::B2DHomMatrix maMatrix; // #i73264#
+ sal_uInt16 mnBitmapId;
+ Color maColor;
+ Gradient maGradient;
+ tools::Rectangle maBoundRect;
+};
+
+/** this class creates a flash movie from vcl geometry */
+class Writer
+{
+ friend class FlashFont;
+
+public:
+ /** creates a writer for a new flash movie.
+ nDocWidth and nDocHeight are the dimensions of the movie.
+ They must be in 100th/mm.
+
+ An invisible shape with the size of the document is placed at depth 1
+ and it clips all shapes on depth 2 and 3.
+ */
+ Writer(sal_Int32 nTWIPWidthOutput, sal_Int32 nTWIPHeightOutput, sal_Int32 nDocWidth,
+ sal_Int32 nDocHeight, sal_Int32 nJPEGcompressMode);
+ ~Writer();
+
+ void storeTo(css::uno::Reference<css::io::XOutputStream> const& xOutStream);
+
+ // geometry
+ void setClipping(const tools::PolyPolygon* pClipPolyPolygon);
+
+ /** defines a flash shape from a filled polygon.
+ The coordinates must be in twips */
+ sal_uInt16 defineShape(const tools::Polygon& rPoly, const FillStyle& rFillStyle);
+
+ /** defines a flash shape from a filled polypolygon.
+ The coordinates must be in twips */
+ sal_uInt16 defineShape(const tools::PolyPolygon& rPolyPoly, const FillStyle& rFillStyle);
+
+ /** defines a flash shape from an outlined polypolygon.
+ The coordinates must be in twips */
+ sal_uInt16 defineShape(const tools::PolyPolygon& rPolyPoly, sal_uInt16 nLineWidth,
+ const Color& rLineColor);
+
+ /** defines a flash shape from a vcl metafile.
+ The mapmode of the metafile is used to map all coordinates to twips.
+ A character id of a flash sprite is returned that contains all geometry
+ from the metafile.
+ */
+ sal_uInt16 defineShape(const GDIMetaFile& rMtf);
+
+ /** defines a bitmap and returns its flash id.
+ */
+ sal_uInt16 defineBitmap(const BitmapEx& bmpSource, sal_Int32 nJPEGQualityLevel);
+
+ // control tags
+
+ /** inserts a place shape tag into the movie stream or the current sprite */
+ void placeShape(sal_uInt16 nID, sal_uInt16 nDepth, sal_Int32 x, sal_Int32 y);
+
+ /** inserts a remove shape tag into the movie stream or the current sprite */
+ void removeShape(sal_uInt16 nDepth);
+
+ /** inserts a show frame tag into the movie stream or the current sprite */
+ void showFrame();
+
+ /** creates a new sprite and sets it as the current sprite for editing.
+ Only one sprite can be edited at one time */
+ sal_uInt16 startSprite();
+
+ /** ends editing of the current sprites and adds it to the movie stream */
+ void endSprite();
+
+ /** inserts a doaction tag with an ActionStop */
+ void stop();
+
+ /** inserts a doaction tag with an ActionStop, place a button on depth nDepth that
+ continues playback on click */
+ void waitOnClick(sal_uInt16 nDepth);
+
+ /** inserts a doaction tag with an ActionGotoFrame */
+ void gotoFrame(sal_uInt16 nFrame);
+
+private:
+ Point map(const Point& rPoint) const;
+ Size map(const Size& rSize) const;
+ void map(tools::PolyPolygon& rPolyPolygon) const;
+ sal_Int32 mapRelative(sal_Int32 n100thMM) const;
+
+ void startTag(sal_uInt8 nTagId);
+ void endTag();
+ sal_uInt16 createID() { return mnNextId++; }
+
+ void Impl_writeBmp(sal_uInt16 nBitmapId, sal_uInt32 width, sal_uInt32 height,
+ sal_uInt8 const* pCompressed, sal_uInt32 compressed_size);
+ void Impl_writeImage(const BitmapEx& rBmpEx, const Point& rPt, const Size& rSz,
+ const Point& rSrcPt, const Size& rSrcSz, const tools::Rectangle& rClipRect,
+ bool bMap);
+ void Impl_writeJPEG(sal_uInt16 nBitmapId, const sal_uInt8* pJpgData, sal_uInt32 nJpgDataLength,
+ sal_uInt8 const* pCompressed, sal_uInt32 compressed_size);
+ void Impl_handleLineInfoPolyPolygons(const LineInfo& rInfo,
+ const basegfx::B2DPolygon& rLinePolygon);
+ void Impl_writeActions(const GDIMetaFile& rMtf);
+ void Impl_writePolygon(const tools::Polygon& rPoly, bool bFilled);
+ void Impl_writePolygon(const tools::Polygon& rPoly, bool bFilled, const Color& rFillColor,
+ const Color& rLineColor);
+ void Impl_writePolyPolygon(const tools::PolyPolygon& rPolyPoly, bool bFilled,
+ sal_uInt8 nTransparence = 0);
+ void Impl_writePolyPolygon(const tools::PolyPolygon& rPolyPoly, bool bFilled,
+ const Color& rFillColor, const Color& rLineColor);
+ void Impl_writeText(const Point& rPos, const OUString& rText, const KernArray* pDXArray,
+ long nWidth);
+ void Impl_writeText(const Point& rPos, const OUString& rText, const KernArray* pDXArray,
+ long nWidth, Color aTextColor);
+ void Impl_writeGradientEx(const tools::PolyPolygon& rPolyPoly, const Gradient& rGradient);
+ void Impl_writeLine(const Point& rPt1, const Point& rPt2, const Color* pLineColor = nullptr);
+ void Impl_writeRect(const tools::Rectangle& rRect, long nRadX, long nRadY);
+ void Impl_writeEllipse(const Point& rCenter, long nRadX, long nRadY);
+ bool Impl_writeFilling(SvtGraphicFill const& rFilling);
+ bool Impl_writeStroke(SvtGraphicStroke const& rStroke);
+
+ FlashFont& Impl_getFont(const vcl::Font& rFont);
+
+ static void Impl_addPolygon(BitStream& rBits, const tools::Polygon& rPoly, bool bFilled);
+
+ static void Impl_addShapeRecordChange(BitStream& rBits, sal_Int16 dx, sal_Int16 dy,
+ bool bFilled);
+ static void Impl_addStraightEdgeRecord(BitStream& rBits, sal_Int16 dx, sal_Int16 dy);
+ static void Impl_addCurvedEdgeRecord(BitStream& rBits, sal_Int16 control_dx,
+ sal_Int16 control_dy, sal_Int16 anchor_dx,
+ sal_Int16 anchor_dy);
+ static void Impl_addEndShapeRecord(BitStream& rBits);
+
+ static void Impl_addStraightLine(BitStream& rBits, Point& rLastPoint, const double P2x,
+ const double P2y);
+ static void Impl_addQuadBezier(BitStream& rBits, Point& rLastPoint, const double P2x,
+ const double P2y, const double P3x, const double P3y);
+ static void Impl_quadBezierApprox(BitStream& rBits, Point& rLastPoint, const double d2,
+ const double P1x, const double P1y, const double P2x,
+ const double P2y, const double P3x, const double P3y,
+ const double P4x, const double P4y);
+
+ css::uno::Reference<css::i18n::XBreakIterator> const& Impl_GetBreakIterator();
+
+private:
+ css::uno::Reference<css::i18n::XBreakIterator> mxBreakIterator;
+
+ std::vector<std::unique_ptr<FlashFont>> maFonts;
+
+ sal_Int32 mnDocWidth;
+ sal_Int32 mnDocHeight;
+
+ // AS: Scaling factor for output.
+ double mnDocXScale;
+ double mnDocYScale;
+
+ sal_uInt16 mnPageButtonId;
+
+ VclPtrInstance<VirtualDevice> mpVDev;
+
+ const tools::PolyPolygon* mpClipPolyPolygon;
+
+ /** holds the information of the objects defined in the movie stream
+ while executing defineShape
+ */
+ std::vector<sal_uInt16> maShapeIds;
+
+ std::unique_ptr<Tag> mpTag;
+ std::unique_ptr<Sprite> mpSprite;
+ std::stack<Sprite*> mvSpriteStack;
+ ChecksumCache mBitmapCache;
+
+ sal_uInt16 mnNextId;
+ sal_uInt32 mnFrames;
+
+ utl::TempFileNamed maMovieTempFile;
+ utl::TempFileNamed maFontsTempFile;
+
+ SvStream* mpMovieStream;
+ SvStream* mpFontsStream;
+
+ sal_uInt8 mnGlobalTransparency;
+ sal_Int32 mnJPEGCompressMode;
+};
+}
+
+#endif
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/filter/source/flash/swfwriter1.cxx b/filter/source/flash/swfwriter1.cxx
new file mode 100644
index 000000000000..93c797370ace
--- /dev/null
+++ b/filter/source/flash/swfwriter1.cxx
@@ -0,0 +1,1960 @@
+/* -*- 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 <com/sun/star/i18n/BreakIterator.hpp>
+#include <com/sun/star/i18n/ScriptType.hpp>
+#include <comphelper/processfactory.hxx>
+#include "swfwriter.hxx"
+#include <vcl/metaact.hxx>
+#include <vcl/gdimtf.hxx>
+#include <vcl/virdev.hxx>
+#include <vcl/metric.hxx>
+#include <basegfx/matrix/b2dhommatrixtools.hxx>
+#include <vcl/graphicfilter.hxx>
+#include <vcl/graphictools.hxx>
+#include <sal/log.hxx>
+#include <tools/helpers.hxx>
+#include <tools/debug.hxx>
+
+#include <zlib.h>
+
+#include <basegfx/polygon/b2dpolygon.hxx>
+#include <basegfx/polygon/b2dpolypolygon.hxx>
+#include <memory>
+
+using namespace ::swf;
+using namespace ::com::sun::star::i18n;
+using namespace ::com::sun::star::uno;
+using namespace ::com::sun::star::lang;
+using namespace ::com::sun::star::io;
+using namespace ::com::sun::star::beans;
+
+static MapMode aTWIPSMode(MapUnit::MapTwip);
+static MapMode a100thmmMode(MapUnit::Map100thMM);
+
+Point Writer::map(const Point& rPoint) const
+{
+ const MapMode& aSourceMapMode = mpVDev->GetMapMode();
+
+ Point retPoint = mpVDev->LogicToLogic(rPoint, &aSourceMapMode, &aTWIPSMode);
+
+ // AS: Produces a 'possible loss of data' warning that we can't fix without
+ // hurting code readability.
+ retPoint.setX(static_cast<long>(retPoint.X() * mnDocXScale));
+ retPoint.setY(static_cast<long>(retPoint.Y() * mnDocYScale));
+
+ return retPoint;
+}
+
+Size Writer::map(const Size& rSize) const
+{
+ const MapMode& aSourceMapMode = mpVDev->GetMapMode();
+
+ Size retSize = mpVDev->LogicToLogic(rSize, &aSourceMapMode, &aTWIPSMode);
+
+ // AS: Produces a 'possible loss of data' warning that we can't fix without
+ // hurting code readability.
+ retSize.setWidth(static_cast<long>(retSize.Width() * mnDocXScale));
+ retSize.setHeight(static_cast<long>(retSize.Height() * mnDocYScale));
+
+ return retSize;
+}
+
+void Writer::map(tools::PolyPolygon& rPolyPolygon) const
+{
+ const sal_uInt16 nPolyCount = rPolyPolygon.Count();
+ if (!nPolyCount)
+ return;
+
+ sal_uInt16 nPoly, nPoint, nPointCount;
+ for (nPoly = 0; nPoly < nPolyCount; nPoly++)
+ {
+ tools::Polygon& rPoly = rPolyPolygon[nPoly];
+ nPointCount = rPoly.GetSize();
+
+ for (nPoint = 0; nPoint < nPointCount; nPoint++)
+ {
+ rPoly[nPoint] = map(rPoly[nPoint]);
+ }
+ }
+}
+
+sal_Int32 Writer::mapRelative(sal_Int32 n100thMM) const
+{
+ MapMode aSourceMapMode(mpVDev->GetMapMode());
+ aSourceMapMode.SetOrigin(Point());
+
+ sal_Int32 nTwips
+ = mpVDev->LogicToLogic(Point(n100thMM, n100thMM), &aSourceMapMode, &aTWIPSMode).X();
+ return nTwips;
+}
+
+void Writer::Impl_addPolygon(BitStream& rBits, const tools::Polygon& rPoly, bool bFilled)
+{
+ Point aLastPoint(rPoly[0]);
+
+ Impl_addShapeRecordChange(rBits, Int16_(aLastPoint.X()), Int16_(aLastPoint.Y()), bFilled);
+
+ sal_uInt16 i = 0, nSize = rPoly.GetSize();
+
+ double d = 16.0f;
+
+ // points
+ while ((i + 1) < nSize)
+ {
+ if ((i + 3) < nSize)
+ {
+ PolyFlags P1(rPoly.GetFlags(i));
+ PolyFlags P4(rPoly.GetFlags(i + 3));
+
+ if ((PolyFlags::Normal == P1 || PolyFlags::Smooth == P1 || PolyFlags::Symmetric == P1)
+ && (PolyFlags::Control == rPoly.GetFlags(i + 1))
+ && (PolyFlags::Control == rPoly.GetFlags(i + 2))
+ && (PolyFlags::Normal == P4 || PolyFlags::Smooth == P4
+ || PolyFlags::Symmetric == P4))
+ {
+ Impl_quadBezierApprox(rBits, aLastPoint, d * d, rPoly.GetPoint(i).X(),
+ rPoly.GetPoint(i).Y(), rPoly.GetPoint(i + 1).X(),
+ rPoly.GetPoint(i + 1).Y(), rPoly.GetPoint(i + 2).X(),
+ rPoly.GetPoint(i + 2).Y(), rPoly.GetPoint(i + 3).X(),
+ rPoly.GetPoint(i + 3).Y());
+ i += 3;
+ continue;
+ }
+ }
+
+ ++i;
+
+ const Point aPolyPoint(rPoly[i]);
+ if (aPolyPoint != aLastPoint)
+ {
+ Impl_addStraightEdgeRecord(rBits, Int16_(aPolyPoint.X() - aLastPoint.X()),
+ Int16_(aPolyPoint.Y() - aLastPoint.Y()));
+ aLastPoint = aPolyPoint;
+ }
+ }
+
+ if (bFilled && (rPoly[0] != rPoly[nSize - 1]))
+ {
+ const Point aPolyPoint(rPoly[0]);
+ if (aPolyPoint != aLastPoint)
+ {
+ Impl_addStraightEdgeRecord(rBits, Int16_(aPolyPoint.X() - aLastPoint.X()),
+ Int16_(aPolyPoint.Y() - aLastPoint.Y()));
+ }
+ }
+}
+
+/** Exports a style change record with a move to (x,y) and depending on bFilled a line style 1 or fill style 1
+*/
+void Writer::Impl_addShapeRecordChange(BitStream& rBits, sal_Int16 dx, sal_Int16 dy, bool bFilled)
+{
+ rBits.writeUB(0, 1); // TypeFlag
+ rBits.writeUB(0, 1); // StateNewStyles
+ rBits.writeUB(sal_uInt32(!bFilled), 1); // StateLineStyle
+ rBits.writeUB(0, 1); // StateFillStyle0
+ rBits.writeUB(bFilled ? 1 : 0, 1); // StateFillStyle1
+ rBits.writeUB(1, 1); // StateMoveTo
+
+ sal_uInt16 nMoveBits = std::max(getMaxBitsSigned(dx), getMaxBitsSigned(dy));
+
+ rBits.writeUB(nMoveBits, 5); // Number of bits per value
+ // TODO: Optimize horizontal and vertical lines
+ rBits.writeSB(dx, nMoveBits); // DeltaX
+ rBits.writeSB(dy, nMoveBits); // DeltaY
+
+ rBits.writeUB(1, 1); // set FillStyle1 or LineStyle to 1
+}
+
+/** Exports a straight edge record
+*/
+void Writer::Impl_addStraightEdgeRecord(BitStream& rBits, sal_Int16 dx, sal_Int16 dy)
+{
+ rBits.writeUB(1, 1); // TypeFlag
+ rBits.writeUB(1, 1); // StraightFlag
+
+ sal_uInt16 nBits = std::max(getMaxBitsSigned(dx), getMaxBitsSigned(dy));
+
+ rBits.writeUB(nBits - 2, 4); // Number of bits per value
+
+ if ((dx != 0) && (dy != 0))
+ {
+ rBits.writeUB(1, 1); // GeneralLineFlag
+ rBits.writeSB(dx, nBits); // DeltaX
+ rBits.writeSB(dy, nBits); // DeltaY
+ }
+ else
+ {
+ rBits.writeUB(0, 1);
+ rBits.writeUB(sal_uInt32(dx == 0), 1);
+ if (dx == 0)
+ {
+ rBits.writeSB(dy, nBits); // DeltaY
+ }
+ else
+ {
+ rBits.writeSB(dx, nBits); // DeltaX
+ }
+ }
+}
+
+/** Exports a curved edge record
+*/
+void Writer::Impl_addCurvedEdgeRecord(BitStream& rBits, sal_Int16 control_dx, sal_Int16 control_dy,
+ sal_Int16 anchor_dx, sal_Int16 anchor_dy)
+{
+ rBits.writeUB(1, 1); // TypeFlag
+ rBits.writeUB(0, 1); // CurvedFlag
+
+ sal_uInt8 nBits = static_cast<sal_uInt8>(
+ std::max(getMaxBitsSigned(control_dx),
+ std::max(getMaxBitsSigned(control_dy),
+ std::max(getMaxBitsSigned(anchor_dx),
+ std::max(getMaxBitsSigned(anchor_dy), sal_uInt16(3))))));
+
+ rBits.writeUB(nBits - 2, 4); // Number of bits per value
+
+ rBits.writeSB(control_dx, nBits); // DeltaX
+ rBits.writeSB(control_dy, nBits); // DeltaY
+ rBits.writeSB(anchor_dx, nBits); // DeltaX
+ rBits.writeSB(anchor_dy, nBits); // DeltaY
+}
+
+/** Exports an end shape record
+*/
+void Writer::Impl_addEndShapeRecord(BitStream& rBits) { rBits.writeUB(0, 6); }
+
+void Writer::Impl_writePolygon(const tools::Polygon& rPoly, bool bFilled)
+{
+ tools::PolyPolygon aPolyPoly(rPoly);
+ Impl_writePolyPolygon(aPolyPoly, bFilled);
+}
+
+void Writer::Impl_writePolygon(const tools::Polygon& rPoly, bool bFilled, const Color& rFillColor,
+ const Color& rLineColor)
+{
+ tools::PolyPolygon aPolyPoly(rPoly);
+ Impl_writePolyPolygon(aPolyPoly, bFilled, rFillColor, rLineColor);
+}
+
+void Writer::Impl_writePolyPolygon(const tools::PolyPolygon& rPolyPoly, bool bFilled,
+ sal_uInt8 nTransparence /* = 0 */)
+{
+ Color aLineColor(mpVDev->GetLineColor());
+ if (!aLineColor.IsTransparent())
+ aLineColor.SetAlpha(255 - nTransparence);
+ Color aFillColor(mpVDev->GetFillColor());
+ if (!aLineColor.IsTransparent())
+ aFillColor.SetAlpha(255 - nTransparence);
+ Impl_writePolyPolygon(rPolyPoly, bFilled, aFillColor, aLineColor);
+}
+
+void Writer::Impl_writePolyPolygon(const tools::PolyPolygon& rPolyPoly, bool bFilled,
+ const Color& rFillColor, const Color& rLineColor)
+{
+ tools::PolyPolygon aPolyPoly(rPolyPoly);
+
+ if (!aPolyPoly.Count())
+ return;
+
+ map(aPolyPoly);
+
+ if (mpClipPolyPolygon)
+ rPolyPoly.GetIntersection(*mpClipPolyPolygon, aPolyPoly);
+
+ sal_uInt16 nID;
+ if (bFilled)
+ {
+ Color aFillColor(rFillColor);
+ if (0 != mnGlobalTransparency)
+ aFillColor.SetAlpha(255 - mnGlobalTransparency);
+
+ FillStyle aStyle(aFillColor);
+ nID = defineShape(aPolyPoly, aStyle);
+ }
+ else
+ {
+ Color aLineColor(rLineColor);
+ if (0 != mnGlobalTransparency)
+ aLineColor.SetAlpha(255 - mnGlobalTransparency);
+
+ nID = defineShape(aPolyPoly, 1, aLineColor);
+ }
+ maShapeIds.push_back(nID);
+}
+
+/** A gradient is a transition from one color to another, rendered inside a given polypolygon */
+void Writer::Impl_writeGradientEx(const tools::PolyPolygon& rPolyPoly, const Gradient& rGradient)
+{
+ if (!rPolyPoly.Count())
+ return;
+
+ tools::PolyPolygon aPolyPolygon(rPolyPoly);
+ map(aPolyPolygon);
+
+ if ((rGradient.GetStyle() == css::awt::GradientStyle::GradientStyle_LINEAR
+ && rGradient.GetAngle() == 900_deg10)
+ || (rGradient.GetStyle() == css::awt::GradientStyle::GradientStyle_RADIAL))
+ {
+ const tools::Rectangle aBoundRect(aPolyPolygon.GetBoundRect());
+
+ FillStyle aFillStyle(aBoundRect, rGradient);
+
+ sal_uInt16 nShapeId = defineShape(aPolyPolygon, aFillStyle);
+ maShapeIds.push_back(nShapeId);
+ }
+ else
+ {
+ setClipping(&aPolyPolygon);
+
+ // render the gradient filling to simple polygons
+ {
+ GDIMetaFile aTmpMtf;
+ rGradient.AddGradientActionsConst(aPolyPolygon.GetBoundRect(), aTmpMtf);
+ Impl_writeActions(aTmpMtf);
+ }
+
+ setClipping(nullptr);
+ }
+}
+
+void Writer::setClipping(const tools::PolyPolygon* pClipPolyPolygon)
+{
+ mpClipPolyPolygon = pClipPolyPolygon;
+}
+
+// AS: Just comparing fonts straight up is too literal. There are some
+// differences in font that actually require different glyphs to be defined,
+// and some that don't. This function is meant to capture all the differences
+// that we care about.
+static bool compare_fonts_for_me(const vcl::Font& rFont1, const vcl::Font& rFont2)
+{
+ return rFont1.GetFamilyName() == rFont2.GetFamilyName()
+ && rFont1.GetWeight() == rFont2.GetWeight() && rFont1.GetItalic() == rFont2.GetItalic()
+ && rFont1.IsOutline() == rFont2.IsOutline() && rFont1.IsShadow() == rFont2.IsShadow()
+ && rFont1.GetRelief() == rFont2.GetRelief();
+}
+
+FlashFont& Writer::Impl_getFont(const vcl::Font& rFont)
+{
+ for (auto const& font : maFonts)
+ {
+ const vcl::Font tempFont = font->getFont();
+ if (compare_fonts_for_me(tempFont, rFont))
+ {
+ return *font;
+ }
+ }
+
+ FlashFont* pFont = new FlashFont(rFont, createID());
+ maFonts.emplace_back(pFont);
+ return *pFont;
+}
+
+void Writer::Impl_writeText(const Point& rPos, const OUString& rText, const KernArray* pDXArray,
+ long nWidth)
+{
+ const FontMetric aMetric(mpVDev->GetFontMetric());
+
+ bool bTextSpecial
+ = aMetric.IsShadow() || aMetric.IsOutline() || (aMetric.GetRelief() != FontRelief::NONE);
+
+ if (!bTextSpecial)
+ {
+ Impl_writeText(rPos, rText, pDXArray, nWidth, mpVDev->GetTextColor());
+ }
+ else
+ {
+ if (aMetric.GetRelief() != FontRelief::NONE)
+ {
+ Color aReliefColor(COL_LIGHTGRAY);
+ Color aTextColor(mpVDev->GetTextColor());
+
+ if (aTextColor == COL_BLACK)
+ aTextColor = COL_WHITE;
+
+ // coverity[copy_paste_error : FALSE] - aReliefColor depending on aTextColor is correct
+ if (aTextColor == COL_WHITE)
+ aReliefColor = COL_BLACK;
+
+ Point aPos(rPos);
+ Point aOffset(6, 6);
+
+ if (aMetric.GetRelief() == FontRelief::Engraved)
+ {
+ aPos -= aOffset;
+ }
+ else
+ {
+ aPos += aOffset;
+ }
+
+ Impl_writeText(aPos, rText, pDXArray, nWidth, aReliefColor);
+ Impl_writeText(rPos, rText, pDXArray, nWidth, aTextColor);
+ }
+ else
+ {
+ if (aMetric.IsShadow())
+ {
+ long nOff = 1 + ((aMetric.GetLineHeight() - 24) / 24);
+ if (aMetric.IsOutline())
+ nOff += 6;
+
+ Color aTextColor(mpVDev->GetTextColor());
+ Color aShadowColor(COL_BLACK);
+
+ if ((aTextColor == COL_BLACK) || (aTextColor.GetLuminance() < 8))
+ aShadowColor = COL_LIGHTGRAY;
+
+ Point aPos(rPos);
+ aPos += Point(nOff, nOff);
+ Impl_writeText(aPos, rText, pDXArray, nWidth, aShadowColor);
+
+ if (!aMetric.IsOutline())
+ {
+ Impl_writeText(rPos, rText, pDXArray, nWidth, aTextColor);
+ }
+ }
+
+ if (aMetric.IsOutline())
+ {
+ Point aPos = rPos + Point(-6, -6);
+ Impl_writeText(aPos, rText, pDXArray, nWidth, mpVDev->GetTextColor());
+ aPos = rPos + Point(+6, +6);
+ Impl_writeText(aPos, rText, pDXArray, nWidth, mpVDev->GetTextColor());
+ aPos = rPos + Point(-6, +0);
+ Impl_writeText(aPos, rText, pDXArray, nWidth, mpVDev->GetTextColor());
+ aPos = rPos + Point(-6, +6);
+ Impl_writeText(aPos, rText, pDXArray, nWidth, mpVDev->GetTextColor());
+ aPos = rPos + Point(+0, +6);
+ Impl_writeText(aPos, rText, pDXArray, nWidth, mpVDev->GetTextColor());
+ aPos = rPos + Point(+0, -6);
+ Impl_writeText(aPos, rText, pDXArray, nWidth, mpVDev->GetTextColor());
+ aPos = rPos + Point(+6, -1);
+ Impl_writeText(aPos, rText, pDXArray, nWidth, mpVDev->GetTextColor());
+ aPos = rPos + Point(+6, +0);
+ Impl_writeText(aPos, rText, pDXArray, nWidth, mpVDev->GetTextColor());
+
+ Impl_writeText(rPos, rText, pDXArray, nWidth, COL_WHITE);
+ }
+ }
+ }
+}
+
+void Writer::Impl_writeText(const Point& rPos, const OUString& rText, const KernArray* pDXArray,
+ long nWidth, Color aTextColor)
+{
+ sal_Int32 nLen = rText.getLength();
+
+ if (!nLen)
+ return;
+
+ const bool bRTL = bool(mpVDev->GetLayoutMode() & vcl::text::ComplexTextLayoutFlags::BiDiRtl);
+
+ sal_Int16 nScriptType = ScriptType::LATIN;
+ Reference<XBreakIterator> xBI(Impl_GetBreakIterator());
+ if (xBI.is())
+ {
+ nScriptType = xBI->getScriptType(rText, 0);
+ }
+
+ // if the text is either right to left or complex or asian, we
+ // ask the output device for a polygon representation.
+ // On complex and asian text, each unicode character can have
+ // different glyph representation, based on context. Also positioning
+ // is not trivial so we let the output device do it for us.
+ if (bRTL || (nScriptType != ScriptType::LATIN))
+ {
+ // todo: optimize me as this will generate a huge amount of duplicate polygons
+ tools::PolyPolygon aPolyPoygon;
+ mpVDev->GetTextOutline(aPolyPoygon, rText, static_cast<sal_uInt16>(nLen), nWidth,
+ *pDXArray);
+ aPolyPoygon.Translate(rPos);
+ Impl_writePolyPolygon(aPolyPoygon, true, aTextColor, aTextColor);
+ }
+ else
+ {
+ Size aNormSize;
+ std::unique_ptr<KernArray> pOwnArray;
+ KernArray aDX;
+
+ // get text sizes
+ if (pDXArray)
+ {
+ aNormSize = Size(mpVDev->GetTextWidth(rText), 0);
+ aDX = *pDXArray;
+ }
+ else
+ {
+ pOwnArray.reset(new KernArray());
+ aNormSize = Size(mpVDev->GetTextArray(rText, pOwnArray.get()), 0);
+ aDX = *pOwnArray;
+ }
+
+ if (nLen > 1)
+ {
+ aNormSize.setWidth(aDX[nLen - 2] + mpVDev->GetTextWidth(OUString(rText[nLen - 1])));
+
+ if (nWidth && aNormSize.Width() && (nWidth != aNormSize.Width()))
+ {
+ const double fFactor = static_cast<double>(nWidth) / aNormSize.Width();
+
+ for (sal_Int32 i = 0; i < (nLen - 1); i++)
+ aDX.set(i, FRound(aDX[i] * fFactor));
+ }
+ }
+
+ vcl::Font aOldFont(mpVDev->GetFont());
+ Point aBaseLinePos(rPos);
+
+ vcl::Font aFont(aOldFont);
+ Degree10 nOrientation = aFont.GetOrientation();
+ aFont.SetOrientation(0_deg10);
+ aFont.SetUnderline(LINESTYLE_NONE);
+ aFont.SetStrikeout(STRIKEOUT_NONE);
+ mpVDev->SetFont(aFont);
+
+ const FontMetric aMetric(mpVDev->GetFontMetric());
+
+ FlashFont& rFlashFont = Impl_getFont(aFont);
+
+ // always adjust text position to match baseline alignment
+ switch (aOldFont.GetAlignment())
+ {
+ case ALIGN_TOP:
+ aBaseLinePos.AdjustY(aMetric.GetAscent());
+ break;
+
+ case ALIGN_BOTTOM:
+ aBaseLinePos.AdjustY(-(aMetric.GetDescent()));
+ break;
+
+ default:
+ break;
+ }
+
+ // get mapped text position
+ const Point aPt(map(aBaseLinePos));
+
+ // write text element
+
+#if 0 // makes the calculated bound rect visible for debugging
+{
+ tools::Polygon aTmpPoly( aPoly );
+ sal_uInt16 nID = FlashGeometryExporter::writePolygonShape( aMovieStream, aTmpPoly, false, COL_MAGENTA, COL_MAGENTA, mpClipPolyPolygon );
+ ImplPlaceObject( nID );
+}
+#endif
+
+ // CL: This is still a hack until we figure out how to calculate a correct bound rect
+ // for rotated text
+ tools::Rectangle textBounds(0, 0, static_cast<long>(mnDocWidth * mnDocXScale),
+ static_cast<long>(mnDocHeight * mnDocYScale));
+ double scale = 1.0;
+
+ // scale width if we have a stretched text
+ if (0 != aFont.GetFontSize().Width())
+ {
+ vcl::Font aTmpFont(aFont);
+ aTmpFont.SetAverageFontWidth(0);
+ mpVDev->SetFont(aTmpFont);
+
+ const FontMetric aMetric2(mpVDev->GetFontMetric());
+ mpVDev->SetFont(aFont);
+
+ const long n1 = aFont.GetFontSize().Width();
+ const long n2 = aMetric2.GetFontSize().Width();
+ scale = static_cast<double>(n1) / static_cast<double>(n2);
+ }
+
+ basegfx::B2DHomMatrix m(basegfx::utils::createRotateB2DHomMatrix(toRadians(nOrientation)));
+ m.translate(aPt.X() / scale, double(aPt.Y()));
+ m.scale(scale, scale);
+
+ sal_Int16 nHeight = Int16_(map(Size(0, aFont.GetFontHeight())).Height());
+
+ startTag(TAG_DEFINETEXT);
+
+ sal_uInt16 nTextId = createID();
+
+ mpTag->addUI16(nTextId);
+ mpTag->addRect(textBounds);
+ mpTag->addMatrix(m);
+
+ sal_uInt8 nGlyphBits = 16;
+ sal_uInt8 nAdvanceBits = 16;
+
+ mpTag->addUI8(nGlyphBits);
+ mpTag->addUI8(nAdvanceBits);
+
+ // text style change record
+ mpTag->addUI8(0x8c);
+ mpTag->addUI16(rFlashFont.getID());
+ mpTag->addRGB(aTextColor);
+ mpTag->addUI16(uInt16_(nHeight));
+
+ DBG_ASSERT(nLen <= 127, "TODO: handle text with more than 127 characters");
+
+ // Glyph record
+ mpTag->addUI8(static_cast<sal_uInt8>(nLen));
+
+ BitStream aBits;
+
+ sal_Int32 nLastDX = 0;
+ sal_Int32 nAdvance;
+ for (sal_Int32 i = 0; i < nLen; i++)
+ {
+ if (i < (nLen - 1))
+ {
+ nAdvance = aDX[i] - nLastDX;
+ nLastDX = aDX[i];
+ }
+ else
+ {
+ nAdvance = 0;
+ }
+
+ aBits.writeUB(rFlashFont.getGlyph(rText[i], mpVDev), nGlyphBits);
+ aBits.writeSB(Int16_(map(Size(static_cast<long>(nAdvance / scale), 0)).Width()),
+ nAdvanceBits);
+ }
+
+ mpTag->addBits(aBits);
+ mpTag->addUI8(0);
+
+ endTag();
+
+ maShapeIds.push_back(nTextId);
+
+ // AS: Write strikeout and underline, if necessary. This code was originally taken from the SVG
+ // export facility, although the positioning had to be tweaked a little. I can't explain the
+ // numbers, but the flash lines up very well with the original OOo document. All of this should
+ // probably be converted to polygons as part of the meta file, though, as we don't handle any
+ // fancy lines (like dashes).
+ if ((aOldFont.GetStrikeout() != STRIKEOUT_NONE)
+ || (aOldFont.GetUnderline() != LINESTYLE_NONE))
+ {
+ tools::Polygon aPoly(4);
+ const long nLineHeight = std::max<long>(FRound(aMetric.GetLineHeight() * 0.05), 1);
+
+ if (aOldFont.GetStrikeout() != STRIKEOUT_NONE)
+ {
+ aPoly[0].setX(aBaseLinePos.X());
+ aPoly[0].setY(aBaseLinePos.Y() - FRound(aMetric.GetAscent() * 0.26) - nLineHeight);
+ aPoly[1].setX(aPoly[0].X() + aNormSize.Width() - 1);
+ aPoly[1].setY(aPoly[0].Y());
+ aPoly[2].setX(aPoly[1].X());
+ aPoly[2].setY(aPoly[1].Y() + nLineHeight - 1);
+ aPoly[3].setX(aPoly[0].X());
+ aPoly[3].setY(aPoly[2].Y());
+
+ Impl_writePolygon(aPoly, true, aTextColor, aTextColor);
+ }
+
+ // AS: The factor of 1.5 on the nLineHeight is a magic number. I'm not sure why it works,
+ // but it looks good to me.
+ if (aOldFont.GetUnderline() != LINESTYLE_NONE)
+ {
+ aPoly[0].setX(aBaseLinePos.X());
+ aPoly[0].setY(static_cast<long>(aBaseLinePos.Y() + 1.5 * nLineHeight));
+ aPoly[1].setX(aPoly[0].X() + aNormSize.Width() - 1);
+ aPoly[1].setY(aPoly[0].Y());
+ aPoly[2].setX(aPoly[1].X());
+ aPoly[2].setY(aPoly[1].Y() + nLineHeight - 1);
+ aPoly[3].setX(aPoly[0].X());
+ aPoly[3].setY(aPoly[2].Y());
+
+ Impl_writePolygon(aPoly, true, aTextColor, aTextColor);
+ }
+ }
+
+ mpVDev->SetFont(aOldFont);
+ }
+}
+
+sal_uInt16 Writer::defineBitmap(const BitmapEx& bmpSource, sal_Int32 nJPEGQualityLevel)
+{
+ BitmapChecksum bmpChecksum = bmpSource.GetChecksum();
+
+ ChecksumCache::iterator it = mBitmapCache.find(bmpChecksum);
+
+ // AS: We already exported this bitmap, so just return its ID.
+ if (mBitmapCache.end() != it)
+ return it->second;
+
+ sal_uInt16 nBitmapId = createID();
+ mBitmapCache[bmpChecksum] = nBitmapId;
+
+ // AS: OK, we have a good image, so now we decide whether or not to JPEG it or
+ // or Lossless compress it.
+
+ // Figure out lossless size
+ std::vector<sal_uInt8> aImageData, aAlphaData;
+
+ sal_uInt32 width = bmpSource.GetPrefSize().Width();
+ sal_uInt32 height = bmpSource.GetPrefSize().Height();
+ bmpSource.GetSplitData(aImageData, aAlphaData);
+ sal_uInt32 raw_size = width * height * 4;
+ uLongf compressed_size = raw_size + static_cast<sal_uInt32>(raw_size / 100) + 12;
+ std::unique_ptr<sal_uInt8[]> pCompressed(new sal_uInt8[compressed_size]);
+
+ if (compress2(pCompressed.get(), &compressed_size, aImageData.data(), raw_size,
+ Z_BEST_COMPRESSION)
+ != Z_OK)
+ {
+ SAL_WARN("filter.flash", "compress2 failed!");
+ ((void)0);
+ }
+
+ // AS: SWF files let you provide an Alpha mask for JPEG images, but we have
+ // to ZLIB compress the alpha channel separately.
+ uLong alpha_compressed_size = 0;
+ std::unique_ptr<sal_uInt8[]> pAlphaCompressed;
+ if (bmpSource.IsAlpha())
+ {
+ alpha_compressed_size
+ = uLongf(width * height + static_cast<sal_uInt32>(raw_size / 100) + 12);
+ pAlphaCompressed.reset(new sal_uInt8[compressed_size]);
+
+ if (compress2(pAlphaCompressed.get(), &alpha_compressed_size, aAlphaData.data(),
+ width * height, Z_BEST_COMPRESSION)
+ != Z_OK)
+ {
+ SAL_WARN("filter.flash", "compress2 failed!");
+ ((void)0);
+ }
+ }
+
+ // clear these early for less peak memory usage
+ aImageData.resize(0);
+ aAlphaData.resize(0);
+
+ // Figure out JPEG size
+ const sal_uInt8* pJpgData = nullptr;
+ sal_uInt32 nJpgDataLength = 0xffffffff;
+
+ Graphic aGraphic(bmpSource);
+ SvMemoryStream aDstStm(65535, 65535);
+
+ GraphicFilter aFilter;
+
+ Sequence<PropertyValue> aFilterData(sal_Int32(nJPEGQualityLevel != -1));
+ auto pFilterData = aFilterData.getArray();
+
+ if (nJPEGQualityLevel != -1)
+ {
+ pFilterData[0].Name = "Quality";
+ pFilterData[0].Value <<= nJPEGQualityLevel;
+ }
+
+ if (aFilter.ExportGraphic(aGraphic, std::u16string_view(), aDstStm,
+ aFilter.GetExportFormatNumberForShortName(JPG_SHORTNAME),
+ &aFilterData)
+ == ERRCODE_NONE)
+ {
+ pJpgData = static_cast<const sal_uInt8*>(aDstStm.GetData());
+ nJpgDataLength = aDstStm.TellEnd();
+ }
+
+ // AS: Ok, now go ahead and use whichever is smaller. If JPEG is smaller, then
+ // we have to export as TAG_DEFINEBITSJPEG3 in the case that there is alpha
+ // channel data.
+ if (pJpgData && (nJpgDataLength + alpha_compressed_size < compressed_size))
+ Impl_writeJPEG(nBitmapId, pJpgData, nJpgDataLength, pAlphaCompressed.get(),
+ alpha_compressed_size);
+ else
+ Impl_writeBmp(nBitmapId, width, height, pCompressed.get(), compressed_size);
+
+ return nBitmapId;
+}
+
+void Writer::Impl_writeImage(const BitmapEx& rBmpEx, const Point& rPt, const Size& rSz,
+ const Point& /* rSrcPt */, const Size& /* rSrcSz */,
+ const tools::Rectangle& rClipRect, bool bNeedToMapClipRect)
+{
+ if (rBmpEx.IsEmpty())
+ return;
+
+ BitmapEx bmpSource(rBmpEx);
+
+ tools::Rectangle originalPixelRect(Point(), bmpSource.GetSizePixel());
+
+ Point srcPt(map(rPt));
+ Size srcSize(map(rSz));
+ tools::Rectangle destRect(srcPt, srcSize);
+
+ // AS: Christian, my scaling factors are different than yours, and work better for me.
+ // However, I can't explain why exactly. I got some of this by trial and error.
+ double XScale = destRect.GetWidth()
+ ? static_cast<double>(originalPixelRect.GetWidth()) / destRect.GetWidth()
+ : 1.0;
+ double YScale = destRect.GetHeight()
+ ? static_cast<double>(originalPixelRect.GetHeight()) / destRect.GetHeight()
+ : 1.0;
+
+ // AS: If rClipRect has a value set, then we need to crop the bmp appropriately.
+ // If a map event already occurred in the metafile, then we do not need to map
+ // the clip rect as it's already been done.
+ if (!rClipRect.IsEmpty())
+ {
+ // AS: Christian, I also don't understand why bNeedToMapClipRect is necessary, but it
+ // works like a charm. Usually, the map event in the meta file does not cause the
+ // clipping rectangle to get mapped. However, sometimes there are multiple layers
+ // of mapping which eventually do cause the clipping rect to be mapped.
+ Size clipSize(bNeedToMapClipRect ? map(rClipRect.GetSize()) : rClipRect.GetSize());
+ tools::Rectangle clipRect(Point(), clipSize);
+ destRect.Intersection(clipRect);
+
+ tools::Rectangle cropRect(destRect);
+
+ // AS: The bmp origin is always 0,0 so we have to adjust before we crop.
+ cropRect.Move(-srcPt.X(), -srcPt.Y());
+ // AS: Rectangle has no scale function (?!) so I do it manually...
+ tools::Rectangle cropPixelRect(static_cast<long>(cropRect.Left() * XScale),
+ static_cast<long>(cropRect.Top() * YScale),
+ static_cast<long>(cropRect.Right() * XScale),
+ static_cast<long>(cropRect.Bottom() * YScale));
+
+ bmpSource.Crop(cropPixelRect);
+ }
+
+ if (bmpSource.IsEmpty())
+ return;
+
+ // #105949# fix images that are under 16 pixels width or height by
+ // expanding them. Some swf players can't display such small
+ // bitmaps
+ const Size& rSizePixel = bmpSource.GetSizePixel();
+ if ((rSizePixel.Width() < 16) || (rSizePixel.Height() < 16))
+ {
+ const sal_uInt32 nDX = rSizePixel.Width() < 16 ? 16 - rSizePixel.Width() : 0;
+ const sal_uInt32 nDY = rSizePixel.Height() < 16 ? 16 - rSizePixel.Height() : 0;
+ bmpSource.Expand(nDX, nDY);
+ }
+
+ sal_Int32 nJPEGQuality = mnJPEGCompressMode;
+
+ Size szDestPixel = mpVDev->LogicToPixel(srcSize, aTWIPSMode);
+
+ double pixXScale = originalPixelRect.GetWidth()
+ ? static_cast<double>(szDestPixel.Width()) / originalPixelRect.GetWidth()
+ : 1.0;
+ double pixYScale = originalPixelRect.GetHeight() ? static_cast<double>(szDestPixel.Height())
+ / originalPixelRect.GetHeight()
+ : 1.0;
+
+ // AS: If the image has been scaled down, then scale down the quality
+ // that we use for JPEG compression.
+ if (pixXScale < 1.0 && pixYScale < 1.0)
+ {
+ double qualityScale = (pixXScale + pixYScale) / 2;
+
+ nJPEGQuality = static_cast<sal_Int32>(nJPEGQuality * qualityScale);
+
+ if (nJPEGQuality < 10)
+ nJPEGQuality += 3;
+ }
+
+ sal_uInt16 nBitmapId = defineBitmap(bmpSource, nJPEGQuality);
+
+ tools::Polygon aPoly(destRect);
+
+ // AS: Since images are being cropped now, no translation is normally necessary.
+ // However, some things like graphical bullet points still get translated.
+ ::basegfx::B2DHomMatrix m; // #i73264#
+ m.scale(1.0 / XScale, 1.0 / YScale);
+ if (destRect.Left() || destRect.Top())
+ m.translate(destRect.Left(), destRect.Top());
+
+ FillStyle aFillStyle(nBitmapId, true, m);
+
+ sal_uInt16 nShapeId = defineShape(aPoly, aFillStyle);
+
+ maShapeIds.push_back(nShapeId);
+}
+
+void Writer::Impl_writeBmp(sal_uInt16 nBitmapId, sal_uInt32 width, sal_uInt32 height,
+ sal_uInt8 const* pCompressed, sal_uInt32 compressed_size)
+{
+ startTag(TAG_DEFINEBITSLOSSLESS2);
+
+ mpTag->addUI16(nBitmapId);
+ mpTag->addUI8(5);
+ mpTag->addUI16(uInt16_(width));
+ mpTag->addUI16(uInt16_(height));
+
+ mpTag->WriteBytes(pCompressed, compressed_size);
+
+ endTag();
+}
+
+void Writer::Impl_writeJPEG(sal_uInt16 nBitmapId, const sal_uInt8* pJpgData,
+ sal_uInt32 nJpgDataLength, sal_uInt8 const* pAlphaCompressed,
+ sal_uInt32 alpha_compressed_size)
+{
+ // AS: Go through the actual JPEG bits, separating out the
+ // header fields from the actual image fields. Fields are
+ // identified by 0xFFXX where XX is the field type. Both
+ // the header and the image need start and stop (D8 and D9),
+ // so that's why you see those written to both. I don't
+ // really know what the rest of these are, I got it to work
+ // kind of by trial and error and by comparing with known
+ // good SWF files.
+ sal_uInt8 cType = 0x01;
+ const sal_uInt8* pJpgSearch = pJpgData;
+
+ int nLength = 0;
+
+ SvMemoryStream EncodingTableStream;
+ SvMemoryStream ImageBitsStream;
+ for (; pJpgSearch < pJpgData + nJpgDataLength; pJpgSearch += nLength)
+ {
+#ifdef DBG_UTIL
+ if (0xFF != *pJpgSearch)
+ {
+ OSL_FAIL("Expected JPEG marker.");
+ ((void)0);
+ }
+#endif
+
+ cType = *(pJpgSearch + 1);
+
+ if (0xD8 == cType || 0xD9 == cType)
+ {
+ nLength = 2;
+ }
+ else if (0xDA == cType)
+ {
+ //AS: This is the actual image data, and runs to the
+ // end of the file (as best I know), minus 2 bytes
+ // for the closing 0xFFD9.
+ nLength = nJpgDataLength - (pJpgSearch - pJpgData) - 2;
+ }
+ else
+ {
+ // AS: Lengths are big endian.
+
+ // Beware. pJpgSearch is not necessarily word-aligned,
+ // so we access it byte-wise.
+
+ // AS: Add 2 to the length to include the 0xFFXX itself.
+ nLength = 2 + (pJpgSearch[2] << 8) + pJpgSearch[3];
+ }
+
+ // AS: I'm referring to libjpeg for a list of what these
+ // markers are. See jdmarker.c for a list.
+ // AS: I'm ignoring application specific markers 0xE1...0xEF
+ // and comments 0xFE. I don't know what
+ // 0xF0 or 0xFD are for, and they don't come up.
+ // Additionally, 0xDE and 0xDF aren't clear to me.
+ switch (cType)
+ {
+ case 0xD8:
+ case 0xD9:
+ EncodingTableStream.WriteBytes(pJpgSearch, nLength);
+ ImageBitsStream.WriteBytes(pJpgSearch, nLength);
+ break;
+
+ case 0x01:
+ case 0xDB:
+ case 0xDC:
+ case 0xDD:
+ case 0xC4:
+ EncodingTableStream.WriteBytes(pJpgSearch, nLength);
+ break;
+
+ case 0xC0:
+ case 0xC1:
+ case 0xC2:
+ case 0xC3:
+ case 0xC5:
+ case 0xC6:
+ case 0xC7:
+ // case 0xC8: Apparently reserved for JPEG extensions?
+ case 0xC9:
+ case 0xCA:
+ case 0xCB:
+ case 0xCD:
+ case 0xCE:
+ case 0xCF:
+ case 0xDA:
+ case 0xE0:
+ ImageBitsStream.WriteBytes(pJpgSearch, nLength);
+ break;
+
+ default:
+ OSL_FAIL("JPEG marker I didn't handle!");
+ }
+ }
+
+ sal_uInt32 nEncodingTableSize = EncodingTableStream.TellEnd();
+ EncodingTableStream.Seek(STREAM_SEEK_TO_BEGIN);
+
+ sal_uInt32 nImageBitsSize = ImageBitsStream.TellEnd();
+ ImageBitsStream.Seek(STREAM_SEEK_TO_BEGIN);
+
+ // AS: If we need alpha support, use TAG_DEFINEBITSJPEG3.
+ if (alpha_compressed_size > 0)
+ {
+ startTag(TAG_DEFINEBITSJPEG3);
+
+ mpTag->addUI16(nBitmapId);
+
+ mpTag->addUI32(nEncodingTableSize + nImageBitsSize);
+
+ mpTag->WriteBytes(EncodingTableStream.GetData(), nEncodingTableSize);
+ mpTag->WriteBytes(ImageBitsStream.GetData(), nImageBitsSize);
+
+ mpTag->WriteBytes(pAlphaCompressed, alpha_compressed_size);
+
+ endTag();
+ }
+ else
+ {
+ startTag(TAG_DEFINEBITSJPEG2);
+
+ mpTag->addUI16(nBitmapId);
+
+ mpTag->WriteBytes(EncodingTableStream.GetData(), nEncodingTableSize);
+ mpTag->WriteBytes(ImageBitsStream.GetData(), nImageBitsSize);
+
+ endTag();
+ }
+}
+
+void Writer::Impl_writeLine(const Point& rPt1, const Point& rPt2, const Color* pLineColor)
+{
+ Color aOldColor(mpVDev->GetLineColor());
+ if (pLineColor)
+ mpVDev->SetLineColor(*pLineColor);
+
+ const Point aPtAry[2] = { rPt1, rPt2 };
+ tools::Polygon aPoly(2, aPtAry);
+ Impl_writePolygon(aPoly, false);
+
+ mpVDev->SetLineColor(aOldColor);
+}
+
+void Writer::Impl_writeRect(const tools::Rectangle& rRect, long nRadX, long nRadY)
+{
+ if ((rRect.Top() == rRect.Bottom()) || (rRect.Left() == rRect.Right()))
+ {
+ Color aColor(mpVDev->GetFillColor());
+ Impl_writeLine(rRect.TopLeft(), rRect.BottomRight(), &aColor);
+ }
+ else
+ {
+ tools::Polygon aPoly(rRect, nRadX, nRadY);
+ Impl_writePolygon(aPoly, true);
+ }
+}
+
+void Writer::Impl_writeEllipse(const Point& rCenter, long nRadX, long nRadY)
+{
+ tools::Polygon aPoly(rCenter, nRadX, nRadY);
+ Impl_writePolygon(aPoly, false);
+}
+
+/** Writes the stroke defined by SvtGraphicStroke and returns true or it returns
+ false if it can't handle this stroke.
+*/
+bool Writer::Impl_writeStroke(SvtGraphicStroke const& rStroke)
+{
+ tools::Polygon aPolygon;
+ rStroke.getPath(aPolygon);
+ tools::PolyPolygon aPolyPolygon(aPolygon);
+
+ map(aPolyPolygon);
+
+ // as log as not LINESTYLE2 and DefineShape4 is used (which
+ // added support for LineJoin), only round LineJoins are
+ // supported. Fallback to MetaActionType::POLYLINE and MetaActionType::LINE
+ if (SvtGraphicStroke::joinRound != rStroke.getJoinType())
+ return false;
+
+ tools::PolyPolygon aStartArrow;
+ rStroke.getStartArrow(aStartArrow);
+ if (0 != aStartArrow.Count())
+ return false; // todo: Implement line ends
+
+ tools::PolyPolygon aEndArrow;
+ rStroke.getEndArrow(aEndArrow);
+ if (0 != aEndArrow.Count())
+ return false; // todo: Implement line ends
+
+ SvtGraphicStroke::DashArray aDashArray;
+ rStroke.getDashArray(aDashArray);
+ if (!aDashArray.empty())
+ return false; // todo: implement dashes
+
+ Color aColor(mpVDev->GetLineColor());
+
+ if (0.0 != rStroke.getTransparency())
+ aColor.SetAlpha(255
+ - sal::static_int_cast<sal_uInt8>(
+ std::clamp(rStroke.getTransparency() * 0xff, 0.0, 255.0)));
+
+ sal_uInt16 nShapeId = defineShape(aPolyPolygon,
+ sal::static_int_cast<sal_uInt16>(mapRelative(
+ static_cast<sal_Int32>(rStroke.getStrokeWidth()))),
+ aColor);
+ maShapeIds.push_back(nShapeId);
+ return true;
+}
+
+/** Writes the filling defined by SvtGraphicFill and returns true or it returns
+ false if it can't handle this filling.
+*/
+bool Writer::Impl_writeFilling(SvtGraphicFill const& rFilling)
+{
+ tools::PolyPolygon aPolyPolygon;
+ rFilling.getPath(aPolyPolygon);
+
+ tools::Rectangle aOldRect(aPolyPolygon.GetBoundRect());
+
+ map(aPolyPolygon);
+
+ tools::Rectangle aNewRect(aPolyPolygon.GetBoundRect());
+
+ switch (rFilling.getFillType())
+ {
+ case SvtGraphicFill::fillSolid:
+ {
+ Color aColor(rFilling.getFillColor());
+
+ if (0.0 != rFilling.getTransparency())
+ aColor.SetAlpha(255
+ - sal::static_int_cast<sal_uInt8>(
+ std::clamp(rFilling.getTransparency() * 0xff, 0.0, 255.0)));
+
+ FillStyle aFillStyle(aColor);
+
+ sal_uInt16 nShapeId = defineShape(aPolyPolygon, aFillStyle);
+ maShapeIds.push_back(nShapeId);
+ }
+ break;
+ case SvtGraphicFill::fillGradient:
+ return false;
+ case SvtGraphicFill::fillHatch:
+ return false;
+ case SvtGraphicFill::fillTexture:
+ {
+ Graphic aGraphic;
+ rFilling.getGraphic(aGraphic);
+
+ // CL->AS: Should we also scale down the quality here depending on image scale?
+ sal_uInt16 nBitmapId = defineBitmap(aGraphic.GetBitmapEx(), mnJPEGCompressMode);
+
+ ::basegfx::B2DHomMatrix aMatrix; // #i73264#
+
+ SvtGraphicFill::Transform aTransform;
+
+ rFilling.getTransform(aTransform);
+
+ sal_uInt16 a, b;
+ for (a = 0; a < 2; a++)
+ {
+ for (b = 0; b < 3; b++)
+ {
+ aMatrix.set(a, b, aTransform.matrix[a * 3 + b]);
+ }
+ }
+ aMatrix.set(2, 0, 0.0);
+ aMatrix.set(2, 1, 0.0);
+ aMatrix.set(2, 2, 1.0);
+
+ // scale bitmap
+ double XScale = aOldRect.GetWidth()
+ ? static_cast<double>(aNewRect.GetWidth()) / aOldRect.GetWidth()
+ : 1.0;
+ double YScale = aOldRect.GetHeight()
+ ? static_cast<double>(aNewRect.GetHeight()) / aOldRect.GetHeight()
+ : 1.0;
+
+ aMatrix.scale(XScale, YScale);
+
+ FillStyle aFillStyle(nBitmapId, !rFilling.isTiling(), aMatrix);
+
+ sal_uInt16 nShapeId = defineShape(aPolyPolygon, aFillStyle);
+ maShapeIds.push_back(nShapeId);
+ }
+ break;
+ }
+ return true;
+}
+
+/* CL: The idea was to export page fields as text fields that get their
+ string from a variable set with actionscript by each page. This didn't
+ work out since the formatting is always wrong when text follows the
+ page number field since pages greater one may require more space than
+ page 1
+*/
+#if 0
+bool Writer::Impl_writePageField( Rectangle& rTextBounds )
+{
+ startTag( TAG_DEFINEEDITTEXT );
+
+ sal_uInt16 nTextId = createID();
+
+ mpTag->addUI16( nTextId );
+ mpTag->addRect( rTextBounds );
+
+ BitStream aBits;
+ aBits.writeUB( 1, 1 ); // HasText
+ aBits.writeUB( 0, 1 ); // WordWrap
+ aBits.writeUB( 0, 1 ); // MultiLine
+ aBits.writeUB( 0, 1 ); // Password
+ aBits.writeUB( 1, 1 ); // HasTextColor
+ aBits.writeUB( 0, 1 ); // HasMaxLength
+ aBits.writeUB( 0, 1 ); // HasFont
+ aBits.writeUB( 0, 1 ); // Reserved
+ aBits.writeUB( 0, 1 ); // AutoSize
+ aBits.writeUB( 0, 1 ); // HasLayout
+ aBits.writeUB( 1, 1 ); // NoSelect
+ aBits.writeUB( 1, 1 ); // Border
+ aBits.writeUB( 0, 1 ); // Reserved
+ aBits.writeUB( 0, 1 ); // HTML
+ aBits.writeUB( 0, 1 ); // UseOutlines
+ mpTag->addBits( aBits );
+
+ Color aCOL_BLACK );
+ mpTag->addRGB( aColor );
+ mpTag->addString( "PageNumber" );
+ mpTag->addString( "XXX" );
+
+ endTag();
+
+ maShapeIds.push_back( nTextId );
+
+ return true;
+}
+#endif
+
+void Writer::Impl_handleLineInfoPolyPolygons(const LineInfo& rInfo,
+ const basegfx::B2DPolygon& rLinePolygon)
+{
+ if (!rLinePolygon.count())
+ return;
+
+ basegfx::B2DPolyPolygon aLinePolyPolygon(rLinePolygon);
+ basegfx::B2DPolyPolygon aFillPolyPolygon;
+
+ rInfo.applyToB2DPolyPolygon(aLinePolyPolygon, aFillPolyPolygon);
+
+ if (aLinePolyPolygon.count())
+ {
+ for (sal_uInt32 a(0); a < aLinePolyPolygon.count(); a++)
+ {
+ const basegfx::B2DPolygon& aCandidate(aLinePolyPolygon.getB2DPolygon(a));
+ Impl_writePolygon(tools::Polygon(aCandidate), false);
+ }
+ }
+
+ if (!aFillPolyPolygon.count())
+ return;
+
+ const Color aOldLineColor(mpVDev->GetLineColor());
+ const Color aOldFillColor(mpVDev->GetFillColor());
+
+ mpVDev->SetLineColor();
+ mpVDev->SetFillColor(aOldLineColor);
+
+ for (sal_uInt32 a(0); a < aFillPolyPolygon.count(); a++)
+ {
+ const tools::Polygon aPolygon(aFillPolyPolygon.getB2DPolygon(a));
+ Impl_writePolyPolygon(tools::PolyPolygon(aPolygon), true);
+ }
+
+ mpVDev->SetLineColor(aOldLineColor);
+ mpVDev->SetFillColor(aOldFillColor);
+}
+
+void Writer::Impl_writeActions(const GDIMetaFile& rMtf)
+{
+ tools::Rectangle clipRect;
+ int bMap = 0;
+ for (size_t i = 0, nCount = rMtf.GetActionSize(); i < nCount; i++)
+ {
+ const MetaAction* pAction = rMtf.GetAction(i);
+ const MetaActionType nType = pAction->GetType();
+
+ switch (nType)
+ {
+ case MetaActionType::PIXEL:
+ {
+ const MetaPixelAction* pA = static_cast<const MetaPixelAction*>(pAction);
+
+ Impl_writeLine(pA->GetPoint(), pA->GetPoint(), &pA->GetColor());
+ }
+ break;
+
+ case MetaActionType::POINT:
+ {
+ const MetaPointAction* pA = static_cast<const MetaPointAction*>(pAction);
+
+ Impl_writeLine(pA->GetPoint(), pA->GetPoint());
+ }
+ break;
+
+ case MetaActionType::LINE:
+ {
+ const MetaLineAction* pA = static_cast<const MetaLineAction*>(pAction);
+
+ if (pA->GetLineInfo().IsDefault())
+ {
+ Impl_writeLine(pA->GetStartPoint(), pA->GetEndPoint());
+ }
+ else
+ {
+ // LineInfo used; handle Dash/Dot and fat lines
+ basegfx::B2DPolygon aPolygon;
+ aPolygon.append(
+ basegfx::B2DPoint(pA->GetStartPoint().X(), pA->GetStartPoint().Y()));
+ aPolygon.append(
+ basegfx::B2DPoint(pA->GetEndPoint().X(), pA->GetEndPoint().Y()));
+ Impl_handleLineInfoPolyPolygons(pA->GetLineInfo(), aPolygon);
+ }
+ }
+ break;
+
+ case MetaActionType::RECT:
+ {
+ Impl_writeRect(static_cast<const MetaRectAction*>(pAction)->GetRect(), 0, 0);
+ }
+ break;
+
+ case MetaActionType::ROUNDRECT:
+ {
+ const MetaRoundRectAction* pA = static_cast<const MetaRoundRectAction*>(pAction);
+
+ Impl_writeRect(pA->GetRect(), pA->GetHorzRound(), pA->GetVertRound());
+ }
+ break;
+
+ case MetaActionType::ELLIPSE:
+ {
+ const MetaEllipseAction* pA = static_cast<const MetaEllipseAction*>(pAction);
+ const tools::Rectangle& rRect = pA->GetRect();
+
+ Impl_writeEllipse(rRect.Center(), rRect.GetWidth() >> 1, rRect.GetHeight() >> 1);
+ }
+ break;
+
+ case MetaActionType::ARC:
+ case MetaActionType::PIE:
+ case MetaActionType::CHORD:
+ case MetaActionType::POLYGON:
+ {
+ tools::Polygon aPoly;
+
+ switch (nType)
+ {
+ case MetaActionType::ARC:
+ {
+ const MetaArcAction* pA = static_cast<const MetaArcAction*>(pAction);
+ aPoly = tools::Polygon(pA->GetRect(), pA->GetStartPoint(),
+ pA->GetEndPoint(), PolyStyle::Arc);
+ }
+ break;
+
+ case MetaActionType::PIE:
+ {
+ const MetaPieAction* pA = static_cast<const MetaPieAction*>(pAction);
+ aPoly = tools::Polygon(pA->GetRect(), pA->GetStartPoint(),
+ pA->GetEndPoint(), PolyStyle::Pie);
+ }
+ break;
+
+ case MetaActionType::CHORD:
+ {
+ const MetaChordAction* pA = static_cast<const MetaChordAction*>(pAction);
+ aPoly = tools::Polygon(pA->GetRect(), pA->GetStartPoint(),
+ pA->GetEndPoint(), PolyStyle::Chord);
+ }
+ break;
+
+ case MetaActionType::POLYGON:
+ aPoly = static_cast<const MetaPolygonAction*>(pAction)->GetPolygon();
+ break;
+ default:
+ break;
+ }
+
+ if (aPoly.GetSize())
+ {
+ Impl_writePolygon(aPoly, true);
+ }
+ }
+ break;
+
+ case MetaActionType::POLYLINE:
+ {
+ const MetaPolyLineAction* pA = static_cast<const MetaPolyLineAction*>(pAction);
+ const tools::Polygon& rPoly = pA->GetPolygon();
+
+ if (rPoly.GetSize())
+ {
+ if (pA->GetLineInfo().IsDefault())
+ {
+ Impl_writePolygon(rPoly, false);
+ }
+ else
+ {
+ // LineInfo used; handle Dash/Dot and fat lines
+ Impl_handleLineInfoPolyPolygons(pA->GetLineInfo(), rPoly.getB2DPolygon());
+ }
+ }
+ }
+ break;
+
+ case MetaActionType::POLYPOLYGON:
+ {
+ const MetaPolyPolygonAction* pA
+ = static_cast<const MetaPolyPolygonAction*>(pAction);
+ const tools::PolyPolygon& rPolyPoly = pA->GetPolyPolygon();
+
+ if (rPolyPoly.Count())
+ Impl_writePolyPolygon(rPolyPoly, true);
+ }
+ break;
+
+ case MetaActionType::GRADIENT:
+ {
+ const MetaGradientAction* pA = static_cast<const MetaGradientAction*>(pAction);
+
+ tools::Polygon aPoly(pA->GetRect());
+ tools::PolyPolygon aPolyPoly(aPoly);
+ Impl_writeGradientEx(aPolyPoly, pA->GetGradient());
+ }
+ break;
+
+ case MetaActionType::GRADIENTEX:
+ {
+ const MetaGradientExAction* pA = static_cast<const MetaGradientExAction*>(pAction);
+ Impl_writeGradientEx(pA->GetPolyPolygon(), pA->GetGradient());
+ }
+ break;
+
+ case MetaActionType::HATCH:
+ {
+ const MetaHatchAction* pA = static_cast<const MetaHatchAction*>(pAction);
+ GDIMetaFile aTmpMtf;
+
+ mpVDev->AddHatchActions(pA->GetPolyPolygon(), pA->GetHatch(), aTmpMtf);
+ Impl_writeActions(aTmpMtf);
+ }
+ break;
+
+ case MetaActionType::Transparent:
+ {
+ const MetaTransparentAction* pA
+ = static_cast<const MetaTransparentAction*>(pAction);
+ const tools::PolyPolygon& rPolyPoly = pA->GetPolyPolygon();
+
+ if (rPolyPoly.Count())
+ {
+ // convert transparence from percent into 0x00 - 0xff
+ sal_uInt8 nTransparence = static_cast<sal_uInt8>(
+ std::clamp(pA->GetTransparence() * 2.55, 0.0, 255.0));
+ Impl_writePolyPolygon(rPolyPoly, true, nTransparence);
+ }
+ }
+ break;
+
+ case MetaActionType::FLOATTRANSPARENT:
+ {
+ const MetaFloatTransparentAction* pA
+ = static_cast<const MetaFloatTransparentAction*>(pAction);
+ GDIMetaFile aTmpMtf(pA->GetGDIMetaFile());
+ Point aSrcPt(aTmpMtf.GetPrefMapMode().GetOrigin());
+ const Size aSrcSize(aTmpMtf.GetPrefSize());
+ const Point aDestPt(pA->GetPoint());
+ const Size aDestSize(pA->GetSize());
+ const double fScaleX
+ = aSrcSize.Width() ? static_cast<double>(aDestSize.Width()) / aSrcSize.Width()
+ : 1.0;
+ const double fScaleY = aSrcSize.Height() ? static_cast<double>(aDestSize.Height())
+ / aSrcSize.Height()
+ : 1.0;
+ 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);
+
+ const Gradient& rGradient = pA->GetGradient();
+ sal_uInt32 nLuminance
+ = (static_cast<sal_Int32>(rGradient.GetStartColor().GetLuminance())
+ + static_cast<sal_Int32>(rGradient.GetEndColor().GetLuminance()))
+ >> 1;
+
+ sal_uInt8 nOldGlobalTransparency = mnGlobalTransparency;
+ mnGlobalTransparency = static_cast<sal_uInt8>(std::clamp(
+ nLuminance, static_cast<sal_uInt32>(0), static_cast<sal_uInt32>(255)));
+
+ mpVDev->Push();
+ Impl_writeActions(aTmpMtf);
+ mpVDev->Pop();
+
+ mnGlobalTransparency = nOldGlobalTransparency;
+ }
+ break;
+
+ case MetaActionType::EPS:
+ {
+ const MetaEPSAction* pA = static_cast<const MetaEPSAction*>(pAction);
+ const GDIMetaFile& aGDIMetaFile(pA->GetSubstitute());
+ bool bFound = false;
+
+ for (size_t j = 0, nC = aGDIMetaFile.GetActionSize(); (j < nC) && !bFound; j++)
+ {
+ const MetaAction* pSubstAct = aGDIMetaFile.GetAction(j);
+
+ if (pSubstAct->GetType() == MetaActionType::BMPSCALE)
+ {
+ bFound = true;
+ const MetaBmpScaleAction* pBmpScaleAction
+ = static_cast<const MetaBmpScaleAction*>(pSubstAct);
+ Impl_writeImage(BitmapEx(pBmpScaleAction->GetBitmap()), pA->GetPoint(),
+ pA->GetSize(), Point(),
+ pBmpScaleAction->GetBitmap().GetSizePixel(), clipRect,
+ 1 == bMap);
+ }
+ }
+ }
+ break;
+
+ case MetaActionType::COMMENT:
+ {
+ const MetaCommentAction* pA = static_cast<const MetaCommentAction*>(pAction);
+ const sal_uInt8* pData = pA->GetData();
+
+ if (pA->GetComment().equalsIgnoreAsciiCase("XGRAD_SEQ_BEGIN"))
+ {
+ const MetaGradientExAction* pGradAction = nullptr;
+ bool bDone = false;
+
+ while (!bDone && (++i < nCount))
+ {
+ pAction = rMtf.GetAction(i);
+
+ 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")))
+ {
+ bDone = true;
+ }
+ }
+
+ if (pGradAction)
+ Impl_writeGradientEx(pGradAction->GetPolyPolygon(),
+ pGradAction->GetGradient());
+ }
+ else if (pA->GetComment().equalsIgnoreAsciiCase("XPATHFILL_SEQ_BEGIN") && pData)
+ {
+ // this comment encapsulates all high level information for a filling that caused
+ // the meta actions between the "XPATHFILL_SEQ_BEGIN" and "XPATHFILL_SEQ_END" comment.
+
+ SvtGraphicFill aFilling;
+ SvMemoryStream aMemStm(const_cast<sal_uInt8*>(pData), pA->GetDataSize(),
+ StreamMode::READ);
+
+ // read the fill info
+ ReadSvtGraphicFill(aMemStm, aFilling);
+
+ // if impl_writeFilling can handle this high level filling, it returns true and we
+ // skip all meta actions until "XPATHFILL_SEQ_END"
+ if (Impl_writeFilling(aFilling))
+ {
+ bool bDone = false;
+
+ while (!bDone && (++i < nCount))
+ {
+ pAction = rMtf.GetAction(i);
+
+ if ((pAction->GetType() == MetaActionType::COMMENT)
+ && (static_cast<const MetaCommentAction*>(pAction)
+ ->GetComment()
+ .equalsIgnoreAsciiCase("XPATHFILL_SEQ_END")))
+ {
+ bDone = true;
+ }
+ }
+ }
+ }
+ else if (pA->GetComment().equalsIgnoreAsciiCase("XPATHSTROKE_SEQ_BEGIN") && pData)
+ {
+ // this comment encapsulates all high level information for a filling that caused
+ // the meta actions between the "XPATHFILL_SEQ_BEGIN" and "XPATHFILL_SEQ_END" comment.
+
+ SvtGraphicStroke aStroke;
+ SvMemoryStream aMemStm(const_cast<sal_uInt8*>(pData), pA->GetDataSize(),
+ StreamMode::READ);
+
+ // read the fill info
+ ReadSvtGraphicStroke(aMemStm, aStroke);
+
+ // if impl_writeStroke can handle this high level stroke, it returns true and we
+ // skip all meta actions until "XPATHSTROKE_SEQ_END"
+ if (Impl_writeStroke(aStroke))
+ {
+ bool bDone = false;
+
+ while (!bDone && (++i < nCount))
+ {
+ pAction = rMtf.GetAction(i);
+
+ if ((pAction->GetType() == MetaActionType::COMMENT)
+ && (static_cast<const MetaCommentAction*>(pAction)
+ ->GetComment()
+ .equalsIgnoreAsciiCase("XPATHSTROKE_SEQ_END")))
+ {
+ bDone = true;
+ }
+ }
+ }
+ }
+ }
+ break;
+
+ case MetaActionType::BMPSCALE:
+ {
+ const MetaBmpScaleAction* pA = static_cast<const MetaBmpScaleAction*>(pAction);
+
+ Impl_writeImage(BitmapEx(pA->GetBitmap()), pA->GetPoint(), pA->GetSize(), Point(),
+ pA->GetBitmap().GetSizePixel(), clipRect, 1 == bMap);
+ }
+ break;
+
+ case MetaActionType::BMP:
+ {
+ const MetaBmpAction* pA = static_cast<const MetaBmpAction*>(pAction);
+ Impl_writeImage(BitmapEx(pA->GetBitmap()), pA->GetPoint(),
+ mpVDev->PixelToLogic(pA->GetBitmap().GetSizePixel()), Point(),
+ pA->GetBitmap().GetSizePixel(), clipRect, 1 == bMap);
+ }
+ break;
+
+ case MetaActionType::BMPSCALEPART:
+ {
+ const MetaBmpScalePartAction* pA
+ = static_cast<const MetaBmpScalePartAction*>(pAction);
+ Impl_writeImage(BitmapEx(pA->GetBitmap()), pA->GetDestPoint(), pA->GetDestSize(),
+ pA->GetSrcPoint(), pA->GetSrcSize(), clipRect, 1 == bMap);
+ }
+ break;
+
+ case MetaActionType::BMPEX:
+ {
+ const MetaBmpExAction* pA = static_cast<const MetaBmpExAction*>(pAction);
+ Impl_writeImage(pA->GetBitmapEx(), pA->GetPoint(),
+ mpVDev->PixelToLogic(pA->GetBitmapEx().GetSizePixel()), Point(),
+ pA->GetBitmapEx().GetSizePixel(), clipRect, 1 == bMap);
+ }
+ break;
+
+ case MetaActionType::BMPEXSCALE:
+ {
+ const MetaBmpExScaleAction* pA = static_cast<const MetaBmpExScaleAction*>(pAction);
+ Impl_writeImage(pA->GetBitmapEx(), pA->GetPoint(), pA->GetSize(), Point(),
+ pA->GetBitmapEx().GetSizePixel(), clipRect, 1 == bMap);
+ }
+ break;
+
+ case MetaActionType::BMPEXSCALEPART:
+ {
+ const MetaBmpExScalePartAction* pA
+ = static_cast<const MetaBmpExScalePartAction*>(pAction);
+ Impl_writeImage(pA->GetBitmapEx(), pA->GetDestPoint(), pA->GetDestSize(),
+ pA->GetSrcPoint(), pA->GetSrcSize(), clipRect, 1 == bMap);
+ }
+ break;
+
+ case MetaActionType::TEXT:
+ {
+ const MetaTextAction* pA = static_cast<const MetaTextAction*>(pAction);
+ Impl_writeText(pA->GetPoint(), pA->GetText().copy(pA->GetIndex(), pA->GetLen()),
+ nullptr, 0);
+ }
+ break;
+
+ case MetaActionType::TEXTRECT:
+ {
+ const MetaTextRectAction* pA = static_cast<const MetaTextRectAction*>(pAction);
+ Impl_writeText(pA->GetRect().TopLeft(), pA->GetText(), nullptr, 0);
+ }
+ break;
+
+ case MetaActionType::TEXTARRAY:
+ {
+ const MetaTextArrayAction* pA = static_cast<const MetaTextArrayAction*>(pAction);
+ Impl_writeText(pA->GetPoint(), pA->GetText().copy(pA->GetIndex(), pA->GetLen()),
+ &pA->GetDXArray(), 0);
+ }
+ break;
+
+ case MetaActionType::STRETCHTEXT:
+ {
+ const MetaStretchTextAction* pA
+ = static_cast<const MetaStretchTextAction*>(pAction);
+ Impl_writeText(pA->GetPoint(), pA->GetText().copy(pA->GetIndex(), pA->GetLen()),
+ nullptr, pA->GetWidth());
+ }
+ break;
+
+ case MetaActionType::ISECTRECTCLIPREGION:
+ {
+ const MetaISectRectClipRegionAction* pA
+ = static_cast<const MetaISectRectClipRegionAction*>(pAction);
+ clipRect = pA->GetRect();
+ [[fallthrough]];
+ }
+ case MetaActionType::CLIPREGION:
+ case MetaActionType::ISECTREGIONCLIPREGION:
+ case MetaActionType::MOVECLIPREGION:
+ {
+ const_cast<MetaAction*>(pAction)->Execute(mpVDev);
+ }
+ break;
+
+ case MetaActionType::MAPMODE:
+ {
+ bMap++;
+ [[fallthrough]];
+ }
+ case MetaActionType::REFPOINT:
+ case MetaActionType::LINECOLOR:
+ case MetaActionType::FILLCOLOR:
+ case MetaActionType::TEXTLINECOLOR:
+ case MetaActionType::TEXTFILLCOLOR:
+ case MetaActionType::TEXTCOLOR:
+ case MetaActionType::TEXTALIGN:
+ case MetaActionType::FONT:
+ case MetaActionType::PUSH:
+ case MetaActionType::POP:
+ case MetaActionType::LAYOUTMODE:
+ {
+ const_cast<MetaAction*>(pAction)->Execute(mpVDev);
+ }
+ break;
+
+ case MetaActionType::RASTEROP:
+ case MetaActionType::MASK:
+ case MetaActionType::MASKSCALE:
+ case MetaActionType::MASKSCALEPART:
+ case MetaActionType::WALLPAPER:
+ case MetaActionType::TEXTLINE:
+ {
+ // !!! >>> we don't want to support these actions
+ }
+ break;
+
+ default:
+ break;
+ }
+ }
+}
+
+void Writer::Impl_addStraightLine(BitStream& rBits, Point& rLastPoint, const double P2x,
+ const double P2y)
+{
+ Point aPoint(FRound(P2x), FRound(P2y));
+
+ Impl_addStraightEdgeRecord(rBits, Int16_(aPoint.X() - rLastPoint.X()),
+ Int16_(aPoint.Y() - rLastPoint.Y()));
+ rLastPoint = aPoint;
+}
+
+void Writer::Impl_addQuadBezier(BitStream& rBits, Point& rLastPoint, const double P2x,
+ const double P2y, const double P3x, const double P3y)
+{
+ Point aControlPoint(FRound(P2x), FRound(P2y));
+ Point aAnchorPoint(FRound(P3x), FRound(P3y));
+
+ Impl_addCurvedEdgeRecord(rBits, Int16_(aControlPoint.X() - rLastPoint.X()),
+ Int16_(aControlPoint.Y() - rLastPoint.Y()),
+ Int16_(aAnchorPoint.X() - aControlPoint.X()),
+ Int16_(aAnchorPoint.Y() - aControlPoint.Y()));
+ rLastPoint = aAnchorPoint;
+}
+
+/* Approximate given cubic bezier curve by quadratic bezier segments */
+void Writer::Impl_quadBezierApprox(BitStream& rBits, Point& rLastPoint, const double d2,
+ const double P1x, const double P1y, const double P2x,
+ const double P2y, const double P3x, const double P3y,
+ const double P4x, const double P4y)
+{
+ // Check for degenerate case, where the given cubic bezier curve
+ // is already quadratic: P4 == 3P3 - 3P2 + P1
+ if (P4x == 3.0 * P3x - 3.0 * P2x + P1x && P4y == 3.0 * P3y - 3.0 * P2y + P1y)
+ {
+ Impl_addQuadBezier(rBits, rLastPoint, 3.0 / 2.0 * P2x - 1.0 / 2.0 * P1x,
+ 3.0 / 2.0 * P2y - 1.0 / 2.0 * P1y, P4x, P4y);
+ }
+ else
+ {
+ // Create quadratic segment for given cubic:
+ // Start and end point must coincide, determine quadratic control
+ // point in such a way that it lies on the intersection of the
+ // tangents at start and end point, resp. Thus, both cubic and
+ // quadratic curve segments will match in 0th and 1st derivative
+ // at the start and end points
+
+ // Intersection of P2P1 and P4P3
+ // (P2y-P4y)(P3x-P4x)-(P2x-P4x)(P3y-P4y)
+ // lambda = -------------------------------------
+ // (P1x-P2x)(P3y-P4y)-(P1y-P2y)(P3x-P4x)
+ //
+ // Intersection point IP is now
+ // IP = P2 + lambda(P1-P2)
+ //
+ const double nominator((P2y - P4y) * (P3x - P4x) - (P2x - P4x) * (P3y - P4y));
+ const double denominator((P1x - P2x) * (P3y - P4y) - (P1y - P2y) * (P3x - P4x));
+ const double lambda(nominator / denominator);
+
+ const double IPx(P2x + lambda * (P1x - P2x));
+ const double IPy(P2y + lambda * (P1y - P2y));
+
+ // Introduce some alias names: quadratic start point is P1, end
+ // point is P4, control point is IP
+ const double QP1x(P1x);
+ const double QP1y(P1y);
+ const double QP2x(IPx);
+ const double QP2y(IPy);
+ const double QP3x(P4x);
+ const double QP3y(P4y);
+
+ // Adapted bezier flatness test (lecture notes from R. Schaback,
+ // Mathematics of Computer-Aided Design, Uni Goettingen, 2000)
+ //
+ // ||C(t) - Q(t)|| <= max ||c_j - q_j||
+ // 0<=j<=n
+ //
+ // In this case, we don't need the distance from the cubic bezier
+ // to a straight line, but to a quadratic bezier. The c_j's are
+ // the cubic bezier's bernstein coefficients, the q_j's the
+ // quadratic bezier's. We have the c_j's given, the q_j's can be
+ // calculated from QPi like this (sorry, mixed index notation, we
+ // use [1,n], formulas use [0,n-1]):
+ //
+ // q_0 = QP1 = P1
+ // q_1 = 1/3 QP1 + 2/3 QP2
+ // q_2 = 2/3 QP2 + 1/3 QP3
+ // q_3 = QP3 = P4
+ //
+ // We can drop case 0 and 3, since there the curves coincide
+ // (distance is zero)
+
+ // calculate argument of max for j=1 and j=2
+ const double fJ1x(P2x - 1.0 / 3.0 * QP1x - 2.0 / 3.0 * QP2x);
+ const double fJ1y(P2y - 1.0 / 3.0 * QP1y - 2.0 / 3.0 * QP2y);
+ const double fJ2x(P3x - 2.0 / 3.0 * QP2x - 1.0 / 3.0 * QP3x);
+ const double fJ2y(P3y - 2.0 / 3.0 * QP2y - 1.0 / 3.0 * QP3y);
+
+ // stop if distance from cubic curve is guaranteed to be bounded by d
+ // Should denominator be 0: then P1P2 and P3P4 are parallel (P1P2^T R[90,P3P4] = 0.0),
+ // meaning that either we have a straight line or an inflexion point (see else block below)
+ if (0.0 != denominator
+ && ::std::max(fJ1x * fJ1x + fJ1y * fJ1y, fJ2x * fJ2x + fJ2y * fJ2y) < d2)
+ {
+ // requested resolution reached.
+ // Add end points to output file.
+ // order is preserved, since this is so to say depth first traversal.
+ Impl_addQuadBezier(rBits, rLastPoint, QP2x, QP2y, QP3x, QP3y);
+ }
+ else
+ {
+ // Maybe subdivide further
+
+ // This is for robustness reasons, since the line intersection
+ // method below gets instable if the curve gets closer to a
+ // straight line. If the given cubic bezier does not deviate by
+ // more than d/4 from a straight line, either:
+ // - take the line (that's what we do here)
+ // - express the line by a quadratic bezier
+
+ // Perform bezier flatness test (lecture notes from R. Schaback,
+ // Mathematics of Computer-Aided Design, Uni Goettingen, 2000)
+ //
+ // ||P(t) - L(t)|| <= max ||b_j - b_0 - j/n(b_n - b_0)||
+ // 0<=j<=n
+ //
+ // What is calculated here is an upper bound to the distance from
+ // a line through b_0 and b_3 (P1 and P4 in our notation) and the
+ // curve. We can drop 0 and n from the running indices, since the
+ // argument of max becomes zero for those cases.
+ const double fJ1x2(P2x - P1x - 1.0 / 3.0 * (P4x - P1x));
+ const double fJ1y2(P2y - P1y - 1.0 / 3.0 * (P4y - P1y));
+ const double fJ2x2(P3x - P1x - 2.0 / 3.0 * (P4x - P1x));
+ const double fJ2y2(P3y - P1y - 2.0 / 3.0 * (P4y - P1y));
+
+ // stop if distance from line is guaranteed to be bounded by d/4
+ if (::std::max(fJ1x2 * fJ1x2 + fJ1y2 * fJ1y2, fJ2x2 * fJ2x2 + fJ2y2 * fJ2y2)
+ < d2 / 16.0)
+ {
+ // do not subdivide further, add straight line instead
+ Impl_addStraightLine(rBits, rLastPoint, P4x, P4y);
+ }
+ else
+ {
+ // deCasteljau bezier arc, split at t=0.5
+ // Foley/vanDam, p. 508
+ const double L1x(P1x), L1y(P1y);
+ const double L2x((P1x + P2x) * 0.5), L2y((P1y + P2y) * 0.5);
+ const double Hx((P2x + P3x) * 0.5), Hy((P2y + P3y) * 0.5);
+ const double L3x((L2x + Hx) * 0.5), L3y((L2y + Hy) * 0.5);
+ const double R4x(P4x), R4y(P4y);
+ const double R3x((P3x + P4x) * 0.5), R3y((P3y + P4y) * 0.5);
+ const double R2x((Hx + R3x) * 0.5), R2y((Hy + R3y) * 0.5);
+ const double R1x((L3x + R2x) * 0.5), R1y((L3y + R2y) * 0.5);
+ const double L4x(R1x), L4y(R1y);
+
+ // subdivide further
+ Impl_quadBezierApprox(rBits, rLastPoint, d2, L1x, L1y, L2x, L2y, L3x, L3y, L4x,
+ L4y);
+ Impl_quadBezierApprox(rBits, rLastPoint, d2, R1x, R1y, R2x, R2y, R3x, R3y, R4x,
+ R4y);
+ }
+ }
+ }
+}
+
+Reference<XBreakIterator> const& Writer::Impl_GetBreakIterator()
+{
+ if (!mxBreakIterator.is())
+ {
+ Reference<XComponentContext> xContext(::comphelper::getProcessComponentContext());
+ mxBreakIterator = BreakIterator::create(xContext);
+ }
+ return mxBreakIterator;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/filter/source/flash/swfwriter2.cxx b/filter/source/flash/swfwriter2.cxx
new file mode 100644
index 000000000000..050bdadc179d
--- /dev/null
+++ b/filter/source/flash/swfwriter2.cxx
@@ -0,0 +1,575 @@
+/* -*- 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 "swfwriter.hxx"
+#include <vcl/virdev.hxx>
+#include <basegfx/matrix/b2dhommatrixtools.hxx>
+#include <tools/debug.hxx>
+
+#include <math.h>
+
+using namespace ::swf;
+using namespace ::com::sun::star::uno;
+using namespace ::com::sun::star::io;
+
+static sal_uInt16 getMaxBitsUnsigned(sal_uInt32 nValue)
+{
+ sal_uInt16 nBits = 0;
+
+ while (nValue)
+ {
+ nBits++;
+ nValue >>= 1;
+ }
+
+ return nBits;
+}
+
+sal_uInt16 getMaxBitsSigned(sal_Int32 nValue)
+{
+ if (nValue < 0)
+ nValue *= -1;
+
+ return getMaxBitsUnsigned(static_cast<sal_uInt32>(nValue)) + 1;
+}
+
+BitStream::BitStream()
+{
+ mnBitPos = 8;
+ mnCurrentByte = 0;
+}
+
+void BitStream::writeUB(sal_uInt32 nValue, sal_uInt16 nBits)
+{
+ while (nBits != 0)
+ {
+ mnCurrentByte |= nValue << (32 - nBits) >> (32 - mnBitPos);
+
+ if (nBits > mnBitPos)
+ {
+ nBits = nBits - mnBitPos;
+ mnBitPos = 0;
+ }
+ else
+ {
+ mnBitPos = sal::static_int_cast<sal_uInt8>(mnBitPos - nBits);
+ nBits = 0;
+ }
+
+ if (0 == mnBitPos)
+ pad();
+ }
+}
+
+void BitStream::writeSB(sal_Int32 nValue, sal_uInt16 nBits)
+{
+ writeUB(static_cast<sal_uInt32>(nValue), nBits);
+}
+
+void BitStream::writeFB(sal_uInt32 nValue, sal_uInt16 nBits) { writeUB(nValue, nBits); }
+
+void BitStream::pad()
+{
+ if (8 != mnBitPos)
+ {
+ maData.push_back(mnCurrentByte);
+ mnCurrentByte = 0;
+ mnBitPos = 8;
+ }
+}
+
+void BitStream::writeTo(SvStream& out)
+{
+ pad();
+
+ for (auto const& data : maData)
+ {
+ out.WriteUChar(data);
+ }
+}
+
+sal_uInt32 BitStream::getOffset() const { return maData.size(); }
+
+Tag::Tag(sal_uInt8 nTagId) { mnTagId = nTagId; }
+
+void Tag::write(SvStream& out)
+{
+ sal_uInt32 nSz = TellEnd();
+ Seek(STREAM_SEEK_TO_BEGIN);
+
+ if (mnTagId != 0xff)
+ {
+ bool bLarge = nSz > 62;
+
+ sal_uInt16 nCode = (mnTagId << 6) | (bLarge ? 0x3f : uInt16_(nSz));
+
+ out.WriteUChar(nCode);
+ out.WriteUChar(nCode >> 8);
+
+ if (bLarge)
+ {
+ sal_uInt32 nTmp = nSz;
+
+ out.WriteUChar(nTmp);
+ nTmp >>= 8;
+ out.WriteUChar(nTmp);
+ nTmp >>= 8;
+ out.WriteUChar(nTmp);
+ nTmp >>= 8;
+ out.WriteUChar(nTmp);
+ }
+ }
+
+ out.WriteBytes(GetData(), nSz);
+}
+#if 0
+
+
+void Tag::addI32( sal_Int32 nValue )
+{
+ addUI32( static_cast<sal_uInt32>( nValue ) );
+}
+#endif
+
+void Tag::addUI32(sal_uInt32 nValue) { WriteUInt32(nValue); }
+#if 0
+
+
+void Tag::addI16( sal_Int16 nValue )
+{
+ addUI16( static_cast<sal_uInt16>( nValue ) );
+}
+#endif
+
+void Tag::addUI16(sal_uInt16 nValue)
+{
+ WriteUChar(nValue);
+ WriteUChar(nValue >> 8);
+}
+
+void Tag::addUI8(sal_uInt8 nValue) { WriteUChar(nValue); }
+
+void Tag::addBits(BitStream& rIn) { rIn.writeTo(*this); }
+
+void Tag::addRGBA(const Color& rColor)
+{
+ addUI8(rColor.GetRed());
+ addUI8(rColor.GetGreen());
+ addUI8(rColor.GetBlue());
+ addUI8(rColor.GetAlpha());
+}
+
+void Tag::addRGB(const Color& rColor)
+{
+ addUI8(rColor.GetRed());
+ addUI8(rColor.GetGreen());
+ addUI8(rColor.GetBlue());
+}
+
+void Tag::addRect(const tools::Rectangle& rRect) { writeRect(*this, rRect); }
+
+void Tag::writeRect(SvStream& rOut, const tools::Rectangle& rRect)
+{
+ BitStream aBits;
+
+ sal_Int32 minX, minY, maxX, maxY;
+
+ if (rRect.Left() < rRect.Right())
+ {
+ minX = rRect.Left();
+ maxX = rRect.Right();
+ }
+ else
+ {
+ maxX = rRect.Left();
+ minX = rRect.Right();
+ }
+
+ if (rRect.Top() < rRect.Bottom())
+ {
+ minY = rRect.Top();
+ maxY = rRect.Bottom();
+ }
+ else
+ {
+ maxY = rRect.Top();
+ minY = rRect.Bottom();
+ }
+
+ // AS: Figure out the maximum number of bits required to represent any of the
+ // rectangle coordinates. Since minX or minY could be negative, they could
+ // actually require more bits than maxX or maxY.
+ // AS: Christian, can they be negative, or is that a wasted check?
+ // CL: I think so, f.e. for shapes that have the top and/or left edge outside
+ // the page origin
+ sal_uInt8 nBits1
+ = sal::static_int_cast<sal_uInt8>(std::max(getMaxBitsSigned(minX), getMaxBitsSigned(minY)));
+ sal_uInt8 nBits2
+ = sal::static_int_cast<sal_uInt8>(std::max(getMaxBitsSigned(maxX), getMaxBitsSigned(maxY)));
+ sal_uInt8 nBitsMax = std::max(nBits1, nBits2);
+
+ aBits.writeUB(nBitsMax, 5);
+ aBits.writeSB(minX, nBitsMax);
+ aBits.writeSB(maxX, nBitsMax);
+ aBits.writeSB(minY, nBitsMax);
+ aBits.writeSB(maxY, nBitsMax);
+
+ aBits.writeTo(rOut);
+}
+
+void Tag::addMatrix(const ::basegfx::B2DHomMatrix& rMatrix) // #i73264#
+{
+ writeMatrix(*this, rMatrix);
+}
+
+void Tag::writeMatrix(SvStream& rOut, const ::basegfx::B2DHomMatrix& rMatrix) // #i73264#
+{
+ BitStream aBits;
+
+ const bool bHasScale = rMatrix.get(0, 0) != 1.0 || rMatrix.get(1, 1) != 1.0;
+
+ aBits.writeUB(int(bHasScale), 1);
+
+ if (bHasScale)
+ {
+ sal_uInt8 nScaleBits = 31;
+
+ aBits.writeUB(nScaleBits, 5);
+ aBits.writeFB(getFixed(rMatrix.get(0, 0)), nScaleBits); // Scale X
+ aBits.writeFB(getFixed(rMatrix.get(1, 1)), nScaleBits); // Scale Y
+ }
+
+ const bool bHasRotate = rMatrix.get(0, 1) != 0.0 || rMatrix.get(1, 0) != 0.0;
+
+ aBits.writeUB(int(bHasRotate), 1);
+
+ if (bHasRotate)
+ {
+ sal_uInt8 nRotateBits = 31;
+
+ aBits.writeUB(nRotateBits, 5);
+ aBits.writeFB(getFixed(rMatrix.get(0, 1)), nRotateBits); // RotateSkew0
+ aBits.writeFB(getFixed(rMatrix.get(1, 0)), nRotateBits); // RotateSkew1
+ }
+
+ sal_uInt8 nTranslateBits = 16;
+
+ aBits.writeUB(nTranslateBits, 5);
+ aBits.writeSB(static_cast<sal_Int16>(rMatrix.get(0, 2)), nTranslateBits); // Translate X
+ aBits.writeSB(static_cast<sal_Int16>(rMatrix.get(1, 2)), nTranslateBits); // Translate Y
+
+ aBits.writeTo(rOut);
+}
+
+void Tag::addStream(SvStream& rIn) { (*this).WriteStream(rIn); }
+
+Sprite::Sprite(sal_uInt16 nId)
+ : mnId(nId)
+ , mnFrames(0)
+{
+}
+
+Sprite::~Sprite() {}
+
+void Sprite::write(SvStream& out)
+{
+ SvMemoryStream aTmp;
+ for (auto const& tag : maTags)
+ tag->write(aTmp);
+
+ if (!mnFrames)
+ mnFrames = 1;
+
+ aTmp.Seek(0);
+
+ Tag aTag(TAG_DEFINESPRITE);
+ aTag.addUI16(mnId);
+ aTag.addUI16(uInt16_(mnFrames));
+ aTag.addStream(aTmp);
+ aTag.write(out);
+}
+
+void Sprite::addTag(std::unique_ptr<Tag> pNewTag)
+{
+ if (pNewTag->getTagId() == TAG_SHOWFRAME)
+ mnFrames++;
+
+ maTags.push_back(std::move(pNewTag));
+}
+
+sal_uInt32 swf::getFixed(double fValue)
+{
+ sal_Int16 nUpper = static_cast<sal_Int16>(floor(fValue));
+ sal_uInt16 nLower = static_cast<sal_uInt16>((fValue - floor(fValue)) * 0x10000);
+
+ sal_uInt32 temp = static_cast<sal_Int32>(nUpper) << 16;
+ temp |= nLower;
+
+ return temp;
+}
+
+/** constructs a new flash font for the given VCL Font */
+FlashFont::FlashFont(const vcl::Font& rFont, sal_uInt16 nId)
+ : maFont(rFont)
+ , mnNextIndex(0)
+ , mnId(nId)
+{
+}
+
+FlashFont::~FlashFont() {}
+
+/** gets the glyph id for the given character. The glyphs are created on demand */
+sal_uInt16 FlashFont::getGlyph(sal_uInt16 nChar, VirtualDevice* pVDev)
+{
+ // see if we already created a glyph for this character
+ std::map<sal_uInt16, sal_uInt16>::iterator aIter(maGlyphIndex.find(nChar));
+ if (aIter != maGlyphIndex.end())
+ {
+ return aIter->second;
+ }
+
+ // if not, we create one now
+
+ maGlyphIndex[nChar] = mnNextIndex;
+
+ vcl::Font aOldFont(pVDev->GetFont());
+ vcl::Font aNewFont(aOldFont);
+ aNewFont.SetAlignment(ALIGN_BASELINE);
+ pVDev->SetFont(aNewFont);
+ aOldFont.SetOrientation(0_deg10);
+
+ // let the virtual device convert the character to polygons
+ tools::PolyPolygon aPolyPoly;
+ pVDev->GetTextOutline(aPolyPoly, OUString(sal_Unicode(nChar)));
+
+ maGlyphOffsets.push_back(uInt16_(maGlyphData.getOffset()));
+
+ // Number of fill and line index bits set to 1
+ maGlyphData.writeUB(0x11, 8);
+
+ const sal_uInt16 nCount = aPolyPoly.Count();
+ sal_uInt16 i, n;
+ for (i = 0; i < nCount; i++)
+ {
+ tools::Polygon& rPoly = aPolyPoly[i];
+
+ const sal_uInt16 nSize = rPoly.GetSize();
+ if (nSize)
+ {
+ // convert polygon to flash EM_SQUARE (1024x1024)
+ for (n = 0; n < nSize; n++)
+ {
+ Point aPoint(rPoly[n]);
+ aPoint.setX(static_cast<long>((double(aPoint.X()) * 1024.0)
+ / double(aOldFont.GetFontHeight())));
+ aPoint.setY(static_cast<long>((double(aPoint.Y()) * 1024.0)
+ / double(aOldFont.GetFontHeight())));
+ rPoly[n] = aPoint;
+ }
+ Writer::Impl_addPolygon(maGlyphData, rPoly, true);
+ }
+ }
+ Writer::Impl_addEndShapeRecord(maGlyphData);
+
+ maGlyphData.pad();
+
+ pVDev->SetFont(aOldFont);
+
+ return mnNextIndex++;
+}
+
+void FlashFont::write(SvStream& out)
+{
+ Tag aTag(TAG_DEFINEFONT);
+
+ aTag.addUI16(mnId);
+
+ sal_uInt16 nGlyphs = uInt16_(maGlyphOffsets.size());
+ sal_uInt16 nOffset = nGlyphs * sizeof(sal_uInt16);
+
+ for (auto const& glyphOffset : maGlyphOffsets)
+ aTag.addUI16(nOffset + glyphOffset);
+
+ aTag.addBits(maGlyphData);
+
+ aTag.write(out);
+}
+
+/** this c'tor creates a solid fill style */
+FillStyle::FillStyle(const Color& rSolidColor)
+ : meType(solid)
+ , mnBitmapId(0)
+ , maColor(rSolidColor)
+{
+}
+
+/** this c'tor creates a tiled or clipped bitmap fill style */
+FillStyle::FillStyle(sal_uInt16 nBitmapId, bool bClipped,
+ const ::basegfx::B2DHomMatrix& rMatrix) // #i73264#
+ : meType(bClipped ? clipped_bitmap : tiled_bitmap)
+ , maMatrix(rMatrix)
+ , mnBitmapId(nBitmapId)
+{
+}
+
+static FillStyle::FillStyleType Impl_getFillStyleType(const Gradient& rGradient)
+{
+ switch (rGradient.GetStyle())
+ {
+ case css::awt::GradientStyle::GradientStyle_ELLIPTICAL:
+ case css::awt::GradientStyle::GradientStyle_RADIAL:
+ return FillStyle::radial_gradient;
+ case css::awt::GradientStyle::GradientStyle_AXIAL:
+ case css::awt::GradientStyle::GradientStyle_SQUARE:
+ case css::awt::GradientStyle::GradientStyle_RECT:
+ case css::awt::GradientStyle::GradientStyle_LINEAR:
+ default:
+ return FillStyle::linear_gradient;
+ }
+}
+
+/** this c'tor creates a linear or radial gradient fill style */
+FillStyle::FillStyle(const tools::Rectangle& rBoundRect, const Gradient& rGradient)
+ : meType(Impl_getFillStyleType(rGradient))
+ , mnBitmapId(0)
+ , maGradient(rGradient)
+ , maBoundRect(rBoundRect)
+{
+}
+
+void FillStyle::addTo(Tag* pTag) const
+{
+ pTag->addUI8(sal::static_int_cast<sal_uInt8>(meType));
+ switch (meType)
+ {
+ case solid:
+ pTag->addRGBA(maColor);
+ break;
+ case linear_gradient:
+ case radial_gradient:
+ Impl_addGradient(pTag);
+ break;
+ case tiled_bitmap:
+ case clipped_bitmap:
+ pTag->addUI16(mnBitmapId);
+ pTag->addMatrix(maMatrix);
+ break;
+ }
+}
+
+namespace
+{
+struct GradRecord
+{
+ sal_uInt8 mnRatio;
+ Color maColor;
+
+ GradRecord(sal_uInt8 nRatio, const Color& rColor)
+ : mnRatio(nRatio)
+ , maColor(rColor)
+ {
+ }
+};
+}
+
+// TODO: better emulation of our gradients
+void FillStyle::Impl_addGradient(Tag* pTag) const
+{
+ std::vector<struct GradRecord> aGradientRecords;
+ basegfx::B2DHomMatrix m(
+ basegfx::utils::createRotateB2DHomMatrix(toRadians(maGradient.GetAngle() - 900_deg10)));
+
+ switch (maGradient.GetStyle())
+ {
+ case css::awt::GradientStyle::GradientStyle_ELLIPTICAL:
+ case css::awt::GradientStyle::GradientStyle_RADIAL:
+ {
+ aGradientRecords.emplace_back(0x00, maGradient.GetEndColor());
+ aGradientRecords.emplace_back(0xff, maGradient.GetStartColor());
+
+ double tx = (maGradient.GetOfsX() * 32768.0) / 100.0;
+ double ty = (maGradient.GetOfsY() * 32768.0) / 100.0;
+ double scalex = static_cast<double>(maBoundRect.GetWidth()) / 32768.0;
+ double scaley = static_cast<double>(maBoundRect.GetHeight()) / 32768.0;
+
+ m.scale(1.2, 1.2);
+
+ if (scalex > scaley)
+ {
+ double scale_move = scaley / scalex;
+
+ m.translate(tx, scale_move * ty);
+
+ m.scale(scalex, scalex);
+ }
+ else
+ {
+ double scale_move = scalex / scaley;
+
+ m.translate(scale_move * tx, ty);
+
+ m.scale(scaley, scaley);
+ }
+ }
+ break;
+ case css::awt::GradientStyle::GradientStyle_AXIAL:
+ {
+ aGradientRecords.emplace_back(0x00, maGradient.GetEndColor());
+ aGradientRecords.emplace_back(0x80, maGradient.GetStartColor());
+ aGradientRecords.emplace_back(0xff, maGradient.GetEndColor());
+ double scalex = static_cast<double>(maBoundRect.GetWidth()) / 32768.0;
+ double scaley = static_cast<double>(maBoundRect.GetHeight()) / 32768.0;
+ m.translate(32768.0 / 2.0, 32768.0 / 2.0);
+ m.scale(scalex, scaley);
+ }
+ break;
+ case css::awt::GradientStyle::GradientStyle_SQUARE:
+ case css::awt::GradientStyle::GradientStyle_RECT:
+ case css::awt::GradientStyle::GradientStyle_LINEAR:
+ {
+ aGradientRecords.emplace_back(0x00, maGradient.GetStartColor());
+ aGradientRecords.emplace_back(0xff, maGradient.GetEndColor());
+ double scalex = static_cast<double>(maBoundRect.GetWidth()) / 32768.0;
+ double scaley = static_cast<double>(maBoundRect.GetHeight()) / 32768.0;
+
+ m.scale(scalex, scaley);
+
+ m.translate(maBoundRect.GetWidth() / 2.0, maBoundRect.GetHeight() / 2.0);
+ }
+ break;
+ case css::awt::GradientStyle::GradientStyle_MAKE_FIXED_SIZE:
+ break;
+ }
+
+ m.translate(maBoundRect.Left(), maBoundRect.Top());
+
+ pTag->addMatrix(m);
+
+ DBG_ASSERT(aGradientRecords.size() < 8, "Illegal FlashGradient!");
+
+ pTag->addUI8(static_cast<sal_uInt8>(aGradientRecords.size()));
+
+ for (auto const& gradientRecord : aGradientRecords)
+ {
+ pTag->addUI8(gradientRecord.mnRatio);
+ pTag->addRGBA(gradientRecord.maColor);
+ }
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/filter/uiconfig/ui/impswfdialog.ui b/filter/uiconfig/ui/impswfdialog.ui
new file mode 100644
index 000000000000..6617cd7128f2
--- /dev/null
+++ b/filter/uiconfig/ui/impswfdialog.ui
@@ -0,0 +1,277 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- Generated with glade 3.22.1 -->
+<interface domain="flt">
+ <requires lib="gtk+" version="3.18"/>
+ <object class="GtkAdjustment" id="adjustmentquality">
+ <property name="lower">1</property>
+ <property name="upper">100</property>
+ <property name="step_increment">1</property>
+ <property name="page_increment">10</property>
+ </object>
+ <object class="GtkDialog" id="ImpSWFDialog">
+ <property name="can_focus">False</property>
+ <property name="border_width">6</property>
+ <property name="title" translatable="yes" context="impswfdialog|ImpSWFDialog">Flash (SWF) Options</property>
+ <property name="resizable">False</property>
+ <property name="modal">True</property>
+ <property name="default_width">0</property>
+ <property name="default_height">0</property>
+ <property name="type_hint">dialog</property>
+ <child>
+ <placeholder/>
+ </child>
+ <child internal-child="vbox">
+ <object class="GtkBox" id="dialog-vbox1">
+ <property name="can_focus">False</property>
+ <property name="orientation">vertical</property>
+ <property name="spacing">12</property>
+ <child internal-child="action_area">
+ <object class="GtkButtonBox" id="dialog-action_area1">
+ <property name="can_focus">False</property>
+ <property name="margin_top">5</property>
+ <property name="layout_style">end</property>
+ <child>
+ <object class="GtkButton" id="ok">
+ <property name="label">gtk-ok</property>
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="can_default">True</property>
+ <property name="has_default">True</property>
+ <property name="receives_default">True</property>
+ <property name="use_stock">True</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkButton" id="cancel">
+ <property name="label">gtk-cancel</property>
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="receives_default">True</property>
+ <property name="use_stock">True</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkButton" id="help">
+ <property name="label">gtk-help</property>
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="receives_default">True</property>
+ <property name="use_stock">True</property>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">2</property>
+ <property name="secondary">True</property>
+ </packing>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="pack_type">end</property>
+ <property name="position">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkGrid" id="grid1">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="row_spacing">6</property>
+ <child>
+ <object class="GtkGrid" id="grid2">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="column_spacing">12</property>
+ <child>
+ <object class="GtkLabel" id="label1">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="label" translatable="yes" context="impswfdialog|label1">1: min. quality
+100: max. quality</property>
+ <property name="use_underline">True</property>
+ <property name="justify">fill</property>
+ <property name="mnemonic_widget">quality</property>
+ <property name="xalign">0</property>
+ </object>
+ <packing>
+ <property name="left_attach">0</property>
+ <property name="top_attach">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkSpinButton" id="quality">
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="valign">center</property>
+ <property name="activates_default">True</property>
+ <property name="xalign">0.5</property>
+ <property name="input_purpose">digits</property>
+ <property name="adjustment">adjustmentquality</property>
+ <property name="numeric">True</property>
+ </object>
+ <packing>
+ <property name="left_attach">1</property>
+ <property name="top_attach">0</property>
+ </packing>
+ </child>
+ </object>
+ <packing>
+ <property name="left_attach">0</property>
+ <property name="top_attach">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkCheckButton" id="exportall">
+ <property name="label" translatable="yes" context="impswfdialog|exportall">Export _all slides (uncheck to export current slide)</property>
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="receives_default">False</property>
+ <property name="use_underline">True</property>
+ <property name="xalign">0</property>
+ <property name="draw_indicator">True</property>
+ </object>
+ <packing>
+ <property name="left_attach">0</property>
+ <property name="top_attach">1</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkGrid" id="grid3">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="row_spacing">6</property>
+ <child>
+ <object class="GtkCheckButton" id="exportmultiplefiles">
+ <property name="label" translatable="yes" context="impswfdialog|exportmultiplefiles">Export as _multiple files</property>
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="receives_default">False</property>
+ <property name="use_underline">True</property>
+ <property name="xalign">0</property>
+ <property name="draw_indicator">True</property>
+ </object>
+ <packing>
+ <property name="left_attach">0</property>
+ <property name="top_attach">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkGrid" id="grid4">
+ <property name="visible">True</property>
+ <property name="can_focus">False</property>
+ <property name="margin_left">20</property>
+ <property name="row_spacing">6</property>
+ <child>
+ <object class="GtkCheckButton" id="exportbackgrounds">
+ <property name="label" translatable="yes" context="impswfdialog|exportbackgrounds">Export _backgrounds</property>
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="receives_default">False</property>
+ <property name="use_underline">True</property>
+ <property name="xalign">0</property>
+ <property name="draw_indicator">True</property>
+ </object>
+ <packing>
+ <property name="left_attach">0</property>
+ <property name="top_attach">0</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkCheckButton" id="exportbackgroundobjects">
+ <property name="label" translatable="yes" context="impswfdialog|exportbackgroundobjects">Export back_ground objects</property>
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="receives_default">False</property>
+ <property name="use_underline">True</property>
+ <property name="xalign">0</property>
+ <property name="draw_indicator">True</property>
+ </object>
+ <packing>
+ <property name="left_attach">0</property>
+ <property name="top_attach">1</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkCheckButton" id="exportslidecontents">
+ <property name="label" translatable="yes" context="impswfdialog|exportslidecontents">Export _slide contents</property>
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="receives_default">False</property>
+ <property name="use_underline">True</property>
+ <property name="xalign">0</property>
+ <property name="draw_indicator">True</property>
+ </object>
+ <packing>
+ <property name="left_attach">0</property>
+ <property name="top_attach">2</property>
+ </packing>
+ </child>
+ </object>
+ <packing>
+ <property name="left_attach">0</property>
+ <property name="top_attach">1</property>
+ </packing>
+ </child>
+ </object>
+ <packing>
+ <property name="left_attach">0</property>
+ <property name="top_attach">2</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkCheckButton" id="exportsound">
+ <property name="label" translatable="yes" context="impswfdialog|exportsound">Export _Verilogix Slide Annotations</property>
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="receives_default">False</property>
+ <property name="use_underline">True</property>
+ <property name="xalign">0</property>
+ <property name="draw_indicator">True</property>
+ </object>
+ <packing>
+ <property name="left_attach">0</property>
+ <property name="top_attach">3</property>
+ </packing>
+ </child>
+ <child>
+ <object class="GtkCheckButton" id="exportoleasjpeg">
+ <property name="label" translatable="yes" context="impswfdialog|exportoleasjpeg">Export OLE objects as _JPEG images</property>
+ <property name="visible">True</property>
+ <property name="can_focus">True</property>
+ <property name="receives_default">False</property>
+ <property name="use_underline">True</property>
+ <property name="xalign">0</property>
+ <property name="draw_indicator">True</property>
+ </object>
+ <packing>
+ <property name="left_attach">0</property>
+ <property name="top_attach">4</property>
+ </packing>
+ </child>
+ </object>
+ <packing>
+ <property name="expand">False</property>
+ <property name="fill">True</property>
+ <property name="position">1</property>
+ </packing>
+ </child>
+ </object>
+ </child>
+ <action-widgets>
+ <action-widget response="-5">ok</action-widget>
+ <action-widget response="-6">cancel</action-widget>
+ <action-widget response="-11">help</action-widget>
+ </action-widgets>
+ </object>
+</interface>
diff --git a/include/sal/log-areas.dox b/include/sal/log-areas.dox
index 2ee98e7985b7..29ee4ac8253f 100644
--- a/include/sal/log-areas.dox
+++ b/include/sal/log-areas.dox
@@ -228,6 +228,7 @@ certain functionality.
@li @c filter.config
@li @c filter.eps
+@li @c filter.flash
@li @c filter.hwp - Hangul word processor import
@li @c filter.icgm
@li @c filter.ms - escher import/export
diff --git a/include/vcl/gdimetafiletools.hxx b/include/vcl/gdimetafiletools.hxx
index 576ba41f372d..0d04759da428 100644
--- a/include/vcl/gdimetafiletools.hxx
+++ b/include/vcl/gdimetafiletools.hxx
@@ -34,11 +34,11 @@ class GDIMetaFile;
// reimplement these im/exports to use primitives and not metafiles as base
// information.
-void clipMetafileContentAgainstOwnRegions(GDIMetaFile& rSource);
+VCL_DLLPUBLIC void clipMetafileContentAgainstOwnRegions(GDIMetaFile& rSource);
// Allow to check if a Metafile contains clipping or not
-bool usesClipActions(const GDIMetaFile& rSource);
+VCL_DLLPUBLIC bool usesClipActions(const GDIMetaFile& rSource);
// hook to access metafile members in classes of modules above vcl. Currently
// used in MetafilePrimitive2D to be able to access the local Metafile member
diff --git a/include/vcl/gradient.hxx b/include/vcl/gradient.hxx
index fdb8df0b9a4c..b03ad1f3c675 100644
--- a/include/vcl/gradient.hxx
+++ b/include/vcl/gradient.hxx
@@ -81,6 +81,7 @@ public:
void GetBoundRect( const tools::Rectangle& rRect, tools::Rectangle &rBoundRect, Point& rCenter ) const;
void AddGradientActions(tools::Rectangle const& rRect, GDIMetaFile& rMetaFile);
+ void AddGradientActionsConst(tools::Rectangle const& rRect, GDIMetaFile& rMetaFile) const;
Gradient& operator=( const Gradient& rGradient );
Gradient& operator=( Gradient&& rGradient );
diff --git a/scp2/source/graphicfilter/module_graphicfilter.ulf b/scp2/source/graphicfilter/module_graphicfilter.ulf
index 6f4c286325ff..0598718d821a 100644
--- a/scp2/source/graphicfilter/module_graphicfilter.ulf
+++ b/scp2/source/graphicfilter/module_graphicfilter.ulf
@@ -99,3 +99,9 @@ en-US = "SVG Export"
[STR_DESC_MODULE_OPTIONAL_GRFFLT_SVG]
en-US = "SVG Export Filter"
+
+[STR_NAME_MODULE_OPTIONAL_GRFFLT_FLASH]
+en-US = "Macromedia Flash (SWF)"
+
+[STR_DESC_MODULE_OPTIONAL_GRFFLT_FLASH]
+en-US = "Macromedia Flash (SWF) Export Filter"
diff --git a/sfx2/source/dialog/filtergrouping.cxx b/sfx2/source/dialog/filtergrouping.cxx
index b6232e301e43..6a4718053441 100644
--- a/sfx2/source/dialog/filtergrouping.cxx
+++ b/sfx2/source/dialog/filtergrouping.cxx
@@ -969,6 +969,7 @@ namespace sfx2
sal_Int32 nHTMLIndex = -1;
sal_Int32 nXHTMLIndex = -1;
sal_Int32 nPDFIndex = -1;
+ sal_Int32 nFlashIndex = -1;
OUString sUIName;
OUString sExtensions;
std::vector< ExportFilter > aImportantFilterGroup;
@@ -1008,6 +1009,18 @@ namespace sfx2
aImportantFilterGroup.insert( aIter, aExportFilter );
nPDFIndex = 0;
}
+ else if ( nFlashIndex == -1 && sTypeName == "graphic_SWF" )
+ {
+ std::vector< ExportFilter >::iterator aIter = aImportantFilterGroup.begin();
+ if ( nHTMLIndex != -1 )
+ ++aIter;
+ if ( nXHTMLIndex != -1 )
+ ++aIter;
+ if ( nPDFIndex != -1 )
+ ++aIter;
+ aImportantFilterGroup.insert( aIter, aExportFilter );
+ nFlashIndex = 0;
+ }
else
aFilterGroup.push_back( aExportFilter );
}
diff --git a/solenv/inc/mime.types b/solenv/inc/mime.types
index 9248e092f88f..e5afd9376a93 100644
--- a/solenv/inc/mime.types
+++ b/solenv/inc/mime.types
@@ -100,6 +100,7 @@ application/x-rad rad
application/x-rpm rpm spm
application/x-sh sh
application/x-shar shar
+application/x-shockwave-flash swf
application/x-stuffit sit
application/x-sv4cpio sv4cpio
application/x-sv4crc sv4crc
@@ -178,6 +179,8 @@ audio/vnd.rn-realaudio ra
application/smil smi smil
text/vnd.rn-realtext rt
video/vnd.rn-realvideo rv
+image/vnd.rn-realflash rf swf
+application/x-shockwave-flash2-preview rf swf
application/sdp sdp
application/x-sdp sdp
application/vnd.rn-realmedia rm
diff --git a/sysui/desktop/apparmor/program.soffice.bin b/sysui/desktop/apparmor/program.soffice.bin
index 42053db2abef..76d567eb7e8c 100644
--- a/sysui/desktop/apparmor/program.soffice.bin
+++ b/sysui/desktop/apparmor/program.soffice.bin
@@ -65,6 +65,8 @@
#Impress/Draw
@{libreoffice_ext} += [pP][pP][tTsS]{,x,X}
@{libreoffice_ext} += [pP][oO][tT]{,m,M}
+#Flash
+@{libreoffice_ext} += [sS][wW][fF]
#Photoshop
@{libreoffice_ext} += [pP][sS][dD]
diff --git a/vcl/source/gdi/gradient.cxx b/vcl/source/gdi/gradient.cxx
index 75a53a2a93a7..7de2f2b3b2f3 100644
--- a/vcl/source/gdi/gradient.cxx
+++ b/vcl/source/gdi/gradient.cxx
@@ -332,6 +332,37 @@ void Gradient::AddGradientActions(tools::Rectangle const& rRect, GDIMetaFile& rM
rMetaFile.AddAction(new MetaPopAction());
}
+void Gradient::AddGradientActionsConst(tools::Rectangle const& rRect, GDIMetaFile& rMetaFile) const
+{
+ tools::Rectangle aRect(rRect);
+ aRect.Normalize();
+
+ // do nothing if the rectangle is empty
+ if (aRect.IsEmpty())
+ return;
+
+ rMetaFile.AddAction(new MetaPushAction(vcl::PushFlags::ALL));
+ rMetaFile.AddAction(new MetaISectRectClipRegionAction(aRect));
+ rMetaFile.AddAction(new MetaLineColorAction(Color(), false));
+
+ // because we draw with no border line, we have to expand gradient
+ // rect to avoid missing lines on the right and bottom edge
+ aRect.AdjustLeft(-1);
+ aRect.AdjustTop(-1);
+ aRect.AdjustRight(1);
+ aRect.AdjustBottom(1);
+
+ // we can't mutate the stepcount as that would lose us our const qualifier, so we need it to already be set...
+ assert(GetSteps());
+
+ if (GetStyle() == css::awt::GradientStyle_LINEAR || GetStyle() == css::awt::GradientStyle_AXIAL)
+ DrawLinearGradientToMetafile(aRect, rMetaFile);
+ else
+ DrawComplexGradientToMetafile(aRect, rMetaFile);
+
+ rMetaFile.AddAction(new MetaPopAction());
+}
+
tools::Long Gradient::GetMetafileSteps(tools::Rectangle const& rRect) const
{
// calculate step count