From ed5640d8b587fbcfed7dd7967f3de04b37a76f26 Mon Sep 17 00:00:00 2001 From: Daniel Baumann Date: Sun, 7 Apr 2024 11:06:44 +0200 Subject: Adding upstream version 4:7.4.7. Signed-off-by: Daniel Baumann --- filter/source/msfilter/countryid.cxx | 319 ++ filter/source/msfilter/dffpropset.cxx | 1360 +++++ filter/source/msfilter/dffrecordheader.cxx | 43 + filter/source/msfilter/escherex.cxx | 5295 +++++++++++++++++++ filter/source/msfilter/eschesdo.cxx | 1237 +++++ filter/source/msfilter/eschesdo.hxx | 138 + filter/source/msfilter/mscodec.cxx | 642 +++ filter/source/msfilter/msdffimp.cxx | 7610 +++++++++++++++++++++++++++ filter/source/msfilter/msfilter.component | 26 + filter/source/msfilter/msocximex.cxx | 146 + filter/source/msfilter/msoleexp.cxx | 340 ++ filter/source/msfilter/mstoolbar.cxx | 824 +++ filter/source/msfilter/msvbahelper.cxx | 768 +++ filter/source/msfilter/rtfutil.cxx | 401 ++ filter/source/msfilter/svdfppt.cxx | 7832 ++++++++++++++++++++++++++++ filter/source/msfilter/svxmsbas2.cxx | 80 + filter/source/msfilter/util.cxx | 1340 +++++ filter/source/msfilter/viscache.hxx | 52 + 18 files changed, 28453 insertions(+) create mode 100644 filter/source/msfilter/countryid.cxx create mode 100644 filter/source/msfilter/dffpropset.cxx create mode 100644 filter/source/msfilter/dffrecordheader.cxx create mode 100644 filter/source/msfilter/escherex.cxx create mode 100644 filter/source/msfilter/eschesdo.cxx create mode 100644 filter/source/msfilter/eschesdo.hxx create mode 100644 filter/source/msfilter/mscodec.cxx create mode 100644 filter/source/msfilter/msdffimp.cxx create mode 100644 filter/source/msfilter/msfilter.component create mode 100644 filter/source/msfilter/msocximex.cxx create mode 100644 filter/source/msfilter/msoleexp.cxx create mode 100644 filter/source/msfilter/mstoolbar.cxx create mode 100644 filter/source/msfilter/msvbahelper.cxx create mode 100644 filter/source/msfilter/rtfutil.cxx create mode 100644 filter/source/msfilter/svdfppt.cxx create mode 100644 filter/source/msfilter/svxmsbas2.cxx create mode 100644 filter/source/msfilter/util.cxx create mode 100644 filter/source/msfilter/viscache.hxx (limited to 'filter/source/msfilter') diff --git a/filter/source/msfilter/countryid.cxx b/filter/source/msfilter/countryid.cxx new file mode 100644 index 000000000..9ad504ab3 --- /dev/null +++ b/filter/source/msfilter/countryid.cxx @@ -0,0 +1,319 @@ +/* -*- 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 + +#include +#include + + +namespace msfilter { + +// Mapping table ============================================================== + +namespace { + + +/** Table entry for Windows country ID <-> language type conversion. + + The first member is the Windows country ID, as defined in the header. + + The second member contains the corresponding language type for each country + ID. This must be a full language, not only the primary language type. + + The last bool flag defines, if the sub language type should be evaluated to + find the country ID from a language. If not set, all languages map to the + country which contain the given primary language type. + + Example: The language entry (COUNTRY_USA,LANGUAGE_ENGLISH_US,false) maps + the country ID for USA to the language LANGUAGE_ENGLISH_US. The clear sub + language flag causes all english languages LANGUAGE_ENGLISH_*** to map to + this country ID by default. To map the special case LANGUAGE_ENGLISH_EIRE + to the country ID COUNTRY_IRELAND, the sub language flag must be set in the + respective table entry, here (COUNTRY_IRELAND,LANGUAGE_ENGLISH_EIRE,true). + */ +struct CountryEntry +{ + CountryId meCountry; /// Windows country ID. + LanguageType meLanguage; /// Corresponding language type. + bool mbUseSubLang; /// false = Primary only, true = Primary and sub language. +}; + + +/** Table for Windows country ID <-> language type conversion. + + To map the same language to different country IDs, some of the entries + should contain a set sub language flag (see description of CountryEntry). + All table entries with a set flag take priority over the entry with the + same primary language, but cleared sub language flag, regardless of the + position in the table. + + To map different languages to the same country ID, several entries with the + same country ID may be inserted. In this case the conversion to a language + is done with the first found entry (starting from top) containing the given + country ID. + + For now all entries are sorted by country ID, but this is not required. + */ +const CountryEntry pTable[] = +{ + { COUNTRY_USA, LANGUAGE_ENGLISH_US, false }, + { COUNTRY_DOMINICAN_REPUBLIC, LANGUAGE_SPANISH_DOMINICAN_REPUBLIC, true }, + { COUNTRY_JAMAICA, LANGUAGE_ENGLISH_JAMAICA, true }, + { COUNTRY_PUERTO_RICO, LANGUAGE_SPANISH_PUERTO_RICO, true }, + { COUNTRY_TRINIDAD_Y_TOBAGO, LANGUAGE_ENGLISH_TRINIDAD, true }, + { COUNTRY_CANADA, LANGUAGE_ENGLISH_CAN, true }, + { COUNTRY_CANADA, LANGUAGE_FRENCH_CANADIAN, true }, + { COUNTRY_RUSSIA, LANGUAGE_RUSSIAN, false }, + { COUNTRY_KAZAKHSTAN, LANGUAGE_KAZAKH, false }, + { COUNTRY_TATARSTAN, LANGUAGE_TATAR, false }, + { COUNTRY_EGYPT, LANGUAGE_ARABIC_EGYPT, true }, + { COUNTRY_SOUTH_AFRICA, LANGUAGE_AFRIKAANS, false }, + { COUNTRY_SOUTH_AFRICA, LANGUAGE_ENGLISH_SAFRICA, true }, + { COUNTRY_SOUTH_AFRICA, LANGUAGE_TSONGA, false }, + { COUNTRY_SOUTH_AFRICA, LANGUAGE_VENDA, false }, + { COUNTRY_SOUTH_AFRICA, LANGUAGE_XHOSA, false }, + { COUNTRY_SOUTH_AFRICA, LANGUAGE_ZULU, false }, + { COUNTRY_GREECE, LANGUAGE_GREEK, false }, + { COUNTRY_NETHERLANDS, LANGUAGE_DUTCH, false }, + { COUNTRY_NETHERLANDS, LANGUAGE_FRISIAN_NETHERLANDS, false }, + { COUNTRY_BELGIUM, LANGUAGE_DUTCH_BELGIAN, true }, + { COUNTRY_BELGIUM, LANGUAGE_FRENCH_BELGIAN, true }, + { COUNTRY_FRANCE, LANGUAGE_FRENCH, false }, + { COUNTRY_SPAIN, LANGUAGE_SPANISH_MODERN, false }, + { COUNTRY_SPAIN, LANGUAGE_SPANISH_DATED, false }, + { COUNTRY_SPAIN, LANGUAGE_CATALAN, false }, + { COUNTRY_SPAIN, LANGUAGE_BASQUE, false }, + { COUNTRY_SPAIN, LANGUAGE_GALICIAN, false }, + { COUNTRY_HUNGARY, LANGUAGE_HUNGARIAN, false }, + { COUNTRY_ITALY, LANGUAGE_ITALIAN, false }, + { COUNTRY_ROMANIA, LANGUAGE_ROMANIAN, false }, + { COUNTRY_SWITZERLAND, LANGUAGE_GERMAN_SWISS, true }, + { COUNTRY_SWITZERLAND, LANGUAGE_FRENCH_SWISS, true }, + { COUNTRY_SWITZERLAND, LANGUAGE_ITALIAN_SWISS, true }, + { COUNTRY_SWITZERLAND, LANGUAGE_RHAETO_ROMAN, false }, + { COUNTRY_AUSTRIA, LANGUAGE_GERMAN_AUSTRIAN, true }, + { COUNTRY_UNITED_KINGDOM, LANGUAGE_ENGLISH_UK, true }, + { COUNTRY_UNITED_KINGDOM, LANGUAGE_GAELIC_SCOTLAND, true }, + { COUNTRY_UNITED_KINGDOM, LANGUAGE_WELSH, false }, + { COUNTRY_DENMARK, LANGUAGE_DANISH, false }, + { COUNTRY_SWEDEN, LANGUAGE_SWEDISH, false }, + { COUNTRY_SWEDEN, LANGUAGE_SAMI_LAPPISH, false }, + { COUNTRY_NORWAY, LANGUAGE_NORWEGIAN_BOKMAL, false }, + { COUNTRY_POLAND, LANGUAGE_POLISH, false }, + { COUNTRY_GERMANY, LANGUAGE_GERMAN, false }, + { COUNTRY_GERMANY, LANGUAGE_SORBIAN, false }, + { COUNTRY_PERU, LANGUAGE_SPANISH_PERU, true }, + { COUNTRY_MEXICO, LANGUAGE_SPANISH_MEXICAN, true }, + { COUNTRY_ARGENTINA, LANGUAGE_SPANISH_ARGENTINA, true }, + { COUNTRY_BRAZIL, LANGUAGE_PORTUGUESE_BRAZILIAN, true }, + { COUNTRY_CHILE, LANGUAGE_SPANISH_CHILE, true }, + { COUNTRY_COLOMBIA, LANGUAGE_SPANISH_COLOMBIA, true }, + { COUNTRY_VENEZUELA, LANGUAGE_SPANISH_VENEZUELA, true }, + { COUNTRY_MALAYSIA, LANGUAGE_MALAY_MALAYSIA, false }, + { COUNTRY_AUSTRALIA, LANGUAGE_ENGLISH_AUS, true }, + { COUNTRY_INDONESIA, LANGUAGE_INDONESIAN, false }, + { COUNTRY_PHILIPPINES, LANGUAGE_ENGLISH_PHILIPPINES, true }, + { COUNTRY_NEW_ZEALAND, LANGUAGE_MAORI_NEW_ZEALAND, false }, + { COUNTRY_NEW_ZEALAND, LANGUAGE_ENGLISH_NZ, true }, + { COUNTRY_SINGAPORE, LANGUAGE_CHINESE_SINGAPORE, true }, + { COUNTRY_THAILAND, LANGUAGE_THAI, false }, + { COUNTRY_JAPAN, LANGUAGE_JAPANESE, false }, + { COUNTRY_SOUTH_KOREA, LANGUAGE_KOREAN, false }, + { COUNTRY_VIET_NAM, LANGUAGE_VIETNAMESE, false }, + { COUNTRY_PR_CHINA, LANGUAGE_CHINESE_SIMPLIFIED, false }, + { COUNTRY_TIBET, LANGUAGE_TIBETAN, false }, + { COUNTRY_TURKEY, LANGUAGE_TURKISH, false }, + { COUNTRY_INDIA, LANGUAGE_HINDI, false }, + { COUNTRY_INDIA, LANGUAGE_URDU_INDIA, true }, + { COUNTRY_INDIA, LANGUAGE_PUNJABI, false }, + { COUNTRY_INDIA, LANGUAGE_GUJARATI, false }, + { COUNTRY_INDIA, LANGUAGE_ODIA, false }, + { COUNTRY_INDIA, LANGUAGE_TAMIL, false }, + { COUNTRY_INDIA, LANGUAGE_TELUGU, false }, + { COUNTRY_INDIA, LANGUAGE_KANNADA, false }, + { COUNTRY_INDIA, LANGUAGE_MALAYALAM, false }, + { COUNTRY_INDIA, LANGUAGE_ASSAMESE, false }, + { COUNTRY_INDIA, LANGUAGE_MARATHI, false }, + { COUNTRY_INDIA, LANGUAGE_SANSKRIT, false }, + { COUNTRY_INDIA, LANGUAGE_KONKANI, false }, + { COUNTRY_INDIA, LANGUAGE_MANIPURI, false }, + { COUNTRY_INDIA, LANGUAGE_SINDHI, false }, + { COUNTRY_INDIA, LANGUAGE_KASHMIRI, false }, + { COUNTRY_PAKISTAN, LANGUAGE_URDU_PAKISTAN, false }, + { COUNTRY_MYANMAR, LANGUAGE_BURMESE, false }, + { COUNTRY_MOROCCO, LANGUAGE_ARABIC_MOROCCO, true }, + { COUNTRY_ALGERIA, LANGUAGE_ARABIC_ALGERIA, true }, + { COUNTRY_TUNISIA, LANGUAGE_ARABIC_TUNISIA, true }, + { COUNTRY_LIBYA, LANGUAGE_ARABIC_LIBYA, true }, + { COUNTRY_SENEGAL, LANGUAGE_FRENCH_SENEGAL, true }, + { COUNTRY_MALI, LANGUAGE_FRENCH_MALI, true }, + { COUNTRY_COTE_D_IVOIRE, LANGUAGE_FRENCH_COTE_D_IVOIRE, true }, + { COUNTRY_CAMEROON, LANGUAGE_FRENCH_CAMEROON, true }, + { COUNTRY_ZAIRE, LANGUAGE_FRENCH_ZAIRE, true }, + { COUNTRY_RWANDA, LANGUAGE_KINYARWANDA_RWANDA, false }, + { COUNTRY_KENYA, LANGUAGE_SWAHILI, false }, + { COUNTRY_REUNION, LANGUAGE_FRENCH_REUNION, true }, + { COUNTRY_ZIMBABWE, LANGUAGE_ENGLISH_ZIMBABWE, true }, + { COUNTRY_LESOTHO, LANGUAGE_SESOTHO, false }, + { COUNTRY_BOTSWANA, LANGUAGE_TSWANA, false }, + { COUNTRY_FAEROE_ISLANDS, LANGUAGE_FAEROESE, false }, + { COUNTRY_PORTUGAL, LANGUAGE_PORTUGUESE, false }, + { COUNTRY_LUXEMBOURG, LANGUAGE_GERMAN_LUXEMBOURG, true }, + { COUNTRY_LUXEMBOURG, LANGUAGE_FRENCH_LUXEMBOURG, true }, + { COUNTRY_IRELAND, LANGUAGE_ENGLISH_EIRE, true }, + { COUNTRY_IRELAND, LANGUAGE_GAELIC_IRELAND, true }, + { COUNTRY_ICELAND, LANGUAGE_ICELANDIC, false }, + { COUNTRY_ALBANIA, LANGUAGE_ALBANIAN, false }, + { COUNTRY_MALTA, LANGUAGE_MALTESE, false }, + { COUNTRY_FINLAND, LANGUAGE_FINNISH, false }, + { COUNTRY_FINLAND, LANGUAGE_SWEDISH_FINLAND, true }, + { COUNTRY_BULGARIA, LANGUAGE_BULGARIAN, false }, + { COUNTRY_LITHUANIA, LANGUAGE_LITHUANIAN, false }, + { COUNTRY_LATVIA, LANGUAGE_LATVIAN, false }, + { COUNTRY_ESTONIA, LANGUAGE_ESTONIAN, false }, + { COUNTRY_MOLDOVA, LANGUAGE_ROMANIAN_MOLDOVA, true }, + { COUNTRY_MOLDOVA, LANGUAGE_RUSSIAN_MOLDOVA, true }, + { COUNTRY_ARMENIA, LANGUAGE_ARMENIAN, false }, + { COUNTRY_BELARUS, LANGUAGE_BELARUSIAN, false }, + { COUNTRY_MONACO, LANGUAGE_FRENCH_MONACO, true }, + { COUNTRY_UKRAINE, LANGUAGE_UKRAINIAN, false }, + { COUNTRY_SERBIA, LANGUAGE_SERBIAN_LATIN_SAM, false }, + { COUNTRY_CROATIA, LANGUAGE_CROATIAN, true }, // sub type of LANGUAGE_SERBIAN + { COUNTRY_SLOVENIA, LANGUAGE_SLOVENIAN, false }, + { COUNTRY_MACEDONIA, LANGUAGE_MACEDONIAN, false }, + { COUNTRY_CZECH, LANGUAGE_CZECH, false }, + { COUNTRY_SLOVAK, LANGUAGE_SLOVAK, false }, + { COUNTRY_LIECHTENSTEIN, LANGUAGE_GERMAN_LIECHTENSTEIN, true }, + { COUNTRY_BELIZE, LANGUAGE_ENGLISH_BELIZE, true }, + { COUNTRY_GUATEMALA, LANGUAGE_SPANISH_GUATEMALA, true }, + { COUNTRY_EL_SALVADOR, LANGUAGE_SPANISH_EL_SALVADOR, true }, + { COUNTRY_HONDURAS, LANGUAGE_SPANISH_HONDURAS, true }, + { COUNTRY_NICARAGUA, LANGUAGE_SPANISH_NICARAGUA, true }, + { COUNTRY_COSTA_RICA, LANGUAGE_SPANISH_COSTARICA, true }, + { COUNTRY_PANAMA, LANGUAGE_SPANISH_PANAMA, true }, + { COUNTRY_BOLIVIA, LANGUAGE_SPANISH_BOLIVIA, true }, + { COUNTRY_ECUADOR, LANGUAGE_SPANISH_ECUADOR, true }, + { COUNTRY_PARAGUAY, LANGUAGE_SPANISH_PARAGUAY, true }, + { COUNTRY_URUGUAY, LANGUAGE_SPANISH_URUGUAY, true }, + { COUNTRY_BRUNEI_DARUSSALAM, LANGUAGE_MALAY_BRUNEI_DARUSSALAM, true }, + { COUNTRY_HONG_KONG, LANGUAGE_CHINESE_HONGKONG, true }, + { COUNTRY_MACAU, LANGUAGE_CHINESE_MACAU, true }, + { COUNTRY_CAMBODIA, LANGUAGE_KHMER, false }, + { COUNTRY_LAOS, LANGUAGE_LAO, false }, + { COUNTRY_BANGLADESH, LANGUAGE_BENGALI, false }, + { COUNTRY_TAIWAN, LANGUAGE_CHINESE_TRADITIONAL, true }, + { COUNTRY_MALDIVES, LANGUAGE_DHIVEHI, false }, + { COUNTRY_LEBANON, LANGUAGE_ARABIC_LEBANON, true }, + { COUNTRY_JORDAN, LANGUAGE_ARABIC_JORDAN, true }, + { COUNTRY_SYRIA, LANGUAGE_ARABIC_SYRIA, true }, + { COUNTRY_IRAQ, LANGUAGE_ARABIC_IRAQ, true }, + { COUNTRY_KUWAIT, LANGUAGE_ARABIC_KUWAIT, true }, + { COUNTRY_SAUDI_ARABIA, LANGUAGE_ARABIC_SAUDI_ARABIA, true }, + { COUNTRY_YEMEN, LANGUAGE_ARABIC_YEMEN, true }, + { COUNTRY_OMAN, LANGUAGE_ARABIC_OMAN, true }, + { COUNTRY_UAE, LANGUAGE_ARABIC_UAE, true }, + { COUNTRY_ISRAEL, LANGUAGE_HEBREW, false }, + { COUNTRY_BAHRAIN, LANGUAGE_ARABIC_BAHRAIN, true }, + { COUNTRY_QATAR, LANGUAGE_ARABIC_QATAR, true }, + { COUNTRY_MONGOLIA, LANGUAGE_MONGOLIAN_CYRILLIC_MONGOLIA, false }, + { COUNTRY_NEPAL, LANGUAGE_NEPALI, false }, + { COUNTRY_IRAN, LANGUAGE_FARSI, false }, + { COUNTRY_TAJIKISTAN, LANGUAGE_TAJIK, false }, + { COUNTRY_TURKMENISTAN, LANGUAGE_TURKMEN, false }, + { COUNTRY_AZERBAIJAN, LANGUAGE_AZERI_LATIN, false }, + { COUNTRY_GEORGIA, LANGUAGE_GEORGIAN, false }, + { COUNTRY_KYRGYZSTAN, LANGUAGE_KIRGHIZ, false }, + { COUNTRY_UZBEKISTAN, LANGUAGE_UZBEK_LATIN, false } +}; + +const CountryEntry * const pEnd = pTable + SAL_N_ELEMENTS( pTable ); + +/** Predicate comparing a country ID with the member of a CountryEntry. */ +struct CountryEntryPred_Country +{ + CountryId meCountry; + + explicit CountryEntryPred_Country( CountryId eCountry ) : + meCountry( eCountry ) {} + + bool operator()( const CountryEntry& rCmp ) const + { return rCmp.meCountry == meCountry; } +}; + +/** Predicate comparing a language type with the member of a CountryEntry. + + Compares by primary language only, if the passed CountryEntry allows it + (the member mbUseSubLang is cleared), otherwise by full language type. */ +struct CountryEntryPred_Language +{ + LanguageType meLanguage; + + explicit CountryEntryPred_Language( LanguageType eLanguage ) : + meLanguage( eLanguage ) {} + + bool operator()( const CountryEntry& rCmp ) const; +}; + +bool CountryEntryPred_Language::operator()( const CountryEntry& rCmp ) const +{ + // rCmp.mbUseSubLang==true -> compare full language type + // rCmp.mbUseSubLang==false -> compare primary language only + return rCmp.mbUseSubLang ? (meLanguage == rCmp.meLanguage) : + (primary(meLanguage) == primary(rCmp.meLanguage)); +} + +} // namespace + +// Country ID <-> Language type conversion ==================================== + +CountryId ConvertLanguageToCountry( LanguageType eLanguage ) +{ + // country of a found primary language type + CountryId ePrimCountry = COUNTRY_DONTKNOW; + + // find an exact match and a primary-language-only match, in one pass + const CountryEntry* pEntry = pTable; + do + { + pEntry = std::find_if( pEntry, pEnd, CountryEntryPred_Language( eLanguage ) ); + if( pEntry != pEnd ) + { + if( pEntry->mbUseSubLang ) + return pEntry->meCountry; // exact match found -> return + if( ePrimCountry == COUNTRY_DONTKNOW ) + ePrimCountry = pEntry->meCountry; + ++pEntry; // one entry forward for next find_if() call + } + } + while( pEntry != pEnd ); + + return ePrimCountry; +} + +LanguageType ConvertCountryToLanguage( CountryId eCountry ) +{ + // just find the first occurrence of eCountry and return the language type + const CountryEntry* pEntry = std::find_if( pTable, pEnd, CountryEntryPred_Country( eCountry ) ); + return (pEntry != pEnd) ? pEntry->meLanguage : LANGUAGE_DONTKNOW; +} + +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/filter/source/msfilter/dffpropset.cxx b/filter/source/msfilter/dffpropset.cxx new file mode 100644 index 000000000..e6ef672b0 --- /dev/null +++ b/filter/source/msfilter/dffpropset.cxx @@ -0,0 +1,1360 @@ +/* -*- 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 +#include +#include +#include +#include +#include +#include + +const DffPropSetEntry mso_PropSetDefaults[] = { + +// 0 +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, + +// 64 +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { true, false, false, true }, 0, 0 }, // DFF_Prop_LockAgainstGrouping + +// 128 +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { true, false, false, true }, 0, 0x0010 }, // DFF_Prop_FitTextToShape + +// 192 +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { true, false, false, true }, 0, 0 }, // DFF_Prop_gtextFStrikethrough + +//256 +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { true, false, false, true }, 0, 0 }, // DFF_Prop_pictureActive + +// 320 +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { true, false, false, true }, 0, 0x0039 }, // DFF_Prop_fFillOK + +// 384 +{ { false, false, false, false }, 0, 0 }, +{ { true, false, false, true }, 0, 0xffffff }, // DFF_Prop_fillColor +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { true, false, false, true }, 0, 0x001c }, // DFF_Prop_fNoFillHitTest + +// 448 +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { true, false, false, true }, 0, 0x001e }, // DFF_Prop_fNoLineDrawDash + +// 512 +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { true, false, false, true }, 0, 0 }, // DFF_Prop_fshadowObscured + +// 576 +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { true, false, false, true }, 0, 0 }, // DFF_Prop_fPerspective + +// 640 +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { true, false, false, true }, 0, 0x0001 }, // DFF_Prop_fc3DLightFace + +// 704 +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { true, false, false, true }, 0, 0x0016 }, // DFF_Prop_fc3DFillHarsh + +// 768 +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { true, false, false, true }, 0, 0 }, // DFF_Prop_fBackground + +// 832 +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { true, false, false, true }, 0, 0x0010 }, // DFF_Prop_fCalloutLengthSpecified + +// 896 +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { true, false, false, true }, 0, 0x0001 }, // DFF_Prop_fPrint + +// 960 +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 }, +{ { false, false, false, false }, 0, 0 } + +}; + +DffPropSet::DffPropSet() +{ + mpPropSetEntries = reinterpret_cast< DffPropSetEntry* >( new sal_uInt8[ 1024 * sizeof( DffPropSetEntry ) ] ); +} + +DffPropSet::~DffPropSet() +{ + delete[] reinterpret_cast< sal_uInt8* >( mpPropSetEntries ); +} + +void DffPropSet::ReadPropSet( SvStream& rIn, bool bSetUninitializedOnly ) +{ + DffRecordHeader aHd; + ReadDffRecordHeader( rIn, aHd ); + + if ( !bSetUninitializedOnly ) + { + InitializePropSet( aHd.nRecType ); + maOffsets.clear(); + } + + sal_uInt32 nPropCount = aHd.nRecInstance; + + sal_uInt32 nComplexDataFilePos = rIn.Tell() + ( nPropCount * 6 ); + + const size_t nMaxPossibleRecords = rIn.remainingSize() / (sizeof(sal_uInt16) + sizeof(sal_uInt32)); + if (nPropCount > nMaxPossibleRecords) + { + SAL_WARN("filter.ms", "Parsing error: " << nMaxPossibleRecords << + " max possible entries, but " << nPropCount << " claimed, truncating"); + nPropCount = nMaxPossibleRecords; + } + + for (sal_uInt32 nPropNum = 0; nPropNum < nPropCount; ++nPropNum) + { + sal_uInt16 nTmp(0); + sal_uInt32 nContent(0); + rIn.ReadUInt16( nTmp ) + .ReadUInt32( nContent ); + + sal_uInt32 nRecType = nTmp & 0x3fff; + + if ( nRecType > 0x3ff ) + break; + if ( ( nRecType & 0x3f ) == 0x3f ) + { + if ( bSetUninitializedOnly ) + { + sal_uInt32 nCurrentFlags = mpPropSetEntries[ nRecType ].nContent; + sal_uInt32 nMergeFlags = nContent; + + nMergeFlags &= ( nMergeFlags >> 16 ) | 0xffff0000; // clearing low word + nMergeFlags &= ( ( nCurrentFlags & 0xffff0000 ) // remove already hard set + | ( nCurrentFlags >> 16 ) ) ^ 0xffffffff; // attributes from mergeflags + nCurrentFlags &= ( ( nMergeFlags & 0xffff0000 ) // apply zero master bits + | ( nMergeFlags >> 16 ) ) ^ 0xffffffff; + nCurrentFlags |= static_cast(nMergeFlags); // apply filled master bits + mpPropSetEntries[ nRecType ].nContent = nCurrentFlags; + mpPropSetEntries[ nRecType ].nComplexIndexOrFlagsHAttr |= static_cast< sal_uInt16 >( nContent >> 16 ); + } + else + { + // clear flags that have to be cleared + mpPropSetEntries[ nRecType ].nContent &= ( ( nContent >> 16 ) ^ 0xffffffff ); + // set flags that have to be set + mpPropSetEntries[ nRecType ].nContent |= nContent; + mpPropSetEntries[ nRecType ].nComplexIndexOrFlagsHAttr = static_cast< sal_uInt16 >( nContent >> 16 ); + } + } + else + { + bool bSetProperty = !bSetUninitializedOnly || ( !IsProperty( nRecType ) || !IsHardAttribute( nRecType ) ); + + DffPropFlags aPropFlag = { true, false, false, false }; + if ( nTmp & 0x4000 ) + aPropFlag.bBlip = true; + if ( nTmp & 0x8000 ) + aPropFlag.bComplex = true; + if ( aPropFlag.bComplex && nContent && ( nComplexDataFilePos < aHd.GetRecEndFilePos() ) ) + { + // normally nContent is the complete size of the complex property, + // but this is not always true for IMsoArrays ( what the hell is a IMsoArray ? ) + + // I love special treatments :-( + if ( ( nRecType == DFF_Prop_pVertices ) || ( nRecType == DFF_Prop_pSegmentInfo ) + || ( nRecType == DFF_Prop_fillShadeColors ) || ( nRecType == DFF_Prop_lineDashStyle ) + || ( nRecType == DFF_Prop_pWrapPolygonVertices ) || ( nRecType == DFF_Prop_connectorPoints ) + || ( nRecType == DFF_Prop_Handles ) || ( nRecType == DFF_Prop_pFormulas ) + || ( nRecType == DFF_Prop_textRectangles ) ) + { + // now check if the current content size is possible, or 6 bytes too small + sal_uInt32 nOldPos = rIn.Tell(); + + sal_Int16 nNumElem(0), nNumElemReserved(0), nSize(0); + if (checkSeek(rIn, nComplexDataFilePos)) + rIn.ReadInt16(nNumElem).ReadInt16(nNumElemReserved).ReadInt16(nSize); + if (nNumElemReserved >= nNumElem) + { + // the size of these array elements is nowhere defined, + // what if the size is negative ? + // ok, we will make it positive and shift it. + // for -16 this works + if ( nSize < 0 ) + nSize = ( -nSize ) >> 2; + sal_uInt32 nDataSize = static_cast( nSize * nNumElem ); + + // sometimes the content size is 6 bytes too small (array header information is missing ) + if ( nDataSize == nContent ) + nContent += 6; + + // check if array fits into the PropertyContainer + if ( ( nComplexDataFilePos + nContent ) > aHd.GetRecEndFilePos() ) + nContent = 0; + } + else + nContent = 0; + rIn.Seek( nOldPos ); + } + if ( nContent ) + { + if ( bSetProperty ) + { + mpPropSetEntries[ nRecType ].nComplexIndexOrFlagsHAttr = static_cast< sal_uInt16 >( maOffsets.size() ); + maOffsets.push_back( nComplexDataFilePos ); // insert the filepos of this property; + } + nComplexDataFilePos += nContent; // store filepos, that is used for the next complex property + } + else // a complex property needs content + aPropFlag.bSet = false; // otherwise something is wrong + } + if ( bSetProperty ) + { + // tdf#130262: ignore negative values for distances (maybe this list needs to be extended) + // LO does not allow negative values but [MS-ODRAW] does not forbid them + if ( nRecType == DFF_Prop_dxWrapDistLeft || nRecType == DFF_Prop_dxWrapDistRight + || nRecType == DFF_Prop_dyWrapDistTop || nRecType == DFF_Prop_dyWrapDistBottom ) + { + if ( static_cast(nContent) < 0 ) + { + break; + } + } + + mpPropSetEntries[ nRecType ].nContent = nContent; + mpPropSetEntries[ nRecType ].aFlags = aPropFlag; + } + } + } + aHd.SeekToEndOfRecord( rIn ); +} + +SvStream& ReadDffPropSet( SvStream& rIn, DffPropSet& rRec ) +{ + rRec.ReadPropSet( rIn, false ); + return rIn; +} + +SvStream& operator|=( SvStream& rIn, DffPropSet& rRec ) +{ + rRec.ReadPropSet( rIn, true ); + return rIn; +} + +void DffPropSet::InitializePropSet( sal_uInt16 nPropSetType ) const +{ + /* + cmc: + " Boolean properties are grouped in bitfields by property set; note that + the Boolean properties in each property set are contiguous. They are saved + under the property ID of the last Boolean property in the set, and are + placed in the value field in reverse order starting with the last property + in the low bit. " + + e.g. + + fEditedWrap + fBehindDocument + fOnDblClickNotify + fIsButton + fOneD + fHidden + fPrint + + are all part of a group and all are by default false except for fPrint, + which equates to a default bit sequence for the group of 0000001 -> 0x1 + + If at a later stage word sets fBehindDocument away from the default it + will be done by having a property named fPrint whose bitsequence will have + the fBehindDocument bit set. e.g. a DFF_Prop_fPrint with value 0x200020 + has set bit 6 on so as to enable fBehindDocument (as well as disabling + everything else) + */ + if ( nPropSetType == DFF_msofbtOPT ) + { + memcpy( mpPropSetEntries, mso_PropSetDefaults, 0x400 * sizeof( DffPropSetEntry ) ); + } + else + { + memset( mpPropSetEntries, 0, 0x400 * sizeof( DffPropSetEntry ) ); + } +} + +bool DffPropSet::IsHardAttribute( sal_uInt32 nId ) const +{ + bool bRetValue = true; + nId &= 0x3ff; + if ( ( nId & 0x3f ) >= 48 ) // is this a flag id + bRetValue = (mpPropSetEntries[nId | 0x3f].nComplexIndexOrFlagsHAttr + & (1 << (0xf - (nId & 0xf)))) != 0; + else + bRetValue = !mpPropSetEntries[ nId ].aFlags.bSoftAttr; + return bRetValue; +}; + +sal_uInt32 DffPropSet::GetPropertyValue( sal_uInt32 nId, sal_uInt32 nDefault ) const +{ + nId &= 0x3ff; + return ( mpPropSetEntries[ nId ].aFlags.bSet ) ? mpPropSetEntries[ nId ].nContent : nDefault; +}; + +bool DffPropSet::GetPropertyBool( sal_uInt32 nId ) const +{ + sal_uInt32 nBaseId = nId | 31; // base ID to get the sal_uInt32 property value + sal_uInt32 nMask = 1 << (nBaseId - nId); // bit mask of the boolean property + + sal_uInt32 nPropValue = GetPropertyValue( nBaseId, 0 ); + return (nPropValue & nMask) != 0; +} + +OUString DffPropSet::GetPropertyString( sal_uInt32 nId, SvStream& rStrm ) const +{ + sal_uInt64 const nOldPos = rStrm.Tell(); + OUStringBuffer aBuffer; + sal_uInt32 nBufferSize = GetPropertyValue( nId, 0 ); + if( (nBufferSize > 0) && SeekToContent( nId, rStrm ) ) + { + sal_Int32 nStrLen = static_cast< sal_Int32 >( nBufferSize / 2 ); + //clip initial size of buffer to something sane in case of silly length + //strings. If there really is a silly amount of data available it still + //works out ok of course + aBuffer.ensureCapacity(std::min(nStrLen,static_cast(8192))); + for( sal_Int32 nCharIdx = 0; nCharIdx < nStrLen; ++nCharIdx ) + { + sal_uInt16 nChar = 0; + rStrm.ReadUInt16( nChar ); + if( nChar > 0 ) + aBuffer.append( static_cast< sal_Unicode >( nChar ) ); + else + break; + } + } + rStrm.Seek( nOldPos ); + return aBuffer.makeStringAndClear(); +} + +bool DffPropSet::SeekToContent( sal_uInt32 nRecType, SvStream& rStrm ) const +{ + nRecType &= 0x3ff; + if ( mpPropSetEntries[ nRecType ].aFlags.bSet ) + { + if ( mpPropSetEntries[ nRecType ].aFlags.bComplex ) + { + sal_uInt16 nIndex = mpPropSetEntries[ nRecType ].nComplexIndexOrFlagsHAttr; + if ( nIndex < maOffsets.size() ) + { + return checkSeek(rStrm, maOffsets[nIndex]); + } + } + } + return false; +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/filter/source/msfilter/dffrecordheader.cxx b/filter/source/msfilter/dffrecordheader.cxx new file mode 100644 index 000000000..c94bec53b --- /dev/null +++ b/filter/source/msfilter/dffrecordheader.cxx @@ -0,0 +1,43 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include + +bool ReadDffRecordHeader(SvStream& rIn, DffRecordHeader& rRec) +{ + rRec.nFilePos = rIn.Tell(); + sal_uInt16 nTmp(0); + rIn.ReadUInt16(nTmp); + rRec.nImpVerInst = nTmp; + rRec.nRecVer = sal::static_int_cast(nTmp & 0x000F); + rRec.nRecInstance = nTmp >> 4; + rRec.nRecType = 0; + rIn.ReadUInt16(rRec.nRecType); + rRec.nRecLen = 0; + rIn.ReadUInt32(rRec.nRecLen); + + // preserving overflow, optimally we would check + // the record size against the parent header + if (rRec.nRecLen > (SAL_MAX_UINT32 - rRec.nFilePos)) + rIn.SetError(SVSTREAM_FILEFORMAT_ERROR); + + return rIn.good(); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/filter/source/msfilter/escherex.cxx b/filter/source/msfilter/escherex.cxx new file mode 100644 index 000000000..5f84a0df7 --- /dev/null +++ b/filter/source/msfilter/escherex.cxx @@ -0,0 +1,5295 @@ +/* -*- 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 "eschesdo.hxx" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +using namespace css; + +EscherExContainer::EscherExContainer( SvStream& rSt, const sal_uInt16 nRecType, const sal_uInt16 nInstance ) : + rStrm ( rSt ) +{ + rStrm.WriteUInt32( ( 0xf | ( nInstance << 4 ) ) | ( nRecType << 16 ) ).WriteUInt32( 0 ); + nContPos = rStrm.Tell(); +} +EscherExContainer::~EscherExContainer() +{ + sal_uInt32 nPos = rStrm.Tell(); + sal_uInt32 nSize= nPos - nContPos; + if ( nSize ) + { + rStrm.Seek( nContPos - 4 ); + rStrm.WriteUInt32( nSize ); + rStrm.Seek( nPos ); + } +} + +EscherExAtom::EscherExAtom( SvStream& rSt, const sal_uInt16 nRecType, const sal_uInt16 nInstance, const sal_uInt8 nVersion ) : + rStrm ( rSt ) +{ + rStrm.WriteUInt32( ( nVersion | ( nInstance << 4 ) ) | ( nRecType << 16 ) ).WriteUInt32( 0 ); + nContPos = rStrm.Tell(); +} +EscherExAtom::~EscherExAtom() +{ + sal_uInt32 nPos = rStrm.Tell(); + sal_uInt32 nSize= nPos - nContPos; + if ( nSize ) + { + rStrm.Seek( nContPos - 4 ); + rStrm.WriteUInt32( nSize ); + rStrm.Seek( nPos ); + } +} + +EscherExClientRecord_Base::~EscherExClientRecord_Base() +{ +} + +EscherExClientAnchor_Base::~EscherExClientAnchor_Base() +{ +} + +EscherPropertyContainer::EscherPropertyContainer( + EscherGraphicProvider * pGraphProv, SvStream * pPiOutStrm, + tools::Rectangle * pBoundRect): + pGraphicProvider(pGraphProv), + pPicOutStrm(pPiOutStrm), + pShapeBoundRect(pBoundRect), + nCountCount(0), + nCountSize(0), + bHasComplexData(false) +{ + pSortStruct.reserve(64); +} + +EscherPropertyContainer::EscherPropertyContainer() + : EscherPropertyContainer(nullptr, nullptr, nullptr) +{} + +EscherPropertyContainer::EscherPropertyContainer( + EscherGraphicProvider& rGraphProv, + SvStream* pPiOutStrm, + tools::Rectangle& rBoundRect ) : + EscherPropertyContainer(&rGraphProv, pPiOutStrm, &rBoundRect) +{} + +EscherPropertyContainer::~EscherPropertyContainer() +{ +}; + +void EscherPropertyContainer::AddOpt( + sal_uInt16 nPropID, + bool bBlib, + sal_uInt32 nSizeReduction, + SvMemoryStream& rStream) +{ + sal_uInt8 const* pBuf(static_cast(rStream.GetData())); + const sal_uInt64 nSize(rStream.GetSize()); + std::vector aBuf; + aBuf.reserve(nSize); + + for(sal_uInt64 a(0); a < nSize; a++) + { + aBuf.push_back(*pBuf++); + } + + sal_uInt32 nPropValue(static_cast(nSize)); + + if(0 != nSizeReduction && nPropValue > nSizeReduction) + { + nPropValue -= nSizeReduction; + } + + AddOpt(nPropID, bBlib, nPropValue, aBuf); +} + +void EscherPropertyContainer::AddOpt( + sal_uInt16 nPropID, + sal_uInt32 nPropValue, + bool bBlib) +{ + AddOpt(nPropID, bBlib, nPropValue, std::vector()); +} + +void EscherPropertyContainer::AddOpt( + sal_uInt16 nPropID, + const OUString& rString) +{ + std::vector aBuf; + aBuf.reserve(rString.getLength() * 2 + 2); + + for(sal_Int32 i(0); i < rString.getLength(); i++) + { + const sal_Unicode nUnicode(rString[i]); + aBuf.push_back(static_cast(nUnicode)); + aBuf.push_back(static_cast(nUnicode >> 8)); + } + + aBuf.push_back(0); + aBuf.push_back(0); + + AddOpt(nPropID, true, aBuf.size(), aBuf); +} + +void EscherPropertyContainer::AddOpt( + sal_uInt16 nPropID, + bool bBlib, + sal_uInt32 nPropValue, + const std::vector& rProp) +{ + if ( bBlib ) // bBlib is only valid when fComplex = 0 + nPropID |= 0x4000; + if ( !rProp.empty() ) + nPropID |= 0x8000; // fComplex = sal_True; + + for( size_t i = 0; i < pSortStruct.size(); i++ ) + { + if ( ( pSortStruct[ i ].nPropId &~0xc000 ) == ( nPropID &~0xc000 ) ) // check, whether the Property only gets replaced + { + pSortStruct[ i ].nPropId = nPropID; + if ( !pSortStruct[ i ].nProp.empty() ) + { + nCountSize -= pSortStruct[ i ].nProp.size(); + } + pSortStruct[ i ].nProp = rProp; + pSortStruct[ i ].nPropValue = nPropValue; + if ( !rProp.empty() ) + nCountSize += rProp.size(); + return; + } + } + nCountCount++; + nCountSize += 6; + pSortStruct.emplace_back(); + pSortStruct.back().nPropId = nPropID; // insert property + pSortStruct.back().nProp = rProp; + pSortStruct.back().nPropValue = nPropValue; + + if ( !rProp.empty() ) + { + nCountSize += rProp.size(); + bHasComplexData = true; + } +} + +bool EscherPropertyContainer::GetOpt( sal_uInt16 nPropId, sal_uInt32& rPropValue ) const +{ + EscherPropSortStruct aPropStruct; + + if ( GetOpt( nPropId, aPropStruct ) ) + { + rPropValue = aPropStruct.nPropValue; + return true; + } + return false; +} + +bool EscherPropertyContainer::GetOpt( sal_uInt16 nPropId, EscherPropSortStruct& rPropValue ) const +{ + for( size_t i = 0; i < pSortStruct.size(); i++ ) + { + if ( ( pSortStruct[ i ].nPropId &~0xc000 ) == ( nPropId &~0xc000 ) ) + { + rPropValue = pSortStruct[ i ]; + return true; + } + } + return false; +} + +const EscherProperties & EscherPropertyContainer::GetOpts() const +{ + return pSortStruct; +} + +extern "C" { + +static int EscherPropSortFunc( const void* p1, const void* p2 ) +{ + sal_Int16 nID1 = static_cast(p1)->nPropId &~0xc000; + sal_Int16 nID2 = static_cast(p2)->nPropId &~0xc000; + + if( nID1 < nID2 ) + return -1; + else if( nID1 > nID2 ) + return 1; + else + return 0; +} + +} + +void EscherPropertyContainer::Commit( SvStream& rSt, sal_uInt16 nVersion, sal_uInt16 nRecType ) +{ + rSt.WriteUInt16( ( nCountCount << 4 ) | ( nVersion & 0xf ) ).WriteUInt16( nRecType ).WriteUInt32( nCountSize ); + if ( pSortStruct.empty() ) + return; + + qsort( pSortStruct.data(), pSortStruct.size(), sizeof( EscherPropSortStruct ), EscherPropSortFunc ); + + for ( size_t i = 0; i < pSortStruct.size(); i++ ) + { + sal_uInt32 nPropValue = pSortStruct[ i ].nPropValue; + sal_uInt16 nPropId = pSortStruct[ i ].nPropId; + + rSt.WriteUInt16( nPropId ) + .WriteUInt32( nPropValue ); + } + if ( bHasComplexData ) + { + for ( size_t i = 0; i < pSortStruct.size(); i++ ) + { + if ( !pSortStruct[ i ].nProp.empty() ) + rSt.WriteBytes( + pSortStruct[i].nProp.data(), + pSortStruct[i].nProp.size()); + } + } +} + +bool EscherPropertyContainer::IsFontWork() const +{ + sal_uInt32 nTextPathFlags = 0; + GetOpt( DFF_Prop_gtextFStrikethrough, nTextPathFlags ); + return ( nTextPathFlags & 0x4000 ) != 0; +} + +sal_uInt32 EscherPropertyContainer::ImplGetColor( const sal_uInt32 nSOColor, bool bSwap ) +{ + if ( bSwap ) + { + sal_uInt32 nColor = nSOColor & 0xff00; // green + nColor |= static_cast(nSOColor) << 16; // red + nColor |= static_cast( nSOColor >> 16 ); // blue + return nColor; + } + else + return nSOColor & 0xffffff; +} + +sal_uInt32 EscherPropertyContainer::GetGradientColor( + const awt::Gradient* pGradient, + sal_uInt32 nStartColor ) +{ + sal_uInt32 nIntensity = 100; + Color aColor; + + if ( pGradient ) + { + if ( nStartColor & 1 ) + { + nIntensity = pGradient->StartIntensity; + aColor = Color(ColorTransparency, pGradient->StartColor); + } + else + { + nIntensity = pGradient->EndIntensity; + aColor = Color(ColorTransparency, pGradient->EndColor); + } + } + sal_uInt32 nRed = ( aColor.GetRed() * nIntensity ) / 100; + sal_uInt32 nGreen = ( ( aColor.GetGreen() * nIntensity ) / 100 ) << 8; + sal_uInt32 nBlue = ( ( aColor.GetBlue() * nIntensity ) / 100 ) << 16; + return nRed | nGreen | nBlue; +} + +void EscherPropertyContainer::CreateGradientProperties( + const awt::Gradient & rGradient ) +{ + sal_uInt32 nFillType = ESCHER_FillShadeScale; + sal_uInt32 nAngle = 0; + sal_uInt32 nFillFocus = 0; + sal_uInt32 nFillLR = 0; + sal_uInt32 nFillTB = 0; + sal_uInt32 nFirstColor = 0; + bool bWriteFillTo = false; + + switch ( rGradient.Style ) + { + case awt::GradientStyle_LINEAR : + case awt::GradientStyle_AXIAL : + { + nFillType = ESCHER_FillShadeScale; + nAngle = (rGradient.Angle * 0x10000) / 10; + nFillFocus = (sal::static_int_cast(rGradient.Style) == + sal::static_int_cast(GradientStyle::Linear)) ? 0 : 50; + } + break; + case awt::GradientStyle_RADIAL : + case awt::GradientStyle_ELLIPTICAL : + case awt::GradientStyle_SQUARE : + case awt::GradientStyle_RECT : + { + nFillLR = (rGradient.XOffset * 0x10000) / 100; + nFillTB = (rGradient.YOffset * 0x10000) / 100; + if ( ((nFillLR > 0) && (nFillLR < 0x10000)) || ((nFillTB > 0) && (nFillTB < 0x10000)) ) + nFillType = ESCHER_FillShadeShape; + else + nFillType = ESCHER_FillShadeCenter; + nFirstColor = 1; + bWriteFillTo = true; + } + break; + case awt::GradientStyle::GradientStyle_MAKE_FIXED_SIZE : break; + } + AddOpt( ESCHER_Prop_fillType, nFillType ); + AddOpt( ESCHER_Prop_fillAngle, nAngle ); + AddOpt( ESCHER_Prop_fillColor, GetGradientColor( &rGradient, nFirstColor ) ); + AddOpt( ESCHER_Prop_fillBackColor, GetGradientColor( &rGradient, nFirstColor ^ 1 ) ); + AddOpt( ESCHER_Prop_fillFocus, nFillFocus ); + if ( bWriteFillTo ) + { + AddOpt( ESCHER_Prop_fillToLeft, nFillLR ); + AddOpt( ESCHER_Prop_fillToTop, nFillTB ); + AddOpt( ESCHER_Prop_fillToRight, nFillLR ); + AddOpt( ESCHER_Prop_fillToBottom, nFillTB ); + } +} + +void EscherPropertyContainer::CreateGradientProperties( + const uno::Reference & rXPropSet , bool bTransparentGradient) +{ + uno::Any aAny; + awt::Gradient const * pGradient = nullptr; + + sal_uInt32 nFillType = ESCHER_FillShadeScale; + sal_Int32 nAngle = 0; + sal_uInt32 nFillFocus = 0; + sal_uInt32 nFillLR = 0; + sal_uInt32 nFillTB = 0; + sal_uInt32 nFirstColor = 0;// like the control var nChgColors in import logic + bool bWriteFillTo = false; + + // Transparency gradient: Means the third setting in transparency page is set + if (bTransparentGradient && EscherPropertyValueHelper::GetPropertyValue( + aAny, rXPropSet, "FillTransparenceGradient" ) ) + { + pGradient = o3tl::doAccess(aAny); + + uno::Any aAnyTemp; + if ( EscherPropertyValueHelper::GetPropertyValue( + aAnyTemp, rXPropSet, "FillStyle" ) ) + { + drawing::FillStyle eFS; + if ( ! ( aAnyTemp >>= eFS ) ) + eFS = drawing::FillStyle_SOLID; + // solid and transparency + if ( eFS == drawing::FillStyle_SOLID) + { + if ( EscherPropertyValueHelper::GetPropertyValue( + aAnyTemp, rXPropSet, "FillColor" ) ) + { + const_cast(pGradient)->StartColor = ImplGetColor( *o3tl::doAccess(aAnyTemp), false ); + const_cast(pGradient)->EndColor = ImplGetColor( *o3tl::doAccess(aAnyTemp), false ); + } + } + // gradient and transparency. + else if( eFS == drawing::FillStyle_GRADIENT ) + { + if ( EscherPropertyValueHelper::GetPropertyValue( + aAny, rXPropSet, "FillGradient" ) ) + pGradient = o3tl::doAccess(aAny); + } + } + + } + // Not transparency gradient + else if ( EscherPropertyValueHelper::GetPropertyValue( + aAny, rXPropSet, "FillGradient" ) ) + { + pGradient = o3tl::doAccess(aAny); + } + + if ( pGradient ) + { + switch ( pGradient->Style ) + { + case awt::GradientStyle_LINEAR : + case awt::GradientStyle_AXIAL : + { + nFillType = ESCHER_FillShadeScale; + nAngle = pGradient->Angle; + while ( nAngle > 0 ) nAngle -= 3600; + while ( nAngle <= -3600 ) nAngle += 3600; + // Value of the real number = Integral + (Fractional / 65536.0) + nAngle = ( nAngle * 0x10000) / 10; + + nFillFocus = (pGradient->Style == awt::GradientStyle_LINEAR) ? + ( pGradient->XOffset + pGradient->YOffset )/2 : -50; + if( !nFillFocus ) + nFirstColor=nFirstColor ^ 1; + if ( !nAngle ) + nFirstColor=nFirstColor ^ 1; + } + break; + case awt::GradientStyle_RADIAL : + case awt::GradientStyle_ELLIPTICAL : + case awt::GradientStyle_SQUARE : + case awt::GradientStyle_RECT : + { + // according to the import logic and rect type fill** value + nFillLR = (pGradient->XOffset * 0x10000) / 100; + nFillTB = (pGradient->YOffset * 0x10000) / 100; + if ( ((nFillLR > 0) && (nFillLR < 0x10000)) || ((nFillTB > 0) && (nFillTB < 0x10000)) ) + nFillType = ESCHER_FillShadeShape; + else + nFillType = ESCHER_FillShadeCenter; + nFirstColor = 1; + bWriteFillTo = true; + } + break; + default: break; + } + } + + AddOpt( ESCHER_Prop_fillType, nFillType ); + AddOpt( ESCHER_Prop_fillAngle, nAngle ); + AddOpt( ESCHER_Prop_fillColor, GetGradientColor( pGradient, nFirstColor ) ); + AddOpt( ESCHER_Prop_fillBackColor, GetGradientColor( pGradient, nFirstColor ^ 1 ) ); + AddOpt( ESCHER_Prop_fillFocus, nFillFocus ); + if ( bWriteFillTo ) + { + // according to rect type fillTo** value + if(nFillLR) + { + AddOpt( ESCHER_Prop_fillToLeft, nFillLR ); + AddOpt( ESCHER_Prop_fillToRight, nFillLR ); + } + if(nFillTB) + { + AddOpt( ESCHER_Prop_fillToTop, nFillTB ); + AddOpt( ESCHER_Prop_fillToBottom, nFillTB ); + } + } + + // Transparency gradient + if (bTransparentGradient && EscherPropertyValueHelper::GetPropertyValue( + aAny, rXPropSet, "FillTransparenceGradient" ) ) + { + pGradient = o3tl::doAccess(aAny); + if ( pGradient ) + { + sal_uInt32 nBlue = GetGradientColor( pGradient, nFirstColor ) >> 16; + AddOpt( ESCHER_Prop_fillOpacity,( ( 100 - ( nBlue * 100 / 255 ) ) << 16 ) / 100 ); + nBlue = GetGradientColor( pGradient, nFirstColor ^ 1 ) >>16 ; + AddOpt( ESCHER_Prop_fillBackOpacity,( ( 100 - ( nBlue * 100 / 255 ) ) << 16 )/ 100 ); + } + } +} + +void EscherPropertyContainer::CreateFillProperties( + const uno::Reference & rXPropSet, + bool bEdge , const uno::Reference & rXShape ) +{ + if ( rXShape.is() ) + { + SdrObject* pObj = SdrObject::getSdrObjectFromXShape(rXShape); + if ( pObj ) + { + const SfxItemSet& aAttr( pObj->GetMergedItemSet() ); + // transparency with gradient. Means the third setting in transparency page is set + bool bTransparentGradient = ( aAttr.GetItemState( XATTR_FILLFLOATTRANSPARENCE ) == SfxItemState::SET ) && + aAttr.Get( XATTR_FILLFLOATTRANSPARENCE ).IsEnabled(); + CreateFillProperties( rXPropSet, bEdge, bTransparentGradient ); + } + } +} + +void EscherPropertyContainer::CreateFillProperties( + const uno::Reference & rXPropSet, + bool bEdge , bool bTransparentGradient) + +{ + uno::Any aAny; + AddOpt( ESCHER_Prop_WrapText, ESCHER_WrapNone ); + AddOpt( ESCHER_Prop_AnchorText, ESCHER_AnchorMiddle ); + static const OUStringLiteral aPropName( u"FillStyle" ); + + if ( EscherPropertyValueHelper::GetPropertyValue( + aAny, rXPropSet, aPropName ) ) + { + drawing::FillStyle eFS; + if ( ! ( aAny >>= eFS ) ) + eFS = drawing::FillStyle_SOLID; + sal_uInt32 nFillBackColor = 0; + switch( eFS ) + { + case drawing::FillStyle_GRADIENT : + { + CreateGradientProperties( rXPropSet , bTransparentGradient ); + AddOpt( ESCHER_Prop_fNoFillHitTest, 0x140014 ); + } + break; + + case drawing::FillStyle_BITMAP : + { + CreateGraphicProperties(rXPropSet, "FillBitmap", true); + AddOpt( ESCHER_Prop_fNoFillHitTest, 0x140014 ); + AddOpt( ESCHER_Prop_fillBackColor, nFillBackColor ); + } + break; + case drawing::FillStyle_HATCH : + { + CreateGraphicProperties( rXPropSet, "FillHatch", true ); + } + break; + case drawing::FillStyle_SOLID : + default: + { + if ( bTransparentGradient ) + CreateGradientProperties( rXPropSet , bTransparentGradient ); + else + { + beans::PropertyState ePropState = EscherPropertyValueHelper::GetPropertyState( + rXPropSet, aPropName ); + if ( ePropState == beans::PropertyState_DIRECT_VALUE ) + AddOpt( ESCHER_Prop_fillType, ESCHER_FillSolid ); + + if ( EscherPropertyValueHelper::GetPropertyValue( + aAny, rXPropSet, "FillColor" ) ) + { + sal_uInt32 nFillColor = ImplGetColor( *o3tl::doAccess(aAny) ); + nFillBackColor = nFillColor ^ 0xffffff; + AddOpt( ESCHER_Prop_fillColor, nFillColor ); + } + AddOpt( ESCHER_Prop_fNoFillHitTest, 0x100010 ); + AddOpt( ESCHER_Prop_fillBackColor, nFillBackColor ); + } + break; + } + case drawing::FillStyle_NONE : + AddOpt( ESCHER_Prop_fNoFillHitTest, 0x100000 ); + break; + } + if ( eFS != drawing::FillStyle_NONE ) + { + sal_uInt16 nTransparency = ( EscherPropertyValueHelper::GetPropertyValue( + aAny, rXPropSet, "FillTransparence", true ) ) + ? *o3tl::doAccess(aAny) : 0; + if ( nTransparency ) + AddOpt( ESCHER_Prop_fillOpacity, ( ( 100 - nTransparency ) << 16 ) / 100 ); + } + } + CreateLineProperties( rXPropSet, bEdge ); +} + +void EscherPropertyContainer::CreateTextProperties( + const uno::Reference< beans::XPropertySet > & rXPropSet, sal_uInt32 nTextId, + const bool bIsCustomShape, const bool bIsTextFrame ) +{ + uno::Any aAny; + text::WritingMode eWM( text::WritingMode_LR_TB ); + drawing::TextVerticalAdjust eVA( drawing::TextVerticalAdjust_TOP ); + drawing::TextHorizontalAdjust eHA( drawing::TextHorizontalAdjust_LEFT ); + + sal_Int32 nLeft ( 0 ); + sal_Int32 nTop ( 0 ); + sal_Int32 nRight ( 0 ); + sal_Int32 nBottom ( 0 ); + + // used with normal shapes: + bool bAutoGrowWidth ( false ); + const bool bAutoGrowHeight ( false ); //#ii63936 not setting autogrowheight, because minframeheight would be ignored + // used with ashapes: + bool bWordWrap ( false ); + bool bAutoGrowSize ( false ); + + if ( EscherPropertyValueHelper::GetPropertyValue( aAny, rXPropSet, "TextWritingMode", true ) ) + aAny >>= eWM; + if ( EscherPropertyValueHelper::GetPropertyValue( aAny, rXPropSet, "TextVerticalAdjust", true ) ) + aAny >>= eVA; + if ( EscherPropertyValueHelper::GetPropertyValue( aAny, rXPropSet, "TextHorizontalAdjust", true ) ) + aAny >>= eHA; + if ( bIsCustomShape ) + { + if ( EscherPropertyValueHelper::GetPropertyValue( aAny, rXPropSet, "TextWordWrap" ) ) + aAny >>= bWordWrap; + if ( EscherPropertyValueHelper::GetPropertyValue( aAny, rXPropSet, "TextAutoGrowHeight", true ) ) + aAny >>= bAutoGrowSize; + } + else if ( bIsTextFrame ) + { + if ( EscherPropertyValueHelper::GetPropertyValue( aAny, rXPropSet, "TextAutoGrowWidth", true ) ) + aAny >>= bAutoGrowWidth; + +// i63936 not setting autogrowheight, because otherwise +// the minframeheight of the text will be ignored +// +// if ( EscherPropertyValueHelper::GetPropertyValue( aAny, rXPropSet, "TextAutoGrowHeight", sal_True ) ) +// aAny >>= bAutoGrowHeight; + } + if ( EscherPropertyValueHelper::GetPropertyValue( aAny, rXPropSet, "TextLeftDistance" ) ) + aAny >>= nLeft; + if ( EscherPropertyValueHelper::GetPropertyValue( aAny, rXPropSet, "TextUpperDistance" ) ) + aAny >>= nTop; + if ( EscherPropertyValueHelper::GetPropertyValue( aAny, rXPropSet, "TextRightDistance" ) ) + aAny >>= nRight; + if ( EscherPropertyValueHelper::GetPropertyValue( aAny, rXPropSet, "TextLowerDistance" ) ) + aAny >>= nBottom; + + ESCHER_AnchorText eAnchor = ESCHER_AnchorTop; + ESCHER_WrapMode eWrapMode = ESCHER_WrapSquare; + sal_uInt32 nTextAttr = 0x40004; // rotate text with shape + + if ( eWM == text::WritingMode_TB_RL ) + { // vertical writing + switch ( eHA ) + { + case drawing::TextHorizontalAdjust_LEFT : + eAnchor = ESCHER_AnchorBottom; + break; + case drawing::TextHorizontalAdjust_CENTER : + eAnchor = ESCHER_AnchorMiddle; + break; + default : + case drawing::TextHorizontalAdjust_BLOCK : + case drawing::TextHorizontalAdjust_RIGHT : + eAnchor = ESCHER_AnchorTop; + break; + } + if ( eVA == drawing::TextVerticalAdjust_CENTER ) + { + switch ( eAnchor ) + { + case ESCHER_AnchorMiddle : + eAnchor = ESCHER_AnchorMiddleCentered; + break; + case ESCHER_AnchorBottom : + eAnchor = ESCHER_AnchorBottomCentered; + break; + default : + case ESCHER_AnchorTop : + eAnchor = ESCHER_AnchorTopCentered; + break; + } + } + if ( bIsCustomShape ) + { + if ( bWordWrap ) + eWrapMode = ESCHER_WrapSquare; + else + eWrapMode = ESCHER_WrapNone; + if ( bAutoGrowSize ) + nTextAttr |= 0x20002; + } + else + { + if ( bAutoGrowHeight ) + eWrapMode = ESCHER_WrapNone; + if ( bAutoGrowWidth ) + nTextAttr |= 0x20002; + } + + AddOpt( ESCHER_Prop_txflTextFlow, ESCHER_txflTtoBA ); // rotate text within shape by 90 + } + else + { // normal from left to right + switch ( eVA ) + { + case drawing::TextVerticalAdjust_CENTER : + eAnchor = ESCHER_AnchorMiddle; + break; + + case drawing::TextVerticalAdjust_BOTTOM : + eAnchor = ESCHER_AnchorBottom; + break; + + default : + case drawing::TextVerticalAdjust_TOP : + eAnchor = ESCHER_AnchorTop; + break; + } + if ( eHA == drawing::TextHorizontalAdjust_CENTER ) + { + switch( eAnchor ) + { + case ESCHER_AnchorMiddle : + eAnchor = ESCHER_AnchorMiddleCentered; + break; + case ESCHER_AnchorBottom : + eAnchor = ESCHER_AnchorBottomCentered; + break; + case ESCHER_AnchorTop : + eAnchor = ESCHER_AnchorTopCentered; + break; + default: break; + } + } + if ( bIsCustomShape ) + { + if ( bWordWrap ) + eWrapMode = ESCHER_WrapSquare; + else + eWrapMode = ESCHER_WrapNone; + if ( bAutoGrowSize ) + nTextAttr |= 0x20002; + } + else + { + if ( bAutoGrowWidth ) + eWrapMode = ESCHER_WrapNone; + if ( bAutoGrowHeight ) + nTextAttr |= 0x20002; + } + } + AddOpt( ESCHER_Prop_dxTextLeft, nLeft * 360 ); + AddOpt( ESCHER_Prop_dxTextRight, nRight * 360 ); + AddOpt( ESCHER_Prop_dyTextTop, nTop * 360 ); + AddOpt( ESCHER_Prop_dyTextBottom, nBottom * 360 ); + + AddOpt( ESCHER_Prop_WrapText, eWrapMode ); + AddOpt( ESCHER_Prop_AnchorText, eAnchor ); + AddOpt( ESCHER_Prop_FitTextToShape, nTextAttr ); + + if ( nTextId ) + AddOpt( ESCHER_Prop_lTxid, nTextId ); + + // n#404221: In case of rotation we need to write the txtflTextFlow + // attribute too. + // fdo#58204: not custom shapes (TODO: other cases when it doesn't work?) + if (!bIsTextFrame || bIsCustomShape) + return; + + sal_uInt16 nAngle = EscherPropertyValueHelper::GetPropertyValue( + aAny, rXPropSet, "RotateAngle", true ) ? + static_cast( ( *o3tl::doAccess(aAny) ) + 5 ) / 10 : 0; + if (nAngle==900) + { + AddOpt( ESCHER_Prop_txflTextFlow, ESCHER_txflBtoT ); + } + if (nAngle==2700) + { + AddOpt( ESCHER_Prop_txflTextFlow, ESCHER_txflTtoBA ); + } +} + +bool EscherPropertyContainer::GetLineArrow( const bool bLineStart, + const uno::Reference & rXPropSet, + ESCHER_LineEnd& reLineEnd, sal_Int32& rnArrowLength, sal_Int32& rnArrowWidth ) +{ + const OUString sLine ( bLineStart ? OUString("LineStart") : OUString("LineEnd") ); + const OUString sLineName ( bLineStart ? OUString("LineStartName") : OUString("LineEndName") ); + + bool bIsArrow = false; + + uno::Any aAny; + if ( EscherPropertyValueHelper::GetPropertyValue( aAny, rXPropSet, sLine ) ) + { + tools::PolyPolygon aPolyPoly( EscherPropertyContainer::GetPolyPolygon( aAny ) ); + if ( aPolyPoly.Count() && aPolyPoly[ 0 ].GetSize() ) + { + bIsArrow = true; + + reLineEnd = ESCHER_LineArrowEnd; + rnArrowLength = 1; + rnArrowWidth = 1; + + if ( EscherPropertyValueHelper::GetPropertyValue( aAny, rXPropSet, sLineName ) ) + { + OUString aArrowStartName = *o3tl::doAccess(aAny); + sal_uInt16 nWhich = bLineStart ? sal_uInt16(XATTR_LINESTART) : sal_uInt16(XATTR_LINEEND); + + // remove extra space separated number + sal_Int32 nPos = aArrowStartName.lastIndexOf(' '); + if (nPos > -1 && aArrowStartName.lastIndexOf(' ', nPos) > -1) + aArrowStartName = aArrowStartName.copy(0, nPos); + + OUString aApiName = SvxUnogetApiNameForItem(nWhich, aArrowStartName); + bool bIsMapped = true; + if ( !aApiName.isEmpty() ) + { + + // TODO: calculate the best option for ArrowLength and ArrowWidth + if ( aApiName == "Arrow concave" ) + reLineEnd = ESCHER_LineArrowStealthEnd; + else if ( aApiName == "Square 45" ) + reLineEnd = ESCHER_LineArrowDiamondEnd; + else if ( aApiName == "Small Arrow" ) + reLineEnd = ESCHER_LineArrowEnd; + else if ( aApiName == "Dimension Lines" ) + { + rnArrowLength = 0; + rnArrowWidth = 2; + reLineEnd = ESCHER_LineArrowOvalEnd; + } + else if ( aApiName == "Double Arrow" ) + reLineEnd = ESCHER_LineArrowEnd; + else if ( aApiName == "Rounded short Arrow" ) + reLineEnd = ESCHER_LineArrowEnd; + else if ( aApiName == "Symmetric Arrow" ) + reLineEnd = ESCHER_LineArrowEnd; + else if ( aApiName == "Line Arrow" ) + reLineEnd = ESCHER_LineArrowOpenEnd; + else if ( aApiName == "Rounded large Arrow" ) + reLineEnd = ESCHER_LineArrowEnd; + else if ( aApiName == "Circle" ) + reLineEnd = ESCHER_LineArrowOvalEnd; + else if ( aApiName == "Square" ) + reLineEnd = ESCHER_LineArrowDiamondEnd; + else if ( aApiName == "Arrow" ) + reLineEnd = ESCHER_LineArrowEnd; + else + bIsMapped = false; + + } + if ( !bIsMapped && comphelper::string::getTokenCount(aArrowStartName, ' ') == 2 ) + { + sal_Int32 nIdx{ 0 }; + std::u16string_view aArrowName( o3tl::getToken(aArrowStartName, 0, ' ', nIdx ) ); + if ( aArrowName == u"msArrowEnd" ) + reLineEnd = ESCHER_LineArrowEnd; + else if ( aArrowName == u"msArrowOpenEnd" ) + reLineEnd = ESCHER_LineArrowOpenEnd; + else if ( aArrowName == u"msArrowStealthEnd" ) + reLineEnd = ESCHER_LineArrowStealthEnd; + else if ( aArrowName == u"msArrowDiamondEnd" ) + reLineEnd = ESCHER_LineArrowDiamondEnd; + else if ( aArrowName == u"msArrowOvalEnd" ) + reLineEnd = ESCHER_LineArrowOvalEnd; + else + nIdx = -1; + + // now we have the arrow, and try to determine the arrow size; + if ( nIdx>0 ) + { + std::u16string_view aArrowSize = o3tl::getToken(aArrowStartName, 0, ' ', nIdx ); + sal_Int32 nArrowSize = o3tl::toInt32(aArrowSize); + rnArrowWidth = ( nArrowSize - 1 ) / 3; + rnArrowLength = nArrowSize - ( rnArrowWidth * 3 ) - 1; + } + } + } + } + } + return bIsArrow; +} + +void EscherPropertyContainer::CreateLineProperties( + const uno::Reference & rXPropSet, bool bEdge ) +{ + uno::Any aAny; + sal_uInt32 nLineFlags = 0x80008; + + ESCHER_LineEnd eLineEnd; + sal_Int32 nArrowLength; + sal_Int32 nArrowWidth; + + bool bSwapLineEnds = false; + if ( EscherPropertyValueHelper::GetPropertyValue( aAny, rXPropSet, "CircleKind", true ) ) + { + drawing::CircleKind eCircleKind; + if ( aAny >>= eCircleKind ) + { + if ( eCircleKind == drawing::CircleKind_ARC ) + bSwapLineEnds = true; + } + } + if ( GetLineArrow( !bSwapLineEnds, rXPropSet, eLineEnd, nArrowLength, nArrowWidth ) ) + { + AddOpt( ESCHER_Prop_lineStartArrowLength, nArrowLength ); + AddOpt( ESCHER_Prop_lineStartArrowWidth, nArrowWidth ); + AddOpt( ESCHER_Prop_lineStartArrowhead, eLineEnd ); + nLineFlags |= 0x100010; + } + if ( GetLineArrow( bSwapLineEnds, rXPropSet, eLineEnd, nArrowLength, nArrowWidth ) ) + { + AddOpt( ESCHER_Prop_lineEndArrowLength, nArrowLength ); + AddOpt( ESCHER_Prop_lineEndArrowWidth, nArrowWidth ); + AddOpt( ESCHER_Prop_lineEndArrowhead, eLineEnd ); + nLineFlags |= 0x100010; + } + + // support LineCaps + if(EscherPropertyValueHelper::GetPropertyValue(aAny, rXPropSet, "LineCap")) + { + drawing::LineCap aLineCap(drawing::LineCap_BUTT); + + if(aAny >>= aLineCap) + { + switch (aLineCap) + { + default: /* drawing::LineCap_BUTT */ + { + AddOpt(ESCHER_Prop_lineEndCapStyle, ESCHER_LineEndCapFlat); + break; + } + case drawing::LineCap_ROUND: + { + AddOpt(ESCHER_Prop_lineEndCapStyle, ESCHER_LineEndCapRound); + break; + } + case drawing::LineCap_SQUARE: + { + AddOpt(ESCHER_Prop_lineEndCapStyle, ESCHER_LineEndCapSquare); + break; + } + } + } + } + + sal_uInt32 nLineWidth = ( EscherPropertyValueHelper::GetPropertyValue( aAny, rXPropSet, "LineWidth" ) ) + ? *o3tl::doAccess(aAny) : 0; + if ( nLineWidth > 1 ) + AddOpt( ESCHER_Prop_lineWidth, nLineWidth * 360 ); // 100TH MM -> PT , 1PT = 12700 EMU + + if ( EscherPropertyValueHelper::GetPropertyValue( aAny, rXPropSet, "LineStyle" ) ) + { + drawing::LineStyle eLS; + if ( aAny >>= eLS ) + { + switch ( eLS ) + { + case drawing::LineStyle_NONE : + AddOpt( ESCHER_Prop_fNoLineDrawDash, 0x90000 ); // 80000 + break; + + case drawing::LineStyle_DASH : + { + if ( EscherPropertyValueHelper::GetPropertyValue( aAny, rXPropSet, "LineDash" ) ) + { + ESCHER_LineDashing eDash = ESCHER_LineSolid; + auto pLineDash = o3tl::doAccess(aAny); + switch ( pLineDash->Style ) + { + case drawing::DashStyle_ROUND : + case drawing::DashStyle_ROUNDRELATIVE : + AddOpt( ESCHER_Prop_lineEndCapStyle, 0 ); // set Style Round + break; + default : break; + } + // Try to detect exact prstDash styles. Use a similar method as in oox export. + // Map it to a roughly fitting prstDash in other cases. + bool bIsConverted = false; + bool bIsRelative = pLineDash->Style == drawing::DashStyle_RECTRELATIVE + || pLineDash->Style == drawing::DashStyle_ROUNDRELATIVE; + sal_Int16 nDashes = pLineDash->Dashes; + sal_Int16 nDots = pLineDash->Dots; + sal_Int32 nDashLen = pLineDash->DashLen; + sal_Int32 nDotLen = pLineDash->DotLen; + sal_Int32 nDistance = pLineDash->Distance; + + // Caution! The names are misleading. "dot" is always the first dash and "dash" + // the second one, regardless of the actual length. All prstDash + // definitions start with the longer dash and have exact one longer dash. + // Preset line style definitions for binary format are the same as for OOXML. + if (bIsRelative && nDots == 1) + { + // I'm not sure that LO always uses 100%, because in case of absolute values, LO + // sets length to 0 but treats it as 100%, if the attribute is missing in ODF. + // So to be sure set 100% explicitly in case of relative too. + if (nDashes > 0 && nDashLen == 0) + nDashLen = 100; + if (nDotLen == 0) + nDotLen = 100; + bIsConverted = true; + if (nDotLen == 100 && nDashes == 0 && nDashLen == 0 && nDistance == 300) + eDash = ESCHER_LineDotGEL; + else if (nDotLen == 400 && nDashes == 0 && nDashLen == 0 && nDistance == 300) + eDash = ESCHER_LineDashGEL; + else if (nDotLen == 400 && nDashes == 1 && nDashLen == 100 && nDistance == 300) + eDash = ESCHER_LineDashDotGEL; + else if (nDotLen == 800 && nDashes == 0 && nDashLen == 0 && nDistance == 300) + eDash = ESCHER_LineLongDashGEL; + else if (nDotLen == 800 && nDashes == 1 && nDashLen == 100 && nDistance == 300) + eDash = ESCHER_LineLongDashDotGEL; + else if (nDotLen == 800 && nDashes == 2 && nDashLen == 100 && nDistance == 300) + eDash = ESCHER_LineLongDashDotDotGEL; + else if (nDotLen == 100 && nDashes == 0 && nDashLen == 0 && nDistance == 100) + eDash = ESCHER_LineDotSys; + else if (nDotLen == 300 && nDashes == 0 && nDashLen == 0 && nDistance == 100) + eDash = ESCHER_LineDashSys; + else if (nDotLen == 300 && nDashes == 1 && nDashLen == 100 && nDistance == 100) + eDash = ESCHER_LineDashDotSys; + else if (nDotLen == 300 && nDashes == 2 && nDashLen == 100 && nDistance == 100) + eDash = ESCHER_LineDashDotDotSys; + else + bIsConverted = false; + } + + if (!bIsConverted) + { // Map the style roughly to preset line styles. + if (((!(pLineDash->Dots)) || (!(pLineDash->Dashes))) + || (pLineDash->DotLen == pLineDash->DashLen)) + { + sal_Int32 nLen = pLineDash->DotLen; + if (pLineDash->Dashes) + nLen = pLineDash->DashLen; + if (nLen >= nDistance) + eDash = ESCHER_LineLongDashGEL; + else if (pLineDash->Dots) + eDash = ESCHER_LineDotSys; + else + eDash = ESCHER_LineDashGEL; + } + else // X Y + { + if (pLineDash->Dots != pLineDash->Dashes) + { + if ((pLineDash->DashLen > nDistance) || (pLineDash->DotLen > nDistance)) + eDash = ESCHER_LineLongDashDotDotGEL; + else + eDash = ESCHER_LineDashDotDotSys; + } + else // X Y Y + { + if ((pLineDash->DashLen > nDistance) || (pLineDash->DotLen > nDistance)) + eDash = ESCHER_LineLongDashDotGEL; + else + eDash = ESCHER_LineDashDotGEL; + } + } + } + AddOpt( ESCHER_Prop_lineDashing, eDash ); + } + } + [[fallthrough]]; + case drawing::LineStyle_SOLID : + default: + { + AddOpt( ESCHER_Prop_fNoLineDrawDash, nLineFlags ); + } + break; + } + } + if ( EscherPropertyValueHelper::GetPropertyValue( aAny, rXPropSet, "LineColor" ) ) + { + sal_uInt32 nLineColor = ImplGetColor( *o3tl::doAccess(aAny) ); + AddOpt( ESCHER_Prop_lineColor, nLineColor ); + AddOpt( ESCHER_Prop_lineBackColor, nLineColor ^ 0xffffff ); + } + } + + ESCHER_LineJoin eLineJoin = ESCHER_LineJoinMiter; + if ( EscherPropertyValueHelper::GetPropertyValue( aAny, rXPropSet, "LineJoint", true ) ) + { + drawing::LineJoint eLJ; + if ( aAny >>= eLJ ) + { + switch ( eLJ ) + { + case drawing::LineJoint_NONE : + case drawing::LineJoint_BEVEL : + eLineJoin = ESCHER_LineJoinBevel; + break; + default: + case drawing::LineJoint_MIDDLE : + case drawing::LineJoint_MITER : + eLineJoin = ESCHER_LineJoinMiter; + break; + case drawing::LineJoint_ROUND : + eLineJoin = ESCHER_LineJoinRound; + break; + } + } + } + AddOpt( ESCHER_Prop_lineJoinStyle, eLineJoin ); + + if ( EscherPropertyValueHelper::GetPropertyValue( + aAny, rXPropSet, "LineTransparence", true ) ) + { + sal_Int16 nTransparency = 0; + if ( aAny >>= nTransparency ) + AddOpt( ESCHER_Prop_lineOpacity, ( ( 100 - nTransparency ) << 16 ) / 100 ); + } + + + if ( !bEdge ) + { + AddOpt( ESCHER_Prop_fFillOK, 0x1001 ); + AddOpt( ESCHER_Prop_fNoFillHitTest, 0x100000 ); + } +} + +static Size lcl_SizeToEmu(Size aPrefSize, const MapMode& aPrefMapMode) +{ + Size aRetSize; + if (aPrefMapMode.GetMapUnit() == MapUnit::MapPixel) + aRetSize = Application::GetDefaultDevice()->PixelToLogic(aPrefSize, MapMode(MapUnit::Map100thMM)); + else + aRetSize = OutputDevice::LogicToLogic(aPrefSize, aPrefMapMode, MapMode(MapUnit::Map100thMM)); + return aRetSize; +} + +void EscherPropertyContainer::ImplCreateGraphicAttributes( const uno::Reference & rXPropSet, + sal_uInt32 nBlibId, bool bCreateCroppingAttributes ) +{ + uno::Any aAny; + + sal_uInt32 nPicFlags = 0; + drawing::ColorMode eColorMode( drawing::ColorMode_STANDARD ); + sal_Int16 nLuminance = 0; + sal_Int32 nContrast = 0; + + if ( EscherPropertyValueHelper::GetPropertyValue( aAny, rXPropSet, "GraphicColorMode" ) ) + aAny >>= eColorMode; + if ( EscherPropertyValueHelper::GetPropertyValue( aAny, rXPropSet, "AdjustLuminance" ) ) + aAny >>= nLuminance; + if ( EscherPropertyValueHelper::GetPropertyValue( aAny, rXPropSet, "AdjustContrast" ) ) + { + sal_Int16 nC = sal_Int16(); + aAny >>= nC; + nContrast = nC; + } + + if ( eColorMode == drawing::ColorMode_WATERMARK ) + { + eColorMode = drawing::ColorMode_STANDARD; + nLuminance += 70; + if ( nLuminance > 100 ) + nLuminance = 100; + nContrast -= 70; + if ( nContrast < -100 ) + nContrast = -100; + } + if ( eColorMode == drawing::ColorMode_GREYS ) + nPicFlags |= 0x40004; + else if ( eColorMode == drawing::ColorMode_MONO ) + nPicFlags |= 0x60006; + + if ( nContrast ) + { + nContrast += 100; + if ( nContrast == 100) + nContrast = 0x10000; + else if ( nContrast < 100 ) + { + nContrast *= 0x10000; + nContrast /= 100; + } + else if ( nContrast < 200 ) + nContrast = ( 100 * 0x10000 ) / ( 200 - nContrast ); + else + nContrast = 0x7fffffff; + AddOpt( ESCHER_Prop_pictureContrast, nContrast ); + } + if ( nLuminance ) + AddOpt( ESCHER_Prop_pictureBrightness, nLuminance * 327 ); + if ( nPicFlags ) + AddOpt( ESCHER_Prop_pictureActive, nPicFlags ); + + if ( !(bCreateCroppingAttributes && pGraphicProvider) ) + return; + + Size aPrefSize; + MapMode aPrefMapMode; + if ( !pGraphicProvider->GetPrefSize( nBlibId, aPrefSize, aPrefMapMode ) ) + return; + + Size aCropSize(lcl_SizeToEmu(aPrefSize, aPrefMapMode)); + if ( !(aCropSize.Width() && aCropSize.Height()) ) + return; + + if ( !EscherPropertyValueHelper::GetPropertyValue( aAny, rXPropSet, "GraphicCrop" ) ) + return; + + text::GraphicCrop aGraphCrop; + if ( !(aAny >>= aGraphCrop) ) + return; + + if ( aGraphCrop.Left ) + { + sal_uInt32 nLeft = ( aGraphCrop.Left * 65536 ) / aCropSize.Width(); + AddOpt( ESCHER_Prop_cropFromLeft, nLeft ); + } + if ( aGraphCrop.Top ) + { + sal_uInt32 nTop = ( aGraphCrop.Top * 65536 ) / aCropSize.Height(); + AddOpt( ESCHER_Prop_cropFromTop, nTop ); + } + if ( aGraphCrop.Right ) + { + sal_uInt32 nRight = ( aGraphCrop.Right * 65536 ) / aCropSize.Width(); + AddOpt( ESCHER_Prop_cropFromRight, nRight ); + } + if ( aGraphCrop.Bottom ) + { + sal_uInt32 nBottom = ( aGraphCrop.Bottom * 65536 ) / aCropSize.Height(); + AddOpt( ESCHER_Prop_cropFromBottom, nBottom ); + } +} + +void EscherPropertyContainer::CreateShapeProperties( const uno::Reference & rXShape ) +{ + uno::Reference< beans::XPropertySet > aXPropSet( rXShape, uno::UNO_QUERY ); + if ( !aXPropSet.is() ) + return; + + bool bVisible = false; + bool bPrintable = false; + uno::Any aAny; + sal_uInt32 nShapeAttr = 0; + if (EscherPropertyValueHelper::GetPropertyValue(aAny, aXPropSet, "Visible", true) && (aAny >>= bVisible)) + { + if ( !bVisible ) + nShapeAttr |= 0x20002; // set fHidden = true + } + // This property (fPrint) isn't used in Excel anymore, leaving it for legacy reasons + // one change, based on XLSX: hidden implies not printed, let's not export the fPrint property in that case + if (bVisible && EscherPropertyValueHelper::GetPropertyValue(aAny, aXPropSet, "Printable", true) && (aAny >>= bPrintable)) + { + if ( !bPrintable ) + nShapeAttr |= 0x10000; // set fPrint = false; + } + if ( nShapeAttr ) + AddOpt( ESCHER_Prop_fPrint, nShapeAttr ); +} + +bool EscherPropertyContainer::CreateOLEGraphicProperties(const uno::Reference & rXShape) +{ + bool bRetValue = false; + + if ( rXShape.is() ) + { + SdrObject* pObject = SdrObject::getSdrObjectFromXShape(rXShape); // SJ: leaving unoapi, because currently there is + if (auto pOle2Obj = dynamic_cast(pObject)) // no access to the native graphic object + { + const Graphic* pGraphic = pOle2Obj->GetGraphic(); + if (pGraphic) + { + Graphic aGraphic(*pGraphic); + GraphicObject aGraphicObject(aGraphic); + bRetValue = CreateGraphicProperties(rXShape, aGraphicObject); + } + } + } + return bRetValue; +} + +bool EscherPropertyContainer::CreateGraphicProperties(const uno::Reference & rXShape, const GraphicObject& rGraphicObj) +{ + bool bRetValue = false; + OString aUniqueId(rGraphicObj.GetUniqueID()); + if ( !aUniqueId.isEmpty() ) + { + AddOpt( ESCHER_Prop_fillType, ESCHER_FillPicture ); + uno::Reference< beans::XPropertySet > aXPropSet( rXShape, uno::UNO_QUERY ); + + if ( pGraphicProvider && pPicOutStrm && pShapeBoundRect && aXPropSet.is() ) + { + uno::Any aAny; + std::unique_ptr pVisArea; + if ( EscherPropertyValueHelper::GetPropertyValue( aAny, aXPropSet, "VisibleArea" ) ) + { + pVisArea.reset(new awt::Rectangle); + aAny >>= *pVisArea; + } + sal_uInt32 nBlibId = pGraphicProvider->GetBlibID( *pPicOutStrm, rGraphicObj, pVisArea.get() ); + if ( nBlibId ) + { + AddOpt( ESCHER_Prop_pib, nBlibId, true ); + ImplCreateGraphicAttributes( aXPropSet, nBlibId, false ); + bRetValue = true; + } + } + } + return bRetValue; +} + +bool EscherPropertyContainer::CreateMediaGraphicProperties(const uno::Reference & rXShape) +{ + bool bRetValue = false; + if ( rXShape.is() ) + { + SdrObject* pSdrObject(SdrObject::getSdrObjectFromXShape(rXShape)); // SJ: leaving unoapi, because currently there is + if (auto pSdrMediaObj = dynamic_cast(pSdrObject)) // no access to the native graphic object + { + GraphicObject aGraphicObject(pSdrMediaObj->getSnapshot()); + bRetValue = CreateGraphicProperties(rXShape, aGraphicObject); + } + } + return bRetValue; +} + +bool EscherPropertyContainer::ImplCreateEmbeddedBmp(GraphicObject const & rGraphicObject) +{ + if (rGraphicObject.GetType() != GraphicType::NONE) + { + EscherGraphicProvider aProvider; + SvMemoryStream aMemStrm; + + if (aProvider.GetBlibID( aMemStrm, rGraphicObject)) + { + AddOpt(ESCHER_Prop_fillBlip, true, 0, aMemStrm); + return true; + } + } + return false; +} + +void EscherPropertyContainer::CreateEmbeddedBitmapProperties( + uno::Reference const & rxBitmap, drawing::BitmapMode eBitmapMode ) +{ + uno::Reference xGraphic(rxBitmap, uno::UNO_QUERY); + if (!xGraphic.is()) + return; + const Graphic aGraphic(xGraphic); + if (aGraphic.IsNone()) + return; + const GraphicObject aGraphicObject(aGraphic); + if (aGraphicObject.GetType() == GraphicType::NONE) + return; + if (ImplCreateEmbeddedBmp(aGraphicObject)) + { + // bitmap mode property + bool bRepeat = eBitmapMode == drawing::BitmapMode_REPEAT; + AddOpt( ESCHER_Prop_fillType, bRepeat ? ESCHER_FillTexture : ESCHER_FillPicture ); + } +} + +namespace { + +Graphic lclDrawHatch( const drawing::Hatch& rHatch, const Color& rBackColor, bool bFillBackground, const tools::Rectangle& rRect ) +{ + // #i121183# For hatch, do no longer create a bitmap with the fixed size of 28x28 pixels. Also + // do not create a bitmap in page size, that would explode file sizes (and have no good quality). + // Better use a MetaFile graphic in page size; thus we have good quality due to vector format and + // no bit file sizes. + ScopedVclPtrInstance< VirtualDevice > pVDev; + GDIMetaFile aMtf; + + pVDev->SetOutputSizePixel(Size(2, 2)); + pVDev->EnableOutput(false); + pVDev->SetMapMode(MapMode(MapUnit::Map100thMM)); + aMtf.Clear(); + aMtf.Record(pVDev); + pVDev->SetLineColor(); + pVDev->SetFillColor(bFillBackground ? rBackColor : COL_TRANSPARENT); + pVDev->DrawRect(rRect); + pVDev->DrawHatch(tools::PolyPolygon(rRect), Hatch(static_cast(rHatch.Style), Color(ColorTransparency, rHatch.Color), rHatch.Distance, + Degree10(rHatch.Angle))); + aMtf.Stop(); + aMtf.WindStart(); + aMtf.SetPrefMapMode(MapMode(MapUnit::Map100thMM)); + aMtf.SetPrefSize(rRect.GetSize()); + + return Graphic(aMtf); +} + +} // namespace + +void EscherPropertyContainer::CreateEmbeddedHatchProperties(const drawing::Hatch& rHatch, const Color& rBackColor, bool bFillBackground ) +{ + const tools::Rectangle aRect(pShapeBoundRect ? *pShapeBoundRect : tools::Rectangle(Point(0,0), Size(28000, 21000))); + Graphic aGraphic(lclDrawHatch(rHatch, rBackColor, bFillBackground, aRect)); + GraphicObject aGraphicObject(aGraphic); + + if (ImplCreateEmbeddedBmp(aGraphicObject)) + AddOpt( ESCHER_Prop_fillType, ESCHER_FillTexture ); +} + +bool EscherPropertyContainer::CreateGraphicProperties(const uno::Reference & rXPropSet, + const OUString& rSource, + const bool bCreateFillBitmap, + const bool bCreateCroppingAttributes, + const bool bFillBitmapModeAllowed, + const bool bOOxmlExport ) +{ + bool bRetValue = false; + bool bCreateFillStyles = false; + + std::unique_ptr pGraphicAttr; + uno::Reference xGraphic; + + uno::Any aAny; + + if ( EscherPropertyValueHelper::GetPropertyValue( aAny, rXPropSet, rSource ) ) + { + bool bMirrored = false; + bool bRotate = true; + bool bIsGraphicMtf = false; + sal_Int16 nTransparency(0); + sal_Int16 nRed(0); + sal_Int16 nGreen(0); + sal_Int16 nBlue(0); + double fGamma(1.0); + drawing::BitmapMode eBitmapMode(drawing::BitmapMode_NO_REPEAT); + OUString aGraphicUrl; + + sal_uInt16 nAngle = 0; + if ( rSource == "MetaFile" ) + { + auto & aSeq = *o3tl::doAccess>(aAny); + const sal_Int8* pArray = aSeq.getConstArray(); + sal_uInt32 nArrayLength = aSeq.getLength(); + + // the metafile is already rotated + bRotate = false; + + if (pArray && nArrayLength) + { + Graphic aGraphic; + SvMemoryStream aStream(const_cast(pArray), nArrayLength, StreamMode::READ); + ErrCode nErrCode = GraphicConverter::Import(aStream, aGraphic, ConvertDataFormat::WMF); + if ( nErrCode == ERRCODE_NONE ) + { + xGraphic = aGraphic.GetXGraphic(); + bIsGraphicMtf = aGraphic.GetType() == GraphicType::GdiMetafile; + } + } + } + else if (rSource == "Bitmap" || rSource == "FillBitmap") + { + auto xBitmap = aAny.get>(); + if (xBitmap.is()) + { + xGraphic.set(xBitmap, uno::UNO_QUERY); + Graphic aGraphic(xGraphic); + bIsGraphicMtf = aGraphic.GetType() == GraphicType::GdiMetafile; + } + } + else if ( rSource == "Graphic" ) + { + xGraphic = aAny.get>(); + bCreateFillStyles = true; + } + else if ( rSource == "FillHatch" ) + { + drawing::Hatch aHatch; + if ( aAny >>= aHatch ) + { + Color aBackColor; + if ( EscherPropertyValueHelper::GetPropertyValue( aAny, rXPropSet, "FillColor" ) ) + { + aBackColor = Color(ColorTransparency, ImplGetColor( *o3tl::doAccess(aAny), false )); + } + bool bFillBackground = false; + if ( EscherPropertyValueHelper::GetPropertyValue( aAny, rXPropSet, "FillBackground", true ) ) + { + aAny >>= bFillBackground; + } + + const tools::Rectangle aRect(Point(0, 0), pShapeBoundRect ? pShapeBoundRect->GetSize() : Size(28000, 21000)); + Graphic aGraphic(lclDrawHatch(aHatch, aBackColor, bFillBackground, aRect)); + xGraphic = aGraphic.GetXGraphic(); + eBitmapMode = drawing::BitmapMode_REPEAT; + bIsGraphicMtf = aGraphic.GetType() == GraphicType::GdiMetafile; + } + } + + if ( EscherPropertyValueHelper::GetPropertyValue( aAny, rXPropSet, "IsMirrored", true ) ) + aAny >>= bMirrored; + + // #121074# transparency of graphic is not supported in MS formats, get and apply it + // in the GetTransformedGraphic call in GetBlibID + if(EscherPropertyValueHelper::GetPropertyValue(aAny, rXPropSet, "Transparency")) + { + aAny >>= nTransparency; + } + + if ( EscherPropertyValueHelper::GetPropertyValue( aAny, rXPropSet, "AdjustRed" ) ) + { + aAny >>= nRed; + } + + if ( EscherPropertyValueHelper::GetPropertyValue( aAny, rXPropSet, "AdjustGreen" ) ) + { + aAny >>= nGreen; + } + + if ( EscherPropertyValueHelper::GetPropertyValue( aAny, rXPropSet, "AdjustBlue" ) ) + { + aAny >>= nBlue; + } + + if ( EscherPropertyValueHelper::GetPropertyValue( aAny, rXPropSet, "Gamma" ) ) + { + aAny >>= fGamma; + } + + if ( bCreateFillBitmap && bFillBitmapModeAllowed ) + { + if ( EscherPropertyValueHelper::GetPropertyValue( aAny, rXPropSet, "FillBitmapMode", true ) ) + aAny >>= eBitmapMode; + } + else + { + nAngle = bRotate && EscherPropertyValueHelper::GetPropertyValue( aAny, rXPropSet, "RotateAngle", true ) + ? static_cast( ( *o3tl::doAccess(aAny) ) + 5 ) / 10 + : 0; + } + + if (xGraphic.is()) + { + Graphic aGraphic(xGraphic); + aGraphicUrl = aGraphic.getOriginURL(); + } + + if (!aGraphicUrl.isEmpty()) + { + bool bConverted = false; + + // externally, linked graphic? convert to embedded + // one, if transformations are needed. this is because + // everything < msoxp cannot even handle rotated + // bitmaps. + // And check whether the graphic link target is + // actually supported by mso. + INetURLObject aTmp( aGraphicUrl ); + GraphicDescriptor aDescriptor(aTmp); + (void)aDescriptor.Detect(); + const GraphicFileFormat nFormat = aDescriptor.GetFileFormat(); + + // can MSO handle it? + if ( bMirrored || nAngle || nTransparency || nRed || nGreen || nBlue || (1.0 != fGamma) || + (nFormat != GraphicFileFormat::BMP && + nFormat != GraphicFileFormat::GIF && + nFormat != GraphicFileFormat::JPG && + nFormat != GraphicFileFormat::PNG && + nFormat != GraphicFileFormat::TIF && + nFormat != GraphicFileFormat::PCT && + nFormat != GraphicFileFormat::WMF && + nFormat != GraphicFileFormat::EMF) ) + { + std::unique_ptr pIn(::utl::UcbStreamHelper::CreateStream( + aTmp.GetMainURL( INetURLObject::DecodeMechanism::NONE ), StreamMode::READ )); + if ( pIn ) + { + Graphic aGraphic; + ErrCode nErrCode = GraphicConverter::Import( *pIn, aGraphic ); + + if ( nErrCode == ERRCODE_NONE ) + { + xGraphic = aGraphic.GetXGraphic(); + bConverted = true; + } + // else: simply keep the graphic link + } + } + + if (!bConverted && pGraphicProvider ) + { + const OUString& rBaseURI( pGraphicProvider->GetBaseURI() ); + INetURLObject aBaseURI( rBaseURI ); + if( aBaseURI.GetProtocol() == aTmp.GetProtocol() ) + { + OUString aRelUrl( INetURLObject::GetRelURL( rBaseURI, aGraphicUrl ) ); + if ( !aRelUrl.isEmpty() ) + aGraphicUrl = aRelUrl; + } + } + } + + if (!aGraphicUrl.isEmpty() || xGraphic.is()) + { + if(bMirrored || nTransparency || nRed || nGreen || nBlue || (1.0 != fGamma)) + { + pGraphicAttr.reset(new GraphicAttr); + + if(bMirrored) + { + pGraphicAttr->SetMirrorFlags(BmpMirrorFlags::Horizontal); + } + + if(nTransparency) + { + pGraphicAttr->SetAlpha(255 - (nTransparency * 255) / 100); + } + + if(nRed) + { + pGraphicAttr->SetChannelR(nRed); + } + + if(nGreen) + { + pGraphicAttr->SetChannelG(nGreen); + } + + if(nBlue) + { + pGraphicAttr->SetChannelB(nBlue); + } + + if(1.0 != fGamma) + { + pGraphicAttr->SetGamma(fGamma); + } + } + + if(nAngle && bIsGraphicMtf) + { + AddOpt( ESCHER_Prop_Rotation, ( ( (static_cast(nAngle) << 16 ) / 10 ) + 0x8000 ) &~ 0xffff ); + } + + if ( eBitmapMode == drawing::BitmapMode_REPEAT ) + { + sal_Int32 nSizeX = 0,nSizeY = 0,nOffsetX = 0,nOffsetY = 0,nPosOffsetX = 0,nPosOffsetY = 0; + if ( EscherPropertyValueHelper::GetPropertyValue( aAny, rXPropSet, "FillBitmapSizeX", true ) ) + { + aAny >>= nSizeX; + } + if ( EscherPropertyValueHelper::GetPropertyValue( aAny, rXPropSet, "FillBitmapSizeY", true ) ) + { + aAny >>= nSizeY; + } + if ( EscherPropertyValueHelper::GetPropertyValue( aAny, rXPropSet, "FillBitmapOffsetX", true ) ) + { + aAny >>= nOffsetX; + } + if ( EscherPropertyValueHelper::GetPropertyValue( aAny, rXPropSet, "FillBitmapOffsetY", true ) ) + { + aAny >>= nOffsetY; + } + if ( EscherPropertyValueHelper::GetPropertyValue( aAny, rXPropSet, "FillBitmapPositionOffsetX", true ) ) + { + aAny >>= nPosOffsetX; + } + if ( EscherPropertyValueHelper::GetPropertyValue( aAny, rXPropSet, "FillBitmapPositionOffsetY", true ) ) + { + aAny >>= nPosOffsetY; + } + if(nSizeX == -100 && nSizeY == -100 && nOffsetX == 0 && nOffsetY == 0 && nPosOffsetX == 0 && nPosOffsetY == 0) + AddOpt( ESCHER_Prop_fillType, ESCHER_FillPicture ); + else + AddOpt( ESCHER_Prop_fillType, ESCHER_FillTexture ); + } + else + AddOpt( ESCHER_Prop_fillType, ESCHER_FillPicture ); + + if (xGraphic.is()) + { + Graphic aGraphic(xGraphic); + if (!aGraphic.getOriginURL().isEmpty()) + { + AddOpt(ESCHER_Prop_pibName, aGraphicUrl); + sal_uInt32 nPibFlags = 0; + GetOpt(ESCHER_Prop_pibFlags, nPibFlags); + AddOpt(ESCHER_Prop_pibFlags, ESCHER_BlipFlagLinkToFile | ESCHER_BlipFlagFile | ESCHER_BlipFlagDoNotSave | nPibFlags); + } + else if (pGraphicProvider && pPicOutStrm && pShapeBoundRect) // write out embedded graphic + { + GraphicObject aGraphicObject(aGraphic); + const sal_uInt32 nBlibId(pGraphicProvider->GetBlibID(*pPicOutStrm, aGraphicObject, nullptr, pGraphicAttr.get())); + + if(nBlibId) + { + if(bCreateFillBitmap) + { + AddOpt(ESCHER_Prop_fillBlip, nBlibId, true); + } + else + { + AddOpt( ESCHER_Prop_pib, nBlibId, true ); + ImplCreateGraphicAttributes( rXPropSet, nBlibId, bCreateCroppingAttributes ); + } + + bRetValue = true; + } + } + else + { + EscherGraphicProvider aProvider; + SvMemoryStream aMemStrm; + GraphicObject aGraphicObject(aGraphic); + + if (aProvider.GetBlibID(aMemStrm, aGraphicObject, nullptr, pGraphicAttr.get(), bOOxmlExport)) + { + AddOpt(ESCHER_Prop_fillBlip, true, 0, aMemStrm); + bRetValue = true; + } + } + } + } + } + pGraphicAttr.reset(); + if ( bCreateFillStyles ) + CreateFillProperties( rXPropSet, true ); + + return bRetValue; +} + +tools::PolyPolygon EscherPropertyContainer::GetPolyPolygon( const uno::Reference< drawing::XShape > & rXShape ) +{ + tools::PolyPolygon aRetPolyPoly; + uno::Reference< beans::XPropertySet > aXPropSet; + uno::Any aAny( rXShape->queryInterface( + cppu::UnoType::get())); + + if ( aAny >>= aXPropSet ) + { + bool bHasProperty = EscherPropertyValueHelper::GetPropertyValue( aAny, aXPropSet, "PolyPolygonBezier", true ); + if ( !bHasProperty ) + bHasProperty = EscherPropertyValueHelper::GetPropertyValue( aAny, aXPropSet, "PolyPolygon", true ); + if ( !bHasProperty ) + bHasProperty = EscherPropertyValueHelper::GetPropertyValue( aAny, aXPropSet, "Polygon", true ); + if ( bHasProperty ) + aRetPolyPoly = GetPolyPolygon( aAny ); + } + return aRetPolyPoly; +} + +// adapting to basegfx::B2DPolyPolygon now, has no sense to do corrections in the +// old tools::PolyPolygon creation code. Convert to that at return time +tools::PolyPolygon EscherPropertyContainer::GetPolyPolygon( const uno::Any& rAny ) +{ + basegfx::B2DPolyPolygon aRetval; + + if(auto pBCC = o3tl::tryAccess(rAny)) + { + aRetval = basegfx::utils::UnoPolyPolygonBezierCoordsToB2DPolyPolygon(*pBCC); + } + else if(auto pCC = o3tl::tryAccess(rAny)) + { + aRetval = basegfx::utils::UnoPointSequenceSequenceToB2DPolyPolygon(*pCC); + } + else if(auto pC = o3tl::tryAccess(rAny)) + { + aRetval.append(basegfx::utils::UnoPointSequenceToB2DPolygon(*pC)); + } + + basegfx::B2DPolyPolygon aRetval2; + + for(sal_uInt32 a(0); a < aRetval.count(); a++) + { + if(0 != aRetval.getB2DPolygon(a).count()) + { + aRetval2.append(aRetval.getB2DPolygon(a)); + } + } + + return tools::PolyPolygon(aRetval2); +} + +bool EscherPropertyContainer::CreatePolygonProperties( + const uno::Reference & rXPropSet, + sal_uInt32 nFlags, + bool bBezier, + awt::Rectangle& rGeoRect, + tools::Polygon const * pPolygon ) +{ + tools::PolyPolygon aPolyPolygon; + + if(nullptr != pPolygon) + { + aPolyPolygon.Insert(*pPolygon); + } + else + { + uno::Any aAny; + + if(EscherPropertyValueHelper::GetPropertyValue( + aAny, + rXPropSet, + bBezier ? OUString("PolyPolygonBezier") : OUString("PolyPolygon"), + true)) + { + aPolyPolygon = GetPolyPolygon(aAny); + } + else + { + return false; + } + } + + if(0 == aPolyPolygon.Count()) + { + return false; + } + + if(0 != (nFlags & ESCHER_CREATEPOLYGON_LINE)) + { + if((1 == aPolyPolygon.Count()) && (2 == aPolyPolygon[0].GetSize())) + { + const tools::Polygon& rPoly(aPolyPolygon[0]); + + rGeoRect = awt::Rectangle( + rPoly[0].X(), + rPoly[0].Y(), + rPoly[1].X() - rPoly[0].X(), + rPoly[1].Y() - rPoly[0].Y()); + + return true; + } + + return false; + } + + const tools::Rectangle aRect(aPolyPolygon.GetBoundRect()); + + rGeoRect = awt::Rectangle( + aRect.Left(), + aRect.Top(), + aRect.GetWidth(), + aRect.GetHeight()); + + const sal_uInt16 nPolyCount(aPolyPolygon.Count()); + sal_uInt32 nTotalPoints(0); + + std::vector< sal_uInt8 > aVertices + { + 0, 0, 0, 0, + static_cast(0xf0), + static_cast(0xff) + }; + + std::vector< sal_uInt8 > aSegments + { + 0, 0, 0, 0, + static_cast(2), + static_cast(0) + }; + + for(sal_uInt16 j(0); j < nPolyCount; ++j) + { + const tools::Polygon aPolygon(aPolyPolygon[j]); + const sal_uInt16 nPoints(aPolygon.GetSize()); + + if(0 == nPoints) + { + continue; + } + + // Polygon start + aSegments.push_back(static_cast(0x0)); + aSegments.push_back(static_cast(0x40)); + + sal_uInt16 nSegmentIgnoreCounter(0); + + // write points from polygon to buffer + for(sal_uInt16 i(0); i < nPoints; ++i) + { + Point aPoint(aPolygon[i]); + + aPoint.AdjustX(-(rGeoRect.X)); + aPoint.AdjustY(-(rGeoRect.Y)); + + aVertices.push_back(static_cast(aPoint.X())); + aVertices.push_back(static_cast(aPoint.X() >> 8)); + aVertices.push_back(static_cast(aPoint.Y())); + aVertices.push_back(static_cast(aPoint.Y() >> 8)); + + nTotalPoints++; + + if(0 != nSegmentIgnoreCounter) + { + nSegmentIgnoreCounter--; + } + else + { + aSegments.push_back(static_cast(0)); + + if(bBezier) + { + aSegments.push_back(static_cast(0xb3)); + } + else + { + aSegments.push_back(static_cast(0xac)); + } + + if(i + 1 == nPoints) + { + if(nPolyCount > 1) + { + // end of polygon + aSegments.push_back(static_cast(1)); + aSegments.push_back(static_cast(0x60)); + } + } + else + { + aSegments.push_back(static_cast(1)); + + if(PolyFlags::Control == aPolygon.GetFlags(i + 1)) + { + aSegments.push_back(static_cast(0x20)); + nSegmentIgnoreCounter = 2; + } + else + { + aSegments.push_back(static_cast(0)); + } + } + } + } + } + + if(0 == nTotalPoints || aSegments.size() < 6 || aVertices.size() < 6) + return false; + + // Little endian + aVertices[0] = static_cast(nTotalPoints); + aVertices[1] = static_cast(nTotalPoints >> 8); + aVertices[2] = static_cast(nTotalPoints); + aVertices[3] = static_cast(nTotalPoints >> 8); + + aSegments.push_back(static_cast(0)); + aSegments.push_back(static_cast(0x80)); + + const sal_uInt32 nSegmentBufSize(aSegments.size() - 6); + aSegments[0] = static_cast(nSegmentBufSize >> 1); + aSegments[1] = static_cast(nSegmentBufSize >> 9); + aSegments[2] = static_cast(nSegmentBufSize >> 1); + aSegments[3] = static_cast(nSegmentBufSize >> 9); + + AddOpt( + ESCHER_Prop_geoRight, + rGeoRect.Width); + AddOpt( + ESCHER_Prop_geoBottom, + rGeoRect.Height); + AddOpt( + ESCHER_Prop_shapePath, + ESCHER_ShapeComplex); + AddOpt( + ESCHER_Prop_pVertices, + true, + aVertices.size() - 6, + aVertices); + AddOpt( + ESCHER_Prop_pSegmentInfo, + true, + aSegments.size(), + aSegments); + + return true; +} + + +/* +in MS,the connector including 9 types : +"straightConnector1", +"bentConnector2","bentConnector3","bentConnector4","bentConnector5" +"curvedConnector2","curvedConnector3","curvedConnector4","curvedConnector5" +in AOO,including 4 types:"standard","lines","line","curve" +when save as MS file, the connector must be convert to corresponding type. +"line" and "lines" <-> "straightConnector1" +"standard" <-> "bentConnector2-5" +"curve" <-> "curvedConnector2-5" +*/ +static sal_Int32 lcl_GetAdjustValueCount( const XPolygon& rPoly ) +{ + int nRet = 0; + switch ( rPoly.GetSize() ) + { + case 2 : + case 3: + nRet = 0; + break; + case 4: + nRet = 1; + break; + case 5: + nRet = 2; + break; + default: + if ( rPoly.GetSize()>=6 ) + nRet = 3; + break; + } + return nRet; +} + +// Adjust value decide the position which connector should turn a corner +static sal_Int32 lcl_GetConnectorAdjustValue ( const XPolygon& rPoly, sal_uInt16 nIndex ) +{ + sal_uInt16 k = rPoly.GetSize(); + OSL_ASSERT ( k >= ( 3 + nIndex ) ); + + Point aPt; + Point aStart = rPoly[0]; + Point aEnd = rPoly[k-1]; + if ( aEnd.Y() == aStart.Y() ) + aEnd.setY( aStart.Y() +4 ); + if ( aEnd.X() == aStart.X() ) + aEnd.setX( aStart.X() +4 ); + + bool bVertical = ( rPoly[1].X()-aStart.X() ) == 0 ; + // vertical and horizon alternate + if ( nIndex%2 == 1 ) bVertical = !bVertical; + aPt = rPoly[ nIndex + 1]; + + sal_Int32 nAdjustValue; + if ( bVertical ) + nAdjustValue = ( aPt.Y()-aStart.Y())* 21600 /(aEnd.Y()-aStart.Y()); + else + nAdjustValue = ( aPt.X()-aStart.X() )* 21600 /(aEnd.X()-aStart.X()); + + return nAdjustValue; +} + + +static void lcl_Rotate(Degree100 nAngle, Point center, Point& pt) +{ + nAngle = NormAngle36000(nAngle); + + int cs, sn; + switch (nAngle.get()) + { + case 0: + cs =1; + sn =0; + break; + case 9000: + cs =0; + sn =1; + break; + case 18000: + cs = -1; + sn = 0; + break; + case 27000: + cs = 0; + sn = -1; + break; + default: + return; + } + sal_Int32 x0 =pt.X()-center.X(); + sal_Int32 y0 =pt.Y()-center.Y(); + pt.setX(center.X()+ x0*cs-y0*sn ); + pt.setY(center.Y()+ y0*cs+x0*sn ); +} +/* + FlipV defines that the shape will be flipped vertically about the center of its bounding box. +Generally, draw the connector from top to bottom, from left to right when meet the adjust value, +but when (X1>X2 or Y1>Y2),the draw director must be reverse, FlipV or FlipH should be set to true. +*/ +static bool lcl_GetAngle(tools::Polygon &rPoly, ShapeFlag& rShapeFlags,sal_Int32& nAngle ) +{ + Point aStart = rPoly[0]; + Point aEnd = rPoly[rPoly.GetSize()-1]; + nAngle = ( rPoly[1].X() == aStart.X() ) ? 9000: 0 ; + Point p1(aStart.X(),aStart.Y()); + Point p2(aEnd.X(),aEnd.Y()); + if ( nAngle ) + { + Point center((aEnd.X()+aStart.X())>>1,(aEnd.Y()+aStart.Y())>>1); + lcl_Rotate(Degree100(-nAngle), center,p1); + lcl_Rotate(Degree100(-nAngle), center,p2); + } + if ( p1.X() > p2.X() ) + { + if ( nAngle ) + rShapeFlags |= ShapeFlag::FlipV; + else + rShapeFlags |= ShapeFlag::FlipH; + + } + if ( p1.Y() > p2.Y() ) + { + if ( nAngle ) + rShapeFlags |= ShapeFlag::FlipH; + else + rShapeFlags |= ShapeFlag::FlipV; + } + + if ( (rShapeFlags&ShapeFlag::FlipH) && (rShapeFlags&ShapeFlag::FlipV) ) + { + rShapeFlags &= ~ShapeFlag( ShapeFlag::FlipH | ShapeFlag::FlipV ); + nAngle +=18000; + } + + if ( nAngle ) + { + // Set angle properties + nAngle *= 655; + nAngle += 0x8000; + nAngle &=~0xffff; // round nAngle to whole number of degrees + return true; + } + return false; +} +bool EscherPropertyContainer::CreateConnectorProperties( + const uno::Reference & rXShape, + EscherSolverContainer& rSolverContainer, awt::Rectangle& rGeoRect, + sal_uInt16& rShapeType, ShapeFlag& rShapeFlags ) +{ + bool bRetValue = false; + rShapeType = 0; + rShapeFlags = ShapeFlag::NONE; + + if ( rXShape.is() ) + { + uno::Reference aXPropSet; + uno::Reference aShapeA, aShapeB; + uno::Any aAny( rXShape->queryInterface( cppu::UnoType::get())); + if ( aAny >>= aXPropSet ) + { + if ( EscherPropertyValueHelper::GetPropertyValue( aAny, aXPropSet, "EdgeKind", true ) ) + { + drawing::ConnectorType eCt; + aAny >>= eCt; + if ( EscherPropertyValueHelper::GetPropertyValue( aAny, aXPropSet, "EdgeStartPoint" ) ) + { + awt::Point aStartPoint = *o3tl::doAccess(aAny); + if ( EscherPropertyValueHelper::GetPropertyValue( aAny, aXPropSet, "EdgeEndPoint" ) ) + { + awt::Point aEndPoint = *o3tl::doAccess(aAny); + + rShapeFlags = ShapeFlag::HaveAnchor | ShapeFlag::HaveShapeProperty | ShapeFlag::Connector; + rGeoRect = awt::Rectangle( aStartPoint.X, aStartPoint.Y, + ( aEndPoint.X - aStartPoint.X ) + 1, ( aEndPoint.Y - aStartPoint.Y ) + 1 ); + // set standard's FLIP in below code + if ( eCt != drawing::ConnectorType_STANDARD) + { + if ( rGeoRect.Height < 0 ) // justify + { + rShapeFlags |= ShapeFlag::FlipV; + rGeoRect.Y = aEndPoint.Y; + rGeoRect.Height = -rGeoRect.Height; + } + if ( rGeoRect.Width < 0 ) + { + rShapeFlags |= ShapeFlag::FlipH; + rGeoRect.X = aEndPoint.X; + rGeoRect.Width = -rGeoRect.Width; + } + } + sal_uInt32 nAdjustValue1, nAdjustValue2; + nAdjustValue1 = nAdjustValue2 = 0x2a30; + + if ( EscherPropertyValueHelper::GetPropertyValue( aAny, aXPropSet, "EdgeStartConnection" ) ) + aAny >>= aShapeA; + if ( EscherPropertyValueHelper::GetPropertyValue( aAny, aXPropSet, "EdgeEndConnection" ) ) + aAny >>= aShapeB; + rSolverContainer.AddConnector( rXShape, aStartPoint, aShapeA, aEndPoint, aShapeB ); + switch ( eCt ) + { + case drawing::ConnectorType_CURVE : + { + rShapeType = ESCHER_ShpInst_CurvedConnector3; + AddOpt( ESCHER_Prop_cxstyle, ESCHER_cxstyleCurved ); + AddOpt( ESCHER_Prop_adjustValue, nAdjustValue1 ); + AddOpt( ESCHER_Prop_adjust2Value, -static_cast(nAdjustValue2) ); + } + break; + + case drawing::ConnectorType_STANDARD :// Connector 2->5 + { + if ( EscherPropertyValueHelper::GetPropertyValue( aAny, aXPropSet, "PolyPolygonBezier" ) ) + { + tools::PolyPolygon aPolyPolygon = GetPolyPolygon( aAny ); + tools::Polygon aPoly; + if ( aPolyPolygon.Count() > 0 ) + { + AddOpt( ESCHER_Prop_cxstyle, ESCHER_cxstyleBent ); + aPoly = aPolyPolygon[ 0 ]; + sal_Int32 nAdjCount = lcl_GetAdjustValueCount( aPoly ); + rShapeType = static_cast( ESCHER_ShpInst_BentConnector2 + nAdjCount); + for ( sal_Int32 i = 0 ; i < nAdjCount; ++ i) + AddOpt( static_cast( ESCHER_Prop_adjustValue+i) , lcl_GetConnectorAdjustValue( aPoly, i ) ); + } + sal_Int32 nAngle=0; + if (lcl_GetAngle(aPoly,rShapeFlags,nAngle )) + { + AddOpt( ESCHER_Prop_Rotation, nAngle ); + } + } + else + { + rShapeType = ESCHER_ShpInst_BentConnector3; + AddOpt( ESCHER_Prop_cxstyle, ESCHER_cxstyleBent ); + } + } + break; + default: + case drawing::ConnectorType_LINE : + case drawing::ConnectorType_LINES : // Connector 2->5 + { + rShapeType = ESCHER_ShpInst_StraightConnector1; + AddOpt( ESCHER_Prop_cxstyle, ESCHER_cxstyleStraight ); + } + break; + } + CreateLineProperties( aXPropSet, false ); + bRetValue = true; + } + } + } + } + } + return bRetValue; +} + +void EscherPropertyContainer::CreateShadowProperties( + const uno::Reference & rXPropSet ) +{ + uno::Any aAny; + + sal_uInt32 nLineFlags = 0; // default : shape has no line + sal_uInt32 nFillFlags = 0x10; // shape is filled + + GetOpt( ESCHER_Prop_fNoLineDrawDash, nLineFlags ); + GetOpt( ESCHER_Prop_fNoFillHitTest, nFillFlags ); + + sal_uInt32 nDummy; + bool bGraphic = GetOpt( DFF_Prop_pib, nDummy ) || GetOpt( DFF_Prop_pibName, nDummy ) || GetOpt( DFF_Prop_pibFlags, nDummy ); + + sal_uInt32 nShadowFlags = 0x20000; + if ( ( nLineFlags & 8 ) || ( nFillFlags & 0x10 ) || bGraphic ) + { + if ( EscherPropertyValueHelper::GetPropertyValue( aAny, rXPropSet, "Shadow", true ) ) + { + bool bHasShadow = false; // shadow is possible only if at least a fillcolor, linecolor or graphic is set + if ( (aAny >>= bHasShadow) && bHasShadow ) + { + nShadowFlags |= 2; + if ( EscherPropertyValueHelper::GetPropertyValue( aAny, rXPropSet, "ShadowColor" ) ) + AddOpt( ESCHER_Prop_shadowColor, ImplGetColor( *o3tl::doAccess(aAny) ) ); + if ( EscherPropertyValueHelper::GetPropertyValue( aAny, rXPropSet, "ShadowXDistance" ) ) + AddOpt( ESCHER_Prop_shadowOffsetX, *o3tl::doAccess(aAny) * 360 ); + if ( EscherPropertyValueHelper::GetPropertyValue( aAny, rXPropSet, "ShadowYDistance" ) ) + AddOpt( ESCHER_Prop_shadowOffsetY, *o3tl::doAccess(aAny) * 360 ); + if ( EscherPropertyValueHelper::GetPropertyValue( aAny, rXPropSet, "ShadowTransparence" ) ) + AddOpt( ESCHER_Prop_shadowOpacity, 0x10000 - (static_cast(*o3tl::doAccess(aAny)) * 655 ) ); + } + } + } + AddOpt( ESCHER_Prop_fshadowObscured, nShadowFlags ); +} + +sal_Int32 EscherPropertyContainer::GetValueForEnhancedCustomShapeParameter( const drawing::EnhancedCustomShapeParameter& rParameter, + const std::vector< sal_Int32 >& rEquationOrder, bool bAdjustTrans ) +{ + sal_Int32 nValue = 0; + if ( rParameter.Value.getValueTypeClass() == uno::TypeClass_DOUBLE ) + { + double fValue(0.0); + if ( rParameter.Value >>= fValue ) + nValue = static_cast(fValue); + } + else + rParameter.Value >>= nValue; + + switch( rParameter.Type ) + { + case drawing::EnhancedCustomShapeParameterType::EQUATION : + { + size_t nIndex = static_cast(nValue); + OSL_ASSERT(nIndex < rEquationOrder.size()); + if ( nIndex < rEquationOrder.size() ) + { + nValue = static_cast(rEquationOrder[ nIndex ]); + nValue |= sal_uInt32(0x80000000); + } + } + break; + case drawing::EnhancedCustomShapeParameterType::ADJUSTMENT: + { + if(bAdjustTrans) + { + sal_uInt32 nAdjustValue = 0; + bool bGot = GetOpt(static_cast( DFF_Prop_adjustValue + nValue ), nAdjustValue); + if(bGot) nValue = static_cast(nAdjustValue); + } + } + break; + case drawing::EnhancedCustomShapeParameterType::NORMAL : + default: + break; +/* not sure if it is allowed to set following values +(but they are not yet used) + case drawing::EnhancedCustomShapeParameterType::BOTTOM : + case drawing::EnhancedCustomShapeParameterType::RIGHT : + case drawing::EnhancedCustomShapeParameterType::TOP : + case drawing::EnhancedCustomShapeParameterType::LEFT : +*/ + } + return nValue; +} + +static bool GetValueForEnhancedCustomShapeHandleParameter( sal_Int32& nRetValue, const drawing::EnhancedCustomShapeParameter& rParameter ) +{ + bool bSpecial = false; + nRetValue = 0; + if ( rParameter.Value.getValueTypeClass() == uno::TypeClass_DOUBLE ) + { + double fValue(0.0); + if ( rParameter.Value >>= fValue ) + nRetValue = static_cast(fValue); + } + else + rParameter.Value >>= nRetValue; + + switch( rParameter.Type ) + { + case drawing::EnhancedCustomShapeParameterType::EQUATION : + { + nRetValue += 3; + bSpecial = true; + } + break; + case drawing::EnhancedCustomShapeParameterType::ADJUSTMENT : + { + nRetValue += 0x100; + bSpecial = true; + } + break; + case drawing::EnhancedCustomShapeParameterType::TOP : + case drawing::EnhancedCustomShapeParameterType::LEFT : + { + nRetValue = 0; + bSpecial = true; + } + break; + case drawing::EnhancedCustomShapeParameterType::RIGHT : + case drawing::EnhancedCustomShapeParameterType::BOTTOM : + { + nRetValue = 1; + bSpecial = true; + } + break; + case drawing::EnhancedCustomShapeParameterType::NORMAL : + { + + } + break; + } + return bSpecial; +} + +static void ConvertEnhancedCustomShapeEquation( + const SdrObjCustomShape& rSdrObjCustomShape, + std::vector< EnhancedCustomShapeEquation >& rEquations, + std::vector< sal_Int32 >& rEquationOrder ) +{ + uno::Sequence< OUString > sEquationSource; + const SdrCustomShapeGeometryItem& rGeometryItem = + rSdrObjCustomShape.GetMergedItem( SDRATTR_CUSTOMSHAPE_GEOMETRY ); + const uno::Any* pAny = rGeometryItem.GetPropertyValueByName( "Equations" ); + if ( pAny ) + *pAny >>= sEquationSource; + sal_Int32 nEquationSourceCount = sEquationSource.getLength(); + if ( !(nEquationSourceCount && (nEquationSourceCount <= 128)) ) + return; + + sal_Int32 i; + for ( i = 0; i < nEquationSourceCount; i++ ) + { + EnhancedCustomShape2d aCustomShape2d( + const_cast< SdrObjCustomShape& >(rSdrObjCustomShape)); + try + { + std::shared_ptr< EnhancedCustomShape::ExpressionNode > aExpressNode( + EnhancedCustomShape::FunctionParser::parseFunction( + sEquationSource[ i ], aCustomShape2d)); + drawing::EnhancedCustomShapeParameter aPara( aExpressNode->fillNode( rEquations, nullptr, 0 ) ); + if ( aPara.Type != drawing::EnhancedCustomShapeParameterType::EQUATION ) + { + EnhancedCustomShapeEquation aEquation; + aEquation.nOperation = 0; + EnhancedCustomShape::FillEquationParameter( aPara, 0, aEquation ); + rEquations.push_back( aEquation ); + } + } + catch ( const EnhancedCustomShape::ParseError& ) + { + EnhancedCustomShapeEquation aEquation; // ups, we should not be here, + aEquation.nOperation = 0; // creating a default equation with value 1 + aEquation.nPara[ 0 ] = 1; // hoping that this will not break anything + rEquations.push_back( aEquation ); + } + catch ( ... ) + { + EnhancedCustomShapeEquation aEquation; // #i112309# EnhancedCustomShape::Parse error + aEquation.nOperation = 0; // not caught on linux platform + aEquation.nPara[ 0 ] = 1; + rEquations.push_back( aEquation ); + } + rEquationOrder.push_back( rEquations.size() - 1 ); + } + // now updating our old equation indices, they are marked with a bit in the hiword of nOperation + for (auto & equation : rEquations) + { + sal_uInt32 nMask = 0x20000000; + for( i = 0; i < 3; i++ ) + { + if ( equation.nOperation & nMask ) + { + equation.nOperation ^= nMask; + const size_t nIndex(equation.nPara[ i ] & 0x3ff); + + // #i124661# check index access, there are cases where this is out of bound leading + // to errors up to crashes when executed + if(nIndex < rEquationOrder.size()) + { + equation.nPara[ i ] = rEquationOrder[ nIndex ] | 0x400; + } + else + { + OSL_ENSURE(false, "Attempted out of bound access to rEquationOrder of CustomShape (!)"); + } + } + nMask <<= 1; + } + } +} + +bool EscherPropertyContainer::IsDefaultObject( + const SdrObjCustomShape& rSdrObjCustomShape, + const MSO_SPT eShapeType) +{ + switch(eShapeType) + { + // if the custom shape is not default shape of ppt, return false; + case mso_sptTearDrop: + return false; + + default: + break; + } + + return rSdrObjCustomShape.IsDefaultGeometry( SdrObjCustomShape::DefaultType::Equations ) + && rSdrObjCustomShape.IsDefaultGeometry( SdrObjCustomShape::DefaultType::Viewbox ) + && rSdrObjCustomShape.IsDefaultGeometry( SdrObjCustomShape::DefaultType::Path ) + && rSdrObjCustomShape.IsDefaultGeometry( SdrObjCustomShape::DefaultType::Gluepoints ) + && rSdrObjCustomShape.IsDefaultGeometry( SdrObjCustomShape::DefaultType::Segments ) + && rSdrObjCustomShape.IsDefaultGeometry( SdrObjCustomShape::DefaultType::StretchX ) + && rSdrObjCustomShape.IsDefaultGeometry( SdrObjCustomShape::DefaultType::StretchY ) + && rSdrObjCustomShape.IsDefaultGeometry( SdrObjCustomShape::DefaultType::TextFrames ); +} + +void EscherPropertyContainer::LookForPolarHandles( const MSO_SPT eShapeType, sal_Int32& nAdjustmentsWhichNeedsToBeConverted ) +{ + const mso_CustomShape* pDefCustomShape = GetCustomShapeContent( eShapeType ); + if ( !(pDefCustomShape && pDefCustomShape->nHandles && pDefCustomShape->pHandles) ) + return; + + sal_Int32 k, nkCount = pDefCustomShape->nHandles; + const SvxMSDffHandle* pData = pDefCustomShape->pHandles; + for ( k = 0; k < nkCount; k++, pData++ ) + { + if ( pData->nFlags & SvxMSDffHandleFlags::POLAR ) + { + if ( ( pData->nPositionY >= 0x256 ) || ( pData->nPositionY <= 0x107 ) ) + nAdjustmentsWhichNeedsToBeConverted |= ( 1 << k ); + } + } +} + +bool EscherPropertyContainer::GetAdjustmentValue( const drawing::EnhancedCustomShapeAdjustmentValue & rkProp, sal_Int32 nIndex, sal_Int32 nAdjustmentsWhichNeedsToBeConverted, sal_Int32& nValue ) +{ + if ( rkProp.State != beans::PropertyState_DIRECT_VALUE ) + return false; + + bool bUseFixedFloat = ( nAdjustmentsWhichNeedsToBeConverted & ( 1 << nIndex ) ) != 0; + if ( rkProp.Value.getValueTypeClass() == uno::TypeClass_DOUBLE ) + { + double fValue(0.0); + rkProp.Value >>= fValue; + if ( bUseFixedFloat ) + fValue *= 65536.0; + nValue = static_cast(fValue); + } + else + { + rkProp.Value >>= nValue; + if ( bUseFixedFloat ) + nValue <<= 16; + } + + return true; +} + +void EscherPropertyContainer::CreateCustomShapeProperties( const MSO_SPT eShapeType, const uno::Reference< drawing::XShape > & rXShape ) +{ + uno::Reference< beans::XPropertySet > aXPropSet( rXShape, uno::UNO_QUERY ); + if ( !aXPropSet.is() ) + return; + + SdrObjCustomShape* pSdrObjCustomShape = dynamic_cast< SdrObjCustomShape* >(SdrObject::getSdrObjectFromXShape(rXShape)); + if(!pSdrObjCustomShape) + { + return; + } + + SdrObjCustomShape& rSdrObjCustomShape = *pSdrObjCustomShape; + uno::Any aGeoPropSet = aXPropSet->getPropertyValue( "CustomShapeGeometry" ); + uno::Sequence< beans::PropertyValue > aGeoPropSeq; + if ( !(aGeoPropSet >>= aGeoPropSeq) ) + return; + + static const OUStringLiteral sViewBox ( u"ViewBox" ); + static const OUStringLiteral sTextRotateAngle ( u"TextRotateAngle" ); + static const OUStringLiteral sExtrusion ( u"Extrusion" ); + static const OUStringLiteral sEquations ( u"Equations" ); + static const OUStringLiteral sPath ( u"Path" ); + static const OUStringLiteral sTextPath ( u"TextPath" ); + static const OUStringLiteral sHandles ( u"Handles" ); + static const OUStringLiteral sAdjustmentValues ( u"AdjustmentValues" ); + + bool bAdjustmentValuesProp = false; + uno::Any aAdjustmentValuesProp; + bool bPathCoordinatesProp = false; + uno::Any aPathCoordinatesProp; + + sal_Int32 nAdjustmentsWhichNeedsToBeConverted = 0; + uno::Sequence< beans::PropertyValues > aHandlesPropSeq; + bool bPredefinedHandlesUsed = true; + const bool bIsDefaultObject( + IsDefaultObject( + rSdrObjCustomShape, + eShapeType)); + + // convert property "Equations" into std::vector< EnhancedCustomShapeEquationEquation > + std::vector< EnhancedCustomShapeEquation > aEquations; + std::vector< sal_Int32 > aEquationOrder; + ConvertEnhancedCustomShapeEquation( + rSdrObjCustomShape, + aEquations, + aEquationOrder); + + sal_Int32 i, nCount = aGeoPropSeq.getLength(); + for ( i = 0; i < nCount; i++ ) + { + const beans::PropertyValue& rProp = aGeoPropSeq[ i ]; + if ( rProp.Name == sViewBox ) + { + if ( !bIsDefaultObject ) + { + awt::Rectangle aViewBox; + if ( rProp.Value >>= aViewBox ) + { + AddOpt( DFF_Prop_geoLeft, aViewBox.X ); + AddOpt( DFF_Prop_geoTop, aViewBox.Y ); + AddOpt( DFF_Prop_geoRight, aViewBox.X + aViewBox.Width ); + AddOpt( DFF_Prop_geoBottom,aViewBox.Y + aViewBox.Height ); + } + } + } + else if ( rProp.Name == sTextRotateAngle ) + { + double f = 0; + if ( rProp.Value >>= f ) + { + double fTextRotateAngle = fmod( f, 360.0 ); + if ( fTextRotateAngle < 0 ) + fTextRotateAngle = 360 + fTextRotateAngle; + if ( ( fTextRotateAngle < 271.0 ) && ( fTextRotateAngle > 269.0 ) ) + AddOpt( DFF_Prop_cdirFont, mso_cdir90 ); + else if ( ( fTextRotateAngle < 181.0 ) && ( fTextRotateAngle > 179.0 ) ) + AddOpt( DFF_Prop_cdirFont, mso_cdir180 ); + else if ( ( fTextRotateAngle < 91.0 ) && ( fTextRotateAngle > 79.0 ) ) + AddOpt( DFF_Prop_cdirFont, mso_cdir270 ); + } + } + else if ( rProp.Name == sExtrusion ) + { + uno::Sequence< beans::PropertyValue > aExtrusionPropSeq; + if ( rProp.Value >>= aExtrusionPropSeq ) + { + sal_uInt32 nLightFaceFlagsOrg, nLightFaceFlags; + sal_uInt32 nFillHarshFlagsOrg, nFillHarshFlags; + nLightFaceFlagsOrg = nLightFaceFlags = 0x000001; + nFillHarshFlagsOrg = nFillHarshFlags = 0x00001e; + if ( GetOpt( DFF_Prop_fc3DLightFace, nLightFaceFlags ) ) + nLightFaceFlagsOrg = nLightFaceFlags; + if ( GetOpt( DFF_Prop_fc3DFillHarsh, nFillHarshFlags ) ) + nFillHarshFlagsOrg = nFillHarshFlags; + + sal_Int32 r, nrCount = aExtrusionPropSeq.getLength(); + for ( r = 0; r < nrCount; r++ ) + { + const beans::PropertyValue& rrProp = aExtrusionPropSeq[ r ]; + + if ( rrProp.Name == sExtrusion ) + { + bool bExtrusionOn; + if ( rrProp.Value >>= bExtrusionOn ) + { + nLightFaceFlags |= 0x80000; + if ( bExtrusionOn ) + nLightFaceFlags |= 8; + else + nLightFaceFlags &=~8; + } + } + else if ( rrProp.Name == "Brightness" ) + { + double fExtrusionBrightness = 0; + if ( rrProp.Value >>= fExtrusionBrightness ) + AddOpt( DFF_Prop_c3DAmbientIntensity, static_cast( fExtrusionBrightness * 655.36 ) ); + } + else if ( rrProp.Name == "Depth" ) + { + double fDepth = 0; + double fFraction = 0; + drawing::EnhancedCustomShapeParameterPair aDepthParaPair; + if ( ( rrProp.Value >>= aDepthParaPair ) && ( aDepthParaPair.First.Value >>= fDepth ) && ( aDepthParaPair.Second.Value >>= fFraction ) ) + { + double fForeDepth = fDepth * fFraction; + double fBackDepth = fDepth - fForeDepth; + + fBackDepth *= 360.0; + AddOpt( DFF_Prop_c3DExtrudeBackward, static_cast(fBackDepth) ); + + if ( fForeDepth != 0.0 ) + { + fForeDepth *= 360.0; + AddOpt( DFF_Prop_c3DExtrudeForward, static_cast(fForeDepth) ); + } + } + } + else if ( rrProp.Name == "Diffusion" ) + { + double fExtrusionDiffusion = 0; + if ( rrProp.Value >>= fExtrusionDiffusion ) + AddOpt( DFF_Prop_c3DDiffuseAmt, static_cast( fExtrusionDiffusion * 655.36 ) ); + } + else if ( rrProp.Name == "NumberOfLineSegments" ) + { + sal_Int32 nExtrusionNumberOfLineSegments = 0; + if ( rrProp.Value >>= nExtrusionNumberOfLineSegments ) + AddOpt( DFF_Prop_c3DTolerance, nExtrusionNumberOfLineSegments ); + } + else if ( rrProp.Name == "LightFace" ) + { + bool bExtrusionLightFace; + if ( rrProp.Value >>= bExtrusionLightFace ) + { + nLightFaceFlags |= 0x10000; + if ( bExtrusionLightFace ) + nLightFaceFlags |= 1; + else + nLightFaceFlags &=~1; + } + } + else if ( rrProp.Name == "FirstLightHarsh" ) + { + bool bExtrusionFirstLightHarsh; + if ( rrProp.Value >>= bExtrusionFirstLightHarsh ) + { + nFillHarshFlags |= 0x20000; + if ( bExtrusionFirstLightHarsh ) + nFillHarshFlags |= 2; + else + nFillHarshFlags &=~2; + } + } + else if ( rrProp.Name == "SecondLightHarsh" ) + { + bool bExtrusionSecondLightHarsh; + if ( rrProp.Value >>= bExtrusionSecondLightHarsh ) + { + nFillHarshFlags |= 0x10000; + if ( bExtrusionSecondLightHarsh ) + nFillHarshFlags |= 1; + else + nFillHarshFlags &=~1; + } + } + else if ( rrProp.Name == "FirstLightLevel" ) + { + double fExtrusionFirstLightLevel = 0; + if ( rrProp.Value >>= fExtrusionFirstLightLevel ) + AddOpt( DFF_Prop_c3DKeyIntensity, static_cast( fExtrusionFirstLightLevel * 655.36 ) ); + } + else if ( rrProp.Name == "SecondLightLevel" ) + { + double fExtrusionSecondLightLevel = 0; + if ( rrProp.Value >>= fExtrusionSecondLightLevel ) + AddOpt( DFF_Prop_c3DFillIntensity, static_cast( fExtrusionSecondLightLevel * 655.36 ) ); + } + else if ( rrProp.Name == "FirstLightDirection" ) + { + drawing::Direction3D aExtrusionFirstLightDirection; + if ( rrProp.Value >>= aExtrusionFirstLightDirection ) + { + AddOpt( DFF_Prop_c3DKeyX, static_cast(aExtrusionFirstLightDirection.DirectionX) ); + AddOpt( DFF_Prop_c3DKeyY, static_cast(aExtrusionFirstLightDirection.DirectionY) ); + AddOpt( DFF_Prop_c3DKeyZ, static_cast(aExtrusionFirstLightDirection.DirectionZ) ); + } + } + else if ( rrProp.Name == "SecondLightDirection" ) + { + drawing::Direction3D aExtrusionSecondLightPosition; + if ( rrProp.Value >>= aExtrusionSecondLightPosition ) + { + AddOpt( DFF_Prop_c3DFillX, static_cast(aExtrusionSecondLightPosition.DirectionX) ); + AddOpt( DFF_Prop_c3DFillY, static_cast(aExtrusionSecondLightPosition.DirectionY) ); + AddOpt( DFF_Prop_c3DFillZ, static_cast(aExtrusionSecondLightPosition.DirectionZ) ); + } + } + else if ( rrProp.Name == "Metal" ) + { + bool bExtrusionMetal; + if ( rrProp.Value >>= bExtrusionMetal ) + { + nLightFaceFlags |= 0x40000; + if ( bExtrusionMetal ) + nLightFaceFlags |= 4; + else + nLightFaceFlags &=~4; + } + } + else if ( rrProp.Name == "ShadeMode" ) + { + drawing::ShadeMode eExtrusionShadeMode; + if ( rrProp.Value >>= eExtrusionShadeMode ) + { + sal_uInt32 nRenderMode; + switch( eExtrusionShadeMode ) + { + default: + case drawing::ShadeMode_FLAT : + case drawing::ShadeMode_PHONG : + case drawing::ShadeMode_SMOOTH : + nRenderMode = mso_FullRender; + break; + case drawing::ShadeMode_DRAFT : + { + nRenderMode = mso_Wireframe; + } + break; + } + AddOpt( DFF_Prop_c3DRenderMode, nRenderMode ); + } + } + else if ( rrProp.Name == "RotateAngle" ) + { + double fExtrusionAngleX = 0; + double fExtrusionAngleY = 0; + drawing::EnhancedCustomShapeParameterPair aRotateAnglePair; + if ( ( rrProp.Value >>= aRotateAnglePair ) && ( aRotateAnglePair.First.Value >>= fExtrusionAngleX ) && ( aRotateAnglePair.Second.Value >>= fExtrusionAngleY ) ) + { + fExtrusionAngleX *= 65536; + fExtrusionAngleY *= 65536; + AddOpt( DFF_Prop_c3DXRotationAngle, static_cast(fExtrusionAngleX) ); + AddOpt( DFF_Prop_c3DYRotationAngle, static_cast(fExtrusionAngleY) ); + } + } + else if ( rrProp.Name == "RotationCenter" ) + { + drawing::Direction3D aExtrusionRotationCenter; + if ( rrProp.Value >>= aExtrusionRotationCenter ) + { + // tdf#145904 X- and Y-component is fraction, Z-component in EMU + AddOpt( DFF_Prop_c3DRotationCenterX, static_cast( aExtrusionRotationCenter.DirectionX * 65536.0 ) ); + AddOpt( DFF_Prop_c3DRotationCenterY, static_cast( aExtrusionRotationCenter.DirectionY * 65536.0 ) ); + AddOpt( DFF_Prop_c3DRotationCenterZ, static_cast( aExtrusionRotationCenter.DirectionZ * 360.0 ) ); + nFillHarshFlags &=~8; // don't use AutoRotationCenter; + } + } + else if ( rrProp.Name == "Shininess" ) + { + double fExtrusionShininess = 0; + if ( rrProp.Value >>= fExtrusionShininess ) + { + // ODF to MS Office conversion invers to msdffimp.cxx + fExtrusionShininess = basegfx::fround(fExtrusionShininess / 10.0); + AddOpt( DFF_Prop_c3DShininess, static_cast(fExtrusionShininess) ); + } + } + else if ( rrProp.Name == "Skew" ) + { + double fSkewAmount = 0; + double fSkewAngle = 0; + drawing::EnhancedCustomShapeParameterPair aSkewParaPair; + if ( ( rrProp.Value >>= aSkewParaPair ) && ( aSkewParaPair.First.Value >>= fSkewAmount ) && ( aSkewParaPair.Second.Value >>= fSkewAngle ) ) + { + AddOpt( DFF_Prop_c3DSkewAmount, static_cast(fSkewAmount) ); + AddOpt( DFF_Prop_c3DSkewAngle, static_cast( fSkewAngle * 65536 ) ); + } + } + else if ( rrProp.Name == "Specularity" ) + { + double fExtrusionSpecularity = 0; + if ( rrProp.Value >>= fExtrusionSpecularity ) + AddOpt( DFF_Prop_c3DSpecularAmt, static_cast( fExtrusionSpecularity * 655.36 ) ); + } + else if ( rrProp.Name == "ProjectionMode" ) + { + drawing::ProjectionMode eExtrusionProjectionMode; + if ( rrProp.Value >>= eExtrusionProjectionMode ) + { + nFillHarshFlags |= 0x40000; + if ( eExtrusionProjectionMode == drawing::ProjectionMode_PARALLEL ) + nFillHarshFlags |= 4; + else + nFillHarshFlags &=~4; + } + } + else if ( rrProp.Name == "ViewPoint" ) + { + drawing::Position3D aExtrusionViewPoint; + if ( rrProp.Value >>= aExtrusionViewPoint ) + { + aExtrusionViewPoint.PositionX *= 360.0; + aExtrusionViewPoint.PositionY *= 360.0; + aExtrusionViewPoint.PositionZ *= 360.0; + AddOpt( DFF_Prop_c3DXViewpoint, static_cast(aExtrusionViewPoint.PositionX) ); + AddOpt( DFF_Prop_c3DYViewpoint, static_cast(aExtrusionViewPoint.PositionY) ); + AddOpt( DFF_Prop_c3DZViewpoint, static_cast(aExtrusionViewPoint.PositionZ) ); + } + } + else if ( rrProp.Name == "Origin" ) + { + double fExtrusionOriginX = 0; + double fExtrusionOriginY = 0; + drawing::EnhancedCustomShapeParameterPair aOriginPair; + if ( ( rrProp.Value >>= aOriginPair ) && ( aOriginPair.First.Value >>= fExtrusionOriginX ) && ( aOriginPair.Second.Value >>= fExtrusionOriginY ) ) + { + AddOpt( DFF_Prop_c3DOriginX, static_cast( fExtrusionOriginX * 65536 ) ); + AddOpt( DFF_Prop_c3DOriginY, static_cast( fExtrusionOriginY * 65536 ) ); + } + } + else if ( rrProp.Name == "Color" ) + { + bool bExtrusionColor; + if ( rrProp.Value >>= bExtrusionColor ) + { + nLightFaceFlags |= 0x20000; + if ( bExtrusionColor ) + { + nLightFaceFlags |= 2; + uno::Any aFillColor2; + if ( EscherPropertyValueHelper::GetPropertyValue( aFillColor2, aXPropSet, "FillColor2", true ) ) + { + sal_uInt32 nFillColor = ImplGetColor( *o3tl::doAccess(aFillColor2) ); + AddOpt( DFF_Prop_c3DExtrusionColor, nFillColor ); + } + } + else + nLightFaceFlags &=~2; + } + } + } + if ( nLightFaceFlags != nLightFaceFlagsOrg ) + AddOpt( DFF_Prop_fc3DLightFace, nLightFaceFlags ); + if ( nFillHarshFlags != nFillHarshFlagsOrg ) + AddOpt( DFF_Prop_fc3DFillHarsh, nFillHarshFlags ); + } + } + else if ( rProp.Name == sEquations ) + { + if ( !bIsDefaultObject ) + { + sal_uInt16 nElements = static_cast(aEquations.size()); + if ( nElements ) + { + sal_uInt16 nElementSize = 8; + sal_uInt32 nStreamSize = nElementSize * nElements + 6; + SvMemoryStream aMemStrm( nStreamSize ); + aMemStrm.WriteUInt16( nElements ) + .WriteUInt16( nElements ) + .WriteUInt16( nElementSize ); + + for (auto const& equation : aEquations) + { + aMemStrm.WriteUInt16( equation.nOperation ) + .WriteInt16( + std::clamp( + equation.nPara[ 0 ], sal_Int32(SAL_MIN_INT16), + sal_Int32(SAL_MAX_INT16)) ) + .WriteInt16( + std::clamp( + equation.nPara[ 1 ], sal_Int32(SAL_MIN_INT16), + sal_Int32(SAL_MAX_INT16)) ) + .WriteInt16( + std::clamp( + equation.nPara[ 2 ], sal_Int32(SAL_MIN_INT16), + sal_Int32(SAL_MAX_INT16)) ); + } + + AddOpt(DFF_Prop_pFormulas, true, 6, aMemStrm); + } + else + { + AddOpt(DFF_Prop_pFormulas, 0, true); + } + } + } + else if ( rProp.Name == sPath ) + { + uno::Sequence< beans::PropertyValue > aPathPropSeq; + if ( rProp.Value >>= aPathPropSeq ) + { + sal_uInt32 nPathFlags, nPathFlagsOrg; + nPathFlagsOrg = nPathFlags = 0x39; + if ( GetOpt( DFF_Prop_fFillOK, nPathFlags ) ) + nPathFlagsOrg = nPathFlags; + + sal_Int32 r, nrCount = aPathPropSeq.getLength(); + for ( r = 0; r < nrCount; r++ ) + { + const beans::PropertyValue& rrProp = aPathPropSeq[ r ]; + + if ( rrProp.Name == "ExtrusionAllowed" ) + { + bool bExtrusionAllowed; + if ( rrProp.Value >>= bExtrusionAllowed ) + { + nPathFlags |= 0x100000; + if ( bExtrusionAllowed ) + nPathFlags |= 16; + else + nPathFlags &=~16; + } + } + else if ( rrProp.Name == "ConcentricGradientFillAllowed" ) + { + bool bConcentricGradientFillAllowed; + if ( rrProp.Value >>= bConcentricGradientFillAllowed ) + { + nPathFlags |= 0x20000; + if ( bConcentricGradientFillAllowed ) + nPathFlags |= 2; + else + nPathFlags &=~2; + } + } + else if ( rrProp.Name == "TextPathAllowed" ) + { + bool bTextPathAllowed; + if ( rrProp.Value >>= bTextPathAllowed ) + { + nPathFlags |= 0x40000; + if ( bTextPathAllowed ) + nPathFlags |= 4; + else + nPathFlags &=~4; + } + } + else if ( rrProp.Name == "Coordinates" ) + { + if ( !bIsDefaultObject ) + { + aPathCoordinatesProp = rrProp.Value; + bPathCoordinatesProp = true; + } + } + else if ( rrProp.Name == "GluePoints" ) + { + if ( !bIsDefaultObject ) + { + uno::Sequence aGluePoints; + if ( rrProp.Value >>= aGluePoints ) + { + // creating the vertices + sal_uInt16 nElements = static_cast(aGluePoints.getLength()); + if ( nElements ) + { + sal_uInt16 j, nElementSize = 8; + sal_uInt32 nStreamSize = nElementSize * nElements + 6; + SvMemoryStream aMemStrm( nStreamSize ); + aMemStrm.WriteUInt16( nElements ) + .WriteUInt16( nElements ) + .WriteUInt16( nElementSize ); + for( j = 0; j < nElements; j++ ) + { + sal_Int32 X = GetValueForEnhancedCustomShapeParameter( aGluePoints[ j ].First, aEquationOrder ); + sal_Int32 Y = GetValueForEnhancedCustomShapeParameter( aGluePoints[ j ].Second, aEquationOrder ); + aMemStrm.WriteInt32( X ) + .WriteInt32( Y ); + } + + AddOpt(DFF_Prop_connectorPoints, true, 6, aMemStrm); // -6 + } + else + { + AddOpt(DFF_Prop_connectorPoints, 0, true); + } + } + } + } + else if ( rrProp.Name == "GluePointType" ) + { + sal_Int16 nGluePointType = sal_Int16(); + if ( rrProp.Value >>= nGluePointType ) + AddOpt( DFF_Prop_connectorType, static_cast(nGluePointType) ); + } + else if ( rrProp.Name == "Segments" ) + { + if ( !bIsDefaultObject ) + { + uno::Sequence aSegments; + if ( rrProp.Value >>= aSegments ) + { + // creating seginfo + if ( aSegments.hasElements() ) + { + sal_uInt16 j, nElements = static_cast(aSegments.getLength()); + sal_uInt16 nElementSize = 2; + sal_uInt32 nStreamSize = nElementSize * nElements + 6; + SvMemoryStream aMemStrm( nStreamSize ); + aMemStrm.WriteUInt16( nElements ) + .WriteUInt16( nElements ) + .WriteUInt16( nElementSize ); + for ( j = 0; j < nElements; j++ ) + { + // The segment type is stored in the upper 3 bits + // and segment count is stored in the lower 13 + // bits. + // + // If the segment type is msopathEscape, the lower 13 bits + // are divided in a 5 bit escape code and 8 bit + // vertex count (not segment count!) + sal_uInt16 nVal = static_cast(aSegments[ j ].Count); + switch( aSegments[ j ].Command ) + { + case drawing::EnhancedCustomShapeSegmentCommand::UNKNOWN : + case drawing::EnhancedCustomShapeSegmentCommand::LINETO : + break; + case drawing::EnhancedCustomShapeSegmentCommand::MOVETO : + nVal = (msopathMoveTo << 13); + break; + case drawing::EnhancedCustomShapeSegmentCommand::CURVETO : + { + nVal |= (msopathCurveTo << 13); + } + break; + case drawing::EnhancedCustomShapeSegmentCommand::CLOSESUBPATH : + { + nVal = 1; + nVal |= (msopathClose << 13); + } + break; + case drawing::EnhancedCustomShapeSegmentCommand::ENDSUBPATH : + { + nVal = (msopathEnd << 13); + } + break; + case drawing::EnhancedCustomShapeSegmentCommand::NOFILL : + { + nVal = (msopathEscape << 13) | (10 << 8); + } + break; + case drawing::EnhancedCustomShapeSegmentCommand::NOSTROKE : + { + nVal = (msopathEscape << 13) | (11 << 8); + } + break; + case drawing::EnhancedCustomShapeSegmentCommand::ANGLEELLIPSETO : + { + nVal *= 3; + nVal |= (msopathEscape << 13) | (1 << 8); + } + break; + case drawing::EnhancedCustomShapeSegmentCommand::ANGLEELLIPSE : + { + nVal *= 3; + nVal |= (msopathEscape << 13) | (2 << 8); + } + break; + case drawing::EnhancedCustomShapeSegmentCommand::ARCTO : + { + nVal <<= 2; + nVal |= (msopathEscape << 13) | (3 << 8); + } + break; + case drawing::EnhancedCustomShapeSegmentCommand::ARC : + { + nVal <<= 2; + nVal |= (msopathEscape << 13) | (4 << 8); + } + break; + case drawing::EnhancedCustomShapeSegmentCommand::CLOCKWISEARCTO : + { + nVal <<= 2; + nVal |= (msopathEscape << 13) | (5 << 8); + } + break; + case drawing::EnhancedCustomShapeSegmentCommand::CLOCKWISEARC : + { + nVal <<= 2; + nVal |= (msopathEscape << 13) | (6 << 8); + } + break; + case drawing::EnhancedCustomShapeSegmentCommand::ELLIPTICALQUADRANTX : + { + nVal |= (msopathEscape << 13) | (7 << 8); + } + break; + case drawing::EnhancedCustomShapeSegmentCommand::ELLIPTICALQUADRANTY : + { + nVal |= (msopathEscape << 13) | (8 << 8); + } + break; + } + aMemStrm.WriteUInt16( nVal ); + } + + AddOpt(DFF_Prop_pSegmentInfo, false, 6, aMemStrm); + } + else + { + AddOpt(DFF_Prop_pSegmentInfo, 0, true); + } + } + } + } + else if ( rrProp.Name == "StretchX" ) + { + if ( !bIsDefaultObject ) + { + sal_Int32 nStretchX = 0; + if ( rrProp.Value >>= nStretchX ) + AddOpt( DFF_Prop_stretchPointX, nStretchX ); + } + } + else if ( rrProp.Name == "StretchY" ) + { + if ( !bIsDefaultObject ) + { + sal_Int32 nStretchY = 0; + if ( rrProp.Value >>= nStretchY ) + AddOpt( DFF_Prop_stretchPointY, nStretchY ); + } + } + else if ( rrProp.Name == "TextFrames" ) + { + if ( !bIsDefaultObject ) + { + uno::Sequence aPathTextFrames; + if ( rrProp.Value >>= aPathTextFrames ) + { + if ( aPathTextFrames.hasElements() ) + { + sal_uInt16 j, nElements = static_cast(aPathTextFrames.getLength()); + sal_uInt16 nElementSize = 16; + sal_uInt32 nStreamSize = nElementSize * nElements + 6; + SvMemoryStream aMemStrm( nStreamSize ); + aMemStrm.WriteUInt16( nElements ) + .WriteUInt16( nElements ) + .WriteUInt16( nElementSize ); + for ( j = 0; j < nElements; j++ ) + { + sal_Int32 nLeft = GetValueForEnhancedCustomShapeParameter( aPathTextFrames[ j ].TopLeft.First, aEquationOrder ); + sal_Int32 nTop = GetValueForEnhancedCustomShapeParameter( aPathTextFrames[ j ].TopLeft.Second, aEquationOrder ); + sal_Int32 nRight = GetValueForEnhancedCustomShapeParameter( aPathTextFrames[ j ].BottomRight.First, aEquationOrder ); + sal_Int32 nBottom = GetValueForEnhancedCustomShapeParameter( aPathTextFrames[ j ].BottomRight.Second, aEquationOrder ); + + aMemStrm.WriteInt32( nLeft ) + .WriteInt32( nTop ) + .WriteInt32( nRight ) + .WriteInt32( nBottom ); + } + + AddOpt(DFF_Prop_textRectangles, true, 6, aMemStrm); + } + else + { + AddOpt(DFF_Prop_textRectangles, 0, true); + } + } + } + } + } + if ( nPathFlags != nPathFlagsOrg ) + AddOpt( DFF_Prop_fFillOK, nPathFlags ); + } + } + else if ( rProp.Name == sTextPath ) + { + uno::Sequence< beans::PropertyValue > aTextPathPropSeq; + if ( rProp.Value >>= aTextPathPropSeq ) + { + sal_uInt32 nTextPathFlagsOrg, nTextPathFlags; + nTextPathFlagsOrg = nTextPathFlags = 0xffff1000; // default + if ( GetOpt( DFF_Prop_gtextFStrikethrough, nTextPathFlags ) ) + nTextPathFlagsOrg = nTextPathFlags; + + sal_Int32 r, nrCount = aTextPathPropSeq.getLength(); + for ( r = 0; r < nrCount; r++ ) + { + const beans::PropertyValue& rrProp = aTextPathPropSeq[ r ]; + + if ( rrProp.Name == sTextPath ) + { + bool bTextPathOn; + if ( rrProp.Value >>= bTextPathOn ) + { + nTextPathFlags |= 0x40000000; + if ( bTextPathOn ) + { + nTextPathFlags |= 0x4000; + + sal_uInt32 nPathFlags = 0x39; + GetOpt( DFF_Prop_fFillOK, nPathFlags ); // SJ: can be removed if we are supporting the TextPathAllowed property in XML + nPathFlags |= 0x40004; + AddOpt( DFF_Prop_fFillOK, nPathFlags ); + } + else + nTextPathFlags &=~0x4000; + } + } + else if ( rrProp.Name == "TextPathMode" ) + { + drawing::EnhancedCustomShapeTextPathMode eTextPathMode; + if ( rrProp.Value >>= eTextPathMode ) + { + nTextPathFlags |= 0x05000000; + nTextPathFlags &=~0x500; // TextPathMode_NORMAL + if ( eTextPathMode == drawing::EnhancedCustomShapeTextPathMode_PATH ) + nTextPathFlags |= 0x100; + else if ( eTextPathMode == drawing::EnhancedCustomShapeTextPathMode_SHAPE ) + nTextPathFlags |= 0x500; + } + } + else if ( rrProp.Name == "ScaleX" ) + { + bool bTextPathScaleX; + if ( rrProp.Value >>= bTextPathScaleX ) + { + nTextPathFlags |= 0x00400000; + if ( bTextPathScaleX ) + nTextPathFlags |= 0x40; + else + nTextPathFlags &=~0x40; + } + } + else if ( rrProp.Name == "SameLetterHeights" ) + { + bool bSameLetterHeights; + if ( rrProp.Value >>= bSameLetterHeights ) + { + nTextPathFlags |= 0x00800000; + if ( bSameLetterHeights ) + nTextPathFlags |= 0x80; + else + nTextPathFlags &=~0x80; + } + } + } + if ( nTextPathFlags & 0x4000 ) // Is FontWork ? + { + // FontWork Text + OUString aText; + uno::Reference< text::XSimpleText > xText( rXShape, uno::UNO_QUERY ); + if ( xText.is() ) + aText = xText->getString(); + if ( aText.isEmpty() ) + aText = "your text"; // TODO: moving into a resource + AddOpt( DFF_Prop_gtextUNICODE, aText ); + + // FontWork Font + OUString aFontName; + uno::Any aAny = aXPropSet->getPropertyValue( "CharFontName" ); + aAny >>= aFontName; + if ( aFontName.isEmpty() ) + aFontName = "Arial Black"; + AddOpt( DFF_Prop_gtextFont, aFontName ); + + if ( EscherPropertyValueHelper::GetPropertyValue( aAny, aXPropSet, "CharScaleWidth", true ) ) + { + sal_Int16 nCharScaleWidth = 100; + if ( aAny >>= nCharScaleWidth ) + { + if ( nCharScaleWidth != 100 ) + { + sal_Int32 nVal = nCharScaleWidth * 655; + AddOpt( DFF_Prop_gtextSpacing, nVal ); + } + } + } + if ( EscherPropertyValueHelper::GetPropertyValue( aAny, aXPropSet, "CharHeight", true ) ) + { + float fCharHeight = 0.0; + if ( aAny >>= fCharHeight ) + { + sal_Int32 nTextSize = static_cast< sal_Int32 > ( fCharHeight * 65536 ); + AddOpt(ESCHER_Prop_gtextSize, nTextSize); + } + } + if ( EscherPropertyValueHelper::GetPropertyValue( aAny, aXPropSet, "CharKerning", true ) ) + { + sal_Int16 nCharKerning = sal_Int16(); + if ( aAny >>= nCharKerning ) + { + nTextPathFlags |= 0x10000000; + if ( nCharKerning ) + nTextPathFlags |= 0x1000; + else + nTextPathFlags &=~0x1000; + } + } + if ( EscherPropertyValueHelper::GetPropertyValue( aAny, aXPropSet, "CharPosture", true ) ) + { + awt::FontSlant eFontSlant; + if ( aAny >>= eFontSlant ) + { + nTextPathFlags |= 0x100010; + if ( eFontSlant != awt::FontSlant_NONE ) + nTextPathFlags |= 0x10; + else + nTextPathFlags &=~0x10; + } + } + if ( EscherPropertyValueHelper::GetPropertyValue( aAny, aXPropSet, "CharWeight", true ) ) + { + float fFontWidth = 0; + if ( aAny >>= fFontWidth ) + { + nTextPathFlags |= 0x200020; + if ( fFontWidth > awt::FontWeight::NORMAL ) + nTextPathFlags |= 0x20; + else + nTextPathFlags &=~0x20; + } + } + // export gTextAlign attr + if ( EscherPropertyValueHelper::GetPropertyValue( aAny, aXPropSet, "TextHorizontalAdjust", true ) ) + { + MSO_GeoTextAlign gTextAlign = mso_alignTextCenter; + drawing::TextHorizontalAdjust eHA( drawing::TextHorizontalAdjust_LEFT ); + aAny >>= eHA; + switch( eHA ) + { + case drawing::TextHorizontalAdjust_LEFT : + gTextAlign = mso_alignTextLeft; + break; + case drawing::TextHorizontalAdjust_CENTER: + gTextAlign = mso_alignTextCenter; + break; + case drawing::TextHorizontalAdjust_RIGHT: + gTextAlign = mso_alignTextRight; + break; + case drawing::TextHorizontalAdjust_BLOCK: + { + drawing::TextFitToSizeType const eFTS( + rSdrObjCustomShape.GetMergedItem( SDRATTR_TEXT_FITTOSIZE ).GetValue() ); + if (eFTS == drawing::TextFitToSizeType_ALLLINES || + eFTS == drawing::TextFitToSizeType_PROPORTIONAL) + { + gTextAlign = mso_alignTextStretch; + } + else + { + gTextAlign = mso_alignTextWordJust; + } + break; + } + default: + break; + } + AddOpt(DFF_Prop_gtextAlign,gTextAlign); + } + } + if((nTextPathFlags & 0x4000) != 0) // Is Font work + { + OutlinerParaObject* pOutlinerParaObject(rSdrObjCustomShape.GetOutlinerParaObject()); + if ( pOutlinerParaObject && pOutlinerParaObject->IsEffectivelyVertical() ) + nTextPathFlags |= 0x2000; + } + + // Use gtextFStretch for Watermark like MSO does + nTextPathFlags |= use_gtextFBestFit | gtextFBestFit + | use_gtextFStretch | gtextFStretch + | use_gtextFShrinkFit | gtextFShrinkFit; + + if ( nTextPathFlags != nTextPathFlagsOrg ) + AddOpt( DFF_Prop_gtextFStrikethrough, nTextPathFlags ); + } + } + else if ( rProp.Name == sHandles ) + { + if ( !bIsDefaultObject ) + { + bPredefinedHandlesUsed = false; + if ( rProp.Value >>= aHandlesPropSeq ) + { + sal_uInt16 nElements = static_cast(aHandlesPropSeq.getLength()); + if ( nElements ) + { + sal_uInt16 k, nElementSize = 36; + sal_uInt32 nStreamSize = nElementSize * nElements + 6; + SvMemoryStream aMemStrm( nStreamSize ); + aMemStrm.WriteUInt16( nElements ) + .WriteUInt16( nElements ) + .WriteUInt16( nElementSize ); + + for ( k = 0; k < nElements; k++ ) + { + sal_uInt32 nFlags = 0; + sal_Int32 nXPosition = 0; + sal_Int32 nYPosition = 0; + sal_Int32 nXMap = 0; + sal_Int32 nYMap = 0; + sal_Int32 nXRangeMin = 0x80000000; + sal_Int32 nXRangeMax = 0x7fffffff; + sal_Int32 nYRangeMin = 0x80000000; + sal_Int32 nYRangeMax = 0x7fffffff; + + const uno::Sequence< beans::PropertyValue >& rPropSeq = aHandlesPropSeq[ k ]; + for ( const beans::PropertyValue& rPropVal: rPropSeq ) + { + if ( rPropVal.Name == "Position" ) + { + drawing::EnhancedCustomShapeParameterPair aPosition; + if ( rPropVal.Value >>= aPosition ) + { + GetValueForEnhancedCustomShapeHandleParameter( nXPosition, aPosition.First ); + GetValueForEnhancedCustomShapeHandleParameter( nYPosition, aPosition.Second ); + } + } + else if ( rPropVal.Name == "MirroredX" ) + { + bool bMirroredX; + if ( rPropVal.Value >>= bMirroredX ) + { + if ( bMirroredX ) + nFlags |= 1; + } + } + else if ( rPropVal.Name == "MirroredY" ) + { + bool bMirroredY; + if ( rPropVal.Value >>= bMirroredY ) + { + if ( bMirroredY ) + nFlags |= 2; + } + } + else if ( rPropVal.Name == "Switched" ) + { + bool bSwitched; + if ( rPropVal.Value >>= bSwitched ) + { + if ( bSwitched ) + nFlags |= 4; + } + } + else if ( rPropVal.Name == "Polar" ) + { + drawing::EnhancedCustomShapeParameterPair aPolar; + if ( rPropVal.Value >>= aPolar ) + { + if ( GetValueForEnhancedCustomShapeHandleParameter( nXMap, aPolar.First ) ) + nFlags |= 0x800; + if ( GetValueForEnhancedCustomShapeHandleParameter( nYMap, aPolar.Second ) ) + nFlags |= 0x1000; + nFlags |= 8; + } + } + else if ( rPropVal.Name == "RadiusRangeMinimum" ) + { + nYRangeMin = sal_Int32(0xff4c0000); // the range of angles seems to be a not + nYRangeMax = sal_Int32(0x00b40000); // used feature, so we are defaulting this + + drawing::EnhancedCustomShapeParameter aRadiusRangeMinimum; + if ( rPropVal.Value >>= aRadiusRangeMinimum ) + { + if ( GetValueForEnhancedCustomShapeHandleParameter( nXRangeMin, aRadiusRangeMinimum ) ) + nFlags |= 0x80; + nFlags |= 0x2000; + } + } + else if ( rPropVal.Name == "RadiusRangeMaximum" ) + { + nYRangeMin = sal_Int32(0xff4c0000); // the range of angles seems to be a not + nYRangeMax = sal_Int32(0x00b40000); // used feature, so we are defaulting this + + drawing::EnhancedCustomShapeParameter aRadiusRangeMaximum; + if ( rPropVal.Value >>= aRadiusRangeMaximum ) + { + if ( GetValueForEnhancedCustomShapeHandleParameter( nXRangeMax, aRadiusRangeMaximum ) ) + nFlags |= 0x100; + nFlags |= 0x2000; + } + } + else if ( rPropVal.Name == "RangeXMinimum" ) + { + drawing::EnhancedCustomShapeParameter aXRangeMinimum; + if ( rPropVal.Value >>= aXRangeMinimum ) + { + if ( GetValueForEnhancedCustomShapeHandleParameter( nXRangeMin, aXRangeMinimum ) ) + nFlags |= 0x80; + nFlags |= 0x20; + } + } + else if ( rPropVal.Name == "RangeXMaximum" ) + { + drawing::EnhancedCustomShapeParameter aXRangeMaximum; + if ( rPropVal.Value >>= aXRangeMaximum ) + { + if ( GetValueForEnhancedCustomShapeHandleParameter( nXRangeMax, aXRangeMaximum ) ) + nFlags |= 0x100; + nFlags |= 0x20; + } + } + else if ( rPropVal.Name == "RangeYMinimum" ) + { + drawing::EnhancedCustomShapeParameter aYRangeMinimum; + if ( rPropVal.Value >>= aYRangeMinimum ) + { + if ( GetValueForEnhancedCustomShapeHandleParameter( nYRangeMin, aYRangeMinimum ) ) + nFlags |= 0x200; + nFlags |= 0x20; + } + } + else if ( rPropVal.Name == "RangeYMaximum" ) + { + drawing::EnhancedCustomShapeParameter aYRangeMaximum; + if ( rPropVal.Value >>= aYRangeMaximum ) + { + if ( GetValueForEnhancedCustomShapeHandleParameter( nYRangeMax, aYRangeMaximum ) ) + nFlags |= 0x400; + nFlags |= 0x20; + } + } + } + aMemStrm.WriteUInt32( nFlags ) + .WriteInt32( nXPosition ) + .WriteInt32( nYPosition ) + .WriteInt32( nXMap ) + .WriteInt32( nYMap ) + .WriteInt32( nXRangeMin ) + .WriteInt32( nXRangeMax ) + .WriteInt32( nYRangeMin ) + .WriteInt32( nYRangeMax ); + + if ( nFlags & 8 ) + nAdjustmentsWhichNeedsToBeConverted |= ( 1 << ( nYPosition - 0x100 ) ); + } + + AddOpt(DFF_Prop_Handles, true, 6, aMemStrm); + } + else + { + AddOpt(DFF_Prop_Handles, 0, true); + } + } + } + } + else if ( rProp.Name == sAdjustmentValues ) + { + // it is required, that the information which handle is polar has already be read, + // so we are able to change the polar value to a fixed float + aAdjustmentValuesProp = rProp.Value; + bAdjustmentValuesProp = true; + } + } + if ( bAdjustmentValuesProp ) + { + uno::Sequence aAdjustmentSeq; + if ( aAdjustmentValuesProp >>= aAdjustmentSeq ) + { + if ( bPredefinedHandlesUsed ) + LookForPolarHandles( eShapeType, nAdjustmentsWhichNeedsToBeConverted ); + + sal_Int32 k, nValue = 0, nAdjustmentValues = aAdjustmentSeq.getLength(); + for ( k = 0; k < nAdjustmentValues; k++ ) + if( GetAdjustmentValue( aAdjustmentSeq[ k ], k, nAdjustmentsWhichNeedsToBeConverted, nValue ) ) + AddOpt( static_cast( DFF_Prop_adjustValue + k ), static_cast(nValue) ); + } + } + if( !bPathCoordinatesProp ) + return; + + uno::Sequence aCoordinates; + if ( !(aPathCoordinatesProp >>= aCoordinates) ) + return; + + // creating the vertices + if (aCoordinates.hasElements()) + { + sal_uInt16 j, nElements = static_cast(aCoordinates.getLength()); + sal_uInt16 nElementSize = 8; + sal_uInt32 nStreamSize = nElementSize * nElements + 6; + SvMemoryStream aMemStrm( nStreamSize ); + aMemStrm.WriteUInt16( nElements ) + .WriteUInt16( nElements ) + .WriteUInt16( nElementSize ); + for( j = 0; j < nElements; j++ ) + { + sal_Int32 X = GetValueForEnhancedCustomShapeParameter( aCoordinates[ j ].First, aEquationOrder, true ); + sal_Int32 Y = GetValueForEnhancedCustomShapeParameter( aCoordinates[ j ].Second, aEquationOrder, true ); + aMemStrm.WriteInt32( X ) + .WriteInt32( Y ); + } + + AddOpt(DFF_Prop_pVertices, true, 6, aMemStrm); // -6 + } + else + { + AddOpt(DFF_Prop_pVertices, 0, true); + } +} + +MSO_SPT EscherPropertyContainer::GetCustomShapeType( const uno::Reference< drawing::XShape > & rXShape, ShapeFlag& nMirrorFlags, OUString& rShapeType, bool bOOXML ) +{ + MSO_SPT eShapeType = mso_sptNil; + nMirrorFlags = ShapeFlag::NONE; + uno::Reference< beans::XPropertySet > aXPropSet( rXShape, uno::UNO_QUERY ); + if ( aXPropSet.is() ) + { + try + { + uno::Any aGeoPropSet = aXPropSet->getPropertyValue( "CustomShapeGeometry" ); + uno::Sequence< beans::PropertyValue > aGeoPropSeq; + if ( aGeoPropSet >>= aGeoPropSeq ) + { + sal_Int32 i, nCount = aGeoPropSeq.getLength(); + for ( i = 0; i < nCount; i++ ) + { + const beans::PropertyValue& rProp = aGeoPropSeq[ i ]; + if ( rProp.Name == "Type" ) + { + if ( rProp.Value >>= rShapeType ) + { + if (bOOXML) + { + // In case of VML export, try to handle the + // ooxml- prefix in rShapeType. If that fails, + // just do the same as the binary export. + eShapeType = msfilter::util::GETVMLShapeType(rShapeType); + if (eShapeType == mso_sptNil) + eShapeType = EnhancedCustomShapeTypeNames::Get(rShapeType); + } + else + eShapeType = EnhancedCustomShapeTypeNames::Get( rShapeType ); + } + } + else if ( rProp.Name == "MirroredX" ) + { + bool bMirroredX; + if ( ( rProp.Value >>= bMirroredX ) && bMirroredX ) + nMirrorFlags |= ShapeFlag::FlipH; + } + else if ( rProp.Name == "MirroredY" ) + { + bool bMirroredY; + if ( ( rProp.Value >>= bMirroredY ) && bMirroredY ) + nMirrorFlags |= ShapeFlag::FlipV; + } + } + } + } + catch( const uno::Exception& ) + { + } + } + return eShapeType; +} + + +// Implement for form control export +bool EscherPropertyContainer::CreateBlipPropertiesforOLEControl(const uno::Reference & rXPropSet, + const uno::Reference & rXShape) +{ + SdrObject* pShape = SdrObject::getSdrObjectFromXShape(rXShape); + if ( !pShape ) + return false; + + const Graphic aGraphic(SdrExchangeView::GetObjGraphic(*pShape)); + const GraphicObject aGraphicObject(aGraphic); + + if (!aGraphicObject.GetUniqueID().isEmpty()) + { + if ( pGraphicProvider && pPicOutStrm && pShapeBoundRect ) + { + sal_uInt32 nBlibId = pGraphicProvider->GetBlibID(*pPicOutStrm, aGraphicObject); + if ( nBlibId ) + { + AddOpt( ESCHER_Prop_pib, nBlibId, true ); + ImplCreateGraphicAttributes( rXPropSet, nBlibId, false ); + return true; + } + } + } + + return false; +} + +EscherPersistTable::EscherPersistTable() +{ +} + +EscherPersistTable::~EscherPersistTable() +{ +} + +bool EscherPersistTable::PtIsID( sal_uInt32 nID ) +{ + for(auto const & pPtr : maPersistTable) { + if ( pPtr->mnID == nID ) { + return true; + } + } + return false; +} + +void EscherPersistTable::PtInsert( sal_uInt32 nID, sal_uInt32 nOfs ) +{ + maPersistTable.push_back( std::make_unique( nID, nOfs ) ); +} + +void EscherPersistTable::PtDelete( sal_uInt32 nID ) +{ + auto it = std::find_if(maPersistTable.begin(), maPersistTable.end(), + [&nID](const std::unique_ptr& rxEntry) { return rxEntry->mnID == nID; }); + if (it != maPersistTable.end()) + maPersistTable.erase( it ); +} + +sal_uInt32 EscherPersistTable::PtGetOffsetByID( sal_uInt32 nID ) +{ + for(auto const & pPtr : maPersistTable) { + if ( pPtr->mnID == nID ) { + return pPtr->mnOffset; + } + } + return 0; +}; + +void EscherPersistTable::PtReplace( sal_uInt32 nID, sal_uInt32 nOfs ) +{ + for(auto const & pPtr : maPersistTable) { + if ( pPtr->mnID == nID ) { + pPtr->mnOffset = nOfs; + return; + } + } +} + +void EscherPersistTable::PtReplaceOrInsert( sal_uInt32 nID, sal_uInt32 nOfs ) +{ + for(auto const & pPtr : maPersistTable) { + if ( pPtr->mnID == nID ) { + pPtr->mnOffset = nOfs; + return; + } + } + PtInsert( nID, nOfs ); +} + +bool EscherPropertyValueHelper::GetPropertyValue( + uno::Any& rAny, + const uno::Reference & rXPropSet, + const OUString& rString, + bool bTestPropertyAvailability) +{ + bool bRetValue = true; + if ( bTestPropertyAvailability ) + { + bRetValue = false; + try + { + uno::Reference + aXPropSetInfo( rXPropSet->getPropertySetInfo() ); + if ( aXPropSetInfo.is() ) + bRetValue = aXPropSetInfo->hasPropertyByName( rString ); + } + catch( const uno::Exception& ) + { + bRetValue = false; + } + } + if ( bRetValue ) + { + try + { + rAny = rXPropSet->getPropertyValue( rString ); + if ( !rAny.hasValue() ) + bRetValue = false; + } + catch( const uno::Exception& ) + { + bRetValue = false; + } + } + return bRetValue; +} + +beans::PropertyState EscherPropertyValueHelper::GetPropertyState( + const uno::Reference & rXPropSet, + const OUString& rPropertyName ) +{ + beans::PropertyState eRetValue = beans::PropertyState_AMBIGUOUS_VALUE; + try + { + uno::Reference aXPropState + ( rXPropSet, uno::UNO_QUERY ); + if ( aXPropState.is() ) + eRetValue = aXPropState->getPropertyState( rPropertyName ); + } + catch( const uno::Exception& ) + { + } + return eRetValue; +} + +EscherBlibEntry::EscherBlibEntry( sal_uInt32 nPictureOffset, const GraphicObject& rObject, const OString& rId, + const GraphicAttr* pGraphicAttr ) : + maPrefMapMode ( rObject.GetPrefMapMode() ), + maPrefSize ( rObject.GetPrefSize() ), + mnPictureOffset ( nPictureOffset ), + mnRefCount ( 1 ), + mnSizeExtra ( 0 ), + mbIsEmpty ( true ) +{ + mbIsNativeGraphicPossible = ( pGraphicAttr == nullptr ); + meBlibType = UNKNOWN; + mnSize = 0; + + sal_uInt32 nLen = static_cast(rId.getLength()); + const char* pData = rId.getStr(); + GraphicType eType( rObject.GetType() ); + if (!(nLen && (eType != GraphicType::NONE))) + return; + + mnIdentifier[ 0 ] = rtl_crc32( 0,pData, nLen ); + mnIdentifier[ 1 ] = 0; + + if ( pGraphicAttr ) + { + if ( pGraphicAttr->IsSpecialDrawMode() + || pGraphicAttr->IsMirrored() + || pGraphicAttr->IsCropped() + || pGraphicAttr->IsRotated() + || pGraphicAttr->IsTransparent() + || pGraphicAttr->IsAdjusted() ) + { + SvMemoryStream aSt( sizeof( GraphicAttr ) ); + aSt.WriteUInt16( static_cast(pGraphicAttr->GetDrawMode()) ) + .WriteUInt32( static_cast(pGraphicAttr->GetMirrorFlags()) ) + .WriteInt32( pGraphicAttr->GetLeftCrop() ) + .WriteInt32( pGraphicAttr->GetTopCrop() ) + .WriteInt32( pGraphicAttr->GetRightCrop() ) + .WriteInt32( pGraphicAttr->GetBottomCrop() ) + .WriteUInt16( pGraphicAttr->GetRotation().get() ) + .WriteInt16( pGraphicAttr->GetLuminance() ) + .WriteInt16( pGraphicAttr->GetContrast() ) + .WriteInt16( pGraphicAttr->GetChannelR() ) + .WriteInt16( pGraphicAttr->GetChannelG() ) + .WriteInt16( pGraphicAttr->GetChannelB() ) + .WriteDouble( pGraphicAttr->GetGamma() ); + aSt.WriteBool( pGraphicAttr->IsInvert() ) + .WriteUChar( 255 - pGraphicAttr->GetAlpha() ); // transparency + mnIdentifier[ 1 ] = rtl_crc32( 0, aSt.GetData(), aSt.Tell() ); + } + else + mbIsNativeGraphicPossible = true; + } + sal_uInt32 i, nTmp, n1, n2; + n1 = n2 = 0; + for ( i = 0; i < nLen; i++ ) + { + nTmp = n2 >> 28; // rotating 4 bit + n2 <<= 4; + n2 |= n1 >> 28; + n1 <<= 4; + n1 |= nTmp; + n1 ^= *pData++ - '0'; + } + mnIdentifier[ 2 ] = n1; + mnIdentifier[ 3 ] = n2; + mbIsEmpty = false; +}; + +void EscherBlibEntry::WriteBlibEntry( SvStream& rSt, bool bWritePictureOffset, sal_uInt32 nResize ) +{ + sal_uInt32 nPictureOffset = bWritePictureOffset ? mnPictureOffset : 0; + + rSt.WriteUInt32( ( ESCHER_BSE << 16 ) | ( ( static_cast(meBlibType) << 4 ) | 2 ) ) + .WriteUInt32( 36 + nResize ) + .WriteUChar( meBlibType ); + + switch ( meBlibType ) + { + case EMF : + case WMF : // converting EMF/WMF on OS2 to Pict + rSt.WriteUChar( PICT ); + break; + default: + rSt.WriteUChar( meBlibType ); + } + + rSt.WriteBytes(&mnIdentifier[0], 16); + rSt.WriteUInt16( 0 ) + .WriteUInt32( mnSize + mnSizeExtra ) + .WriteUInt32( mnRefCount ) + .WriteUInt32( nPictureOffset ) + .WriteUInt32( 0 ); +} + +EscherBlibEntry::~EscherBlibEntry() +{ +}; + +bool EscherBlibEntry::operator==( const EscherBlibEntry& rEscherBlibEntry ) const +{ + for ( int i = 0; i < 3; i++ ) + { + if ( mnIdentifier[ i ] != rEscherBlibEntry.mnIdentifier[ i ] ) + return false; + } + return true; +} + +EscherGraphicProvider::EscherGraphicProvider( EscherGraphicProviderFlags nFlags ) : + mnFlags ( nFlags ) +{ +} + +EscherGraphicProvider::~EscherGraphicProvider() +{ +} + +void EscherGraphicProvider::SetNewBlipStreamOffset( sal_Int32 nOffset ) +{ + for( size_t i = 0; i < mvBlibEntrys.size(); i++ ) + { + mvBlibEntrys[ i ]->mnPictureOffset += nOffset; + } +} + +sal_uInt32 EscherGraphicProvider::ImplInsertBlib( EscherBlibEntry* p_EscherBlibEntry ) +{ + mvBlibEntrys.push_back( std::unique_ptr(p_EscherBlibEntry) ); + return mvBlibEntrys.size(); +} + +sal_uInt32 EscherGraphicProvider::GetBlibStoreContainerSize( SvStream const * pMergePicStreamBSE ) const +{ + sal_uInt32 nSize = 44 * mvBlibEntrys.size() + 8; + if ( pMergePicStreamBSE ) + { + for ( size_t i = 0; i < mvBlibEntrys.size(); i++ ) + nSize += mvBlibEntrys[ i ]->mnSize + mvBlibEntrys[ i ]->mnSizeExtra; + } + return nSize; +} + +void EscherGraphicProvider::WriteBlibStoreEntry(SvStream& rSt, + sal_uInt32 nBlipId, sal_uInt32 nResize) +{ + if (nBlipId > mvBlibEntrys.size() || nBlipId == 0) + return; + mvBlibEntrys[nBlipId-1]->WriteBlibEntry(rSt, true/*bWritePictureOffSet*/, nResize); +} + +void EscherGraphicProvider::WriteBlibStoreContainer( SvStream& rSt, SvStream* pMergePicStreamBSE ) +{ + sal_uInt32 nSize = GetBlibStoreContainerSize( pMergePicStreamBSE ); + if ( !nSize ) + return; + + rSt.WriteUInt32( ( ESCHER_BstoreContainer << 16 ) | 0x1f ) + .WriteUInt32( nSize - 8 ); + + if ( pMergePicStreamBSE ) + { + sal_uInt32 nBlipSize, nOldPos = pMergePicStreamBSE->Tell(); + const sal_uInt32 nBuf = 0x40000; // 256KB buffer + std::unique_ptr pBuf(new sal_uInt8[ nBuf ]); + + for ( size_t i = 0; i < mvBlibEntrys.size(); i++ ) + { + EscherBlibEntry* pBlibEntry = mvBlibEntrys[ i ].get(); + + ESCHER_BlibType nBlibType = pBlibEntry->meBlibType; + nBlipSize = pBlibEntry->mnSize + pBlibEntry->mnSizeExtra; + pBlibEntry->WriteBlibEntry( rSt, false, nBlipSize ); + + // BLIP + pMergePicStreamBSE->Seek( pBlibEntry->mnPictureOffset ); + sal_uInt16 n16; + // record version and instance + pMergePicStreamBSE->ReadUInt16( n16 ); + rSt.WriteUInt16( n16 ); + // record type + pMergePicStreamBSE->ReadUInt16( n16 ); + rSt.WriteUInt16( ESCHER_BlipFirst + nBlibType ); + DBG_ASSERT( n16 == ESCHER_BlipFirst + nBlibType , "EscherGraphicProvider::WriteBlibStoreContainer: BLIP record types differ" ); + sal_uInt32 n32; + // record size + pMergePicStreamBSE->ReadUInt32( n32 ); + nBlipSize -= 8; + rSt.WriteUInt32( nBlipSize ); + DBG_ASSERT( nBlipSize == n32, "EscherGraphicProvider::WriteBlibStoreContainer: BLIP sizes differ" ); + // record + while ( nBlipSize ) + { + sal_uInt32 nBytes = std::min( nBlipSize, nBuf ); + pMergePicStreamBSE->ReadBytes(pBuf.get(), nBytes); + rSt.WriteBytes(pBuf.get(), nBytes); + nBlipSize -= nBytes; + } + } + pMergePicStreamBSE->Seek( nOldPos ); + } + else + { + for ( size_t i = 0; i < mvBlibEntrys.size(); i++ ) + mvBlibEntrys[ i ]->WriteBlibEntry( rSt, true ); + } +} + +bool EscherGraphicProvider::GetPrefSize( const sal_uInt32 nBlibId, Size& rPrefSize, MapMode& rPrefMapMode ) +{ + bool bInRange = nBlibId && ( ( nBlibId - 1 ) < mvBlibEntrys.size() ); + if ( bInRange ) + { + EscherBlibEntry* pEntry = mvBlibEntrys[ nBlibId - 1 ].get(); + rPrefSize = pEntry->maPrefSize; + rPrefMapMode = pEntry->maPrefMapMode; + } + return bInRange; +} + +sal_uInt32 EscherGraphicProvider::GetBlibID( SvStream& rPicOutStrm, GraphicObject const & rGraphicObject, + const awt::Rectangle* pVisArea, + const GraphicAttr* pGraphicAttr, const bool bOOxmlExport ) +{ + sal_uInt32 nBlibId = 0; + + std::unique_ptr p_EscherBlibEntry( new EscherBlibEntry( rPicOutStrm.Tell(), rGraphicObject, rGraphicObject.GetUniqueID(), pGraphicAttr ) ); + if ( !p_EscherBlibEntry->IsEmpty() ) + { + for ( size_t i = 0; i < mvBlibEntrys.size(); i++ ) + { + if ( *( mvBlibEntrys[ i ] ) == *p_EscherBlibEntry ) + { + mvBlibEntrys[ i ]->mnRefCount++; + return i + 1; + } + } + + bool bUseNativeGraphic( false ); + + Graphic aGraphic(rGraphicObject.GetTransformedGraphic(pGraphicAttr)); + GfxLink aGraphicLink; + SvMemoryStream aStream; + + const sal_uInt8* pGraphicAry = nullptr; + + if ( p_EscherBlibEntry->mbIsNativeGraphicPossible && aGraphic.IsGfxLink() ) + { + aGraphicLink = aGraphic.GetGfxLink(); + + p_EscherBlibEntry->mnSize = aGraphicLink.GetDataSize(); + pGraphicAry = aGraphicLink.GetData(); + + if ( p_EscherBlibEntry->mnSize && pGraphicAry ) + { + switch ( aGraphicLink.GetType() ) + { + case GfxLinkType::NativeJpg : p_EscherBlibEntry->meBlibType = PEG; break; + case GfxLinkType::NativePng : p_EscherBlibEntry->meBlibType = PNG; break; + + // #i15508# added BMP type for better exports; need to check this + // checked - does not work that way, so keep out for now. It may + // work somehow with direct DIB data, but that would need to be checked + // carefully + // for more comments please check RtfAttributeOutput::FlyFrameGraphic + // + // case GfxLinkType::NativeBmp : p_EscherBlibEntry->meBlibType = DIB; break; + + case GfxLinkType::NativeWmf : + { + if ( aGraphicLink.IsEMF() ) + { + p_EscherBlibEntry->meBlibType = EMF; + } + else if ( pGraphicAry && ( p_EscherBlibEntry->mnSize > 0x2c ) ) + { + p_EscherBlibEntry->meBlibType = WMF; + if ( ( pGraphicAry[ 0 ] == 0xd7 ) && ( pGraphicAry[ 1 ] == 0xcd ) + && ( pGraphicAry[ 2 ] == 0xc6 ) && ( pGraphicAry[ 3 ] == 0x9a ) ) + { // we have to get rid of the metafileheader + pGraphicAry += 22; + p_EscherBlibEntry->mnSize -= 22; + } + } + } + break; + default: break; + } + if ( p_EscherBlibEntry->meBlibType != UNKNOWN ) + bUseNativeGraphic = true; + } + } + if ( !bUseNativeGraphic ) + { + GraphicType eGraphicType = aGraphic.GetType(); + if ( ( eGraphicType == GraphicType::Bitmap ) || ( eGraphicType == GraphicType::GdiMetafile ) ) + { + ErrCode nErrCode; + if ( !aGraphic.IsAnimated() ) + nErrCode = GraphicConverter::Export( aStream, aGraphic, ( eGraphicType == GraphicType::Bitmap ) ? ConvertDataFormat::PNG : ConvertDataFormat::EMF ); + else + { // to store an animation, a gif has to be included into the msOG chunk of a png #I5583# + GraphicFilter &rFilter = GraphicFilter::GetGraphicFilter(); + SvMemoryStream aGIFStream; + const char* const pString = "MSOFFICE9.0"; + aGIFStream.WriteBytes(pString, strlen(pString)); + nErrCode = rFilter.ExportGraphic( aGraphic, u"", aGIFStream, + rFilter.GetExportFormatNumberForShortName( u"GIF" ) ); + SAL_WARN_IF( + nErrCode != ERRCODE_NONE, "filter.ms", + "ExportGraphic to GIF failed with " << nErrCode); + if (nErrCode == ERRCODE_NONE) + { + sal_uInt32 nGIFSreamLen = aGIFStream.Tell(); + uno::Sequence aGIFSeq( nGIFSreamLen ); + sal_Int8* pSeq = aGIFSeq.getArray(); + aGIFStream.Seek( STREAM_SEEK_TO_BEGIN ); + aGIFStream.ReadBytes(pSeq, nGIFSreamLen); + beans::PropertyValue aChunkProp, aFilterProp; + aChunkProp.Name = "msOG"; + aChunkProp.Value <<= aGIFSeq; + uno::Sequence aAdditionalChunkSequence{ aChunkProp }; + aFilterProp.Name = "AdditionalChunks"; + aFilterProp.Value <<= aAdditionalChunkSequence; + uno::Sequence aFilterData{ aFilterProp }; + nErrCode = rFilter.ExportGraphic( aGraphic, u"", aStream, + rFilter.GetExportFormatNumberForShortName( u"PNG" ), &aFilterData ); + } + } + if ( nErrCode == ERRCODE_NONE ) + { + p_EscherBlibEntry->meBlibType = ( eGraphicType == GraphicType::Bitmap ) ? PNG : EMF; + p_EscherBlibEntry->mnSize = aStream.TellEnd(); + pGraphicAry = static_cast(aStream.GetData()); + } + } + } + + ESCHER_BlibType eBlibType = p_EscherBlibEntry->meBlibType; + if ( p_EscherBlibEntry->mnSize && pGraphicAry && ( eBlibType != UNKNOWN ) ) + { + sal_uInt32 nExtra, nAtomSize = 0; + sal_uInt32 nInstance, nUncompressedSize = p_EscherBlibEntry->mnSize; + + if ( mnFlags & EscherGraphicProviderFlags::UseInstances ) + { + rPicOutStrm.WriteUInt32( 0x7f90000 | static_cast( mvBlibEntrys.size() << 4 ) ) + .WriteUInt32( 0 ); + nAtomSize = rPicOutStrm.Tell(); + if ( eBlibType == PNG ) + rPicOutStrm.WriteUInt16( 0x0606 ); + else if ( eBlibType == WMF ) + rPicOutStrm.WriteUInt16( 0x0403 ); + else if ( eBlibType == EMF ) + rPicOutStrm.WriteUInt16( 0x0402 ); + else if ( eBlibType == PEG ) + rPicOutStrm.WriteUInt16( 0x0505 ); + } + + // fdo#69607 do not compress WMF files if we are in OOXML export + if ( ( eBlibType == PEG ) || ( eBlibType == PNG ) // || ( eBlibType == DIB )) // #i15508# + || ( ( ( eBlibType == WMF ) || ( eBlibType == EMF ) ) && bOOxmlExport ) ) + { + nExtra = 17; + p_EscherBlibEntry->mnSizeExtra = nExtra + 8; + + // #i15508# type see SvxMSDffManager::GetBLIPDirect (checked, does not work this way) + // see RtfAttributeOutput::FlyFrameGraphic for more comments + // maybe it would work with direct DIB data, but that would need thorough testing + if( eBlibType == PNG ) + { + nInstance = 0xf01e6e00; + } + else // if( eBlibType == PEG ) + { + nInstance = 0xf01d46a0; + } + //else // eBlibType == DIB + //{ + // nInstance = 0xf01d7A80; + //} + + // #i15508# + //nInstance = ( eBlibType == PNG ) ? 0xf01e6e00 : 0xf01d46a0; + + + rPicOutStrm.WriteUInt32( nInstance ).WriteUInt32( p_EscherBlibEntry->mnSize + nExtra ); + rPicOutStrm.WriteBytes(p_EscherBlibEntry->mnIdentifier, 16); + rPicOutStrm.WriteUChar( 0xff ); + rPicOutStrm.WriteBytes(pGraphicAry, p_EscherBlibEntry->mnSize); + } + else + { + ZCodec aZCodec( 0x8000, 0x8000 ); + aZCodec.BeginCompression(); + SvMemoryStream aDestStrm; + aZCodec.Write( aDestStrm, pGraphicAry, p_EscherBlibEntry->mnSize ); + aZCodec.EndCompression(); + p_EscherBlibEntry->mnSize = aDestStrm.TellEnd(); + pGraphicAry = static_cast(aDestStrm.GetData()); + if ( p_EscherBlibEntry->mnSize && pGraphicAry ) + { + nExtra = eBlibType == WMF ? 0x42 : 0x32; // !EMF -> no change + p_EscherBlibEntry->mnSizeExtra = nExtra + 8; + nInstance = ( eBlibType == WMF ) ? 0xf01b2170 : 0xf01a3d40; // !EMF -> no change + rPicOutStrm.WriteUInt32( nInstance ).WriteUInt32( p_EscherBlibEntry->mnSize + nExtra ); + if ( eBlibType == WMF ) // !EMF -> no change + rPicOutStrm.WriteBytes(p_EscherBlibEntry->mnIdentifier, 16); + rPicOutStrm.WriteBytes(p_EscherBlibEntry->mnIdentifier, 16); + + /* + ##913## + For Word the stored size of the graphic is critical the + metafile boundaries must match the actual graphics + boundaries, and the width and height must be in EMU's + + If you don't do it this way then objects edited in the + msoffice app may show strange behaviour as the size jumps + around, and the original size and scaling factor in word + will be a very strange figure + */ + sal_uInt32 nPrefWidth = p_EscherBlibEntry->maPrefSize.Width(); + sal_uInt32 nPrefHeight = p_EscherBlibEntry->maPrefSize.Height(); + sal_uInt32 nWidth, nHeight; + if ( pVisArea ) + { + nWidth = pVisArea->Width * 360; + nHeight = pVisArea->Height * 360; + } + else + { + Size aPrefSize(lcl_SizeToEmu(p_EscherBlibEntry->maPrefSize, p_EscherBlibEntry->maPrefMapMode)); + nWidth = aPrefSize.Width() * 360; + nHeight = aPrefSize.Height() * 360; + } + rPicOutStrm.WriteUInt32( nUncompressedSize ) // WMFSize without FileHeader + .WriteInt32( 0 ) // since we can't find out anymore what the original size of + .WriteInt32( 0 ) // the WMF (without Fileheader) was we write 10cm / x + .WriteUInt32( nPrefWidth ) + .WriteUInt32( nPrefHeight ) + .WriteUInt32( nWidth ) + .WriteUInt32( nHeight ) + .WriteUInt32( p_EscherBlibEntry->mnSize ) + .WriteUInt16( 0xfe00 ); // compression Flags + rPicOutStrm.WriteBytes(pGraphicAry, p_EscherBlibEntry->mnSize); + } + } + if ( nAtomSize ) + { + sal_uInt32 nPos = rPicOutStrm.Tell(); + rPicOutStrm.Seek( nAtomSize - 4 ); + rPicOutStrm.WriteUInt32( nPos - nAtomSize ); + rPicOutStrm.Seek( nPos ); + } + nBlibId = ImplInsertBlib( p_EscherBlibEntry.release() ); + } + } + return nBlibId; +} + +namespace { + +struct EscherConnectorRule +{ + sal_uInt32 nRuleId; + sal_uInt32 nShapeA; // SPID of shape A + sal_uInt32 nShapeB; // SPID of shape B + sal_uInt32 nShapeC; // SPID of connector shape + sal_uInt32 ncptiA; // Connection site Index of shape A + sal_uInt32 ncptiB; // Connection site Index of shape B +}; + +} + +struct EscherShapeListEntry +{ + uno::ReferenceaXShape; + sal_uInt32 n_EscherId; + + EscherShapeListEntry(uno::Reference xShape, sal_uInt32 nId) + : aXShape(std::move(xShape)) + , n_EscherId(nId) + {} +}; + +sal_uInt32 EscherConnectorListEntry::GetClosestPoint( const tools::Polygon& rPoly, const awt::Point& rPoint ) +{ + sal_uInt16 nCount = rPoly.GetSize(); + sal_uInt16 nClosest = nCount; + double fDist = sal_uInt32(0xffffffff); + while( nCount-- ) + { + double fDistance = hypot( rPoint.X - rPoly[ nCount ].X(), rPoint.Y - rPoly[ nCount ].Y() ); + if ( fDistance < fDist ) + { + nClosest = nCount; + fDist = fDistance; + } + } + return nClosest; +}; + + +// for rectangles for ellipses for polygons +// +// nRule = 0 ->Top 0 ->Top nRule = index to a (Poly)Polygon point +// 1 ->Left 2 ->Left +// 2 ->Bottom 4 ->Bottom +// 3 ->Right 6 ->Right + +sal_uInt32 EscherConnectorListEntry::GetConnectorRule( bool bFirst ) +{ + sal_uInt32 nRule = 0; + + uno::Any aAny; + awt::Point aRefPoint( bFirst ? maPointA : maPointB ); + uno::Reference + aXShape( bFirst ? mXConnectToA : mXConnectToB ); + + OUString aString(aXShape->getShapeType()); + OStringBuffer aBuf(OUStringToOString(aString, RTL_TEXTENCODING_UTF8)); + aBuf.remove( 0, 13 ); // removing "com.sun.star." + sal_Int16 nPos = aBuf.toString().indexOf("Shape"); + aBuf.remove(nPos, 5); + OString aType = aBuf.makeStringAndClear(); + + uno::Reference + aPropertySet( aXShape, uno::UNO_QUERY ); + + if ((aType == "drawing.PolyPolygon") || (aType == "drawing.PolyLine")) + { + if ( aPropertySet.is() ) + { + if ( EscherPropertyValueHelper::GetPropertyValue( aAny, aPropertySet, "PolyPolygon" ) ) + { + auto pSourcePolyPolygon = + o3tl::doAccess(aAny); + sal_Int32 nOuterSequenceCount = pSourcePolyPolygon->getLength(); + drawing::PointSequence const * pOuterSequence = pSourcePolyPolygon->getConstArray(); + + if ( pOuterSequence ) + { + sal_Int32 a, b, nIndex = 0; + sal_uInt32 nDistance = 0xffffffff; + for( a = 0; a < nOuterSequenceCount; a++ ) + { + drawing::PointSequence const * pInnerSequence = pOuterSequence++; + if ( pInnerSequence ) + { + awt::Point const * pArray = pInnerSequence->getConstArray(); + if ( pArray ) + { + for ( b = 0; b < pInnerSequence->getLength(); b++, nIndex++, pArray++ ) + { + sal_uInt32 nDist = static_cast(hypot( aRefPoint.X - pArray->X, aRefPoint.Y - pArray->Y )); + if ( nDist < nDistance ) + { + nRule = nIndex; + nDistance = nDist; + } + } + } + } + } + } + } + } + } + else if ((aType == "drawing.OpenBezier") || (aType == "drawing.OpenFreeHand") || (aType == "drawing.PolyLinePath") + || (aType == "drawing.ClosedBezier") || ( aType == "drawing.ClosedFreeHand") || (aType == "drawing.PolyPolygonPath") ) + { + uno::Reference + aPropertySet2( aXShape, uno::UNO_QUERY ); + if ( aPropertySet2.is() ) + { + if ( EscherPropertyValueHelper::GetPropertyValue( aAny, aPropertySet2, "PolyPolygonBezier" ) ) + { + auto pSourcePolyPolygon = + o3tl::doAccess(aAny); + sal_Int32 nOuterSequenceCount = pSourcePolyPolygon->Coordinates.getLength(); + + // get pointer of inner sequences + drawing::PointSequence const * pOuterSequence = + pSourcePolyPolygon->Coordinates.getConstArray(); + drawing::FlagSequence const * pOuterFlags = + pSourcePolyPolygon->Flags.getConstArray(); + + if ( pOuterSequence && pOuterFlags ) + { + sal_Int32 a, b, nIndex = 0; + sal_uInt32 nDistance = 0xffffffff; + + for ( a = 0; a < nOuterSequenceCount; a++ ) + { + drawing::PointSequence const * pInnerSequence = pOuterSequence++; + drawing::FlagSequence const * pInnerFlags = pOuterFlags++; + if ( pInnerSequence && pInnerFlags ) + { + awt::Point const * pArray = pInnerSequence->getConstArray(); + drawing::PolygonFlags const * pFlags = pInnerFlags->getConstArray(); + if ( pArray && pFlags ) + { + for ( b = 0; b < pInnerSequence->getLength(); b++, pArray++ ) + { + drawing::PolygonFlags ePolyFlags = *pFlags++; + if ( ePolyFlags == drawing::PolygonFlags_CONTROL ) + continue; + sal_uInt32 nDist = static_cast(hypot( aRefPoint.X - pArray->X, aRefPoint.Y - pArray->Y )); + if ( nDist < nDistance ) + { + nRule = nIndex; + nDistance = nDist; + } + nIndex++; + } + } + } + } + } + } + } + } + else + { + bool bRectangularConnection = true; + + if (aType == "drawing.Custom") + { + if (auto pSdrObjCustomShape = dynamic_cast< SdrObjCustomShape* >(SdrObject::getSdrObjectFromXShape(aXShape))) + { + const SdrCustomShapeGeometryItem& rGeometryItem = + pSdrObjCustomShape->GetMergedItem( SDRATTR_CUSTOMSHAPE_GEOMETRY ); + + OUString sShapeType; + const uno::Any* pType = rGeometryItem.GetPropertyValueByName( "Type" ); + if ( pType ) + *pType >>= sShapeType; + MSO_SPT eSpType = EnhancedCustomShapeTypeNames::Get( sShapeType ); + + uno::Any* pGluePointType = const_cast(rGeometryItem).GetPropertyValueByName( "Path", "GluePointType" ); + + sal_Int16 nGluePointType = sal_Int16(); + if ( !( pGluePointType && + ( *pGluePointType >>= nGluePointType ) ) ) + nGluePointType = GetCustomShapeConnectionTypeDefault( eSpType ); + + if ( nGluePointType == drawing::EnhancedCustomShapeGluePointType::CUSTOM ) + { + const SdrGluePointList* pList = pSdrObjCustomShape->GetGluePointList(); + if ( pList ) + { + tools::Polygon aPoly; + sal_uInt16 nNum, nCnt = pList->GetCount(); + if ( nCnt ) + { + for ( nNum = 0; nNum < nCnt; nNum++ ) + { + const SdrGluePoint& rGP = (*pList)[ nNum ]; + Point aPt(rGP.GetAbsolutePos(*pSdrObjCustomShape)); + aPoly.Insert( POLY_APPEND, aPt ); + } + nRule = GetClosestPoint( aPoly, aRefPoint ); + bRectangularConnection = false; + } + } + } + else if ( nGluePointType == drawing::EnhancedCustomShapeGluePointType::SEGMENTS ) + { + tools::PolyPolygon aPolyPoly; + SdrObjectUniquePtr pTemporaryConvertResultObject(pSdrObjCustomShape->DoConvertToPolyObj(true, true)); + SdrPathObj* pSdrPathObj(dynamic_cast< SdrPathObj* >(pTemporaryConvertResultObject.get())); + + if(pSdrPathObj) + { + // #i74631# use explicit constructor here. Also XPolyPolygon is not necessary, + // reducing to PolyPolygon + aPolyPoly = tools::PolyPolygon(pSdrPathObj->GetPathPoly()); + } + + // do *not* forget to delete the temporary used SdrObject - possible memory leak (!) + pTemporaryConvertResultObject.reset(); + pSdrPathObj = nullptr; + + if(0 != aPolyPoly.Count()) + { + sal_Int16 nIndex = 0; + sal_uInt16 a, b; + sal_uInt32 nDistance = 0xffffffff; + + for ( a = 0; a < aPolyPoly.Count(); a++ ) + { + const tools::Polygon& rPoly = aPolyPoly.GetObject( a ); + for ( b = 0; b < rPoly.GetSize(); b++ ) + { + if ( rPoly.GetFlags( b ) != PolyFlags::Normal ) + continue; + const Point& rPt = rPoly[ b ]; + sal_uInt32 nDist = static_cast(hypot( aRefPoint.X - rPt.X(), aRefPoint.Y - rPt.Y() )); + if ( nDist < nDistance ) + { + nRule = nIndex; + nDistance = nDist; + } + nIndex++; + } + } + + if ( nDistance != 0xffffffff ) + bRectangularConnection = false; + } + } + } + } + if ( bRectangularConnection ) + { + awt::Point aPoint( aXShape->getPosition() ); + awt::Size aSize( aXShape->getSize() ); + + tools::Rectangle aRect( Point( aPoint.X, aPoint.Y ), Size( aSize.Width, aSize.Height ) ); + Point aCenter( aRect.Center() ); + tools::Polygon aPoly( 4 ); + + aPoly[ 0 ] = Point( aCenter.X(), aRect.Top() ); + aPoly[ 1 ] = Point( aRect.Left(), aCenter.Y() ); + aPoly[ 2 ] = Point( aCenter.X(), aRect.Bottom() ); + aPoly[ 3 ] = Point( aRect.Right(), aCenter.Y() ); + + sal_Int32 nAngle = ( EscherPropertyValueHelper::GetPropertyValue( aAny, aPropertySet, "RotateAngle", true ) ) + ? *o3tl::doAccess(aAny) : 0; + if ( nAngle ) + aPoly.Rotate( aRect.TopLeft(), Degree10(static_cast( ( nAngle + 5 ) / 10 )) ); + nRule = GetClosestPoint( aPoly, aRefPoint ); + + if (aType == "drawing.Ellipse") + nRule <<= 1; // In PPT an ellipse has 8 ways to connect + } + } + return nRule; +} + +EscherSolverContainer::EscherSolverContainer() +{ +} + +EscherSolverContainer::~EscherSolverContainer() +{ +} + +void EscherSolverContainer::AddShape( const uno::Reference & rXShape, sal_uInt32 nId ) +{ + maShapeList.push_back( std::make_unique( rXShape, nId ) ); +} + +void EscherSolverContainer::AddConnector( + const uno::Reference & rConnector, + const awt::Point& rPA, + uno::Reference const & rConA, + const awt::Point& rPB, + uno::Reference const & rConB +) +{ + maConnectorList.push_back( std::make_unique( rConnector, rPA, rConA, rPB, rConB ) ); +} + +sal_uInt32 EscherSolverContainer::GetShapeId( const uno::Reference & rXShape ) const +{ + for (auto const & pPtr : maShapeList) + { + if ( rXShape == pPtr->aXShape ) + return pPtr->n_EscherId; + } + return 0; +} + +void EscherSolverContainer::WriteSolver( SvStream& rStrm ) +{ + sal_uInt32 nCount = maConnectorList.size(); + if ( !nCount ) + return; + + sal_uInt32 nRecHdPos, nCurrentPos, nSize; + rStrm .WriteUInt16( ( nCount << 4 ) | 0xf ) // open an ESCHER_SolverContainer + .WriteUInt16( ESCHER_SolverContainer ) + .WriteUInt32( 0 ); + + nRecHdPos = rStrm.Tell() - 4; + + EscherConnectorRule aConnectorRule; + aConnectorRule.nRuleId = 2; + for (auto const & pPtr : maConnectorList) + { + aConnectorRule.ncptiA = aConnectorRule.ncptiB = 0xffffffff; + aConnectorRule.nShapeC = GetShapeId( pPtr->mXConnector ); + aConnectorRule.nShapeA = GetShapeId( pPtr->mXConnectToA ); + aConnectorRule.nShapeB = GetShapeId( pPtr->mXConnectToB ); + + if ( aConnectorRule.nShapeC ) + { + if ( aConnectorRule.nShapeA ) + aConnectorRule.ncptiA = pPtr->GetConnectorRule( true ); + if ( aConnectorRule.nShapeB ) + aConnectorRule.ncptiB = pPtr->GetConnectorRule( false ); + } + rStrm .WriteUInt32( ( ESCHER_ConnectorRule << 16 ) | 1 ) // atom hd + .WriteUInt32( 24 ) + .WriteUInt32( aConnectorRule.nRuleId ) + .WriteUInt32( aConnectorRule.nShapeA ) + .WriteUInt32( aConnectorRule.nShapeB ) + .WriteUInt32( aConnectorRule.nShapeC ) + .WriteUInt32( aConnectorRule.ncptiA ) + .WriteUInt32( aConnectorRule.ncptiB ); + + aConnectorRule.nRuleId += 2; + } + + nCurrentPos = rStrm.Tell(); // close the ESCHER_SolverContainer + nSize = ( nCurrentPos - nRecHdPos ) - 4; + rStrm.Seek( nRecHdPos ); + rStrm.WriteUInt32( nSize ); + rStrm.Seek( nCurrentPos ); +} + +EscherExGlobal::EscherExGlobal() : + EscherGraphicProvider( EscherGraphicProviderFlags::NONE ), + mpPicStrm( nullptr ), + mbHasDggCont( false ), + mbPicStrmQueried( false ) +{ +} + +EscherExGlobal::~EscherExGlobal() +{ +} + +sal_uInt32 EscherExGlobal::GenerateDrawingId() +{ + // new drawing starts a new cluster in the cluster table (cluster identifiers are one-based) + sal_uInt32 nClusterId = static_cast< sal_uInt32 >( maClusterTable.size() + 1 ); + // drawing identifiers are one-based + sal_uInt32 nDrawingId = static_cast< sal_uInt32 >( maDrawingInfos.size() + 1 ); + // prepare new entries in the tables + maClusterTable.emplace_back( nDrawingId ); + maDrawingInfos.emplace_back( nClusterId ); + // return the new drawing identifier + return nDrawingId; +} + +sal_uInt32 EscherExGlobal::GenerateShapeId( sal_uInt32 nDrawingId, bool bIsInSpgr ) +{ + // drawing identifier is one-based + // make sure the drawing is valid (bnc#656503) + if ( nDrawingId == 0 ) + return 0; + // create index from the identifier + size_t nDrawingIdx = nDrawingId - 1; + OSL_ENSURE( nDrawingIdx < maDrawingInfos.size(), "EscherExGlobal::GenerateShapeId - invalid drawing ID" ); + if( nDrawingIdx >= maDrawingInfos.size() ) + return 0; + DrawingInfo& rDrawingInfo = maDrawingInfos[ nDrawingIdx ]; + + // cluster identifier in drawing info struct is one-based + ClusterEntry* pClusterEntry = &maClusterTable[ rDrawingInfo.mnClusterId - 1 ]; + + // check cluster overflow, create new cluster entry + if( pClusterEntry->mnNextShapeId == DFF_DGG_CLUSTER_SIZE ) + { + // start a new cluster in the cluster table + maClusterTable.emplace_back( nDrawingId ); + pClusterEntry = &maClusterTable.back(); + // new size of maClusterTable is equal to one-based identifier of the new cluster + rDrawingInfo.mnClusterId = static_cast< sal_uInt32 >( maClusterTable.size() ); + } + + // build shape identifier from cluster identifier and next free cluster shape identifier + rDrawingInfo.mnLastShapeId = static_cast< sal_uInt32 >( rDrawingInfo.mnClusterId * DFF_DGG_CLUSTER_SIZE + pClusterEntry->mnNextShapeId ); + // update free shape identifier in cluster entry + ++pClusterEntry->mnNextShapeId; + /* Old code has counted the shapes only, if we are in a SPGRCONTAINER. Is + this really intended? Maybe it's always true... */ + if( bIsInSpgr ) + ++rDrawingInfo.mnShapeCount; + + // return the new shape identifier + return rDrawingInfo.mnLastShapeId; +} + +sal_uInt32 EscherExGlobal::GetDrawingShapeCount( sal_uInt32 nDrawingId ) const +{ + size_t nDrawingIdx = nDrawingId - 1; + OSL_ENSURE( nDrawingIdx < maDrawingInfos.size(), "EscherExGlobal::GetDrawingShapeCount - invalid drawing ID" ); + return (nDrawingIdx < maDrawingInfos.size()) ? maDrawingInfos[ nDrawingIdx ].mnShapeCount : 0; +} + +sal_uInt32 EscherExGlobal::GetLastShapeId( sal_uInt32 nDrawingId ) const +{ + size_t nDrawingIdx = nDrawingId - 1; + OSL_ENSURE( nDrawingIdx < maDrawingInfos.size(), "EscherExGlobal::GetLastShapeId - invalid drawing ID" ); + return (nDrawingIdx < maDrawingInfos.size()) ? maDrawingInfos[ nDrawingIdx ].mnLastShapeId : 0; +} + +sal_uInt32 EscherExGlobal::GetDggAtomSize() const +{ + // 8 bytes header, 16 bytes fixed DGG data, 8 bytes for each cluster + return static_cast< sal_uInt32 >( 24 + 8 * maClusterTable.size() ); +} + +void EscherExGlobal::WriteDggAtom( SvStream& rStrm ) const +{ + sal_uInt32 nDggSize = GetDggAtomSize(); + + // write the DGG record header (do not include the 8 bytes of the header in the data size) + rStrm.WriteUInt32( ESCHER_Dgg << 16 ).WriteUInt32( nDggSize - 8 ); + + // calculate and write the fixed DGG data + sal_uInt32 nShapeCount = 0; + sal_uInt32 nLastShapeId = 0; + for (auto const& drawingInfo : maDrawingInfos) + { + nShapeCount += drawingInfo.mnShapeCount; + nLastShapeId = ::std::max( nLastShapeId, drawingInfo.mnLastShapeId ); + } + // the non-existing cluster with index #0 is counted too + sal_uInt32 nClusterCount = static_cast< sal_uInt32 >( maClusterTable.size() + 1 ); + sal_uInt32 nDrawingCount = static_cast< sal_uInt32 >( maDrawingInfos.size() ); + rStrm.WriteUInt32( nLastShapeId ).WriteUInt32( nClusterCount ).WriteUInt32( nShapeCount ).WriteUInt32( nDrawingCount ); + + // write the cluster table + for (auto const& elem : maClusterTable) + rStrm.WriteUInt32( elem.mnDrawingId ).WriteUInt32( elem.mnNextShapeId ); +} + +SvStream* EscherExGlobal::QueryPictureStream() +{ + if( !mbPicStrmQueried ) + { + mpPicStrm = ImplQueryPictureStream(); + mbPicStrmQueried = true; + } + return mpPicStrm; +} + +SvStream* EscherExGlobal::ImplQueryPictureStream() +{ + return nullptr; +} + +namespace { + +// Implementation of an empty stream that silently succeeds, but does nothing. +// +// In fact, this is a hack. The right solution is to abstract EscherEx to be +// able to work without SvStream; but at the moment it is better to live with +// this I guess. +class SvNullStream : public SvStream +{ +protected: + virtual std::size_t GetData( void* pData, std::size_t nSize ) override { memset( pData, 0, nSize ); return nSize; } + virtual std::size_t PutData( const void*, std::size_t nSize ) override { return nSize; } + virtual sal_uInt64 SeekPos( sal_uInt64 nPos ) override { return nPos; } + virtual void SetSize( sal_uInt64 ) override {} + virtual void FlushData() override {} + +public: + SvNullStream() {} +}; + +} + +EscherEx::EscherEx(std::shared_ptr xGlobal, SvStream* pOutStrm, bool bOOXML) + : mxGlobal(std::move(xGlobal)) + , mpOutStrm(pOutStrm) + , mbOwnsStrm(false) + , mnCurrentDg(0) + , mnCountOfs(0) + , mnGroupLevel(0) + , mnHellLayerId(SDRLAYER_NOTFOUND) + , mbEscherSpgr(false) + , mbEscherDg(false) + , mbOOXML(bOOXML) +{ + if (!mpOutStrm) + { + mpOutStrm = new SvNullStream(); + mbOwnsStrm = true; + } + mnStrmStartOfs = mpOutStrm->Tell(); + mpImplEESdrWriter.reset( new ImplEESdrWriter( *this ) ); +} + +EscherEx::~EscherEx() +{ + if (mbOwnsStrm) + delete mpOutStrm; +} + +void EscherEx::Flush( SvStream* pPicStreamMergeBSE /* = NULL */ ) +{ + if ( !mxGlobal->HasDggContainer() ) + return; + + // store the current stream position at ESCHER_Persist_CurrentPosition key + PtReplaceOrInsert( ESCHER_Persist_CurrentPosition, mpOutStrm->Tell() ); + if ( DoSeek( ESCHER_Persist_Dgg ) ) + { + /* The DGG record is still not written. ESCHER_Persist_Dgg seeks + to the place where the complete record has to be inserted. */ + InsertAtCurrentPos( mxGlobal->GetDggAtomSize() ); + mxGlobal->WriteDggAtom( *mpOutStrm ); + + if ( mxGlobal->HasGraphics() ) + { + /* Calculate the total size of the BSTORECONTAINER including + all BSE records containing the picture data contained in + the passed in pPicStreamMergeBSE. */ + sal_uInt32 nBSCSize = mxGlobal->GetBlibStoreContainerSize( pPicStreamMergeBSE ); + if ( nBSCSize > 0 ) + { + InsertAtCurrentPos( nBSCSize ); + mxGlobal->WriteBlibStoreContainer( *mpOutStrm, pPicStreamMergeBSE ); + } + } + + /* Forget the stream position stored for the DGG which is invalid + after the call to InsertAtCurrentPos() anyway. */ + PtDelete( ESCHER_Persist_Dgg ); + } + // seek to initial position (may be different due to inserted DGG and BLIPs) + mpOutStrm->Seek( PtGetOffsetByID( ESCHER_Persist_CurrentPosition ) ); +} + +void EscherEx::InsertAtCurrentPos( sal_uInt32 nBytes ) +{ + sal_uInt32 nSize, nType, nSource, nBufSize, nToCopy, nCurPos = mpOutStrm->Tell(); + + // adjust persist table + for(auto const & pPtr : maPersistTable) { + sal_uInt32 nOfs = pPtr->mnOffset; + if ( nOfs >= nCurPos ) { + pPtr->mnOffset += nBytes; + } + } + + // adapt container and atom sizes + mpOutStrm->Seek( mnStrmStartOfs ); + while ( mpOutStrm->Tell() < nCurPos ) + { + mpOutStrm->ReadUInt32( nType ).ReadUInt32( nSize ); + sal_uInt32 nEndOfRecord = mpOutStrm->Tell() + nSize; + bool bContainer = (nType & 0x0F) == 0x0F; + /* Expand the record, if the insertion position is inside, or if the + position is at the end of a container (expands always), or at the + end of an atom and bExpandEndOfAtom is set. */ + if ( (nCurPos < nEndOfRecord) || ((nCurPos == nEndOfRecord) && bContainer) ) + { + mpOutStrm->SeekRel( -4 ); + mpOutStrm->WriteUInt32( nSize + nBytes ); + if ( !bContainer ) + mpOutStrm->SeekRel( nSize ); + } + else + mpOutStrm->SeekRel( nSize ); + } + for (auto & offset : mOffsets) + { + if ( offset > nCurPos ) + offset += nBytes; + } + nSource = mpOutStrm->TellEnd(); + nToCopy = nSource - nCurPos; // increase the size of the stream by nBytes + std::unique_ptr pBuf(new sal_uInt8[ 0x40000 ]); // 256KB Buffer + while ( nToCopy ) + { + nBufSize = ( nToCopy >= 0x40000 ) ? 0x40000 : nToCopy; + nToCopy -= nBufSize; + nSource -= nBufSize; + mpOutStrm->Seek( nSource ); + mpOutStrm->ReadBytes(pBuf.get(), nBufSize); + mpOutStrm->Seek( nSource + nBytes ); + mpOutStrm->WriteBytes(pBuf.get(), nBufSize); + } + mpOutStrm->Seek( nCurPos ); +} + +void EscherEx::InsertPersistOffset( sal_uInt32 nKey, sal_uInt32 nOffset ) +{ + PtInsert( ESCHER_Persist_PrivateEntry | nKey, nOffset ); +} + +void EscherEx::ReplacePersistOffset( sal_uInt32 nKey, sal_uInt32 nOffset ) +{ + PtReplace( ESCHER_Persist_PrivateEntry | nKey, nOffset ); +} + +void EscherEx::SetEditAs( const OUString& rEditAs ) +{ + mEditAs = rEditAs; +} + +sal_uInt32 EscherEx::GetPersistOffset( sal_uInt32 nKey ) +{ + return PtGetOffsetByID( ESCHER_Persist_PrivateEntry | nKey ); +} + +bool EscherEx::DoSeek( sal_uInt32 nKey ) +{ + sal_uInt32 nPos = PtGetOffsetByID( nKey ); + if ( nPos ) + mpOutStrm->Seek( nPos ); + else + { + if (! PtIsID( nKey ) ) + return false; + mpOutStrm->Seek( 0 ); + } + return true; +} + +bool EscherEx::SeekToPersistOffset( sal_uInt32 nKey ) +{ + return DoSeek( ESCHER_Persist_PrivateEntry | nKey ); +} + +void EscherEx::InsertAtPersistOffset( sal_uInt32 nKey, sal_uInt32 nValue ) +{ + sal_uInt32 nOldPos = mpOutStrm->Tell(); + bool bRetValue = SeekToPersistOffset( nKey ); + if ( bRetValue ) + { + mpOutStrm->WriteUInt32( nValue ); + mpOutStrm->Seek( nOldPos ); + } +} + +void EscherEx::OpenContainer( sal_uInt16 nEscherContainer, int nRecInstance ) +{ + mpOutStrm->WriteUInt16( ( nRecInstance << 4 ) | 0xf ).WriteUInt16( nEscherContainer ).WriteUInt32( 0 ); + mOffsets.push_back( mpOutStrm->Tell() - 4 ); + mRecTypes.push_back( nEscherContainer ); + switch( nEscherContainer ) + { + case ESCHER_DggContainer : + { + mxGlobal->SetDggContainer(); + mnCurrentDg = 0; + /* Remember the current position as start position of the DGG + record and BSTORECONTAINER, but do not write them actually. + This will be done later in Flush() when the number of drawings, + the size and contents of the FIDCL cluster table, and the size + of the BLIP container are known. */ + PtReplaceOrInsert( ESCHER_Persist_Dgg, mpOutStrm->Tell() ); + } + break; + + case ESCHER_DgContainer : + { + if ( mxGlobal->HasDggContainer() ) + { + if ( !mbEscherDg ) + { + mbEscherDg = true; + mnCurrentDg = mxGlobal->GenerateDrawingId(); + AddAtom( 8, ESCHER_Dg, 0, mnCurrentDg ); + PtReplaceOrInsert( ESCHER_Persist_Dg | mnCurrentDg, mpOutStrm->Tell() ); + mpOutStrm->WriteUInt32( 0 ) // The number of shapes in this drawing + .WriteUInt32( 0 ); // The last MSOSPID given to an SP in this DG + } + } + } + break; + + case ESCHER_SpgrContainer : + { + if ( mbEscherDg ) + { + mbEscherSpgr = true; + } + } + break; + + case ESCHER_SpContainer : + { + } + break; + + default: + break; + } +} + +void EscherEx::CloseContainer() +{ + sal_uInt32 nSize, nPos = mpOutStrm->Tell(); + nSize = ( nPos - mOffsets.back() ) - 4; + mpOutStrm->Seek( mOffsets.back() ); + mpOutStrm->WriteUInt32( nSize ); + + switch( mRecTypes.back() ) + { + case ESCHER_DgContainer : + { + if ( mbEscherDg ) + { + mbEscherDg = false; + if ( DoSeek( ESCHER_Persist_Dg | mnCurrentDg ) ) + mpOutStrm->WriteUInt32( mxGlobal->GetDrawingShapeCount( mnCurrentDg ) ).WriteUInt32( mxGlobal->GetLastShapeId( mnCurrentDg ) ); + } + } + break; + + case ESCHER_SpgrContainer : + { + if ( mbEscherSpgr ) + { + mbEscherSpgr = false; + + } + } + break; + + default: + break; + } + mOffsets.pop_back(); + mRecTypes.pop_back(); + mpOutStrm->Seek( nPos ); +} + +void EscherEx::BeginAtom() +{ + mnCountOfs = mpOutStrm->Tell(); + mpOutStrm->WriteUInt32( 0 ).WriteUInt32( 0 ); // record header will be written later +} + +void EscherEx::EndAtom( sal_uInt16 nRecType, int nRecVersion, int nRecInstance ) +{ + sal_uInt32 nOldPos = mpOutStrm->Tell(); + mpOutStrm->Seek( mnCountOfs ); + sal_uInt32 nSize = nOldPos - mnCountOfs; + mpOutStrm->WriteUInt16( ( nRecInstance << 4 ) | ( nRecVersion & 0xf ) ).WriteUInt16( nRecType ).WriteUInt32( nSize - 8 ); + mpOutStrm->Seek( nOldPos ); +} + +void EscherEx::AddAtom( sal_uInt32 nAtomSize, sal_uInt16 nRecType, int nRecVersion, int nRecInstance ) +{ + mpOutStrm->WriteUInt16( ( nRecInstance << 4 ) | ( nRecVersion & 0xf ) ).WriteUInt16( nRecType ).WriteUInt32( nAtomSize ); +} + +void EscherEx::AddChildAnchor( const tools::Rectangle& rRect ) +{ + AddAtom( 16, ESCHER_ChildAnchor ); + mpOutStrm ->WriteInt32( rRect.Left() ) + .WriteInt32( rRect.Top() ) + .WriteInt32( rRect.Right() ) + .WriteInt32( rRect.Bottom() ); +} + +void EscherEx::AddClientAnchor( const tools::Rectangle& rRect ) +{ + AddAtom( 8, ESCHER_ClientAnchor ); + mpOutStrm->WriteInt16( rRect.Top() ) + .WriteInt16( rRect.Left() ) + .WriteInt16( rRect.GetWidth() + rRect.Left() ) + .WriteInt16( rRect.GetHeight() + rRect.Top() ); +} + +EscherExHostAppData* EscherEx::EnterAdditionalTextGroup() +{ + return nullptr; +} + +sal_uInt32 EscherEx::EnterGroup( const OUString& rShapeName, const tools::Rectangle* pBoundRect ) +{ + tools::Rectangle aRect; + if( pBoundRect ) + aRect = *pBoundRect; + + OpenContainer( ESCHER_SpgrContainer ); + OpenContainer( ESCHER_SpContainer ); + AddAtom( 16, ESCHER_Spgr, 1 ); + PtReplaceOrInsert( ESCHER_Persist_Grouping_Snap | mnGroupLevel, + mpOutStrm->Tell() ); + mpOutStrm ->WriteInt32( aRect.Left() ) // Bounding box for the grouped shapes to which they will be attached + .WriteInt32( aRect.Top() ) + .WriteInt32( aRect.IsWidthEmpty() ? aRect.Left() : aRect.Right() ) + .WriteInt32( aRect.IsHeightEmpty() ? aRect.Top() : aRect.Bottom() ); + + sal_uInt32 nShapeId = GenerateShapeId(); + if ( !mnGroupLevel ) + AddShape( ESCHER_ShpInst_Min, ShapeFlag::Group | ShapeFlag::Patriarch, nShapeId ); + else + { + AddShape( ESCHER_ShpInst_Min, ShapeFlag::Group | ShapeFlag::HaveAnchor, nShapeId ); + EscherPropertyContainer aPropOpt; + aPropOpt.AddOpt( ESCHER_Prop_LockAgainstGrouping, 0x00040004 ); + aPropOpt.AddOpt( ESCHER_Prop_dxWrapDistLeft, 0 ); + aPropOpt.AddOpt( ESCHER_Prop_dxWrapDistRight, 0 ); + + // #i51348# shape name + if( rShapeName.getLength() > 0 ) + aPropOpt.AddOpt( ESCHER_Prop_wzName, rShapeName ); + + Commit( aPropOpt, aRect ); + if ( mnGroupLevel > 1 ) + AddChildAnchor( aRect ); + + EscherExHostAppData* pAppData = mpImplEESdrWriter->ImplGetHostData(); + if( pAppData ) + { + if ( mnGroupLevel <= 1 ) + pAppData->WriteClientAnchor( *this, aRect ); + pAppData->WriteClientData( *this ); + } + } + CloseContainer(); // ESCHER_SpContainer + mnGroupLevel++; + return nShapeId; +} + +sal_uInt32 EscherEx::EnterGroup( const tools::Rectangle* pBoundRect ) +{ + return EnterGroup( OUString(), pBoundRect ); +} + +void EscherEx::SetGroupSnapRect( sal_uInt32 nGroupLevel, const tools::Rectangle& rRect ) +{ + if ( nGroupLevel ) + { + sal_uInt32 nCurrentPos = mpOutStrm->Tell(); + if ( DoSeek( ESCHER_Persist_Grouping_Snap | ( nGroupLevel - 1 ) ) ) + { + mpOutStrm ->WriteInt32( rRect.Left() ) // Bounding box for the grouped shapes to which they will be attached + .WriteInt32( rRect.Top() ) + .WriteInt32( rRect.Right() ) + .WriteInt32( rRect.Bottom() ); + mpOutStrm->Seek( nCurrentPos ); + } + } +} + +void EscherEx::SetGroupLogicRect( sal_uInt32 nGroupLevel, const tools::Rectangle& rRect ) +{ + if ( nGroupLevel ) + { + sal_uInt32 nCurrentPos = mpOutStrm->Tell(); + if ( DoSeek( ESCHER_Persist_Grouping_Logic | ( nGroupLevel - 1 ) ) ) + { + mpOutStrm->WriteInt16( rRect.Top() ).WriteInt16( rRect.Left() ).WriteInt16( rRect.Right() ).WriteInt16( rRect.Bottom() ); + mpOutStrm->Seek( nCurrentPos ); + } + } +} + +void EscherEx::LeaveGroup() +{ + --mnGroupLevel; + PtDelete( ESCHER_Persist_Grouping_Snap | mnGroupLevel ); + PtDelete( ESCHER_Persist_Grouping_Logic | mnGroupLevel ); + CloseContainer(); +} + +void EscherEx::AddShape( sal_uInt32 nShpInstance, ShapeFlag nFlags, sal_uInt32 nShapeID ) +{ + AddAtom( 8, ESCHER_Sp, 2, nShpInstance ); + + if ( !nShapeID ) + nShapeID = GenerateShapeId(); + + if (nFlags ^ ShapeFlag::Group) // no pure group shape + { + if ( mnGroupLevel > 1 ) + nFlags |= ShapeFlag::Child; // this not a topmost shape + } + mpOutStrm->WriteUInt32( nShapeID ).WriteUInt32( static_cast(nFlags) ); +} + +void EscherEx::Commit( EscherPropertyContainer& rProps, const tools::Rectangle& ) +{ + rProps.Commit( GetStream() ); +} + +sal_uInt32 EscherEx::GetColor( const sal_uInt32 nSOColor ) +{ + sal_uInt32 nColor = nSOColor & 0xff00; // Green + nColor |= static_cast(nSOColor) << 16; // Red + nColor |= static_cast( nSOColor >> 16 ); // Blue + return nColor; +} + +sal_uInt32 EscherEx::GetColor( const Color& rSOColor ) +{ + sal_uInt32 nColor = ( rSOColor.GetRed() << 16 ); + nColor |= ( rSOColor.GetGreen() << 8 ); + nColor |= rSOColor.GetBlue(); + nColor = GetColor( nColor ); + return nColor; +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/filter/source/msfilter/eschesdo.cxx b/filter/source/msfilter/eschesdo.cxx new file mode 100644 index 000000000..d052b5968 --- /dev/null +++ b/filter/source/msfilter/eschesdo.cxx @@ -0,0 +1,1237 @@ +/* -*- 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 +#include "eschesdo.hxx" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +using namespace ::com::sun::star; +using namespace ::com::sun::star::beans; +using namespace ::com::sun::star::container; +using namespace ::com::sun::star::uno; +using namespace ::com::sun::star::drawing; +using namespace ::com::sun::star::text; +using namespace ::com::sun::star::task; + +constexpr o3tl::Length geUnitsSrc(o3tl::Length::mm100); +// PowerPoint: 576 dpi, WinWord: 1440 dpi, Excel: 1440 dpi +constexpr o3tl::Length geUnitsDest(o3tl::Length::twip); + +ImplEESdrWriter::ImplEESdrWriter( EscherEx& rEx ) + : mpEscherEx(&rEx) + , mpPicStrm(nullptr) + , mpHostAppData(nullptr) + , mbIsTitlePossible(false) + , mpSdrPage( nullptr ) +{ +} + + + +Point ImplEESdrWriter::ImplMapPoint( const Point& rPoint ) +{ + return o3tl::convert( rPoint, geUnitsSrc, geUnitsDest ); +} + +Size ImplEESdrWriter::ImplMapSize( const Size& rSize ) +{ + Size aRetSize( o3tl::convert( rSize, geUnitsSrc, geUnitsDest ) ); + + if ( !aRetSize.Width() ) + aRetSize.AdjustWidth( 1 ); + if ( !aRetSize.Height() ) + aRetSize.AdjustHeight( 1 ); + return aRetSize; +} + +void ImplEESdrWriter::ImplFlipBoundingBox( ImplEESdrObject& rObj, EscherPropertyContainer& rPropOpt ) +{ + sal_Int32 nAngle = rObj.GetAngle(); + tools::Rectangle aRect( rObj.GetRect() ); + + // for position calculations, we normalize the angle between 0 and 90 degrees + if ( nAngle < 0 ) + nAngle = ( 36000 + nAngle ) % 36000; + if ( nAngle % 18000 == 0 ) + nAngle = 0; + while ( nAngle > 9000 ) + nAngle = ( 18000 - ( nAngle % 18000 ) ); + + double fVal = basegfx::deg2rad<100>(nAngle); + double fCos = cos( fVal ); + double fSin = sin( fVal ); + + double nWidthHalf = static_cast(aRect.GetWidth()) / 2; + double nHeightHalf = static_cast(aRect.GetHeight()) / 2; + + // fdo#70838: + // when you rotate an object, the top-left corner of its bounding box is moved + // nXDiff and nYDiff pixels. To get their values we use these equations: + // + // fSin * nHeightHalf + fCos * nWidthHalf == nXDiff + nWidthHalf + // fSin * nWidthHalf + fCos * nHeightHalf == nYDiff + nHeightHalf + + double nXDiff = fSin * nHeightHalf + fCos * nWidthHalf - nWidthHalf; + double nYDiff = fSin * nWidthHalf + fCos * nHeightHalf - nHeightHalf; + + aRect.Move( static_cast(nXDiff), static_cast(nYDiff) ); + + // calculate the proper angle value to be saved + nAngle = rObj.GetAngle(); + if ( nAngle < 0 ) + nAngle = ( 36000 + nAngle ) % 36000; + else + nAngle = ( 36000 - ( nAngle % 36000 ) ); + + nAngle *= 655; + nAngle += 0x8000; + nAngle &=~0xffff; // nAngle round to full degrees + rPropOpt.AddOpt( ESCHER_Prop_Rotation, nAngle ); + + rObj.SetAngle( nAngle ); + rObj.SetRect( aRect ); +} + + +sal_uInt32 ImplEESdrWriter::ImplWriteShape( ImplEESdrObject& rObj, + EscherSolverContainer& rSolverContainer, + const bool bOOxmlExport ) +{ + sal_uInt32 nShapeID = 0; + sal_uInt16 nShapeType = 0; + bool bDontWriteText = false; // if a metafile is written as shape replacement, then the text is already part of the metafile + bool bAdditionalText = false; + sal_uInt32 nGrpShapeID = 0; + auto addShape = [this, &rObj, &rSolverContainer, &nShapeID, &nShapeType](sal_uInt16 nType, ShapeFlag nFlags) + { + nShapeType = nType; + nShapeID = mpEscherEx->GenerateShapeId(); + rObj.SetShapeId( nShapeID ); + mpEscherEx->AddShape( nType, nFlags, nShapeID ); + rSolverContainer.AddShape( rObj.GetShapeRef(), nShapeID ); + }; + + do { + mpHostAppData = mpEscherEx->StartShape( rObj.GetShapeRef(), (mpEscherEx->GetGroupLevel() > 1) ? &rObj.GetRect() : nullptr ); + if ( mpHostAppData && mpHostAppData->DontWriteShape() ) + break; + + // #i51348# get shape name + OUString aShapeName; + if( const SdrObject* pSdrObj = rObj.GetSdrObject() ) + if (!pSdrObj->GetName().isEmpty()) + aShapeName = pSdrObj->GetName(); + uno::Reference< drawing::XShape> xShape = rObj.GetShapeRef(); + if (xShape.is()) + { + uno::Reference xPropertySet(xShape, uno::UNO_QUERY); + if (xPropertySet.is()) + { + uno::Sequence aGrabBag; + uno::Reference< XPropertySetInfo > xPropInfo = xPropertySet->getPropertySetInfo(); + if ( xPropInfo.is() && xPropInfo->hasPropertyByName( "InteropGrabBag" ) ) + { + xPropertySet->getPropertyValue( "InteropGrabBag" ) >>= aGrabBag; + for (const beans::PropertyValue& rProp : std::as_const(aGrabBag)) + { + if (rProp.Name == "mso-edit-as") + { + OUString rEditAs; + rProp.Value >>= rEditAs; + mpEscherEx->SetEditAs(rEditAs); + break; + } + } + } + } + } + + if( rObj.GetType() == "drawing.Group" ) + { + Reference< XIndexAccess > xXIndexAccess( rObj.GetShapeRef(), UNO_QUERY ); + + if( xXIndexAccess.is() && 0 != xXIndexAccess->getCount() ) + { + nShapeID = mpEscherEx->EnterGroup( aShapeName, &rObj.GetRect() ); + nShapeType = ESCHER_ShpInst_Min; + + for( sal_uInt32 n = 0, nCnt = xXIndexAccess->getCount(); + n < nCnt; ++n ) + { + ImplEESdrObject aObj( *o3tl::doAccess>( + xXIndexAccess->getByIndex( n )) ); + if( aObj.IsValid() ) + { + aObj.SetOOXML(bOOxmlExport); + ImplWriteShape( aObj, rSolverContainer, bOOxmlExport ); + } + } + mpEscherEx->LeaveGroup(); + } + break; + } + rObj.SetAngle( rObj.ImplGetInt32PropertyValue( "RotateAngle" )); + + if( ( rObj.ImplGetPropertyValue( "IsFontwork" ) && + ::cppu::any2bool( rObj.GetUsrAny() ) ) || + rObj.GetType() == "drawing.Measure" ) + { + rObj.SetType("drawing.dontknow"); + } + + const css::awt::Size aSize100thmm( rObj.GetShapeRef()->getSize() ); + const css::awt::Point aPoint100thmm( rObj.GetShapeRef()->getPosition() ); + tools::Rectangle aRect100thmm( Point( aPoint100thmm.X, aPoint100thmm.Y ), Size( aSize100thmm.Width, aSize100thmm.Height ) ); + if ( !mpPicStrm ) + mpPicStrm = mpEscherEx->QueryPictureStream(); + EscherPropertyContainer aPropOpt( mpEscherEx->GetGraphicProvider(), mpPicStrm, aRect100thmm ); + + // #i51348# shape name + if (!aShapeName.isEmpty()) + aPropOpt.AddOpt( ESCHER_Prop_wzName, aShapeName ); + if ( InteractionInfo* pInteraction = mpHostAppData ? mpHostAppData->GetInteractionInfo():nullptr ) + { + const std::unique_ptr< SvMemoryStream >& pMemStrm = pInteraction->getHyperlinkRecord(); + if (pMemStrm) + { + aPropOpt.AddOpt(ESCHER_Prop_pihlShape, false, 0, *pMemStrm); + } + aPropOpt.AddOpt( ESCHER_Prop_fPrint, 0x00080008 ); + } + + if ( rObj.GetType() == "drawing.Custom" ) + { + mpEscherEx->OpenContainer( ESCHER_SpContainer ); + ShapeFlag nMirrorFlags; + + OUString sCustomShapeType; + MSO_SPT eShapeType = EscherPropertyContainer::GetCustomShapeType( rObj.GetShapeRef(), nMirrorFlags, sCustomShapeType, rObj.GetOOXML() ); + if ( sCustomShapeType == "col-502ad400" || sCustomShapeType == "col-60da8460" ) + { + addShape( ESCHER_ShpInst_PictureFrame, ShapeFlag::HaveShapeProperty | ShapeFlag::HaveAnchor ); + + if ( aPropOpt.CreateGraphicProperties( rObj.mXPropSet, "MetaFile", false ) ) + { + aPropOpt.AddOpt( ESCHER_Prop_LockAgainstGrouping, 0x800080 ); + aPropOpt.AddOpt( ESCHER_Prop_fNoFillHitTest, 0x100000 ); // no fill + aPropOpt.AddOpt( ESCHER_Prop_fNoLineDrawDash, 0x90000 ); // no linestyle + SdrObject* pObj = SdrObject::getSdrObjectFromXShape(rObj.GetShapeRef()); + if ( pObj ) + { + tools::Rectangle aBound = pObj->GetCurrentBoundRect(); + Point aPosition( ImplMapPoint( aBound.TopLeft() ) ); + Size aSize( ImplMapSize( aBound.GetSize() ) ); + rObj.SetRect( tools::Rectangle( aPosition, aSize ) ); + rObj.SetAngle( 0 ); + bDontWriteText = true; + } + } + } + else + { + const Reference< XPropertySet > xPropSet = rObj.mXPropSet; + drawing::FillStyle eFS = drawing::FillStyle_NONE; + if(xPropSet.is()) + { + uno::Reference< XPropertySetInfo > xPropInfo = xPropSet->getPropertySetInfo(); + if ( xPropInfo.is() && xPropInfo->hasPropertyByName("FillStyle")) + xPropSet->getPropertyValue("FillStyle") >>= eFS; + } + + if (eFS == drawing::FillStyle_BITMAP && eShapeType == mso_sptMax) + { + // We can't map this custom shape to a DOC preset and it has a bitmap fill. + // Make sure that at least the bitmap fill is not lost. + addShape( ESCHER_ShpInst_PictureFrame, ShapeFlag::HaveShapeProperty | ShapeFlag::HaveAnchor ); + if ( aPropOpt.CreateGraphicProperties( rObj.mXPropSet, "Bitmap", false, true, true, bOOxmlExport ) ) + aPropOpt.AddOpt( ESCHER_Prop_LockAgainstGrouping, 0x800080 ); + } + else + { + addShape(sal::static_int_cast< sal_uInt16 >(eShapeType), + nMirrorFlags | ShapeFlag::HaveShapeProperty | ShapeFlag::HaveAnchor); + aPropOpt.CreateCustomShapeProperties( eShapeType, rObj.GetShapeRef() ); + aPropOpt.CreateFillProperties( rObj.mXPropSet, true ); + if ( rObj.ImplGetText() ) + { + if ( !aPropOpt.IsFontWork() ) + aPropOpt.CreateTextProperties( rObj.mXPropSet, mpEscherEx->QueryTextID( + rObj.GetShapeRef(), rObj.GetShapeId() ), true, false ); + } + } + } + } + else if ( rObj.GetType() == "drawing.Rectangle" ) + { + mpEscherEx->OpenContainer( ESCHER_SpContainer ); + sal_Int32 nRadius = rObj.ImplGetInt32PropertyValue("CornerRadius"); + if( nRadius ) + { + nRadius = ImplMapSize( Size( nRadius, 0 )).Width(); + addShape( ESCHER_ShpInst_RoundRectangle, ShapeFlag::HaveShapeProperty | ShapeFlag::HaveAnchor ); + sal_Int32 nLength = rObj.GetRect().GetWidth(); + if ( nLength > rObj.GetRect().GetHeight() ) + nLength = rObj.GetRect().GetHeight(); + nLength >>= 1; + if ( nRadius >= nLength || nLength == 0 ) + nRadius = 0x2a30; // 0x2a30 is PPTs maximum radius + else + nRadius = ( 0x2a30 * nRadius ) / nLength; + aPropOpt.AddOpt( ESCHER_Prop_adjustValue, nRadius ); + } + else + { + addShape( ESCHER_ShpInst_Rectangle, ShapeFlag::HaveShapeProperty | ShapeFlag::HaveAnchor ); + } + aPropOpt.CreateFillProperties( rObj.mXPropSet, true ); + if( rObj.ImplGetText() ) + aPropOpt.CreateTextProperties( rObj.mXPropSet, + mpEscherEx->QueryTextID( rObj.GetShapeRef(), + rObj.GetShapeId() ), false, false ); + } + else if ( rObj.GetType() == "drawing.Ellipse" ) + { + CircleKind eCircleKind = CircleKind_FULL; + PolyStyle ePolyKind = PolyStyle(); + if ( rObj.ImplGetPropertyValue( "CircleKind" ) ) + { + eCircleKind = *o3tl::doAccess(rObj.GetUsrAny()); + switch ( eCircleKind ) + { + case CircleKind_SECTION : + { + ePolyKind = PolyStyle::Pie; + } + break; + case CircleKind_ARC : + { + ePolyKind = PolyStyle::Arc; + } + break; + + case CircleKind_CUT : + { + ePolyKind = PolyStyle::Chord; + } + break; + + default: + eCircleKind = CircleKind_FULL; + } + } + if ( eCircleKind == CircleKind_FULL ) + { + mpEscherEx->OpenContainer( ESCHER_SpContainer ); + addShape( ESCHER_ShpInst_Ellipse, ShapeFlag::HaveShapeProperty | ShapeFlag::HaveAnchor ); + aPropOpt.CreateFillProperties( rObj.mXPropSet, true ); + } + else + { + sal_Int32 nStartAngle, nEndAngle; + if ( !rObj.ImplGetPropertyValue( "CircleStartAngle" ) ) + break; + nStartAngle = *o3tl::doAccess(rObj.GetUsrAny()); + if( !rObj.ImplGetPropertyValue( "CircleEndAngle" ) ) + break; + nEndAngle = *o3tl::doAccess(rObj.GetUsrAny()); + + Point aStart, aEnd, aCenter; + aStart.setX( static_cast( cos( basegfx::deg2rad<100>(nStartAngle) ) * 100.0 ) ); + aStart.setY( - static_cast( sin( basegfx::deg2rad<100>(nStartAngle) ) * 100.0 ) ); + aEnd.setX( static_cast( cos( basegfx::deg2rad<100>(nEndAngle) ) * 100.0 ) ); + aEnd.setY( - static_cast( sin( basegfx::deg2rad<100>(nEndAngle) ) * 100.0 ) ); + const tools::Rectangle& rRect = aRect100thmm; + aCenter.setX( rRect.Left() + ( rRect.GetWidth() / 2 ) ); + aCenter.setY( rRect.Top() + ( rRect.GetHeight() / 2 ) ); + aStart.AdjustX(aCenter.X() ); + aStart.AdjustY(aCenter.Y() ); + aEnd.AdjustX(aCenter.X() ); + aEnd.AdjustY(aCenter.Y() ); + tools::Polygon aPolygon( rRect, aStart, aEnd, ePolyKind ); + if( rObj.GetAngle() ) + { + aPolygon.Rotate( rRect.TopLeft(), Degree10(static_cast( rObj.GetAngle() / 10 )) ); + rObj.SetAngle( 0 ); + } + mpEscherEx->OpenContainer( ESCHER_SpContainer ); + addShape( ESCHER_ShpInst_NotPrimitive, ShapeFlag::HaveShapeProperty | ShapeFlag::HaveAnchor ); + css::awt::Rectangle aNewRect; + switch ( ePolyKind ) + { + case PolyStyle::Pie : + case PolyStyle::Chord : + { + aPropOpt.CreatePolygonProperties( rObj.mXPropSet, ESCHER_CREATEPOLYGON_POLYPOLYGON, false, aNewRect, &aPolygon ); + aPropOpt.CreateFillProperties( rObj.mXPropSet, true ); + } + break; + + case PolyStyle::Arc : + { + aPropOpt.CreatePolygonProperties( rObj.mXPropSet, ESCHER_CREATEPOLYGON_POLYLINE, false, aNewRect, &aPolygon ); + aPropOpt.CreateLineProperties( rObj.mXPropSet, false ); + } + break; + } + rObj.SetRect( tools::Rectangle( ImplMapPoint( Point( aNewRect.X, aNewRect.Y ) ), + ImplMapSize( Size( aNewRect.Width, aNewRect.Height ) ) ) ); + } + if ( rObj.ImplGetText() ) + aPropOpt.CreateTextProperties( rObj.mXPropSet, + mpEscherEx->QueryTextID( rObj.GetShapeRef(), + rObj.GetShapeId() ), false, false ); + + } + else if ( rObj.GetType() == "drawing.Control" ) + { + const Reference< XPropertySet > xPropSet = rObj.mXPropSet; + const Reference xPropInfo = xPropSet.is() ? xPropSet->getPropertySetInfo() : Reference(); + // This code is expected to be called only for DOCX/XLSX formats. + if (xPropInfo.is() && bOOxmlExport) + { + bool bInline = false; + if (xPropInfo->hasPropertyByName("AnchorType")) + { + text::TextContentAnchorType eAnchorType; + xPropSet->getPropertyValue("AnchorType") >>= eAnchorType; + bInline = eAnchorType == text::TextContentAnchorType_AS_CHARACTER; + } + + mpEscherEx->OpenContainer( ESCHER_SpContainer ); + if(bInline) + { + addShape( ESCHER_ShpInst_PictureFrame, ShapeFlag::HaveShapeProperty | ShapeFlag::HaveAnchor ); + } + else + { + addShape( ESCHER_ShpInst_HostControl, ShapeFlag::HaveShapeProperty | ShapeFlag::HaveAnchor ); + } + } + else + break; + } + else if ( rObj.GetType() == "drawing.Connector" ) + { + sal_uInt16 nSpType; + ShapeFlag nSpFlags; + css::awt::Rectangle aNewRect; + if ( ! aPropOpt.CreateConnectorProperties( rObj.GetShapeRef(), + rSolverContainer, aNewRect, nSpType, nSpFlags ) ) + break; + rObj.SetRect( tools::Rectangle( ImplMapPoint( Point( aNewRect.X, aNewRect.Y ) ), + ImplMapSize( Size( aNewRect.Width, aNewRect.Height ) ) ) ); + + mpEscherEx->OpenContainer( ESCHER_SpContainer ); + addShape( nSpType, nSpFlags ); + } + else if ( rObj.GetType() == "drawing.Measure" ) + { + break; + } + else if ( rObj.GetType() == "drawing.Line" ) + { + css::awt::Rectangle aNewRect; + aPropOpt.CreatePolygonProperties( rObj.mXPropSet, ESCHER_CREATEPOLYGON_LINE, false, aNewRect ); + //i27942: Poly/Lines/Bezier do not support text. + + mpEscherEx->OpenContainer( ESCHER_SpContainer ); + ShapeFlag nFlags = ShapeFlag::HaveShapeProperty | ShapeFlag::HaveAnchor; + if( aNewRect.Height < 0 ) + nFlags |= ShapeFlag::FlipV; + if( aNewRect.Width < 0 ) + nFlags |= ShapeFlag::FlipH; + + addShape( ESCHER_ShpInst_Line, nFlags ); + aPropOpt.AddOpt( ESCHER_Prop_shapePath, ESCHER_ShapeComplex ); + aPropOpt.CreateLineProperties( rObj.mXPropSet, false ); + rObj.SetAngle( 0 ); + } + else if ( rObj.GetType() == "drawing.PolyPolygon" ) + { + if( rObj.ImplHasText() ) + { + nGrpShapeID = ImplEnterAdditionalTextGroup( rObj.GetShapeRef(), &rObj.GetRect() ); + bAdditionalText = true; + } + mpEscherEx->OpenContainer( ESCHER_SpContainer ); + addShape( ESCHER_ShpInst_NotPrimitive, ShapeFlag::HaveShapeProperty | ShapeFlag::HaveAnchor ); + css::awt::Rectangle aNewRect; + aPropOpt.CreatePolygonProperties( rObj.mXPropSet, ESCHER_CREATEPOLYGON_POLYPOLYGON, false, aNewRect ); + aPropOpt.CreateFillProperties( rObj.mXPropSet, true ); + rObj.SetAngle( 0 ); + } + else if ( rObj.GetType() == "drawing.PolyLine" ) + { + //i27942: Poly/Lines/Bezier do not support text. + + mpEscherEx->OpenContainer( ESCHER_SpContainer ); + addShape( ESCHER_ShpInst_NotPrimitive, ShapeFlag::HaveShapeProperty | ShapeFlag::HaveAnchor ); + css::awt::Rectangle aNewRect; + aPropOpt.CreatePolygonProperties( rObj.mXPropSet, ESCHER_CREATEPOLYGON_POLYLINE, false, aNewRect ); + aPropOpt.CreateLineProperties( rObj.mXPropSet, false ); + rObj.SetAngle( 0 ); + } + else if ( rObj.GetType() == "drawing.OpenBezier" ) + { + //i27942: Poly/Lines/Bezier do not support text. + + mpEscherEx->OpenContainer( ESCHER_SpContainer ); + addShape( ESCHER_ShpInst_NotPrimitive, ShapeFlag::HaveShapeProperty | ShapeFlag::HaveAnchor ); + css::awt::Rectangle aNewRect; + aPropOpt.CreatePolygonProperties( rObj.mXPropSet, ESCHER_CREATEPOLYGON_POLYLINE, true, aNewRect ); + aPropOpt.CreateLineProperties( rObj.mXPropSet, false ); + rObj.SetAngle( 0 ); + } + else if ( rObj.GetType() == "drawing.ClosedBezier" ) + { + if ( rObj.ImplHasText() ) + { + nGrpShapeID = ImplEnterAdditionalTextGroup( rObj.GetShapeRef(), &rObj.GetRect() ); + bAdditionalText = true; + } + mpEscherEx->OpenContainer( ESCHER_SpContainer ); + addShape( ESCHER_ShpInst_NotPrimitive, ShapeFlag::HaveShapeProperty | ShapeFlag::HaveAnchor ); + css::awt::Rectangle aNewRect; + aPropOpt.CreatePolygonProperties( rObj.mXPropSet, ESCHER_CREATEPOLYGON_POLYPOLYGON, true, aNewRect ); + aPropOpt.CreateFillProperties( rObj.mXPropSet, true ); + rObj.SetAngle( 0 ); + } + else if ( rObj.GetType() == "drawing.GraphicObject" ) + { + mpEscherEx->OpenContainer( ESCHER_SpContainer ); + + // a GraphicObject can also be a ClickMe element + if( rObj.IsEmptyPresObj() ) + { + addShape( ESCHER_ShpInst_Rectangle, ShapeFlag::HaveMaster | ShapeFlag::HaveAnchor ); + sal_uInt32 nTxtBxId = mpEscherEx->QueryTextID( rObj.GetShapeRef(), + rObj.GetShapeId() ); + aPropOpt.AddOpt( ESCHER_Prop_lTxid, nTxtBxId ); + aPropOpt.AddOpt( ESCHER_Prop_fNoFillHitTest, 0x10001 ); + aPropOpt.AddOpt( ESCHER_Prop_fNoLineDrawDash, 0x10001 ); + aPropOpt.AddOpt( ESCHER_Prop_hspMaster, 0 ); + } + else + { + if( rObj.ImplGetText() ) + { + /* SJ #i34951#: because M. documents are not allowing GraphicObjects containing text, we + have to create a simple Rectangle with fill bitmap instead (while not allowing BitmapMode_Repeat). + */ + addShape( ESCHER_ShpInst_Rectangle, ShapeFlag::HaveShapeProperty | ShapeFlag::HaveAnchor ); + if ( aPropOpt.CreateGraphicProperties( rObj.mXPropSet, "Graphic", true, true, false ) ) + { + aPropOpt.AddOpt( ESCHER_Prop_WrapText, ESCHER_WrapNone ); + aPropOpt.AddOpt( ESCHER_Prop_AnchorText, ESCHER_AnchorMiddle ); + aPropOpt.AddOpt( ESCHER_Prop_fNoFillHitTest, 0x140014 ); + aPropOpt.AddOpt( ESCHER_Prop_fillBackColor, 0x8000000 ); + aPropOpt.AddOpt( ESCHER_Prop_fNoLineDrawDash, 0x80000 ); + if ( rObj.ImplGetText() ) + aPropOpt.CreateTextProperties( rObj.mXPropSet, + mpEscherEx->QueryTextID( rObj.GetShapeRef(), + rObj.GetShapeId() ), false, false ); + } + } + else + { + addShape( ESCHER_ShpInst_PictureFrame, ShapeFlag::HaveShapeProperty | ShapeFlag::HaveAnchor ); + if ( aPropOpt.CreateGraphicProperties( rObj.mXPropSet, "Graphic", false, true, true, bOOxmlExport ) ) + aPropOpt.AddOpt( ESCHER_Prop_LockAgainstGrouping, 0x800080 ); + } + } + } + else if ( rObj.GetType() == "drawing.Text" ) + { + mpEscherEx->OpenContainer( ESCHER_SpContainer ); + addShape( ESCHER_ShpInst_TextBox, ShapeFlag::HaveShapeProperty | ShapeFlag::HaveAnchor ); + aPropOpt.CreateFillProperties( rObj.mXPropSet, true ); + if( rObj.ImplGetText() ) + aPropOpt.CreateTextProperties( rObj.mXPropSet, + mpEscherEx->QueryTextID( rObj.GetShapeRef(), + rObj.GetShapeId() ) ); + } + else if ( rObj.GetType() == "drawing.Page" ) + { + mpEscherEx->OpenContainer( ESCHER_SpContainer ); + addShape( ESCHER_ShpInst_Rectangle, ShapeFlag::HaveShapeProperty | ShapeFlag::HaveAnchor ); + aPropOpt.AddOpt( ESCHER_Prop_LockAgainstGrouping, 0x40004 ); + aPropOpt.AddOpt( ESCHER_Prop_fFillOK, 0x100001 ); + aPropOpt.AddOpt( ESCHER_Prop_fNoFillHitTest, 0x110011 ); + aPropOpt.AddOpt( ESCHER_Prop_fNoLineDrawDash, 0x90008 ); + aPropOpt.AddOpt( ESCHER_Prop_fshadowObscured, 0x10001 ); + } + else if ( rObj.GetType() == "drawing.Frame" ) + { + break; + } + else if ( rObj.GetType() == "drawing.OLE2" ) + { + mpEscherEx->OpenContainer( ESCHER_SpContainer ); + if( rObj.IsEmptyPresObj() ) + { + addShape( ESCHER_ShpInst_Rectangle, ShapeFlag::HaveMaster | ShapeFlag::HaveAnchor ); + sal_uInt32 nTxtBxId = mpEscherEx->QueryTextID( rObj.GetShapeRef(), + rObj.GetShapeId() ); + aPropOpt.AddOpt( ESCHER_Prop_lTxid, nTxtBxId ); + aPropOpt.AddOpt( ESCHER_Prop_fNoFillHitTest, 0x10001 ); + aPropOpt.AddOpt( ESCHER_Prop_fNoLineDrawDash, 0x10001 ); + aPropOpt.AddOpt( ESCHER_Prop_hspMaster, 0 ); + } + else + { + //2do: could be made an option in HostAppData whether OLE object should be written or not + const bool bAppOLE = true; + addShape( ESCHER_ShpInst_PictureFrame, + ShapeFlag::HaveShapeProperty | ShapeFlag::HaveAnchor | (bAppOLE ? ShapeFlag::OLEShape : ShapeFlag::NONE) ); + if ( aPropOpt.CreateOLEGraphicProperties( rObj.GetShapeRef() ) ) + { + if ( bAppOLE ) + { // snooped from Xcl hex dump, nobody knows the trouble I have seen + aPropOpt.AddOpt( ESCHER_Prop_FitTextToShape, 0x00080008 ); + aPropOpt.AddOpt( ESCHER_Prop_pictureId, 0x00000001 ); + aPropOpt.AddOpt( ESCHER_Prop_fillColor, 0x08000041 ); + aPropOpt.AddOpt( ESCHER_Prop_fillBackColor, 0x08000041 ); + aPropOpt.AddOpt( ESCHER_Prop_fNoFillHitTest, 0x00110010 ); + aPropOpt.AddOpt( ESCHER_Prop_lineColor, 0x08000040 ); + aPropOpt.AddOpt( ESCHER_Prop_fNoLineDrawDash,0x00080008 ); + aPropOpt.AddOpt( ESCHER_Prop_fPrint, 0x00080000 ); + } + aPropOpt.AddOpt( ESCHER_Prop_LockAgainstGrouping, 0x800080 ); + } + } + } + else if( '3' == rObj.GetType()[8] && + 'D' == rObj.GetType()[9] ) // drawing.3D + { + // SceneObject, CubeObject, SphereObject, LatheObject, ExtrudeObject, PolygonObject + if ( !rObj.ImplGetPropertyValue( "Bitmap" ) ) + break; + + mpEscherEx->OpenContainer( ESCHER_SpContainer ); + addShape( ESCHER_ShpInst_PictureFrame, ShapeFlag::HaveShapeProperty | ShapeFlag::HaveAnchor ); + + if ( aPropOpt.CreateGraphicProperties( rObj.mXPropSet, "Bitmap", false ) ) + aPropOpt.AddOpt( ESCHER_Prop_LockAgainstGrouping, 0x800080 ); + } + else if ( rObj.GetType() == "drawing.Caption" ) + { + rObj.SetAngle( 0 ); + mpEscherEx->OpenContainer( ESCHER_SpContainer ); + addShape( ESCHER_ShpInst_TextBox, ShapeFlag::HaveShapeProperty | ShapeFlag::HaveAnchor ); + if ( aPropOpt.CreateGraphicProperties( rObj.mXPropSet, "MetaFile", false ) ) + aPropOpt.AddOpt( ESCHER_Prop_LockAgainstGrouping, 0x800080 ); + } + else if ( rObj.GetType() == "drawing.dontknow" ) + { + rObj.SetAngle( 0 ); + mpEscherEx->OpenContainer( ESCHER_SpContainer ); + addShape( ESCHER_ShpInst_PictureFrame, ShapeFlag::HaveShapeProperty | ShapeFlag::HaveAnchor ); + if ( aPropOpt.CreateGraphicProperties( rObj.mXPropSet, "MetaFile", false ) ) + aPropOpt.AddOpt( ESCHER_Prop_LockAgainstGrouping, 0x800080 ); + } + else + { + break; + } + aPropOpt.CreateShadowProperties( rObj.mXPropSet ); + + if( SDRLAYER_NOTFOUND != mpEscherEx->GetHellLayerId() && + rObj.ImplGetPropertyValue( "LayerID" ) && + *o3tl::doAccess(rObj.GetUsrAny()) == mpEscherEx->GetHellLayerId().get() ) + { + aPropOpt.AddOpt( ESCHER_Prop_fPrint, 0x200020 ); + } + + { + tools::Rectangle aRect( rObj.GetRect() ); + aRect.Justify(); + rObj.SetRect( aRect ); + } + + if( rObj.GetAngle() ) + ImplFlipBoundingBox( rObj, aPropOpt ); + + aPropOpt.CreateShapeProperties( rObj.GetShapeRef() ); + const SdrObject* sdrObj = rObj.GetSdrObject(); + mpEscherEx->AddSdrObjectVMLObject(*sdrObj ); + mpEscherEx->Commit( aPropOpt, rObj.GetRect()); + if( mpEscherEx->GetGroupLevel() > 1 ) + mpEscherEx->AddChildAnchor( rObj.GetRect() ); + + if ( mpHostAppData ) + { //! with AdditionalText the App has to control whether these are written or not + mpHostAppData->WriteClientAnchor( *mpEscherEx, rObj.GetRect() ); + mpHostAppData->WriteClientData( *mpEscherEx ); + if ( !bDontWriteText ) + mpHostAppData->WriteClientTextbox( *mpEscherEx ); + } + mpEscherEx->CloseContainer(); // ESCHER_SpContainer + + if( bAdditionalText ) + { + mpEscherEx->EndShape( nShapeType, nShapeID ); + ImplWriteAdditionalText( rObj ); + } + + } while ( false ); + + if ( bAdditionalText ) + mpEscherEx->EndShape( ESCHER_ShpInst_Min, nGrpShapeID ); + else + mpEscherEx->EndShape( nShapeType, nShapeID ); + return nShapeID; +} + +void ImplEESdrWriter::ImplWriteAdditionalText( ImplEESdrObject& rObj ) +{ + sal_uInt32 nShapeID = 0; + sal_uInt16 nShapeType = 0; + do + { + mpHostAppData = mpEscherEx->StartShape( rObj.GetShapeRef(), (mpEscherEx->GetGroupLevel() > 1) ? &rObj.GetRect() : nullptr ); + if ( mpHostAppData && mpHostAppData->DontWriteShape() ) + break; + + const css::awt::Size aSize100thmm( rObj.GetShapeRef()->getSize() ); + const css::awt::Point aPoint100thmm( rObj.GetShapeRef()->getPosition() ); + tools::Rectangle aRect100thmm( Point( aPoint100thmm.X, aPoint100thmm.Y ), Size( aSize100thmm.Width, aSize100thmm.Height ) ); + if ( !mpPicStrm ) + mpPicStrm = mpEscherEx->QueryPictureStream(); + EscherPropertyContainer aPropOpt( mpEscherEx->GetGraphicProvider(), mpPicStrm, aRect100thmm ); + rObj.SetAngle( rObj.ImplGetInt32PropertyValue( "RotateAngle" )); + sal_Int32 nAngle = rObj.GetAngle(); + if( rObj.GetType() == "drawing.Line" ) + { +//2do: this does not work right + double fDist = hypot( rObj.GetRect().GetWidth(), + rObj.GetRect().GetHeight() ); + rObj.SetRect( tools::Rectangle( Point(), + Point( static_cast( fDist ), -1 ) ) ); + + mpEscherEx->OpenContainer( ESCHER_SpContainer ); + mpEscherEx->AddShape( ESCHER_ShpInst_TextBox, ShapeFlag::HaveShapeProperty | ShapeFlag::HaveAnchor ); + if ( rObj.ImplGetText() ) + aPropOpt.CreateTextProperties( rObj.mXPropSet, + mpEscherEx->QueryTextID( rObj.GetShapeRef(), + rObj.GetShapeId() ) ); + + aPropOpt.AddOpt( ESCHER_Prop_fNoLineDrawDash, 0x90000 ); + aPropOpt.AddOpt( ESCHER_Prop_fNoFillHitTest, 0x100000 ); + aPropOpt.AddOpt( ESCHER_Prop_FitTextToShape, 0x60006 ); // Size Shape To Fit Text + if ( nAngle < 0 ) + nAngle = ( 36000 + nAngle ) % 36000; + if ( nAngle ) + ImplFlipBoundingBox( rObj, aPropOpt ); + } + else + { + mpEscherEx->OpenContainer( ESCHER_SpContainer ); + nShapeID = mpEscherEx->GenerateShapeId(); + nShapeType = ESCHER_ShpInst_TextBox; + mpEscherEx->AddShape( nShapeType, ShapeFlag::HaveShapeProperty | ShapeFlag::HaveAnchor, nShapeID ); + if ( rObj.ImplGetText() ) + aPropOpt.CreateTextProperties( rObj.mXPropSet, + mpEscherEx->QueryTextID( rObj.GetShapeRef(), + rObj.GetShapeId() ) ); + aPropOpt.AddOpt( ESCHER_Prop_fNoLineDrawDash, 0x90000 ); + aPropOpt.AddOpt( ESCHER_Prop_fNoFillHitTest, 0x100000 ); + + if( nAngle < 0 ) + nAngle = ( 36000 + nAngle ) % 36000; + else + nAngle = ( 36000 - ( nAngle % 36000 ) ); + + nAngle *= 655; + nAngle += 0x8000; + nAngle &=~0xffff; // nAngle round to full degrees + aPropOpt.AddOpt( ESCHER_Prop_Rotation, nAngle ); + mpEscherEx->SetGroupSnapRect( mpEscherEx->GetGroupLevel(), + rObj.GetRect() ); + mpEscherEx->SetGroupLogicRect( mpEscherEx->GetGroupLevel(), + rObj.GetRect() ); + } + rObj.SetAngle( nAngle ); + aPropOpt.CreateShapeProperties( rObj.GetShapeRef() ); + const SdrObject* sdrObj = rObj.GetSdrObject(); + mpEscherEx->AddSdrObjectVMLObject(*sdrObj ); + mpEscherEx->Commit( aPropOpt, rObj.GetRect()); + + // write the childanchor + mpEscherEx->AddChildAnchor( rObj.GetRect() ); + +#if defined EES_WRITE_EPP + // ClientAnchor + mpEscherEx->AddClientAnchor( maRect ); + // ClientTextbox + mpEscherEx->OpenContainer( ESCHER_ClientTextbox ); + mpEscherEx->AddAtom( 4, EPP_TextHeaderAtom ); + *mpStrm << (sal_uInt32)EPP_TEXTTYPE_Other; // Text in a Shape + ImplWriteTextStyleAtom(); + mpEscherEx->CloseContainer(); // ESCHER_ClientTextBox +#else // !EES_WRITE_EPP + if ( mpHostAppData ) + { //! the App has to control whether these are written or not + mpHostAppData->WriteClientAnchor( *mpEscherEx, rObj.GetRect() ); + mpHostAppData->WriteClientData( *mpEscherEx ); + mpHostAppData->WriteClientTextbox( *mpEscherEx ); + } +#endif // EES_WRITE_EPP + mpEscherEx->CloseContainer(); // ESCHER_SpContainer + } while ( false ); + mpEscherEx->LeaveGroup(); + mpEscherEx->EndShape( nShapeType, nShapeID ); +} + + +sal_uInt32 ImplEESdrWriter::ImplEnterAdditionalTextGroup( const Reference< XShape >& rShape, + const tools::Rectangle* pBoundRect ) +{ + mpHostAppData = mpEscherEx->EnterAdditionalTextGroup(); + sal_uInt32 nGrpId = mpEscherEx->EnterGroup( pBoundRect ); + mpHostAppData = mpEscherEx->StartShape( rShape, pBoundRect ); + return nGrpId; +} + + +void ImplEESdrWriter::ImplInitPageValues() +{ + mbIsTitlePossible = true; // With more than one title PowerPoint will fail. +} + +void ImplEESdrWriter::ImplWritePage( + EscherSolverContainer& rSolverContainer, bool ooxmlExport ) +{ + ImplInitPageValues(); + + const sal_uInt32 nShapes = mXShapes->getCount(); + for( sal_uInt32 n = 0; n < nShapes; ++n ) + { + ImplEESdrObject aObj( *o3tl::doAccess>( + mXShapes->getByIndex( n )) ); + if( aObj.IsValid() ) + { + ImplWriteShape( aObj, rSolverContainer, ooxmlExport ); + } + } +} + +ImplEESdrWriter::~ImplEESdrWriter() +{ + DBG_ASSERT( !mpSolverContainer, "ImplEESdrWriter::~ImplEESdrWriter: unwritten SolverContainer" ); + Reference xComp(mXDrawPage, UNO_QUERY); + if (xComp.is()) + xComp->dispose(); +} + + +bool ImplEESdrWriter::ImplInitPage( const SdrPage& rPage ) +{ + rtl::Reference pSvxDrawPage; + if ( mpSdrPage != &rPage || !mXDrawPage.is() ) + { + // eventually write SolverContainer of current page, deletes the Solver + ImplFlushSolverContainer(); + + mpSdrPage = nullptr; + Reference xOldDrawPage(mXDrawPage, UNO_QUERY); + if (xOldDrawPage.is()) + xOldDrawPage->dispose(); + mXDrawPage = pSvxDrawPage = new SvxFmDrawPage( const_cast(&rPage) ); + mXShapes = mXDrawPage; + if ( !mXShapes.is() ) + return false; + ImplInitPageValues(); + mpSdrPage = &rPage; + + mpSolverContainer.reset( new EscherSolverContainer ); + } + else + pSvxDrawPage = comphelper::getFromUnoTunnel(mXDrawPage); + + return pSvxDrawPage != nullptr; +} + +bool ImplEESdrWriter::ImplInitUnoShapes( const Reference< XShapes >& rxShapes ) +{ + // eventually write SolverContainer of current page, deletes the Solver + ImplFlushSolverContainer(); + + if( !rxShapes.is() ) + return false; + + mpSdrPage = nullptr; + mXDrawPage.clear(); + mXShapes = rxShapes; + + ImplInitPageValues(); + + mpSolverContainer.reset( new EscherSolverContainer ); + return true; +} + +void ImplEESdrWriter::ImplExitPage() +{ + // close all groups before the solver container is written + while( mpEscherEx->GetGroupLevel() ) + mpEscherEx->LeaveGroup(); + + ImplFlushSolverContainer(); + mpSdrPage = nullptr; // reset page for next init +} + + +void ImplEESdrWriter::ImplFlushSolverContainer() +{ + if ( mpSolverContainer ) + { + mpSolverContainer->WriteSolver( mpEscherEx->GetStream() ); + mpSolverContainer.reset(); + } +} + +void ImplEESdrWriter::ImplWriteCurrentPage(bool ooxmlExport) +{ + assert(mpSolverContainer && "ImplEESdrWriter::ImplWriteCurrentPage: no SolverContainer"); + ImplWritePage( *mpSolverContainer, ooxmlExport ); + ImplExitPage(); +} + +sal_uInt32 ImplEESdrWriter::ImplWriteTheShape( ImplEESdrObject& rObj , bool ooxmlExport ) +{ + assert(mpSolverContainer && "ImplEESdrWriter::ImplWriteShape: no SolverContainer"); + return ImplWriteShape( rObj, *mpSolverContainer, ooxmlExport ); +} + +void EscherEx::AddSdrPage( const SdrPage& rPage, bool ooxmlExport ) +{ + if ( mpImplEESdrWriter->ImplInitPage( rPage ) ) + mpImplEESdrWriter->ImplWriteCurrentPage(ooxmlExport); +} + +void EscherEx::AddUnoShapes( const Reference< XShapes >& rxShapes, bool ooxmlExport ) +{ + if ( mpImplEESdrWriter->ImplInitUnoShapes( rxShapes ) ) + mpImplEESdrWriter->ImplWriteCurrentPage(ooxmlExport); +} + +sal_uInt32 EscherEx::AddSdrObject( const SdrObject& rObj, bool ooxmlExport ) +{ + ImplEESdrObject aObj( *mpImplEESdrWriter, rObj, mbOOXML ); + if( aObj.IsValid() ) + return mpImplEESdrWriter->ImplWriteTheShape( aObj, ooxmlExport ); + return 0; +} + + +void EscherEx::EndSdrObjectPage() +{ + mpImplEESdrWriter->ImplExitPage(); +} + +EscherExHostAppData* EscherEx::StartShape( const Reference< XShape >& /* rShape */, const tools::Rectangle* /*pChildAnchor*/ ) +{ + return nullptr; +} + +void EscherEx::EndShape( sal_uInt16 /* nShapeType */, sal_uInt32 /* nShapeID */ ) +{ +} + +sal_uInt32 EscherEx::QueryTextID( const Reference< XShape >&, sal_uInt32 ) +{ + return 0; +} + +// add a dummy rectangle shape into the escher stream +sal_uInt32 EscherEx::AddDummyShape() +{ + OpenContainer( ESCHER_SpContainer ); + sal_uInt32 nShapeID = GenerateShapeId(); + AddShape( ESCHER_ShpInst_Rectangle, ShapeFlag::HaveShapeProperty | ShapeFlag::HaveAnchor, nShapeID ); + CloseContainer(); + + return nShapeID; +} + +// static +const SdrObject* EscherEx::GetSdrObject( const Reference< XShape >& rShape ) +{ + const SdrObject* pRet = SdrObject::getSdrObjectFromXShape( rShape ); + DBG_ASSERT( pRet, "EscherEx::GetSdrObject: no SdrObj" ); + return pRet; +} + + +ImplEESdrObject::ImplEESdrObject( ImplEESdrWriter& rEx, + const SdrObject& rObj, bool bOOXML ) : + mnShapeId( 0 ), + mnTextSize( 0 ), + mnAngle( 0 ), + mbValid( false ), + mbPresObj( false ), + mbEmptyPresObj( false ), + mbOOXML(bOOXML) +{ + SdrPage* pPage = rObj.getSdrPageFromSdrObject(); + DBG_ASSERT( pPage, "ImplEESdrObject::ImplEESdrObject: no SdrPage" ); + if( pPage && rEx.ImplInitPage( *pPage ) ) + { + // why not declare a const parameter if the object will + // not be modified? + mXShape.set( const_cast(&rObj)->getUnoShape(), UNO_QUERY ); + Init(); + } +} + +ImplEESdrObject::ImplEESdrObject( const Reference< XShape >& rShape ) : + mXShape( rShape ), + mnShapeId( 0 ), + mnTextSize( 0 ), + mnAngle( 0 ), + mbValid( false ), + mbPresObj( false ), + mbEmptyPresObj( false ), + mbOOXML(false) +{ + Init(); +} + + +ImplEESdrObject::~ImplEESdrObject() +{ +} + +static basegfx::B2DRange getUnrotatedGroupBoundRange(const Reference< XShape >& rxShape) +{ + basegfx::B2DRange aRetval; + + try + { + if(rxShape.is()) + { + if(rxShape->getShapeType() == "com.sun.star.drawing.GroupShape") + { + // it's a group shape, iterate over children + const Reference< XIndexAccess > xXIndexAccess(rxShape, UNO_QUERY); + + if(xXIndexAccess.is()) + { + for(sal_uInt32 n(0), nCnt = xXIndexAccess->getCount(); n < nCnt; ++n) + { + const Reference< XShape > axShape(xXIndexAccess->getByIndex(n), UNO_QUERY); + + if(axShape.is()) + { + // we are calculating the bound for a group, correct rotation for sub-objects + // to get the unrotated bounds for the group + const basegfx::B2DRange aExtend(getUnrotatedGroupBoundRange(axShape)); + + aRetval.expand(aExtend); + } + } + } + } + else + { + // iT#s a xShape, get its transformation + const Reference< XPropertySet > xPropSet(rxShape, UNO_QUERY); + + if(xPropSet.is()) + { + const Any aAny = xPropSet->getPropertyValue("Transformation"); + + if(aAny.hasValue()) + { + HomogenMatrix3 aMatrix; + + if(aAny >>= aMatrix) + { + basegfx::B2DHomMatrix aHomogenMatrix; + + aHomogenMatrix.set(0, 0, aMatrix.Line1.Column1); + aHomogenMatrix.set(0, 1, aMatrix.Line1.Column2); + aHomogenMatrix.set(0, 2, aMatrix.Line1.Column3); + aHomogenMatrix.set(1, 0, aMatrix.Line2.Column1); + aHomogenMatrix.set(1, 1, aMatrix.Line2.Column2); + aHomogenMatrix.set(1, 2, aMatrix.Line2.Column3); + aHomogenMatrix.set(2, 0, aMatrix.Line3.Column1); + aHomogenMatrix.set(2, 1, aMatrix.Line3.Column2); + aHomogenMatrix.set(2, 2, aMatrix.Line3.Column3); + + basegfx::B2DVector aScale, aTranslate; + double fRotate, fShearX; + + // decompose transformation + aHomogenMatrix.decompose(aScale, aTranslate, fRotate, fShearX); + + // check if rotation needs to be corrected + if(!basegfx::fTools::equalZero(fRotate)) + { + // to correct, keep in mind that ppt graphics are rotated around their center + const basegfx::B2DPoint aCenter(aHomogenMatrix * basegfx::B2DPoint(0.5, 0.5)); + + aHomogenMatrix.translate(-aCenter.getX(), -aCenter.getY()); + aHomogenMatrix.rotate(-fRotate); + aHomogenMatrix.translate(aCenter.getX(), aCenter.getY()); + } + + + // check if shear needs to be corrected (always correct shear, + // ppt does not know about it) + if(!basegfx::fTools::equalZero(fShearX)) + { + const basegfx::B2DPoint aMinimum(aHomogenMatrix * basegfx::B2DPoint(0.0, 0.0)); + + aHomogenMatrix.translate(-aMinimum.getX(), -aMinimum.getY()); + aHomogenMatrix.shearX(-fShearX); + aHomogenMatrix.translate(aMinimum.getX(), aMinimum.getY()); + } + + // create range. It's no longer rotated (or sheared), so use + // minimum and maximum values + aRetval.expand(aHomogenMatrix * basegfx::B2DPoint(0.0, 0.0)); + aRetval.expand(aHomogenMatrix * basegfx::B2DPoint(1.0, 1.0)); + } + } + } + } + } + } + catch(css::uno::Exception&) + { + } + + return aRetval; +} + +void ImplEESdrObject::Init() +{ + mXPropSet.set( mXShape, UNO_QUERY ); + if( !mXPropSet.is() ) + return; + + // detect name first to make below test (is group) work + mType = mXShape->getShapeType(); + (void)mType.startsWith( "com.sun.star.", &mType ); // strip "com.sun.star." + (void)mType.endsWith( "Shape", &mType ); // strip "Shape" + + if(GetType() == "drawing.Group") + { + // if it's a group, the unrotated range is needed for that group + const basegfx::B2DRange aUnrotatedRange(getUnrotatedGroupBoundRange(mXShape)); + const Point aNewP(basegfx::fround(aUnrotatedRange.getMinX()), basegfx::fround(aUnrotatedRange.getMinY())); + const Size aNewS(basegfx::fround(aUnrotatedRange.getWidth()), basegfx::fround(aUnrotatedRange.getHeight())); + + SetRect(ImplEESdrWriter::ImplMapPoint(aNewP), ImplEESdrWriter::ImplMapSize(aNewS)); + } + else + { + // if it's no group, use position and size directly, rotated/sheared or not + const Point aOldP(mXShape->getPosition().X, mXShape->getPosition().Y); + const Size aOldS(mXShape->getSize().Width, mXShape->getSize().Height); + + SetRect(ImplEESdrWriter::ImplMapPoint(aOldP), ImplEESdrWriter::ImplMapSize(aOldS)); + } + + if( ImplGetPropertyValue( "IsPresentationObject" ) ) + mbPresObj = ::cppu::any2bool( mAny ); + + if( mbPresObj && ImplGetPropertyValue( "IsEmptyPresentationObject" ) ) + mbEmptyPresObj = ::cppu::any2bool( mAny ); + + mbValid = true; +} + +bool ImplEESdrObject::ImplGetPropertyValue( const OUString& rString ) +{ + bool bRetValue = false; + if( mbValid ) + { + try + { + mAny = mXPropSet->getPropertyValue( rString ); + if( mAny.hasValue() ) + bRetValue = true; + } + catch( const css::uno::Exception& ) + { + bRetValue = false; + } + } + return bRetValue; +} + +void ImplEESdrObject::SetRect( const Point& rPos, const Size& rSz ) +{ + maRect = tools::Rectangle( rPos, rSz ); +} + +const SdrObject* ImplEESdrObject::GetSdrObject() const +{ + return EscherEx::GetSdrObject( mXShape ); +} + +// loads and converts text from shape, result is saved in mnTextSize +sal_uInt32 ImplEESdrObject::ImplGetText() +{ + Reference< XText > xXText( mXShape, UNO_QUERY ); + mnTextSize = 0; + if (xXText.is()) + { + try + { + mnTextSize = xXText->getString().getLength(); + } + catch (const uno::RuntimeException&) + { + TOOLS_WARN_EXCEPTION("filter.ms", "ImplGetText"); + } + } + return mnTextSize; +} + +bool ImplEESdrObject::ImplHasText() const +{ + Reference< XText > xXText( mXShape, UNO_QUERY ); + return xXText.is() && !xXText->getString().isEmpty(); +} + + +void ImplEESdrObject::SetOOXML(bool bOOXML) +{ + mbOOXML = bOOXML; +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/filter/source/msfilter/eschesdo.hxx b/filter/source/msfilter/eschesdo.hxx new file mode 100644 index 000000000..cf46f49b6 --- /dev/null +++ b/filter/source/msfilter/eschesdo.hxx @@ -0,0 +1,138 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ +#pragma once +#include +#include + + +// fractions of Draw PPTWriter etc. + +class ImplEESdrWriter; +class SdrObject; +class SdrPage; + +class ImplEESdrObject +{ + css::uno::Reference< css::drawing::XShape > mXShape; + css::uno::Any mAny; + tools::Rectangle maRect; + OUString mType; + sal_uInt32 mnShapeId; + sal_uInt32 mnTextSize; + sal_Int32 mnAngle; + bool mbValid : 1; + bool mbPresObj : 1; + bool mbEmptyPresObj : 1; + bool mbOOXML; + + void Init(); +public: + css::uno::Reference< css::beans::XPropertySet > mXPropSet; + + ImplEESdrObject( ImplEESdrWriter& rEx, const SdrObject& rObj, bool bOOXML ); + ImplEESdrObject( const css::uno::Reference< css::drawing::XShape >& rShape ); + ~ImplEESdrObject(); + + bool ImplGetPropertyValue( const OUString& rString ); + + sal_Int32 ImplGetInt32PropertyValue( const OUString& rStr ) + { return ImplGetPropertyValue( rStr ) ? *o3tl::doAccess(mAny) : 0; } + + const css::uno::Reference< css::drawing::XShape >& GetShapeRef() const { return mXShape; } + const css::uno::Any& GetUsrAny() const { return mAny; } + const OUString& GetType() const { return mType; } + void SetType( const OUString& rS ) { mType = rS; } + + const tools::Rectangle& GetRect() const { return maRect; } + void SetRect( const Point& rPos, const Size& rSz ); + void SetRect( const tools::Rectangle& rRect ) + { maRect = rRect; } + + sal_Int32 GetAngle() const { return mnAngle; } + void SetAngle( sal_Int32 nVal ) { mnAngle = nVal; } + + bool IsValid() const { return mbValid; } + + bool IsEmptyPresObj() const { return mbEmptyPresObj; } + sal_uInt32 GetShapeId() const { return mnShapeId; } + void SetShapeId( sal_uInt32 nVal ) { mnShapeId = nVal; } + + const SdrObject* GetSdrObject() const; + + sal_uInt32 ImplGetText(); + bool ImplHasText() const; + bool GetOOXML() const { return mbOOXML;} + void SetOOXML(bool bOOXML); +}; + + +// fractions of the Draw PPTWriter + +class EscherEx; +namespace com::sun::star { + namespace drawing { + class XDrawPage; + class XShape; + } + namespace task { + class XStatusIndicator; + } +} +class EscherExHostAppData; + +class ImplEESdrWriter +{ + EscherEx* mpEscherEx; + css::uno::Reference< css::drawing::XDrawPage > mXDrawPage; + css::uno::Reference< css::drawing::XShapes > mXShapes; + SvStream* mpPicStrm; + // own extensions + EscherExHostAppData* mpHostAppData; + bool mbIsTitlePossible; + const SdrPage* mpSdrPage; + std::unique_ptr mpSolverContainer; + + void ImplInitPageValues(); + void ImplWritePage( EscherSolverContainer& rSolver, bool ooxmlExport ); + sal_uInt32 ImplWriteShape( ImplEESdrObject& rObj, + EscherSolverContainer& rSolver, + const bool bOOxmlExport = false ); // returns ShapeID + static void ImplFlipBoundingBox( ImplEESdrObject& rObj, EscherPropertyContainer& rPropOpt ); + void ImplWriteAdditionalText( + ImplEESdrObject& rObj ); + sal_uInt32 ImplEnterAdditionalTextGroup( + const css::uno::Reference< css::drawing::XShape >& rShape, + const tools::Rectangle* pBoundRect ); + void ImplFlushSolverContainer(); + +public: + explicit ImplEESdrWriter( EscherEx& rEx ); + ~ImplEESdrWriter(); + static Point ImplMapPoint( const Point& rPoint ); + static Size ImplMapSize( const Size& rSize ); + EscherExHostAppData* ImplGetHostData() { return mpHostAppData; } + bool ImplInitPage( const SdrPage& rPage ); + bool ImplInitUnoShapes( const css::uno::Reference< css::drawing::XShapes >& rxShapes ); + void ImplWriteCurrentPage( bool ooxmlExport ); + sal_uInt32 ImplWriteTheShape( ImplEESdrObject& rObj, bool ooxmlExport ); + void ImplExitPage(); +}; + + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/filter/source/msfilter/mscodec.cxx b/filter/source/msfilter/mscodec.cxx new file mode 100644 index 000000000..376a45320 --- /dev/null +++ b/filter/source/msfilter/mscodec.cxx @@ -0,0 +1,642 @@ +/* -*- 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 + +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +#define DEBUG_MSO_ENCRYPTION_STD97 0 + +#if DEBUG_MSO_ENCRYPTION_STD97 +#include +#endif + +using namespace ::com::sun::star; + +namespace msfilter { + + +namespace { + +/** Rotates rnValue left by nBits bits. */ +template< typename Type > +void lclRotateLeft( Type& rnValue, int nBits ) +{ + OSL_ASSERT( + nBits >= 0 && + sal::static_int_cast< unsigned int >(nBits) < sizeof( Type ) * 8 ); + rnValue = static_cast< Type >( (rnValue << nBits) | (rnValue >> (sizeof( Type ) * 8 - nBits)) ); +} + +/** Rotates the lower nWidth bits of rnValue left by nBits bits. */ +template< typename Type > +void lclRotateLeft( Type& rnValue, sal_uInt8 nBits, sal_uInt8 nWidth ) +{ + OSL_ASSERT( (nBits < nWidth) && (nWidth < sizeof( Type ) * 8) ); + Type nMask = static_cast< Type >( (1UL << nWidth) - 1 ); + rnValue = static_cast< Type >( + ((rnValue << nBits) | ((rnValue & nMask) >> (nWidth - nBits))) & nMask ); +} + +std::size_t lclGetLen( const sal_uInt8* pnPassData, std::size_t nBufferSize ) +{ + std::size_t nLen = 0; + while( (nLen < nBufferSize) && pnPassData[ nLen ] ) ++nLen; + return nLen; +} + +sal_uInt16 lclGetKey( const sal_uInt8* pnPassData, std::size_t nBufferSize ) +{ + std::size_t nLen = lclGetLen( pnPassData, nBufferSize ); + if( !nLen ) return 0; + + sal_uInt16 nKey = 0; + sal_uInt16 nKeyBase = 0x8000; + sal_uInt16 nKeyEnd = 0xFFFF; + const sal_uInt8* pnChar = pnPassData + nLen - 1; + for( std::size_t nIndex = 0; nIndex < nLen; ++nIndex, --pnChar ) + { + sal_uInt8 cChar = *pnChar & 0x7F; + for( sal_uInt8 nBit = 0; nBit < 8; ++nBit ) + { + lclRotateLeft( nKeyBase, 1 ); + if( nKeyBase & 1 ) nKeyBase ^= 0x1020; + if( cChar & 1 ) nKey ^= nKeyBase; + cChar >>= 1; + lclRotateLeft( nKeyEnd, 1 ); + if( nKeyEnd & 1 ) nKeyEnd ^= 0x1020; + } + } + return nKey ^ nKeyEnd; +} + +sal_uInt16 lclGetHash( const sal_uInt8* pnPassData, std::size_t nBufferSize ) +{ + std::size_t nLen = lclGetLen( pnPassData, nBufferSize ); + + sal_uInt16 nHash = static_cast< sal_uInt16 >( nLen ); + if( nLen ) + nHash ^= 0xCE4B; + + const sal_uInt8* pnChar = pnPassData; + for( std::size_t nIndex = 0; nIndex < nLen; ++nIndex, ++pnChar ) + { + sal_uInt16 cChar = *pnChar; + sal_uInt8 nRot = static_cast< sal_uInt8 >( (nIndex + 1) % 15 ); + lclRotateLeft( cChar, nRot, 15 ); + nHash ^= cChar; + } + return nHash; +} + + +} // namespace + + +MSCodec_Xor95::MSCodec_Xor95(int nRotateDistance) : + mnOffset( 0 ), + mnKey( 0 ), + mnHash( 0 ), + mnRotateDistance( nRotateDistance ) +{ +} + +MSCodec_Xor95::~MSCodec_Xor95() +{ + memset( mpnKey, 0, sizeof( mpnKey ) ); + mnKey = mnHash = 0; +} + +void MSCodec_Xor95::InitKey( const sal_uInt8 pnPassData[ 16 ] ) +{ + mnKey = lclGetKey( pnPassData, 16 ); + mnHash = lclGetHash( pnPassData, 16 ); + + memcpy( mpnKey, pnPassData, 16 ); + + static const sal_uInt8 spnFillChars[] = + { + 0xBB, 0xFF, 0xFF, 0xBA, + 0xFF, 0xFF, 0xB9, 0x80, + 0x00, 0xBE, 0x0F, 0x00, + 0xBF, 0x0F, 0x00, 0x00 + }; + + std::size_t nLen = lclGetLen( pnPassData, 16 ); + const sal_uInt8* pnFillChar = spnFillChars; + for (std::size_t nIndex = nLen; nIndex < sizeof(mpnKey); ++nIndex, ++pnFillChar) + mpnKey[ nIndex ] = *pnFillChar; + + SVBT16 pnOrigKey; + ShortToSVBT16( mnKey, pnOrigKey ); + sal_uInt8* pnKeyChar = mpnKey; + for (std::size_t nIndex = 0; nIndex < sizeof(mpnKey); ++nIndex, ++pnKeyChar) + { + *pnKeyChar ^= pnOrigKey[ nIndex & 0x01 ]; + lclRotateLeft( *pnKeyChar, mnRotateDistance ); + } +} + +bool MSCodec_Xor95::InitCodec( const uno::Sequence< beans::NamedValue >& aData ) +{ + bool bResult = false; + + ::comphelper::SequenceAsHashMap aHashData( aData ); + uno::Sequence< sal_Int8 > aKey = aHashData.getUnpackedValueOrDefault("XOR95EncryptionKey", uno::Sequence< sal_Int8 >() ); + + if ( aKey.getLength() == 16 ) + { + memcpy( mpnKey, aKey.getConstArray(), 16 ); + bResult = true; + + mnKey = static_cast(aHashData.getUnpackedValueOrDefault("XOR95BaseKey", sal_Int16(0) )); + mnHash = static_cast(aHashData.getUnpackedValueOrDefault("XOR95PasswordHash", sal_Int16(0) )); + } + else + OSL_FAIL( "Unexpected key size!" ); + + return bResult; +} + +uno::Sequence< beans::NamedValue > MSCodec_Xor95::GetEncryptionData() +{ + ::comphelper::SequenceAsHashMap aHashData; + // coverity[overrun-buffer-arg : FALSE] - coverity has difficulty with css::uno::Sequence + aHashData[ OUString( "XOR95EncryptionKey" ) ] <<= uno::Sequence( reinterpret_cast(mpnKey), 16 ); + aHashData[ OUString( "XOR95BaseKey" ) ] <<= static_cast(mnKey); + aHashData[ OUString( "XOR95PasswordHash" ) ] <<= static_cast(mnHash); + + return aHashData.getAsConstNamedValueList(); +} + +bool MSCodec_Xor95::VerifyKey( sal_uInt16 nKey, sal_uInt16 nHash ) const +{ + return (nKey == mnKey) && (nHash == mnHash); +} + +void MSCodec_Xor95::InitCipher() +{ + mnOffset = 0; +} + +void MSCodec_XorXLS95::Decode( sal_uInt8* pnData, std::size_t nBytes ) +{ + const sal_uInt8* pnCurrKey = mpnKey + mnOffset; + const sal_uInt8* pnKeyLast = mpnKey + 0x0F; + + for( const sal_uInt8* pnDataEnd = pnData + nBytes; pnData < pnDataEnd; ++pnData ) + { + lclRotateLeft( *pnData, 3 ); + *pnData ^= *pnCurrKey; + if( pnCurrKey < pnKeyLast ) ++pnCurrKey; else pnCurrKey = mpnKey; + } + + // update mnOffset + Skip( nBytes ); +} + +void MSCodec_XorWord95::Decode( sal_uInt8* pnData, std::size_t nBytes ) +{ + const sal_uInt8* pnCurrKey = mpnKey + mnOffset; + const sal_uInt8* pnKeyLast = mpnKey + 0x0F; + + for( const sal_uInt8* pnDataEnd = pnData + nBytes; pnData < pnDataEnd; ++pnData ) + { + const sal_uInt8 cChar = *pnData ^ *pnCurrKey; + if (*pnData && cChar) + *pnData = cChar; + + if( pnCurrKey < pnKeyLast ) + ++pnCurrKey; + else + pnCurrKey = mpnKey; + } + + // update mnOffset + Skip( nBytes ); +} + + +void MSCodec_Xor95::Skip( std::size_t nBytes ) +{ + mnOffset = (mnOffset + nBytes) & 0x0F; +} + +MSCodec97::MSCodec97(size_t nHashLen, OUString aEncKeyName) + : m_sEncKeyName(std::move(aEncKeyName)) + , m_nHashLen(nHashLen) + , m_hCipher(rtl_cipher_create(rtl_Cipher_AlgorithmARCFOUR, rtl_Cipher_ModeStream)) + , m_aDocId(16, 0) + , m_aDigestValue(nHashLen, 0) +{ + assert(m_hCipher != nullptr); +} + +MSCodec_Std97::MSCodec_Std97() + : MSCodec97(RTL_DIGEST_LENGTH_MD5, "STD97EncryptionKey") +{ + m_hDigest = rtl_digest_create(rtl_Digest_AlgorithmMD5); + assert(m_hDigest != nullptr); +} + +MSCodec_CryptoAPI::MSCodec_CryptoAPI() + : MSCodec97(RTL_DIGEST_LENGTH_SHA1, "CryptoAPIEncryptionKey") +{ +} + +MSCodec97::~MSCodec97() +{ + memset(m_aDigestValue.data(), 0, m_aDigestValue.size()); + memset(m_aDocId.data(), 0, m_aDocId.size()); + rtl_cipher_destroy(m_hCipher); +} + +MSCodec_Std97::~MSCodec_Std97() +{ + rtl_digest_destroy(m_hDigest); +} + +#if DEBUG_MSO_ENCRYPTION_STD97 +static void lcl_PrintDigest(const sal_uInt8* pDigest, const char* msg) +{ + printf("digest: (%s)\n", msg); + for (int i = 0; i < 16; ++i) + printf("%2.2x ", pDigest[i]); + printf("\n"); +} +#else +static void lcl_PrintDigest(const sal_uInt8* /*pDigest*/, const char* /*msg*/) +{ +} +#endif + +bool MSCodec97::InitCodec( const uno::Sequence< beans::NamedValue >& aData ) +{ +#if DEBUG_MSO_ENCRYPTION_STD97 + fprintf(stdout, "MSCodec_Std97::InitCodec: --begin\n");fflush(stdout); +#endif + bool bResult = false; + + ::comphelper::SequenceAsHashMap aHashData( aData ); + uno::Sequence aKey = aHashData.getUnpackedValueOrDefault(m_sEncKeyName, uno::Sequence()); + const size_t nKeyLen = aKey.getLength(); + if (nKeyLen == m_nHashLen) + { + assert(m_aDigestValue.size() == m_nHashLen); + memcpy(m_aDigestValue.data(), aKey.getConstArray(), m_nHashLen); + uno::Sequence< sal_Int8 > aUniqueID = aHashData.getUnpackedValueOrDefault("STD97UniqueID", uno::Sequence< sal_Int8 >() ); + if ( aUniqueID.getLength() == 16 ) + { + assert(m_aDocId.size() == static_cast(aUniqueID.getLength())); + memcpy(m_aDocId.data(), aUniqueID.getConstArray(), m_aDocId.size()); + bResult = true; + lcl_PrintDigest(m_aDigestValue.data(), "digest value"); + lcl_PrintDigest(m_aDocId.data(), "DocId value"); + } + else + OSL_FAIL( "Unexpected document ID!" ); + } + else + OSL_FAIL( "Unexpected key size!" ); + + return bResult; +} + +uno::Sequence< beans::NamedValue > MSCodec97::GetEncryptionData() +{ + ::comphelper::SequenceAsHashMap aHashData; + assert(m_aDigestValue.size() == m_nHashLen); + aHashData[m_sEncKeyName] <<= uno::Sequence(reinterpret_cast(m_aDigestValue.data()), m_nHashLen); + aHashData[ OUString( "STD97UniqueID" ) ] <<= uno::Sequence< sal_Int8 >( reinterpret_cast(m_aDocId.data()), m_aDocId.size() ); + + return aHashData.getAsConstNamedValueList(); +} + +void MSCodec_Std97::InitKey ( + const sal_uInt16 pPassData[16], + const sal_uInt8 pDocId[16]) +{ +#if DEBUG_MSO_ENCRYPTION_STD97 + fprintf(stdout, "MSCodec_Std97::InitKey: --begin\n");fflush(stdout); +#endif + uno::Sequence< sal_Int8 > aKey = ::comphelper::DocPasswordHelper::GenerateStd97Key(pPassData, pDocId); + // Fill raw digest of above updates into DigestValue. + + const size_t nKeyLen = aKey.getLength(); + if (m_aDigestValue.size() == nKeyLen) + memcpy(m_aDigestValue.data(), aKey.getConstArray(), m_aDigestValue.size()); + else + memset(m_aDigestValue.data(), 0, m_aDigestValue.size()); + + lcl_PrintDigest(m_aDigestValue.data(), "digest value"); + + memcpy (m_aDocId.data(), pDocId, 16); + + lcl_PrintDigest(m_aDocId.data(), "DocId value"); +} + +void MSCodec_CryptoAPI::InitKey ( + const sal_uInt16 pPassData[16], + const sal_uInt8 pDocId[16]) +{ + sal_uInt32 const saltSize = 16; + + // Prepare initial data -> salt + password (in 16-bit chars) + std::vector initialData(pDocId, pDocId + saltSize); + + // Fill PassData into KeyData. + for (sal_Int32 nInd = 0; nInd < 16 && pPassData[nInd]; ++nInd) + { + initialData.push_back(sal::static_int_cast((pPassData[nInd] >> 0) & 0xff)); + initialData.push_back(sal::static_int_cast((pPassData[nInd] >> 8) & 0xff)); + } + + // calculate SHA1 hash of initialData + std::vector const sha1(::comphelper::Hash::calculateHash( + initialData.data(), initialData.size(), + ::comphelper::HashType::SHA1)); + m_aDigestValue = sha1; + + lcl_PrintDigest(m_aDigestValue.data(), "digest value"); + + memcpy(m_aDocId.data(), pDocId, 16); + + lcl_PrintDigest(m_aDocId.data(), "DocId value"); + + //generate the old format key while we have the required data + m_aStd97Key = ::comphelper::DocPasswordHelper::GenerateStd97Key(pPassData, pDocId); +} + +bool MSCodec97::VerifyKey(const sal_uInt8* pSaltData, const sal_uInt8* pSaltDigest) +{ + // both the salt data and salt digest (hash) come from the document being imported. + +#if DEBUG_MSO_ENCRYPTION_STD97 + fprintf(stdout, "MSCodec97::VerifyKey: \n"); + lcl_PrintDigest(pSaltData, "salt data"); + lcl_PrintDigest(pSaltDigest, "salt hash"); +#endif + bool result = false; + + if (InitCipher(0)) + { + std::vector aDigest(m_nHashLen); + GetDigestFromSalt(pSaltData, aDigest.data()); + + std::vector aBuffer(m_nHashLen); + // Decode original SaltDigest into Buffer. + rtl_cipher_decode(m_hCipher, pSaltDigest, m_nHashLen, aBuffer.data(), m_nHashLen); + + // Compare Buffer with computed Digest. + result = (memcmp(aBuffer.data(), aDigest.data(), m_nHashLen) == 0); + + // Erase Buffer and Digest arrays. + rtl_secureZeroMemory(aBuffer.data(), m_nHashLen); + rtl_secureZeroMemory(aDigest.data(), m_nHashLen); + } + + return result; +} + +void MSCodec_CryptoAPI::GetDigestFromSalt(const sal_uInt8* pSaltData, sal_uInt8* pDigest) +{ + std::vector verifier(16); + rtl_cipher_decode(m_hCipher, + pSaltData, 16, verifier.data(), verifier.size()); + + std::vector const sha1(::comphelper::Hash::calculateHash( + verifier.data(), verifier.size(), ::comphelper::HashType::SHA1)); + ::std::copy(sha1.begin(), sha1.end(), pDigest); +} + +bool MSCodec_Std97::InitCipher(sal_uInt32 nCounter) +{ + sal_uInt8 pKeyData[64] = {}; // 512-bit message block + + // Fill 40 bit of DigestValue into [0..4]. + memcpy (pKeyData, m_aDigestValue.data(), 5); + + // Fill counter into [5..8]. + pKeyData[ 5] = sal_uInt8((nCounter >> 0) & 0xff); + pKeyData[ 6] = sal_uInt8((nCounter >> 8) & 0xff); + pKeyData[ 7] = sal_uInt8((nCounter >> 16) & 0xff); + pKeyData[ 8] = sal_uInt8((nCounter >> 24) & 0xff); + + pKeyData[ 9] = 0x80; + pKeyData[56] = 0x48; + + // Fill raw digest of KeyData into KeyData. + (void)rtl_digest_updateMD5 ( + m_hDigest, pKeyData, sizeof(pKeyData)); + (void)rtl_digest_rawMD5 ( + m_hDigest, pKeyData, RTL_DIGEST_LENGTH_MD5); + + // Initialize Cipher with KeyData (for decoding). + rtlCipherError result = rtl_cipher_init ( + m_hCipher, rtl_Cipher_DirectionBoth, + pKeyData, RTL_DIGEST_LENGTH_MD5, nullptr, 0); + + // Erase KeyData array and leave. + rtl_secureZeroMemory (pKeyData, sizeof(pKeyData)); + + return (result == rtl_Cipher_E_None); +} + +bool MSCodec_CryptoAPI::InitCipher(sal_uInt32 nCounter) +{ + // data = hash + iterator (4bytes) + std::vector aKeyData(m_aDigestValue); + aKeyData.push_back(sal_uInt8((nCounter >> 0) & 0xff)); + aKeyData.push_back(sal_uInt8((nCounter >> 8) & 0xff)); + aKeyData.push_back(sal_uInt8((nCounter >> 16) & 0xff)); + aKeyData.push_back(sal_uInt8((nCounter >> 24) & 0xff)); + + std::vector const hash(::comphelper::Hash::calculateHash( + aKeyData.data(), aKeyData.size(), ::comphelper::HashType::SHA1)); + + rtlCipherError result = + rtl_cipher_init(m_hCipher, rtl_Cipher_DirectionDecode, + hash.data(), ENCRYPT_KEY_SIZE_AES_128/8, nullptr, 0); + + return (result == rtl_Cipher_E_None); +} + +uno::Sequence MSCodec_CryptoAPI::GetEncryptionData() +{ + ::comphelper::SequenceAsHashMap aHashData(MSCodec97::GetEncryptionData()); + //add in the old encryption key as well as our new key so saving using the + //old crypto scheme can be done without reprompt for the password + aHashData[OUString("STD97EncryptionKey")] <<= m_aStd97Key; + return aHashData.getAsConstNamedValueList(); +} + +void MSCodec_Std97::CreateSaltDigest( const sal_uInt8 nSaltData[16], sal_uInt8 nSaltDigest[16] ) +{ +#if DEBUG_MSO_ENCRYPTION_STD97 + lcl_PrintDigest(nSaltData, "salt data"); +#endif + if (InitCipher(0)) + { + sal_uInt8 pDigest[RTL_DIGEST_LENGTH_MD5]; + GetDigestFromSalt(nSaltData, pDigest); + + rtl_cipher_decode ( + m_hCipher, pDigest, 16, pDigest, sizeof(pDigest)); + + memcpy(nSaltDigest, pDigest, 16); + } +} + +bool MSCodec97::Encode ( + const void *pData, std::size_t nDatLen, + sal_uInt8 *pBuffer, std::size_t nBufLen) +{ + rtlCipherError result = rtl_cipher_encode( + m_hCipher, pData, nDatLen, pBuffer, nBufLen); + + return (result == rtl_Cipher_E_None); +} + +bool MSCodec97::Decode ( + const void *pData, std::size_t nDatLen, + sal_uInt8 *pBuffer, std::size_t nBufLen) +{ + rtlCipherError result = rtl_cipher_decode( + m_hCipher, pData, nDatLen, pBuffer, nBufLen); + + return (result == rtl_Cipher_E_None); +} + +bool MSCodec97::Skip(std::size_t nDatLen) +{ + sal_uInt8 pnDummy[ 1024 ]; + std::size_t nDatLeft = nDatLen; + bool bResult = true; + + while (bResult && nDatLeft) + { + std::size_t nBlockLen = ::std::min< std::size_t >( nDatLeft, sizeof(pnDummy) ); + bResult = Decode( pnDummy, nBlockLen, pnDummy, nBlockLen ); + nDatLeft -= nBlockLen; + } + + return bResult; +} + +void MSCodec_Std97::GetDigestFromSalt(const sal_uInt8* pSaltData, sal_uInt8* pDigest) +{ + sal_uInt8 pBuffer[64]; + sal_uInt8 pDigestLocal[16]; + + // Decode SaltData into Buffer. + rtl_cipher_decode ( + m_hCipher, pSaltData, 16, pBuffer, sizeof(pBuffer)); + + // set the 129th bit to make the buffer 128-bit in length. + pBuffer[16] = 0x80; + + // erase the rest of the buffer with zeros. + memset (pBuffer + 17, 0, sizeof(pBuffer) - 17); + + // set the 441st bit. + pBuffer[56] = 0x80; + + // Fill raw digest of Buffer into Digest. + rtl_digest_updateMD5 ( + m_hDigest, pBuffer, sizeof(pBuffer)); + rtl_digest_rawMD5 ( + m_hDigest, pDigestLocal, sizeof(pDigestLocal)); + + memcpy(pDigest, pDigestLocal, 16); +} + +void MSCodec_Std97::GetEncryptKey ( + const sal_uInt8 pSalt[16], + sal_uInt8 pSaltData[16], + sal_uInt8 pSaltDigest[16]) +{ + if (!InitCipher(0)) + return; + + sal_uInt8 pDigest[RTL_DIGEST_LENGTH_MD5]; + sal_uInt8 pBuffer[64]; + + rtl_cipher_encode ( + m_hCipher, pSalt, 16, pSaltData, sizeof(pBuffer)); + + memcpy( pBuffer, pSalt, 16 ); + + pBuffer[16] = 0x80; + memset (pBuffer + 17, 0, sizeof(pBuffer) - 17); + pBuffer[56] = 0x80; + + rtl_digest_updateMD5 ( + m_hDigest, pBuffer, sizeof(pBuffer)); + rtl_digest_rawMD5 ( + m_hDigest, pDigest, sizeof(pDigest)); + + rtl_cipher_encode ( + m_hCipher, pDigest, 16, pSaltDigest, 16); + + rtl_secureZeroMemory (pBuffer, sizeof(pBuffer)); + rtl_secureZeroMemory (pDigest, sizeof(pDigest)); +} + +void MSCodec97::GetDocId( sal_uInt8 pDocId[16] ) +{ + assert(m_aDocId.size() == 16); + memcpy(pDocId, m_aDocId.data(), 16); +} + +EncryptionStandardHeader::EncryptionStandardHeader() +{ + flags = 0; + sizeExtra = 0; + algId = 0; + algIdHash = 0; + keyBits = 0; + providedType = 0; + reserved1 = 0; + reserved2 = 0; +} + +EncryptionVerifierAES::EncryptionVerifierAES() + : saltSize(SALT_LENGTH) + , encryptedVerifierHashSize(comphelper::SHA1_HASH_LENGTH) +{ +} + +EncryptionVerifierRC4::EncryptionVerifierRC4() + : saltSize(SALT_LENGTH) + , encryptedVerifierHashSize(comphelper::SHA1_HASH_LENGTH) +{ +} + +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/filter/source/msfilter/msdffimp.cxx b/filter/source/msfilter/msdffimp.cxx new file mode 100644 index 000000000..66bac102a --- /dev/null +++ b/filter/source/msfilter/msdffimp.cxx @@ -0,0 +1,7610 @@ +/* -*- 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 +#include + +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "viscache.hxx" + +// SvxItem-Mapping. Is needed to successfully include the SvxItem-Header +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +using namespace ::com::sun::star ; +using namespace ::com::sun::star::drawing; +using namespace uno ; +using namespace beans ; +using namespace drawing ; +using namespace container ; + +// static counter for OLE-Objects +static sal_uInt32 nMSOleObjCntr = 0; +constexpr OUStringLiteral MSO_OLE_Obj = u"MSO_OLE_Obj"; + +namespace { +/* Office File Formats - 2.2.23 */ +enum class OfficeArtBlipRecInstance : sal_uInt32 +{ + EMF = 0x3D4, // defined in section 2.2.24. + WMF = 0x216, // defined in section 2.2.25. + PICT = 0x542, // as defined in section 2.2.26. + JPEG_RGB = 0x46A, // defined in section 2.2.27. + JPEG_CMYK = 0x6E2, // defined in section 2.2.27. + PNG = 0x6E0, // defined in section 2.2.28. + DIB = 0x7A8, // defined in section 2.2.29. + TIFF = 0x6E4 // defined in section 2.2.30. +}; + +struct SvxMSDffBLIPInfo +{ + sal_uLong nFilePos; ///< offset of the BLIP in data stream + explicit SvxMSDffBLIPInfo(sal_uLong nFPos) + : nFilePos(nFPos) + { + } +}; + +} + +/// the following will be sorted by the order of their appearance: +struct SvxMSDffBLIPInfos : public std::vector {}; + +/************************************************************************/ +void Impl_OlePres::Write( SvStream & rStm ) +{ + WriteClipboardFormat( rStm, SotClipboardFormatId::GDIMETAFILE ); + rStm.WriteInt32( 4 ); // a TargetDevice that's always empty + rStm.WriteUInt32( nAspect ); + rStm.WriteInt32( -1 ); //L-Index always -1 + rStm.WriteInt32( nAdvFlags ); + rStm.WriteInt32( 0 ); //Compression + rStm.WriteInt32( aSize.Width() ); + rStm.WriteInt32( aSize.Height() ); + sal_uInt64 nPos = rStm.Tell(); + rStm.WriteInt32( 0 ); + + if( nFormat == SotClipboardFormatId::GDIMETAFILE && pMtf ) + { + // Always to 1/100 mm, until Mtf-Solution found + // Assumption (no scaling, no origin translation) + DBG_ASSERT( pMtf->GetPrefMapMode().GetScaleX() == Fraction( 1, 1 ), + "x-scale in the Mtf is wrong" ); + DBG_ASSERT( pMtf->GetPrefMapMode().GetScaleY() == Fraction( 1, 1 ), + "y-scale in the Mtf is wrong" ); + DBG_ASSERT( pMtf->GetPrefMapMode().GetOrigin() == Point(), + "origin-shift in the Mtf is wrong" ); + MapUnit nMU = pMtf->GetPrefMapMode().GetMapUnit(); + if( MapUnit::Map100thMM != nMU ) + { + Size aPrefS( pMtf->GetPrefSize() ); + Size aS = OutputDevice::LogicToLogic(aPrefS, MapMode(nMU), MapMode(MapUnit::Map100thMM)); + + pMtf->Scale( Fraction( aS.Width(), aPrefS.Width() ), + Fraction( aS.Height(), aPrefS.Height() ) ); + pMtf->SetPrefMapMode(MapMode(MapUnit::Map100thMM)); + pMtf->SetPrefSize( aS ); + } + WriteWindowMetafileBits( rStm, *pMtf ); + } + else + { + OSL_FAIL( "unknown format" ); + } + sal_uInt64 nEndPos = rStm.Tell(); + rStm.Seek( nPos ); + rStm.WriteUInt32( nEndPos - nPos - 4 ); + rStm.Seek( nEndPos ); +} + +DffPropertyReader::DffPropertyReader( const SvxMSDffManager& rMan ) + : rManager(rMan) + , mnFix16Angle(0) + , mbRotateGranientFillWithAngle(false) +{ + InitializePropSet( DFF_msofbtOPT ); +} + +void DffPropertyReader::SetDefaultPropSet( SvStream& rStCtrl, sal_uInt32 nOffsDgg ) const +{ + const_cast(this)->pDefaultPropSet.reset(); + sal_uInt64 nOldPos = rStCtrl.Tell(); + bool bOk = checkSeek(rStCtrl, nOffsDgg); + DffRecordHeader aRecHd; + if (bOk) + bOk = ReadDffRecordHeader( rStCtrl, aRecHd ); + if (bOk && aRecHd.nRecType == DFF_msofbtDggContainer) + { + if ( SvxMSDffManager::SeekToRec( rStCtrl, DFF_msofbtOPT, aRecHd.GetRecEndFilePos() ) ) + { + const_cast(this)->pDefaultPropSet.reset( new DffPropSet ); + ReadDffPropSet( rStCtrl, *pDefaultPropSet ); + } + } + rStCtrl.Seek( nOldPos ); +} + +#ifdef DBG_CUSTOMSHAPE +void DffPropertyReader::ReadPropSet( SvStream& rIn, SvxMSDffClientData* pClientData, sal_uInt32 nShapeId ) const +#else +void DffPropertyReader::ReadPropSet( SvStream& rIn, SvxMSDffClientData* pClientData ) const +#endif +{ + sal_uInt64 nFilePos = rIn.Tell(); + ReadDffPropSet( rIn, const_cast(*this) ); + + if ( IsProperty( DFF_Prop_hspMaster ) ) + { + if ( rManager.SeekToShape( rIn, pClientData, GetPropertyValue( DFF_Prop_hspMaster, 0 ) ) ) + { + DffRecordHeader aRecHd; + bool bOk = ReadDffRecordHeader(rIn, aRecHd); + if (bOk && SvxMSDffManager::SeekToRec(rIn, DFF_msofbtOPT, aRecHd.GetRecEndFilePos())) + { + rIn |= const_cast(*this); + } + } + } + + const_cast(this)->mnFix16Angle = Fix16ToAngle( GetPropertyValue( DFF_Prop_Rotation, 0 ) ); + +#ifdef DBG_CUSTOMSHAPE + + OUString aURLStr; + + if( osl::FileBase::getFileURLFromSystemPath( OUString("d:\\ashape.dbg"), aURLStr ) == osl::FileBase::E_None ) + { + std::unique_ptr xOut(::utl::UcbStreamHelper::CreateStream( aURLStr, StreamMode::WRITE )); + + if( xOut ) + { + xOut->Seek( STREAM_SEEK_TO_END ); + + if ( IsProperty( DFF_Prop_adjustValue ) || IsProperty( DFF_Prop_pVertices ) ) + { + xOut->WriteLine( "" ); + OString aString("ShapeId: " + OString::number(nShapeId)); + xOut->WriteLine(aString); + } + for ( sal_uInt32 i = DFF_Prop_adjustValue; i <= DFF_Prop_adjust10Value; i++ ) + { + if ( IsProperty( i ) ) + { + OString aString("Prop_adjustValue" + OString::number( ( i - DFF_Prop_adjustValue ) + 1 ) + + ":" + OString::number(GetPropertyValue(i)) ); + xOut->WriteLine(aString); + } + } + sal_Int32 i; + for ( i = 320; i < 383; i++ ) + { + if ( ( i >= DFF_Prop_adjustValue ) && ( i <= DFF_Prop_adjust10Value ) ) + continue; + if ( IsProperty( i ) ) + { + if ( SeekToContent( i, rIn ) ) + { + sal_Int32 nLen = (sal_Int32)GetPropertyValue( i ); + if ( nLen ) + { + xOut->WriteLine( "" ); + OStringBuffer aDesc("Property:" + OString::number(i) + + " Size:" + OString::number(nLen)); + xOut->WriteLine(aDesc.makeStringAndClear()); + sal_Int16 nNumElem, nNumElemMem, nNumSize; + rIn >> nNumElem >> nNumElemMem >> nNumSize; + aDesc.append("Entries: " + OString::number(nNumElem) + + " Size:" + OString::number(nNumSize)); + xOut->WriteLine(aDesc.makeStringAndClear()); + if ( nNumSize < 0 ) + nNumSize = ( ( -nNumSize ) >> 2 ); + if ( !nNumSize ) + nNumSize = 16; + nLen -= 6; + while ( nLen > 0 ) + { + for ( sal_uInt32 j = 0; nLen && ( j < ( nNumSize >> 1 ) ); j++ ) + { + for ( sal_uInt32 k = 0; k < 2; k++ ) + { + if ( nLen ) + { + sal_uInt8 nVal; + rIn >> nVal; + if ( ( nVal >> 4 ) > 9 ) + *xOut << (sal_uInt8)( ( nVal >> 4 ) + 'A' - 10 ); + else + *xOut << (sal_uInt8)( ( nVal >> 4 ) + '0' ); + + if ( ( nVal & 0xf ) > 9 ) + *xOut << (sal_uInt8)( ( nVal & 0xf ) + 'A' - 10 ); + else + *xOut << (sal_uInt8)( ( nVal & 0xf ) + '0' ); + + nLen--; + } + } + *xOut << (char)( ' ' ); + } + xOut->WriteLine( OString() ); + } + } + } + else + { + OString aString("Property" + OString::number(i) + + ":" + OString::number(GetPropertyValue(i))); + xOut->WriteLine(aString); + } + } + } + } + } + +#endif + + rIn.Seek( nFilePos ); +} + + +Degree100 DffPropertyReader::Fix16ToAngle( sal_Int32 nContent ) +{ + Degree100 nAngle(0); + if ( nContent ) + { + nAngle = Degree100(( static_cast( nContent >> 16) * 100L ) + ( ( ( nContent & 0x0000ffff) * 100L ) >> 16 )); + nAngle = NormAngle36000( -nAngle ); + } + return nAngle; +} + +DffPropertyReader::~DffPropertyReader() +{ +} + +static SvStream& operator>>( SvStream& rIn, SvxMSDffConnectorRule& rRule ) +{ + sal_uInt32 nRuleId; + rIn.ReadUInt32( nRuleId ) + .ReadUInt32( rRule.nShapeA ) + .ReadUInt32( rRule.nShapeB ) + .ReadUInt32( rRule.nShapeC ) + .ReadUInt32( rRule.ncptiA ) + .ReadUInt32( rRule.ncptiB ); + + return rIn; +} + +SvxMSDffSolverContainer::SvxMSDffSolverContainer() +{ +} + +SvxMSDffSolverContainer::~SvxMSDffSolverContainer() +{ +} + +SvStream& ReadSvxMSDffSolverContainer( SvStream& rIn, SvxMSDffSolverContainer& rContainer ) +{ + DffRecordHeader aHd; + bool bOk = ReadDffRecordHeader( rIn, aHd ); + if (!bOk || aHd.nRecType != DFF_msofbtSolverContainer) + return rIn; + + DffRecordHeader aCRule; + auto nEndPos = DffPropSet::SanitizeEndPos(rIn, aHd.GetRecEndFilePos()); + while ( rIn.good() && ( rIn.Tell() < nEndPos ) ) + { + if (!ReadDffRecordHeader(rIn, aCRule)) + break; + if ( aCRule.nRecType == DFF_msofbtConnectorRule ) + { + std::unique_ptr pRule(new SvxMSDffConnectorRule); + rIn >> *pRule; + rContainer.aCList.push_back( std::move(pRule) ); + } + if (!aCRule.SeekToEndOfRecord(rIn)) + break; + } + return rIn; +} + +void SvxMSDffManager::SolveSolver( const SvxMSDffSolverContainer& rSolver ) +{ + size_t i, nCnt; + for ( i = 0, nCnt = rSolver.aCList.size(); i < nCnt; i++ ) + { + SvxMSDffConnectorRule* pPtr = rSolver.aCList[ i ].get(); + if ( pPtr->pCObj ) + { + for ( int nN = 0; nN < 2; nN++ ) + { + SdrObject* pO; + sal_uInt32 nC; + ShapeFlag nSpFlags; + if ( !nN ) + { + pO = pPtr->pAObj; + nC = pPtr->ncptiA; + nSpFlags = pPtr->nSpFlagsA; + } + else + { + pO = pPtr->pBObj; + nC = pPtr->ncptiB; + nSpFlags = pPtr->nSpFlagsB; + } + if ( pO ) + { + SdrGluePoint aGluePoint; + Reference< XShape > aXShape( pO->getUnoShape(), UNO_QUERY ); + Reference< XShape > aXConnector( pPtr->pCObj->getUnoShape(), UNO_QUERY ); + SdrGluePointList* pList = pO->ForceGluePointList(); + + sal_Int32 nId = nC; + SdrInventor nInventor = pO->GetObjInventor(); + + if( nInventor == SdrInventor::Default ) + { + bool bValidGluePoint = false; + SdrObjKind nObjId = pO->GetObjIdentifier(); + switch( nObjId ) + { + case SdrObjKind::Group : + case SdrObjKind::Graphic : + case SdrObjKind::Rectangle : + case SdrObjKind::Text : + case SdrObjKind::Page : + case SdrObjKind::TitleText : + case SdrObjKind::OutlineText : + { + if ( nC & 1 ) + { + if ( nSpFlags & ShapeFlag::FlipH ) + nC ^= 2; // 1 <-> 3 + } + else + { + if ( nSpFlags & ShapeFlag::FlipV ) + nC ^= 1; // 0 <-> 2 + } + switch( nC ) + { + case 0 : + nId = 0; // SdrAlign::VERT_TOP; + break; + case 1 : + nId = 3; // SdrAlign::HORZ_RIGHT; + break; + case 2 : + nId = 2; // SdrAlign::VERT_BOTTOM; + break; + case 3 : + nId = 1; // SdrAlign::HORZ_LEFT; + break; + } + if ( nId <= 3 ) + bValidGluePoint = true; + } + break; + case SdrObjKind::Polygon : + case SdrObjKind::PolyLine : + case SdrObjKind::Line : + case SdrObjKind::PathLine : + case SdrObjKind::PathFill : + case SdrObjKind::FreehandLine : + case SdrObjKind::FreehandFill : + case SdrObjKind::SplineLine : + case SdrObjKind::SplineFill : + case SdrObjKind::PathPoly : + case SdrObjKind::PathPolyLine : + { + if (pList) + { + if (pList->GetCount() > nC ) + { + bValidGluePoint = true; + nId = static_cast((*pList)[ static_cast(nC)].GetId() + 3 ); + } + else + { + bool bNotFound = true; + + tools::PolyPolygon aPolyPoly( EscherPropertyContainer::GetPolyPolygon( aXShape ) ); + sal_uInt16 k, j, nPolySize = aPolyPoly.Count(); + if ( nPolySize ) + { + tools::Rectangle aBoundRect( aPolyPoly.GetBoundRect() ); + if ( aBoundRect.GetWidth() && aBoundRect.GetHeight() ) + { + sal_uInt32 nPointCount = 0; + for ( k = 0; bNotFound && ( k < nPolySize ); k++ ) + { + const tools::Polygon& rPolygon = aPolyPoly.GetObject( k ); + for ( j = 0; bNotFound && ( j < rPolygon.GetSize() ); j++ ) + { + PolyFlags eFlags = rPolygon.GetFlags( j ); + if ( eFlags == PolyFlags::Normal ) + { + if ( nC == nPointCount ) + { + const Point& rPoint = rPolygon.GetPoint( j ); + double fXRel = rPoint.X() - aBoundRect.Left(); + double fYRel = rPoint.Y() - aBoundRect.Top(); + sal_Int32 nWidth = aBoundRect.GetWidth(); + if ( !nWidth ) + nWidth = 1; + sal_Int32 nHeight= aBoundRect.GetHeight(); + if ( !nHeight ) + nHeight = 1; + fXRel /= static_cast(nWidth); + fXRel *= 10000; + fYRel /= static_cast(nHeight); + fYRel *= 10000; + aGluePoint.SetPos( Point( static_cast(fXRel), static_cast(fYRel) ) ); + aGluePoint.SetPercent( true ); + aGluePoint.SetAlign( SdrAlign::VERT_TOP | SdrAlign::HORZ_LEFT ); + aGluePoint.SetEscDir( SdrEscapeDirection::SMART ); + nId = static_cast((*pList)[ pList->Insert( aGluePoint ) ].GetId() + 3 ); + bNotFound = false; + } + nPointCount++; + } + } + } + } + } + if ( !bNotFound ) + { + bValidGluePoint = true; + } + } + } + } + break; + + case SdrObjKind::CustomShape : + { + const SfxPoolItem& aCustomShape = static_cast(pO)->GetMergedItem( SDRATTR_CUSTOMSHAPE_GEOMETRY ); + SdrCustomShapeGeometryItem aGeometryItem( static_cast(aCustomShape) ); + static const OUStringLiteral sPath( u"Path" ); + sal_Int16 nGluePointType = EnhancedCustomShapeGluePointType::SEGMENTS; + css::uno::Any* pAny = aGeometryItem.GetPropertyValueByName( sPath, "GluePointType" ); + if ( pAny ) + *pAny >>= nGluePointType; + else + { + OUString sShapeType; + pAny = aGeometryItem.GetPropertyValueByName( "Type" ); + if ( pAny ) + *pAny >>= sShapeType; + MSO_SPT eSpType = EnhancedCustomShapeTypeNames::Get( sShapeType ); + nGluePointType = GetCustomShapeConnectionTypeDefault( eSpType ); + } + if ( nGluePointType == EnhancedCustomShapeGluePointType::CUSTOM ) + { + if ( pList && ( pList->GetCount() > nC ) ) + { + bValidGluePoint = true; + nId = static_cast((*pList)[ static_cast(nC)].GetId() + 3 ); + } + } + else if ( nGluePointType == EnhancedCustomShapeGluePointType::RECT ) + { + if ( nC & 1 ) + { + if ( nSpFlags & ShapeFlag::FlipH ) + nC ^= 2; // 1 <-> 3 + } + else + { + if ( nSpFlags & ShapeFlag::FlipV ) + nC ^= 1; // 0 <-> 2 + } + switch( nC ) + { + case 0 : + nId = 0; // SdrAlign::VERT_TOP; + break; + case 1 : + nId = 3; // SdrAlign::HORZ_RIGHT; + break; + case 2 : + nId = 2; // SdrAlign::VERT_BOTTOM; + break; + case 3 : + nId = 1; // SdrAlign::HORZ_LEFT; + break; + } + if ( nId <= 3 ) + bValidGluePoint = true; + } + else if ( nGluePointType == EnhancedCustomShapeGluePointType::SEGMENTS ) + { + sal_uInt32 nPt = nC; + css::uno::Sequence< css::drawing::EnhancedCustomShapeSegment > aSegments; + pAny = aGeometryItem.GetPropertyValueByName( sPath, "Segments" ); + if ( pAny && (*pAny >>= aSegments) ) + { + nPt = 0; + for ( sal_Int32 k = 1; nC && ( k < aSegments.getLength() ); k++ ) + { + sal_Int16 j, nCnt2 = aSegments[ k ].Count; + if ( aSegments[ k ].Command != EnhancedCustomShapeSegmentCommand::UNKNOWN ) + { + for ( j = 0; nC && ( j < nCnt2 ); j++ ) + { + switch( aSegments[ k ].Command ) + { + case EnhancedCustomShapeSegmentCommand::ENDSUBPATH : + case EnhancedCustomShapeSegmentCommand::CLOSESUBPATH : + case EnhancedCustomShapeSegmentCommand::LINETO : + case EnhancedCustomShapeSegmentCommand::MOVETO : + { + nC--; + nPt++; + } + break; + case EnhancedCustomShapeSegmentCommand::ELLIPTICALQUADRANTX : + case EnhancedCustomShapeSegmentCommand::ELLIPTICALQUADRANTY : + break; + + case EnhancedCustomShapeSegmentCommand::CURVETO : + { + nC--; + nPt += 3; + } + break; + + case EnhancedCustomShapeSegmentCommand::ANGLEELLIPSETO : + case EnhancedCustomShapeSegmentCommand::ANGLEELLIPSE : + { + nC--; + nPt += 3; + } + break; + case EnhancedCustomShapeSegmentCommand::ARCTO : + case EnhancedCustomShapeSegmentCommand::ARC : + case EnhancedCustomShapeSegmentCommand::CLOCKWISEARCTO : + case EnhancedCustomShapeSegmentCommand::CLOCKWISEARC : + { + nC--; + nPt += 4; + } + break; + } + } + } + } + } + pAny = aGeometryItem.GetPropertyValueByName( sPath, "Coordinates" ); + if ( pAny ) + { + css::uno::Sequence< css::drawing::EnhancedCustomShapeParameterPair > aCoordinates; + *pAny >>= aCoordinates; + if ( nPt < o3tl::make_unsigned(aCoordinates.getLength()) ) + { + nId = 4; + css::drawing::EnhancedCustomShapeParameterPair& rPara = aCoordinates.getArray()[ nPt ]; + sal_Int32 nX = 0, nY = 0; + if ( ( rPara.First.Value >>= nX ) && ( rPara.Second.Value >>= nY ) ) + { + static const OUStringLiteral sGluePoints( u"GluePoints" ); + css::uno::Sequence< css::drawing::EnhancedCustomShapeParameterPair > aGluePoints; + pAny = aGeometryItem.GetPropertyValueByName( sPath, sGluePoints ); + if ( pAny ) + *pAny >>= aGluePoints; + sal_Int32 nGluePoints = aGluePoints.getLength(); + aGluePoints.realloc( nGluePoints + 1 ); + auto pGluePoints = aGluePoints.getArray(); + EnhancedCustomShape2d::SetEnhancedCustomShapeParameter( pGluePoints[ nGluePoints ].First, nX ); + EnhancedCustomShape2d::SetEnhancedCustomShapeParameter( pGluePoints[ nGluePoints ].Second, nY ); + PropertyValue aProp; + aProp.Name = sGluePoints; + aProp.Value <<= aGluePoints; + aGeometryItem.SetPropertyValue( sPath, aProp ); + bValidGluePoint = true; + static_cast(pO)->SetMergedItem( aGeometryItem ); + SdrGluePointList* pLst = pO->ForceGluePointList(); + if ( pLst->GetCount() > nGluePoints ) + nId = static_cast((*pLst)[ static_cast(nGluePoints) ].GetId() + 3 ); + } + } + } + } + } + break; + default: ; + } + if ( bValidGluePoint ) + { + Reference< XPropertySet > xPropSet( aXConnector, UNO_QUERY ); + if ( xPropSet.is() ) + { + if ( nN ) + { + OUString aPropName( "EndShape" ); + SetPropValue( Any(aXShape), xPropSet, aPropName ); + aPropName = "EndGluePointIndex"; + SetPropValue( Any(nId), xPropSet, aPropName ); + } + else + { + OUString aPropName( "StartShape" ); + SetPropValue( Any(aXShape), xPropSet, aPropName ); + aPropName = "StartGluePointIndex"; + SetPropValue( Any(nId), xPropSet, aPropName ); + } + + // Not sure what this is good for, repaint or broadcast of object change. + //( Thus I am adding repaint here + pO->SetChanged(); + pO->BroadcastObjectChange(); + } + } + } + } + } + } + } +} + +static basegfx::B2DPolyPolygon GetLineArrow( const sal_Int32 nLineWidth, const sal_uInt32 eLineEnd, + const sal_uInt32 eLineWidth, const sal_uInt32 eLineLength, + sal_Int32& rnArrowWidth, bool& rbArrowCenter, + OUString& rsArrowName, bool bScaleArrow ) +{ + basegfx::B2DPolyPolygon aRetPolyPoly; + // 70 100mm = 2pt = 40 twip. In MS, line width less than 2pt has the same size arrow as 2pt + //If the unit is twip. Make all use this unit especially the critical value 70/40. + sal_Int32 nLineWidthCritical = bScaleArrow ? 40 : 70; + double fLineWidth = nLineWidth < nLineWidthCritical ? nLineWidthCritical : nLineWidth; + + double fLengthMul, fWidthMul; + sal_Int32 nLineNumber; + switch( eLineLength ) + { + default : + case mso_lineMediumLenArrow : fLengthMul = 3.0; nLineNumber = 2; break; + case mso_lineShortArrow : fLengthMul = 2.0; nLineNumber = 1; break; + case mso_lineLongArrow : fLengthMul = 5.0; nLineNumber = 3; break; + } + switch( eLineWidth ) + { + default : + case mso_lineMediumWidthArrow : fWidthMul = 3.0; nLineNumber += 3; break; + case mso_lineNarrowArrow : fWidthMul = 2.0; break; + case mso_lineWideArrow : fWidthMul = 5.0; nLineNumber += 6; break; + } + + rbArrowCenter = false; + OUStringBuffer aArrowName; + switch ( eLineEnd ) + { + case mso_lineArrowEnd : + { + basegfx::B2DPolygon aTriangle; + aTriangle.append(basegfx::B2DPoint( fWidthMul * fLineWidth * 0.50, 0.0 )); + aTriangle.append(basegfx::B2DPoint( fWidthMul * fLineWidth, fLengthMul * fLineWidth )); + aTriangle.append(basegfx::B2DPoint( 0.0, fLengthMul * fLineWidth )); + aTriangle.setClosed(true); + aRetPolyPoly = basegfx::B2DPolyPolygon(aTriangle); + aArrowName.append("msArrowEnd "); + } + break; + + case mso_lineArrowOpenEnd : + { + switch( eLineLength ) + { + default : + case mso_lineMediumLenArrow : fLengthMul = 4.5; break; + case mso_lineShortArrow : fLengthMul = 3.5; break; + case mso_lineLongArrow : fLengthMul = 6.0; break; + } + switch( eLineWidth ) + { + default : + case mso_lineMediumWidthArrow : fWidthMul = 4.5; break; + case mso_lineNarrowArrow : fWidthMul = 3.5; break; + case mso_lineWideArrow : fWidthMul = 6.0; break; + } + basegfx::B2DPolygon aTriangle; + aTriangle.append(basegfx::B2DPoint( fWidthMul * fLineWidth * 0.50 , 0.0 )); + aTriangle.append(basegfx::B2DPoint( fWidthMul * fLineWidth, fLengthMul * fLineWidth * 0.91 )); + aTriangle.append(basegfx::B2DPoint( fWidthMul * fLineWidth * 0.85, fLengthMul * fLineWidth )); + aTriangle.append(basegfx::B2DPoint( fWidthMul * fLineWidth * 0.50, fLengthMul * fLineWidth * 0.36 )); + aTriangle.append(basegfx::B2DPoint( fWidthMul * fLineWidth * 0.15, fLengthMul * fLineWidth )); + aTriangle.append(basegfx::B2DPoint( 0.0, fLengthMul * fLineWidth * 0.91 )); + aTriangle.setClosed(true); + aRetPolyPoly = basegfx::B2DPolyPolygon(aTriangle); + aArrowName.append("msArrowOpenEnd "); + } + break; + case mso_lineArrowStealthEnd : + { + basegfx::B2DPolygon aTriangle; + aTriangle.append(basegfx::B2DPoint( fWidthMul * fLineWidth * 0.50 , 0.0 )); + aTriangle.append(basegfx::B2DPoint( fWidthMul * fLineWidth , fLengthMul * fLineWidth )); + aTriangle.append(basegfx::B2DPoint( fWidthMul * fLineWidth * 0.50 , fLengthMul * fLineWidth * 0.60 )); + aTriangle.append(basegfx::B2DPoint( 0.0, fLengthMul * fLineWidth )); + aTriangle.setClosed(true); + aRetPolyPoly = basegfx::B2DPolyPolygon(aTriangle); + aArrowName.append("msArrowStealthEnd "); + } + break; + case mso_lineArrowDiamondEnd : + { + basegfx::B2DPolygon aTriangle; + aTriangle.append(basegfx::B2DPoint( fWidthMul * fLineWidth * 0.50 , 0.0 )); + aTriangle.append(basegfx::B2DPoint( fWidthMul * fLineWidth , fLengthMul * fLineWidth * 0.50 )); + aTriangle.append(basegfx::B2DPoint( fWidthMul * fLineWidth * 0.50 , fLengthMul * fLineWidth )); + aTriangle.append(basegfx::B2DPoint( 0.0, fLengthMul * fLineWidth * 0.50 )); + aTriangle.setClosed(true); + aRetPolyPoly = basegfx::B2DPolyPolygon(aTriangle); + rbArrowCenter = true; + aArrowName.append("msArrowDiamondEnd "); + } + break; + case mso_lineArrowOvalEnd : + { + aRetPolyPoly = basegfx::B2DPolyPolygon( + XPolygon( + Point( static_cast( fWidthMul * fLineWidth * 0.50 ), 0 ), + static_cast( fWidthMul * fLineWidth * 0.50 ), + static_cast( fLengthMul * fLineWidth * 0.50 ), + 0_deg100, 36000_deg100 ).getB2DPolygon() ); + rbArrowCenter = true; + aArrowName.append("msArrowOvalEnd "); + } + break; + default: break; + } + aArrowName.append(nLineNumber); + rsArrowName = aArrowName.makeStringAndClear(); + rnArrowWidth = static_cast( fLineWidth * fWidthMul ); + + return aRetPolyPoly; +} + +void DffPropertyReader::ApplyLineAttributes( SfxItemSet& rSet, const MSO_SPT eShapeType ) const // #i28269# +{ + sal_uInt32 nLineFlags(GetPropertyValue( DFF_Prop_fNoLineDrawDash, 0 )); + + if(!IsHardAttribute( DFF_Prop_fLine ) && !IsCustomShapeStrokedByDefault( eShapeType )) + { + nLineFlags &= ~0x08; + } + + if ( nLineFlags & 8 ) + { + // Line Attributes + sal_Int32 nLineWidth = static_cast(GetPropertyValue( DFF_Prop_lineWidth, 9525 )); + + // support LineCap + auto eLineCap = GetPropertyValue(DFF_Prop_lineEndCapStyle, mso_lineEndCapFlat); + + switch(eLineCap) + { + default: /* case mso_lineEndCapFlat */ + { + // no need to set, it is the default. If this changes, this needs to be activated + // rSet.Put(XLineCapItem(css::drawing::LineCap_BUTT)); + break; + } + case mso_lineEndCapRound: + { + rSet.Put(XLineCapItem(css::drawing::LineCap_ROUND)); + break; + } + case mso_lineEndCapSquare: + { + rSet.Put(XLineCapItem(css::drawing::LineCap_SQUARE)); + break; + } + } + + auto eLineDashing = GetPropertyValue( DFF_Prop_lineDashing, mso_lineSolid); + if (eLineDashing == mso_lineSolid || nLineWidth < 0) + rSet.Put(XLineStyleItem( drawing::LineStyle_SOLID ) ); + else + { + // Despite of naming "dot" and "dash", that are all dashes and a "dot" can be longer + // than a "dash". The naming indicates the order, "dot" is always the first dash and + // "dash" is always the second dash. MS Office always starts with the longer dash, so + // set it here accordingly. + // The preset from binary is essentially the same as from OOXML. So here the same + // setting is used as in oox import. The comment corresponds to + // "dots, dotLen, dashes, dashLen, distance" there. + // MS Office uses always relative length, so no need to consider nLineWidth + // here. Values are of kind 300 for 300% in css::drawing::DashStyle, for example. + + sal_uInt16 nDots = 1; // in all cases, "solid" is treated above + // initialize, will be changed if necessary + sal_uInt32 nDotLen = 300; + sal_uInt16 nDashes = 0; + sal_uInt32 nDashLen = 0; + sal_uInt32 nDistance = 300; + switch ( eLineDashing ) + { + default: + case mso_lineDotSys : // 1 1 0 0 1 + { + nDotLen =100; + nDistance = 100; + } + break; + + case mso_lineDashGEL : // 1 4 0 0 3 + { + nDotLen = 400; + } + break; + + case mso_lineDashDotGEL : // 1 4 1 1 3 + { + nDotLen = 400; + nDashes = 1; + nDashLen = 100; + } + break; + + case mso_lineLongDashGEL : // 1 8 0 0 3 + { + nDotLen = 800; + } + break; + + case mso_lineLongDashDotGEL : // 1 8 1 1 3 + { + nDotLen = 800; + nDashes = 1; + nDashLen = 100; + } + break; + + case mso_lineLongDashDotDotGEL: // 1 8 2 1 3 + { + nDotLen = 800; + nDashes = 2; + nDashLen = 100; + } + break; + + case mso_lineDotGEL: // 1 1 0 0 3 + { + nDotLen = 100; + } + break; + + case mso_lineDashSys: // 1 3 0 0 1 + { + nDistance = 100; + } + break; + + case mso_lineDashDotSys: // 1 3 1 1 1 + { + nDashes = 1; + nDashLen = 100; + nDistance = 100; + } + break; + + case mso_lineDashDotDotSys: // 1 3 2 1 1 + { + nDashes = 2; + nDashLen = 100; + nDistance = 100; + } + break; + } + rSet.Put( XLineDashItem( OUString(), XDash( css::drawing::DashStyle_RECTRELATIVE, nDots, nDotLen, nDashes, nDashLen, nDistance ) ) ); + rSet.Put( XLineStyleItem( drawing::LineStyle_DASH ) ); + } + rSet.Put( XLineColorItem( OUString(), rManager.MSO_CLR_ToColor( GetPropertyValue( DFF_Prop_lineColor, 0 ) ) ) ); + if ( IsProperty( DFF_Prop_lineOpacity ) ) + { + double nTrans = GetPropertyValue(DFF_Prop_lineOpacity, 0x10000); + nTrans = (nTrans * 100) / 65536; + rSet.Put(XLineTransparenceItem( + sal_uInt16(100 - ::rtl::math::round(nTrans)))); + } + + rManager.ScaleEmu( nLineWidth ); + rSet.Put( XLineWidthItem( nLineWidth ) ); + + // SJ: LineJoint (setting each time a line is set, because our internal joint type has another default) + MSO_LineJoin eLineJointDefault = mso_lineJoinMiter; + if ( eShapeType == mso_sptMin ) + eLineJointDefault = mso_lineJoinRound; + auto eLineJoint = GetPropertyValue(DFF_Prop_lineJoinStyle, eLineJointDefault); + css::drawing::LineJoint eXLineJoint( css::drawing::LineJoint_MITER ); + if ( eLineJoint == mso_lineJoinBevel ) + eXLineJoint = css::drawing::LineJoint_BEVEL; + else if ( eLineJoint == mso_lineJoinRound ) + eXLineJoint = css::drawing::LineJoint_ROUND; + rSet.Put( XLineJointItem( eXLineJoint ) ); + + if ( nLineFlags & 0x10 ) + { + bool bScaleArrows = rManager.pSdrModel->GetScaleUnit() == MapUnit::MapTwip; + + // LineStart + + if ( IsProperty( DFF_Prop_lineStartArrowhead ) ) + { + auto eLineEnd = GetPropertyValue(DFF_Prop_lineStartArrowhead, 0); + auto eWidth = GetPropertyValue(DFF_Prop_lineStartArrowWidth, mso_lineMediumWidthArrow); + auto eLength = GetPropertyValue(DFF_Prop_lineStartArrowLength, mso_lineMediumLenArrow); + + sal_Int32 nArrowWidth; + bool bArrowCenter; + OUString aArrowName; + basegfx::B2DPolyPolygon aPolyPoly(GetLineArrow( nLineWidth, eLineEnd, eWidth, eLength, nArrowWidth, bArrowCenter, aArrowName, bScaleArrows )); + + rSet.Put( XLineStartWidthItem( nArrowWidth ) ); + rSet.Put( XLineStartItem( aArrowName, aPolyPoly) ); + rSet.Put( XLineStartCenterItem( bArrowCenter ) ); + } + + // LineEnd + + if ( IsProperty( DFF_Prop_lineEndArrowhead ) ) + { + auto eLineEnd = GetPropertyValue(DFF_Prop_lineEndArrowhead, 0); + auto eWidth = GetPropertyValue(DFF_Prop_lineEndArrowWidth, mso_lineMediumWidthArrow); + auto eLength = GetPropertyValue(DFF_Prop_lineEndArrowLength, mso_lineMediumLenArrow); + + sal_Int32 nArrowWidth; + bool bArrowCenter; + OUString aArrowName; + basegfx::B2DPolyPolygon aPolyPoly(GetLineArrow( nLineWidth, eLineEnd, eWidth, eLength, nArrowWidth, bArrowCenter, aArrowName, bScaleArrows )); + + rSet.Put( XLineEndWidthItem( nArrowWidth ) ); + rSet.Put( XLineEndItem( aArrowName, aPolyPoly ) ); + rSet.Put( XLineEndCenterItem( bArrowCenter ) ); + } + } + } + else + rSet.Put( XLineStyleItem( drawing::LineStyle_NONE ) ); +} + +namespace { + +struct ShadeColor +{ + Color aColor; + double fDist; + + ShadeColor( const Color& rC, double fR ) : aColor( rC ), fDist( fR ) {}; +}; + +} + +static void GetShadeColors( const SvxMSDffManager& rManager, const DffPropertyReader& rProperties, SvStream& rIn, std::vector< ShadeColor >& rShadeColors ) +{ + sal_uInt64 nPos = rIn.Tell(); + if ( rProperties.IsProperty( DFF_Prop_fillShadeColors ) ) + { + sal_uInt16 i = 0, nNumElem = 0; + bool bOk = false; + if (rProperties.SeekToContent(DFF_Prop_fillShadeColors, rIn)) + { + sal_uInt16 nNumElemReserved = 0, nSize = 0; + rIn.ReadUInt16( nNumElem ).ReadUInt16( nNumElemReserved ).ReadUInt16( nSize ); + //sanity check that the stream is long enough to fulfill nNumElem * 2 sal_Int32s + bOk = rIn.remainingSize() / (2*sizeof(sal_Int32)) >= nNumElem; + } + if (bOk) + { + for ( ; i < nNumElem; i++ ) + { + sal_Int32 nColor(0); + sal_Int32 nDist(0); + + rIn.ReadInt32( nColor ).ReadInt32( nDist ); + rShadeColors.emplace_back( rManager.MSO_CLR_ToColor( nColor, DFF_Prop_fillColor ), 1.0 - ( nDist / 65536.0 ) ); + } + } + } + if ( rShadeColors.empty() ) + { + rShadeColors.emplace_back( rManager.MSO_CLR_ToColor( rProperties.GetPropertyValue( DFF_Prop_fillBackColor, sal_uInt32(COL_WHITE) ), DFF_Prop_fillBackColor ), 0 ); + rShadeColors.emplace_back( rManager.MSO_CLR_ToColor( rProperties.GetPropertyValue( DFF_Prop_fillColor, sal_uInt32(COL_WHITE) ), DFF_Prop_fillColor ), 1 ); + } + rIn.Seek( nPos ); +} + +static void ApplyRectangularGradientAsBitmap( const SvxMSDffManager& rManager, SvStream& rIn, SfxItemSet& rSet, const std::vector< ShadeColor >& rShadeColors, const DffObjData& rObjData, Degree100 nFix16Angle ) +{ + Size aBitmapSizePixel( static_cast< sal_Int32 >( ( rObjData.aBoundRect.GetWidth() / 2540.0 ) * 90.0 ), // we will create a bitmap with 90 dpi + static_cast< sal_Int32 >( ( rObjData.aBoundRect.GetHeight() / 2540.0 ) * 90.0 ) ); + if (aBitmapSizePixel.IsEmpty() || aBitmapSizePixel.Width() > 1024 || aBitmapSizePixel.Height() > 1024) + return; + + double fFocusX = rManager.GetPropertyValue( DFF_Prop_fillToRight, 0 ) / 65536.0; + double fFocusY = rManager.GetPropertyValue( DFF_Prop_fillToBottom, 0 ) / 65536.0; + + vcl::bitmap::RawBitmap aBitmap(aBitmapSizePixel, 24); + + for ( tools::Long nY = 0; nY < aBitmapSizePixel.Height(); nY++ ) + { + for ( tools::Long nX = 0; nX < aBitmapSizePixel.Width(); nX++ ) + { + double fX = static_cast< double >( nX ) / aBitmapSizePixel.Width(); + double fY = static_cast< double >( nY ) / aBitmapSizePixel.Height(); + + double fD, fDist; + if ( fX < fFocusX ) + { + if ( fY < fFocusY ) + { + if ( fX > fY ) + { + fDist = fY; + fD = fFocusY; + } + else + { + fDist = fX; + fD = fFocusX; + } + } + else + { + if ( fX > ( 1 - fY ) ) + { + fDist = 1 - fY; + fD = 1 - fFocusY; + } + else + { + fDist = fX; + fD = fFocusX; + } + } + } + else + { + if ( fY < fFocusY ) + { + if ( ( 1 - fX ) > fY ) + { + fDist = fY; + fD = fFocusY; + } + else + { + fDist = 1 - fX; + fD = 1 - fFocusX; + } + } + else + { + if ( ( 1 - fX ) > ( 1 - fY ) ) + { + fDist = 1 - fY; + fD = 1 - fFocusY; + } + else + { + fDist = 1 - fX; + fD = 1 - fFocusX; + } + } + } + if ( fD != 0.0 ) + fDist /= fD; + + double fA = 0.0; + Color aColorA = rShadeColors.front().aColor; + double fB = 1.0; + Color aColorB( aColorA ); + for ( const auto& rShadeColor : rShadeColors ) + { + if ( fA <= rShadeColor.fDist && rShadeColor.fDist <= fDist ) + { + fA = rShadeColor.fDist; + aColorA = rShadeColor.aColor; + } + if ( fDist < rShadeColor.fDist && rShadeColor.fDist <= fB ) + { + fB = rShadeColor.fDist; + aColorB = rShadeColor.aColor; + } + } + double fRed = aColorA.GetRed(), fGreen = aColorA.GetGreen(), fBlue = aColorA.GetBlue(); + double fD1 = fB - fA; + if ( fD1 != 0.0 ) + { + fRed += ( ( ( fDist - fA ) * ( aColorB.GetRed() - aColorA.GetRed() ) ) / fD1 ); // + aQuantErrCurrScan[ nX ].fRed; + fGreen += ( ( ( fDist - fA ) * ( aColorB.GetGreen() - aColorA.GetGreen() ) ) / fD1 ); // + aQuantErrCurrScan[ nX ].fGreen; + fBlue += ( ( ( fDist - fA ) * ( aColorB.GetBlue() - aColorA.GetBlue() ) ) / fD1 ); // + aQuantErrCurrScan[ nX ].fBlue; + } + sal_Int16 nRed = static_cast< sal_Int16 >( fRed + 0.5 ); + sal_Int16 nGreen = static_cast< sal_Int16 >( fGreen + 0.5 ); + sal_Int16 nBlue = static_cast< sal_Int16 >( fBlue + 0.5 ); + if ( nRed < 0 ) + nRed = 0; + if ( nRed > 255 ) + nRed = 255; + if ( nGreen < 0 ) + nGreen = 0; + if ( nGreen > 255 ) + nGreen = 255; + if ( nBlue < 0 ) + nBlue = 0; + if ( nBlue > 255 ) + nBlue = 255; + + aBitmap.SetPixel(nY, nX, Color(static_cast(nRed), static_cast(nGreen), static_cast(nBlue))); + } + } + BitmapEx aBitmapEx = vcl::bitmap::CreateFromData( std::move(aBitmap) ); + + if ( nFix16Angle ) + { + bool bRotateWithShape = true; // sal_True seems to be default + sal_uInt64 nPos = rIn.Tell(); + if ( const_cast< SvxMSDffManager& >( rManager ).maShapeRecords.SeekToContent( rIn, DFF_msofbtUDefProp, SEEK_FROM_CURRENT_AND_RESTART ) ) + { + const_cast< SvxMSDffManager& >( rManager ).maShapeRecords.Current()->SeekToBegOfRecord( rIn ); + DffPropertyReader aSecPropSet( rManager ); + aSecPropSet.ReadPropSet( rIn, nullptr ); + sal_Int32 nSecFillProperties = aSecPropSet.GetPropertyValue( DFF_Prop_fNoFillHitTest, 0x200020 ); + bRotateWithShape = ( nSecFillProperties & 0x0020 ); + } + rIn.Seek( nPos ); + if ( bRotateWithShape ) + { + // convert from 100th to 10th degrees + aBitmapEx.Rotate( to(nFix16Angle), rShadeColors[ 0 ].aColor ); + + BmpMirrorFlags nMirrorFlags = BmpMirrorFlags::NONE; + if ( rObjData.nSpFlags & ShapeFlag::FlipV ) + nMirrorFlags |= BmpMirrorFlags::Vertical; + if ( rObjData.nSpFlags & ShapeFlag::FlipH ) + nMirrorFlags |= BmpMirrorFlags::Horizontal; + if ( nMirrorFlags != BmpMirrorFlags::NONE ) + aBitmapEx.Mirror( nMirrorFlags ); + } + } + + rSet.Put(XFillBmpTileItem(false)); + rSet.Put(XFillBitmapItem(OUString(), Graphic(aBitmapEx))); +} + +void DffPropertyReader::ApplyFillAttributes( SvStream& rIn, SfxItemSet& rSet, const DffObjData& rObjData ) const +{ + sal_uInt32 nFillFlags(GetPropertyValue( DFF_Prop_fNoFillHitTest, 0 )); + + std::vector< ShadeColor > aShadeColors; + GetShadeColors( rManager, *this, rIn, aShadeColors ); + + if(!IsHardAttribute( DFF_Prop_fFilled ) && !IsCustomShapeFilledByDefault( rObjData.eShapeType )) + { + nFillFlags &= ~0x10; + } + + if ( nFillFlags & 0x10 ) + { + auto eMSO_FillType = GetPropertyValue(DFF_Prop_fillType, mso_fillSolid); + drawing::FillStyle eXFill = drawing::FillStyle_NONE; + switch( eMSO_FillType ) + { + case mso_fillSolid : // Fill with a solid color + eXFill = drawing::FillStyle_SOLID; + break; + case mso_fillPattern : // Fill with a pattern (bitmap) + case mso_fillTexture : // A texture (pattern with its own color map) + case mso_fillPicture : // Center a picture in the shape + eXFill = drawing::FillStyle_BITMAP; + break; + case mso_fillShadeCenter : // Shade from bounding rectangle to end point + { + //If it is imported as a bitmap, it will not work well with transparency especially 100 + //But the gradient look well comparing with imported as gradient. And rotate with shape + //also works better. So here just keep it. + if ( rObjData.aBoundRect.IsEmpty() )// size of object needed to be able + eXFill = drawing::FillStyle_GRADIENT; // to create a bitmap substitution + else + eXFill = drawing::FillStyle_BITMAP; + } + break; + case mso_fillShade : // Shade from start to end points + case mso_fillShadeShape : // Shade from shape outline to end point + case mso_fillShadeScale : // Similar to mso_fillShade, but the fillAngle + case mso_fillShadeTitle : // special type - shade to title --- for PP + eXFill = drawing::FillStyle_GRADIENT; + break; +// case mso_fillBackground : // Use the background fill color/pattern + default: break; + } + rSet.Put( XFillStyleItem( eXFill ) ); + + double dTrans = 1.0; + double dBackTrans = 1.0; + if (IsProperty(DFF_Prop_fillOpacity)) + { + dTrans = GetPropertyValue(DFF_Prop_fillOpacity, 0) / 65536.0; + if ( eXFill != drawing::FillStyle_GRADIENT ) + { + dTrans = dTrans * 100; + rSet.Put(XFillTransparenceItem( + sal_uInt16(100 - ::rtl::math::round(dTrans)))); + } + } + + if ( IsProperty(DFF_Prop_fillBackOpacity) ) + dBackTrans = GetPropertyValue(DFF_Prop_fillBackOpacity, 0) / 65536.0; + + if ( ( eMSO_FillType == mso_fillShadeCenter ) && ( eXFill == drawing::FillStyle_BITMAP ) ) + { + ApplyRectangularGradientAsBitmap( rManager, rIn, rSet, aShadeColors, rObjData, mnFix16Angle ); + } + else if ( eXFill == drawing::FillStyle_GRADIENT ) + { + ImportGradientColor ( rSet, eMSO_FillType, dTrans , dBackTrans ); + } + else if ( eXFill == drawing::FillStyle_BITMAP ) + { + if( IsProperty( DFF_Prop_fillBlip ) ) + { + Graphic aGraf; + // first try to get BLIP from cache + bool bOK = const_cast(rManager).GetBLIP( GetPropertyValue( DFF_Prop_fillBlip, 0 ), aGraf ); + // then try directly from stream (i.e. Excel chart hatches/bitmaps) + if ( !bOK ) + bOK = SeekToContent( DFF_Prop_fillBlip, rIn ) && SvxMSDffManager::GetBLIPDirect( rIn, aGraf ); + if ( bOK ) + { + if ( eMSO_FillType == mso_fillPattern ) + { + Bitmap aBmp( aGraf.GetBitmapEx().GetBitmap() ); + if (aBmp.GetSizePixel().Width() == 8 && + aBmp.GetSizePixel().Height() == 8 && + aBmp.getPixelFormat() == vcl::PixelFormat::N1_BPP) + { + Color aCol1( COL_WHITE ), aCol2( COL_WHITE ); + + if ( IsProperty( DFF_Prop_fillColor ) ) + aCol1 = rManager.MSO_CLR_ToColor( GetPropertyValue( DFF_Prop_fillColor, 0 ), DFF_Prop_fillColor ); + + if ( IsProperty( DFF_Prop_fillBackColor ) ) + aCol2 = rManager.MSO_CLR_ToColor( GetPropertyValue( DFF_Prop_fillBackColor, 0 ), DFF_Prop_fillBackColor ); + + // Create a bitmap for the pattern with expected colors + vcl::bitmap::RawBitmap aResult(Size(8, 8), 24); + { + Bitmap::ScopedReadAccess pRead(aBmp); + + for (tools::Long y = 0; y < aResult.Height(); ++y) + { + Scanline pScanlineRead = pRead->GetScanline( y ); + for (tools::Long x = 0; x < aResult.Width(); ++x) + { + Color aReadColor; + if (pRead->HasPalette()) + aReadColor = pRead->GetPaletteColor(pRead->GetIndexFromData(pScanlineRead, x)); + else + aReadColor = pRead->GetPixelFromData(pScanlineRead, x); + + if (aReadColor == Color(0)) + aResult.SetPixel(y, x, aCol2); + else + aResult.SetPixel(y, x, aCol1); + } + } + } + aGraf = Graphic(vcl::bitmap::CreateFromData(std::move(aResult))); + } + + rSet.Put(XFillBitmapItem(OUString(), aGraf)); + } + else if ( eMSO_FillType == mso_fillTexture ) + { + rSet.Put(XFillBmpTileItem(true)); + rSet.Put(XFillBitmapItem(OUString(), aGraf)); + rSet.Put(XFillBmpSizeXItem(GetPropertyValue(DFF_Prop_fillWidth, 0) / 360)); + rSet.Put(XFillBmpSizeYItem(GetPropertyValue(DFF_Prop_fillHeight, 0) / 360)); + rSet.Put(XFillBmpSizeLogItem(true)); + } + else + { + rSet.Put(XFillBitmapItem(OUString(), aGraf)); + rSet.Put(XFillBmpTileItem(false)); + } + } + } + } + } + else + rSet.Put( XFillStyleItem( drawing::FillStyle_NONE ) ); +} + +void DffPropertyReader::ApplyCustomShapeTextAttributes( SfxItemSet& rSet ) const +{ + bool bVerticalText = false; + sal_Int32 nTextLeft = GetPropertyValue( DFF_Prop_dxTextLeft, 25 * 3600 ) / 360; // 0.25 cm (emu) + sal_Int32 nTextRight = GetPropertyValue( DFF_Prop_dxTextRight, 25 * 3600 ) / 360; // 0.25 cm (emu) + sal_Int32 nTextTop = GetPropertyValue( DFF_Prop_dyTextTop, 13 * 3600 ) / 360; // 0.13 cm (emu) + sal_Int32 nTextBottom = GetPropertyValue( DFF_Prop_dyTextBottom, 13 * 3600 ) /360; // 0.13 cm (emu) + + SdrTextVertAdjust eTVA; + SdrTextHorzAdjust eTHA; + + if ( IsProperty( DFF_Prop_txflTextFlow ) ) + { + auto eTextFlow = GetPropertyValue(DFF_Prop_txflTextFlow, 0) & 0xFFFF; + switch( eTextFlow ) + { + case mso_txflTtoBA : /* #68110# */ // Top to Bottom @-font, oben -> unten + case mso_txflTtoBN : // Top to Bottom non-@, oben -> unten + case mso_txflVertN : // Vertical, non-@, oben -> unten + bVerticalText = true; // nTextRotationAngle += 27000; + break; + default: break; + } + } + sal_Int32 nFontDirection = GetPropertyValue( DFF_Prop_cdirFont, mso_cdir0 ); + if ( ( nFontDirection == 1 ) || ( nFontDirection == 3 ) ) + bVerticalText = !bVerticalText; + + if ( bVerticalText ) + { + eTHA = SDRTEXTHORZADJUST_CENTER; + + // read text anchor + sal_uInt32 eTextAnchor = GetPropertyValue( DFF_Prop_anchorText, mso_anchorTop ); + + switch( eTextAnchor ) + { + case mso_anchorTop: + case mso_anchorTopCentered: + case mso_anchorTopBaseline: + case mso_anchorTopCenteredBaseline: + eTHA = SDRTEXTHORZADJUST_RIGHT; + break; + + case mso_anchorMiddle : + case mso_anchorMiddleCentered: + eTHA = SDRTEXTHORZADJUST_CENTER; + break; + + case mso_anchorBottom: + case mso_anchorBottomCentered: + case mso_anchorBottomBaseline: + case mso_anchorBottomCenteredBaseline: + eTHA = SDRTEXTHORZADJUST_LEFT; + break; + } + // if there is a 100% use of following attributes, the textbox can been aligned also in vertical direction + switch ( eTextAnchor ) + { + case mso_anchorTopCentered : + case mso_anchorMiddleCentered : + case mso_anchorBottomCentered : + case mso_anchorTopCenteredBaseline: + case mso_anchorBottomCenteredBaseline: + eTVA = SDRTEXTVERTADJUST_CENTER; + break; + + default : + eTVA = SDRTEXTVERTADJUST_TOP; + break; + } + } + else + { + eTVA = SDRTEXTVERTADJUST_CENTER; + + // read text anchor + sal_uInt32 eTextAnchor = GetPropertyValue( DFF_Prop_anchorText, mso_anchorTop ); + + switch( eTextAnchor ) + { + case mso_anchorTop: + case mso_anchorTopCentered: + case mso_anchorTopBaseline: + case mso_anchorTopCenteredBaseline: + eTVA = SDRTEXTVERTADJUST_TOP; + break; + + case mso_anchorMiddle : + case mso_anchorMiddleCentered: + eTVA = SDRTEXTVERTADJUST_CENTER; + break; + + case mso_anchorBottom: + case mso_anchorBottomCentered: + case mso_anchorBottomBaseline: + case mso_anchorBottomCenteredBaseline: + eTVA = SDRTEXTVERTADJUST_BOTTOM; + break; + } + // if there is a 100% usage of following attributes, the textbox can be aligned also in horizontal direction + switch ( eTextAnchor ) + { + case mso_anchorTopCentered : + case mso_anchorMiddleCentered : + case mso_anchorBottomCentered : + case mso_anchorTopCenteredBaseline: + case mso_anchorBottomCenteredBaseline: + eTHA = SDRTEXTHORZADJUST_CENTER; // the text has to be displayed using the full width; + break; + + default : + eTHA = SDRTEXTHORZADJUST_LEFT; + break; + } + } + rSet.Put( SvxFrameDirectionItem( bVerticalText ? SvxFrameDirection::Vertical_RL_TB : SvxFrameDirection::Horizontal_LR_TB, EE_PARA_WRITINGDIR ) ); + + rSet.Put( SdrTextVertAdjustItem( eTVA ) ); + rSet.Put( SdrTextHorzAdjustItem( eTHA ) ); + + rSet.Put( makeSdrTextLeftDistItem( nTextLeft ) ); + rSet.Put( makeSdrTextRightDistItem( nTextRight ) ); + rSet.Put( makeSdrTextUpperDistItem( nTextTop ) ); + rSet.Put( makeSdrTextLowerDistItem( nTextBottom ) ); + + rSet.Put( makeSdrTextWordWrapItem( GetPropertyValue(DFF_Prop_WrapText, mso_wrapSquare) != mso_wrapNone ) ); + rSet.Put( makeSdrTextAutoGrowHeightItem( ( GetPropertyValue( DFF_Prop_FitTextToShape, 0 ) & 2 ) != 0 ) ); +} + +void DffPropertyReader::ApplyCustomShapeGeometryAttributes( SvStream& rIn, SfxItemSet& rSet, const DffObjData& rObjData ) const +{ + + sal_uInt32 nAdjustmentsWhichNeedsToBeConverted = 0; + + + // creating SdrCustomShapeGeometryItem + + typedef std::vector< beans::PropertyValue > PropVec; + + // aPropVec will be filled with all PropertyValues + PropVec aPropVec; + PropertyValue aProp; + + + // "Type" property, including the predefined CustomShape type name + + aProp.Name = "Type"; + aProp.Value <<= EnhancedCustomShapeTypeNames::Get( rObjData.eShapeType ); + aPropVec.push_back( aProp ); + + + // "ViewBox" + + + sal_Int32 nCoordWidth = 21600; // needed to replace handle type center with absolute value + sal_Int32 nCoordHeight= 21600; + if ( IsProperty( DFF_Prop_geoLeft ) || IsProperty( DFF_Prop_geoTop ) || IsProperty( DFF_Prop_geoRight ) || IsProperty( DFF_Prop_geoBottom ) ) + { + css::awt::Rectangle aViewBox; + aViewBox.X = GetPropertyValue( DFF_Prop_geoLeft, 0 ); + aViewBox.Y = GetPropertyValue( DFF_Prop_geoTop, 0 ); + aViewBox.Width = nCoordWidth = o3tl::saturating_sub(GetPropertyValue(DFF_Prop_geoRight, 21600), aViewBox.X); + aViewBox.Height = nCoordHeight = o3tl::saturating_sub(GetPropertyValue(DFF_Prop_geoBottom, 21600), aViewBox.Y); + aProp.Name = "ViewBox"; + aProp.Value <<= aViewBox; + aPropVec.push_back( aProp ); + } + + // TextRotateAngle + + if ( IsProperty( DFF_Prop_txflTextFlow ) || IsProperty( DFF_Prop_cdirFont ) ) + { + sal_Int32 nTextRotateAngle = 0; + auto eTextFlow = GetPropertyValue(DFF_Prop_txflTextFlow, 0) & 0xFFFF; + + if ( eTextFlow == mso_txflBtoT ) // Bottom to Top non-@ + nTextRotateAngle += 90; + switch( GetPropertyValue( DFF_Prop_cdirFont, mso_cdir0 ) ) // SJ: mso_cdir90 and mso_cdir270 will be simulated by + { // activating vertical writing for the text objects + case mso_cdir90 : + { + if ( eTextFlow == mso_txflTtoBA ) + nTextRotateAngle -= 180; + } + break; + case mso_cdir180: nTextRotateAngle -= 180; break; + case mso_cdir270: + { + if ( eTextFlow != mso_txflTtoBA ) + nTextRotateAngle -= 180; + } + break; + default: break; + } + if ( nTextRotateAngle ) + { + double fTextRotateAngle = nTextRotateAngle; + aProp.Name = "TextRotateAngle"; + aProp.Value <<= fTextRotateAngle; + aPropVec.push_back( aProp ); + } + } + + // "Extrusion" PropertySequence element + + bool bExtrusionOn = ( GetPropertyValue( DFF_Prop_fc3DLightFace, 0 ) & 8 ) != 0; + if ( bExtrusionOn ) + { + PropVec aExtrusionPropVec; + + // "Extrusion" + aProp.Name = "Extrusion"; + aProp.Value <<= bExtrusionOn; + aExtrusionPropVec.push_back( aProp ); + + // "Brightness" + // MS Office default 0x00004E20 16.16 FixedPoint, 20000/65536=0.30517, ODF default 33%. + // Thus must set value even if default. + double fBrightness = 20000.0; + if ( IsProperty( DFF_Prop_c3DAmbientIntensity ) ) + { + // Value must be in range 0.0 to 1.0 in MS Office binary specification, but larger + // values are in fact interpreted. + fBrightness = GetPropertyValue( DFF_Prop_c3DAmbientIntensity, 0 ); + } + fBrightness /= 655.36; + aProp.Name = "Brightness"; + aProp.Value <<= fBrightness; + aExtrusionPropVec.push_back( aProp ); + + // "Depth" in 1/100mm + if ( IsProperty( DFF_Prop_c3DExtrudeBackward ) || IsProperty( DFF_Prop_c3DExtrudeForward ) ) + { + double fBackDepth = static_cast(static_cast(GetPropertyValue( DFF_Prop_c3DExtrudeBackward, 1270 * 360 ))) / 360.0; + double fForeDepth = static_cast(static_cast(GetPropertyValue( DFF_Prop_c3DExtrudeForward, 0 ))) / 360.0; + double fDepth = fBackDepth + fForeDepth; + double fFraction = fDepth != 0.0 ? fForeDepth / fDepth : 0; + EnhancedCustomShapeParameterPair aDepthParaPair; + aDepthParaPair.First.Value <<= fDepth; + aDepthParaPair.First.Type = EnhancedCustomShapeParameterType::NORMAL; + aDepthParaPair.Second.Value <<= fFraction; + aDepthParaPair.Second.Type = EnhancedCustomShapeParameterType::NORMAL; + aProp.Name = "Depth"; + aProp.Value <<= aDepthParaPair; + aExtrusionPropVec.push_back( aProp ); + } + // "Diffusion" + // ODF default is 0%, MS Office default is 100%. Thus must set value even if default. + double fDiffusion = 100; + if ( IsProperty( DFF_Prop_c3DDiffuseAmt ) ) + { + fDiffusion = static_cast(GetPropertyValue( DFF_Prop_c3DDiffuseAmt, 0 )); + fDiffusion /= 655.36; + } + aProp.Name = "Diffusion"; + aProp.Value <<= fDiffusion; + aExtrusionPropVec.push_back( aProp ); + + // "NumberOfLineSegments" + if ( IsProperty( DFF_Prop_c3DTolerance ) ) + { + aProp.Name = "NumberOfLineSegments"; + aProp.Value <<= static_cast(GetPropertyValue( DFF_Prop_c3DTolerance, 0 )); + aExtrusionPropVec.push_back( aProp ); + } + // "LightFace" + bool bExtrusionLightFace = ( GetPropertyValue( DFF_Prop_fc3DLightFace, 0 ) & 1 ) != 0; + aProp.Name = "LightFace"; + aProp.Value <<= bExtrusionLightFace; + aExtrusionPropVec.push_back( aProp ); + // "FirstLightHarsh" + bool bExtrusionFirstLightHarsh = ( GetPropertyValue( DFF_Prop_fc3DFillHarsh, 0 ) & 2 ) != 0; + aProp.Name = "FirstLightHarsh"; + aProp.Value <<= bExtrusionFirstLightHarsh; + aExtrusionPropVec.push_back( aProp ); + // "SecondLightHarsh" + bool bExtrusionSecondLightHarsh = ( GetPropertyValue( DFF_Prop_fc3DFillHarsh, 0 ) & 1 ) != 0; + aProp.Name = "SecondLightHarsh"; + aProp.Value <<= bExtrusionSecondLightHarsh; + aExtrusionPropVec.push_back( aProp ); + + // "FirstLightLevel" + // MS Office default 0x00009470 16.16 FixedPoint, 38000/65536 = 0.5798, ODF default 66%. + // Thus must set value even if default. + double fFirstLightLevel = 38000.0; + if ( IsProperty( DFF_Prop_c3DKeyIntensity ) ) + { + // value<0 and value>1 are allowed in MS Office. Clamp such in ODF export, not here. + fFirstLightLevel = static_cast(GetPropertyValue( DFF_Prop_c3DKeyIntensity, 0 )); + } + fFirstLightLevel /= 655.36; + aProp.Name = "FirstLightLevel"; + aProp.Value <<= fFirstLightLevel; + aExtrusionPropVec.push_back( aProp ); + + // "SecondLightLevel" + // MS Office default 0x00009470 16.16 FixedPoint, 38000/65536 = 0.5798, ODF default 66%. + // Thus must set value even if default. + double fSecondLightLevel = 38000.0; + if ( IsProperty( DFF_Prop_c3DFillIntensity ) ) + { + // value<0 and value>1 are allowed in MS Office. Clamp such in ODF export, not here. + fSecondLightLevel = static_cast(GetPropertyValue( DFF_Prop_c3DFillIntensity, 0 )); + } + fSecondLightLevel /= 655.36; + aProp.Name = "SecondLightLevel"; + aProp.Value <<= fSecondLightLevel; + aExtrusionPropVec.push_back( aProp ); + + // "FirstLightDirection" + if ( IsProperty( DFF_Prop_c3DKeyX ) || IsProperty( DFF_Prop_c3DKeyY ) || IsProperty( DFF_Prop_c3DKeyZ ) ) + { + double fLightX = static_cast(static_cast(GetPropertyValue( DFF_Prop_c3DKeyX, 50000 ))); + double fLightY = static_cast(static_cast(GetPropertyValue( DFF_Prop_c3DKeyY, 0 ))); + double fLightZ = static_cast(static_cast(GetPropertyValue( DFF_Prop_c3DKeyZ, 10000 ))); + css::drawing::Direction3D aExtrusionFirstLightDirection( fLightX, fLightY, fLightZ ); + aProp.Name = "FirstLightDirection"; + aProp.Value <<= aExtrusionFirstLightDirection; + aExtrusionPropVec.push_back( aProp ); + } + // "SecondLightDirection" + if ( IsProperty( DFF_Prop_c3DFillX ) || IsProperty( DFF_Prop_c3DFillY ) || IsProperty( DFF_Prop_c3DFillZ ) ) + { + double fLight2X = static_cast(static_cast(GetPropertyValue( DFF_Prop_c3DFillX, sal_uInt32(-50000) ))); + double fLight2Y = static_cast(static_cast(GetPropertyValue( DFF_Prop_c3DFillY, 0 ))); + double fLight2Z = static_cast(static_cast(GetPropertyValue( DFF_Prop_c3DFillZ, 10000 ))); + css::drawing::Direction3D aExtrusionSecondLightDirection( fLight2X, fLight2Y, fLight2Z ); + aProp.Name = "SecondLightDirection"; + aProp.Value <<= aExtrusionSecondLightDirection; + aExtrusionPropVec.push_back( aProp ); + } + + // "Metal" + bool bExtrusionMetal = ( GetPropertyValue( DFF_Prop_fc3DLightFace, 0 ) & 4 ) != 0; + aProp.Name = "Metal"; + aProp.Value <<= bExtrusionMetal; + aExtrusionPropVec.push_back( aProp ); + aProp.Name = "MetalType"; + aProp.Value <<= css::drawing::EnhancedCustomShapeMetalType::MetalMSCompatible; + aExtrusionPropVec.push_back(aProp); + + // "ShadeMode" + if ( IsProperty( DFF_Prop_c3DRenderMode ) ) + { + sal_uInt32 nExtrusionRenderMode = GetPropertyValue( DFF_Prop_c3DRenderMode, 0 ); + css::drawing::ShadeMode eExtrusionShadeMode( css::drawing::ShadeMode_FLAT ); + if ( nExtrusionRenderMode == mso_Wireframe ) + eExtrusionShadeMode = css::drawing::ShadeMode_DRAFT; + + aProp.Name = "ShadeMode"; + aProp.Value <<= eExtrusionShadeMode; + aExtrusionPropVec.push_back( aProp ); + } + // "RotateAngle" in Degree + if ( IsProperty( DFF_Prop_c3DXRotationAngle ) || IsProperty( DFF_Prop_c3DYRotationAngle ) ) + { + double fAngleX = static_cast(static_cast(GetPropertyValue( DFF_Prop_c3DXRotationAngle, 0 ))) / 65536.0; + double fAngleY = static_cast(static_cast(GetPropertyValue( DFF_Prop_c3DYRotationAngle, 0 ))) / 65536.0; + EnhancedCustomShapeParameterPair aRotateAnglePair; + aRotateAnglePair.First.Value <<= fAngleX; + aRotateAnglePair.First.Type = EnhancedCustomShapeParameterType::NORMAL; + aRotateAnglePair.Second.Value <<= fAngleY; + aRotateAnglePair.Second.Type = EnhancedCustomShapeParameterType::NORMAL; + aProp.Name = "RotateAngle"; + aProp.Value <<= aRotateAnglePair; + aExtrusionPropVec.push_back( aProp ); + } + + // "AutoRotationCenter" + if ( ( GetPropertyValue( DFF_Prop_fc3DFillHarsh, 0 ) & 8 ) == 0 ) + { + // "RotationCenter" + if ( IsProperty( DFF_Prop_c3DRotationCenterX ) || IsProperty( DFF_Prop_c3DRotationCenterY ) || IsProperty( DFF_Prop_c3DRotationCenterZ ) ) + { + // tdf#145904 X- and Y-component is fraction, Z-component in EMU + css::drawing::Direction3D aRotationCenter( + static_cast(static_cast(GetPropertyValue( DFF_Prop_c3DRotationCenterX, 0 ))) / 65536.0, + static_cast(static_cast(GetPropertyValue( DFF_Prop_c3DRotationCenterY, 0 ))) / 65536.0, + static_cast(static_cast(GetPropertyValue( DFF_Prop_c3DRotationCenterZ, 0 ))) / 360.0 ); + + aProp.Name = "RotationCenter"; + aProp.Value <<= aRotationCenter; + aExtrusionPropVec.push_back( aProp ); + } + } + // "Shininess" + // MS Office default 5, ODF default 50%. + if ( IsProperty( DFF_Prop_c3DShininess ) ) + { + double fShininess = static_cast(GetPropertyValue( DFF_Prop_c3DShininess, 0 )); + fShininess *= 10.0; // error in [MS ODRAW] (2021), type is not FixedPoint but long. + aProp.Name = "Shininess"; + aProp.Value <<= fShininess; + aExtrusionPropVec.push_back( aProp ); + } + + // "Skew" + // MS Office angle file value is 16.16 FixedPoint, default 0xFF790000, + // -8847360/65536=-135, ODF default 45. Thus must set value even if default. + double fSkewAngle = -135.0; + // MS Office amount file value is signed integer in range 0xFFFFFF9C to 0x00000064, + // default 0x00000032, ODF default 50.0 + double fSkewAmount = 50.0; + if ( IsProperty( DFF_Prop_c3DSkewAmount ) || IsProperty( DFF_Prop_c3DSkewAngle ) ) + { + fSkewAmount = static_cast(GetPropertyValue( DFF_Prop_c3DSkewAmount, 50 )); + fSkewAngle = static_cast(GetPropertyValue( DFF_Prop_c3DSkewAngle, sal::static_int_cast< sal_uInt32 >(-135 * 65536) )); + fSkewAngle /= 65536.0; + } + EnhancedCustomShapeParameterPair aSkewPair; + aSkewPair.First.Value <<= fSkewAmount; + aSkewPair.First.Type = EnhancedCustomShapeParameterType::NORMAL; + aSkewPair.Second.Value <<= fSkewAngle; + aSkewPair.Second.Type = EnhancedCustomShapeParameterType::NORMAL; + aProp.Name = "Skew"; + aProp.Value <<= aSkewPair; + aExtrusionPropVec.push_back( aProp ); + + // "Specularity" + // Type Fixed point 16.16, percent in API + if ( IsProperty( DFF_Prop_c3DSpecularAmt ) ) + { + double fSpecularity = static_cast(GetPropertyValue( DFF_Prop_c3DSpecularAmt, 0 )); + fSpecularity /= 655.36; + aProp.Name = "Specularity"; + aProp.Value <<= fSpecularity; + aExtrusionPropVec.push_back( aProp ); + } + // "ProjectionMode" + ProjectionMode eProjectionMode = (GetPropertyValue( DFF_Prop_fc3DFillHarsh, 0 ) & 4) ? ProjectionMode_PARALLEL : ProjectionMode_PERSPECTIVE; + aProp.Name = "ProjectionMode"; + aProp.Value <<= eProjectionMode; + aExtrusionPropVec.push_back( aProp ); + + // "ViewPoint" in 1/100mm + // MS Office default 1250000 EMU=3472.222 Hmm, ODF default 3.5cm + // Thus must set value even if default. + double fViewX = 1250000.0 / 360.0; + double fViewY = -1250000.0 / 360.0;; + double fViewZ = 9000000.0 / 360.0; + if ( IsProperty( DFF_Prop_c3DXViewpoint ) || IsProperty( DFF_Prop_c3DYViewpoint ) || IsProperty( DFF_Prop_c3DZViewpoint ) ) + { + fViewX = static_cast(static_cast(GetPropertyValue( DFF_Prop_c3DXViewpoint, 1250000 ))) / 360.0; + fViewY = static_cast(static_cast(GetPropertyValue( DFF_Prop_c3DYViewpoint, sal_uInt32(-1250000) )))/ 360.0; + fViewZ = static_cast(static_cast(GetPropertyValue( DFF_Prop_c3DZViewpoint, 9000000 ))) / 360.0; + } + css::drawing::Position3D aExtrusionViewPoint( fViewX, fViewY, fViewZ ); + aProp.Name = "ViewPoint"; + aProp.Value <<= aExtrusionViewPoint; + aExtrusionPropVec.push_back( aProp ); + + // "Origin" + if ( IsProperty( DFF_Prop_c3DOriginX ) || IsProperty( DFF_Prop_c3DOriginY ) ) + { + double fOriginX = static_cast(static_cast(GetPropertyValue( DFF_Prop_c3DOriginX, 32768 ))); + double fOriginY = static_cast(static_cast(GetPropertyValue( DFF_Prop_c3DOriginY, sal_uInt32(-32768) ))); + fOriginX /= 65536; + fOriginY /= 65536; + EnhancedCustomShapeParameterPair aOriginPair; + aOriginPair.First.Value <<= fOriginX; + aOriginPair.First.Type = EnhancedCustomShapeParameterType::NORMAL; + aOriginPair.Second.Value <<= fOriginY; + aOriginPair.Second.Type = EnhancedCustomShapeParameterType::NORMAL; + aProp.Name = "Origin"; + aProp.Value <<= aOriginPair; + aExtrusionPropVec.push_back( aProp ); + } + // "ExtrusionColor" + bool bExtrusionColor = IsProperty( DFF_Prop_c3DExtrusionColor ); // ( GetPropertyValue( DFF_Prop_fc3DLightFace ) & 2 ) != 0; + aProp.Name = "Color"; + aProp.Value <<= bExtrusionColor; + aExtrusionPropVec.push_back( aProp ); + if ( IsProperty( DFF_Prop_c3DExtrusionColor ) ) + rSet.Put( XSecondaryFillColorItem( OUString(), rManager.MSO_CLR_ToColor( + GetPropertyValue( DFF_Prop_c3DExtrusionColor, 0 ), DFF_Prop_c3DExtrusionColor ) ) ); + // pushing the whole Extrusion element + aProp.Name = "Extrusion"; + aProp.Value <<= comphelper::containerToSequence(aExtrusionPropVec); + aPropVec.push_back( aProp ); + } + + + // "Equations" PropertySequence element + + if ( IsProperty( DFF_Prop_pFormulas ) ) + { + sal_uInt16 nNumElem = 0; + + if ( SeekToContent( DFF_Prop_pFormulas, rIn ) ) + { + sal_uInt16 nNumElemMem = 0; + sal_uInt16 nElemSize = 8; + rIn.ReadUInt16( nNumElem ).ReadUInt16( nNumElemMem ).ReadUInt16( nElemSize ); + } + if ( nNumElem <= 128 ) + { + uno::Sequence< OUString > aEquations( nNumElem ); + for ( auto& rEquation : asNonConstRange(aEquations) ) + { + sal_Int16 nP1(0), nP2(0), nP3(0); + sal_uInt16 nFlags(0); + rIn.ReadUInt16( nFlags ).ReadInt16( nP1 ).ReadInt16( nP2 ).ReadInt16( nP3 ); + rEquation = EnhancedCustomShape2d::GetEquation( nFlags, nP1, nP2, nP3 ); + } + // pushing the whole Equations element + aProp.Name = "Equations"; + aProp.Value <<= aEquations; + aPropVec.push_back( aProp ); + } + } + + + // "Handles" PropertySequence element + + if ( IsProperty( DFF_Prop_Handles ) ) + { + sal_uInt16 nNumElem = 0; + sal_uInt16 nElemSize = 36; + + if ( SeekToContent( DFF_Prop_Handles, rIn ) ) + { + sal_uInt16 nNumElemMem = 0; + rIn.ReadUInt16( nNumElem ).ReadUInt16( nNumElemMem ).ReadUInt16( nElemSize ); + } + bool bImport = false; + if (nElemSize == 36) + { + //sanity check that the stream is long enough to fulfill nNumElem * nElemSize; + bImport = rIn.remainingSize() / nElemSize >= nNumElem; + } + if (bImport) + { + uno::Sequence< beans::PropertyValues > aHandles( nNumElem ); + auto aHandlesRange = asNonConstRange(aHandles); + for (sal_uInt32 i = 0; i < nNumElem; ++i) + { + PropVec aHandlePropVec; + sal_uInt32 nFlagsTmp(0); + sal_Int32 nPositionX(0), nPositionY(0), nCenterX(0), nCenterY(0), nRangeXMin(0), nRangeXMax(0), nRangeYMin(0), nRangeYMax(0); + rIn.ReadUInt32( nFlagsTmp ) + .ReadInt32( nPositionX ) + .ReadInt32( nPositionY ) + .ReadInt32( nCenterX ) + .ReadInt32( nCenterY ) + .ReadInt32( nRangeXMin ) + .ReadInt32( nRangeXMax ) + .ReadInt32( nRangeYMin ) + .ReadInt32( nRangeYMax ); + SvxMSDffHandleFlags nFlags = static_cast(nFlagsTmp); + if ( nPositionX == 2 ) // replacing center position with absolute value + nPositionX = nCoordWidth / 2; + if ( nPositionY == 2 ) + nPositionY = nCoordHeight / 2; + EnhancedCustomShapeParameterPair aPosition; + EnhancedCustomShape2d::SetEnhancedCustomShapeHandleParameter( aPosition.First, nPositionX, true, true ); + EnhancedCustomShape2d::SetEnhancedCustomShapeHandleParameter( aPosition.Second, nPositionY, true, false ); + aProp.Name = "Position"; + aProp.Value <<= aPosition; + aHandlePropVec.push_back( aProp ); + + if ( nFlags & SvxMSDffHandleFlags::MIRRORED_X ) + { + aProp.Name = "MirroredX"; + aProp.Value <<= true; + aHandlePropVec.push_back( aProp ); + } + if ( nFlags & SvxMSDffHandleFlags::MIRRORED_Y ) + { + aProp.Name = "MirroredY"; + aProp.Value <<= true; + aHandlePropVec.push_back( aProp ); + } + if ( nFlags & SvxMSDffHandleFlags::SWITCHED ) + { + aProp.Name = "Switched"; + aProp.Value <<= true; + aHandlePropVec.push_back( aProp ); + } + if ( nFlags & SvxMSDffHandleFlags::POLAR ) + { + if ( nCenterX == 2 ) + nCenterX = nCoordWidth / 2; + if ( nCenterY == 2 ) + nCenterY = nCoordHeight / 2; + if ((nPositionY >= 0x256 || nPositionY <= 0x107) && i < sizeof(sal_uInt32) * 8) // position y + nAdjustmentsWhichNeedsToBeConverted |= ( 1U << i ); + EnhancedCustomShapeParameterPair aPolar; + EnhancedCustomShape2d::SetEnhancedCustomShapeHandleParameter( aPolar.First, nCenterX, bool( nFlags & SvxMSDffHandleFlags::CENTER_X_IS_SPECIAL ), true ); + EnhancedCustomShape2d::SetEnhancedCustomShapeHandleParameter( aPolar.Second, nCenterY, bool( nFlags & SvxMSDffHandleFlags::CENTER_Y_IS_SPECIAL ), false ); + aProp.Name = "Polar"; + aProp.Value <<= aPolar; + aHandlePropVec.push_back( aProp ); + } + if ( nFlags & SvxMSDffHandleFlags::MAP ) + { + if ( nCenterX == 2 ) + nCenterX = nCoordWidth / 2; + if ( nCenterY == 2 ) + nCenterY = nCoordHeight / 2; + EnhancedCustomShapeParameterPair aMap; + EnhancedCustomShape2d::SetEnhancedCustomShapeHandleParameter( aMap.First, nCenterX, bool( nFlags & SvxMSDffHandleFlags::CENTER_X_IS_SPECIAL ), true ); + EnhancedCustomShape2d::SetEnhancedCustomShapeHandleParameter( aMap.Second, nCenterY, bool( nFlags & SvxMSDffHandleFlags::CENTER_Y_IS_SPECIAL ), false ); + aProp.Name = "Map"; + aProp.Value <<= aMap; + aHandlePropVec.push_back( aProp ); + } + if ( nFlags & SvxMSDffHandleFlags::RANGE ) + { + if ( static_cast(nRangeXMin) != 0x80000000 ) + { + if ( nRangeXMin == 2 ) + nRangeXMin = nCoordWidth / 2; + EnhancedCustomShapeParameter aRangeXMinimum; + EnhancedCustomShape2d::SetEnhancedCustomShapeHandleParameter( aRangeXMinimum, nRangeXMin, + bool( nFlags & SvxMSDffHandleFlags::RANGE_X_MIN_IS_SPECIAL ), true ); + aProp.Name = "RangeXMinimum"; + aProp.Value <<= aRangeXMinimum; + aHandlePropVec.push_back( aProp ); + } + if ( static_cast(nRangeXMax) != 0x7fffffff ) + { + if ( nRangeXMax == 2 ) + nRangeXMax = nCoordWidth / 2; + EnhancedCustomShapeParameter aRangeXMaximum; + EnhancedCustomShape2d::SetEnhancedCustomShapeHandleParameter( aRangeXMaximum, nRangeXMax, + bool( nFlags & SvxMSDffHandleFlags::RANGE_X_MAX_IS_SPECIAL ), false ); + aProp.Name = "RangeXMaximum"; + aProp.Value <<= aRangeXMaximum; + aHandlePropVec.push_back( aProp ); + } + if ( static_cast(nRangeYMin) != 0x80000000 ) + { + if ( nRangeYMin == 2 ) + nRangeYMin = nCoordHeight / 2; + EnhancedCustomShapeParameter aRangeYMinimum; + EnhancedCustomShape2d::SetEnhancedCustomShapeHandleParameter( aRangeYMinimum, nRangeYMin, + bool( nFlags & SvxMSDffHandleFlags::RANGE_Y_MIN_IS_SPECIAL ), true ); + aProp.Name = "RangeYMinimum"; + aProp.Value <<= aRangeYMinimum; + aHandlePropVec.push_back( aProp ); + } + if ( static_cast(nRangeYMax) != 0x7fffffff ) + { + if ( nRangeYMax == 2 ) + nRangeYMax = nCoordHeight / 2; + EnhancedCustomShapeParameter aRangeYMaximum; + EnhancedCustomShape2d::SetEnhancedCustomShapeHandleParameter( aRangeYMaximum, nRangeYMax, + bool( nFlags & SvxMSDffHandleFlags::RANGE_Y_MAX_IS_SPECIAL ), false ); + aProp.Name = "RangeYMaximum"; + aProp.Value <<= aRangeYMaximum; + aHandlePropVec.push_back( aProp ); + } + } + if ( nFlags & SvxMSDffHandleFlags::RADIUS_RANGE ) + { + if ( static_cast(nRangeXMin) != 0x7fffffff ) + { + if ( nRangeXMin == 2 ) + nRangeXMin = nCoordWidth / 2; + EnhancedCustomShapeParameter aRadiusRangeMinimum; + EnhancedCustomShape2d::SetEnhancedCustomShapeHandleParameter( aRadiusRangeMinimum, nRangeXMin, + bool( nFlags & SvxMSDffHandleFlags::RANGE_X_MIN_IS_SPECIAL ), true ); + aProp.Name = "RadiusRangeMinimum"; + aProp.Value <<= aRadiusRangeMinimum; + aHandlePropVec.push_back( aProp ); + } + if ( static_cast(nRangeXMax) != 0x80000000 ) + { + if ( nRangeXMax == 2 ) + nRangeXMax = nCoordWidth / 2; + EnhancedCustomShapeParameter aRadiusRangeMaximum; + EnhancedCustomShape2d::SetEnhancedCustomShapeHandleParameter( aRadiusRangeMaximum, nRangeXMax, + bool( nFlags & SvxMSDffHandleFlags::RANGE_X_MAX_IS_SPECIAL ), false ); + aProp.Name = "RadiusRangeMaximum"; + aProp.Value <<= aRadiusRangeMaximum; + aHandlePropVec.push_back( aProp ); + } + } + if ( !aHandlePropVec.empty() ) + { + aHandlesRange[ i ] = comphelper::containerToSequence(aHandlePropVec); + } + } + // pushing the whole Handles element + aProp.Name = "Handles"; + aProp.Value <<= aHandles; + aPropVec.push_back( aProp ); + } + } + else + { + const mso_CustomShape* pDefCustomShape = GetCustomShapeContent( rObjData.eShapeType ); + if ( pDefCustomShape && pDefCustomShape->nHandles && pDefCustomShape->pHandles ) + { + sal_uInt32 i, nCnt = pDefCustomShape->nHandles; + const SvxMSDffHandle* pData = pDefCustomShape->pHandles; + for ( i = 0; i < nCnt; i++, pData++ ) + { + if ( pData->nFlags & SvxMSDffHandleFlags::POLAR ) + { + if ( ( pData->nPositionY >= 0x256 ) || ( pData->nPositionY <= 0x107 ) ) + nAdjustmentsWhichNeedsToBeConverted |= ( 1U << i ); + } + } + } + } + + // "Path" PropertySequence element + + { + PropVec aPathPropVec; + + // "Path/ExtrusionAllowed" + if ( IsHardAttribute( DFF_Prop_f3DOK ) ) + { + bool bExtrusionAllowed = ( GetPropertyValue( DFF_Prop_fFillOK, 0 ) & 16 ) != 0; + aProp.Name = "ExtrusionAllowed"; + aProp.Value <<= bExtrusionAllowed; + aPathPropVec.push_back( aProp ); + } + // "Path/ConcentricGradientFillAllowed" + if ( IsHardAttribute( DFF_Prop_fFillShadeShapeOK ) ) + { + bool bConcentricGradientFillAllowed = ( GetPropertyValue( DFF_Prop_fFillOK, 0 ) & 2 ) != 0; + aProp.Name = "ConcentricGradientFillAllowed"; + aProp.Value <<= bConcentricGradientFillAllowed; + aPathPropVec.push_back( aProp ); + } + // "Path/TextPathAllowed" + if ( IsHardAttribute( DFF_Prop_fGtextOK ) || ( GetPropertyValue( DFF_Prop_gtextFStrikethrough, 0 ) & 0x4000 ) ) + { + bool bTextPathAllowed = ( GetPropertyValue( DFF_Prop_fFillOK, 0 ) & 4 ) != 0; + aProp.Name = "TextPathAllowed"; + aProp.Value <<= bTextPathAllowed; + aPathPropVec.push_back( aProp ); + } + // Path/Coordinates + if ( IsProperty( DFF_Prop_pVertices ) ) + { + css::uno::Sequence< css::drawing::EnhancedCustomShapeParameterPair > aCoordinates; + sal_uInt16 nNumElemVert = 0; + sal_uInt16 nElemSizeVert = 8; + + if ( SeekToContent( DFF_Prop_pVertices, rIn ) ) + { + sal_uInt16 nNumElemMemVert = 0; + rIn.ReadUInt16( nNumElemVert ).ReadUInt16( nNumElemMemVert ).ReadUInt16( nElemSizeVert ); + // If this value is 0xFFF0 then this record is an array of truncated 8 byte elements. Only the 4 + // low-order bytes are recorded + if (nElemSizeVert == 0xFFF0) + nElemSizeVert = 4; + } + //sanity check that the stream is long enough to fulfill nNumElem * nElemSize; + bool bImport = nElemSizeVert && (rIn.remainingSize() / nElemSizeVert >= nNumElemVert); + if (bImport) + { + aCoordinates.realloc( nNumElemVert ); + for (auto& rCoordinate : asNonConstRange(aCoordinates)) + { + sal_Int32 nX(0), nY(0); + + if ( nElemSizeVert == 8 ) + { + rIn.ReadInt32( nX ) + .ReadInt32( nY ); + } + else + { + // The mso-spt19 (arc) uses this. But it needs unsigned integer. I don't + // know if other shape types also need it. They can be added as necessary. + bool bNeedsUnsigned = rObjData.eShapeType == mso_sptArc; + if (bNeedsUnsigned) + { + sal_uInt16 nTmpA(0), nTmpB(0); + rIn.ReadUInt16(nTmpA) + .ReadUInt16(nTmpB); + nX = nTmpA; + nY = nTmpB; + } + else + { + sal_Int16 nTmpA(0), nTmpB(0); + rIn.ReadInt16( nTmpA ) + .ReadInt16( nTmpB ); + nX = nTmpA; + nY = nTmpB; + } + } + EnhancedCustomShape2d::SetEnhancedCustomShapeParameter( rCoordinate.First, nX ); + EnhancedCustomShape2d::SetEnhancedCustomShapeParameter( rCoordinate.Second, nY ); + } + } + aProp.Name = "Coordinates"; + aProp.Value <<= aCoordinates; + aPathPropVec.push_back( aProp ); + } + // Path/Segments + if ( IsProperty( DFF_Prop_pSegmentInfo ) ) + { + css::uno::Sequence< css::drawing::EnhancedCustomShapeSegment > aSegments; + + sal_uInt16 nNumElemSeg = 0; + + if ( SeekToContent( DFF_Prop_pSegmentInfo, rIn ) ) + { + sal_uInt16 nNumElemMemSeg = 0; + sal_uInt16 nElemSizeSeg = 2; + rIn.ReadUInt16( nNumElemSeg ).ReadUInt16( nNumElemMemSeg ).ReadUInt16( nElemSizeSeg ); + } + sal_uInt64 nMaxEntriesPossible = rIn.remainingSize() / sizeof(sal_uInt16); + if (nNumElemSeg > nMaxEntriesPossible) + { + SAL_WARN("filter.ms", "NumElem list is longer than remaining bytes, ppt or parser is wrong"); + nNumElemSeg = nMaxEntriesPossible; + } + if ( nNumElemSeg ) + { + aSegments.realloc( nNumElemSeg ); + for (auto& rSegment : asNonConstRange(aSegments)) + { + sal_uInt16 nTmp(0); + rIn.ReadUInt16( nTmp ); + sal_Int16 nCommand = EnhancedCustomShapeSegmentCommand::UNKNOWN; + sal_Int16 nCnt = static_cast( nTmp & 0x1fff );//Last 13 bits for segment points number + switch( nTmp >> 13 )//First 3 bits for command type + { + case 0x0: + nCommand = EnhancedCustomShapeSegmentCommand::LINETO; + if ( !nCnt ) nCnt = 1; + break; + case 0x1: + nCommand = EnhancedCustomShapeSegmentCommand::CURVETO; + if ( !nCnt ) nCnt = 1; + break; + case 0x2: + nCommand = EnhancedCustomShapeSegmentCommand::MOVETO; + if ( !nCnt ) nCnt = 1; + break; + case 0x3: + nCommand = EnhancedCustomShapeSegmentCommand::CLOSESUBPATH; + nCnt = 0; + break; + case 0x4: + nCommand = EnhancedCustomShapeSegmentCommand::ENDSUBPATH; + nCnt = 0; + break; + case 0x5: + case 0x6: + { + switch ( ( nTmp >> 8 ) & 0x1f )//5 bits next to command type is for path escape type + { + case 0x0: + { + //It is msopathEscapeExtension which is transformed into LINETO. + //If issue happens, I think this part can be comment so that it will be taken as unknown command. + //When export, origin data will be export without any change. + nCommand = EnhancedCustomShapeSegmentCommand::LINETO; + if ( !nCnt ) + nCnt = 1; + } + break; + case 0x1: + { + nCommand = EnhancedCustomShapeSegmentCommand::ANGLEELLIPSETO; + nCnt = ( nTmp & 0xff ) / 3; + } + break; + case 0x2: + { + nCommand = EnhancedCustomShapeSegmentCommand::ANGLEELLIPSE; + nCnt = ( nTmp & 0xff ) / 3; + } + break; + case 0x3: + { + nCommand = EnhancedCustomShapeSegmentCommand::ARCTO; + nCnt = ( nTmp & 0xff ) >> 2; + }; + break; + case 0x4: + { + nCommand = EnhancedCustomShapeSegmentCommand::ARC; + nCnt = ( nTmp & 0xff ) >> 2; + } + break; + case 0x5: + { + nCommand = EnhancedCustomShapeSegmentCommand::CLOCKWISEARCTO; + nCnt = ( nTmp & 0xff ) >> 2; + } + break; + case 0x6: + { + nCommand = EnhancedCustomShapeSegmentCommand::CLOCKWISEARC; + nCnt = ( nTmp & 0xff ) >> 2; + } + break; + case 0x7: + { + nCommand = EnhancedCustomShapeSegmentCommand::ELLIPTICALQUADRANTX; + nCnt = nTmp & 0xff; + } + break; + case 0x8: + { + nCommand = EnhancedCustomShapeSegmentCommand::ELLIPTICALQUADRANTY; + nCnt = nTmp & 0xff; + } + break; + case 0xa: nCommand = EnhancedCustomShapeSegmentCommand::NOFILL; nCnt = 0; break; + case 0xb: nCommand = EnhancedCustomShapeSegmentCommand::NOSTROKE; nCnt = 0; break; + } + } + break; + } + // if the command is unknown, we will store all the data in nCnt, so it will be possible to export without loss + if ( nCommand == EnhancedCustomShapeSegmentCommand::UNKNOWN ) + nCnt = static_cast(nTmp); + rSegment.Command = nCommand; + rSegment.Count = nCnt; + } + } + aProp.Name = "Segments"; + aProp.Value <<= aSegments; + aPathPropVec.push_back( aProp ); + } + // Path/StretchX + if ( IsProperty( DFF_Prop_stretchPointX ) ) + { + sal_Int32 nStretchX = GetPropertyValue( DFF_Prop_stretchPointX, 0 ); + aProp.Name = "StretchX"; + aProp.Value <<= nStretchX; + aPathPropVec.push_back( aProp ); + } + // Path/StretchX + if ( IsProperty( DFF_Prop_stretchPointY ) ) + { + sal_Int32 nStretchY = GetPropertyValue( DFF_Prop_stretchPointY, 0 ); + aProp.Name = "StretchY"; + aProp.Value <<= nStretchY; + aPathPropVec.push_back( aProp ); + } + // Path/TextFrames + if ( IsProperty( DFF_Prop_textRectangles ) ) + { + sal_uInt16 nNumElem = 0; + sal_uInt16 nElemSize = 16; + + if ( SeekToContent( DFF_Prop_textRectangles, rIn ) ) + { + sal_uInt16 nNumElemMem = 0; + rIn.ReadUInt16( nNumElem ).ReadUInt16( nNumElemMem ).ReadUInt16( nElemSize ); + } + bool bImport = false; + if (nElemSize == 16) + { + //sanity check that the stream is long enough to fulfill nNumElem * nElemSize; + bImport = rIn.remainingSize() / nElemSize >= nNumElem; + } + if (bImport) + { + css::uno::Sequence< css::drawing::EnhancedCustomShapeTextFrame > aTextFrames( nNumElem ); + for (auto& rTextFrame : asNonConstRange(aTextFrames)) + { + sal_Int32 nLeft(0), nTop(0), nRight(0), nBottom(0); + + rIn.ReadInt32( nLeft ) + .ReadInt32( nTop ) + .ReadInt32( nRight ) + .ReadInt32( nBottom ); + + EnhancedCustomShape2d::SetEnhancedCustomShapeParameter( rTextFrame.TopLeft.First, nLeft ); + EnhancedCustomShape2d::SetEnhancedCustomShapeParameter( rTextFrame.TopLeft.Second, nTop ); + EnhancedCustomShape2d::SetEnhancedCustomShapeParameter( rTextFrame.BottomRight.First, nRight ); + EnhancedCustomShape2d::SetEnhancedCustomShapeParameter( rTextFrame.BottomRight.Second, nBottom); + } + aProp.Name = "TextFrames"; + aProp.Value <<= aTextFrames; + aPathPropVec.push_back( aProp ); + } + } + //Path/GluePoints + if ( IsProperty( DFF_Prop_connectorPoints ) ) + { + css::uno::Sequence< css::drawing::EnhancedCustomShapeParameterPair > aGluePoints; + sal_uInt16 nNumElemVert = 0; + sal_uInt16 nElemSizeVert = 8; + + if ( SeekToContent( DFF_Prop_connectorPoints, rIn ) ) + { + sal_uInt16 nNumElemMemVert = 0; + rIn.ReadUInt16( nNumElemVert ).ReadUInt16( nNumElemMemVert ).ReadUInt16( nElemSizeVert ); + // If this value is 0xFFF0 then this record is an array of truncated 8 byte elements. Only the 4 + // low-order bytes are recorded + if (nElemSizeVert == 0xFFF0) + nElemSizeVert = 4; + } + + // sanity check that the stream is long enough to fulfill nNumElemVert * nElemSizeVert; + bool bImport = nElemSizeVert && (rIn.remainingSize() / nElemSizeVert >= nNumElemVert); + if (bImport) + { + aGluePoints.realloc( nNumElemVert ); + for (auto& rGluePoint : asNonConstRange(aGluePoints)) + { + sal_Int32 nX(0), nY(0); + if ( nElemSizeVert == 8 ) + { + rIn.ReadInt32( nX ) + .ReadInt32( nY ); + } + else + { + sal_Int16 nTmpA(0), nTmpB(0); + + rIn.ReadInt16( nTmpA ) + .ReadInt16( nTmpB ); + + nX = nTmpA; + nY = nTmpB; + } + EnhancedCustomShape2d::SetEnhancedCustomShapeParameter( rGluePoint.First, nX ); + EnhancedCustomShape2d::SetEnhancedCustomShapeParameter( rGluePoint.Second, nY ); + } + } + aProp.Name = "GluePoints"; + aProp.Value <<= aGluePoints; + aPathPropVec.push_back( aProp ); + } + if ( IsProperty( DFF_Prop_connectorType ) ) + { + sal_Int16 nGluePointType = static_cast(GetPropertyValue( DFF_Prop_connectorType, 0 )); + aProp.Name = "GluePointType"; + aProp.Value <<= nGluePointType; + aPathPropVec.push_back( aProp ); + } + // pushing the whole Path element + if ( !aPathPropVec.empty() ) + { + aProp.Name = "Path"; + aProp.Value <<= comphelper::containerToSequence(aPathPropVec); + aPropVec.push_back( aProp ); + } + } + + // "TextPath" PropertySequence element + + bool bTextPathOn = ( GetPropertyValue( DFF_Prop_gtextFStrikethrough, 0 ) & 0x4000 ) != 0; + if ( bTextPathOn ) + { + PropVec aTextPathPropVec; + + // TextPath + aProp.Name = "TextPath"; + aProp.Value <<= bTextPathOn; + aTextPathPropVec.push_back( aProp ); + + // TextPathMode + bool bTextPathFitPath = ( GetPropertyValue( DFF_Prop_gtextFStrikethrough, 0 ) & 0x100 ) != 0; + + bool bTextPathFitShape; + if ( IsHardAttribute( DFF_Prop_gtextFStretch ) ) + bTextPathFitShape = ( GetPropertyValue( DFF_Prop_gtextFStrikethrough, 0 ) & 0x400 ) != 0; + else + { + bTextPathFitShape = true; + switch( rObjData.eShapeType ) + { + case mso_sptTextArchUpCurve : + case mso_sptTextArchDownCurve : + case mso_sptTextCircleCurve : + case mso_sptTextButtonCurve : + bTextPathFitShape = false; + break; + default : break; + } + } + EnhancedCustomShapeTextPathMode eTextPathMode( EnhancedCustomShapeTextPathMode_NORMAL ); + if ( bTextPathFitShape ) + eTextPathMode = EnhancedCustomShapeTextPathMode_SHAPE; + else if ( bTextPathFitPath ) + eTextPathMode = EnhancedCustomShapeTextPathMode_PATH; + aProp.Name = "TextPathMode"; + aProp.Value <<= eTextPathMode; + aTextPathPropVec.push_back( aProp ); + + // ScaleX + bool bTextPathScaleX = ( GetPropertyValue( DFF_Prop_gtextFStrikethrough, 0 ) & 0x40 ) != 0; + aProp.Name = "ScaleX"; + aProp.Value <<= bTextPathScaleX; + aTextPathPropVec.push_back( aProp ); + // SameLetterHeights + bool bSameLetterHeight = ( GetPropertyValue( DFF_Prop_gtextFStrikethrough, 0 ) & 0x80 ) != 0; + aProp.Name = "SameLetterHeights"; + aProp.Value <<= bSameLetterHeight; + aTextPathPropVec.push_back( aProp ); + + // pushing the whole TextPath element + aProp.Name = "TextPath"; + aProp.Value <<= comphelper::containerToSequence(aTextPathPropVec); + aPropVec.push_back( aProp ); + } + + // "AdjustmentValues" // The AdjustmentValues are imported at last, because depending to the type of the + //////////////////////// handle (POLAR) we will convert the adjustment value from a fixed float to double + + // checking the last used adjustment handle, so we can determine how many handles are to allocate + sal_uInt32 i = DFF_Prop_adjust10Value; + while ( ( i >= DFF_Prop_adjustValue ) && !IsProperty( i ) ) + i--; + sal_Int32 nAdjustmentValues = ( i - DFF_Prop_adjustValue ) + 1; + if ( nAdjustmentValues ) + { + uno::Sequence< css::drawing::EnhancedCustomShapeAdjustmentValue > aAdjustmentSeq( nAdjustmentValues ); + auto pAdjustmentSeq = aAdjustmentSeq.getArray(); + while( --nAdjustmentValues >= 0 ) + { + sal_Int32 nValue = 0; + beans::PropertyState ePropertyState = beans::PropertyState_DEFAULT_VALUE; + if ( IsProperty( i ) ) + { + nValue = GetPropertyValue( i, 0 ); + ePropertyState = beans::PropertyState_DIRECT_VALUE; + } + if ( nAdjustmentsWhichNeedsToBeConverted & ( 1 << ( i - DFF_Prop_adjustValue ) ) ) + { + double fValue = nValue; + fValue /= 65536; + pAdjustmentSeq[ nAdjustmentValues ].Value <<= fValue; + } + else + pAdjustmentSeq[ nAdjustmentValues ].Value <<= nValue; + pAdjustmentSeq[ nAdjustmentValues ].State = ePropertyState; + i--; + } + aProp.Name = "AdjustmentValues"; + aProp.Value <<= aAdjustmentSeq; + aPropVec.push_back( aProp ); + } + + // creating the whole property set + rSet.Put( SdrCustomShapeGeometryItem( comphelper::containerToSequence(aPropVec) ) ); +} + +void DffPropertyReader::ApplyAttributes( SvStream& rIn, SfxItemSet& rSet ) const +{ + DffRecordHeader aHdTemp; + DffObjData aDffObjTemp( aHdTemp, tools::Rectangle(), 0 ); + ApplyAttributes( rIn, rSet, aDffObjTemp ); +} + +void DffPropertyReader::ApplyAttributes( SvStream& rIn, SfxItemSet& rSet, DffObjData const & rObjData ) const +{ + bool bHasShadow = false; + bool bNonZeroShadowOffset = false; + + if ( IsProperty( DFF_Prop_gtextSize ) ) + rSet.Put( SvxFontHeightItem( rManager.ScalePt( GetPropertyValue( DFF_Prop_gtextSize, 0 ) ), 100, EE_CHAR_FONTHEIGHT ) ); + sal_uInt32 nFontAttributes = GetPropertyValue( DFF_Prop_gtextFStrikethrough, 0 ); + if ( nFontAttributes & 0x20 ) + rSet.Put( SvxWeightItem( (nFontAttributes & 0x20) ? WEIGHT_BOLD : WEIGHT_NORMAL, EE_CHAR_WEIGHT ) ); + if ( nFontAttributes & 0x10 ) + rSet.Put( SvxPostureItem( (nFontAttributes & 0x10) ? ITALIC_NORMAL : ITALIC_NONE, EE_CHAR_ITALIC ) ); + if ( nFontAttributes & 0x08 ) + rSet.Put( SvxUnderlineItem( (nFontAttributes & 0x08) ? LINESTYLE_SINGLE : LINESTYLE_NONE, EE_CHAR_UNDERLINE ) ); + if ( nFontAttributes & 0x40 ) + rSet.Put( SvxShadowedItem( (nFontAttributes & 0x40) != 0, EE_CHAR_SHADOW ) ); +// if ( nFontAttributes & 0x02 ) +// rSet.Put( SvxCaseMapItem( nFontAttributes & 0x02 ? SvxCaseMap::SmallCaps : SvxCaseMap::NotMapped ) ); + if ( nFontAttributes & 0x01 ) + rSet.Put( SvxCrossedOutItem( (nFontAttributes & 0x01) ? STRIKEOUT_SINGLE : STRIKEOUT_NONE, EE_CHAR_STRIKEOUT ) ); + if ( IsProperty( DFF_Prop_fillColor ) ) + rSet.Put( XFillColorItem( OUString(), rManager.MSO_CLR_ToColor( GetPropertyValue( DFF_Prop_fillColor, 0 ), DFF_Prop_fillColor ) ) ); + if ( IsProperty( DFF_Prop_shadowColor ) ) + rSet.Put( makeSdrShadowColorItem( rManager.MSO_CLR_ToColor( GetPropertyValue( DFF_Prop_shadowColor, 0 ), DFF_Prop_shadowColor ) ) ); + else + { + //The default value for this property is 0x00808080 + rSet.Put( makeSdrShadowColorItem( rManager.MSO_CLR_ToColor( 0x00808080, DFF_Prop_shadowColor ) ) ); + } + if ( IsProperty( DFF_Prop_shadowOpacity ) ) + rSet.Put( makeSdrShadowTransparenceItem( static_cast( ( 0x10000 - GetPropertyValue( DFF_Prop_shadowOpacity, 0 ) ) / 655 ) ) ); + if ( IsProperty( DFF_Prop_shadowOffsetX ) ) + { + sal_Int32 nVal = static_cast< sal_Int32 >( GetPropertyValue( DFF_Prop_shadowOffsetX, 0 ) ); + rManager.ScaleEmu( nVal ); + rSet.Put( makeSdrShadowXDistItem( nVal ) ); + bNonZeroShadowOffset = ( nVal > 0 ); + } + if ( IsProperty( DFF_Prop_shadowOffsetY ) ) + { + sal_Int32 nVal = static_cast< sal_Int32 >( GetPropertyValue( DFF_Prop_shadowOffsetY, 0 ) ); + rManager.ScaleEmu( nVal ); + rSet.Put( makeSdrShadowYDistItem( nVal ) ); + bNonZeroShadowOffset = ( nVal > 0 ); + } + if ( IsProperty( DFF_Prop_fshadowObscured ) ) + { + bHasShadow = ( GetPropertyValue( DFF_Prop_fshadowObscured, 0 ) & 2 ) != 0; + if ( bHasShadow ) + { + if ( !IsProperty( DFF_Prop_shadowOffsetX ) ) + rSet.Put( makeSdrShadowXDistItem( 35 ) ); + if ( !IsProperty( DFF_Prop_shadowOffsetY ) ) + rSet.Put( makeSdrShadowYDistItem( 35 ) ); + } + } + if ( IsProperty( DFF_Prop_shadowType ) ) + { + auto eShadowType = GetPropertyValue(DFF_Prop_shadowType, 0); + if( eShadowType != mso_shadowOffset && !bNonZeroShadowOffset ) + { + //0.12" == 173 twip == 302 100mm + sal_uInt32 nDist = rManager.pSdrModel->GetScaleUnit() == MapUnit::MapTwip ? 173: 302; + rSet.Put( makeSdrShadowXDistItem( nDist ) ); + rSet.Put( makeSdrShadowYDistItem( nDist ) ); + } + } + if ( bHasShadow ) + { + static bool bCheckShadow(false); // loplugin:constvars:ignore + + // #i124477# Found no reason not to set shadow, esp. since it is applied to evtl. existing text + // and will lead to an error if in PPT someone used text and added the object shadow to the + // object carrying that text. I found no cases where this leads to problems (the old bugtracker + // task #160376# from sj is unfortunately no longer available). Keeping the code for now + // to allow easy fallback when this shows problems in the future + if(bCheckShadow) + { + // #160376# sj: activating shadow only if fill and or linestyle is used + // this is required because of the latest drawing layer core changes. + // #i104085# is related to this. + sal_uInt32 nLineFlags(GetPropertyValue( DFF_Prop_fNoLineDrawDash, 0 )); + if(!IsHardAttribute( DFF_Prop_fLine ) && !IsCustomShapeStrokedByDefault( rObjData.eShapeType )) + nLineFlags &= ~0x08; + sal_uInt32 nFillFlags(GetPropertyValue( DFF_Prop_fNoFillHitTest, 0 )); + if(!IsHardAttribute( DFF_Prop_fFilled ) && !IsCustomShapeFilledByDefault( rObjData.eShapeType )) + nFillFlags &= ~0x10; + if ( nFillFlags & 0x10 ) + { + auto eMSO_FillType = GetPropertyValue(DFF_Prop_fillType, mso_fillSolid); + switch( eMSO_FillType ) + { + case mso_fillSolid : + case mso_fillPattern : + case mso_fillTexture : + case mso_fillPicture : + case mso_fillShade : + case mso_fillShadeCenter : + case mso_fillShadeShape : + case mso_fillShadeScale : + case mso_fillShadeTitle : + break; + default: + nFillFlags &=~0x10; // no fillstyle used + break; + } + } + if ( ( ( nLineFlags & 0x08 ) == 0 ) && ( ( nFillFlags & 0x10 ) == 0 ) && ( rObjData.eShapeType != mso_sptPictureFrame )) // if there is no fillstyle and linestyle + bHasShadow = false; // we are turning shadow off. + } + + if ( bHasShadow ) + rSet.Put( makeSdrShadowItem( bHasShadow ) ); + } + ApplyLineAttributes( rSet, rObjData.eShapeType ); // #i28269# + ApplyFillAttributes( rIn, rSet, rObjData ); + if ( rObjData.eShapeType != mso_sptNil || IsProperty( DFF_Prop_pVertices ) ) + { + ApplyCustomShapeGeometryAttributes( rIn, rSet, rObjData ); + ApplyCustomShapeTextAttributes( rSet ); + if ( rManager.GetSvxMSDffSettings() & SVXMSDFF_SETTINGS_IMPORT_EXCEL ) + { + if ( mnFix16Angle || ( rObjData.nSpFlags & ShapeFlag::FlipV ) ) + CheckAndCorrectExcelTextRotation( rIn, rSet, rObjData ); + } + } +} + +void DffPropertyReader::CheckAndCorrectExcelTextRotation( SvStream& rIn, SfxItemSet& rSet, DffObjData const & rObjData ) const +{ + bool bRotateTextWithShape = rObjData.bRotateTextWithShape; + if ( rObjData.bOpt2 ) // sj: #158494# is the second property set available ? if then we have to check the xml data of + { // the shape, because the textrotation of Excel 2003 and greater versions is stored there + // (upright property of the textbox) + if ( rManager.pSecPropSet->SeekToContent( DFF_Prop_metroBlob, rIn ) ) + { + sal_uInt32 nLen = rManager.pSecPropSet->GetPropertyValue( DFF_Prop_metroBlob, 0 ); + if ( nLen ) + { + css::uno::Sequence< sal_Int8 > aXMLDataSeq( nLen ); + rIn.ReadBytes(aXMLDataSeq.getArray(), nLen); + css::uno::Reference< css::io::XInputStream > xInputStream + ( new ::comphelper::SequenceInputStream( aXMLDataSeq ) ); + try + { + css::uno::Reference< css::uno::XComponentContext > xContext( ::comphelper::getProcessComponentContext() ); + css::uno::Reference< css::embed::XStorage > xStorage + ( ::comphelper::OStorageHelper::GetStorageOfFormatFromInputStream( + OFOPXML_STORAGE_FORMAT_STRING, xInputStream, xContext, true ) ); + if ( xStorage.is() ) + { + css::uno::Reference< css::embed::XStorage > + xStorageDRS( xStorage->openStorageElement( "drs", css::embed::ElementModes::SEEKABLEREAD ) ); + if ( xStorageDRS.is() ) + { + css::uno::Reference< css::io::XStream > xShapeXMLStream( xStorageDRS->openStreamElement( "shapexml.xml", css::embed::ElementModes::SEEKABLEREAD ) ); + if ( xShapeXMLStream.is() ) + { + css::uno::Reference< css::io::XInputStream > xShapeXMLInputStream( xShapeXMLStream->getInputStream() ); + if ( xShapeXMLInputStream.is() ) + { + css::uno::Sequence< sal_Int8 > aSeq; + sal_Int32 nBytesRead = xShapeXMLInputStream->readBytes( aSeq, 0x7fffffff ); + if ( nBytesRead ) + { // for only one property I spare to use a XML parser at this point, this + // should be enhanced if needed + + bRotateTextWithShape = true; // using the correct xml default + const char* pArry = reinterpret_cast< char* >( aSeq.getArray() ); + const char* const pUpright = "upright="; + const char* pEnd = pArry + nBytesRead; + const char* pPtr = pArry; + while( ( pPtr + 12 ) < pEnd ) + { + if ( !memcmp( pUpright, pPtr, 8 ) ) + { + bRotateTextWithShape = ( pPtr[ 9 ] != '1' ) && ( pPtr[ 9 ] != 't' ); + break; + } + else + pPtr++; + } + } + } + } + } + } + } + catch( css::uno::Exception& ) + { + } + } + } + } + if ( bRotateTextWithShape ) + return; + + const css::uno::Any* pAny; + SdrCustomShapeGeometryItem aGeometryItem(rSet.Get( SDRATTR_CUSTOMSHAPE_GEOMETRY )); + static const OUStringLiteral sTextRotateAngle( u"TextRotateAngle" ); + pAny = aGeometryItem.GetPropertyValueByName( sTextRotateAngle ); + double fExtraTextRotateAngle = 0.0; + if ( pAny ) + *pAny >>= fExtraTextRotateAngle; + + if ( rManager.mnFix16Angle ) + fExtraTextRotateAngle += toDegrees(mnFix16Angle); + if ( rObjData.nSpFlags & ShapeFlag::FlipV ) + fExtraTextRotateAngle -= 180.0; + + css::beans::PropertyValue aTextRotateAngle; + aTextRotateAngle.Name = sTextRotateAngle; + aTextRotateAngle.Value <<= fExtraTextRotateAngle; + aGeometryItem.SetPropertyValue( aTextRotateAngle ); + rSet.Put( aGeometryItem ); +} + + +void DffPropertyReader::ImportGradientColor( SfxItemSet& aSet, sal_uInt32 eMSO_FillType, double dTrans , double dBackTrans) const +{ + //MS Focus prop will impact the start and end color position. And AOO does not + //support this prop. So need some swap for the two color to keep fidelity with AOO and MS shape. + //So below var is defined. + sal_Int32 nChgColors = 0; + sal_Int32 nAngleFix16 = GetPropertyValue( DFF_Prop_fillAngle, 0 ); + if(nAngleFix16 >= 0) + nChgColors ^= 1; + + //Translate a MS clockwise(+) or count clockwise angle(-) into an AOO count clock wise angle + Degree10 nAngle( 3600_deg10 - to( Fix16ToAngle(nAngleFix16) ) ); + //Make sure this angle belongs to 0~3600 + while ( nAngle >= 3600_deg10 ) nAngle -= 3600_deg10; + while ( nAngle < 0_deg10 ) nAngle += 3600_deg10; + + //Rotate angle + if ( mbRotateGranientFillWithAngle ) + { + sal_Int32 nRotateAngle = GetPropertyValue( DFF_Prop_Rotation, 0 ); + //nAngle is a clockwise angle. If nRotateAngle is a clockwise angle, then gradient needs to be rotated a little less + //or it needs to be rotated a little more + nAngle -= to(Fix16ToAngle(nRotateAngle)); + } + while ( nAngle >= 3600_deg10 ) nAngle -= 3600_deg10; + while ( nAngle < 0_deg10 ) nAngle += 3600_deg10; + + css::awt::GradientStyle eGrad = css::awt::GradientStyle_LINEAR; + + sal_Int32 nFocus = GetPropertyValue( DFF_Prop_fillFocus, 0 ); + if ( !nFocus ) + nChgColors ^= 1; + else if ( nFocus < 0 )//If it is a negative focus, the color will be swapped + { + nFocus = o3tl::saturating_toggle_sign(nFocus); + nChgColors ^= 1; + } + + if( nFocus > 40 && nFocus < 60 ) + { + eGrad = css::awt::GradientStyle_AXIAL;//A axial gradient other than linear + nChgColors ^= 1; + } + //if the type is linear or axial, just save focus to nFocusX and nFocusY for export + //Core function does no need them. They serve for rect gradient(CenterXY). + sal_uInt16 nFocusX = static_cast(nFocus); + sal_uInt16 nFocusY = static_cast(nFocus); + + switch( eMSO_FillType ) + { + case mso_fillShadeShape : + { + eGrad = css::awt::GradientStyle_RECT; + nFocusY = nFocusX = 50; + nChgColors ^= 1; + } + break; + case mso_fillShadeCenter : + { + eGrad = css::awt::GradientStyle_RECT; + //A MS fillTo prop specifies the relative position of the left boundary + //of the center rectangle in a concentric shaded fill. Use 100 or 0 to keep fidelity + nFocusX=(GetPropertyValue( DFF_Prop_fillToRight, 0 )==0x10000) ? 100 : 0; + nFocusY=(GetPropertyValue( DFF_Prop_fillToBottom,0 )==0x10000) ? 100 : 0; + nChgColors ^= 1; + } + break; + default: break; + } + + Color aCol1( rManager.MSO_CLR_ToColor( GetPropertyValue( DFF_Prop_fillColor, sal_uInt32(COL_WHITE) ), DFF_Prop_fillColor ) ); + Color aCol2( rManager.MSO_CLR_ToColor( GetPropertyValue( DFF_Prop_fillBackColor, sal_uInt32(COL_WHITE) ), DFF_Prop_fillBackColor ) ); + if ( nChgColors ) + { + //Swap start and end color + Color aZwi( aCol1 ); + aCol1 = aCol2; + aCol2 = aZwi; + //Swap two colors' transparency + double dTemp = dTrans; + dTrans = dBackTrans; + dBackTrans = dTemp; + } + + //Construct gradient item + XGradient aGrad( aCol2, aCol1, eGrad, nAngle, nFocusX, nFocusY ); + //Intensity has been merged into color. So here just set is as 100 + aGrad.SetStartIntens( 100 ); + aGrad.SetEndIntens( 100 ); + aSet.Put( XFillGradientItem( OUString(), aGrad ) ); + //Construct transparency item. This item can coordinate with both solid and gradient. + if ( dTrans < 1.0 || dBackTrans < 1.0 ) + { + sal_uInt8 nStartCol = static_cast( (1 - dTrans )* 255 ); + sal_uInt8 nEndCol = static_cast( ( 1- dBackTrans ) * 255 ); + aCol1 = Color(nStartCol, nStartCol, nStartCol); + aCol2 = Color(nEndCol, nEndCol, nEndCol); + + XGradient aGrad2( aCol2 , aCol1 , eGrad, nAngle, nFocusX, nFocusY ); + aSet.Put( XFillFloatTransparenceItem( OUString(), aGrad2 ) ); + } +} + + +//- Record Manager ---------------------------------------------------------- + + +DffRecordList::DffRecordList( DffRecordList* pList ) : + nCount ( 0 ), + nCurrent ( 0 ), + pPrev ( pList ) +{ + if ( pList ) + pList->pNext.reset( this ); +} + +DffRecordList::~DffRecordList() +{ +} + +DffRecordManager::DffRecordManager() : + DffRecordList ( nullptr ), + pCList ( static_cast(this) ) +{ +} + +DffRecordManager::DffRecordManager( SvStream& rIn ) : + DffRecordList ( nullptr ), + pCList ( static_cast(this) ) +{ + Consume( rIn ); +} + +void DffRecordManager::Consume( SvStream& rIn, sal_uInt32 nStOfs ) +{ + Clear(); + sal_uInt64 nOldPos = rIn.Tell(); + if ( !nStOfs ) + { + DffRecordHeader aHd; + bool bOk = ReadDffRecordHeader( rIn, aHd ); + if (bOk && aHd.nRecVer == DFF_PSFLAG_CONTAINER) + nStOfs = aHd.GetRecEndFilePos(); + } + if ( !nStOfs ) + return; + + pCList = this; + while ( pCList->pNext ) + pCList = pCList->pNext.get(); + while (rIn.good() && ( ( rIn.Tell() + 8 ) <= nStOfs )) + { + if ( pCList->nCount == DFF_RECORD_MANAGER_BUF_SIZE ) + pCList = new DffRecordList( pCList ); + if (!ReadDffRecordHeader(rIn, pCList->mHd[ pCList->nCount ])) + break; + bool bSeekSucceeded = pCList->mHd[ pCList->nCount++ ].SeekToEndOfRecord(rIn); + if (!bSeekSucceeded) + break; + } + rIn.Seek( nOldPos ); +} + +void DffRecordManager::Clear() +{ + pCList = this; + pNext.reset(); + nCurrent = 0; + nCount = 0; +} + +DffRecordHeader* DffRecordManager::Current() +{ + DffRecordHeader* pRet = nullptr; + if ( pCList->nCurrent < pCList->nCount ) + pRet = &pCList->mHd[ pCList->nCurrent ]; + return pRet; +} + +DffRecordHeader* DffRecordManager::First() +{ + DffRecordHeader* pRet = nullptr; + pCList = this; + if ( pCList->nCount ) + { + pCList->nCurrent = 0; + pRet = &pCList->mHd[ 0 ]; + } + return pRet; +} + +DffRecordHeader* DffRecordManager::Next() +{ + DffRecordHeader* pRet = nullptr; + sal_uInt32 nC = pCList->nCurrent + 1; + if ( nC < pCList->nCount ) + { + pCList->nCurrent++; + pRet = &pCList->mHd[ nC ]; + } + else if ( pCList->pNext ) + { + pCList = pCList->pNext.get(); + pCList->nCurrent = 0; + pRet = &pCList->mHd[ 0 ]; + } + return pRet; +} + +DffRecordHeader* DffRecordManager::Prev() +{ + DffRecordHeader* pRet = nullptr; + sal_uInt32 nCur = pCList->nCurrent; + if ( !nCur && pCList->pPrev ) + { + pCList = pCList->pPrev; + nCur = pCList->nCount; + } + if ( nCur-- ) + { + pCList->nCurrent = nCur; + pRet = &pCList->mHd[ nCur ]; + } + return pRet; +} + +DffRecordHeader* DffRecordManager::Last() +{ + DffRecordHeader* pRet = nullptr; + while ( pCList->pNext ) + pCList = pCList->pNext.get(); + sal_uInt32 nCnt = pCList->nCount; + if ( nCnt-- ) + { + pCList->nCurrent = nCnt; + pRet = &pCList->mHd[ nCnt ]; + } + return pRet; +} + +bool DffRecordManager::SeekToContent( SvStream& rIn, sal_uInt16 nRecId, DffSeekToContentMode eMode ) +{ + DffRecordHeader* pHd = GetRecordHeader( nRecId, eMode ); + if ( pHd ) + { + pHd->SeekToContent( rIn ); + return true; + } + else + return false; +} + +DffRecordHeader* DffRecordManager::GetRecordHeader( sal_uInt16 nRecId, DffSeekToContentMode eMode ) +{ + sal_uInt32 nOldCurrent = pCList->nCurrent; + DffRecordList* pOldList = pCList; + DffRecordHeader* pHd; + + if ( eMode == SEEK_FROM_BEGINNING ) + pHd = First(); + else + pHd = Next(); + + while ( pHd ) + { + if ( pHd->nRecType == nRecId ) + break; + pHd = Next(); + } + if ( !pHd && eMode == SEEK_FROM_CURRENT_AND_RESTART ) + { + DffRecordHeader* pBreak = &pOldList->mHd[ nOldCurrent ]; + pHd = First(); + if ( pHd ) + { + while ( pHd != pBreak ) + { + if ( pHd->nRecType == nRecId ) + break; + pHd = Next(); + } + if ( pHd->nRecType != nRecId ) + pHd = nullptr; + } + } + if ( !pHd ) + { + pCList = pOldList; + pOldList->nCurrent = nOldCurrent; + } + return pHd; +} + + +// private methods + + +bool CompareSvxMSDffShapeInfoById::operator() ( + std::shared_ptr const& lhs, + std::shared_ptr const& rhs) const +{ + return lhs->nShapeId < rhs->nShapeId; +} + +bool CompareSvxMSDffShapeInfoByTxBxComp::operator() ( + std::shared_ptr const& lhs, + std::shared_ptr const& rhs) const +{ + return lhs->nTxBxComp < rhs->nTxBxComp; +} + +void SvxMSDffManager::Scale( sal_Int32& rVal ) const +{ + if ( bNeedMap ) + rVal = BigMulDiv( rVal, nMapMul, nMapDiv ); +} + +void SvxMSDffManager::Scale( Point& rPos ) const +{ + rPos.AdjustX(nMapXOfs ); + rPos.AdjustY(nMapYOfs ); + if ( bNeedMap ) + { + rPos.setX( BigMulDiv( rPos.X(), nMapMul, nMapDiv ) ); + rPos.setY( BigMulDiv( rPos.Y(), nMapMul, nMapDiv ) ); + } +} + +void SvxMSDffManager::Scale( Size& rSiz ) const +{ + if ( bNeedMap ) + { + rSiz.setWidth( BigMulDiv( rSiz.Width(), nMapMul, nMapDiv ) ); + rSiz.setHeight( BigMulDiv( rSiz.Height(), nMapMul, nMapDiv ) ); + } +} + +void SvxMSDffManager::ScaleEmu( sal_Int32& rVal ) const +{ + rVal = BigMulDiv( rVal, nEmuMul, nEmuDiv ); +} + +sal_uInt32 SvxMSDffManager::ScalePt( sal_uInt32 nVal ) const +{ + MapUnit eMap = pSdrModel->GetScaleUnit(); + Fraction aFact( GetMapFactor( MapUnit::MapPoint, eMap ).X() ); + tools::Long aMul = aFact.GetNumerator(); + tools::Long aDiv = aFact.GetDenominator() * 65536; + aFact = Fraction( aMul, aDiv ); // try again to shorten it + return BigMulDiv( nVal, aFact.GetNumerator(), aFact.GetDenominator() ); +} + +sal_Int32 SvxMSDffManager::ScalePoint( sal_Int32 nVal ) const +{ + return BigMulDiv( nVal, nPntMul, nPntDiv ); +}; + +void SvxMSDffManager::SetModel(SdrModel* pModel, tools::Long nApplicationScale) +{ + pSdrModel = pModel; + if( pModel && (0 < nApplicationScale) ) + { + // PPT works in units of 576DPI + // WW on the other side uses twips, i.e. 1440DPI. + MapUnit eMap = pSdrModel->GetScaleUnit(); + Fraction aFact( GetMapFactor(MapUnit::MapInch, eMap).X() ); + tools::Long nMul=aFact.GetNumerator(); + tools::Long nDiv=aFact.GetDenominator()*nApplicationScale; + aFact=Fraction(nMul,nDiv); // try again to shorten it + // For 100TH_MM -> 2540/576=635/144 + // For Twip -> 1440/576=5/2 + nMapMul = aFact.GetNumerator(); + nMapDiv = aFact.GetDenominator(); + bNeedMap = nMapMul!=nMapDiv; + + // MS-DFF-Properties are mostly given in EMU (English Metric Units) + // 1mm=36000emu, 1twip=635emu + aFact=GetMapFactor(MapUnit::Map100thMM,eMap).X(); + nMul=aFact.GetNumerator(); + nDiv=aFact.GetDenominator()*360; + aFact=Fraction(nMul,nDiv); // try again to shorten it + // For 100TH_MM -> 1/360 + // For Twip -> 14,40/(25,4*360)=144/91440=1/635 + nEmuMul=aFact.GetNumerator(); + nEmuDiv=aFact.GetDenominator(); + + // And something for typographic Points + aFact=GetMapFactor(MapUnit::MapPoint,eMap).X(); + nPntMul=aFact.GetNumerator(); + nPntDiv=aFact.GetDenominator(); + } + else + { + pModel = nullptr; + nMapMul = nMapDiv = nMapXOfs = nMapYOfs = nEmuMul = nEmuDiv = nPntMul = nPntDiv = 0; + bNeedMap = false; + } +} + +bool SvxMSDffManager::SeekToShape( SvStream& rSt, SvxMSDffClientData* /* pClientData */, sal_uInt32 nId ) const +{ + bool bRet = false; + if ( !maFidcls.empty() ) + { + sal_uInt64 nOldPos = rSt.Tell(); + sal_uInt32 nSec = ( nId >> 10 ) - 1; + if ( nSec < mnIdClusters ) + { + OffsetMap::const_iterator it = maDgOffsetTable.find( maFidcls[ nSec ].dgid ); + if ( it != maDgOffsetTable.end() ) + { + sal_uInt64 nOfs = it->second; + rSt.Seek( nOfs ); + DffRecordHeader aEscherF002Hd; + bool bOk = ReadDffRecordHeader( rSt, aEscherF002Hd ); + sal_uLong nEscherF002End = bOk ? aEscherF002Hd.GetRecEndFilePos() : 0; + while (rSt.good() && rSt.Tell() < nEscherF002End) + { + DffRecordHeader aEscherObjListHd; + if (!ReadDffRecordHeader(rSt, aEscherObjListHd)) + break; + if ( aEscherObjListHd.nRecVer != 0xf ) + { + bool bSeekSuccess = aEscherObjListHd.SeekToEndOfRecord(rSt); + if (!bSeekSuccess) + break; + } + else if ( aEscherObjListHd.nRecType == DFF_msofbtSpContainer ) + { + DffRecordHeader aShapeHd; + if ( SeekToRec( rSt, DFF_msofbtSp, aEscherObjListHd.GetRecEndFilePos(), &aShapeHd ) ) + { + sal_uInt32 nShapeId(0); + rSt.ReadUInt32( nShapeId ); + if ( nId == nShapeId ) + { + aEscherObjListHd.SeekToBegOfRecord( rSt ); + bRet = true; + break; + } + } + bool bSeekSuccess = aEscherObjListHd.SeekToEndOfRecord(rSt); + if (!bSeekSuccess) + break; + } + } + } + } + if ( !bRet ) + rSt.Seek( nOldPos ); + } + return bRet; +} + +bool SvxMSDffManager::SeekToRec( SvStream& rSt, sal_uInt16 nRecId, sal_uLong nMaxFilePos, DffRecordHeader* pRecHd, sal_uLong nSkipCount ) +{ + bool bRet = false; + sal_uInt64 nOldFPos = rSt.Tell(); // store FilePos to restore it later if necessary + do + { + DffRecordHeader aHd; + if (!ReadDffRecordHeader(rSt, aHd)) + break; + if (aHd.nRecLen > nMaxLegalDffRecordLength) + break; + if ( aHd.nRecType == nRecId ) + { + if ( nSkipCount ) + nSkipCount--; + else + { + bRet = true; + if ( pRecHd != nullptr ) + *pRecHd = aHd; + else + { + bool bSeekSuccess = aHd.SeekToBegOfRecord(rSt); + if (!bSeekSuccess) + { + bRet = false; + break; + } + } + } + } + if ( !bRet ) + { + bool bSeekSuccess = aHd.SeekToEndOfRecord(rSt); + if (!bSeekSuccess) + break; + } + } + while ( rSt.good() && rSt.Tell() < nMaxFilePos && !bRet ); + if ( !bRet ) + rSt.Seek( nOldFPos ); // restore original FilePos + return bRet; +} + +bool SvxMSDffManager::SeekToRec2( sal_uInt16 nRecId1, sal_uInt16 nRecId2, sal_uLong nMaxFilePos ) const +{ + bool bRet = false; + sal_uInt64 nOldFPos = rStCtrl.Tell(); // remember FilePos for conditionally later restoration + do + { + DffRecordHeader aHd; + if (!ReadDffRecordHeader(rStCtrl, aHd)) + break; + if ( aHd.nRecType == nRecId1 || aHd.nRecType == nRecId2 ) + { + bRet = true; + bool bSeekSuccess = aHd.SeekToBegOfRecord(rStCtrl); + if (!bSeekSuccess) + { + bRet = false; + break; + } + } + if ( !bRet ) + { + bool bSeekSuccess = aHd.SeekToEndOfRecord(rStCtrl); + if (!bSeekSuccess) + break; + } + } + while ( rStCtrl.good() && rStCtrl.Tell() < nMaxFilePos && !bRet ); + if ( !bRet ) + rStCtrl.Seek( nOldFPos ); // restore FilePos + return bRet; +} + + +bool SvxMSDffManager::GetColorFromPalette( sal_uInt16 /* nNum */, Color& rColor ) const +{ + // This method has to be overwritten in the class + // derived for the excel export + rColor = COL_WHITE; + return true; +} + +// sj: the documentation is not complete, especially in ppt the normal rgb for text +// color is written as 0xfeRRGGBB, this can't be explained by the documentation, nearly +// every bit in the upper code is set -> so there seems to be a special handling for +// ppt text colors, i decided not to fix this in MSO_CLR_ToColor because of possible +// side effects, instead MSO_TEXT_CLR_ToColor is called for PPT text colors, to map +// the color code to something that behaves like the other standard color codes used by +// fill and line color +Color SvxMSDffManager::MSO_TEXT_CLR_ToColor( sal_uInt32 nColorCode ) const +{ + // for text colors: Header is 0xfeRRGGBB + if ( ( nColorCode & 0xfe000000 ) == 0xfe000000 ) + nColorCode &= 0x00ffffff; + else + { + // for colorscheme colors the color index are the lower three bits of the upper byte + if ( ( nColorCode & 0xf8000000 ) == 0 ) // this must be a colorscheme index + { + nColorCode >>= 24; + nColorCode |= 0x8000000; + } + } + return MSO_CLR_ToColor( nColorCode ); +} + +Color SvxMSDffManager::MSO_CLR_ToColor( sal_uInt32 nColorCode, sal_uInt16 nContentProperty ) const +{ + Color aColor( mnDefaultColor ); + + // for text colors: Header is 0xfeRRGGBB + if ( ( nColorCode & 0xfe000000 ) == 0xfe000000 ) // sj: it needs to be checked if 0xfe is used in + nColorCode &= 0x00ffffff; // other cases than ppt text -> if not this code can be removed + + sal_uInt8 nUpper = static_cast( nColorCode >> 24 ); + + // sj: below change from 0x1b to 0x19 was done because of i84812 (0x02 -> rgb color), + // now I have some problems to fix i104685 (there the color value is 0x02000000 which requires + // a 0x2 scheme color to be displayed properly), the color docu seems to be incomplete + if( nUpper & 0x19 ) // if( nUpper & 0x1f ) + { + if( ( nUpper & 0x08 ) || ( ( nUpper & 0x10 ) == 0 ) ) + { + // SCHEMECOLOR + if ( !GetColorFromPalette( ( nUpper & 8 ) ? static_cast(nColorCode) : nUpper, aColor ) ) + { + switch( nContentProperty ) + { + case DFF_Prop_pictureTransparent : + case DFF_Prop_shadowColor : + case DFF_Prop_fillBackColor : + case DFF_Prop_fillColor : + aColor = COL_WHITE; + break; + case DFF_Prop_lineColor : + { + aColor = COL_BLACK; + } + break; + } + } + } + else // SYSCOLOR + { + const StyleSettings& rStyleSettings = Application::GetSettings().GetStyleSettings(); + + sal_uInt16 nParameter = sal_uInt16(( nColorCode >> 16 ) & 0x00ff); // the HiByte of nParameter is not zero, an exclusive AND is helping :o + sal_uInt16 nFunctionBits = static_cast( ( nColorCode & 0x00000f00 ) >> 8 ); + sal_uInt16 nAdditionalFlags = static_cast( ( nColorCode & 0x0000f000) >> 8 ); + sal_uInt16 nColorIndex = sal_uInt16(nColorCode & 0x00ff); + sal_uInt32 nPropColor = 0; + + sal_uInt16 nCProp = 0; + + switch ( nColorIndex ) + { + case mso_syscolorButtonFace : aColor = rStyleSettings.GetFaceColor(); break; + case mso_syscolorWindowText : aColor = rStyleSettings.GetWindowTextColor(); break; + case mso_syscolorMenu : aColor = rStyleSettings.GetMenuColor(); break; + case mso_syscolor3DLight : + case mso_syscolorButtonHighlight : + case mso_syscolorHighlight : aColor = rStyleSettings.GetHighlightColor(); break; + case mso_syscolorHighlightText : aColor = rStyleSettings.GetHighlightTextColor(); break; + case mso_syscolorCaptionText : aColor = rStyleSettings.GetMenuTextColor(); break; + case mso_syscolorActiveCaption : aColor = rStyleSettings.GetHighlightColor(); break; + case mso_syscolorButtonShadow : aColor = rStyleSettings.GetShadowColor(); break; + case mso_syscolorButtonText : aColor = rStyleSettings.GetButtonTextColor(); break; + case mso_syscolorGrayText : aColor = rStyleSettings.GetDeactiveColor(); break; + case mso_syscolorInactiveCaption : aColor = rStyleSettings.GetDeactiveColor(); break; + case mso_syscolorInactiveCaptionText : aColor = rStyleSettings.GetDeactiveColor(); break; + case mso_syscolorInfoBackground : aColor = rStyleSettings.GetFaceColor(); break; + case mso_syscolorInfoText : aColor = rStyleSettings.GetLabelTextColor(); break; + case mso_syscolorMenuText : aColor = rStyleSettings.GetMenuTextColor(); break; + case mso_syscolorScrollbar : aColor = rStyleSettings.GetFaceColor(); break; + case mso_syscolorWindow : aColor = rStyleSettings.GetWindowColor(); break; + case mso_syscolorWindowFrame : aColor = rStyleSettings.GetWindowColor(); break; + + case mso_colorFillColor : + { + nPropColor = GetPropertyValue( DFF_Prop_fillColor, 0xffffff ); + nCProp = DFF_Prop_fillColor; + } + break; + case mso_colorLineOrFillColor : // ( use the line color only if there is a line ) + { + if ( GetPropertyValue( DFF_Prop_fNoLineDrawDash, 0 ) & 8 ) + { + nPropColor = GetPropertyValue( DFF_Prop_lineColor, 0 ); + nCProp = DFF_Prop_lineColor; + } + else + { + nPropColor = GetPropertyValue( DFF_Prop_fillColor, 0xffffff ); + nCProp = DFF_Prop_fillColor; + } + } + break; + case mso_colorLineColor : + { + nPropColor = GetPropertyValue( DFF_Prop_lineColor, 0 ); + nCProp = DFF_Prop_lineColor; + } + break; + case mso_colorShadowColor : + { + nPropColor = GetPropertyValue( DFF_Prop_shadowColor, 0x808080 ); + nCProp = DFF_Prop_shadowColor; + } + break; + case mso_colorThis : // ( use this color ... ) + { + nPropColor = GetPropertyValue( DFF_Prop_fillColor, 0xffffff ); //????????????? + nCProp = DFF_Prop_fillColor; + } + break; + case mso_colorFillBackColor : + { + nPropColor = GetPropertyValue( DFF_Prop_fillBackColor, 0xffffff ); + nCProp = DFF_Prop_fillBackColor; + } + break; + case mso_colorLineBackColor : + { + nPropColor = GetPropertyValue( DFF_Prop_lineBackColor, 0xffffff ); + nCProp = DFF_Prop_lineBackColor; + } + break; + case mso_colorFillThenLine : // ( use the fillcolor unless no fill and line ) + { + nPropColor = GetPropertyValue( DFF_Prop_fillColor, 0xffffff ); //????????????? + nCProp = DFF_Prop_fillColor; + } + break; + case mso_colorIndexMask : // ( extract the color index ) ? + { + nPropColor = GetPropertyValue( DFF_Prop_fillColor, 0xffffff ); //????????????? + nCProp = DFF_Prop_fillColor; + } + break; + } + if ( nCProp && ( nPropColor & 0x10000000 ) == 0 ) // beware of looping recursive + aColor = MSO_CLR_ToColor( nPropColor, nCProp ); + + if( nAdditionalFlags & 0x80 ) // make color gray + { + sal_uInt8 nZwi = aColor.GetLuminance(); + aColor = Color( nZwi, nZwi, nZwi ); + } + switch( nFunctionBits ) + { + case 0x01 : // darken color by parameter + { + aColor.SetRed( sal::static_int_cast< sal_uInt8 >( ( nParameter * aColor.GetRed() ) >> 8 ) ); + aColor.SetGreen( sal::static_int_cast< sal_uInt8 >( ( nParameter * aColor.GetGreen() ) >> 8 ) ); + aColor.SetBlue( sal::static_int_cast< sal_uInt8 >( ( nParameter * aColor.GetBlue() ) >> 8 ) ); + } + break; + case 0x02 : // lighten color by parameter + { + sal_uInt16 nInvParameter = ( 0x00ff - nParameter ) * 0xff; + aColor.SetRed( sal::static_int_cast< sal_uInt8 >( ( nInvParameter + ( nParameter * aColor.GetRed() ) ) >> 8 ) ); + aColor.SetGreen( sal::static_int_cast< sal_uInt8 >( ( nInvParameter + ( nParameter * aColor.GetGreen() ) ) >> 8 ) ); + aColor.SetBlue( sal::static_int_cast< sal_uInt8 >( ( nInvParameter + ( nParameter * aColor.GetBlue() ) ) >> 8 ) ); + } + break; + case 0x03 : // add grey level RGB(p,p,p) + { + sal_Int16 nR = static_cast(aColor.GetRed()) + static_cast(nParameter); + sal_Int16 nG = static_cast(aColor.GetGreen()) + static_cast(nParameter); + sal_Int16 nB = static_cast(aColor.GetBlue()) + static_cast(nParameter); + if ( nR > 0x00ff ) + nR = 0x00ff; + if ( nG > 0x00ff ) + nG = 0x00ff; + if ( nB > 0x00ff ) + nB = 0x00ff; + aColor = Color( static_cast(nR), static_cast(nG), static_cast(nB) ); + } + break; + case 0x04 : // subtract grey level RGB(p,p,p) + { + sal_Int16 nR = static_cast(aColor.GetRed()) - static_cast(nParameter); + sal_Int16 nG = static_cast(aColor.GetGreen()) - static_cast(nParameter); + sal_Int16 nB = static_cast(aColor.GetBlue()) - static_cast(nParameter); + if ( nR < 0 ) + nR = 0; + if ( nG < 0 ) + nG = 0; + if ( nB < 0 ) + nB = 0; + aColor = Color( static_cast(nR), static_cast(nG), static_cast(nB) ); + } + break; + case 0x05 : // subtract from gray level RGB(p,p,p) + { + sal_Int16 nR = static_cast(nParameter) - static_cast(aColor.GetRed()); + sal_Int16 nG = static_cast(nParameter) - static_cast(aColor.GetGreen()); + sal_Int16 nB = static_cast(nParameter) - static_cast(aColor.GetBlue()); + if ( nR < 0 ) + nR = 0; + if ( nG < 0 ) + nG = 0; + if ( nB < 0 ) + nB = 0; + aColor = Color( static_cast(nR), static_cast(nG), static_cast(nB) ); + } + break; + case 0x06 : // per component: black if < p, white if >= p + { + aColor.SetRed( aColor.GetRed() < nParameter ? 0x00 : 0xff ); + aColor.SetGreen( aColor.GetGreen() < nParameter ? 0x00 : 0xff ); + aColor.SetBlue( aColor.GetBlue() < nParameter ? 0x00 : 0xff ); + } + break; + } + if ( nAdditionalFlags & 0x40 ) // top-bit invert + aColor = Color( aColor.GetRed() ^ 0x80, aColor.GetGreen() ^ 0x80, aColor.GetBlue() ^ 0x80 ); + + if ( nAdditionalFlags & 0x20 ) // invert color + aColor = Color(0xff - aColor.GetRed(), 0xff - aColor.GetGreen(), 0xff - aColor.GetBlue()); + } + } + else if ( ( nUpper & 4 ) && ( ( nColorCode & 0xfffff8 ) == 0 ) ) + { // case of nUpper == 4 powerpoint takes this as argument for a colorschemecolor + GetColorFromPalette( nUpper, aColor ); + } + else // attributed hard, maybe with hint to SYSTEMRGB + aColor = Color( static_cast(nColorCode), static_cast( nColorCode >> 8 ), static_cast( nColorCode >> 16 ) ); + return aColor; +} + +void SvxMSDffManager::ReadObjText( SvStream& rStream, SdrObject* pObj ) +{ + DffRecordHeader aRecHd; + if (!ReadDffRecordHeader(rStream, aRecHd)) + return; + if( aRecHd.nRecType != DFF_msofbtClientTextbox && aRecHd.nRecType != 0x1022 ) + return; + + while (rStream.good() && rStream.Tell() < aRecHd.GetRecEndFilePos()) + { + DffRecordHeader aHd; + if (!ReadDffRecordHeader(rStream, aHd)) + break; + switch( aHd.nRecType ) + { + case DFF_PST_TextBytesAtom: + case DFF_PST_TextCharsAtom: + { + bool bUniCode = ( aHd.nRecType == DFF_PST_TextCharsAtom ); + sal_uInt32 nBytes = aHd.nRecLen; + OUString aStr = MSDFFReadZString( rStream, nBytes, bUniCode ); + ReadObjText( aStr, pObj ); + } + break; + default: + break; + } + bool bSeekSuccess = aHd.SeekToEndOfRecord(rStream); + if (!bSeekSuccess) + break; + } +} + +// sj: I just want to set a string for a text object that may contain multiple +// paragraphs. If I now take a look at the following code I get the impression that +// our outliner is too complicate to be used properly, +void SvxMSDffManager::ReadObjText( const OUString& rText, SdrObject* pObj ) +{ + SdrTextObj* pText = dynamic_cast( pObj ); + if ( !pText ) + return; + + SdrOutliner& rOutliner = pText->ImpGetDrawOutliner(); + rOutliner.Init( OutlinerMode::TextObject ); + + bool bOldUpdateMode = rOutliner.SetUpdateLayout( false ); + rOutliner.SetVertical( pText->IsVerticalWriting() ); + + sal_Int32 nParaIndex = 0; + sal_Int32 nParaSize; + const sal_Unicode* pBuf = rText.getStr(); + const sal_Unicode* pEnd = rText.getStr() + rText.getLength(); + + while( pBuf < pEnd ) + { + const sal_Unicode* pCurrent = pBuf; + + for ( nParaSize = 0; pBuf < pEnd; ) + { + sal_Unicode nChar = *pBuf++; + if ( nChar == 0xa ) + { + if ( ( pBuf < pEnd ) && ( *pBuf == 0xd ) ) + pBuf++; + break; + } + else if ( nChar == 0xd ) + { + if ( ( pBuf < pEnd ) && ( *pBuf == 0xa ) ) + pBuf++; + break; + } + else + ++nParaSize; + } + ESelection aSelection( nParaIndex, 0, nParaIndex, 0 ); + OUString aParagraph( pCurrent, nParaSize ); + if ( !nParaIndex && aParagraph.isEmpty() ) // SJ: we are crashing if the first paragraph is empty ? + aParagraph += " "; // otherwise these two lines can be removed. + rOutliner.Insert( aParagraph, nParaIndex ); + rOutliner.SetParaAttribs( nParaIndex, rOutliner.GetEmptyItemSet() ); + + SfxItemSet aParagraphAttribs( rOutliner.GetEmptyItemSet() ); + if ( !aSelection.nStartPos ) + aParagraphAttribs.Put( SfxBoolItem( EE_PARA_BULLETSTATE, false ) ); + aSelection.nStartPos = 0; + rOutliner.QuickSetAttribs( aParagraphAttribs, aSelection ); + nParaIndex++; + } + std::optional pNewText = rOutliner.CreateParaObject(); + rOutliner.Clear(); + rOutliner.SetUpdateLayout( bOldUpdateMode ); + pText->SetOutlinerParaObject( std::move(pNewText) ); + // tdf#143315: restore stylesheet applied to Outliner's nodes when SdrTextObj initializes + // its attributes, but removed by Outliner::Init, which calls Outliner::Clear. + pText->SetStyleSheet(pText->GetStyleSheet(), true); +} + +//static +OUString SvxMSDffManager::MSDFFReadZString(SvStream& rIn, + sal_uInt32 nLen, bool bUniCode) +{ + if (!nLen) + return OUString(); + + OUString sBuf; + + if( bUniCode ) + sBuf = read_uInt16s_ToOUString(rIn, nLen/2); + else + sBuf = read_uInt8s_ToOUString(rIn, nLen, RTL_TEXTENCODING_MS_1252); + + return comphelper::string::stripEnd(sBuf, 0); +} + +static Size lcl_GetPrefSize(const Graphic& rGraf, const MapMode& aWanted) +{ + MapMode aPrefMapMode(rGraf.GetPrefMapMode()); + if (aPrefMapMode == aWanted) + return rGraf.GetPrefSize(); + Size aRetSize; + if (aPrefMapMode.GetMapUnit() == MapUnit::MapPixel) + { + aRetSize = Application::GetDefaultDevice()->PixelToLogic( + rGraf.GetPrefSize(), aWanted); + } + else + { + aRetSize = OutputDevice::LogicToLogic( + rGraf.GetPrefSize(), rGraf.GetPrefMapMode(), aWanted); + } + return aRetSize; +} + +// sj: if the parameter pSet is null, then the resulting crop bitmap will be stored in rGraf, +// otherwise rGraf is untouched and pSet is used to store the corresponding SdrGrafCropItem +static void lcl_ApplyCropping( const DffPropSet& rPropSet, SfxItemSet* pSet, Graphic& rGraf ) +{ + sal_Int32 nCropTop = static_cast(rPropSet.GetPropertyValue( DFF_Prop_cropFromTop, 0 )); + sal_Int32 nCropBottom = static_cast(rPropSet.GetPropertyValue( DFF_Prop_cropFromBottom, 0 )); + sal_Int32 nCropLeft = static_cast(rPropSet.GetPropertyValue( DFF_Prop_cropFromLeft, 0 )); + sal_Int32 nCropRight = static_cast(rPropSet.GetPropertyValue( DFF_Prop_cropFromRight, 0 )); + + if( !(nCropTop || nCropBottom || nCropLeft || nCropRight) ) + return; + + double fFactor; + Size aCropSize; + BitmapEx aCropBitmap; + sal_uInt32 nTop( 0 ), nBottom( 0 ), nLeft( 0 ), nRight( 0 ); + + // Cropping has to be applied on a loaded graphic. + rGraf.makeAvailable(); + + if ( pSet ) // use crop attributes ? + aCropSize = lcl_GetPrefSize(rGraf, MapMode(MapUnit::Map100thMM)); + else + { + aCropBitmap = rGraf.GetBitmapEx(); + aCropSize = aCropBitmap.GetSizePixel(); + } + if ( nCropTop ) + { + fFactor = static_cast(nCropTop) / 65536.0; + nTop = static_cast( ( static_cast( aCropSize.Height() + 1 ) * fFactor ) + 0.5 ); + } + if ( nCropBottom ) + { + fFactor = static_cast(nCropBottom) / 65536.0; + nBottom = static_cast( ( static_cast( aCropSize.Height() + 1 ) * fFactor ) + 0.5 ); + } + if ( nCropLeft ) + { + fFactor = static_cast(nCropLeft) / 65536.0; + nLeft = static_cast( ( static_cast( aCropSize.Width() + 1 ) * fFactor ) + 0.5 ); + } + if ( nCropRight ) + { + fFactor = static_cast(nCropRight) / 65536.0; + nRight = static_cast( ( static_cast( aCropSize.Width() + 1 ) * fFactor ) + 0.5 ); + } + if ( pSet ) // use crop attributes ? + pSet->Put( SdrGrafCropItem( nLeft, nTop, nRight, nBottom ) ); + else + { + tools::Rectangle aCropRect( nLeft, nTop, aCropSize.Width() - nRight, aCropSize.Height() - nBottom ); + aCropBitmap.Crop( aCropRect ); + rGraf = aCropBitmap; + } +} + +SdrObject* SvxMSDffManager::ImportGraphic( SvStream& rSt, SfxItemSet& rSet, const DffObjData& rObjData ) +{ + SdrObject* pRet = nullptr; + OUString aLinkFileName; + tools::Rectangle aVisArea; + + auto eFlags = GetPropertyValue(DFF_Prop_pibFlags, mso_blipflagDefault); + sal_uInt32 nBlipId = GetPropertyValue( DFF_Prop_pib, 0 ); + bool bGrfRead = false, + + // Graphic linked + bLinkGrf = 0 != ( eFlags & mso_blipflagLinkToFile ); + { + OUString aFileName; + Graphic aGraf; // be sure this graphic is deleted before swapping out + if( SeekToContent( DFF_Prop_pibName, rSt ) ) + aFileName = MSDFFReadZString( rSt, GetPropertyValue( DFF_Prop_pibName, 0 ), true ); + + // AND, OR the following: + if( !( eFlags & mso_blipflagDoNotSave ) ) // Graphic embedded + { + bGrfRead = GetBLIP( nBlipId, aGraf, &aVisArea ); + if ( !bGrfRead ) + { + /* + Still no luck, lets look at the end of this record for a FBSE pool, + this fallback is a specific case for how word does it sometimes + */ + bool bOk = rObjData.rSpHd.SeekToEndOfRecord( rSt ); + DffRecordHeader aHd; + if (bOk) + { + bOk = ReadDffRecordHeader(rSt, aHd); + } + if (bOk && DFF_msofbtBSE == aHd.nRecType) + { + const sal_uLong nSkipBLIPLen = 20; + const sal_uLong nSkipShapePos = 4; + const sal_uLong nSkipBLIP = 4; + const sal_uLong nSkip = + nSkipBLIPLen + 4 + nSkipShapePos + 4 + nSkipBLIP; + + if (nSkip <= aHd.nRecLen) + { + rSt.SeekRel(nSkip); + if (ERRCODE_NONE == rSt.GetError()) + bGrfRead = GetBLIPDirect( rSt, aGraf, &aVisArea ); + } + } + } + } + if ( bGrfRead ) + { + // the writer is doing its own cropping, so this part affects only impress and calc, + // unless we're inside a group, in which case writer doesn't crop either + if (( GetSvxMSDffSettings() & SVXMSDFF_SETTINGS_CROP_BITMAPS ) || rObjData.nCalledByGroup != 0 ) + lcl_ApplyCropping( *this, !bool( rObjData.nSpFlags & ShapeFlag::OLEShape ) ? &rSet : nullptr, aGraf ); + + if ( IsProperty( DFF_Prop_pictureTransparent ) ) + { + sal_uInt32 nTransColor = GetPropertyValue( DFF_Prop_pictureTransparent, 0 ); + + if ( aGraf.GetType() == GraphicType::Bitmap ) + { + BitmapEx aBitmapEx( aGraf.GetBitmapEx() ); + aBitmapEx.CombineMaskOr( MSO_CLR_ToColor( nTransColor, DFF_Prop_pictureTransparent ), 9 ); + aGraf = aBitmapEx; + } + } + + sal_Int32 nContrast = GetPropertyValue( DFF_Prop_pictureContrast, 0x10000 ); + /* + 0x10000 is msoffice 50% + < 0x10000 is in units of 1/50th of 0x10000 per 1% + > 0x10000 is in units where + a msoffice x% is stored as 50/(100-x) * 0x10000 + + plus, a (ui) microsoft % ranges from 0 to 100, OOO + from -100 to 100, so also normalize into that range + */ + if ( nContrast > 0x10000 ) + { + double fX = nContrast; + fX /= 0x10000; + fX /= 51; // 50 + 1 to round + fX = 1/fX; + nContrast = static_cast(fX); + nContrast -= 100; + nContrast = -nContrast; + nContrast = (nContrast-50)*2; + } + else if ( nContrast == 0x10000 ) + nContrast = 0; + else + { + if (o3tl::checked_multiply(nContrast, 101, nContrast)) //100 + 1 to round + { + SAL_WARN("filter.ms", "bad Contrast value:" << nContrast); + nContrast = 0; + } + else + { + nContrast /= 0x10000; + nContrast -= 100; + } + } + sal_Int16 nBrightness = static_cast( static_cast(GetPropertyValue( DFF_Prop_pictureBrightness, 0 )) / 327 ); + sal_Int32 nGamma = GetPropertyValue( DFF_Prop_pictureGamma, 0x10000 ); + GraphicDrawMode eDrawMode = GraphicDrawMode::Standard; + switch ( GetPropertyValue( DFF_Prop_pictureActive, 0 ) & 6 ) + { + case 4 : eDrawMode = GraphicDrawMode::Greys; break; + case 6 : eDrawMode = GraphicDrawMode::Mono; break; + case 0 : + { + //office considers the converted values of (in OOo) 70 to be the + //"watermark" values, which can vary slightly due to rounding from the + //above values + if (( nContrast == -70 ) && ( nBrightness == 70 )) + { + nContrast = 0; + nBrightness = 0; + eDrawMode = GraphicDrawMode::Watermark; + }; + } + break; + } + + if ( nContrast || nBrightness || ( nGamma != 0x10000 ) || ( eDrawMode != GraphicDrawMode::Standard ) ) + { + // MSO uses a different algorithm for contrast+brightness, LO applies contrast before brightness, + // while MSO apparently applies half of brightness before contrast and half after. So if only + // contrast or brightness need to be altered, the result is the same, but if both are involved, + // there's no way to map that, so just force a conversion of the image. + bool needsConversion = nContrast != 0 && nBrightness != 0; + if ( !bool(rObjData.nSpFlags & ShapeFlag::OLEShape) && !needsConversion ) + { + if ( nBrightness ) + rSet.Put( SdrGrafLuminanceItem( nBrightness ) ); + if ( nContrast ) + rSet.Put( SdrGrafContrastItem( static_cast(nContrast) ) ); + if ( nGamma != 0x10000 ) + rSet.Put( SdrGrafGamma100Item( nGamma / 655 ) ); + if ( eDrawMode != GraphicDrawMode::Standard ) + rSet.Put( SdrGrafModeItem( eDrawMode ) ); + } + else + { + if ( eDrawMode == GraphicDrawMode::Watermark ) + { + nContrast = 60; + nBrightness = 70; + eDrawMode = GraphicDrawMode::Standard; + } + switch ( aGraf.GetType() ) + { + case GraphicType::Bitmap : + { + BitmapEx aBitmapEx( aGraf.GetBitmapEx() ); + if ( nBrightness || nContrast || ( nGamma != 0x10000 ) ) + aBitmapEx.Adjust( nBrightness, static_cast(nContrast), 0, 0, 0, static_cast(nGamma) / 0x10000, false, true ); + if ( eDrawMode == GraphicDrawMode::Greys ) + aBitmapEx.Convert( BmpConversion::N8BitGreys ); + else if ( eDrawMode == GraphicDrawMode::Mono ) + aBitmapEx.Convert( BmpConversion::N1BitThreshold ); + aGraf = aBitmapEx; + + } + break; + + case GraphicType::GdiMetafile : + { + GDIMetaFile aGdiMetaFile( aGraf.GetGDIMetaFile() ); + if ( nBrightness || nContrast || ( nGamma != 0x10000 ) ) + aGdiMetaFile.Adjust( nBrightness, static_cast(nContrast), 0, 0, 0, static_cast(nGamma) / 0x10000, false, true ); + if ( eDrawMode == GraphicDrawMode::Greys ) + aGdiMetaFile.Convert( MtfConversion::N8BitGreys ); + else if ( eDrawMode == GraphicDrawMode::Mono ) + aGdiMetaFile.Convert( MtfConversion::N1BitThreshold ); + aGraf = aGdiMetaFile; + } + break; + default: break; + } + } + } + } + + // should it be an OLE object? + if( bGrfRead && !bLinkGrf && IsProperty( DFF_Prop_pictureId ) ) + { + // TODO/LATER: in future probably the correct aspect should be provided here + // #i32596# - pass to method + pRet = ImportOLE( GetPropertyValue( DFF_Prop_pictureId, 0 ), aGraf, rObjData.aBoundRect, aVisArea, rObjData.nCalledByGroup ); + } + if( !pRet ) + { + pRet = new SdrGrafObj(*pSdrModel); + if( bGrfRead ) + static_cast(pRet)->SetGraphic( aGraf ); + + if( bLinkGrf && !bGrfRead ) // sj: #i55484# if the graphic was embedded ( bGrfRead == true ) then + { // we do not need to set a link. TODO: not to lose the information where the graphic is linked from + INetURLObject aAbsURL; + if ( !INetURLObject( maBaseURL ).GetNewAbsURL( aFileName, &aAbsURL ) ) + { + OUString aValidURL; + if( osl::FileBase::getFileURLFromSystemPath( aFileName, aValidURL ) == osl::FileBase::E_None ) + aAbsURL = INetURLObject( aValidURL ); + } + if( aAbsURL.GetProtocol() != INetProtocol::NotValid ) + { + aLinkFileName = aAbsURL.GetMainURL( INetURLObject::DecodeMechanism::ToIUri ); + } + else + aLinkFileName = aFileName; + } + } + + // set the size from BLIP if there is one + if ( bGrfRead && !aVisArea.IsEmpty() ) + pRet->SetBLIPSizeRectangle( aVisArea ); + + if (pRet->GetName().isEmpty()) // SJ 22.02.00 : PPT OLE IMPORT: + { // name is already set in ImportOLE !! + // JP 01.12.99: SetName before SetModel - because in the other order the Bug 70098 is active + if ( ( eFlags & mso_blipflagType ) != mso_blipflagComment ) + { + INetURLObject aURL; + aURL.SetSmartURL( aFileName ); + pRet->SetName( aURL.getBase() ); + } + else + pRet->SetName( aFileName ); + } + } + pRet->NbcSetLogicRect( rObjData.aBoundRect ); + + if (SdrGrafObj* pGrafObj = dynamic_cast(pRet)) + { + if( aLinkFileName.getLength() ) + { + pGrafObj->SetGraphicLink( aLinkFileName ); + Graphic aGraphic(pGrafObj->GetGraphic()); + aGraphic.setOriginURL(aLinkFileName); + } + + if ( bLinkGrf && !bGrfRead ) + { + Graphic aGraf(pGrafObj->GetGraphic()); + lcl_ApplyCropping( *this, &rSet, aGraf ); + } + } + + return pRet; +} + +// PptSlidePersistEntry& rPersistEntry, SdPage* pPage +SdrObject* SvxMSDffManager::ImportObj( SvStream& rSt, SvxMSDffClientData& rClientData, + tools::Rectangle& rClientRect, const tools::Rectangle& rGlobalChildRect, int nCalledByGroup, sal_Int32* pShapeId ) +{ + SdrObject* pRet = nullptr; + DffRecordHeader aObjHd; + bool bOk = ReadDffRecordHeader(rSt, aObjHd); + if (bOk && aObjHd.nRecType == DFF_msofbtSpgrContainer) + { + pRet = ImportGroup( aObjHd, rSt, rClientData, rClientRect, rGlobalChildRect, nCalledByGroup, pShapeId ); + } + else if (bOk && aObjHd.nRecType == DFF_msofbtSpContainer) + { + pRet = ImportShape( aObjHd, rSt, rClientData, rClientRect, rGlobalChildRect, nCalledByGroup, pShapeId ); + } + aObjHd.SeekToBegOfRecord( rSt ); // restore FilePos + return pRet; +} + +SdrObject* SvxMSDffManager::ImportGroup( const DffRecordHeader& rHd, SvStream& rSt, SvxMSDffClientData& rClientData, + tools::Rectangle& rClientRect, const tools::Rectangle& rGlobalChildRect, + int nCalledByGroup, sal_Int32* pShapeId ) +{ + SdrObject* pRet = nullptr; + + if( pShapeId ) + *pShapeId = 0; + + if (!rHd.SeekToContent(rSt)) + return pRet; + + DffRecordHeader aRecHd; // the first atom has to be the SpContainer for the GroupObject + bool bOk = ReadDffRecordHeader(rSt, aRecHd); + if (bOk && aRecHd.nRecType == DFF_msofbtSpContainer) + { + mnFix16Angle = 0_deg100; + if (!aRecHd.SeekToBegOfRecord(rSt)) + return pRet; + pRet = ImportObj( rSt, rClientData, rClientRect, rGlobalChildRect, nCalledByGroup + 1, pShapeId ); + if ( pRet ) + { + Degree100 nGroupRotateAngle(0); + ShapeFlag nSpFlags = nGroupShapeFlags; + nGroupRotateAngle = mnFix16Angle; + + tools::Rectangle aClientRect( rClientRect ); + + tools::Rectangle aGlobalChildRect; + if ( !nCalledByGroup || rGlobalChildRect.IsEmpty() ) + aGlobalChildRect = GetGlobalChildAnchor( rHd, rSt, aClientRect ); + else + aGlobalChildRect = rGlobalChildRect; + + if ( ( nGroupRotateAngle > 4500_deg100 && nGroupRotateAngle <= 13500_deg100 ) + || ( nGroupRotateAngle > 22500_deg100 && nGroupRotateAngle <= 31500_deg100 ) ) + { + sal_Int32 nHalfWidth = ( aClientRect.GetWidth() + 1 ) >> 1; + sal_Int32 nHalfHeight = ( aClientRect.GetHeight() + 1 ) >> 1; + Point aTopLeft( aClientRect.Left() + nHalfWidth - nHalfHeight, + aClientRect.Top() + nHalfHeight - nHalfWidth ); + const tools::Long nRotatedWidth = aClientRect.GetHeight(); + const tools::Long nRotatedHeight = aClientRect.GetWidth(); + Size aNewSize(nRotatedWidth, nRotatedHeight); + tools::Rectangle aNewRect( aTopLeft, aNewSize ); + aClientRect = aNewRect; + } + + // now importing the inner objects of the group + if (!aRecHd.SeekToEndOfRecord(rSt)) + return pRet; + + while (rSt.good() && ( rSt.Tell() < rHd.GetRecEndFilePos())) + { + DffRecordHeader aRecHd2; + if (!ReadDffRecordHeader(rSt, aRecHd2)) + break; + if ( aRecHd2.nRecType == DFF_msofbtSpgrContainer ) + { + tools::Rectangle aGroupClientAnchor, aGroupChildAnchor; + GetGroupAnchors( aRecHd2, rSt, aGroupClientAnchor, aGroupChildAnchor, aClientRect, aGlobalChildRect ); + if (!aRecHd2.SeekToBegOfRecord(rSt)) + return pRet; + sal_Int32 nShapeId; + SdrObject* pTmp = ImportGroup( aRecHd2, rSt, rClientData, aGroupClientAnchor, aGroupChildAnchor, nCalledByGroup + 1, &nShapeId ); + if (pTmp) + { + SdrObjGroup* pGroup = dynamic_cast(pRet); + if (pGroup && pGroup->GetSubList()) + { + pGroup->GetSubList()->NbcInsertObject(pTmp); + if (nShapeId) + insertShapeId(nShapeId, pTmp); + } + else + FreeObj(rClientData, pTmp); + } + } + else if ( aRecHd2.nRecType == DFF_msofbtSpContainer ) + { + if (!aRecHd2.SeekToBegOfRecord(rSt)) + return pRet; + sal_Int32 nShapeId; + SdrObject* pTmp = ImportShape( aRecHd2, rSt, rClientData, aClientRect, aGlobalChildRect, nCalledByGroup + 1, &nShapeId ); + if (pTmp) + { + SdrObjGroup* pGroup = dynamic_cast(pRet); + if (pGroup && pGroup->GetSubList()) + { + pGroup->GetSubList()->NbcInsertObject(pTmp); + if (nShapeId) + insertShapeId(nShapeId, pTmp); + } + else + FreeObj(rClientData, pTmp); + } + } + if (!aRecHd2.SeekToEndOfRecord(rSt)) + return pRet; + } + + if ( nGroupRotateAngle ) + pRet->NbcRotate( aClientRect.Center(), nGroupRotateAngle ); + if ( nSpFlags & ShapeFlag::FlipV ) + { // BoundRect in aBoundRect + Point aLeft( aClientRect.Left(), ( aClientRect.Top() + aClientRect.Bottom() ) >> 1 ); + Point aRight( aLeft.X() + 1000, aLeft.Y() ); + pRet->NbcMirror( aLeft, aRight ); + } + if ( nSpFlags & ShapeFlag::FlipH ) + { // BoundRect in aBoundRect + Point aTop( ( aClientRect.Left() + aClientRect.Right() ) >> 1, aClientRect.Top() ); + Point aBottom( aTop.X(), aTop.Y() + 1000 ); + pRet->NbcMirror( aTop, aBottom ); + } + } + } + if (o3tl::make_unsigned(nCalledByGroup) < maPendingGroupData.size()) + { + // finalization for this group is pending, do it now + pRet = FinalizeObj(maPendingGroupData.back().first, pRet); + maPendingGroupData.pop_back(); + } + return pRet; +} + +SdrObject* SvxMSDffManager::ImportShape( const DffRecordHeader& rHd, SvStream& rSt, SvxMSDffClientData& rClientData, + tools::Rectangle& rClientRect, const tools::Rectangle& rGlobalChildRect, + int nCalledByGroup, sal_Int32* pShapeId ) +{ + if( pShapeId ) + *pShapeId = 0; + + if (!rHd.SeekToBegOfRecord(rSt)) + return nullptr; + + DffObjData aObjData( rHd, rClientRect, nCalledByGroup ); + + aObjData.bRotateTextWithShape = ( GetSvxMSDffSettings() & SVXMSDFF_SETTINGS_IMPORT_EXCEL ) == 0; + maShapeRecords.Consume( rSt ); + if( maShapeRecords.SeekToContent( rSt, + DFF_msofbtUDefProp ) ) + { + sal_uInt32 nBytesLeft = maShapeRecords.Current()->nRecLen; + while( 5 < nBytesLeft ) + { + sal_uInt16 nPID(0); + rSt.ReadUInt16(nPID); + if (!rSt.good()) + break; + sal_uInt32 nUDData(0); + rSt.ReadUInt32(nUDData); + if (!rSt.good()) + break; + if (nPID == 447) + { + mbRotateGranientFillWithAngle = nUDData & 0x20; + break; + } + nBytesLeft -= 6; + } + } + aObjData.bShapeType = maShapeRecords.SeekToContent( rSt, DFF_msofbtSp ); + if ( aObjData.bShapeType ) + { + sal_uInt32 temp(0); + rSt.ReadUInt32( aObjData.nShapeId ) + .ReadUInt32( temp ); + aObjData.nSpFlags = ShapeFlag(temp); + aObjData.eShapeType = static_cast(maShapeRecords.Current()->nRecInstance); + } + else + { + aObjData.nShapeId = 0; + aObjData.nSpFlags = ShapeFlag::NONE; + aObjData.eShapeType = mso_sptNil; + } + + if( pShapeId ) + *pShapeId = aObjData.nShapeId; + + aObjData.bOpt = maShapeRecords.SeekToContent( rSt, DFF_msofbtOPT, SEEK_FROM_CURRENT_AND_RESTART ); + if ( aObjData.bOpt ) + { + if (!maShapeRecords.Current()->SeekToBegOfRecord(rSt)) + return nullptr; +#ifdef DBG_AUTOSHAPE + ReadPropSet( rSt, &rClientData, (sal_uInt32)aObjData.eShapeType ); +#else + ReadPropSet( rSt, &rClientData ); +#endif + } + else + { + InitializePropSet( DFF_msofbtOPT ); // get the default PropSet + static_cast(this)->mnFix16Angle = 0_deg100; + } + + aObjData.bOpt2 = maShapeRecords.SeekToContent( rSt, DFF_msofbtUDefProp, SEEK_FROM_CURRENT_AND_RESTART ); + if ( aObjData.bOpt2 ) + { + maShapeRecords.Current()->SeekToBegOfRecord( rSt ); + pSecPropSet.reset( new DffPropertyReader( *this ) ); + pSecPropSet->ReadPropSet( rSt, nullptr ); + } + + aObjData.bChildAnchor = maShapeRecords.SeekToContent( rSt, DFF_msofbtChildAnchor, SEEK_FROM_CURRENT_AND_RESTART ); + if ( aObjData.bChildAnchor ) + { + sal_Int32 l(0), o(0), r(0), u(0); + rSt.ReadInt32( l ).ReadInt32( o ).ReadInt32( r ).ReadInt32( u ); + Scale( l ); + Scale( o ); + Scale( r ); + Scale( u ); + aObjData.aChildAnchor = tools::Rectangle( l, o, r, u ); + sal_Int32 nWidth, nHeight; + if (!rGlobalChildRect.IsEmpty() && !rClientRect.IsEmpty() && rGlobalChildRect.GetWidth() && rGlobalChildRect.GetHeight() && + !o3tl::checked_sub(r, l, nWidth) && !o3tl::checked_sub(u, o, nHeight)) + { + double fXScale = static_cast(rClientRect.GetWidth()) / static_cast(rGlobalChildRect.GetWidth()); + double fYScale = static_cast(rClientRect.GetHeight()) / static_cast(rGlobalChildRect.GetHeight()); + double fl = ( ( l - rGlobalChildRect.Left() ) * fXScale ) + rClientRect.Left(); + double fo = ( ( o - rGlobalChildRect.Top() ) * fYScale ) + rClientRect.Top(); + double fWidth = nWidth * fXScale; + double fHeight = nHeight * fYScale; + aObjData.aChildAnchor = tools::Rectangle( Point( fl, fo ), Size( fWidth + 1, fHeight + 1 ) ); + } + } + + aObjData.bClientAnchor = maShapeRecords.SeekToContent( rSt, DFF_msofbtClientAnchor, SEEK_FROM_CURRENT_AND_RESTART ); + if ( aObjData.bClientAnchor ) + ProcessClientAnchor2( rSt, *maShapeRecords.Current(), aObjData ); + + if ( aObjData.bChildAnchor ) + aObjData.aBoundRect = aObjData.aChildAnchor; + + if ( aObjData.nSpFlags & ShapeFlag::Background ) + aObjData.aBoundRect = tools::Rectangle( Point(), Size( 1, 1 ) ); + + SdrObjectUniquePtr xRet; + + tools::Rectangle aTextRect; + if ( !aObjData.aBoundRect.IsEmpty() ) + { // apply rotation to the BoundingBox BEFORE an object has been generated + if( mnFix16Angle ) + { + Degree100 nAngle = mnFix16Angle; + if ( ( nAngle > 4500_deg100 && nAngle <= 13500_deg100 ) || ( nAngle > 22500_deg100 && nAngle <= 31500_deg100 ) ) + { + sal_Int32 nHalfWidth = ( aObjData.aBoundRect.GetWidth() + 1 ) >> 1; + sal_Int32 nHalfHeight = ( aObjData.aBoundRect.GetHeight() + 1 ) >> 1; + Point aTopLeft( aObjData.aBoundRect.Left() + nHalfWidth - nHalfHeight, + aObjData.aBoundRect.Top() + nHalfHeight - nHalfWidth ); + Size aNewSize( aObjData.aBoundRect.GetHeight(), aObjData.aBoundRect.GetWidth() ); + tools::Rectangle aNewRect( aTopLeft, aNewSize ); + aObjData.aBoundRect = aNewRect; + } + } + aTextRect = aObjData.aBoundRect; + bool bGraphic = IsProperty( DFF_Prop_pib ) || + IsProperty( DFF_Prop_pibName ) || + IsProperty( DFF_Prop_pibFlags ); + + if ( aObjData.nSpFlags & ShapeFlag::Group ) + { + xRet.reset(new SdrObjGroup(*pSdrModel)); + /* After CWS aw033 has been integrated, an empty group object + cannot store its resulting bounding rectangle anymore. We have + to return this rectangle via rClientRect now, but only, if + caller has not passed an own bounding ractangle. */ + if ( rClientRect.IsEmpty() ) + rClientRect = aObjData.aBoundRect; + nGroupShapeFlags = aObjData.nSpFlags; + } + else if ( ( aObjData.eShapeType != mso_sptNil ) || IsProperty( DFF_Prop_pVertices ) || bGraphic ) + { + SfxItemSet aSet( pSdrModel->GetItemPool() ); + + bool bIsConnector = ( ( aObjData.eShapeType >= mso_sptStraightConnector1 ) && ( aObjData.eShapeType <= mso_sptCurvedConnector5 ) ); + Degree100 nObjectRotation = mnFix16Angle; + ShapeFlag nSpFlags = aObjData.nSpFlags; + + if ( bGraphic ) + { + if (!mbSkipImages) { + xRet.reset(ImportGraphic(rSt, aSet, aObjData)); // SJ: #68396# is no longer true (fixed in ppt2000) + ApplyAttributes( rSt, aSet, aObjData ); + xRet->SetMergedItemSet(aSet); + } + } + else if ( aObjData.eShapeType == mso_sptLine && !( GetPropertyValue( DFF_Prop_fc3DLightFace, 0 ) & 8 ) ) + { + basegfx::B2DPolygon aPoly; + aPoly.append(basegfx::B2DPoint(aObjData.aBoundRect.Left(), aObjData.aBoundRect.Top())); + aPoly.append(basegfx::B2DPoint(aObjData.aBoundRect.Right(), aObjData.aBoundRect.Bottom())); + xRet.reset(new SdrPathObj( + *pSdrModel, + SdrObjKind::Line, + basegfx::B2DPolyPolygon(aPoly))); + ApplyAttributes( rSt, aSet, aObjData ); + xRet->SetMergedItemSet(aSet); + } + else + { + if ( GetCustomShapeContent( aObjData.eShapeType ) || IsProperty( DFF_Prop_pVertices ) ) + { + + ApplyAttributes( rSt, aSet, aObjData ); + + xRet.reset(new SdrObjCustomShape(*pSdrModel)); + + sal_uInt32 ngtextFStrikethrough = GetPropertyValue( DFF_Prop_gtextFStrikethrough, 0 ); + bool bIsFontwork = ( ngtextFStrikethrough & 0x4000 ) != 0; + + // in case of a FontWork, the text is set by the escher import + if ( bIsFontwork ) + { + OUString aObjectText; + OUString aFontName; + + if ( SeekToContent( DFF_Prop_gtextFont, rSt ) ) + { + SvxFontItem aLatin(EE_CHAR_FONTINFO), aAsian(EE_CHAR_FONTINFO_CJK), aComplex(EE_CHAR_FONTINFO_CTL); + GetDefaultFonts( aLatin, aAsian, aComplex ); + + aFontName = MSDFFReadZString( rSt, GetPropertyValue( DFF_Prop_gtextFont, 0 ), true ); + aSet.Put( SvxFontItem( aLatin.GetFamily(), aFontName, aLatin.GetStyleName(), + PITCH_DONTKNOW, RTL_TEXTENCODING_DONTKNOW, EE_CHAR_FONTINFO )); + aSet.Put( SvxFontItem( aLatin.GetFamily(), aFontName, aLatin.GetStyleName(), + PITCH_DONTKNOW, RTL_TEXTENCODING_DONTKNOW, EE_CHAR_FONTINFO_CJK ) ); + aSet.Put( SvxFontItem( aLatin.GetFamily(), aFontName, aLatin.GetStyleName(), + PITCH_DONTKNOW, RTL_TEXTENCODING_DONTKNOW, EE_CHAR_FONTINFO_CTL ) ); + } + + // SJ: applying fontattributes for Fontwork : + if ( IsHardAttribute( DFF_Prop_gtextFItalic ) ) + aSet.Put( SvxPostureItem( ( ngtextFStrikethrough & 0x0010 ) != 0 ? ITALIC_NORMAL : ITALIC_NONE, EE_CHAR_ITALIC ) ); + + if ( IsHardAttribute( DFF_Prop_gtextFBold ) ) + aSet.Put( SvxWeightItem( ( ngtextFStrikethrough & 0x0020 ) != 0 ? WEIGHT_BOLD : WEIGHT_NORMAL, EE_CHAR_WEIGHT ) ); + + // SJ TODO: Vertical Writing is not correct, instead + // this should be replaced through "CharacterRotation" + // by 90 degrees, therefore a new Item has to be + // supported by svx core, api and xml file format + static_cast(xRet.get())->SetVerticalWriting( ( ngtextFStrikethrough & 0x2000 ) != 0 ); + + if ( SeekToContent( DFF_Prop_gtextUNICODE, rSt ) ) + { + aObjectText = MSDFFReadZString( rSt, GetPropertyValue( DFF_Prop_gtextUNICODE, 0 ), true ); + ReadObjText(aObjectText, xRet.get()); + } + + auto eGeoTextAlign = GetPropertyValue(DFF_Prop_gtextAlign, mso_alignTextCenter); + { + SdrTextHorzAdjust eHorzAdjust; + switch( eGeoTextAlign ) + { + case mso_alignTextLetterJust : + case mso_alignTextWordJust : + case mso_alignTextStretch : eHorzAdjust = SDRTEXTHORZADJUST_BLOCK; break; + default: + case mso_alignTextInvalid : + case mso_alignTextCenter : eHorzAdjust = SDRTEXTHORZADJUST_CENTER; break; + case mso_alignTextLeft : eHorzAdjust = SDRTEXTHORZADJUST_LEFT; break; + case mso_alignTextRight : eHorzAdjust = SDRTEXTHORZADJUST_RIGHT; break; + } + aSet.Put( SdrTextHorzAdjustItem( eHorzAdjust ) ); + + drawing::TextFitToSizeType eFTS = drawing::TextFitToSizeType_NONE; + if ( eGeoTextAlign == mso_alignTextStretch ) + eFTS = drawing::TextFitToSizeType_ALLLINES; + aSet.Put( SdrTextFitToSizeTypeItem( eFTS ) ); + } + if ( IsProperty( DFF_Prop_gtextSpacing ) ) + { + sal_Int32 nTextWidth = GetPropertyValue( DFF_Prop_gtextSpacing, 1 << 16 ) / 655; + if ( nTextWidth != 100 ) + aSet.Put( SvxCharScaleWidthItem( static_cast(nTextWidth), EE_CHAR_FONTWIDTH ) ); + } + if ( ngtextFStrikethrough & 0x1000 ) // SJ: Font Kerning On ? + aSet.Put( SvxKerningItem( 1, EE_CHAR_KERNING ) ); + + // #i119496# the resize autoshape to fit text attr of word art in MS PPT is always false + aSet.Put(makeSdrTextAutoGrowHeightItem(false)); + aSet.Put(makeSdrTextAutoGrowWidthItem(false)); + + bool bWithPadding = !( ngtextFStrikethrough & use_gtextFBestFit + && ngtextFStrikethrough & use_gtextFShrinkFit + && ngtextFStrikethrough & use_gtextFStretch + && ngtextFStrikethrough & gtextFBestFit + && ngtextFStrikethrough & gtextFShrinkFit + && ngtextFStrikethrough & gtextFStretch ); + + if ( bWithPadding ) + { + // trim, remove additional space + VclPtr pDevice = VclPtr::Create(); + vcl::Font aFont = pDevice->GetFont(); + aFont.SetFamilyName( aFontName ); + aFont.SetFontSize( Size( 0, 96 ) ); + pDevice->SetFont( aFont ); + + auto nTextWidth = pDevice->GetTextWidth( aObjectText ); + OUString aObjName = GetPropertyString( DFF_Prop_wzName, rSt ); + if ( nTextWidth && aObjData.eShapeType == mso_sptTextPlainText + && aObjName.match( "PowerPlusWaterMarkObject" ) ) + { + double fRatio = static_cast(pDevice->GetTextHeight()) / nTextWidth; + sal_Int32 nNewHeight = fRatio * aObjData.aBoundRect.getWidth(); + sal_Int32 nPaddingY = aObjData.aBoundRect.getHeight() - nNewHeight; + + if ( nPaddingY > 0 ) + aObjData.aBoundRect.setHeight( nNewHeight ); + } + } + } + xRet->SetMergedItemSet( aSet ); + + // sj: taking care of rtl, ltr. In case of fontwork mso. seems not to be able to set + // proper text directions, instead the text default is depending to the string. + // so we have to calculate the a text direction from string: + if ( bIsFontwork ) + { + OutlinerParaObject* pParaObj = static_cast(xRet.get())->GetOutlinerParaObject(); + if ( pParaObj ) + { + SdrOutliner& rOutliner = static_cast(xRet.get())->ImpGetDrawOutliner(); + rOutliner.SetStyleSheetPool(static_cast< SfxStyleSheetPool* >(xRet->getSdrModelFromSdrObject().GetStyleSheetPool())); + bool bOldUpdateMode = rOutliner.SetUpdateLayout( false ); + rOutliner.SetText( *pParaObj ); + ScopedVclPtrInstance< VirtualDevice > pVirDev(DeviceFormat::DEFAULT); + pVirDev->SetMapMode(MapMode(MapUnit::Map100thMM)); + sal_Int32 i, nParagraphs = rOutliner.GetParagraphCount(); + if ( nParagraphs ) + { + bool bCreateNewParaObject = false; + for ( i = 0; i < nParagraphs; i++ ) + { + OUString aString(rOutliner.GetText(rOutliner.GetParagraph(i))); + bool bIsRTL = pVirDev->GetTextIsRTL(aString, 0, aString.getLength()); + if ( bIsRTL ) + { + SfxItemSet aSet2( rOutliner.GetParaAttribs( i ) ); + aSet2.Put( SvxFrameDirectionItem( SvxFrameDirection::Horizontal_RL_TB, EE_PARA_WRITINGDIR ) ); + rOutliner.SetParaAttribs( i, aSet2 ); + bCreateNewParaObject = true; + } + } + if ( bCreateNewParaObject ) + { + std::optional pNewText = rOutliner.CreateParaObject(); + rOutliner.Init( OutlinerMode::TextObject ); + static_cast(xRet.get())->NbcSetOutlinerParaObject( std::move(pNewText) ); + } + } + rOutliner.Clear(); + rOutliner.SetUpdateLayout( bOldUpdateMode ); + } + } + + // mso_sptArc special treating + // tdf#124029: A new custom shape is generated from prototype 'msoArc'. Values, which are + // read here, are adapted and merged. The shape type is changed, so this code + // applies only if importing arcs from MS Office. + if ( aObjData.eShapeType == mso_sptArc ) + { + static const OUStringLiteral sAdjustmentValues( u"AdjustmentValues" ); + static const OUStringLiteral sViewBox( u"ViewBox" ); + static const OUStringLiteral sPath( u"Path" ); + SdrCustomShapeGeometryItem aGeometryItem( static_cast(xRet.get())->GetMergedItem( SDRATTR_CUSTOMSHAPE_GEOMETRY ) ); + PropertyValue aPropVal; + + // The default arc goes form -90deg to 0deg. Replace general defaults used + // when read from stream with this specific values. + double fStartAngle(-90.0); + double fEndAngle(0.0); + css::uno::Sequence< css::drawing::EnhancedCustomShapeAdjustmentValue > seqAdjustmentValues; + const uno::Any* pAny = aGeometryItem.GetPropertyValueByName(sAdjustmentValues); + if (pAny && (*pAny >>= seqAdjustmentValues) && seqAdjustmentValues.getLength() > 1) + { + auto pseqAdjustmentValues = seqAdjustmentValues.getArray(); + if (seqAdjustmentValues[0].State == css::beans::PropertyState_DEFAULT_VALUE) + { + pseqAdjustmentValues[0].Value <<= -90.0; + pseqAdjustmentValues[0].State = com::sun::star::beans::PropertyState_DIRECT_VALUE; + } + if (seqAdjustmentValues[1].State == css::beans::PropertyState_DEFAULT_VALUE) + { + pseqAdjustmentValues[1].Value <<= 0.0; + pseqAdjustmentValues[1].State = com::sun::star::beans::PropertyState_DIRECT_VALUE; + } + seqAdjustmentValues[0].Value >>= fStartAngle; + seqAdjustmentValues[1].Value >>= fEndAngle; + aPropVal.Name = sAdjustmentValues; + aPropVal.Value <<= seqAdjustmentValues; + aGeometryItem.SetPropertyValue(aPropVal); + } + + // arc first command is always wr -- clockwise arc + // the parameters are : (left,top),(right,bottom),start(x,y),end(x,y) + // The left/top vertex of the frame rectangle of the sector is the origin + // of the shape internal coordinate system in MS Office. The default arc + // has an ellipse frame rectangle with LT(-21600,0) and + // RB(21600,43200) in this coordinate system. + basegfx::B2DRectangle aEllipseRect_MS(-21600.0, 0.0, 21600.0, 43200.0); + css::uno::Sequence< css::drawing::EnhancedCustomShapeParameterPair> seqCoordinates; + pAny = aGeometryItem.GetPropertyValueByName( sPath, "Coordinates" ); + if (pAny && (*pAny >>= seqCoordinates) && (seqCoordinates.getLength() >= 2)) + { + auto const nL + = *o3tl::doAccess(seqCoordinates[0].First.Value); + auto const nT + = *o3tl::doAccess(seqCoordinates[0].Second.Value); + auto const nR + = *o3tl::doAccess(seqCoordinates[1].First.Value); + auto const nB + = *o3tl::doAccess(seqCoordinates[1].Second.Value); + aEllipseRect_MS = basegfx::B2DRectangle(nL, nT, nR, nB); + } + + // MS Office uses the pie frame rectangle as reference for outer position + // and size of the shape and for text in the shape. We can get this rectangle + // from imported viewBox or from the arc geometry. + basegfx::B2DRectangle aPieRect_MS(0.0 , 0.0, 21600.0, 21600.0); + pAny = aGeometryItem.GetPropertyValueByName(sPath,sViewBox); + css::awt::Rectangle aImportedViewBox; + if (pAny && (*pAny >>= aImportedViewBox)) + { + aPieRect_MS = basegfx::B2DRectangle( aImportedViewBox.X, + aImportedViewBox.Y, + aImportedViewBox.X + aImportedViewBox.Width, + aImportedViewBox.Y + aImportedViewBox.Height); + } + else + { + double fRadStartAngle(basegfx::deg2rad(NormAngle360(fStartAngle))); + double fRadEndAngle(basegfx::deg2rad(NormAngle360(fEndAngle))); + basegfx::B2DPoint aCenter(aEllipseRect_MS.getCenter()); + basegfx::B2DPolygon aTempPie( + basegfx::utils::createPolygonFromEllipseSegment( + aCenter, + aEllipseRect_MS.getWidth() * 0.5, + aEllipseRect_MS.getHeight() * 0.5, + fRadStartAngle, + fRadEndAngle)); + aTempPie.append(aCenter); + aPieRect_MS = aTempPie.getB2DRange(); + } + + // MS Office uses for mso_sptArc a frame rectangle (=resize handles) + // which encloses only the sector, LibreOffice uses for custom shapes as + // default a frame rectangle, which encloses the entire ellipse. That would + // result in wrong positions in Writer and Calc, see tdf#124029. + // We workaround this problem, by setting a suitable viewBox. + bool bIsImportPPT(GetSvxMSDffSettings() & SVXMSDFF_SETTINGS_IMPORT_PPT); + if (bIsImportPPT || aPieRect_MS.getWidth() == 0 || aPieRect_MS.getHeight() == 0) + { // clear item, so that default from EnhancedCustomShapeGeometry is used + aGeometryItem.ClearPropertyValue(sViewBox); + } + else + { + double fX((aPieRect_MS.getMinX() - aEllipseRect_MS.getMinX()) / 2.0); + double fY((aPieRect_MS.getMinY() - aEllipseRect_MS.getMinY()) / 2.0); + css::awt::Rectangle aViewBox_LO; // in LO coordinate system + aViewBox_LO.X = static_cast(fX); + aViewBox_LO.Y = static_cast(fY); + aViewBox_LO.Width = static_cast(aPieRect_MS.getWidth() / 2.0); + aViewBox_LO.Height = static_cast(aPieRect_MS.getHeight() / 2.0); + aPropVal.Name = sViewBox; + aPropVal.Value <<= aViewBox_LO; + aGeometryItem.SetPropertyValue(aPropVal); + } + + // aObjData.aBoundRect contains position and size of the sector in (outer) + // logic coordinates, e.g. for PPT in 1/100 mm, for Word in twips. + // For Impress the default viewBox is used, so adapt aObjData.aBoundRect. + tools::Rectangle aOldBoundRect(aObjData.aBoundRect); // backup, needed later on + if (bIsImportPPT) + { + double fLogicXOfs(0.0); // LogicLeft_LO = LogicLeft_MS + fXLogicOfs + double fLogicYOfs(0.0); + double fLogicPieWidth(aObjData.aBoundRect.getWidth()); + double fLogicPieHeight(aObjData.aBoundRect.getHeight()); + double fLogicEllipseWidth(0.0); // to be LogicWidth_LO + double fLogicEllipseHeight(0.0); + if (aPieRect_MS.getWidth()) + { + // fXScale = ratio 'logic length' : 'shape internal length' + double fXScale = fLogicPieWidth / aPieRect_MS.getWidth(); + if (nSpFlags & ShapeFlag::FlipH) + fLogicXOfs = (aPieRect_MS.getMaxX() - aEllipseRect_MS.getMaxX()) * fXScale; + else + fLogicXOfs = (aEllipseRect_MS.getMinX() - aPieRect_MS.getMinX()) * fXScale; + fLogicEllipseWidth = aEllipseRect_MS.getWidth() * fXScale; + } + if (aPieRect_MS.getHeight()) + { + double fYScale = fLogicPieHeight / aPieRect_MS.getHeight(); + if (nSpFlags & ShapeFlag::FlipV) + fLogicYOfs = (aPieRect_MS.getMaxY() - aEllipseRect_MS.getMaxY()) * fYScale; + else + fLogicYOfs = (aEllipseRect_MS.getMinY() - aPieRect_MS.getMinY()) * fYScale; + fLogicEllipseHeight = aEllipseRect_MS.getHeight() * fYScale; + } + aObjData.aBoundRect = tools::Rectangle( + Point(aOldBoundRect.Left() + static_cast(fLogicXOfs), + aOldBoundRect.Top() + static_cast(fLogicYOfs)), + Size(static_cast(fLogicEllipseWidth), + static_cast(fLogicEllipseHeight))); + } + // else nothing to do. aObjData.aBoundRect corresponds to changed viewBox. + + // creating the text frame -> scaling into (0,0),(21600,21600) destination coordinate system + double fTextFrameScaleX = 0.0; + double fTextFrameScaleY = 0.0; + if (aEllipseRect_MS.getWidth()) + fTextFrameScaleX = 21600.0 / aEllipseRect_MS.getWidth(); + if (aEllipseRect_MS.getHeight()) + fTextFrameScaleY = 21600.0 / aEllipseRect_MS.getHeight(); + + sal_Int32 nLeft = static_cast((aPieRect_MS.getMinX() - aEllipseRect_MS.getMinX()) * fTextFrameScaleX ); + sal_Int32 nTop = static_cast((aPieRect_MS.getMinY() - aEllipseRect_MS.getMinY()) * fTextFrameScaleY ); + sal_Int32 nRight = static_cast((aPieRect_MS.getMaxX() - aEllipseRect_MS.getMinX()) * fTextFrameScaleX ); + sal_Int32 nBottom= static_cast((aPieRect_MS.getMaxY() - aEllipseRect_MS.getMinY()) * fTextFrameScaleY ); + css::uno::Sequence< css::drawing::EnhancedCustomShapeTextFrame > aTextFrame( 1 ); + auto pTextFrame = aTextFrame.getArray(); + EnhancedCustomShape2d::SetEnhancedCustomShapeParameter( pTextFrame[ 0 ].TopLeft.First, nLeft ); + EnhancedCustomShape2d::SetEnhancedCustomShapeParameter( pTextFrame[ 0 ].TopLeft.Second, nTop ); + EnhancedCustomShape2d::SetEnhancedCustomShapeParameter( pTextFrame[ 0 ].BottomRight.First, nRight ); + EnhancedCustomShape2d::SetEnhancedCustomShapeParameter( pTextFrame[ 0 ].BottomRight.Second,nBottom ); + PropertyValue aProp; + aProp.Name = "TextFrames"; + aProp.Value <<= aTextFrame; + aGeometryItem.SetPropertyValue( sPath, aProp ); + + // sj: taking care of the different rotation points, since the new arc is having a bigger snaprect + if ( mnFix16Angle ) + { + Degree100 nAngle = mnFix16Angle; + if ( nSpFlags & ShapeFlag::FlipH ) + nAngle = 36000_deg100 - nAngle; + if ( nSpFlags & ShapeFlag::FlipV ) + nAngle = -nAngle; + double a = toRadians(nAngle); + double ss = sin( a ); + double cc = cos( a ); + Point aP1( aOldBoundRect.TopLeft() ); + Point aC1( aObjData.aBoundRect.Center() ); + Point aP2( aOldBoundRect.TopLeft() ); + Point aC2( aOldBoundRect.Center() ); + RotatePoint( aP1, aC1, ss, cc ); + RotatePoint( aP2, aC2, ss, cc ); + aObjData.aBoundRect.Move( aP2.X() - aP1.X(), aP2.Y() - aP1.Y() ); + } + + // clearing items, so MergeDefaultAttributes will set the corresponding + // defaults from EnhancedCustomShapeGeometry + aGeometryItem.ClearPropertyValue( "Handles" ); + aGeometryItem.ClearPropertyValue( "Equations" ); + aGeometryItem.ClearPropertyValue( sPath ); + + static_cast(xRet.get())->SetMergedItem( aGeometryItem ); + static_cast(xRet.get())->MergeDefaultAttributes(); + + // now setting a new name, so the above correction is only done once when importing from ms + SdrCustomShapeGeometryItem aGeoName( static_cast(xRet.get())->GetMergedItem( SDRATTR_CUSTOMSHAPE_GEOMETRY ) ); + aPropVal.Name = "Type"; + aPropVal.Value <<= OUString( "mso-spt100" ); + aGeoName.SetPropertyValue( aPropVal ); + static_cast(xRet.get())->SetMergedItem( aGeoName ); + } + else + static_cast(xRet.get())->MergeDefaultAttributes(); + + xRet->SetSnapRect( aObjData.aBoundRect ); + EnhancedCustomShape2d aCustomShape2d(static_cast(*xRet)); + aTextRect = aCustomShape2d.GetTextRect(); + + if( bIsConnector ) + { + if( nObjectRotation ) + xRet->NbcRotate( aObjData.aBoundRect.Center(), nObjectRotation ); + // mirrored horizontally? + if ( nSpFlags & ShapeFlag::FlipH ) + { + tools::Rectangle aBndRect(xRet->GetSnapRect()); + Point aTop( ( aBndRect.Left() + aBndRect.Right() ) >> 1, aBndRect.Top() ); + Point aBottom( aTop.X(), aTop.Y() + 1000 ); + xRet->NbcMirror( aTop, aBottom ); + } + // mirrored vertically? + if ( nSpFlags & ShapeFlag::FlipV ) + { + tools::Rectangle aBndRect(xRet->GetSnapRect()); + Point aLeft( aBndRect.Left(), ( aBndRect.Top() + aBndRect.Bottom() ) >> 1 ); + Point aRight( aLeft.X() + 1000, aLeft.Y() ); + xRet->NbcMirror( aLeft, aRight ); + } + basegfx::B2DPolyPolygon aPoly( static_cast(xRet.get())->GetLineGeometry( true ) ); + + xRet.reset(new SdrEdgeObj(*pSdrModel)); + ApplyAttributes( rSt, aSet, aObjData ); + xRet->SetLogicRect( aObjData.aBoundRect ); + xRet->SetMergedItemSet(aSet); + + // connectors + auto eConnectorStyle = GetPropertyValue(DFF_Prop_cxstyle, mso_cxstyleStraight); + + static_cast(xRet.get())->ConnectToNode(true, nullptr); + static_cast(xRet.get())->ConnectToNode(false, nullptr); + + Point aPoint1( aObjData.aBoundRect.TopLeft() ); + Point aPoint2( aObjData.aBoundRect.BottomRight() ); + + // pay attention to the rotations + if ( nObjectRotation ) + { + double a = toRadians(nObjectRotation); + Point aCenter( aObjData.aBoundRect.Center() ); + double ss = sin(a); + double cc = cos(a); + + RotatePoint(aPoint1, aCenter, ss, cc); + RotatePoint(aPoint2, aCenter, ss, cc); + + // #i120437# reset rotation, it is part of the path and shall not be applied again + nObjectRotation = 0_deg100; + } + + // rotate/mirror line within the area as we need it + if ( nSpFlags & ShapeFlag::FlipH ) + { + sal_Int32 n = aPoint1.X(); + aPoint1.setX( aPoint2.X() ); + aPoint2.setX( n ); + + // #i120437# reset hor flip + nSpFlags &= ~ShapeFlag::FlipH; + } + if ( nSpFlags & ShapeFlag::FlipV ) + { + sal_Int32 n = aPoint1.Y(); + aPoint1.setY( aPoint2.Y() ); + aPoint2.setY( n ); + + // #i120437# reset ver flip + nSpFlags &= ~ShapeFlag::FlipV; + } + + xRet->NbcSetPoint(aPoint1, 0); // start point + xRet->NbcSetPoint(aPoint2, 1); // endpoint + + sal_Int32 n1HorzDist, n1VertDist, n2HorzDist, n2VertDist; + n1HorzDist = n1VertDist = n2HorzDist = n2VertDist = 0; + switch( eConnectorStyle ) + { + case mso_cxstyleBent: + { + aSet.Put( SdrEdgeKindItem( SdrEdgeKind::OrthoLines ) ); + n1HorzDist = n1VertDist = n2HorzDist = n2VertDist = 630; + } + break; + case mso_cxstyleCurved: + aSet.Put( SdrEdgeKindItem( SdrEdgeKind::Bezier ) ); + break; + default: // mso_cxstyleStraight || mso_cxstyleNone + aSet.Put( SdrEdgeKindItem( SdrEdgeKind::OneLine ) ); + break; + } + aSet.Put( SdrEdgeNode1HorzDistItem( n1HorzDist ) ); + aSet.Put( SdrEdgeNode1VertDistItem( n1VertDist ) ); + aSet.Put( SdrEdgeNode2HorzDistItem( n2HorzDist ) ); + aSet.Put( SdrEdgeNode2VertDistItem( n2VertDist ) ); + + static_cast(xRet.get())->SetEdgeTrackPath( aPoly ); + xRet->SetMergedItemSet(aSet); + } + if ( aObjData.eShapeType == mso_sptLine ) + { + xRet->SetMergedItemSet(aSet); + static_cast(xRet.get())->MergeDefaultAttributes(); + } + } + } + + if (xRet) + { + if( nObjectRotation ) + xRet->NbcRotate( aObjData.aBoundRect.Center(), nObjectRotation ); + // mirrored horizontally? + if ( nSpFlags & ShapeFlag::FlipH ) + { + tools::Rectangle aBndRect(xRet->GetSnapRect()); + Point aTop( ( aBndRect.Left() + aBndRect.Right() ) >> 1, aBndRect.Top() ); + Point aBottom( aTop.X(), aTop.Y() + 1000 ); + xRet->NbcMirror(aTop, aBottom); + } + // mirrored vertically? + if ( nSpFlags & ShapeFlag::FlipV ) + { + tools::Rectangle aBndRect(xRet->GetSnapRect()); + Point aLeft( aBndRect.Left(), ( aBndRect.Top() + aBndRect.Bottom() ) >> 1 ); + Point aRight( aLeft.X() + 1000, aLeft.Y() ); + xRet->NbcMirror(aLeft, aRight); + } + } + } + } + + // #i51348# #118052# name of the shape + if (xRet) + { + OUString aObjName = GetPropertyString( DFF_Prop_wzName, rSt ); + if( !aObjName.isEmpty() ) + xRet->SetName(aObjName); + } + + xRet.reset(ProcessObj(rSt, aObjData, rClientData, aTextRect, xRet.release())); + + if (xRet) + { + sal_Int32 nGroupProperties( GetPropertyValue( DFF_Prop_fPrint, 0 ) ); + const bool bVisible = ( ( nGroupProperties & 2 ) == 0 ); + xRet->SetVisible( bVisible ); + // In Excel hidden means not printed + if ( !bVisible ) + { + xRet->SetPrintable(false); + } + else + { + // This property isn't used in Excel anymore, leaving it for legacy reasons + xRet->SetPrintable( ( nGroupProperties & 1 ) != 0 ); + } + } + + //Import alt text as description + if (xRet && SeekToContent(DFF_Prop_wzDescription, rSt)) + { + OUString aAltText = MSDFFReadZString(rSt, GetPropertyValue(DFF_Prop_wzDescription, 0), true); + xRet->SetDescription(aAltText); + } + + // If this shape opens a new group, push back its object data because + // finalization will be called when nested objects have been imported; + // otherwise, just finalize here + if (o3tl::make_unsigned(nCalledByGroup) > maPendingGroupData.size()) + { + auto xHdClone = std::make_shared(aObjData.rSpHd); + maPendingGroupData.emplace_back(DffObjData(xHdClone, aObjData), xHdClone ); + } + else + { + xRet.reset(FinalizeObj(aObjData, xRet.release())); + } + return xRet.release(); +} + +tools::Rectangle SvxMSDffManager::GetGlobalChildAnchor( const DffRecordHeader& rHd, SvStream& rSt, tools::Rectangle& aClientRect ) +{ + tools::Rectangle aChildAnchor; + if (!rHd.SeekToContent(rSt)) + return aChildAnchor; + + bool bIsClientRectRead = false; + while ( ( rSt.GetError() == ERRCODE_NONE ) && ( rSt.Tell() < rHd.GetRecEndFilePos() ) ) + { + DffRecordHeader aShapeHd; + if (!ReadDffRecordHeader(rSt, aShapeHd)) + break; + if ( ( aShapeHd.nRecType == DFF_msofbtSpContainer ) || + ( aShapeHd.nRecType == DFF_msofbtSpgrContainer ) ) + { + DffRecordHeader aShapeHd2( aShapeHd ); + if ( aShapeHd.nRecType == DFF_msofbtSpgrContainer ) + ReadDffRecordHeader( rSt, aShapeHd2 ); + while (rSt.good() && rSt.Tell() < aShapeHd2.GetRecEndFilePos()) + { + DffRecordHeader aShapeAtom; + if (!ReadDffRecordHeader(rSt, aShapeAtom)) + break; + + if ( aShapeAtom.nRecType == DFF_msofbtClientAnchor ) + { + if ( GetSvxMSDffSettings() & SVXMSDFF_SETTINGS_IMPORT_PPT ) + { + sal_Int32 l(0), t(0), r(0), b(0); + if ( aShapeAtom.nRecLen == 16 ) + { + rSt.ReadInt32( l ).ReadInt32( t ).ReadInt32( r ).ReadInt32( b ); + } + else + { + sal_Int16 ls(0), ts(0), rs(0), bs(0); + rSt.ReadInt16( ts ).ReadInt16( ls ).ReadInt16( rs ).ReadInt16( bs ); // the order of coordinates is a bit strange... + l = ls; + t = ts; + r = rs; + b = bs; + } + Scale( l ); + Scale( t ); + Scale( r ); + Scale( b ); + if ( bIsClientRectRead ) + { + tools::Rectangle aChild( l, t, r, b ); + aChildAnchor.Union( aChild ); + } + else + { + aClientRect = tools::Rectangle( l, t, r, b ); + bIsClientRectRead = true; + } + } + break; + } + else if ( aShapeAtom.nRecType == DFF_msofbtChildAnchor ) + { + sal_Int32 l(0), o(0), r(0), u(0); + rSt.ReadInt32( l ).ReadInt32( o ).ReadInt32( r ).ReadInt32( u ); + Scale( l ); + Scale( o ); + Scale( r ); + Scale( u ); + tools::Rectangle aChild( l, o, r, u ); + aChildAnchor.Union( aChild ); + break; + } + if (!aShapeAtom.SeekToEndOfRecord(rSt)) + break; + } + } + if (!aShapeHd.SeekToEndOfRecord(rSt)) + break; + } + return aChildAnchor; +} + +void SvxMSDffManager::GetGroupAnchors( const DffRecordHeader& rHd, SvStream& rSt, + tools::Rectangle& rGroupClientAnchor, tools::Rectangle& rGroupChildAnchor, + const tools::Rectangle& rClientRect, const tools::Rectangle& rGlobalChildRect ) +{ + if (!rHd.SeekToContent(rSt)) + return; + + bool bFirst = true; + DffRecordHeader aShapeHd; + while (rSt.good() && rSt.Tell() < rHd.GetRecEndFilePos()) + { + if (!ReadDffRecordHeader(rSt, aShapeHd)) + break; + if ( ( aShapeHd.nRecType == DFF_msofbtSpContainer ) || + ( aShapeHd.nRecType == DFF_msofbtSpgrContainer ) ) + { + DffRecordHeader aShapeHd2( aShapeHd ); + if ( aShapeHd.nRecType == DFF_msofbtSpgrContainer ) + ReadDffRecordHeader( rSt, aShapeHd2 ); + while (rSt.good() && rSt.Tell() < aShapeHd2.GetRecEndFilePos()) + { + DffRecordHeader aShapeAtom; + if (!ReadDffRecordHeader(rSt, aShapeAtom)) + break; + if ( aShapeAtom.nRecType == DFF_msofbtChildAnchor ) + { + sal_Int32 l(0), o(0), r(0), u(0); + rSt.ReadInt32( l ).ReadInt32( o ).ReadInt32( r ).ReadInt32( u ); + Scale( l ); + Scale( o ); + Scale( r ); + Scale( u ); + tools::Rectangle aChild( l, o, r, u ); + + if ( bFirst ) + { + if ( !rGlobalChildRect.IsEmpty() && !rClientRect.IsEmpty() && rGlobalChildRect.GetWidth() && rGlobalChildRect.GetHeight() ) + { + double fWidth = o3tl::saturating_sub(r, l); + double fHeight= o3tl::saturating_sub(u, o); + double fXScale = static_cast(rClientRect.GetWidth()) / static_cast(rGlobalChildRect.GetWidth()); + double fYScale = static_cast(rClientRect.GetHeight()) / static_cast(rGlobalChildRect.GetHeight()); + double fl = ( ( l - rGlobalChildRect.Left() ) * fXScale ) + rClientRect.Left(); + double fo = ( ( o - rGlobalChildRect.Top() ) * fYScale ) + rClientRect.Top(); + fWidth *= fXScale; + fHeight *= fYScale; + rGroupClientAnchor = tools::Rectangle( Point( static_cast(fl), static_cast(fo) ), Size( static_cast( fWidth + 1 ), static_cast( fHeight + 1 ) ) ); + } + bFirst = false; + } + else + rGroupChildAnchor.Union( aChild ); + break; + } + if (!aShapeAtom.SeekToEndOfRecord(rSt)) + break; + } + } + if (!aShapeHd.SeekToEndOfRecord(rSt)) + break; + } +} + +SvxMSDffImportRec* SvxMSDffImportData::find(const SdrObject* pObj) +{ + auto it = m_ObjToRecMap.find(pObj); + if (it != m_ObjToRecMap.end()) + return it->second; + return nullptr; +} + +void SvxMSDffImportData::insert(std::unique_ptr pImpRec) +{ + auto aRet = m_Records.insert(std::move(pImpRec)); + bool bSuccess = aRet.second; + if (bSuccess) + { + SvxMSDffImportRec* pRec = aRet.first->get(); + m_ObjToRecMap[pRec->pObj] = pRec; + } +} + +void SvxMSDffImportData::NotifyFreeObj(SdrObject* pObj) +{ + if (SvxMSDffImportRec* pRecord = find(pObj)) + { + m_ObjToRecMap.erase(pObj); + pRecord->pObj = nullptr; + } +} + +void SvxMSDffManager::NotifyFreeObj(SvxMSDffClientData& rData, SdrObject* pObj) +{ + if (SdrObjGroup* pGroup = dynamic_cast(pObj)) + { + SdrObjList* pSubList = pGroup->GetSubList(); + size_t nObjCount = pSubList->GetObjCount(); + for (size_t i = 0; i < nObjCount; ++i) + NotifyFreeObj(rData, pSubList->GetObj(i)); + } + + rData.NotifyFreeObj(pObj); +} + +void SvxMSDffManager::FreeObj(SvxMSDffClientData& rData, SdrObject* pObj) +{ + NotifyFreeObj(rData, pObj); + SdrObject::Free(pObj); +} + +SdrObject* SvxMSDffManager::ProcessObj(SvStream& rSt, + DffObjData& rObjData, + SvxMSDffClientData& rData, + tools::Rectangle& rTextRect, + SdrObject* pObj + ) +{ + if( !rTextRect.IsEmpty() ) + { + SvxMSDffImportData& rImportData = static_cast(rData); + SvxMSDffImportRec* pImpRec = new SvxMSDffImportRec; + bool bDeleteImpRec = true; + SvxMSDffImportRec* pTextImpRec = pImpRec; + bool bDeleteTextImpRec = false; + + // fill Import Record with data + pImpRec->nShapeId = rObjData.nShapeId; + pImpRec->eShapeType = rObjData.eShapeType; + + auto eWrapMode = GetPropertyValue(DFF_Prop_WrapText, mso_wrapSquare); + rObjData.bClientAnchor = maShapeRecords.SeekToContent( rSt, + DFF_msofbtClientAnchor, + SEEK_FROM_CURRENT_AND_RESTART ); + if( rObjData.bClientAnchor ) + ProcessClientAnchor( rSt, + maShapeRecords.Current()->nRecLen, + pImpRec->pClientAnchorBuffer, pImpRec->nClientAnchorLen ); + + rObjData.bClientData = maShapeRecords.SeekToContent( rSt, + DFF_msofbtClientData, + SEEK_FROM_CURRENT_AND_RESTART ); + if( rObjData.bClientData ) + ProcessClientData( rSt, + maShapeRecords.Current()->nRecLen, + pImpRec->pClientDataBuffer, pImpRec->nClientDataLen ); + + + // process user (== Winword) defined parameters in 0xF122 record + if( maShapeRecords.SeekToContent( rSt, + DFF_msofbtUDefProp, + SEEK_FROM_CURRENT_AND_RESTART ) + && maShapeRecords.Current()->nRecLen ) + { + sal_uInt32 nBytesLeft = maShapeRecords.Current()->nRecLen; + while( 5 < nBytesLeft ) + { + sal_uInt16 nPID(0); + rSt.ReadUInt16(nPID); + if (!rSt.good()) + break; + sal_uInt32 nUDData(0); + rSt.ReadUInt32(nUDData); + switch (nPID) + { + case 0x038F: pImpRec->nXAlign = nUDData; break; + case 0x0390: + pImpRec->nXRelTo = nUDData; + break; + case 0x0391: pImpRec->nYAlign = nUDData; break; + case 0x0392: + pImpRec->nYRelTo = nUDData; + break; + case 0x03BF: pImpRec->nGroupShapeBooleanProperties = nUDData; break; + case 0x0393: + // This seems to correspond to o:hrpct from .docx (even including + // the difference that it's in 0.1% even though the .docx spec + // says it's in 1%). + pImpRec->relativeHorizontalWidth = nUDData; + break; + case 0x0394: + // And this is really just a guess, but a mere presence of this + // flag makes a horizontal rule be as wide as the page (unless + // overridden by something), so it probably matches o:hr from .docx. + pImpRec->isHorizontalRule = true; + break; + } + if (!rSt.good()) + break; + nBytesLeft -= 6; + } + } + + // text frame, also Title or Outline + SdrObject* pOrgObj = pObj; + SdrRectObj* pTextObj = nullptr; + sal_uInt32 nTextId = GetPropertyValue( DFF_Prop_lTxid, 0 ); + if( nTextId ) + { + SfxItemSet aSet( pSdrModel->GetItemPool() ); + + //Originally anything that as a mso_sptTextBox was created as a + //textbox, this was changed for #88277# to be created as a simple + //rect to keep impress happy. For the rest of us we'd like to turn + //it back into a textbox again. + bool bTextFrame = (pImpRec->eShapeType == mso_sptTextBox); + if (!bTextFrame) + { + //Either + //a) it's a simple text object or + //b) it's a rectangle with text and square wrapping. + bTextFrame = + ( + (pImpRec->eShapeType == mso_sptTextSimple) || + ( + (pImpRec->eShapeType == mso_sptRectangle) + && (eWrapMode == mso_wrapSquare) + && ShapeHasText(pImpRec->nShapeId, rObjData.rSpHd.GetRecBegFilePos() ) + ) + ); + } + + if (bTextFrame) + { + SdrObject::Free( pObj ); + pObj = pOrgObj = nullptr; + } + + // Distance of Textbox to its surrounding Customshape + sal_Int32 nTextLeft = GetPropertyValue( DFF_Prop_dxTextLeft, 91440L); + sal_Int32 nTextRight = GetPropertyValue( DFF_Prop_dxTextRight, 91440L ); + sal_Int32 nTextTop = GetPropertyValue( DFF_Prop_dyTextTop, 45720L ); + sal_Int32 nTextBottom = GetPropertyValue( DFF_Prop_dyTextBottom, 45720L ); + + ScaleEmu( nTextLeft ); + ScaleEmu( nTextRight ); + ScaleEmu( nTextTop ); + ScaleEmu( nTextBottom ); + + Degree100 nTextRotationAngle(0); + bool bVerticalText = false; + if ( IsProperty( DFF_Prop_txflTextFlow ) ) + { + auto eTextFlow = GetPropertyValue(DFF_Prop_txflTextFlow, 0) & 0xFFFF; + switch( eTextFlow ) + { + case mso_txflBtoT: + nTextRotationAngle = 9000_deg100; + break; + case mso_txflVertN: + case mso_txflTtoBN: + nTextRotationAngle = 27000_deg100; + break; + case mso_txflTtoBA: + bVerticalText = true; + break; + case mso_txflHorzA: + bVerticalText = true; + nTextRotationAngle = 9000_deg100; + break; + case mso_txflHorzN: + default : + break; + } + } + + if (nTextRotationAngle) + { + switch (nTextRotationAngle.get()) + { + case 9000: + { + tools::Long nWidth = rTextRect.GetWidth(); + rTextRect.SetRight( rTextRect.Left() + rTextRect.GetHeight() ); + rTextRect.SetBottom( rTextRect.Top() + nWidth ); + + sal_Int32 nOldTextLeft = nTextLeft; + sal_Int32 nOldTextRight = nTextRight; + sal_Int32 nOldTextTop = nTextTop; + sal_Int32 nOldTextBottom = nTextBottom; + + nTextLeft = nOldTextBottom; + nTextRight = nOldTextTop; + nTextTop = nOldTextLeft; + nTextBottom = nOldTextRight; + } + break; + case 27000: + { + tools::Long nWidth = rTextRect.GetWidth(); + rTextRect.SetRight( rTextRect.Left() + rTextRect.GetHeight() ); + rTextRect.SetBottom( rTextRect.Top() + nWidth ); + + sal_Int32 nOldTextLeft = nTextLeft; + sal_Int32 nOldTextRight = nTextRight; + sal_Int32 nOldTextTop = nTextTop; + sal_Int32 nOldTextBottom = nTextBottom; + + nTextLeft = nOldTextTop; + nTextRight = nOldTextBottom; + nTextTop = nOldTextRight; + nTextBottom = nOldTextLeft; + } + break; + } + } + + pTextObj = new SdrRectObj( + *pSdrModel, + SdrObjKind::Text, + rTextRect); + pTextImpRec = new SvxMSDffImportRec(*pImpRec); + bDeleteTextImpRec = true; + + // the vertical paragraph indents are part of the BoundRect, + // here we 'remove' them by calculating + tools::Rectangle aNewRect(rTextRect); + aNewRect.AdjustBottom( -(nTextTop + nTextBottom) ); + aNewRect.AdjustRight( -(nTextLeft + nTextRight) ); + + // Only if it's a simple textbox may Writer replace + // the object with a frame, otherwise + if( bTextFrame ) + { + auto const pTmpRec = std::make_shared(0, pImpRec->nShapeId); + + SvxMSDffShapeInfos_ById::const_iterator const it = + m_xShapeInfosById->find(pTmpRec); + if (it != m_xShapeInfosById->end()) + { + SvxMSDffShapeInfo& rInfo = **it; + pTextImpRec->bReplaceByFly = rInfo.bReplaceByFly; + } + } + + if( !pObj ) + ApplyAttributes( rSt, aSet, rObjData ); + + bool bFitText = false; + if (GetPropertyValue(DFF_Prop_FitTextToShape, 0) & 2) + { + aSet.Put( makeSdrTextAutoGrowHeightItem( true ) ); + aSet.Put( makeSdrTextMinFrameHeightItem( + aNewRect.Bottom() - aNewRect.Top() ) ); + aSet.Put( makeSdrTextMinFrameWidthItem( + aNewRect.Right() - aNewRect.Left() ) ); + bFitText = true; + } + else + { + aSet.Put( makeSdrTextAutoGrowHeightItem( false ) ); + aSet.Put( makeSdrTextAutoGrowWidthItem( false ) ); + } + + switch (GetPropertyValue(DFF_Prop_WrapText, mso_wrapSquare)) + { + case mso_wrapNone : + aSet.Put( makeSdrTextAutoGrowWidthItem( true ) ); + if (bFitText) + { + //can't do autowidth in flys #i107184# + pTextImpRec->bReplaceByFly = false; + } + break; + case mso_wrapByPoints : + aSet.Put( makeSdrTextContourFrameItem( true ) ); + break; + default: break; + } + + // set margins at the border of the textbox + aSet.Put( makeSdrTextLeftDistItem( nTextLeft ) ); + aSet.Put( makeSdrTextRightDistItem( nTextRight ) ); + aSet.Put( makeSdrTextUpperDistItem( nTextTop ) ); + aSet.Put( makeSdrTextLowerDistItem( nTextBottom ) ); + pTextImpRec->nDxTextLeft = nTextLeft; + pTextImpRec->nDyTextTop = nTextTop; + pTextImpRec->nDxTextRight = nTextRight; + pTextImpRec->nDyTextBottom = nTextBottom; + + // read text anchor + if ( IsProperty( DFF_Prop_anchorText ) ) + { + auto eTextAnchor = GetPropertyValue(DFF_Prop_anchorText, 0); + + SdrTextVertAdjust eTVA = SDRTEXTVERTADJUST_CENTER; + bool bTVASet(false); + bool bTHASet(false); + + switch( eTextAnchor ) + { + case mso_anchorTop: + { + eTVA = SDRTEXTVERTADJUST_TOP; + bTVASet = true; + } + break; + case mso_anchorTopCentered: + { + eTVA = SDRTEXTVERTADJUST_TOP; + bTVASet = true; + bTHASet = true; + } + break; + + case mso_anchorMiddle: + bTVASet = true; + break; + case mso_anchorMiddleCentered: + { + bTVASet = true; + bTHASet = true; + } + break; + case mso_anchorBottom: + { + eTVA = SDRTEXTVERTADJUST_BOTTOM; + bTVASet = true; + } + break; + case mso_anchorBottomCentered: + { + eTVA = SDRTEXTVERTADJUST_BOTTOM; + bTVASet = true; + bTHASet = true; + } + break; + default : break; + } + // insert + if ( bTVASet ) + aSet.Put( SdrTextVertAdjustItem( eTVA ) ); + if ( bTHASet ) + aSet.Put( SdrTextHorzAdjustItem( SDRTEXTHORZADJUST_CENTER ) ); + } + + pTextObj->SetMergedItemSet(aSet); + + if (bVerticalText) + pTextObj->SetVerticalWriting(true); + + if (nTextRotationAngle) + { + tools::Long nMinWH = rTextRect.GetWidth() < rTextRect.GetHeight() ? + rTextRect.GetWidth() : rTextRect.GetHeight(); + nMinWH /= 2; + Point aPivot(rTextRect.TopLeft()); + aPivot.AdjustX(nMinWH ); + aPivot.AdjustY(nMinWH ); + pTextObj->SdrAttrObj::NbcRotate(aPivot, nTextRotationAngle); + } + + // rotate text with shape? + if ( mnFix16Angle ) + { + double a = toRadians(mnFix16Angle); + pTextObj->NbcRotate( rObjData.aBoundRect.Center(), mnFix16Angle, + sin( a ), cos( a ) ); + } + + if( !pObj ) + { + pObj = pTextObj; + } + else + { + if( pTextObj != pObj ) + { + SdrObject* pGroup = new SdrObjGroup(*pSdrModel); + pGroup->GetSubList()->NbcInsertObject( pObj ); + pGroup->GetSubList()->NbcInsertObject( pTextObj ); + if (pOrgObj == pObj) + pOrgObj = pGroup; + else + pOrgObj = pObj; + pObj = pGroup; + } + } + } + else if( !pObj ) + { + // simple rectangular objects are ignored by ImportObj() :-( + // this is OK for Draw but not for Calc and Writer + // cause here these objects have a default border + pObj = new SdrRectObj( + *pSdrModel, + rTextRect); + + pOrgObj = pObj; + SfxItemSet aSet( pSdrModel->GetItemPool() ); + ApplyAttributes( rSt, aSet, rObjData ); + + SfxItemState eState = aSet.GetItemState( XATTR_FILLCOLOR ); + if( SfxItemState::DEFAULT == eState ) + aSet.Put( XFillColorItem( OUString(), mnDefaultColor ) ); + pObj->SetMergedItemSet(aSet); + } + + //Means that fBehindDocument is set + if (GetPropertyValue(DFF_Prop_fPrint, 0) & 0x20) + pImpRec->bDrawHell = true; + else + pImpRec->bDrawHell = false; + if (GetPropertyValue(DFF_Prop_fPrint, 0) & 0x02) + pImpRec->bHidden = true; + pTextImpRec->bDrawHell = pImpRec->bDrawHell; + pTextImpRec->bHidden = pImpRec->bHidden; + pImpRec->nNextShapeId = GetPropertyValue( DFF_Prop_hspNext, 0 ); + pTextImpRec->nNextShapeId=pImpRec->nNextShapeId; + + if ( nTextId ) + { + pTextImpRec->aTextId.nTxBxS = static_cast( nTextId >> 16 ); + pTextImpRec->aTextId.nSequence = static_cast(nTextId); + } + + pTextImpRec->nDxWrapDistLeft = GetPropertyValue( + DFF_Prop_dxWrapDistLeft, 114935L ) / 635L; + pTextImpRec->nDyWrapDistTop = GetPropertyValue( + DFF_Prop_dyWrapDistTop, 0 ) / 635L; + pTextImpRec->nDxWrapDistRight = GetPropertyValue( + DFF_Prop_dxWrapDistRight, 114935L ) / 635L; + pTextImpRec->nDyWrapDistBottom = GetPropertyValue( + DFF_Prop_dyWrapDistBottom, 0 ) / 635L; + // 16.16 fraction times total image width or height, as appropriate. + + if (SeekToContent(DFF_Prop_pWrapPolygonVertices, rSt)) + { + pTextImpRec->pWrapPolygon.reset(); + sal_uInt16 nNumElemVert(0), nNumElemMemVert(0), nElemSizeVert(8); + rSt.ReadUInt16( nNumElemVert ).ReadUInt16( nNumElemMemVert ).ReadUInt16( nElemSizeVert ); + // If this value is 0xFFF0 then this record is an array of truncated 8 byte elements. Only the 4 + // low-order bytes are recorded + if (nElemSizeVert == 0xFFF0) + nElemSizeVert = 4; + + // sanity check that the stream is long enough to fulfill nNumElemVert * nElemSizeVert; + bool bOk = nElemSizeVert && (rSt.remainingSize() / nElemSizeVert >= nNumElemVert); + if (bOk) + { + pTextImpRec->pWrapPolygon = tools::Polygon(nNumElemVert); + for (sal_uInt16 i = 0; i < nNumElemVert; ++i) + { + sal_Int32 nX(0), nY(0); + if (nElemSizeVert == 8) + rSt.ReadInt32( nX ).ReadInt32( nY ); + else + { + sal_Int16 nSmallX(0), nSmallY(0); + rSt.ReadInt16( nSmallX ).ReadInt16( nSmallY ); + nX = nSmallX; + nY = nSmallY; + } + (*(pTextImpRec->pWrapPolygon))[i].setX( nX ); + (*(pTextImpRec->pWrapPolygon))[i].setY( nY ); + } + } + } + + pImpRec->nCropFromTop = GetPropertyValue( + DFF_Prop_cropFromTop, 0 ); + pImpRec->nCropFromBottom = GetPropertyValue( + DFF_Prop_cropFromBottom, 0 ); + pImpRec->nCropFromLeft = GetPropertyValue( + DFF_Prop_cropFromLeft, 0 ); + pImpRec->nCropFromRight = GetPropertyValue( + DFF_Prop_cropFromRight, 0 ); + + pImpRec->bVFlip = bool(rObjData.nSpFlags & ShapeFlag::FlipV); + pImpRec->bHFlip = bool(rObjData.nSpFlags & ShapeFlag::FlipH); + + sal_uInt32 nLineFlags = GetPropertyValue( DFF_Prop_fNoLineDrawDash, 0 ); + pImpRec->eLineStyle = (nLineFlags & 8) + ? static_cast(GetPropertyValue( + DFF_Prop_lineStyle, + mso_lineSimple )) + : MSO_LineStyle_NONE; + pTextImpRec->eLineStyle = pImpRec->eLineStyle; + + pImpRec->eLineDashing = static_cast(GetPropertyValue( + DFF_Prop_lineDashing, mso_lineSolid )); + pTextImpRec->eLineDashing = pImpRec->eLineDashing; + + if( pImpRec->nShapeId ) + { + // amend the import record list + if( pOrgObj ) + { + pImpRec->pObj = pOrgObj; + rImportData.insert(std::unique_ptr(pImpRec)); + bDeleteImpRec = false; + if (pImpRec == pTextImpRec) + bDeleteTextImpRec = false; + } + + if( pTextObj && (pOrgObj != pTextObj) ) + { + // Modify ShapeId (must be unique) + pImpRec->nShapeId |= 0x8000000; + pTextImpRec->pObj = pTextObj; + rImportData.insert(std::unique_ptr(pTextImpRec)); + bDeleteTextImpRec = false; + if (pTextImpRec == pImpRec) + bDeleteImpRec = false; + } + + // entry in the z-order-list in order to complement the pointer to this object + /*Only store objects which are not deep inside the tree*/ + if( ( rObjData.nCalledByGroup == 0 ) + || + ( (rObjData.nSpFlags & ShapeFlag::Group) + && (rObjData.nCalledByGroup < 2) ) + ) + StoreShapeOrder( pImpRec->nShapeId, + ( static_cast(pImpRec->aTextId.nTxBxS) << 16 ) + + pImpRec->aTextId.nSequence, pObj ); + } + + if (bDeleteImpRec) + delete pImpRec; + + if (bDeleteTextImpRec) + delete pTextImpRec; + } + + return pObj; +}; + +SdrObject* SvxMSDffManager::FinalizeObj(DffObjData& /* rObjData */, SdrObject* pObj) +{ + return pObj; +} + + +void SvxMSDffManager::StoreShapeOrder(sal_uLong nId, + sal_uLong nTxBx, + SdrObject* pObject, + SwFlyFrameFormat* pFly) const +{ + for (const auto& pOrder : m_aShapeOrders) + { + if (pOrder->nShapeId == nId) + { + pOrder->nTxBxComp = nTxBx; + pOrder->pObj = pObject; + pOrder->pFly = pFly; + } + } +} + + +void SvxMSDffManager::ExchangeInShapeOrder( SdrObject const * pOldObject, + sal_uLong nTxBx, + SdrObject* pObject) const +{ + for (const auto& pOrder : m_aShapeOrders) + { + if (pOrder->pObj == pOldObject) + { + pOrder->pFly = nullptr; + pOrder->pObj = pObject; + pOrder->nTxBxComp = nTxBx; + } + } +} + + +void SvxMSDffManager::RemoveFromShapeOrder( SdrObject const * pObject ) const +{ + for (const auto& pOrder : m_aShapeOrders) + { + if (pOrder->pObj == pObject) + { + pOrder->pObj = nullptr; + pOrder->pFly = nullptr; + pOrder->nTxBxComp = 0; + } + } +} + + +// exported class: Public Methods + +SvxMSDffManager::SvxMSDffManager(SvStream& rStCtrl_, + OUString aBaseURL, + sal_uInt32 nOffsDgg_, + SvStream* pStData_, + SdrModel* pSdrModel_,// see SetModel() below + tools::Long nApplicationScale, + Color mnDefaultColor_, + SvStream* pStData2_, + bool bSkipImages ) + :DffPropertyReader( *this ), + m_pBLIPInfos( new SvxMSDffBLIPInfos ), + m_xShapeInfosByTxBxComp( new SvxMSDffShapeInfos_ByTxBxComp ), + nOffsDgg( nOffsDgg_ ), + nBLIPCount( USHRT_MAX ), // initialize with error, since we first check if the + nGroupShapeFlags(ShapeFlag::NONE), // ensure initialization here, as some corrupted + // files may yield to this being uninitialized + maBaseURL(std::move( aBaseURL )), + mnIdClusters(0), + rStCtrl( rStCtrl_ ), + pStData( pStData_ ), + pStData2( pStData2_ ), + nSvxMSDffSettings( 0 ), + nSvxMSDffOLEConvFlags( 0 ), + mnDefaultColor( mnDefaultColor_), + mbSkipImages (bSkipImages) +{ + SetModel( pSdrModel_, nApplicationScale ); + + // remember FilePos of the stream(s) + sal_uInt64 nOldPosCtrl = rStCtrl.Tell(); + sal_uInt64 nOldPosData = pStData ? pStData->Tell() : nOldPosCtrl; + + // if no data stream is given we assume that the BLIPs + // are in the control stream + if( !pStData ) + pStData = &rStCtrl; + + SetDefaultPropSet( rStCtrl, nOffsDgg ); + + // read control stream, if successful set nBLIPCount + GetCtrlData( nOffsDgg ); + + // check Text-Box-Story-Chain-Infos + CheckTxBxStoryChain(); + + // restore old FilePos of the stream(s) + rStCtrl.Seek( nOldPosCtrl ); + if( &rStCtrl != pStData ) + pStData->Seek( nOldPosData ); +} + +SvxMSDffManager::SvxMSDffManager( SvStream& rStCtrl_, OUString aBaseURL ) + :DffPropertyReader( *this ), + m_pBLIPInfos( new SvxMSDffBLIPInfos ), + m_xShapeInfosByTxBxComp( new SvxMSDffShapeInfos_ByTxBxComp ), + nOffsDgg( 0 ), + nBLIPCount( USHRT_MAX ), // initialize with error, since we first have to check + nGroupShapeFlags(ShapeFlag::NONE), + maBaseURL(std::move( aBaseURL )), + mnIdClusters(0), + rStCtrl( rStCtrl_ ), + pStData( nullptr ), + pStData2( nullptr ), + nSvxMSDffSettings( 0 ), + nSvxMSDffOLEConvFlags( 0 ), + mnDefaultColor( COL_DEFAULT ), + mbSkipImages(false) +{ + SetModel( nullptr, 0 ); +} + +SvxMSDffManager::~SvxMSDffManager() +{ +} + +void SvxMSDffManager::InitSvxMSDffManager( sal_uInt32 nOffsDgg_, SvStream* pStData_, sal_uInt32 nOleConvFlags ) +{ + nOffsDgg = nOffsDgg_; + pStData = pStData_; + nSvxMSDffOLEConvFlags = nOleConvFlags; + + // remember FilePos of the stream(s) + sal_uInt64 nOldPosCtrl = rStCtrl.Tell(); + + SetDefaultPropSet( rStCtrl, nOffsDgg ); + + // insert fidcl cluster table + GetFidclData( nOffsDgg ); + + // read control stream, if successful, set nBLIPCount + GetCtrlData( nOffsDgg ); + + // check Text-Box-Story-Chain-Infos + CheckTxBxStoryChain(); + + // restore old FilePos of the stream(s) + rStCtrl.Seek( nOldPosCtrl ); +} + +void SvxMSDffManager::SetDgContainer( SvStream& rSt ) +{ + sal_uInt64 nFilePos = rSt.Tell(); + DffRecordHeader aDgContHd; + bool bOk = ReadDffRecordHeader(rSt, aDgContHd); + // insert this container only if there is also a DggAtom + if (bOk && SeekToRec(rSt, DFF_msofbtDg, aDgContHd.GetRecEndFilePos())) + { + DffRecordHeader aRecHd; + if (ReadDffRecordHeader(rSt, aRecHd)) + { + sal_uInt32 nDrawingId = aRecHd.nRecInstance; + maDgOffsetTable[nDrawingId] = nFilePos; + } + } + rSt.Seek(nFilePos); +} + +void SvxMSDffManager::GetFidclData( sal_uInt32 nOffsDggL ) +{ + if (!nOffsDggL) + return; + + sal_uInt64 nOldPos = rStCtrl.Tell(); + + if (nOffsDggL == rStCtrl.Seek(nOffsDggL)) + { + DffRecordHeader aRecHd; + bool bOk = ReadDffRecordHeader(rStCtrl, aRecHd); + + DffRecordHeader aDggAtomHd; + if (bOk && SeekToRec(rStCtrl, DFF_msofbtDgg, aRecHd.GetRecEndFilePos(), &aDggAtomHd)) + { + aDggAtomHd.SeekToContent( rStCtrl ); + sal_uInt32 nCurMaxShapeId; + sal_uInt32 nDummy; + rStCtrl.ReadUInt32( nCurMaxShapeId ) + .ReadUInt32( mnIdClusters ) + .ReadUInt32( nDummy ) + .ReadUInt32( nDummy ); // nDrawingsSaved + + if ( mnIdClusters-- > 2 ) + { + const std::size_t nFIDCLsize = sizeof(sal_uInt32) * 2; + if ( aDggAtomHd.nRecLen == ( mnIdClusters * nFIDCLsize + 16 ) ) + { + sal_uInt64 nMaxEntriesPossible = rStCtrl.remainingSize() / nFIDCLsize; + SAL_WARN_IF(nMaxEntriesPossible < mnIdClusters, + "filter.ms", "FIDCL list longer than remaining bytes, ppt or parser is wrong"); + mnIdClusters = std::min(nMaxEntriesPossible, static_cast(mnIdClusters)); + + maFidcls.resize(mnIdClusters); + for (sal_uInt32 i = 0; i < mnIdClusters; ++i) + { + sal_uInt32 cspidCur; ///< number of SPIDs used so far + rStCtrl.ReadUInt32( maFidcls[ i ].dgid ) + .ReadUInt32( cspidCur ); + } + } + } + } + } + rStCtrl.Seek( nOldPos ); +} + +void SvxMSDffManager::CheckTxBxStoryChain() +{ + m_xShapeInfosById.reset(new SvxMSDffShapeInfos_ById); + // mangle old Info array, sorted by nTxBxComp + sal_uInt32 nChain = std::numeric_limits::max(); + bool bSetReplaceFALSE = false; + for (SvxMSDffShapeInfos_ByTxBxComp::iterator iter = + m_xShapeInfosByTxBxComp->begin(), + mark = m_xShapeInfosByTxBxComp->begin(); + iter != m_xShapeInfosByTxBxComp->end(); ++iter) + { + std::shared_ptr const pObj = *iter; + if( pObj->nTxBxComp ) + { + // group change? + // the text id also contains an internal drawing container id + // to distinguish between text id of drawing objects in different + // drawing containers. + if( nChain != pObj->nTxBxComp ) + { + // reset mark and helper flag + mark = iter; + nChain = pObj->nTxBxComp; + bSetReplaceFALSE = !pObj->bReplaceByFly; + } + else if( !pObj->bReplaceByFly ) + { + // object that must NOT be replaced by frame? + bSetReplaceFALSE = true; + // maybe reset flags in start of group + for (SvxMSDffShapeInfos_ByTxBxComp::iterator itemp = mark; + itemp != iter; ++itemp) + { + (*itemp)->bReplaceByFly = false; + } + } + + if( bSetReplaceFALSE ) + { + pObj->bReplaceByFly = false; + } + } + // copy all Shape Info objects to m_xShapeInfosById, sorted by nShapeId + pObj->nTxBxComp = pObj->nTxBxComp & 0xFFFF0000; + m_xShapeInfosById->insert( pObj ); + } + // free original array but don't free its elements + m_xShapeInfosByTxBxComp.reset(); +} + + +/***************************************************************************** + + Reading the Shape-Infos in the Ctor: + --------------------------------- + remembering the Shape-Ids and the associated Blip-Numbers and TextBox-Infos + ========= ============ ============= + and remembering the File-Offsets for each Blip + ============ +******************************************************************************/ +void SvxMSDffManager::GetCtrlData(sal_uInt32 nOffsDggL) +{ + // position control stream + if (!checkSeek(rStCtrl, nOffsDggL)) + return; + + sal_uInt8 nVer; + sal_uInt16 nInst; + sal_uInt16 nFbt; + sal_uInt32 nLength; + if( !ReadCommonRecordHeader( rStCtrl, nVer, nInst, nFbt, nLength ) ) return; + + sal_uInt64 nPos = nOffsDggL + DFF_COMMON_RECORD_HEADER_SIZE; + + // case A: first Drawing Group Container, then n times Drawing Container + if( DFF_msofbtDggContainer != nFbt ) + return; + + bool bOk; + GetDrawingGroupContainerData( rStCtrl, nLength ); + + sal_uInt64 nMaxStrPos = rStCtrl.TellEnd(); + + nPos += nLength; + sal_uInt16 nDrawingContainerId = 1; + do + { + if (!checkSeek(rStCtrl, nPos)) + break; + + bOk = ReadCommonRecordHeader( rStCtrl, nVer, nInst, nFbt, nLength ) && ( DFF_msofbtDgContainer == nFbt ); + + if( !bOk ) + { + nPos++; // ????????? TODO: trying to get a one-hit wonder, this code should be rewritten... + if (nPos != rStCtrl.Seek(nPos)) + break; + bOk = ReadCommonRecordHeader( rStCtrl, nVer, nInst, nFbt, nLength ) + && ( DFF_msofbtDgContainer == nFbt ); + } + if( bOk ) + { + GetDrawingContainerData( rStCtrl, nLength, nDrawingContainerId ); + } + nPos += DFF_COMMON_RECORD_HEADER_SIZE + nLength; + ++nDrawingContainerId; + } + while( ( rStCtrl.GetError() == ERRCODE_NONE ) && ( nPos < nMaxStrPos ) && bOk ); +} + + +// from here on: Drawing Group Container i.e. document-wide valid data + +void SvxMSDffManager::GetDrawingGroupContainerData( SvStream& rSt, sal_uInt32 nLenDgg ) +{ + sal_uInt8 nVer; + sal_uInt16 nInst; + sal_uInt16 nFbt; + sal_uInt32 nLength; + + sal_uInt32 nLenBStoreCont = 0, nLenFBSE = 0; + sal_uLong nRead = 0; + + // search for a BStore Container + bool bOk = true; + do + { + if (!ReadCommonRecordHeader(rSt, nVer, nInst, nFbt, nLength)) + return; + nRead += DFF_COMMON_RECORD_HEADER_SIZE + nLength; + if (DFF_msofbtBstoreContainer == nFbt) + { + nLenBStoreCont = nLength; + break; + } + bOk = checkSeek(rSt, rSt.Tell() + nLength); + } + while (bOk && nRead < nLenDgg); + + if (!bOk || !nLenBStoreCont) + return; + + // Read all atoms of the containers from the BStore container and store all + // relevant data of all contained FBSEs in out pointer array. + // We also count all found FBSEs in member nBLIPCount. + + const sal_uLong nSkipBLIPLen = 20; // skip to get to the nBLIPLen + const sal_uLong nSkipBLIPPos = 4; // thereafter skip up to nBLIPPos + + sal_uInt32 nBLIPLen = 0, nBLIPPos = 0; + + nRead = 0; + do + { + if(!ReadCommonRecordHeader( rSt, nVer, nInst, nFbt, nLength)) return; + nRead += DFF_COMMON_RECORD_HEADER_SIZE + nLength; + if( DFF_msofbtBSE == nFbt && /* magic value from spec */ 0x2 == nVer ) + { + nLenFBSE = nLength; + // is FBSE big enough for our data + bOk = ( nSkipBLIPLen + 4 + nSkipBLIPPos + 4 <= nLenFBSE ); + + if (bOk) + { + rSt.SeekRel( nSkipBLIPLen ); + rSt.ReadUInt32( nBLIPLen ); + rSt.SeekRel( nSkipBLIPPos ); + rSt.ReadUInt32( nBLIPPos ); + bOk = rSt.GetError() == ERRCODE_NONE; + + nLength -= nSkipBLIPLen+ 4 + nSkipBLIPPos + 4; + } + + if (bOk) + { + // specialty: + // If nBLIPLen is less than nLenFBSE AND nBLIPPos is NULL, + // then we assume, that the image is in FBSE! + if( (!nBLIPPos) && (nBLIPLen < nLenFBSE) ) + nBLIPPos = rSt.Tell() + 4; + + if( USHRT_MAX == nBLIPCount ) + nBLIPCount = 1; + else + nBLIPCount++; + + // now save the info for later access + m_pBLIPInfos->push_back(SvxMSDffBLIPInfo(nBLIPPos)); + } + if (!checkSeek(rSt, rSt.Tell() + nLength)) + return; // invalid offset + } + else return; // invalid input + } + while( nRead < nLenBStoreCont ); +} + + +// from now on: Drawing Container which means Pages (Sheet, Slide) - wide valid data +// ================= ====== + +void SvxMSDffManager::GetDrawingContainerData( SvStream& rSt, sal_uInt32 nLenDg, + sal_uInt16 nDrawingContainerId ) +{ + sal_uInt8 nVer;sal_uInt16 nInst;sal_uInt16 nFbt;sal_uInt32 nLength; + + sal_uLong nReadDg = 0; + + // We are now in a drawing container (one per each page) and + // we now have to iterate through all contained shape group containers + do + { + if (!ReadCommonRecordHeader(rSt, nVer, nInst, nFbt, nLength)) + return; + nReadDg += DFF_COMMON_RECORD_HEADER_SIZE; + // Patriarch found (the upmost shape group container) ? + if (DFF_msofbtSpgrContainer == nFbt) + { + if (!GetShapeGroupContainerData(rSt, nLength, true, nDrawingContainerId)) + return; + } + // empty Shape Container ? (outside of shape group container) + else if (DFF_msofbtSpContainer == nFbt) + { + if (!GetShapeContainerData( + rSt, nLength, std::numeric_limits::max(), nDrawingContainerId)) + return; + } + else + { + if (!checkSeek(rSt, rSt.Tell() + nLength)) + return; + } + nReadDg += nLength; + } + while( nReadDg < nLenDg ); +} + +bool SvxMSDffManager::GetShapeGroupContainerData( SvStream& rSt, + sal_uInt32 nLenShapeGroupCont, + bool bPatriarch, + sal_uInt16 nDrawingContainerId ) +{ + sal_uInt8 nVer;sal_uInt16 nInst;sal_uInt16 nFbt;sal_uInt32 nLength; + sal_uInt64 nStartShapeGroupCont = rSt.Tell(); + // We are now in a shape group container (conditionally multiple per page) + // and we now have to iterate through all contained shape containers + bool bFirst = !bPatriarch; + sal_uLong nReadSpGrCont = 0; + do + { + if( !ReadCommonRecordHeader( rSt, nVer, nInst, nFbt, nLength ) ) + return false; + nReadSpGrCont += DFF_COMMON_RECORD_HEADER_SIZE; + // Shape Container? + if( DFF_msofbtSpContainer == nFbt ) + { + sal_uInt64 nGroupOffs = bFirst ? nStartShapeGroupCont - DFF_COMMON_RECORD_HEADER_SIZE : std::numeric_limits::max(); + if ( !GetShapeContainerData( rSt, nLength, nGroupOffs, nDrawingContainerId ) ) + return false; + bFirst = false; + } + // nested shape group container ? + else if( DFF_msofbtSpgrContainer == nFbt ) + { + if ( !GetShapeGroupContainerData( rSt, nLength, false, nDrawingContainerId ) ) + return false; + } + else + { + if (!checkSeek(rSt, rSt.Tell() + nLength)) + return false; + } + nReadSpGrCont += nLength; + } + while( nReadSpGrCont < nLenShapeGroupCont ); + // position the stream correctly + rSt.Seek( nStartShapeGroupCont + nLenShapeGroupCont ); + return true; +} + +bool SvxMSDffManager::GetShapeContainerData( SvStream& rSt, + sal_uInt32 nLenShapeCont, + sal_uInt64 nPosGroup, + sal_uInt16 nDrawingContainerId ) +{ + sal_uInt8 nVer;sal_uInt16 nInst;sal_uInt16 nFbt;sal_uInt32 nLength; + sal_uInt64 nStartShapeCont = rSt.Tell(); + + // We are in a shape container (possibly more than one per shape group) and we now + // have to fetch the shape id and file position (to be able to access them again later) + // and the first BStore reference (if present). + sal_uInt32 nLenShapePropTbl = 0; + sal_uLong nReadSpCont = 0; + + // Store file offset of the shape containers or respectively the group(!). + sal_uInt64 nStartOffs = (std::numeric_limits::max() > nPosGroup) ? + nPosGroup : nStartShapeCont - DFF_COMMON_RECORD_HEADER_SIZE; + SvxMSDffShapeInfo aInfo( nStartOffs ); + + // Can the shape be replaced with a frame? + // (provided that it is a TextBox and the text is not rotated) + bool bCanBeReplaced = nPosGroup >= std::numeric_limits::max(); + + // we don't know yet whether it's a TextBox + MSO_SPT eShapeType = mso_sptNil; + + // analyze Shape + + do + { + if(!ReadCommonRecordHeader( rSt, nVer, nInst, nFbt, nLength)) return false; + nReadSpCont += DFF_COMMON_RECORD_HEADER_SIZE; + // FSP ? + if( ( DFF_msofbtSp == nFbt ) && ( 4 <= nLength ) ) + { + // we've found the FSP: note Shape Type and Id! + eShapeType = static_cast(nInst); + rSt.ReadUInt32( aInfo.nShapeId ); + rSt.SeekRel( nLength - 4 ); + nReadSpCont += nLength; + } + else if( DFF_msofbtOPT == nFbt ) // Shape Property Table ? + { + // We've found the Property Table: + // search for the Blip Property! + sal_uLong nPropRead = 0; + nLenShapePropTbl = nLength; + auto nStartShapePropTbl = rSt.Tell(); + do + { + sal_uInt16 nPropId(0); + sal_uInt32 nPropVal(0); + + rSt.ReadUInt16( nPropId ) + .ReadUInt32( nPropVal ); + nPropRead += 6; + + switch( nPropId ) + { + case DFF_Prop_txflTextFlow : + //Writer can now handle vertical textflows in its + //native frames, to only need to do this for the + //other two formats + + //Writer will handle all textflow except BtoT + if (GetSvxMSDffSettings() & + (SVXMSDFF_SETTINGS_IMPORT_PPT | + SVXMSDFF_SETTINGS_IMPORT_EXCEL)) + { + if( 0 != nPropVal ) + bCanBeReplaced = false; + } + else if ( + (nPropVal != mso_txflHorzN) && + (nPropVal != mso_txflTtoBA) + ) + { + bCanBeReplaced = false; + } + break; + case DFF_Prop_cdirFont : + //Writer can now handle right to left and left + //to right in its native frames, so only do + //this for the other two formats. + if (GetSvxMSDffSettings() & + (SVXMSDFF_SETTINGS_IMPORT_PPT | + SVXMSDFF_SETTINGS_IMPORT_EXCEL)) + { + if( 0 != nPropVal ) + bCanBeReplaced = false; + } + break; + case DFF_Prop_Rotation : + if( 0 != nPropVal ) + bCanBeReplaced = false; + break; + + case DFF_Prop_gtextFStrikethrough : + if( ( 0x20002000 & nPropVal ) == 0x20002000 ) + bCanBeReplaced = false; + break; + + case DFF_Prop_fc3DLightFace : + if( ( 0x00080008 & nPropVal ) == 0x00080008 ) + bCanBeReplaced = false; + break; + + case DFF_Prop_WrapText : + //TODO: eWrapMode = (MSO_WrapMode)nPropVal; + break; + + default: + { + // is the Bit set and valid? + if( 0x4000 == ( nPropId & 0xC000 ) ) + { + // Blip Property found: remember BStore Idx! + nPropRead = nLenShapePropTbl; + } + else if( 0x8000 & nPropId ) + { + // complex Prop found: + // Length is always 6. The length of the appended extra data + // after the actual prop table is of different size. + nPropVal = 6; + } + } + break; + } + } + while (rSt.good() && nPropRead < nLenShapePropTbl); + rSt.Seek( nStartShapePropTbl + nLenShapePropTbl ); + nReadSpCont += nLenShapePropTbl; + } + else if( ( DFF_msofbtClientTextbox == nFbt ) && ( 4 == nLength ) ) // Text-Box-Story-Entry found + { + rSt.ReadUInt32( aInfo.nTxBxComp ); + // Add internal drawing container id to text id. + // Note: The text id uses the first two bytes, while the internal + // drawing container id used the second two bytes. + aInfo.nTxBxComp = ( aInfo.nTxBxComp & 0xFFFF0000 ) + + nDrawingContainerId; + DBG_ASSERT( (aInfo.nTxBxComp & 0x0000FFFF) == nDrawingContainerId, + " - internal drawing container Id could not be correctly merged into DFF_msofbtClientTextbox value." ); + } + else + { + if (!checkSeek(rSt, rSt.Tell() + nLength)) + { + SAL_WARN("filter.ms", "remaining record longer than available data, ppt or parser is wrong"); + break; + } + nReadSpCont += nLength; + } + } + while( nReadSpCont < nLenShapeCont ); + + + // Now possibly store the information for subsequent accesses to the shape + + if( aInfo.nShapeId ) + { + // Possibly allow replacement of textboxes with frames + if( bCanBeReplaced + && aInfo.nTxBxComp + && ( + ( eShapeType == mso_sptTextSimple ) + || ( eShapeType == mso_sptTextBox ) + || ( eShapeType == mso_sptRectangle ) + || ( eShapeType == mso_sptRoundRectangle ) + ) ) + { + aInfo.bReplaceByFly = true; + } + m_xShapeInfosByTxBxComp->insert(std::make_shared( + aInfo)); + m_aShapeOrders.push_back(std::make_unique( + aInfo.nShapeId )); + } + + // and position the Stream correctly again + rSt.Seek( nStartShapeCont + nLenShapeCont ); + return true; +} + + +/***************************************************************************** + + Access to a shape at runtime (via the Shape-Id) + ---------------------------- +******************************************************************************/ +bool SvxMSDffManager::GetShape(sal_uLong nId, SdrObject*& rpShape, + SvxMSDffImportData& rData) +{ + auto const pTmpRec = std::make_shared(0, nId); + + SvxMSDffShapeInfos_ById::const_iterator const it = + m_xShapeInfosById->find(pTmpRec); + if (it == m_xShapeInfosById->end()) + return false; + + // Possibly delete old error flag. + if( rStCtrl.GetError() ) + rStCtrl.ResetError(); + // store FilePos of the stream(s) + sal_uInt64 nOldPosCtrl = rStCtrl.Tell(); + sal_uInt64 nOldPosData = pStData ? pStData->Tell() : nOldPosCtrl; + // jump to the shape in the control stream + sal_uInt64 const nFilePos((*it)->nFilePos); + bool bSeeked = (nFilePos == rStCtrl.Seek(nFilePos)); + + // if it failed, reset error statusF + if (!bSeeked || rStCtrl.GetError()) + rStCtrl.ResetError(); + else + rpShape = ImportObj( rStCtrl, rData, rData.aParentRect, rData.aParentRect, /*nCalledByGroup*/0, /*pShapeId*/nullptr ); + + // restore old FilePos of the stream(s) + rStCtrl.Seek( nOldPosCtrl ); + if( &rStCtrl != pStData && pStData ) + pStData->Seek( nOldPosData ); + return ( nullptr != rpShape ); +} + + +/** Access to a BLIP at runtime (if the Blip-Number is already known) + */ +bool SvxMSDffManager::GetBLIP( sal_uLong nIdx_, Graphic& rGraphic, tools::Rectangle* pVisArea ) +{ + if (!pStData) + return false; + + bool bOk = false; // initialize result variable + + // check if a graphic for this blipId is already imported + if (nIdx_) + { + auto iter = aEscherBlipCache.find(nIdx_); + + if (iter != aEscherBlipCache.end()) + { + /* if this entry is available */ + rGraphic = iter->second; + if (rGraphic.GetType() != GraphicType::NONE) + bOk = true; + else + aEscherBlipCache.erase(iter); + } + } + + if (!bOk) + { + sal_uInt16 nIdx = sal_uInt16( nIdx_ ); + if (!nIdx || (m_pBLIPInfos->size() < nIdx)) + return false; + + // possibly delete old error flag(s) + if( rStCtrl.GetError() ) + rStCtrl.ResetError(); + if( ( &rStCtrl != pStData ) + && pStData->GetError() ) + pStData->ResetError(); + + // remember FilePos of the stream(s) + sal_uInt64 nOldPosCtrl = rStCtrl.Tell(); + sal_uInt64 nOldPosData = pStData->Tell(); + + // fetch matching info struct out of the pointer array + SvxMSDffBLIPInfo& rInfo = (*m_pBLIPInfos)[ nIdx-1 ]; + // jump to the BLIP atom in the data stream + bOk = checkSeek(*pStData, rInfo.nFilePos); + // possibly reset error status + if (!bOk || pStData->GetError()) + pStData->ResetError(); + else + bOk = GetBLIPDirect( *pStData, rGraphic, pVisArea ); + if( pStData2 && !bOk ) + { + // Error, but the is a second chance: There is a second + // data stream in which the graphic could be stored! + if( pStData2->GetError() ) + pStData2->ResetError(); + sal_uInt64 nOldPosData2 = pStData2->Tell(); + // jump to the BLIP atom in the second data stream + bOk = checkSeek(*pStData2, rInfo.nFilePos); + // reset error status if necessary + if (!bOk || pStData2->GetError()) + pStData2->ResetError(); + else + bOk = GetBLIPDirect( *pStData2, rGraphic, pVisArea ); + // restore of FilePos of the second data stream + pStData2->Seek( nOldPosData2 ); + } + // restore old FilePos of the stream(s) + rStCtrl.Seek( nOldPosCtrl ); + if( &rStCtrl != pStData ) + pStData->Seek( nOldPosData ); + + if (bOk) + { + // create new BlipCacheEntry for this graphic + aEscherBlipCache.insert(std::make_pair(nIdx_, rGraphic)); + } + } + + return bOk; +} + +/* access to a BLIP at runtime (with correctly positioned stream) + --------------------------------- +******************************************************************************/ +bool SvxMSDffManager::GetBLIPDirect( SvStream& rBLIPStream, Graphic& rData, tools::Rectangle* pVisArea ) +{ + sal_uInt64 nOldPos = rBLIPStream.Tell(); + + ErrCode nRes = ERRCODE_GRFILTER_OPENERROR; // initialize error variable + + // check whether it's really a BLIP + sal_uInt32 nLength; + sal_uInt16 nInst, nFbt( 0 ); + sal_uInt8 nVer; + if( ReadCommonRecordHeader( rBLIPStream, nVer, nInst, nFbt, nLength) && ( 0xF018 <= nFbt ) && ( 0xF117 >= nFbt ) ) + { + Size aMtfSize100; + bool bMtfBLIP = false; + bool bZCodecCompression = false; + // now position it exactly at the beginning of the embedded graphic + sal_uLong nSkip = (nInst & 0x0001) ? 32 : 16; + const OfficeArtBlipRecInstance aRecInstanse = OfficeArtBlipRecInstance(nInst & 0xFFFE); + switch (aRecInstanse) + { + case OfficeArtBlipRecInstance::EMF: + case OfficeArtBlipRecInstance::WMF: + case OfficeArtBlipRecInstance::PICT: + { + rBLIPStream.SeekRel(nSkip + 20); + + // read in size of metafile in English Metric Units (EMUs) + sal_Int32 width(0), height(0); + rBLIPStream.ReadInt32(width).ReadInt32(height); + aMtfSize100.setWidth(width); + aMtfSize100.setHeight(height); + + // 1 EMU = 1/360,000 of a centimeter + // scale to 1/100mm + aMtfSize100.setWidth(aMtfSize100.Width() / 360); + aMtfSize100.setHeight(aMtfSize100.Height() / 360); + + if (pVisArea) // seem that we currently are skipping the visarea position + *pVisArea = tools::Rectangle(Point(), aMtfSize100); + + // skip rest of header + nSkip = 6; + bMtfBLIP = bZCodecCompression = true; + } + break; + case OfficeArtBlipRecInstance::JPEG_RGB: + case OfficeArtBlipRecInstance::JPEG_CMYK: + case OfficeArtBlipRecInstance::PNG: + case OfficeArtBlipRecInstance::DIB: + case OfficeArtBlipRecInstance::TIFF: + nSkip += 1; // Skip one byte tag + break; + } + rBLIPStream.SeekRel( nSkip ); + + SvStream* pGrStream = &rBLIPStream; + std::unique_ptr xOut; + if( bZCodecCompression ) + { + xOut.reset(new SvMemoryStream( 0x8000, 0x4000 )); + ZCodec aZCodec( 0x8000, 0x8000 ); + aZCodec.BeginCompression(); + aZCodec.Decompress( rBLIPStream, *xOut ); + aZCodec.EndCompression(); + xOut->Seek( STREAM_SEEK_TO_BEGIN ); + xOut->SetResizeOffset( 0 ); // sj: #i102257# setting ResizeOffset of 0 prevents from seeking + // behind the stream end (allocating too much memory) + pGrStream = xOut.get(); + } + +#ifdef DEBUG_FILTER_MSDFFIMP + // extract graphics from ole storage into "dbggfxNNN.*" + static sal_Int32 nGrfCount; + + OUString aFileName = "dbggfx" + OUString::number(nGrfCount++); + switch (aRecInstanse) + { + case OfficeArtBlipRecInstance::WMF: + aFileName += ".wmf"; + break; + case OfficeArtBlipRecInstance::EMF: + aFileName += ".emf"; + break; + case OfficeArtBlipRecInstance::PICT: + aFileName += ".pct"; + break; + case OfficeArtBlipRecInstance::JPEG_RGB: + case OfficeArtBlipRecInstance::JPEG_CMYK: + aFileName += ".jpg"; + break; + case OfficeArtBlipRecInstance::PNG: + aFileName += ".png"; + break; + case OfficeArtBlipRecInstance::DIB: + aFileName += ".bmp"; + break; + case OfficeArtBlipRecInstance::TIFF: + aFileName += ".tif"; + break; + } + + + OUString aURLStr; + if( osl::FileBase::getFileURLFromSystemPath( Application::GetAppFileName(), aURLStr ) == osl::FileBase::E_None ) + { + INetURLObject aURL( aURLStr ); + + aURL.removeSegment(); + aURL.removeFinalSlash(); + aURL.Append( aFileName ); + + aURLStr = aURL.GetMainURL( INetURLObject::DecodeMechanism::NONE ); + + SAL_INFO("filter.ms", "dumping " << aURLStr); + + std::unique_ptr pDbgOut(::utl::UcbStreamHelper::CreateStream(aURLStr, StreamMode::TRUNC | StreamMode::WRITE)); + + if( pDbgOut ) + { + if ( bZCodecCompression ) + { + pDbgOut->WriteBytes(xOut->GetData(), xOut->TellEnd()); + xOut->Seek(STREAM_SEEK_TO_BEGIN); + } + else + { + sal_Int32 nDbgLen = nLength - nSkip; + if ( nDbgLen ) + { + std::vector aData(nDbgLen); + pGrStream->ReadBytes(aData.data(), nDbgLen); + pDbgOut->WriteBytes(aData.data(), nDbgLen); + pGrStream->SeekRel(-nDbgLen); + } + } + } + } +#endif + if (aRecInstanse == OfficeArtBlipRecInstance::DIB) + { // getting the DIBs immediately + Bitmap aNew; + if( ReadDIB(aNew, *pGrStream, false) ) + { + rData = Graphic(BitmapEx(aNew)); + nRes = ERRCODE_NONE; + } + } + else + { // and unleash our filter + GraphicFilter& rGF = GraphicFilter::GetGraphicFilter(); + // ImportUnloadedGraphic() may simply read the entire rest of the stream, + // which may be very large if the whole document is large. Limit the read + // size to the size of this record. + sal_uInt64 maxSize = pGrStream == &rBLIPStream ? nLength : 0; + Graphic aGraphic; + + // Size available in metafile header. + if (aMtfSize100.getWidth() && aMtfSize100.getHeight()) + aGraphic = rGF.ImportUnloadedGraphic(*pGrStream, maxSize, &aMtfSize100); + else + aGraphic = rGF.ImportUnloadedGraphic(*pGrStream, maxSize); + + if (!aGraphic.IsNone()) + { + rData = aGraphic; + nRes = ERRCODE_NONE; + } + else + nRes = rGF.ImportGraphic( rData, u"", *pGrStream ); + + // SJ: I40472, sometimes the aspect ratio (aMtfSize100) does not match and we get scaling problems, + // then it is better to use the prefsize that is stored within the metafile. Bug #72846# for what the + // scaling has been implemented does not happen anymore. + // + // For pict graphics we will furthermore scale the metafile, because font scaling leads to error if the + // dxarray is empty (this has been solved in wmf/emf but not for pict) + if (bMtfBLIP && (ERRCODE_NONE == nRes) && (rData.GetType() == GraphicType::GdiMetafile) + && (aRecInstanse == OfficeArtBlipRecInstance::PICT)) + { + if ( ( aMtfSize100.Width() >= 1000 ) && ( aMtfSize100.Height() >= 1000 ) ) + { // #75956#, scaling does not work properly, if the graphic is less than 1cm + GDIMetaFile aMtf( rData.GetGDIMetaFile() ); + const Size aOldSize( aMtf.GetPrefSize() ); + + if( aOldSize.Width() && ( aOldSize.Width() != aMtfSize100.Width() ) && + aOldSize.Height() && ( aOldSize.Height() != aMtfSize100.Height() ) ) + { + aMtf.Scale( static_cast(aMtfSize100.Width()) / aOldSize.Width(), + static_cast(aMtfSize100.Height()) / aOldSize.Height() ); + aMtf.SetPrefSize( aMtfSize100 ); + aMtf.SetPrefMapMode(MapMode(MapUnit::Map100thMM)); + rData = aMtf; + } + } + } + } + // reset error status if necessary + if ( ERRCODE_IO_PENDING == pGrStream->GetError() ) + pGrStream->ResetError(); + } + rBLIPStream.Seek( nOldPos ); // restore old FilePos of the stream + + return ( ERRCODE_NONE == nRes ); // return result +} + +/* also static */ +bool SvxMSDffManager::ReadCommonRecordHeader(SvStream& rSt, + sal_uInt8& rVer, sal_uInt16& rInst, sal_uInt16& rFbt, sal_uInt32& rLength) +{ + sal_uInt16 nTmp(0); + rSt.ReadUInt16( nTmp ).ReadUInt16( rFbt ).ReadUInt32( rLength ); + rVer = sal::static_int_cast< sal_uInt8 >(nTmp & 15); + rInst = nTmp >> 4; + if (!rSt.good()) + return false; + if (rLength > nMaxLegalDffRecordLength) + return false; + return true; +} + +void SvxMSDffManager::ProcessClientAnchor(SvStream& rStData, sal_uInt32 nDatLen, + std::unique_ptr& rpBuff, sal_uInt32& rBuffLen ) +{ + if( nDatLen ) + { + rBuffLen = std::min(rStData.remainingSize(), static_cast(nDatLen)); + rpBuff.reset( new char[rBuffLen] ); + rBuffLen = rStData.ReadBytes(rpBuff.get(), rBuffLen); + } +} + +void SvxMSDffManager::ProcessClientData(SvStream& rStData, sal_uInt32 nDatLen, + std::unique_ptr& rpBuff, sal_uInt32& rBuffLen ) +{ + if( nDatLen ) + { + rBuffLen = std::min(rStData.remainingSize(), static_cast(nDatLen)); + rpBuff.reset( new char[rBuffLen] ); + rBuffLen = rStData.ReadBytes(rpBuff.get(), rBuffLen); + } +} + + +void SvxMSDffManager::ProcessClientAnchor2( SvStream& /* rSt */, DffRecordHeader& /* rHd */ , DffObjData& /* rObj */ ) +{ + // will be overridden by SJ in Draw +} + +bool SvxMSDffManager::GetOLEStorageName( sal_uInt32, OUString&, tools::SvRef&, uno::Reference < embed::XStorage >& ) const +{ + return false; +} + +bool SvxMSDffManager::ShapeHasText( sal_uLong /* nShapeId */, sal_uLong /* nFilePos */ ) const +{ + return true; +} + +// #i32596# - add new parameter <_nCalledByGroup> +SdrObject* SvxMSDffManager::ImportOLE( sal_uInt32 nOLEId, + const Graphic& rGrf, + const tools::Rectangle& rBoundRect, + const tools::Rectangle& rVisArea, + const int /* _nCalledByGroup */ ) const +{ + SdrObject* pRet = nullptr; + OUString sStorageName; + tools::SvRef xSrcStg; + ErrCode nError = ERRCODE_NONE; + uno::Reference < embed::XStorage > xDstStg; + if( GetOLEStorageName( nOLEId, sStorageName, xSrcStg, xDstStg )) + pRet = CreateSdrOLEFromStorage( + *GetModel(), + sStorageName, + xSrcStg, + xDstStg, + rGrf, + rBoundRect, + rVisArea, + pStData, + nError, + nSvxMSDffOLEConvFlags, + embed::Aspects::MSOLE_CONTENT, + maBaseURL); + return pRet; +} + +bool SvxMSDffManager::MakeContentStream( SotStorage * pStor, const GDIMetaFile & rMtf ) +{ + tools::SvRef xStm = pStor->OpenSotStream(SVEXT_PERSIST_STREAM); + xStm->SetVersion( pStor->GetVersion() ); + xStm->SetBufferSize( 8192 ); + + Impl_OlePres aEle; + // Convert the size in 1/100 mm + // If a not applicable MapUnit (device dependent) is used, + // SV tries to guess a best match for the right value + Size aSize = rMtf.GetPrefSize(); + const MapMode& aMMSrc = rMtf.GetPrefMapMode(); + MapMode aMMDst( MapUnit::Map100thMM ); + aSize = OutputDevice::LogicToLogic( aSize, aMMSrc, aMMDst ); + aEle.SetSize( aSize ); + aEle.SetAspect( ASPECT_CONTENT ); + aEle.SetAdviseFlags( 2 ); + aEle.SetMtf( rMtf ); + aEle.Write( *xStm ); + + xStm->SetBufferSize( 0 ); + return xStm->GetError() == ERRCODE_NONE; +} + +namespace { + +struct ClsIDs { + sal_uInt32 nId; + const char* pSvrName; + const char* pDspName; +}; + +} + +const ClsIDs aClsIDs[] = { + + { 0x000212F0, "MSWordArt", "Microsoft Word Art" }, + { 0x000212F0, "MSWordArt.2", "Microsoft Word Art 2.0" }, + + // MS Apps + { 0x00030000, "ExcelWorksheet", "Microsoft Excel Worksheet" }, + { 0x00030001, "ExcelChart", "Microsoft Excel Chart" }, + { 0x00030002, "ExcelMacrosheet", "Microsoft Excel Macro" }, + { 0x00030003, "WordDocument", "Microsoft Word Document" }, + { 0x00030004, "MSPowerPoint", "Microsoft PowerPoint" }, + { 0x00030005, "MSPowerPointSho", "Microsoft PowerPoint Slide Show"}, + { 0x00030006, "MSGraph", "Microsoft Graph" }, + { 0x00030007, "MSDraw", "Microsoft Draw" }, + { 0x00030008, "Note-It", "Microsoft Note-It" }, + { 0x00030009, "WordArt", "Microsoft Word Art" }, + { 0x0003000a, "PBrush", "Microsoft PaintBrush Picture" }, + { 0x0003000b, "Equation", "Microsoft Equation Editor" }, + { 0x0003000c, "Package", "Package" }, + { 0x0003000d, "SoundRec", "Sound" }, + { 0x0003000e, "MPlayer", "Media Player" }, + // MS Demos + { 0x0003000f, "ServerDemo", "OLE 1.0 Server Demo" }, + { 0x00030010, "Srtest", "OLE 1.0 Test Demo" }, + { 0x00030011, "SrtInv", "OLE 1.0 Inv Demo" }, + { 0x00030012, "OleDemo", "OLE 1.0 Demo" }, + + // Coromandel / Dorai Swamy / 718-793-7963 + { 0x00030013, "CoromandelIntegra", "Coromandel Integra" }, + { 0x00030014, "CoromandelObjServer","Coromandel Object Server" }, + + // 3-d Visions Corp / Peter Hirsch / 310-325-1339 + { 0x00030015, "StanfordGraphics", "Stanford Graphics" }, + + // Deltapoint / Nigel Hearne / 408-648-4000 + { 0x00030016, "DGraphCHART", "DeltaPoint Graph Chart" }, + { 0x00030017, "DGraphDATA", "DeltaPoint Graph Data" }, + + // Corel / Richard V. Woodend / 613-728-8200 x1153 + { 0x00030018, "PhotoPaint", "Corel PhotoPaint" }, + { 0x00030019, "CShow", "Corel Show" }, + { 0x0003001a, "CorelChart", "Corel Chart" }, + { 0x0003001b, "CDraw", "Corel Draw" }, + + // Inset Systems / Mark Skiba / 203-740-2400 + { 0x0003001c, "HJWIN1.0", "Inset Systems" }, + + // Mark V Systems / Mark McGraw / 818-995-7671 + { 0x0003001d, "ObjMakerOLE", "MarkV Systems Object Maker" }, + + // IdentiTech / Mike Gilger / 407-951-9503 + { 0x0003001e, "FYI", "IdentiTech FYI" }, + { 0x0003001f, "FYIView", "IdentiTech FYI Viewer" }, + + // Inventa Corporation / Balaji Varadarajan / 408-987-0220 + { 0x00030020, "Stickynote", "Inventa Sticky Note" }, + + // ShapeWare Corp. / Lori Pearce / 206-467-6723 + { 0x00030021, "ShapewareVISIO10", "Shapeware Visio 1.0" }, + { 0x00030022, "ImportServer", "Spaheware Import Server" }, + + // test app SrTest + { 0x00030023, "SrvrTest", "OLE 1.0 Server Test" }, + + // test app ClTest. Doesn't really work as a server but is in reg db + { 0x00030025, "Cltest", "OLE 1.0 Client Test" }, + + // Microsoft ClipArt Gallery Sherry Larsen-Holmes + { 0x00030026, "MS_ClipArt_Gallery", "Microsoft ClipArt Gallery" }, + // Microsoft Project Cory Reina + { 0x00030027, "MSProject", "Microsoft Project" }, + + // Microsoft Works Chart + { 0x00030028, "MSWorksChart", "Microsoft Works Chart" }, + + // Microsoft Works Spreadsheet + { 0x00030029, "MSWorksSpreadsheet", "Microsoft Works Spreadsheet" }, + + // AFX apps - Dean McCrory + { 0x0003002A, "MinSvr", "AFX Mini Server" }, + { 0x0003002B, "HierarchyList", "AFX Hierarchy List" }, + { 0x0003002C, "BibRef", "AFX BibRef" }, + { 0x0003002D, "MinSvrMI", "AFX Mini Server MI" }, + { 0x0003002E, "TestServ", "AFX Test Server" }, + + // Ami Pro + { 0x0003002F, "AmiProDocument", "Ami Pro Document" }, + + // WordPerfect Presentations For Windows + { 0x00030030, "WPGraphics", "WordPerfect Presentation" }, + { 0x00030031, "WPCharts", "WordPerfect Chart" }, + + // MicroGrafx Charisma + { 0x00030032, "Charisma", "MicroGrafx Charisma" }, + { 0x00030033, "Charisma_30", "MicroGrafx Charisma 3.0" }, + { 0x00030034, "CharPres_30", "MicroGrafx Charisma 3.0 Pres" }, + // MicroGrafx Draw + { 0x00030035, "Draw", "MicroGrafx Draw" }, + // MicroGrafx Designer + { 0x00030036, "Designer_40", "MicroGrafx Designer 4.0" }, + + // STAR DIVISION + { 0x00043AD2, "FontWork", "Star FontWork" }, + + { 0, "", "" } }; + + +bool SvxMSDffManager::ConvertToOle2( SvStream& rStm, sal_uInt32 nReadLen, + const GDIMetaFile * pMtf, const tools::SvRef& rDest ) +{ + bool bMtfRead = false; + tools::SvRef xOle10Stm = rDest->OpenSotStream( "\1Ole10Native", + StreamMode::WRITE| StreamMode::SHARE_DENYALL ); + if( xOle10Stm->GetError() ) + return false; + + OUString aSvrName; + sal_uInt32 nDummy0; + sal_uInt32 nDummy1; + sal_uInt32 nBytesRead = 0; + do + { + sal_uInt32 nType(0); + sal_uInt32 nRecType(0); + sal_uInt32 nStrLen(0); + + rStm.ReadUInt32( nType ); + rStm.ReadUInt32( nRecType ); + rStm.ReadUInt32( nStrLen ); + if( nStrLen ) + { + if( 0x10000L > nStrLen ) + { + std::unique_ptr pBuf(new char[ nStrLen ]); + rStm.ReadBytes(pBuf.get(), nStrLen); + aSvrName = OUString( pBuf.get(), static_cast(nStrLen)-1, osl_getThreadTextEncoding() ); + } + else + break; + } + rStm.ReadUInt32( nDummy0 ); + rStm.ReadUInt32( nDummy1 ); + sal_uInt32 nDataLen(0); + rStm.ReadUInt32( nDataLen ); + + nBytesRead += 6 * sizeof( sal_uInt32 ) + nStrLen + nDataLen; + + if (rStm.good() && nReadLen > nBytesRead && nDataLen) + { + if( xOle10Stm.is() ) + { + std::unique_ptr pData(new sal_uInt8[ nDataLen ]); + rStm.ReadBytes(pData.get(), nDataLen); + + // write to ole10 stream + xOle10Stm->WriteUInt32( nDataLen ); + xOle10Stm->WriteBytes(pData.get(), nDataLen); + xOle10Stm = tools::SvRef(); + + // set the compobj stream + const ClsIDs* pIds; + for( pIds = aClsIDs; pIds->nId; pIds++ ) + { + if( aSvrName == OUString::createFromAscii(pIds->pSvrName) ) + break; + } + + if( pIds->nId ) + { + // found! + SotClipboardFormatId nCbFmt = SotExchange::RegisterFormatName( aSvrName ); + rDest->SetClass( SvGlobalName( pIds->nId, 0, 0, 0xc0,0,0,0,0,0,0,0x46 ), nCbFmt, + OUString::createFromAscii( pIds->pDspName ) ); + } + else + { + SotClipboardFormatId nCbFmt = SotExchange::RegisterFormatName( aSvrName ); + rDest->SetClass( SvGlobalName(), nCbFmt, aSvrName ); + } + } + else if( nRecType == 5 && !pMtf ) + { + sal_uInt64 nPos = rStm.Tell(); + sal_uInt16 sz[4]; + rStm.ReadBytes( sz, 8 ); + Graphic aGraphic; + if( ERRCODE_NONE == GraphicConverter::Import( rStm, aGraphic ) && aGraphic.GetType() != GraphicType::NONE ) + { + const GDIMetaFile& rMtf = aGraphic.GetGDIMetaFile(); + MakeContentStream( rDest.get(), rMtf ); + bMtfRead = true; + } + // set behind the data + rStm.Seek( nPos + nDataLen ); + } + else + rStm.SeekRel( nDataLen ); + } + } while (rStm.good() && nReadLen >= nBytesRead); + + if( !bMtfRead && pMtf ) + { + MakeContentStream( rDest.get(), *pMtf ); + return true; + } + + return false; +} + +static const char* GetInternalServerName_Impl( const SvGlobalName& aGlobName ) +{ + if ( aGlobName == SvGlobalName( SO3_SW_OLE_EMBED_CLASSID_60 ) + || aGlobName == SvGlobalName( SO3_SW_OLE_EMBED_CLASSID_8 ) ) + return "swriter"; + else if ( aGlobName == SvGlobalName( SO3_SC_OLE_EMBED_CLASSID_60 ) + || aGlobName == SvGlobalName( SO3_SC_OLE_EMBED_CLASSID_8 ) ) + return "scalc"; + else if ( aGlobName == SvGlobalName( SO3_SIMPRESS_OLE_EMBED_CLASSID_60 ) + || aGlobName == SvGlobalName( SO3_SIMPRESS_OLE_EMBED_CLASSID_8 ) ) + return "simpress"; + else if ( aGlobName == SvGlobalName( SO3_SDRAW_OLE_EMBED_CLASSID_60 ) + || aGlobName == SvGlobalName( SO3_SDRAW_OLE_EMBED_CLASSID_8 ) ) + return "sdraw"; + else if ( aGlobName == SvGlobalName( SO3_SM_OLE_EMBED_CLASSID_60 ) + || aGlobName == SvGlobalName( SO3_SM_OLE_EMBED_CLASSID_8 ) ) + return "smath"; + else if ( aGlobName == SvGlobalName( SO3_SCH_OLE_EMBED_CLASSID_60 ) + || aGlobName == SvGlobalName( SO3_SCH_OLE_EMBED_CLASSID_8 ) ) + return "schart"; + return nullptr; +} + +OUString SvxMSDffManager::GetFilterNameFromClassID( const SvGlobalName& aGlobName ) +{ + if ( aGlobName == SvGlobalName( SO3_SW_OLE_EMBED_CLASSID_60 ) ) + return "StarOffice XML (Writer)"; + + if ( aGlobName == SvGlobalName( SO3_SW_OLE_EMBED_CLASSID_8 ) ) + return "writer8"; + + if ( aGlobName == SvGlobalName( SO3_SC_OLE_EMBED_CLASSID_60 ) ) + return "StarOffice XML (Calc)"; + + if ( aGlobName == SvGlobalName( SO3_SC_OLE_EMBED_CLASSID_8 ) ) + return "calc8"; + + if ( aGlobName == SvGlobalName( SO3_SIMPRESS_OLE_EMBED_CLASSID_60 ) ) + return "StarOffice XML (Impress)"; + + if ( aGlobName == SvGlobalName( SO3_SIMPRESS_OLE_EMBED_CLASSID_8 ) ) + return "impress8"; + + if ( aGlobName == SvGlobalName( SO3_SDRAW_OLE_EMBED_CLASSID_60 ) ) + return "StarOffice XML (Draw)"; + + if ( aGlobName == SvGlobalName( SO3_SDRAW_OLE_EMBED_CLASSID_8 ) ) + return "draw8"; + + if ( aGlobName == SvGlobalName( SO3_SM_OLE_EMBED_CLASSID_60 ) ) + return "StarOffice XML (Math)"; + + if ( aGlobName == SvGlobalName( SO3_SM_OLE_EMBED_CLASSID_8 ) ) + return "math8"; + + if ( aGlobName == SvGlobalName( SO3_SCH_OLE_EMBED_CLASSID_60 ) ) + return "StarOffice XML (Chart)"; + + if ( aGlobName == SvGlobalName( SO3_SCH_OLE_EMBED_CLASSID_8 ) ) + return "chart8"; + + return OUString(); +} + +void SvxMSDffManager::ExtractOwnStream(SotStorage& rSrcStg, SvMemoryStream& rMemStream) +{ + tools::SvRef xStr + = rSrcStg.OpenSotStream("package_stream", StreamMode::STD_READ); + xStr->ReadStream(rMemStream); +} + +css::uno::Reference < css::embed::XEmbeddedObject > SvxMSDffManager::CheckForConvertToSOObj( sal_uInt32 nConvertFlags, + SotStorage& rSrcStg, const uno::Reference < embed::XStorage >& rDestStorage, + const Graphic& rGrf, + const tools::Rectangle& rVisArea, OUString const& rBaseURL) +{ + uno::Reference < embed::XEmbeddedObject > xObj; + SvGlobalName aStgNm = rSrcStg.GetClassName(); + const char* pName = GetInternalServerName_Impl( aStgNm ); + OUString sStarName; + if ( pName ) + sStarName = OUString::createFromAscii( pName ); + else if ( nConvertFlags ) + { + static struct ObjImpType + { + sal_uInt32 nFlag; + const char* pFactoryNm; + // GlobalNameId + sal_uInt32 n1; + sal_uInt16 n2, n3; + sal_uInt8 b8, b9, b10, b11, b12, b13, b14, b15; + } const aArr[] = { + { OLE_MATHTYPE_2_STARMATH, "smath", MSO_EQUATION3_CLASSID }, + { OLE_MATHTYPE_2_STARMATH, "smath", MSO_EQUATION2_CLASSID }, + { OLE_WINWORD_2_STARWRITER, "swriter", MSO_WW8_CLASSID }, + // Excel table + { OLE_EXCEL_2_STARCALC, "scalc", MSO_EXCEL5_CLASSID }, + { OLE_EXCEL_2_STARCALC, "scalc", MSO_EXCEL8_CLASSID }, + // 114465: additional Excel OLE chart classId to above. + { OLE_EXCEL_2_STARCALC, "scalc", MSO_EXCEL8_CHART_CLASSID }, + // PowerPoint presentation + { OLE_POWERPOINT_2_STARIMPRESS, "simpress", MSO_PPT8_CLASSID }, + // PowerPoint slide + { OLE_POWERPOINT_2_STARIMPRESS, "simpress", MSO_PPT8_SLIDE_CLASSID }, + { 0, nullptr, + 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0 } + }; + + for( const ObjImpType* pArr = aArr; pArr->nFlag; ++pArr ) + { + if( nConvertFlags & pArr->nFlag ) + { + SvGlobalName aTypeName( pArr->n1, pArr->n2, pArr->n3, + pArr->b8, pArr->b9, pArr->b10, pArr->b11, + pArr->b12, pArr->b13, pArr->b14, pArr->b15 ); + + if ( aStgNm == aTypeName ) + { + sStarName = OUString::createFromAscii( pArr->pFactoryNm ); + break; + } + } + } + } + + if ( sStarName.getLength() ) + { + //TODO/MBA: check if (and when) storage and stream will be destroyed! + std::shared_ptr pFilter; + SvMemoryStream aMemStream; + if ( pName ) + { + // TODO/LATER: perhaps we need to retrieve VisArea and Metafile from the storage also + SvxMSDffManager::ExtractOwnStream(rSrcStg, aMemStream); + } + else + { + tools::SvRef xStorage = new SotStorage( false, aMemStream ); + rSrcStg.CopyTo( xStorage.get() ); + xStorage->Commit(); + xStorage.clear(); + OUString aType = SfxFilter::GetTypeFromStorage( rSrcStg ); + if (aType.getLength() && !utl::ConfigManager::IsFuzzing()) + { + SfxFilterMatcher aMatch( sStarName ); + pFilter = aMatch.GetFilter4EA( aType ); + } + } + +#ifdef DEBUG_FILTER_MSFILTER + // extract embedded ole streams into "/tmp/embedded_stream_NNN" + static sal_Int32 nOleCount(0); + OUString aTmpName("/tmp/embedded_stream_"); + aTmpName += OUString::number(nOleCount++); + aTmpName += ".bin"; + SvFileStream aTmpStream(aTmpName,StreamMode::READ|StreamMode::WRITE|StreamMode::TRUNC); + xMemStream->Seek(0); + aTmpStream.WriteStream(*xMemStream); + aTmpStream.Close(); +#endif + if ( pName || pFilter ) + { + //Reuse current ole name + OUString aDstStgName = MSO_OLE_Obj + OUString::number(nMSOleObjCntr); + + OUString aFilterName; + if ( pFilter ) + aFilterName = pFilter->GetName(); + else + aFilterName = SvxMSDffManager::GetFilterNameFromClassID( aStgNm ); + + uno::Sequence aMedium(aFilterName.isEmpty() ? 3 : 4); + auto pMedium = aMedium.getArray(); + pMedium[0].Name = "InputStream"; + uno::Reference < io::XInputStream > xStream = new ::utl::OSeekableInputStreamWrapper( aMemStream ); + pMedium[0].Value <<= xStream; + pMedium[1].Name = "URL"; + pMedium[1].Value <<= OUString( "private:stream" ); + pMedium[2].Name = "DocumentBaseURL"; + pMedium[2].Value <<= rBaseURL; + + if ( !aFilterName.isEmpty() ) + { + pMedium[3].Name = "FilterName"; + pMedium[3].Value <<= aFilterName; + } + + OUString aName( aDstStgName ); + comphelper::EmbeddedObjectContainer aCnt( rDestStorage ); + xObj = aCnt.InsertEmbeddedObject(aMedium, aName, &rBaseURL); + + if ( !xObj.is() ) + { + if( !aFilterName.isEmpty() ) + { + // throw the filter parameter away as workaround + aMedium.realloc( 2 ); + xObj = aCnt.InsertEmbeddedObject(aMedium, aName, &rBaseURL); + } + + if ( !xObj.is() ) + return xObj; + } + + // JP 26.10.2001: Bug 93374 / 91928 the writer + // objects need the correct visarea needs the + // correct visarea, but this is not true for + // PowerPoint (see bugdoc 94908b) + // SJ: 19.11.2001 bug 94908, also chart objects + // needs the correct visarea + + // If pName is set this is an own embedded object, it should have the correct size internally + // TODO/LATER: it might make sense in future to set the size stored in internal object + if( !pName && ( sStarName == "swriter" || sStarName == "scalc" ) ) + { + // TODO/LATER: ViewAspect must be passed from outside! + sal_Int64 nViewAspect = embed::Aspects::MSOLE_CONTENT; + MapMode aMapMode( VCLUnoHelper::UnoEmbed2VCLMapUnit( xObj->getMapUnit( nViewAspect ) ) ); + Size aSz; + if ( rVisArea.IsEmpty() ) + aSz = lcl_GetPrefSize(rGrf, aMapMode ); + else + { + aSz = rVisArea.GetSize(); + aSz = OutputDevice::LogicToLogic( aSz, MapMode( MapUnit::Map100thMM ), aMapMode ); + } + + // don't modify the object + //TODO/LATER: remove those hacks, that needs to be done differently! + //xIPObj->EnableSetModified( sal_False ); + awt::Size aSize; + aSize.Width = aSz.Width(); + aSize.Height = aSz.Height(); + xObj->setVisualAreaSize( nViewAspect, aSize ); + //xIPObj->EnableSetModified( sal_True ); + } + else if ( sStarName == "smath" ) + { // SJ: force the object to recalc its visarea + //TODO/LATER: wait for PrinterChangeNotification + //xIPObj->OnDocumentPrinterChanged( NULL ); + } + } + } + + return xObj; +} + +// TODO/MBA: code review and testing! +SdrOle2Obj* SvxMSDffManager::CreateSdrOLEFromStorage( + SdrModel& rSdrModel, + const OUString& rStorageName, + tools::SvRef const & rSrcStorage, + const uno::Reference < embed::XStorage >& xDestStorage, + const Graphic& rGrf, + const tools::Rectangle& rBoundRect, + const tools::Rectangle& rVisArea, + SvStream* pDataStrm, + ErrCode& rError, + sal_uInt32 nConvertFlags, + sal_Int64 nRecommendedAspect, + OUString const& rBaseURL) +{ + sal_Int64 nAspect = nRecommendedAspect; + SdrOle2Obj* pRet = nullptr; + if( rSrcStorage.is() && xDestStorage.is() && rStorageName.getLength() ) + { + comphelper::EmbeddedObjectContainer aCnt( xDestStorage ); + // does the 01Ole-Stream exist at all? + // (that's not the case for e.g. Fontwork ) + // If that's not the case -> include it as graphic + bool bValidStorage = false; + OUString aDstStgName = MSO_OLE_Obj + OUString::number( ++nMSOleObjCntr ); + + { + tools::SvRef xObjStg = rSrcStorage->OpenSotStorage( rStorageName ); + if( xObjStg.is() ) + { + { + sal_uInt8 aTestA[10]; // exist the \1CompObj-Stream ? + tools::SvRef xSrcTst = xObjStg->OpenSotStream( "\1CompObj" ); + bValidStorage = xSrcTst.is() && sizeof( aTestA ) == + xSrcTst->ReadBytes(aTestA, sizeof(aTestA)); + if( !bValidStorage ) + { + // or the \1Ole-Stream ? + xSrcTst = xObjStg->OpenSotStream( "\1Ole" ); + bValidStorage = xSrcTst.is() && sizeof(aTestA) == + xSrcTst->ReadBytes(aTestA, sizeof(aTestA)); + } + } + + if( bValidStorage ) + { + if ( nAspect != embed::Aspects::MSOLE_ICON ) + { + // check whether the object is iconified one + // usually this information is already known, the only exception + // is a kind of embedded objects in Word documents + // TODO/LATER: should the caller be notified if the aspect changes in future? + + tools::SvRef xObjInfoSrc = xObjStg->OpenSotStream( + "\3ObjInfo", StreamMode::STD_READ ); + if ( xObjInfoSrc.is() && !xObjInfoSrc->GetError() ) + { + sal_uInt8 nByte = 0; + xObjInfoSrc->ReadUChar( nByte ); + if ( ( nByte >> 4 ) & embed::Aspects::MSOLE_ICON ) + nAspect = embed::Aspects::MSOLE_ICON; + } + } + + uno::Reference < embed::XEmbeddedObject > xObj( CheckForConvertToSOObj( + nConvertFlags, *xObjStg, xDestStorage, rGrf, + rVisArea, rBaseURL)); + if ( xObj.is() ) + { + // remember file name to use in the title bar + INetURLObject aURL(rBaseURL); + xObj->setContainerName(aURL.GetLastName(INetURLObject::DecodeMechanism::WithCharset)); + + svt::EmbeddedObjectRef aObj( xObj, nAspect ); + + // TODO/LATER: need MediaType + aObj.SetGraphic( rGrf, OUString() ); + + // TODO/MBA: check setting of PersistName + pRet = new SdrOle2Obj( + rSdrModel, + aObj, + OUString(), + rBoundRect); + + // we have the Object, don't create another + bValidStorage = false; + } + } + } + } + + if( bValidStorage ) + { + // object is not an own object + tools::SvRef xObjStor = SotStorage::OpenOLEStorage( xDestStorage, aDstStgName, StreamMode::READWRITE ); + + if ( xObjStor.is() ) + { + tools::SvRef xSrcStor = rSrcStorage->OpenSotStorage( rStorageName, StreamMode::READ ); + xSrcStor->CopyTo( xObjStor.get() ); + + if( !xObjStor->GetError() ) + xObjStor->Commit(); + + if( xObjStor->GetError() ) + { + rError = xObjStor->GetError(); + bValidStorage = false; + } + else if( !xObjStor.is() ) + bValidStorage = false; + } + } + else if( pDataStrm ) + { + sal_uInt32 nLen(0), nDummy(0); + pDataStrm->ReadUInt32( nLen ).ReadUInt32( nDummy ); + if( ERRCODE_NONE != pDataStrm->GetError() || + // Id in BugDoc - exist there other Ids? + // The ConvertToOle2 - does not check for consistent + 0x30008 != nDummy ) + bValidStorage = false; + else + { + // or is it an OLE-1 Stream in the DataStream? + tools::SvRef xObjStor = SotStorage::OpenOLEStorage( xDestStorage, aDstStgName ); + //TODO/MBA: remove metafile conversion from ConvertToOle2 + //when is this code used?! + GDIMetaFile aMtf; + bValidStorage = ConvertToOle2( *pDataStrm, nLen, &aMtf, xObjStor ); + xObjStor->Commit(); + } + } + + if( bValidStorage ) + { + uno::Reference < embed::XEmbeddedObject > xObj = aCnt.GetEmbeddedObject( aDstStgName ); + if( xObj.is() ) + { + // remember file name to use in the title bar + INetURLObject aURL( rBaseURL ); + xObj->setContainerName( aURL.GetLastName( INetURLObject::DecodeMechanism::WithCharset ) ); + + // the visual area must be retrieved from the metafile (object doesn't know it so far) + + if ( nAspect != embed::Aspects::MSOLE_ICON ) + { + // working with visual area can switch the object to running state + try + { + awt::Size aAwtSz; + // the provided visual area should be used, if there is any + if ( rVisArea.IsEmpty() ) + { + MapUnit aMapUnit = VCLUnoHelper::UnoEmbed2VCLMapUnit( xObj->getMapUnit( nAspect ) ); + Size aSz(lcl_GetPrefSize(rGrf, MapMode(aMapUnit))); + aAwtSz.Width = aSz.Width(); + aAwtSz.Height = aSz.Height(); + } + else + { + aAwtSz.Width = rVisArea.GetWidth(); + aAwtSz.Height = rVisArea.GetHeight(); + } + //xInplaceObj->EnableSetModified( sal_False ); + xObj->setVisualAreaSize( nAspect, aAwtSz ); + //xInplaceObj->EnableSetModified( sal_True ); + } + catch( const uno::Exception& ) + { + OSL_FAIL( "Could not set visual area of the object!" ); + } + } + + svt::EmbeddedObjectRef aObj( xObj, nAspect ); + + // TODO/LATER: need MediaType + aObj.SetGraphic( rGrf, OUString() ); + + pRet = new SdrOle2Obj( + rSdrModel, + aObj, + aDstStgName, + rBoundRect); + } + } + } + + return pRet; +} + +bool SvxMSDffManager::SetPropValue( const uno::Any& rAny, const uno::Reference< css::beans::XPropertySet > & rXPropSet, + const OUString& rPropName ) +{ + bool bRetValue = false; + try + { + uno::Reference< beans::XPropertySetInfo > + aXPropSetInfo( rXPropSet->getPropertySetInfo() ); + if ( aXPropSetInfo.is() ) + bRetValue = aXPropSetInfo->hasPropertyByName( rPropName ); + } + catch( const uno::Exception& ) + { + bRetValue = false; + } + if ( bRetValue ) + { + try + { + rXPropSet->setPropertyValue( rPropName, rAny ); + bRetValue = true; + } + catch( const uno::Exception& ) + { + bRetValue = false; + } + } + return bRetValue; +} + +SvxMSDffImportRec::SvxMSDffImportRec() + : pObj( nullptr ), + nClientAnchorLen( 0 ), + nClientDataLen( 0 ), + nXAlign( 0 ), // position n cm from left + nYAlign( 0 ), // position n cm below + nGroupShapeBooleanProperties(0), // 16 settings: LayoutInCell/AllowOverlap/BehindDocument... + nFlags( ShapeFlag::NONE ), + nDxTextLeft( 144 ), + nDyTextTop( 72 ), + nDxTextRight( 144 ), + nDyTextBottom( 72 ), + nDxWrapDistLeft( 0 ), + nDyWrapDistTop( 0 ), + nDxWrapDistRight( 0 ), + nDyWrapDistBottom(0 ), + nCropFromTop( 0 ), + nCropFromBottom( 0 ), + nCropFromLeft( 0 ), + nCropFromRight( 0 ), + nNextShapeId( 0 ), + nShapeId( 0 ), + eShapeType( mso_sptNil ), + relativeHorizontalWidth( -1 ), + isHorizontalRule( false ) +{ + eLineStyle = mso_lineSimple; // GPF-Bug #66227# + eLineDashing = mso_lineSolid; + bDrawHell = false; + bHidden = false; + + bReplaceByFly = false; + bVFlip = false; + bHFlip = false; + bAutoWidth = false; +} + +SvxMSDffImportRec::SvxMSDffImportRec(const SvxMSDffImportRec& rCopy) + : pObj( rCopy.pObj ), + nXAlign( rCopy.nXAlign ), + nXRelTo( rCopy.nXRelTo ), + nYAlign( rCopy.nYAlign ), + nYRelTo( rCopy.nYRelTo ), + nGroupShapeBooleanProperties(rCopy.nGroupShapeBooleanProperties), + nFlags( rCopy.nFlags ), + nDxTextLeft( rCopy.nDxTextLeft ), + nDyTextTop( rCopy.nDyTextTop ), + nDxTextRight( rCopy.nDxTextRight ), + nDyTextBottom( rCopy.nDyTextBottom ), + nDxWrapDistLeft( rCopy.nDxWrapDistLeft ), + nDyWrapDistTop( rCopy.nDyWrapDistTop ), + nDxWrapDistRight( rCopy.nDxWrapDistRight ), + nDyWrapDistBottom(rCopy.nDyWrapDistBottom ), + nCropFromTop( rCopy.nCropFromTop ), + nCropFromBottom( rCopy.nCropFromBottom ), + nCropFromLeft( rCopy.nCropFromLeft ), + nCropFromRight( rCopy.nCropFromRight ), + aTextId( rCopy.aTextId ), + nNextShapeId( rCopy.nNextShapeId ), + nShapeId( rCopy.nShapeId ), + eShapeType( rCopy.eShapeType ), + relativeHorizontalWidth( rCopy.relativeHorizontalWidth ), + isHorizontalRule( rCopy.isHorizontalRule ) +{ + eLineStyle = rCopy.eLineStyle; // GPF-Bug #66227# + eLineDashing = rCopy.eLineDashing; + bDrawHell = rCopy.bDrawHell; + bHidden = rCopy.bHidden; + bReplaceByFly = rCopy.bReplaceByFly; + bAutoWidth = rCopy.bAutoWidth; + bVFlip = rCopy.bVFlip; + bHFlip = rCopy.bHFlip; + nClientAnchorLen = rCopy.nClientAnchorLen; + if( rCopy.nClientAnchorLen ) + { + pClientAnchorBuffer.reset( new char[ nClientAnchorLen ] ); + memcpy( pClientAnchorBuffer.get(), + rCopy.pClientAnchorBuffer.get(), + nClientAnchorLen ); + } + else + pClientAnchorBuffer = nullptr; + + nClientDataLen = rCopy.nClientDataLen; + if( rCopy.nClientDataLen ) + { + pClientDataBuffer.reset( new char[ nClientDataLen ] ); + memcpy( pClientDataBuffer.get(), + rCopy.pClientDataBuffer.get(), + nClientDataLen ); + } + else + pClientDataBuffer = nullptr; + + if (rCopy.pWrapPolygon) + pWrapPolygon = rCopy.pWrapPolygon; +} + +SvxMSDffImportRec::~SvxMSDffImportRec() +{ +} + +void SvxMSDffManager::insertShapeId( sal_Int32 nShapeId, SdrObject* pShape ) +{ + maShapeIdContainer[nShapeId] = pShape; +} + +void SvxMSDffManager::removeShapeId( SdrObject const * pShape ) +{ + SvxMSDffShapeIdContainer::iterator aIter = std::find_if(maShapeIdContainer.begin(), maShapeIdContainer.end(), + [&pShape](const SvxMSDffShapeIdContainer::value_type& rEntry) { return rEntry.second == pShape; }); + if (aIter != maShapeIdContainer.end()) + maShapeIdContainer.erase( aIter ); +} + +SdrObject* SvxMSDffManager::getShapeForId( sal_Int32 nShapeId ) +{ + SvxMSDffShapeIdContainer::iterator aIter( maShapeIdContainer.find(nShapeId) ); + return aIter != maShapeIdContainer.end() ? (*aIter).second : nullptr; +} + +SvxMSDffImportData::SvxMSDffImportData(const tools::Rectangle& rParentRect) + : aParentRect(rParentRect) +{ +} + +SvxMSDffImportData::~SvxMSDffImportData() +{ +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/filter/source/msfilter/msfilter.component b/filter/source/msfilter/msfilter.component new file mode 100644 index 000000000..0e1a35ee4 --- /dev/null +++ b/filter/source/msfilter/msfilter.component @@ -0,0 +1,26 @@ + + + + + + + + diff --git a/filter/source/msfilter/msocximex.cxx b/filter/source/msfilter/msocximex.cxx new file mode 100644 index 000000000..e3ccf6524 --- /dev/null +++ b/filter/source/msfilter/msocximex.cxx @@ -0,0 +1,146 @@ +/* -*- 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +using namespace ::com::sun::star; + +constexpr OUStringLiteral sWW8_form( u"WW-Standard" ); + +SvxMSConvertOCXControls::SvxMSConvertOCXControls( uno::Reference< frame::XModel > xModel) : mxModel(std::move(xModel)) +{ +} + +SvxMSConvertOCXControls::~SvxMSConvertOCXControls() +{ +} + +void SvxMSConvertOCXControls::GetDrawPage() +{ + if( !xDrawPage.is() && mxModel.is() ) + { + uno::Reference< drawing::XDrawPageSupplier > xTxtDoc(mxModel, + uno::UNO_QUERY); + OSL_ENSURE(xTxtDoc.is(),"no XDrawPageSupplier from XModel"); + xDrawPage = xTxtDoc->getDrawPage(); + OSL_ENSURE( xDrawPage.is(), "no XDrawPage" ); + } +} + + +const uno::Reference< lang::XMultiServiceFactory >& + SvxMSConvertOCXControls::GetServiceFactory() +{ + if( !xServiceFactory.is() && mxModel.is() ) + { + xServiceFactory = uno::Reference< lang::XMultiServiceFactory > + (mxModel, uno::UNO_QUERY); + OSL_ENSURE( xServiceFactory.is(), + "no XMultiServiceFactory from doc Model" ); + } + + return xServiceFactory; +} + +const uno::Reference< drawing::XShapes >& SvxMSConvertOCXControls::GetShapes() +{ + if( !xShapes.is() ) + { + GetDrawPage(); + if( xDrawPage.is() ) + { + xShapes = xDrawPage; + } + } + return xShapes; +} + +const uno::Reference< container::XIndexContainer >& + SvxMSConvertOCXControls::GetFormComps() +{ + if( !xFormComps.is() ) + { + GetDrawPage(); + if( xDrawPage.is() ) + { + uno::Reference< form::XFormsSupplier > xFormsSupplier( xDrawPage, + uno::UNO_QUERY ); + OSL_ENSURE( xFormsSupplier.is(), + "UNO_QUERY failed for XFormsSupplier from XDrawPage" ); + + uno::Reference< container::XNameContainer > xNameCont = + xFormsSupplier->getForms(); + + // The form gets a new name like "WW-Standard[n]" and will + // created new in any case. + OUString sName( sWW8_form ); + sal_uInt16 n = 0; + + while( xNameCont->hasByName( sName ) ) + { + sName = sWW8_form + OUString::number( ++n ); + } + + const uno::Reference< lang::XMultiServiceFactory > &rServiceFactory + = GetServiceFactory(); + if( !rServiceFactory.is() ) + return xFormComps; + + uno::Reference< uno::XInterface > xCreate = + rServiceFactory->createInstance( + "com.sun.star.form.component.Form" ); + if( xCreate.is() ) + { + uno::Reference< beans::XPropertySet > xFormPropSet( xCreate, + uno::UNO_QUERY ); + + uno::Any aTmp(&sName,cppu::UnoType::get()); + xFormPropSet->setPropertyValue( "Name", aTmp ); + + uno::Reference< form::XForm > xForm( xCreate, uno::UNO_QUERY ); + OSL_ENSURE(xForm.is(), "no Form?"); + + uno::Reference< container::XIndexContainer > xForms( xNameCont, + uno::UNO_QUERY ); + OSL_ENSURE( xForms.is(), "XForms not available" ); + + aTmp <<= xForm; + xForms->insertByIndex( xForms->getCount(), aTmp ); + + xFormComps = uno::Reference< container::XIndexContainer > + (xCreate, uno::UNO_QUERY); + } + } + } + + return xFormComps; +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/filter/source/msfilter/msoleexp.cxx b/filter/source/msfilter/msoleexp.cxx new file mode 100644 index 000000000..9e68e5cb0 --- /dev/null +++ b/filter/source/msfilter/msoleexp.cxx @@ -0,0 +1,340 @@ +/* -*- 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +using namespace ::com::sun::star; + +static SvGlobalName GetEmbeddedVersion( const SvGlobalName& aAppName ) +{ + if ( aAppName == SvGlobalName( SO3_SM_CLASSID_60 ) ) + return SvGlobalName( SO3_SM_OLE_EMBED_CLASSID_8 ); + else if ( aAppName == SvGlobalName( SO3_SW_CLASSID_60 ) ) + return SvGlobalName( SO3_SW_OLE_EMBED_CLASSID_8 ); + else if ( aAppName == SvGlobalName( SO3_SC_CLASSID_60 ) ) + return SvGlobalName( SO3_SC_OLE_EMBED_CLASSID_8 ); + else if ( aAppName == SvGlobalName( SO3_SDRAW_CLASSID_60 ) ) + return SvGlobalName( SO3_SDRAW_OLE_EMBED_CLASSID_8 ); + else if ( aAppName == SvGlobalName( SO3_SIMPRESS_CLASSID_60 ) ) + return SvGlobalName( SO3_SIMPRESS_OLE_EMBED_CLASSID_8 ); + else if ( aAppName == SvGlobalName( SO3_SCH_CLASSID_60 ) ) + return SvGlobalName( SO3_SCH_OLE_EMBED_CLASSID_8 ); + + return SvGlobalName(); +} + +static OUString GetStorageType( const SvGlobalName& aEmbName ) +{ + if ( aEmbName == SvGlobalName( SO3_SM_OLE_EMBED_CLASSID_8 ) ) + return "LibreOffice.MathDocument.1"; + else if ( aEmbName == SvGlobalName( SO3_SW_OLE_EMBED_CLASSID_8 ) ) + return "LibreOffice.WriterDocument.1"; + else if ( aEmbName == SvGlobalName( SO3_SC_OLE_EMBED_CLASSID_8 ) ) + return "LibreOffice.CalcDocument.1"; + else if ( aEmbName == SvGlobalName( SO3_SDRAW_OLE_EMBED_CLASSID_8 ) ) + return "LibreOffice.DrawDocument.1"; + else if ( aEmbName == SvGlobalName( SO3_SIMPRESS_OLE_EMBED_CLASSID_8 ) ) + return "LibreOffice.ImpressDocument.1"; + else if ( aEmbName == SvGlobalName( SO3_SCH_OLE_EMBED_CLASSID_8 ) ) + return "LibreOffice.ChartDocument.1"; + return OUString(); +} + +static bool UseOldMSExport() +{ + uno::Reference< lang::XMultiServiceFactory > xProvider( + configuration::theDefaultProvider::get( + comphelper::getProcessComponentContext())); + try { + uno::Sequence< uno::Any > aArg{ uno::Any( + OUString( "/org.openoffice.Office.Common/InternalMSExport" )) }; + uno::Reference< container::XNameAccess > xNameAccess( + xProvider->createInstanceWithArguments( + "com.sun.star.configuration.ConfigurationUpdateAccess", + aArg ), + uno::UNO_QUERY ); + if ( xNameAccess.is() ) + { + uno::Any aResult = xNameAccess->getByName( "UseOldExport" ); + + bool bResult; + if ( aResult >>= bResult ) + return bResult; + } + } + catch( const uno::Exception& ) + { + } + + OSL_FAIL( "Could not get access to configuration entry!" ); + return false; +} + +void SvxMSExportOLEObjects::ExportOLEObject( const css::uno::Reference < css::embed::XEmbeddedObject>& rObj, SotStorage& rDestStg ) +{ + svt::EmbeddedObjectRef aObj( rObj, embed::Aspects::MSOLE_CONTENT ); + ExportOLEObject( aObj, rDestStg ); +} + +void SvxMSExportOLEObjects::ExportOLEObject( svt::EmbeddedObjectRef const & rObj, SotStorage& rDestStg ) +{ + SvGlobalName aOwnGlobalName; + SvGlobalName aObjName( rObj->getClassID() ); + std::shared_ptr pExpFilter; + { + static struct ObjExpType { + sal_uInt32 nFlag; + const char* pFilterNm; + // GlobalNameId + struct GlobalNameIds { + sal_uInt32 n1; + sal_uInt16 n2, n3; + sal_uInt8 b8, b9, b10, b11, b12, b13, b14, b15; + } + aGlNmIds[4]; + } const aArr[] = { + { OLE_STARMATH_2_MATHTYPE, "MathType 3.x", + {{SO3_SM_CLASSID_60}, {SO3_SM_CLASSID_50}, + {SO3_SM_CLASSID_40}, {SO3_SM_CLASSID_30 }}}, + { OLE_STARWRITER_2_WINWORD, "MS Word 97", + {{SO3_SW_CLASSID_60}, {SO3_SW_CLASSID_50}, + {SO3_SW_CLASSID_40}, {SO3_SW_CLASSID_30 }}}, + { OLE_STARCALC_2_EXCEL, "MS Excel 97", + {{SO3_SC_CLASSID_60}, {SO3_SC_CLASSID_50}, + {SO3_SC_CLASSID_40}, {SO3_SC_CLASSID_30 }}}, + { OLE_STARIMPRESS_2_POWERPOINT, "MS PowerPoint 97", + {{SO3_SIMPRESS_CLASSID_60}, {SO3_SIMPRESS_CLASSID_50}, + {SO3_SIMPRESS_CLASSID_40}, {SO3_SIMPRESS_CLASSID_30 }}}, + { 0, "", + {{SO3_SCH_CLASSID_60}, {SO3_SCH_CLASSID_50}, + {SO3_SCH_CLASSID_40}, {SO3_SCH_CLASSID_30 }}}, + { 0, "", + {{SO3_SDRAW_CLASSID_60}, {SO3_SDRAW_CLASSID_50}, // SJ: !!!! SO3_SDRAW_CLASSID is only available up from + {SO3_SDRAW_CLASSID_60}, {SO3_SDRAW_CLASSID_50 }}}, // ver 5.0, it is purpose to have double entries here. + + { 0xffff,nullptr, + {{SO3_SDRAW_CLASSID_60}, {SO3_SDRAW_CLASSID_50}, + {SO3_SDRAW_CLASSID_60}, {SO3_SDRAW_CLASSID_50}}} + }; + + for( const ObjExpType* pArr = aArr; !pExpFilter && ( pArr->nFlag != 0xffff ); ++pArr ) + { + for (const ObjExpType::GlobalNameIds& rId : pArr->aGlNmIds) + { + SvGlobalName aGlbNm( rId.n1, rId.n2, rId.n3, + rId.b8, rId.b9, rId.b10, rId.b11, + rId.b12, rId.b13, rId.b14, rId.b15 ); + if( aObjName == aGlbNm ) + { + aOwnGlobalName = aGlbNm; + + // flags for checking if conversion is wanted at all (SaveOptions?!) + if( nConvertFlags & pArr->nFlag ) + { + pExpFilter = SfxFilterMatcher().GetFilter4FilterName(OUString::createFromAscii(pArr->pFilterNm)); + break; + } + } + } + } + } + + if( pExpFilter ) // use this filter for the export + { + try + { + if ( rObj->getCurrentState() == embed::EmbedStates::LOADED ) + rObj->changeState( embed::EmbedStates::RUNNING ); + //TODO/LATER: is stream instead of outputstream a better choice?! + //TODO/LATER: a "StoreTo" method at embedded object would be nice + SvStream* pStream = new SvMemoryStream; + ::uno::Reference < io::XOutputStream > xOut = new ::utl::OOutputStreamWrapper( *pStream ); + uno::Sequence < beans::PropertyValue > aSeq{ + comphelper::makePropertyValue("OutputStream", xOut), + comphelper::makePropertyValue("FilterName", pExpFilter->GetName()) + }; + uno::Reference < frame::XStorable > xStor( rObj->getComponent(), uno::UNO_QUERY ); + try + { + xStor->storeToURL( "private:stream", aSeq ); + } + catch( const uno::Exception& ) {} // #TODO really handle exceptions - interactionalhandler etc. ? + + tools::SvRef xOLEStor = new SotStorage( pStream, true ); + xOLEStor->CopyTo( &rDestStg ); + rDestStg.Commit(); + } + catch( const uno::Exception& ) + { + // TODO/LATER: Error handling + OSL_FAIL( "The object could not be exported!" ); + } + } + else if( aOwnGlobalName != SvGlobalName() ) + { + // own format, maybe SO6 format or lower + SvGlobalName aEmbName = GetEmbeddedVersion( aOwnGlobalName ); + if ( aEmbName != SvGlobalName() && !UseOldMSExport() ) + { + // this is a SO6 embedded object, save in old binary format + rDestStg.SetVersion( SOFFICE_FILEFORMAT_31 ); + rDestStg.SetClass( aEmbName, + SotClipboardFormatId::EMBEDDED_OBJ_OLE, + GetStorageType( aEmbName ) ); + tools::SvRef xExtStm = rDestStg.OpenSotStream( + "properties_stream"); + + bool bExtentSuccess = false; + if( !xExtStm->GetError() ) + { + // write extent + //TODO/MBA: check if writing a size is enough + if( rObj.GetObject().is() ) + { + // MSOLE objects don't need to be in running state for VisualArea access + awt::Size aSize; + try + { + // this is an own object, the content size must be stored in the + // extension stream + aSize = rObj->getVisualAreaSize( embed::Aspects::MSOLE_CONTENT ); + } + catch( const embed::NoVisualAreaSizeException& ) + { + OSL_FAIL( "Could not get visual area size!" ); + aSize.Width = 5000; + aSize.Height = 5000; + } + catch( const uno::Exception& ) + { + TOOLS_WARN_EXCEPTION( + "filter.ms", "Unexpected exception while getting visual area size!"); + aSize.Width = 5000; + aSize.Height = 5000; + } + + sal_Int32 pRect[4]; + pRect[0] = 0; + pRect[1] = aSize.Width; + pRect[2] = 0; + pRect[3] = aSize.Height; + + sal_Int8 aWriteSet[16]; + for ( int ind = 0; ind < 4; ind++ ) + { + sal_Int32 nVal = pRect[ind]; + for ( int nByte = 0; nByte < 4; nByte++ ) + { + aWriteSet[ind*4+nByte] = static_cast(nVal) % 0x100; + nVal /= 0x100; + } + } + + bExtentSuccess = (xExtStm->WriteBytes(aWriteSet, 16) == 16); + } + } + + if ( bExtentSuccess ) + { + tools::SvRef xEmbStm = rDestStg.OpenSotStream( + "package_stream"); + if( !xEmbStm->GetError() ) + { + try + { + if ( rObj->getCurrentState() == embed::EmbedStates::LOADED ) + rObj->changeState( embed::EmbedStates::RUNNING ); + //TODO/LATER: is stream instead of outputstream a better choice?! + //TODO/LATER: a "StoreTo" method at embedded object would be nice + ::uno::Reference < io::XOutputStream > xOut = new ::utl::OOutputStreamWrapper( *xEmbStm ); + uno::Sequence < beans::PropertyValue > aSeq{ comphelper::makePropertyValue( + "OutputStream", xOut) }; + uno::Reference < frame::XStorable > xStor( rObj->getComponent(), uno::UNO_QUERY ); + xStor->storeToURL( "private:stream", aSeq ); + } + catch( const uno::Exception& ) + { + // TODO/LATER: Error handling + OSL_FAIL( "The object could not be exported!" ); + } + } + } + } + else + { + OSL_FAIL("Own binary format inside own container document!"); + } + } + else + { + // alien objects + //TODO/LATER: a "StoreTo" method at embedded object would be nice + rDestStg.SetVersion( SOFFICE_FILEFORMAT_31 ); + uno::Reference < embed::XStorage > xStor = ::comphelper::OStorageHelper::GetTemporaryStorage(); + uno::Reference < embed::XEmbedPersist > xPers( rObj.GetObject(), uno::UNO_QUERY ); + if ( xPers.is() ) + { + uno::Sequence < beans::PropertyValue > aEmptySeq; + OUString aTempName( "bla" ); + try + { + xPers->storeToEntry( xStor, aTempName, aEmptySeq, aEmptySeq ); + } + catch ( const uno::Exception& ) + {} + + tools::SvRef xOLEStor = SotStorage::OpenOLEStorage( xStor, aTempName, StreamMode::STD_READ ); + xOLEStor->CopyTo( &rDestStg ); + rDestStg.Commit(); + } + } + + //We never need this stream: See #99809# and #i2179# + rDestStg.Remove( SVEXT_PERSIST_STREAM ); +} + + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/filter/source/msfilter/mstoolbar.cxx b/filter/source/msfilter/mstoolbar.cxx new file mode 100644 index 000000000..aa781c471 --- /dev/null +++ b/filter/source/msfilter/mstoolbar.cxx @@ -0,0 +1,824 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +using namespace com::sun::star; + +int TBBase::nIndent = 0; + +void CustomToolBarImportHelper::ScaleImage( uno::Reference< graphic::XGraphic >& xGraphic, tools::Long nNewSize ) +{ + Graphic aGraphic( xGraphic ); + Size aSize = aGraphic.GetSizePixel(); + if ( aSize.Height() && ( aSize.Height() == aSize.Width() ) ) + { + Graphic aImage(xGraphic); + if ( aSize.Height() != nNewSize ) + { + BitmapEx aBitmap = aImage.GetBitmapEx(); + BitmapEx aBitmapex = BitmapEx::AutoScaleBitmap(aBitmap, nNewSize ); + aImage = Graphic(aBitmapex); + xGraphic = aImage.GetXGraphic(); + } + } +} + +void CustomToolBarImportHelper::applyIcons() +{ + for (auto const& concommand : iconcommands) + { + uno::Sequence commands { concommand.sCommand }; + uno::Sequence< uno::Reference< graphic::XGraphic > > images { concommand.image }; + auto pimages = images.getArray(); + + uno::Reference< ui::XImageManager > xImageManager( getCfgManager()->getImageManager(), uno::UNO_QUERY_THROW ); + sal_uInt16 nColor = ui::ImageType::COLOR_NORMAL; + + vcl::Window* topwin = Application::GetActiveTopWindow(); + if ( topwin != nullptr && topwin->GetBackgroundColor().IsDark() ) + nColor = css::ui::ImageType::COLOR_HIGHCONTRAST; + + ScaleImage( pimages[ 0 ], 16 ); + xImageManager->replaceImages( ui::ImageType::SIZE_DEFAULT | nColor, commands, images ); + ScaleImage( pimages[ 0 ], 26 ); + xImageManager->replaceImages( ui::ImageType::SIZE_LARGE | nColor, commands, images ); + } +} + +void CustomToolBarImportHelper::addIcon( const uno::Reference< graphic::XGraphic >& xImage, const OUString& sString ) +{ + iconcontrolitem item; + item.sCommand = sString; + item.image = xImage; + iconcommands.push_back( item ); +} + +CustomToolBarImportHelper::CustomToolBarImportHelper( SfxObjectShell& rDocShell, const css::uno::Reference< css::ui::XUIConfigurationManager>& rxAppCfgMgr ) : mrDocSh( rDocShell ) +{ + m_xCfgSupp.set( mrDocSh.GetModel(), uno::UNO_QUERY_THROW ); + m_xAppCfgMgr.set( rxAppCfgMgr, uno::UNO_SET_THROW ); +} + +uno::Reference< ui::XUIConfigurationManager > +CustomToolBarImportHelper::getCfgManager() +{ + return m_xCfgSupp->getUIConfigurationManager(); +} + + +uno::Any +CustomToolBarImportHelper::createCommandFromMacro( std::u16string_view sCmd ) +{ + //"vnd.sun.star.script:Standard.Module1.Main?language=Basic&location=document" + // create script url + OUString scriptURL + = OUString::Concat("vnd.sun.star.script:") + sCmd + "?language=Basic&location=document"; + return uno::Any( scriptURL ); +} + +OUString CustomToolBarImportHelper::MSOCommandToOOCommand( sal_Int16 msoCmd ) +{ + OUString result; + if (pMSOCmdConvertor) + result = pMSOCmdConvertor->MSOCommandToOOCommand( msoCmd ); + return result; +} + +OUString CustomToolBarImportHelper::MSOTCIDToOOCommand( sal_Int16 msoTCID ) +{ + OUString result; + if (pMSOCmdConvertor) + result = pMSOCmdConvertor->MSOTCIDToOOCommand( msoTCID ); + return result; +} + +bool +CustomToolBarImportHelper::createMenu( const OUString& rName, const uno::Reference< container::XIndexAccess >& xMenuDesc ) +{ + bool bRes = true; + try + { + uno::Reference< ui::XUIConfigurationManager > xCfgManager( getCfgManager() ); + OUString sMenuBar = "private:resource/menubar/" + rName; + uno::Reference< container::XIndexContainer > xPopup( xCfgManager->createSettings(), uno::UNO_SET_THROW ); + uno::Reference< beans::XPropertySet > xProps( xPopup, uno::UNO_QUERY_THROW ); + // set name for menubar + xProps->setPropertyValue("UIName", uno::Any( rName ) ); + if ( xPopup.is() ) + { + uno::Sequence< beans::PropertyValue > aPopupMenu{ + comphelper::makePropertyValue("CommandURL", "vnd.openoffice.org:" + rName), + comphelper::makePropertyValue("Label", rName), + comphelper::makePropertyValue("ItemDescriptorContainer", xMenuDesc), + comphelper::makePropertyValue("Type", sal_Int32( 0 )) + }; + + xPopup->insertByIndex( xPopup->getCount(), uno::Any( aPopupMenu ) ); + xCfgManager->insertSettings( sMenuBar, xPopup ); + uno::Reference< ui::XUIConfigurationPersistence > xPersistence( xCfgManager, uno::UNO_QUERY_THROW ); + xPersistence->store(); + } + } + catch( const uno::Exception& ) + { + bRes = false; + } + return bRes; +} + +#ifdef DEBUG_FILTER_MSTOOLBAR +void TBBase::indent_printf( FILE* fp, const char* format, ... ) +{ + va_list ap; + va_start ( ap, format ); + + // indent nIndent spaces + for ( int i=0; i(); + height = std::make_shared(); + rS.ReadUInt16( *width ).ReadUInt16( *height ); + } + return true; +} + +#ifdef DEBUG_FILTER_MSTOOLBAR +void TBCHeader::Print( FILE* fp ) +{ + Indent a; + indent_printf(fp,"[ 0x%x ] TBCHeader -- dump\n", nOffSet ); + indent_printf(fp," bSignature 0x%x\n", bSignature ); + indent_printf(fp," bVersion 0x%x\n", bVersion ); + indent_printf(fp," bFlagsTCR 0x%x\n", bFlagsTCR ); + indent_printf(fp," tct 0x%x\n", tct ); + indent_printf(fp," tcid 0x%x\n", tcid ); + indent_printf(fp," tbct 0x%x\n", static_cast< unsigned int >( tbct )); + indent_printf(fp," bPriority 0x%x\n", bPriority ); + if ( width.get() ) + indent_printf(fp," width %d(0x%x)\n", *width, *width); + if ( height.get() ) + indent_printf(fp," height %d(0x%x)\n", *height, *height); +} +#endif + +TBCData::TBCData( TBCHeader Header ) : rHeader(std::move( Header )) +{ +} + +bool TBCData::Read(SvStream &rS) +{ + SAL_INFO("filter.ms", "stream pos " << rS.Tell()); + nOffSet = rS.Tell(); + if ( !controlGeneralInfo.Read(rS) /*|| !controlSpecificInfo.Read(rS)*/ ) + return false; + switch ( rHeader.getTct() ) + { + case 0x01: // (Button control) + case 0x10: // (ExpandingGrid control) + controlSpecificInfo = std::make_shared(); + break; + case 0x0A: // (Popup control) + case 0x0C: // (ButtonPopup control) + case 0x0D: // (SplitButtonPopup control) + case 0x0E: // (SplitButtonMRUPopup control) + controlSpecificInfo = std::make_shared(); + break; + case 0x02: // (Edit control) + case 0x04: // (ComboBox control) + case 0x14: // (GraphicCombo control) + case 0x03: // (DropDown control) + case 0x06: // (SplitDropDown control) + case 0x09: // (GraphicDropDown control) + controlSpecificInfo = std::make_shared( rHeader ); + break; + default: + break; + } + if ( controlSpecificInfo ) + return controlSpecificInfo->Read( rS ); + //#FIXME I need to be able to handle different controlSpecificInfo types. + return true; +} + +TBCMenuSpecific* TBCData::getMenuSpecific() +{ + TBCMenuSpecific* pMenu = dynamic_cast< TBCMenuSpecific* >( controlSpecificInfo.get() ); + return pMenu; +} +void TBCData::ImportToolBarControl( CustomToolBarImportHelper& helper, std::vector< css::beans::PropertyValue >& props, bool& bBeginGroup, bool bIsMenuBar ) +{ + sal_uInt16 nStyle = 0; + bBeginGroup = rHeader.isBeginGroup(); + controlGeneralInfo.ImportToolBarControlData( helper, props ); + beans::PropertyValue aProp; + aProp.Name = "Visible"; + aProp.Value <<= rHeader.isVisible(); // where is the visible attribute stored + props.push_back( aProp ); + if ( rHeader.getTct() == 0x01 + || rHeader.getTct() == 0x10 ) + { + TBCBSpecific* pSpecificInfo = dynamic_cast< TBCBSpecific* >( controlSpecificInfo.get() ); + if ( pSpecificInfo ) + { + // if we have an icon then lets set it for the command + OUString sCommand; + for (auto const& property : props) + { + // TODO JNA : couldn't we break if we find CommandURL to avoid keeping on the loop? + if ( property.Name == "CommandURL" ) + property.Value >>= sCommand; + } + if ( TBCBitMap* pIcon = pSpecificInfo->getIcon() ) + { + // Without a command openoffice won't display the icon + if ( !sCommand.isEmpty() ) + { + BitmapEx aBitEx( pIcon->getBitMap() ); + TBCBitMap* pIconMask = pSpecificInfo->getIconMask(); + if (pIconMask) + { + const Bitmap& rMaskBase(pIconMask->getBitMap().GetBitmap()); + Size aMaskSize = rMaskBase.GetSizePixel(); + if (aMaskSize.Width() && aMaskSize.Height()) + { + // according to the spec: + // "the iconMask is white in all the areas in which the icon is + // displayed as transparent and is black in all other areas." + aBitEx = BitmapEx(aBitEx.GetBitmap(), rMaskBase.CreateMask(COL_WHITE)); + } + } + + Graphic aGraphic( aBitEx ); + helper.addIcon( aGraphic.GetXGraphic(), sCommand ); + } + } + else if ( pSpecificInfo->getBtnFace() ) + { + + OUString sBuiltInCmd = helper.MSOTCIDToOOCommand( *pSpecificInfo->getBtnFace() ); + if ( !sBuiltInCmd.isEmpty() ) + { + uno::Sequence sCmds { sBuiltInCmd }; + uno::Reference< ui::XImageManager > xImageManager( helper.getAppCfgManager()->getImageManager(), uno::UNO_QUERY_THROW ); + // 0 = default image size + uno::Sequence< uno::Reference< graphic::XGraphic > > sImages = xImageManager->getImages( 0, sCmds ); + if ( sImages.hasElements() && sImages[0].is() ) + helper.addIcon( sImages[0], sCommand ); + } + } + } + } + else if ( rHeader.getTct() == 0x0a ) + { + aProp.Name = "CommandURL"; + + TBCMenuSpecific* pMenu = getMenuSpecific(); + if ( pMenu ) + { + OUString sMenuBar = "private:resource/menubar/" + pMenu->Name(); + aProp.Value <<= sMenuBar; // name of popup + } + nStyle |= ui::ItemStyle::DROP_DOWN; + props.push_back( aProp ); + } + + short icontext = ( rHeader.getTbct() & 0x03 ); + aProp.Name = "Style"; + if ( bIsMenuBar ) + { + nStyle |= ui::ItemStyle::TEXT; + if ( !icontext || icontext == 0x3 ) + // Text And image + nStyle |= ui::ItemStyle::ICON; + } + else + { + if ( ( icontext & 0x02 ) == 0x02 ) + nStyle |= ui::ItemStyle::TEXT; + if ( !icontext || ( icontext & 0x03 ) == 0x03 ) + nStyle |= ui::ItemStyle::ICON; + } + aProp.Value <<= nStyle; + props.push_back( aProp ); +} + +#ifdef DEBUG_FILTER_MSTOOLBAR +void TBCData::Print( FILE* fp ) +{ + Indent a; + indent_printf(fp,"[ 0x%x ] TBCData -- dump\n", nOffSet ); + indent_printf(fp," dumping controlGeneralInfo( TBCGeneralInfo )\n"); + controlGeneralInfo.Print( fp ); + //if ( rHeader.getTct() == 1 ) + if ( controlSpecificInfo.get() ) + { + indent_printf(fp," dumping controlSpecificInfo( TBCBSpecificInfo )\n"); + controlSpecificInfo->Print( fp ); + } +} +#endif + +bool +WString::Read( SvStream &rS ) +{ + SAL_INFO("filter.ms", "stream pos " << rS.Tell()); + nOffSet = rS.Tell(); + sal_uInt8 nChars = 0; + rS.ReadUChar( nChars ); + sString = read_uInt16s_ToOUString(rS, nChars); + return true; +} + +TBCExtraInfo::TBCExtraInfo() + : idHelpContext(0) + , tbcu(0) + , tbmg(0) +{ +} + +bool +TBCExtraInfo::Read( SvStream &rS ) +{ + SAL_INFO("filter.ms", "stream pos " << rS.Tell()); + nOffSet = rS.Tell(); + if( !wstrHelpFile.Read( rS ) ) + return false; + + rS.ReadInt32( idHelpContext ); + + if ( !wstrTag.Read( rS ) || !wstrOnAction.Read( rS ) || !wstrParam.Read( rS ) ) + return false; + + rS.ReadSChar( tbcu ).ReadSChar( tbmg ); + return true; +} + +#ifdef DEBUG_FILTER_MSTOOLBAR +void +TBCExtraInfo::Print( FILE* fp ) +{ + Indent a; + indent_printf( fp, "[ 0x%x ] TBCExtraInfo -- dump\n", nOffSet ); + indent_printf( fp, " wstrHelpFile %s\n", + OUStringToOString( wstrHelpFile.getString(), RTL_TEXTENCODING_UTF8 ).getStr() ); + indent_printf( fp, " idHelpContext 0x%x\n", static_cast< unsigned int >( idHelpContext ) ); + indent_printf( fp, " wstrTag %s\n", + OUStringToOString( wstrTag.getString(), RTL_TEXTENCODING_UTF8 ).getStr() ); + indent_printf( fp, " wstrOnAction %s\n", + OUStringToOString( wstrOnAction.getString(), RTL_TEXTENCODING_UTF8 ).getStr() ); + indent_printf( fp, " wstrParam %s\n", + OUStringToOString( wstrParam.getString(), RTL_TEXTENCODING_UTF8 ).getStr() ); + indent_printf( fp, " tbcu 0x%x\n", tbcu ); + indent_printf( fp, " tbmg 0x%x\n", tbmg ); +} +#endif + +OUString const & +TBCExtraInfo::getOnAction() const +{ + return wstrOnAction.getString(); +} + +TBCGeneralInfo::TBCGeneralInfo() : bFlags( 0 ) +{ +} + +bool TBCGeneralInfo::Read( SvStream &rS ) +{ + SAL_INFO("filter.ms", "stream pos " << rS.Tell()); + nOffSet = rS.Tell(); + rS.ReadUChar( bFlags ); + + if ( ( bFlags & 0x1 ) && !customText.Read( rS ) ) + return false; + if ( ( bFlags & 0x2 ) && ( !descriptionText.Read( rS ) || !tooltip.Read( rS ) ) ) + return false; + if ( ( bFlags & 0x4 ) && !extraInfo.Read( rS ) ) + return false; + return true; +} + +#ifdef DEBUG_FILTER_MSFILTER +void +TBCGeneralInfo::Print( FILE* fp ) +{ + Indent a; + indent_printf( fp, "[ 0x%x ] TBCGeneralInfo -- dump\n", nOffSet ); + indent_printf( fp, " bFlags 0x%x\n", bFlags ); + indent_printf( fp, " customText %s\n", + OUStringToOString( customText.getString(), RTL_TEXTENCODING_UTF8 ).getStr() ); + indent_printf( fp, " description %s\n", + OUStringToOString( descriptionText.getString(), RTL_TEXTENCODING_UTF8 ).getStr() ); + indent_printf( fp, " tooltip %s\n", + OUStringToOString( tooltip.getString(), RTL_TEXTENCODING_UTF8 ).getStr() ); + if ( bFlags & 0x4 ) + extraInfo.Print( fp ); +} +#endif + +void +TBCGeneralInfo::ImportToolBarControlData( CustomToolBarImportHelper& helper, std::vector< beans::PropertyValue >& sControlData ) +{ + if ( !(bFlags & 0x5) ) + return; + + beans::PropertyValue aProp; + // probably access to the header would be a better test than seeing if there is an action, e.g. + // if ( rHeader.getTct() == 0x01 && rHeader.getTcID() == 0x01 ) // not defined, probably this is a command + if ( !extraInfo.getOnAction().isEmpty() ) + { + aProp.Name = "CommandURL"; + ooo::vba::MacroResolvedInfo aMacroInf = ooo::vba::resolveVBAMacro( &helper.GetDocShell(), extraInfo.getOnAction(), true ); + if ( aMacroInf.mbFound ) + aProp.Value = CustomToolBarImportHelper::createCommandFromMacro( aMacroInf.msResolvedMacro ); + else + aProp.Value <<= "UnResolvedMacro[" + extraInfo.getOnAction() + "]"; + sControlData.push_back( aProp ); + } + + aProp.Name = "Label"; + aProp.Value <<= customText.getString().replace('&','~'); + sControlData.push_back( aProp ); + + aProp.Name = "Type"; + aProp.Value <<= ui::ItemType::DEFAULT; + sControlData.push_back( aProp ); + + aProp.Name = "Tooltip"; + aProp.Value <<= tooltip.getString(); + sControlData.push_back( aProp ); +/* +aToolbarItem(0).Name = "CommandURL" wstrOnAction +aToolbarItem(0).Value = Command +aToolbarItem(1).Name = "Label" customText +aToolbarItem(1).Value = Label +aToolbarItem(2).Name = "Type" +aToolbarItem(2).Value = 0 +aToolbarItem(3).Name = "Visible" +aToolbarItem(3).Value = true +*/ +} + +TBCMenuSpecific::TBCMenuSpecific() : tbid( 0 ) +{ +} + +bool +TBCMenuSpecific::Read( SvStream &rS) +{ + SAL_INFO("filter.ms", "stream pos " << rS.Tell()); + nOffSet = rS.Tell(); + rS.ReadInt32( tbid ); + if ( tbid == 1 ) + { + name = std::make_shared(); + return name->Read( rS ); + } + return true; +} + +#ifdef DEBUG_FILTER_MSFILTER +void +TBCMenuSpecific::Print( FILE* fp ) +{ + Indent a; + indent_printf( fp, "[ 0x%x ] TBCMenuSpecific -- dump\n", nOffSet ); + indent_printf( fp, " tbid 0x%x\n", static_cast< unsigned int >( tbid ) ); + if ( tbid == 1 ) + indent_printf( fp, " name %s\n", OUStringToOString( name->getString(), RTL_TEXTENCODING_UTF8 ).getStr() ); +} +#endif + +OUString TBCMenuSpecific::Name() +{ + OUString aName; + if ( name ) + aName = name->getString(); + return aName; +} +TBCBSpecific::TBCBSpecific() : bFlags( 0 ) +{ +} + +bool TBCBSpecific::Read( SvStream &rS) +{ + SAL_INFO("filter.ms", "stream pos " << rS.Tell()); + nOffSet = rS.Tell(); + rS.ReadUChar( bFlags ); + + // bFlags determines what we read next + + // bFlags.fCustomBitmap = 1 ( 0x8 ) set + if ( bFlags & 0x8 ) + { + icon = std::make_shared(); + iconMask = std::make_shared(); + if ( !icon->Read( rS ) || !iconMask->Read( rS ) ) + return false; + } + // if bFlags.fCustomBtnFace = 1 ( 0x10 ) + if ( bFlags & 0x10 ) + { + iBtnFace = std::make_shared(); + rS.ReadUInt16( *iBtnFace ); + } + // if bFlags.fAccelerator equals 1 ( 0x04 ) + if ( bFlags & 0x04 ) + { + wstrAcc = std::make_shared(); + return wstrAcc->Read( rS ); + } + return true; +} + + +#ifdef DEBUG_FILTER_MSFILTER +void TBCBSpecific::Print( FILE* fp ) +{ + Indent a; + indent_printf( fp, "[ 0x%x ] TBCBSpecific -- dump\n", nOffSet ); + indent_printf( fp, " bFlags 0x%x\n", bFlags ); + bool bResult = ( icon.get() != NULL ); + indent_printf( fp, " icon present? %s\n", bResult ? "true" : "false" ); + if ( bResult ) + { + Indent b; + indent_printf( fp, " icon: \n"); + icon->Print( fp ); // will dump size + } + bResult = ( iconMask.get() != NULL ); + indent_printf( fp, " icon mask present? %s\n", bResult ? "true" : "false" ); + if ( bResult ) + { + Indent c; + indent_printf( fp, " icon mask: \n"); + iconMask->Print( fp ); // will dump size + } + if ( iBtnFace.get() ) + { + indent_printf( fp, " iBtnFace 0x%x\n", *iBtnFace ); + } + bResult = ( wstrAcc.get() != NULL ); + indent_printf( fp, " option string present? %s ->%s<-\n", bResult ? "true" : "false", bResult ? OUStringToOString( wstrAcc->getString(), RTL_TEXTENCODING_UTF8 ).getStr() : "N/A" ); +} +#endif + +TBCBitMap* +TBCBSpecific::getIcon() +{ + return icon.get(); +} + +TBCBitMap* +TBCBSpecific::getIconMask() +{ + return iconMask.get(); +} + +TBCComboDropdownSpecific::TBCComboDropdownSpecific(const TBCHeader& header ) +{ + if ( header.getTcID() == 0x01 ) + data = std::make_shared(); +} + +bool TBCComboDropdownSpecific::Read( SvStream &rS) +{ + nOffSet = rS.Tell(); + if ( data ) + return data->Read( rS ); + return true; +} + +#ifdef DEBUG_FILTER_MSFILTER +void TBCComboDropdownSpecific::Print( FILE* fp) +{ + Indent a; + indent_printf(fp,"[ 0x%x ] TBCComboDropdownSpecific -- dump\n", nOffSet ); + if ( data.get() ) + data->Print( fp ); + else + indent_printf(fp," no data " ); +} +#endif + +TBCCDData::TBCCDData() + : cwstrItems(0) + , cwstrMRU(0) + , iSel(0) + , cLines(0) + , dxWidth(0) +{ +} + +TBCCDData::~TBCCDData() +{ +} + +bool TBCCDData::Read( SvStream &rS) +{ + nOffSet = rS.Tell(); + rS.ReadInt16( cwstrItems ); + if (cwstrItems > 0) + { + auto nItems = o3tl::make_unsigned(cwstrItems); + //each WString is at least one byte + if (rS.remainingSize() < nItems) + return false; + for (decltype(nItems) index = 0; index < nItems; ++index) + { + WString aString; + if ( !aString.Read( rS ) ) + return false; + wstrList.push_back( aString ); + } + } + rS.ReadInt16( cwstrMRU ).ReadInt16( iSel ).ReadInt16( cLines ).ReadInt16( dxWidth ); + + return wstrEdit.Read( rS ); +} + +#ifdef DEBUG_FILTER_MSFILTER +void TBCCDData::Print( FILE* fp) +{ + Indent a; + indent_printf(fp,"[ 0x%x ] TBCCDData -- dump\n", nOffSet ); + indent_printf(fp," cwstrItems items in wstrList %d\n", cwstrItems); + for ( sal_Int32 index=0; index < cwstrItems; ++index ) + { + Indent b; + indent_printf(fp, " wstrList[%d] %s", static_cast< int >( index ), OUStringToOString( wstrList[index].getString(), RTL_TEXTENCODING_UTF8 ).getStr() ); + } + indent_printf(fp," cwstrMRU num most recently used string %d item\n", cwstrMRU); + indent_printf(fp," iSel index of selected item %d item\n", iSel); + indent_printf(fp," cLines num of suggested lines to display %d", cLines); + indent_printf(fp," dxWidth width in pixels %d", dxWidth); + indent_printf(fp," wstrEdit %s", OUStringToOString( wstrEdit.getString(), RTL_TEXTENCODING_UTF8 ).getStr() ); +} +#endif + +TBCBitMap::TBCBitMap() : cbDIB( 0 ) +{ +} + +TBCBitMap::~TBCBitMap() +{ +} + +bool TBCBitMap::Read( SvStream& rS) +{ + SAL_INFO("filter.ms", "stream pos " << rS.Tell()); + nOffSet = rS.Tell(); + rS.ReadInt32( cbDIB ); + // cbDIB = sizeOf(biHeader) + sizeOf(colors) + sizeOf(bitmapData) + 10 + return ReadDIBBitmapEx(mBitMap, rS, false, true); +} + +#ifdef DEBUG_FILTER_MSTOOLBAR +void TBCBitMap::Print( FILE* fp ) +{ + Indent a; + indent_printf(fp, "[ 0x%x ] TBCBitMap -- dump\n", nOffSet ); + indent_printf(fp, " TBCBitMap size of bitmap data 0x%x\n", static_cast< unsigned int > ( cbDIB ) ); +} +#endif + +TB::TB() : bSignature(0x2), +bVersion(0x1), +cCL(0), +ltbid( 0x1 ), +ltbtr(0), +cRowsDefault( 0 ), +bFlags( 0 ) +{ +} + +bool TB::Read(SvStream &rS) +{ + SAL_INFO("filter.ms", "stream pos " << rS.Tell()); + nOffSet = rS.Tell(); + rS.ReadUChar( bSignature ).ReadUChar( bVersion ).ReadInt16( cCL ).ReadInt32( ltbid ).ReadUInt32( ltbtr ).ReadUInt16( cRowsDefault ).ReadUInt16( bFlags ); + name.Read( rS ); + return true; + +} + +bool TB::IsEnabled() const +{ + return ( bFlags & 0x01 ) != 0x01; +} + +#ifdef DEBUG_FILTER_MSTOOLBAR +void TB::Print( FILE* fp ) +{ + Indent a; + indent_printf(fp,"[ 0x%x ] TB -- dump\n", nOffSet ); + indent_printf(fp," bSignature 0x%x\n", bSignature ); + indent_printf(fp," bVersion 0x%x\n", bVersion ); + indent_printf(fp," cCL 0x%x\n", cCL ); + indent_printf(fp," ltbid 0x%x\n", ltbid ); + indent_printf(fp," ltbtr 0x%x\n", ltbtr ); + indent_printf(fp," cRowsDefault 0x%x\n", cRowsDefault ); + indent_printf(fp," bFlags 0x%x\n", bFlags ); + indent_printf(fp, " name %s\n", OUStringToOString( name.getString(), RTL_TEXTENCODING_UTF8 ).getStr() ); +} +#endif + +TBVisualData::TBVisualData() : tbds(0), tbv(0), tbdsDock(0), iRow(0) +{ +} + +bool TBVisualData::Read( SvStream& rS ) +{ + SAL_INFO("filter.ms", "stream pos " << rS.Tell()); + nOffSet = rS.Tell(); + rS.ReadSChar( tbds ).ReadSChar( tbv ).ReadSChar( tbdsDock ).ReadSChar( iRow ); + rcDock.Read( rS ); + rcFloat.Read( rS ); + return true; +} + +#ifdef DEBUG_FILTER_MSTOOLBAR +void SRECT::Print( FILE* fp ) +{ + Indent a; + indent_printf( fp, " left 0x%x\n", left); + indent_printf( fp, " top 0x%x\n", top); + indent_printf( fp, " right 0x%x\n", right); + indent_printf( fp, " bottom 0x%x\n", bottom); +} +#endif + +#ifdef DEBUG_FILTER_MSTOOLBAR +void TBVisualData::Print( FILE* fp ) +{ + Indent a; + indent_printf( fp, "[ 0x%x ] TBVisualData -- dump\n", nOffSet ); + indent_printf( fp, " tbds 0x%x\n", tbds); + indent_printf( fp, " tbv 0x%x\n", tbv); + indent_printf( fp, " tbdsDoc 0x%x\n", tbdsDock); + indent_printf( fp, " iRow 0x%x\n", iRow); + rcDock.Print( fp ); + rcFloat.Print( fp ); +} +#endif + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/filter/source/msfilter/msvbahelper.cxx b/filter/source/msfilter/msvbahelper.cxx new file mode 100644 index 000000000..d55a636fc --- /dev/null +++ b/filter/source/msfilter/msvbahelper.cxx @@ -0,0 +1,768 @@ +/* -*- 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 + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +using namespace ::com::sun::star; + +namespace ooo::vba { + +constexpr OUStringLiteral sUrlPart0( u"vnd.sun.star.script:" ); +constexpr OUStringLiteral sUrlPart1( u"?language=Basic&location=document" ); + +OUString makeMacroURL( std::u16string_view sMacroName ) +{ + return sUrlPart0 + sMacroName + sUrlPart1; +} + +OUString extractMacroName( const OUString& rMacroUrl ) +{ + if( rMacroUrl.startsWith( sUrlPart0 ) && rMacroUrl.endsWith( sUrlPart1 ) ) + { + return rMacroUrl.copy( sUrlPart0.getLength(), + rMacroUrl.getLength() - sUrlPart0.getLength() - sUrlPart1.getLength() ); + } + return OUString(); +} + +static std::u16string_view trimMacroName( std::u16string_view rMacroName ) +{ + // the name may contain whitespaces and may be enclosed in apostrophs + std::u16string_view aMacroName = o3tl::trim(rMacroName); + size_t nMacroLen = aMacroName.size(); + if( (nMacroLen >= 2) && (aMacroName[ 0 ] == '\'') && (aMacroName[ nMacroLen - 1 ] == '\'') ) + aMacroName = o3tl::trim(aMacroName.substr( 1, nMacroLen - 2 )); + return aMacroName; +} + +#if HAVE_FEATURE_SCRIPTING + +static SfxObjectShell* findShellForUrl( const OUString& sMacroURLOrPath ) +{ + SfxObjectShell* pFoundShell=nullptr; + SfxObjectShell* pShell = SfxObjectShell::GetFirst(); + INetURLObject aObj; + aObj.SetURL( sMacroURLOrPath ); + bool bIsURL = aObj.GetProtocol() != INetProtocol::NotValid; + OUString aURL; + if ( bIsURL ) + aURL = sMacroURLOrPath; + else + { + osl::FileBase::getFileURLFromSystemPath( sMacroURLOrPath, aURL ); + aObj.SetURL( aURL ); + } + while ( pShell ) + { + + uno::Reference< frame::XModel > xModel = pShell->GetModel(); + // are we searching for a template? if so we have to cater for the + // fact that in openoffice a document opened from a template is always + // a new document :/ + if ( xModel.is() ) + { + SAL_INFO( + "filter.ms", + "shell " << pShell << " has model with url " << xModel->getURL() + << " and we look for " << aURL); + OUString aName = xModel->getURL() ; + if (aName.isEmpty()) + { + uno::Reference< frame::XFrame > xFrame( xModel->getCurrentController()->getFrame(), uno::UNO_SET_THROW ); + uno::Reference< beans::XPropertySet > xProps( xFrame, uno::UNO_QUERY_THROW ); + xProps->getPropertyValue("Title") >>= aName; + aName = o3tl::trim(o3tl::getToken(aName, 0, '-')); + if( sMacroURLOrPath.lastIndexOf( aName ) >= 0 ) + { + pFoundShell = pShell; + break; + } + } + + if ( sMacroURLOrPath.endsWithIgnoreAsciiCase( ".dot" ) ) + { + uno::Reference const + xDocPropSupp(xModel, uno::UNO_QUERY); + if (xDocPropSupp.is()) + { + uno::Reference< document::XDocumentProperties > const + xDocProps(xDocPropSupp->getDocumentProperties(), + uno::UNO_SET_THROW); + OUString sCurrName = xDocProps->getTemplateName(); + if( sMacroURLOrPath.lastIndexOf( sCurrName ) >= 0 ) + { + pFoundShell = pShell; + break; + } + } + } + else + { + // sometimes just the name of the document ( without the path + // is used + bool bDocNameNoPathMatch = false; + if ( !aURL.isEmpty() && aURL.indexOf( '/' ) == -1 ) + { + sal_Int32 lastSlashIndex = xModel->getURL().lastIndexOf( '/' ); + if ( lastSlashIndex > -1 ) + { + bDocNameNoPathMatch = xModel->getURL().subView( lastSlashIndex + 1 ) == aURL; + if ( !bDocNameNoPathMatch ) + { + OUString aTmpName = OUString::Concat("'") + xModel->getURL().subView( lastSlashIndex + 1 ) + "'"; + bDocNameNoPathMatch = aTmpName == aURL; + } + } + } + + if ( aURL == xModel->getURL() || bDocNameNoPathMatch ) + { + pFoundShell = pShell; + break; + } + } + } + pShell = SfxObjectShell::GetNext( *pShell ); + } + return pFoundShell; +} + +// sMod can be empty ( but we really need the library to search in ) +// if sMod is empty and a macro is found then sMod is updated +// if sMod is empty, only standard modules will be searched (no class, document, form modules) +static bool hasMacro( SfxObjectShell const * pShell, const OUString& sLibrary, OUString& sMod, const OUString& sMacro ) +{ +#if !HAVE_FEATURE_SCRIPTING + (void) pShell; + (void) sLibrary; + (void) sMod; + (void) sMacro; +#else + if ( !sLibrary.isEmpty() && !sMacro.isEmpty() ) + { + BasicManager* pBasicMgr = pShell-> GetBasicManager(); + if ( pBasicMgr ) + { + StarBASIC* pBasic = pBasicMgr->GetLib( sLibrary ); + if ( !pBasic ) + { + sal_uInt16 nId = pBasicMgr->GetLibId( sLibrary ); + pBasicMgr->LoadLib( nId ); + pBasic = pBasicMgr->GetLib( sLibrary ); + } + if ( pBasic ) + { + if ( !sMod.isEmpty() ) // we wish to find the macro is a specific module + { + SbModule* pModule = pBasic->FindModule( sMod ); + if ( pModule && pModule->FindMethod( sMacro, SbxClassType::Method )) + { + return true; + } + } + else + { + for (auto const& rModuleRef : pBasic->GetModules()) + { + if (rModuleRef && rModuleRef->FindMethod(sMacro, SbxClassType::Method)) + { + sMod = rModuleRef->GetName(); + return true; + } + } + } + } + } + } +#endif + return false; +} + +#endif + +#if HAVE_FEATURE_SCRIPTING + +OUString getDefaultProjectName( SfxObjectShell const * pShell ) +{ + OUString aPrjName; + if( BasicManager* pBasicMgr = pShell ? pShell->GetBasicManager() : nullptr ) + { + aPrjName = pBasicMgr->GetName(); + if( aPrjName.isEmpty() ) + aPrjName = "Standard"; + } + return aPrjName; +} + +static void parseMacro( const OUString& sMacro, OUString& sContainer, OUString& sModule, OUString& sProcedure ) +{ + sal_Int32 nMacroDot = sMacro.lastIndexOf( '.' ); + + if ( nMacroDot != -1 ) + { + sProcedure = sMacro.copy( nMacroDot + 1 ); + + sal_Int32 nContainerDot = sMacro.lastIndexOf( '.', nMacroDot - 1 ); + if ( nContainerDot != -1 ) + { + sModule = sMacro.copy( nContainerDot + 1, nMacroDot - nContainerDot - 1 ); + sContainer = sMacro.copy( 0, nContainerDot ); + } + else + sModule = sMacro.copy( 0, nMacroDot ); + } + else + sProcedure = sMacro; +} + +#endif + +OUString resolveVBAMacro( SfxObjectShell const * pShell, const OUString& rLibName, const OUString& rModuleName, const OUString& rMacroName ) +{ +#if !HAVE_FEATURE_SCRIPTING + (void) pShell; + (void) rLibName; + (void) rModuleName; + (void) rMacroName; +#else + if( pShell ) + { + OUString aLibName = rLibName.isEmpty() ? getDefaultProjectName( pShell ) : rLibName ; + OUString aModuleName = rModuleName; + if( hasMacro( pShell, aLibName, aModuleName, rMacroName ) ) + return aLibName + "." + aModuleName + "." + rMacroName; + } +#endif + return OUString(); +} + +MacroResolvedInfo resolveVBAMacro( SfxObjectShell* pShell, const OUString& MacroName, bool bSearchGlobalTemplates ) +{ +#if !HAVE_FEATURE_SCRIPTING + (void) pShell; + (void) MacroName; + (void) bSearchGlobalTemplates; + + return MacroResolvedInfo(); +#else + if( !pShell ) + return MacroResolvedInfo(); + + // the name may be enclosed in apostrophs + std::u16string_view aMacroName = trimMacroName( MacroName ); + + // parse the macro name + size_t nDocSepIndex = aMacroName.find( '!' ); + if( nDocSepIndex > 0 && nDocSepIndex != std::u16string_view::npos ) + { + // macro specified by document name + // find document shell for document name and call ourselves + // recursively + + // assume for now that the document name is *this* document + std::u16string_view sDocUrlOrPath = aMacroName.substr( 0, nDocSepIndex ); + aMacroName = aMacroName.substr( nDocSepIndex + 1 ); + SAL_INFO("filter.ms", "doc search, current shell is " << pShell); + SfxObjectShell* pFoundShell = nullptr; + if( bSearchGlobalTemplates ) + { + SvtPathOptions aPathOpt; + const OUString& aAddinPath = aPathOpt.GetAddinPath(); + if( o3tl::starts_with(sDocUrlOrPath, aAddinPath) ) + pFoundShell = pShell; + } + if( !pFoundShell ) + pFoundShell = findShellForUrl( OUString(sDocUrlOrPath) ); + SAL_INFO( + "filter.ms", + "doc search, after find, found shell is " << pFoundShell); + return resolveVBAMacro( pFoundShell, OUString(aMacroName) ); + } + + // macro is contained in 'this' document ( or code imported from a template + // where that template is a global template or perhaps the template this + // document is created from ) + + MacroResolvedInfo aRes( pShell ); + + // macro format = Container.Module.Procedure + OUString sContainer, sModule, sProcedure; + parseMacro( OUString(aMacroName), sContainer, sModule, sProcedure ); + +#if 0 + // As long as service VBAProjectNameProvider isn't supported in the model, disable the createInstance call + // (the ServiceNotRegisteredException is wrongly caught in ScModelObj::createInstance) + uno::Reference< container::XNameContainer > xPrjNameCache; + uno::Reference< lang::XMultiServiceFactory> xSF( pShell->GetModel(), uno::UNO_QUERY); + if ( xSF.is() ) try + { + xPrjNameCache.set( xSF->createInstance( "ooo.vba.VBAProjectNameProvider" ), uno::UNO_QUERY ); + } + catch( const uno::Exception& ) // createInstance may throw + { + } +#endif + + std::vector< OUString > sSearchList; + + if ( !sContainer.isEmpty() ) + { +// service VBAProjectNameProvider not implemented +#if 0 + // get the Project associated with the Container + if ( xPrjNameCache.is() ) + { + if ( xPrjNameCache->hasByName( sContainer ) ) + { + OUString sProject; + xPrjNameCache->getByName( sContainer ) >>= sProject; + sContainer = sProject; + } + } +#endif + sSearchList.push_back( sContainer ); // First Lib to search + } + else + { + // Ok, if we have no Container specified then we need to search them in order, this document, template this document created from, global templates, + // get the name of Project/Library for 'this' document + OUString sThisProject( "Standard" ); + try + { + uno::Reference< beans::XPropertySet > xProps( pShell->GetModel(), uno::UNO_QUERY_THROW ); + uno::Reference< script::vba::XVBACompatibility > xVBAMode( xProps->getPropertyValue( "BasicLibraries" ), uno::UNO_QUERY_THROW ); + sThisProject = xVBAMode->getProjectName(); + } + catch( const uno::Exception& /*e*/) {} + + sSearchList.push_back( sThisProject ); // First Lib to search + +// service VBAProjectNameProvider not implemented +#if 0 + if ( xPrjNameCache.is() ) + { + // is this document created from a template? + uno::Reference< document::XDocumentPropertiesSupplier > const + xDocPropSupp(pShell->GetModel(), uno::UNO_QUERY_THROW); + uno::Reference< document::XDocumentProperties > xDocProps( xDocPropSupp->getDocumentProperties(), uno::UNO_QUERY_THROW ); + + OUString sCreatedFrom = xDocProps->getTemplateURL(); + if ( !sCreatedFrom.isEmpty() ) + { + INetURLObject aObj; + aObj.SetURL( sCreatedFrom ); + bool bIsURL = aObj.GetProtocol() != INetProtocol::NotValid; + OUString aURL; + if ( bIsURL ) + aURL = sCreatedFrom; + else + { + osl::FileBase::getFileURLFromSystemPath( sCreatedFrom, aURL ); + aObj.SetURL( aURL ); + } + sCreatedFrom = aObj.GetLastName(); + } + + sal_Int32 nIndex = sCreatedFrom.lastIndexOf( '.' ); + if ( nIndex != -1 ) + sCreatedFrom = sCreatedFrom.copy( 0, nIndex ); + + OUString sPrj; + if ( !sCreatedFrom.isEmpty() && xPrjNameCache->hasByName( sCreatedFrom ) ) + { + xPrjNameCache->getByName( sCreatedFrom ) >>= sPrj; + // Make sure we don't double up with this project + if ( !sPrj.equals( sThisProject ) ) + sSearchList.push_back( sPrj ); + } + + // get list of global template Names + uno::Sequence< OUString > sTemplateNames = xPrjNameCache->getElementNames(); + sal_Int32 nLen = sTemplateNames.getLength(); + for ( sal_Int32 index = 0; ( bSearchGlobalTemplates && index < nLen ); ++index ) + { + + if ( !sCreatedFrom.equals( sTemplateNames[ index ] ) ) + { + if ( xPrjNameCache->hasByName( sTemplateNames[ index ] ) ) + { + xPrjNameCache->getByName( sTemplateNames[ index ] ) >>= sPrj; + // Make sure we don't double up with this project + if ( !sPrj.equals( sThisProject ) ) + sSearchList.push_back( sPrj ); + } + } + + } + } +#endif + } + + for (auto const& search : sSearchList) + { + aRes.mbFound = hasMacro( pShell, search, sModule, sProcedure ); + if ( aRes.mbFound ) + { + sContainer = search; + break; + } + } + //aRes.msResolvedMacro = sProcedure.Insert( '.', 0 ).Insert( sModule, 0).Insert( '.', 0 ).Insert( sContainer, 0 ); + aRes.msResolvedMacro = sContainer + "." + sModule + "." + sProcedure; + + return aRes; +#endif +} + +// Treat the args as possible inputs (conversion at bottom of method) +bool executeMacro( SfxObjectShell* pShell, const OUString& sMacroName, uno::Sequence< uno::Any >& aArgs, uno::Any& aRet, const uno::Any& /*aCaller*/) +{ +#if !HAVE_FEATURE_SCRIPTING + (void) pShell; + (void) sMacroName; + (void) aArgs; + (void) aRet; + + return false; +#else + bool bRes = false; + if ( !pShell ) + return bRes; + OUString sUrl = makeMacroURL( sMacroName ); + + uno::Sequence< sal_Int16 > aOutArgsIndex; + uno::Sequence< uno::Any > aOutArgs; + + try + { + ErrCode nErr = pShell->CallXScript(sUrl, aArgs, aRet, aOutArgsIndex, aOutArgs, false); + sal_Int32 nLen = aOutArgs.getLength(); + // convert any out params to seem like they were inputs + if (nLen) + { + auto pArgs = aArgs.getArray(); + for (sal_Int32 index = 0; index < nLen; ++index) + { + sal_Int32 nOutIndex = aOutArgsIndex[index]; + pArgs[nOutIndex] = aOutArgs[index]; + } + } + bRes = ( nErr == ERRCODE_NONE ); + } + catch ( const uno::Exception& ) + { + bRes = false; + } + return bRes; +#endif +} + + + +VBAMacroResolver::VBAMacroResolver() : + mpObjShell( nullptr ) +{ +} + +VBAMacroResolver::~VBAMacroResolver() +{ +} + +// com.sun.star.lang.XServiceInfo interface ----------------------------------- + +OUString SAL_CALL VBAMacroResolver::getImplementationName() +{ + return "com.sun.star.comp.vba.VBAMacroResolver"; +} + +sal_Bool SAL_CALL VBAMacroResolver::supportsService( const OUString& rService ) +{ + return cppu::supportsService(this, rService); +} + +uno::Sequence< OUString > SAL_CALL VBAMacroResolver::getSupportedServiceNames() +{ + return { "com.sun.star.script.vba.VBAMacroResolver" }; +} + +// com.sun.star.lang.XInitialization interface -------------------------------- + +void SAL_CALL VBAMacroResolver::initialize( const uno::Sequence< uno::Any >& rArgs ) +{ + OSL_ENSURE( rArgs.getLength() > 1, "VBAMacroResolver::initialize - missing arguments" ); + if( rArgs.getLength() < 2 ) + throw uno::RuntimeException(); + + // first argument: document model + mxModel.set( rArgs[ 0 ], uno::UNO_QUERY_THROW ); + mpObjShell = comphelper::getFromUnoTunnel(mxModel); + if( !mpObjShell ) + throw uno::RuntimeException(); + + // second argument: VBA project name + if( !(rArgs[ 1 ] >>= maProjectName) || (maProjectName.isEmpty()) ) + throw uno::RuntimeException(); +} + +// com.sun.star.script.vba.XVBAMacroResolver interface ------------------------ + +OUString SAL_CALL VBAMacroResolver::resolveVBAMacroToScriptURL( const OUString& rVBAMacroName ) +{ + if( !mpObjShell ) + throw uno::RuntimeException(); + + // the name may be enclosed in apostrophs + OUString aMacroName( trimMacroName( rVBAMacroName ) ); + if( aMacroName.isEmpty() ) + throw lang::IllegalArgumentException(); + + // external references not supported here (syntax is "url!macroname" or "[url]!macroname" or "[url]macroname") + if( (aMacroName[ 0 ] == '[') || (aMacroName.indexOf( '!' ) >= 0) ) + throw lang::IllegalArgumentException(); + + // check if macro name starts with project name, replace with "Standard" + // TODO: adjust this when custom VBA project name is supported + sal_Int32 nDotPos = aMacroName.indexOf( '.' ); + if( (nDotPos == 0) || (nDotPos + 1 == aMacroName.getLength()) ) + throw lang::IllegalArgumentException(); + if( (nDotPos > 0) && aMacroName.matchIgnoreAsciiCase( maProjectName ) ) + aMacroName = aMacroName.copy( nDotPos + 1 ); + + // try to find the macro + MacroResolvedInfo aInfo = resolveVBAMacro( mpObjShell, aMacroName ); + if( !aInfo.mbFound ) + throw lang::IllegalArgumentException(); + + // build and return the script URL + return makeMacroURL( aInfo.msResolvedMacro ); +} + +OUString SAL_CALL VBAMacroResolver::resolveScriptURLtoVBAMacro( const OUString& /*rScriptURL*/ ) +{ + OSL_ENSURE( false, "VBAMacroResolver::resolveScriptURLtoVBAMacro - not implemented" ); + throw uno::RuntimeException(); +} + +static bool getModifier( sal_Unicode c, sal_uInt16& mod ) +{ + if ( c == '+' ) { + mod |= KEY_SHIFT; + return true; + } else if ( c == '^' ) { + mod |= KEY_MOD1; + return true; + } else if ( c == '%' ) { + mod |= KEY_MOD2; + return true; + } + return false; +} + +/// @throws uno::RuntimeException +static sal_uInt16 parseChar( sal_Unicode c ) +{ + sal_uInt16 nVclKey = 0; + // do we care about locale here for letters/digits? probably not + if ( rtl::isAsciiAlpha( c ) ) + { + nVclKey |= ( rtl::toAsciiUpperCase( c ) - 'A' ) + KEY_A; + if ( rtl::isAsciiUpperCase( c ) ) + nVclKey |= KEY_SHIFT; + } + else if ( rtl::isAsciiDigit( c ) ) + nVclKey |= ( c - '0' ) + KEY_0; + else if ( c == '~' ) // special case + nVclKey = KEY_RETURN; + else if ( c == ' ' ) // special case + nVclKey = KEY_SPACE; + else // I guess we have a problem ( but not sure if locale specific keys might come into play here ) + throw uno::RuntimeException(); + return nVclKey; +} + +namespace { + +struct KeyCodeEntry +{ + const char* sName; + sal_uInt16 nCode; +}; + +} + +KeyCodeEntry const aMSKeyCodesData[] = { + { "BACKSPACE", KEY_BACKSPACE }, + { "BS", KEY_BACKSPACE }, + { "DELETE", KEY_DELETE }, + { "DEL", KEY_DELETE }, + { "DOWN", KEY_DOWN }, + { "UP", KEY_UP }, + { "LEFT", KEY_LEFT }, + { "RIGHT", KEY_RIGHT }, + { "END", KEY_END }, + { "ESCAPE", KEY_ESCAPE }, + { "ESC", KEY_ESCAPE }, + { "HELP", KEY_HELP }, + { "HOME", KEY_HOME }, + { "PGDN", KEY_PAGEDOWN }, + { "PGUP", KEY_PAGEUP }, + { "INSERT", KEY_INSERT }, + { "SCROLLLOCK", KEY_SCROLLLOCK }, + { "NUMLOCK", KEY_NUMLOCK }, + { "TAB", KEY_TAB }, + { "F1", KEY_F1 }, + { "F2", KEY_F2 }, + { "F3", KEY_F3 }, + { "F4", KEY_F4 }, + { "F5", KEY_F5 }, + { "F6", KEY_F6 }, + { "F7", KEY_F7 }, + { "F8", KEY_F8 }, + { "F9", KEY_F9 }, + { "F10", KEY_F10 }, + { "F11", KEY_F11 }, + { "F12", KEY_F12 }, + { "F13", KEY_F13 }, + { "F14", KEY_F14 }, + { "F15", KEY_F15 }, +}; + +awt::KeyEvent parseKeyEvent( const OUString& Key ) +{ + static std::map< OUString, sal_uInt16 > s_KeyCodes = []() + { + std::map< OUString, sal_uInt16 > tmp; + for (KeyCodeEntry const & i : aMSKeyCodesData) + { + tmp[ OUString::createFromAscii( i.sName ) ] = i.nCode; + } + return tmp; + }(); + OUString sKeyCode; + sal_uInt16 nVclKey = 0; + + // parse the modifier if any + for ( int i=0; isecond; + } + } + + awt::KeyEvent aKeyEvent = svt::AcceleratorExecute::st_VCLKey2AWTKey( vcl::KeyCode( nVclKey ) ); + return aKeyEvent; +} + +void applyShortCutKeyBinding ( const uno::Reference< frame::XModel >& rxModel, const awt::KeyEvent& rKeyEvent, const OUString& rMacroName ) +{ + OUString MacroName( rMacroName ); + if ( !MacroName.isEmpty() ) + { + OUString aMacroName = MacroName.trim(); + if( aMacroName.startsWith("!") ) + aMacroName = o3tl::trim(aMacroName.subView(1)); + SfxObjectShell* pShell = nullptr; + if ( rxModel.is() ) + { + pShell = comphelper::getFromUnoTunnel(rxModel); + if ( !pShell ) + throw uno::RuntimeException(); + } + MacroResolvedInfo aMacroInfo = resolveVBAMacro( pShell, aMacroName ); + if( !aMacroInfo.mbFound ) + throw uno::RuntimeException( "The procedure doesn't exist" ); + MacroName = aMacroInfo.msResolvedMacro; + } + uno::Reference< ui::XUIConfigurationManagerSupplier > xCfgSupplier(rxModel, uno::UNO_QUERY_THROW); + uno::Reference< ui::XUIConfigurationManager > xCfgMgr = xCfgSupplier->getUIConfigurationManager(); + + uno::Reference< ui::XAcceleratorConfiguration > xAcc( xCfgMgr->getShortCutManager(), uno::UNO_SET_THROW ); + if ( MacroName.isEmpty() ) + // I believe this should really restore the [application] default. Since + // afaik we don't actually setup application default bindings on import + // we don't even know what the 'default' would be for this key + xAcc->removeKeyEvent( rKeyEvent ); + else + xAcc->setKeyEvent( rKeyEvent, ooo::vba::makeMacroURL( MacroName ) ); + +} + + +} // namespace ooo + +extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface* +filter_VBAMacroResolver_get_implementation( + css::uno::XComponentContext* , css::uno::Sequence const&) +{ + return cppu::acquire(new ooo::vba::VBAMacroResolver()); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ + diff --git a/filter/source/msfilter/rtfutil.cxx b/filter/source/msfilter/rtfutil.cxx new file mode 100644 index 000000000..e20a146c4 --- /dev/null +++ b/filter/source/msfilter/rtfutil.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/. + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +namespace +{ +/** + * If rOle1 is native OLE1 data of size nOle1Size, wraps it in an OLE2 container. + * + * The OLE2 root's CLSID is set based on rClassName. + */ +void WrapOle1InOle2(SvStream& rOle1, sal_uInt32 nOle1Size, SvStream& rOle2, + const OString& rClassName) +{ + tools::SvRef pStorage = new SotStorage(rOle2); + OString aAnsiUserType; + SvGlobalName aName; + if (rClassName == "PBrush") + { + aAnsiUserType = "Bitmap Image"; + aName = SvGlobalName(0x0003000A, 0, 0, 0xc0, 0, 0, 0, 0, 0, 0, 0x46); + } + else + { + if (!rClassName.isEmpty() && rClassName != "Package") + { + SAL_WARN("filter.ms", "WrapOle1InOle2: unexpected class name: '" << rClassName << "'"); + } + aAnsiUserType = "OLE Package"; + aName = SvGlobalName(0x0003000C, 0, 0, 0xc0, 0, 0, 0, 0, 0, 0, 0x46); + } + pStorage->SetClass(aName, SotClipboardFormatId::NONE, ""); + + // [MS-OLEDS] 2.3.7 CompObjHeader + tools::SvRef pCompObj = pStorage->OpenSotStream("\1CompObj"); + // Reserved1 + pCompObj->WriteUInt32(0xfffe0001); + // Version + pCompObj->WriteUInt32(0x00000a03); + // Reserved2 + pCompObj->WriteUInt32(0xffffffff); + pCompObj->WriteUInt32(0x0003000c); + pCompObj->WriteUInt32(0x00000000); + pCompObj->WriteUInt32(0x000000c0); + pCompObj->WriteUInt32(0x46000000); + // Rest of CompObjStream + // AnsiUserType + pCompObj->WriteUInt32(aAnsiUserType.getLength() + 1); + pCompObj->WriteOString(aAnsiUserType); + pCompObj->WriteChar(0); + // AnsiClipboardFormat + pCompObj->WriteUInt32(0x00000000); + // Reserved1 + pCompObj->WriteUInt32(rClassName.getLength() + 1); + pCompObj->WriteOString(rClassName); + pCompObj->WriteChar(0); + // UnicodeMarker + pCompObj->WriteUInt32(0x71B239F4); + // UnicodeUserType + pCompObj->WriteUInt32(0x00000000); + // UnicodeClipboardFormat + pCompObj->WriteUInt32(0x00000000); + // Reserved2 + pCompObj->WriteUInt32(0x00000000); + pCompObj->Commit(); + pCompObj.clear(); + + // [MS-OLEDS] 2.3.6 OLENativeStream + tools::SvRef pOleNative = pStorage->OpenSotStream("\1Ole10Native"); + // NativeDataSize + pOleNative->WriteUInt32(nOle1Size); + pOleNative->WriteStream(rOle1, nOle1Size); + pOleNative->Commit(); + pOleNative.clear(); + + pStorage->Commit(); + pStorage.clear(); + rOle2.Seek(0); +} +} + +namespace msfilter::rtfutil +{ +OString OutHex(sal_uLong nHex, sal_uInt8 nLen) +{ + char aNToABuf[] = "0000000000000000"; + + OSL_ENSURE(nLen < sizeof(aNToABuf), "nLen is too big"); + if (nLen >= sizeof(aNToABuf)) + nLen = (sizeof(aNToABuf) - 1); + + // Set pointer to the buffer end + char* pStr = aNToABuf + (sizeof(aNToABuf) - 1); + for (sal_uInt8 n = 0; n < nLen; ++n) + { + *(--pStr) = static_cast(nHex & 0xf) + 48; + if (*pStr > '9') + *pStr += 39; + nHex >>= 4; + } + return pStr; +} + +// Ideally, this function should work on (sal_uInt32) Unicode scalar values +// instead of (sal_Unicode) UTF-16 code units. However, at least "Rich Text +// Format (RTF) Specification Version 1.9.1" available at +// does not +// look like it allows non-BMP Unicode characters >= 0x10000 in the \uN notation +// (it only talks about "Unicode character", but then explains how values of N +// greater than 32767 will be expressed as negative signed 16-bit numbers, so +// that smells like \uN is limited to BMP). +// However the "Mathematics" section has an example that shows the code point +// U+1D44E being encoded as UTF-16 surrogate pair "\u-10187?\u-9138?", so +// sal_Unicode actually works fine here. +OString OutChar(sal_Unicode c, int* pUCMode, rtl_TextEncoding eDestEnc, bool* pSuccess, + bool bUnicode) +{ + if (pSuccess) + *pSuccess = true; + OStringBuffer aBuf; + const char* pStr = nullptr; + // 0x0b instead of \n, etc because of the replacements in SwWW8AttrIter::GetSnippet() + switch (c) + { + case 0x0b: + // hard line break + pStr = OOO_STRING_SVTOOLS_RTF_LINE; + break; + case '\t': + pStr = OOO_STRING_SVTOOLS_RTF_TAB; + break; + case '\\': + case '}': + case '{': + aBuf.append('\\'); + aBuf.append(static_cast(c)); + break; + case 0xa0: + // non-breaking space + pStr = "\\~"; + break; + case 0x1e: + // non-breaking hyphen + pStr = "\\_"; + break; + case 0x1f: + // optional hyphen + pStr = "\\-"; + break; + default: + if (c >= ' ' && c <= '~') + aBuf.append(static_cast(c)); + else + { + OUString sBuf(&c, 1); + OString sConverted; + if (pSuccess) + *pSuccess &= sBuf.convertToString(&sConverted, eDestEnc, + RTL_UNICODETOTEXT_FLAGS_UNDEFINED_ERROR + | RTL_UNICODETOTEXT_FLAGS_INVALID_ERROR); + else + sBuf.convertToString(&sConverted, eDestEnc, OUSTRING_TO_OSTRING_CVTFLAGS); + const sal_Int32 nLen = sConverted.getLength(); + + if (pUCMode && bUnicode) + { + if (*pUCMode != nLen) + { + aBuf.append("\\uc"); + aBuf.append(nLen); + // #i47831# add an additional whitespace, so that "document whitespaces" are not ignored. + aBuf.append(' '); + *pUCMode = nLen; + } + aBuf.append("\\u"); + aBuf.append(static_cast(c)); + } + + for (sal_Int32 nI = 0; nI < nLen; ++nI) + { + aBuf.append("\\'"); + aBuf.append(OutHex(sConverted[nI], 2)); + } + } + } + if (pStr) + { + aBuf.append(pStr); + switch (c) + { + case 0xa0: + case 0x1e: + case 0x1f: + break; + default: + aBuf.append(' '); + } + } + return aBuf.makeStringAndClear(); +} + +OString OutString(const OUString& rStr, rtl_TextEncoding eDestEnc, bool bUnicode) +{ + SAL_INFO("filter.ms", __func__ << ", rStr = '" << rStr << "'"); + OStringBuffer aBuf; + int nUCMode = 1; + for (sal_Int32 n = 0; n < rStr.getLength(); ++n) + aBuf.append(OutChar(rStr[n], &nUCMode, eDestEnc, nullptr, bUnicode)); + if (nUCMode != 1) + { + aBuf.append(OOO_STRING_SVTOOLS_RTF_UC); + aBuf.append(sal_Int32(1)); + aBuf.append( + " "); // #i47831# add an additional whitespace, so that "document whitespaces" are not ignored.; + } + return aBuf.makeStringAndClear(); +} + +/// Checks if lossless conversion of the string to eDestEnc is possible or not. +static bool TryOutString(const OUString& rStr, rtl_TextEncoding eDestEnc) +{ + int nUCMode = 1; + for (sal_Int32 n = 0; n < rStr.getLength(); ++n) + { + bool bRet; + OutChar(rStr[n], &nUCMode, eDestEnc, &bRet); + if (!bRet) + return false; + } + return true; +} + +OString OutStringUpr(const char* pToken, const OUString& rStr, rtl_TextEncoding eDestEnc) +{ + if (TryOutString(rStr, eDestEnc)) + return OString::Concat("{") + pToken + " " + OutString(rStr, eDestEnc) + "}"; + + return OString::Concat("{" OOO_STRING_SVTOOLS_RTF_UPR "{") + pToken + " " + + OutString(rStr, eDestEnc, /*bUnicode =*/false) + + "}{" OOO_STRING_SVTOOLS_RTF_IGNORE OOO_STRING_SVTOOLS_RTF_UD "{" + pToken + " " + + OutString(rStr, eDestEnc) + "}}}"; +} + +int AsHex(char ch) +{ + int ret = 0; + if (rtl::isAsciiDigit(static_cast(ch))) + ret = ch - '0'; + else + { + if (ch >= 'a' && ch <= 'f') + ret = ch - 'a'; + else if (ch >= 'A' && ch <= 'F') + ret = ch - 'A'; + else + return -1; + ret += 10; + } + return ret; +} + +OString WriteHex(const sal_uInt8* pData, sal_uInt32 nSize, SvStream* pStream, sal_uInt32 nLimit) +{ + OStringBuffer aRet; + + sal_uInt32 nBreak = 0; + for (sal_uInt32 i = 0; i < nSize; i++) + { + OString sNo = OString::number(pData[i], 16); + if (sNo.getLength() < 2) + { + if (pStream) + pStream->WriteChar('0'); + else + aRet.append('0'); + } + if (pStream) + pStream->WriteOString(sNo); + else + aRet.append(sNo); + if (++nBreak == nLimit) + { + if (pStream) + pStream->WriteCharPtr(SAL_NEWLINE_STRING); + else + aRet.append(SAL_NEWLINE_STRING); + nBreak = 0; + } + } + + return aRet.makeStringAndClear(); +} + +bool ExtractOLE2FromObjdata(const OString& rObjdata, SvStream& rOle2) +{ + SvMemoryStream aStream; + int b = 0; + int count = 2; + + // Feed the destination text to a stream. + for (int i = 0; i < rObjdata.getLength(); ++i) + { + char ch = rObjdata[i]; + if (ch != 0x0d && ch != 0x0a) + { + b = b << 4; + sal_Int8 parsed = msfilter::rtfutil::AsHex(ch); + if (parsed == -1) + return false; + b += parsed; + count--; + if (!count) + { + aStream.WriteChar(b); + count = 2; + b = 0; + } + } + } + + // Skip ObjectHeader, see [MS-OLEDS] 2.2.4. + if (!aStream.Tell()) + return true; + + aStream.Seek(0); + sal_uInt32 nData; + aStream.ReadUInt32(nData); // OLEVersion + aStream.ReadUInt32(nData); // FormatID + aStream.ReadUInt32(nData); // ClassName + OString aClassName; + if (nData) + { + // -1 because it is null-terminated. + aClassName = read_uInt8s_ToOString(aStream, nData - 1); + // Skip null-termination. + aStream.SeekRel(1); + } + aStream.ReadUInt32(nData); // TopicName + aStream.SeekRel(nData); + aStream.ReadUInt32(nData); // ItemName + aStream.SeekRel(nData); + aStream.ReadUInt32(nData); // NativeDataSize + + if (!nData) + return true; + + sal_uInt64 nPos = aStream.Tell(); + sal_uInt8 aSignature[8]; + aStream.ReadBytes(aSignature, SAL_N_ELEMENTS(aSignature)); + aStream.Seek(nPos); + const sal_uInt8 aOle2Signature[8] = { 0xD0, 0xCF, 0x11, 0xE0, 0xA1, 0xB1, 0x1A, 0xE1 }; + // Don't use Storage::IsStorageFile() here, that would seek to the start of the stream, + // where the magic will always mismatch. + if (std::memcmp(aSignature, aOle2Signature, SAL_N_ELEMENTS(aSignature)) == 0) + { + // NativeData + rOle2.WriteStream(aStream, nData); + } + else + { + SvMemoryStream aStorage; + WrapOle1InOle2(aStream, nData, aStorage, aClassName); + rOle2.WriteStream(aStorage); + } + rOle2.Seek(0); + + return true; +} + +bool StripMetafileHeader(const sal_uInt8*& rpGraphicAry, sal_uInt64& rSize) +{ + if (rpGraphicAry && (rSize > 0x22)) + { + if ((rpGraphicAry[0] == 0xd7) && (rpGraphicAry[1] == 0xcd) && (rpGraphicAry[2] == 0xc6) + && (rpGraphicAry[3] == 0x9a)) + { + // we have to get rid of the metafileheader + rpGraphicAry += 22; + rSize -= 22; + return true; + } + } + return false; +} +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/filter/source/msfilter/svdfppt.cxx b/filter/source/msfilter/svdfppt.cxx new file mode 100644 index 000000000..74f8da16f --- /dev/null +++ b/filter/source/msfilter/svdfppt.cxx @@ -0,0 +1,7832 @@ +/* -*- 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 + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +// PPT ColorScheme Slots +#define PPT_COLSCHEME (0x08000000) +#define PPT_COLSCHEME_HINTERGRUND (0x08000000) +#define PPT_COLSCHEME_TEXT_UND_ZEILEN (0x08000001) +#define PPT_COLSCHEME_TITELTEXT (0x08000003) +#define PPT_COLSCHEME_A_UND_HYPERLINK (0x08000006) + +#define ANSI_CHARSET 0 +#define SYMBOL_CHARSET 2 + +/* Font Families */ +#define FF_ROMAN 0x10 +#define FF_SWISS 0x20 +#define FF_MODERN 0x30 +#define FF_SCRIPT 0x40 +#define FF_DECORATIVE 0x50 + +#define DEFAULT_PITCH 0x00 +#define FIXED_PITCH 0x01 +#define VARIABLE_PITCH 0x02 + +using namespace ::com::sun::star ; +using namespace uno ; +using namespace beans ; +using namespace drawing ; +using namespace container ; +using namespace table ; + +PowerPointImportParam::PowerPointImportParam( SvStream& rDocStrm ) : + rDocStream ( rDocStrm ), + nImportFlags ( 0 ) +{ +} + +SvStream& ReadPptCurrentUserAtom( SvStream& rIn, PptCurrentUserAtom& rAtom ) +{ + DffRecordHeader aHd; + ReadDffRecordHeader( rIn, aHd ); + if ( aHd.nRecType == PPT_PST_CurrentUserAtom ) + { + sal_uInt32 nLen; + sal_uInt16 nUserNameLen(0), nPad; + rIn.ReadUInt32( nLen ) + .ReadUInt32( rAtom.nMagic ) + .ReadUInt32( rAtom.nCurrentUserEdit ) + .ReadUInt16( nUserNameLen ) + .ReadUInt16( rAtom.nDocFileVersion ) + .ReadUChar( rAtom.nMajorVersion ) + .ReadUChar( rAtom.nMinorVersion ) + .ReadUInt16( nPad ); + rAtom.aCurrentUser = SvxMSDffManager::MSDFFReadZString( rIn, nUserNameLen, true ); + } + aHd.SeekToEndOfRecord( rIn ); + return rIn; +} + +void PptSlidePersistAtom::Clear() +{ + nReserved = nPsrReference = nFlags = nNumberTexts = nSlideId = 0; +} + +SvStream& ReadPptSlidePersistAtom( SvStream& rIn, PptSlidePersistAtom& rAtom ) +{ + DffRecordHeader aHd; + ReadDffRecordHeader( rIn, aHd ); + rIn + .ReadUInt32( rAtom.nPsrReference ) + .ReadUInt32( rAtom.nFlags ) + .ReadUInt32( rAtom.nNumberTexts ) + .ReadUInt32( rAtom.nSlideId ); + aHd.SeekToEndOfRecord( rIn ); + return rIn; +} + +PptSlidePersistList::PptSlidePersistList() {} + +PptSlidePersistList::~PptSlidePersistList() {} + +sal_uInt16 PptSlidePersistList::FindPage(sal_uInt32 nId) const +{ + for ( size_t i=0; i < mvEntries.size(); i++ ) + { + if (mvEntries[ i ]->GetSlideId() == nId) return i; + } + return PPTSLIDEPERSIST_ENTRY_NOTFOUND; +} + +SvStream& ReadPptInteractiveInfoAtom( SvStream& rIn, PptInteractiveInfoAtom& rAtom ) +{ + rIn.ReadUInt32( rAtom.nSoundRef ) + .ReadUInt32( rAtom.nExHyperlinkId ) + .ReadUChar( rAtom.nAction ) + .ReadUChar( rAtom.nOleVerb ) + .ReadUChar( rAtom.nJump ) + .ReadUChar( rAtom.nFlags ) + .ReadUChar( rAtom.nHyperlinkType ) + .ReadUChar( rAtom.nUnknown1 ) + .ReadUChar( rAtom.nUnknown2 ) + .ReadUChar( rAtom.nUnknown3 ); + return rIn; +} + +SvStream& ReadPptExOleObjAtom( SvStream& rIn, PptExOleObjAtom& rAtom ) +{ + sal_uInt32 nDummy1; + sal_uInt32 nDummy2; + sal_uInt32 nDummy4; + + rIn.ReadUInt32( rAtom.nAspect ) + .ReadUInt32( nDummy1 ) + .ReadUInt32( rAtom.nId ) + .ReadUInt32( nDummy2 ) + .ReadUInt32( rAtom.nPersistPtr ) + .ReadUInt32( nDummy4 ); + return rIn; +} + +SvStream& ReadPptDocumentAtom(SvStream& rIn, PptDocumentAtom& rAtom) +{ +// Actual format: +// 00 aSlidePageSizeXY 8 +// 08 aNotesPageSizeXY 8 +// 16 aZoomRatio (OLE) 8 +// 24 nNotesMasterPersist 4 +// 28 nHandoutMasterPersist 4 +// 32 n1stPageNumber 2 +// 34 ePageFormat 2 +// 36 bEmbeddedTrueType 1 +// 37 bOmitTitlePlace 1 +// 38 bRightToLeft 1 +// 39 bShowComments 1 + + DffRecordHeader aHd; + sal_Int32 nSlideX(0), nSlideY(0), nNoticeX(0), nNoticeY(0), nDummy; + sal_uInt16 nSlidePageFormat(0); + sal_Int8 nEmbeddedTrueType(0), nTitlePlaceHoldersOmitted(0), nRightToLeft(0), nShowComments(0); + + ReadDffRecordHeader( rIn, aHd ); + rIn + .ReadInt32( nSlideX ).ReadInt32( nSlideY ) + .ReadInt32( nNoticeX ).ReadInt32( nNoticeY ) + .ReadInt32( nDummy ).ReadInt32( nDummy ) // skip ZoomRatio + .ReadUInt32( rAtom.nNotesMasterPersist ) + .ReadUInt32( rAtom.nHandoutMasterPersist ) + .ReadUInt16( rAtom.n1stPageNumber ) + .ReadUInt16( nSlidePageFormat ) + .ReadSChar( nEmbeddedTrueType ) + .ReadSChar( nTitlePlaceHoldersOmitted ) + .ReadSChar( nRightToLeft ) + .ReadSChar( nShowComments ); + // clamp dodgy data to avoid overflow in later calculations + const sal_Int32 nPageClamp = SAL_MAX_INT32/5; + rAtom.aSlidesPageSize.setWidth( std::clamp(nSlideX, -nPageClamp, nPageClamp) ); + rAtom.aSlidesPageSize.setHeight( std::clamp(nSlideY, -nPageClamp, nPageClamp) ); + const sal_Int32 nNoteClamp = 65536; + rAtom.aNotesPageSize.setWidth( std::clamp(nNoticeX, -nNoteClamp, nNoteClamp) ); + rAtom.aNotesPageSize.setHeight( std::clamp(nNoticeY, -nNoteClamp, nNoteClamp) ); + rAtom.eSlidesPageFormat = static_cast(nSlidePageFormat); + rAtom.bEmbeddedTrueType = nEmbeddedTrueType; + rAtom.bTitlePlaceholdersOmitted = nTitlePlaceHoldersOmitted; + rAtom.bRightToLeft = nRightToLeft; + rAtom.bShowComments = nShowComments; + aHd.SeekToEndOfRecord( rIn ); + return rIn; +} + +void PptSlideLayoutAtom::Clear() +{ + eLayout = PptSlideLayout::TITLESLIDE; + for (PptPlaceholder & i : aPlaceholderId) + i = PptPlaceholder::NONE; +} + +SvStream& ReadPptSlideLayoutAtom( SvStream& rIn, PptSlideLayoutAtom& rAtom ) +{ + sal_Int32 nTmp; + rIn.ReadInt32(nTmp); + rAtom.eLayout = static_cast(nTmp); + static_assert(sizeof(rAtom.aPlaceholderId) == 8, "wrong size of serialized array"); + rIn.ReadBytes(rAtom.aPlaceholderId, 8); + return rIn; +} + +SvStream& ReadPptSlideAtom( SvStream& rIn, PptSlideAtom& rAtom ) +{ + DffRecordHeader aHd; + ReadDffRecordHeader( rIn, aHd ); + ReadPptSlideLayoutAtom( rIn, rAtom.aLayout ); + rIn.ReadUInt32( rAtom.nMasterId ) + .ReadUInt32( rAtom.nNotesId ) + .ReadUInt16( rAtom.nFlags ); + aHd.SeekToEndOfRecord( rIn ); + return rIn; +} + +void PptSlideAtom::Clear() +{ + nMasterId = nNotesId = 0; + nFlags = 0; +} + +SvStream& ReadPptNotesAtom( SvStream& rIn, PptNotesAtom& rAtom ) +{ + DffRecordHeader aHd; + ReadDffRecordHeader( rIn, aHd ); + rIn + .ReadUInt32( rAtom.nSlideId ) + .ReadUInt16( rAtom.nFlags ); + aHd.SeekToEndOfRecord( rIn ); + return rIn; +} + +void PptNotesAtom::Clear() +{ + nSlideId = 0; + nFlags = 0; +} + +PptColorSchemeAtom::PptColorSchemeAtom() +{ +} + +Color PptColorSchemeAtom::GetColor( sal_uInt16 nNum ) const +{ + Color aRetval; + if ( nNum < 8 ) + { + nNum <<= 2; + aRetval.SetRed( aData[ nNum++ ] ); + aRetval.SetGreen( aData[ nNum++ ] ); + aRetval.SetBlue( aData[ nNum++ ] ); + } + return aRetval; +} + +SvStream& ReadPptColorSchemeAtom( SvStream& rIn, PptColorSchemeAtom& rAtom ) +{ + DffRecordHeader aHd; + ReadDffRecordHeader( rIn, aHd ); + rIn.ReadBytes(rAtom.aData, 32); + aHd.SeekToEndOfRecord( rIn ); + return rIn; +} + +SvStream& ReadPptFontEntityAtom( SvStream& rIn, PptFontEntityAtom& rAtom ) +{ + DffRecordHeader aHd; + ReadDffRecordHeader( rIn, aHd ); + sal_Unicode nTemp, cData[ 32 ]; + rIn.ReadBytes(cData, 64); + + sal_uInt8 lfCharset, lfPitchAndFamily; + + rIn.ReadUChar( lfCharset ) + .ReadUChar( rAtom.lfClipPrecision ) + .ReadUChar( rAtom.lfQuality ) + .ReadUChar( lfPitchAndFamily ); + + switch( lfCharset ) + { + case SYMBOL_CHARSET : + rAtom.eCharSet = RTL_TEXTENCODING_SYMBOL; + break; + case ANSI_CHARSET : + rAtom.eCharSet = RTL_TEXTENCODING_MS_1252; + break; + + default : + rAtom.eCharSet = osl_getThreadTextEncoding(); + } + switch ( lfPitchAndFamily & 0xf0 ) + { + case FF_ROMAN: + rAtom.eFamily = FAMILY_ROMAN; + break; + + case FF_SWISS: + rAtom.eFamily = FAMILY_SWISS; + break; + + case FF_MODERN: + rAtom.eFamily = FAMILY_MODERN; + break; + + case FF_SCRIPT: + rAtom.eFamily = FAMILY_SCRIPT; + break; + + case FF_DECORATIVE: + rAtom.eFamily = FAMILY_DECORATIVE; + break; + + default: + rAtom.eFamily = FAMILY_DONTKNOW; + break; + } + + switch ( lfPitchAndFamily & 0x0f ) + { + case FIXED_PITCH: + rAtom.ePitch = PITCH_FIXED; + break; + + case DEFAULT_PITCH: + case VARIABLE_PITCH: + default: + rAtom.ePitch = PITCH_VARIABLE; + break; + } + sal_uInt16 i; + for ( i = 0; i < 32; i++ ) + { + nTemp = cData[ i ]; + if ( !nTemp ) + break; +#ifdef OSL_BIGENDIAN + cData[ i ] = ( nTemp >> 8 ) | ( nTemp << 8 ); +#endif + } + rAtom.aName = OUString(cData, i); + OutputDevice* pDev = Application::GetDefaultDevice(); + rAtom.bAvailable = pDev->IsFontAvailable( rAtom.aName ); + aHd.SeekToEndOfRecord( rIn ); + return rIn; +} + +SvStream& ReadPptUserEditAtom( SvStream& rIn, PptUserEditAtom& rAtom ) +{ + sal_Int16 lastViewType = 0; + ReadDffRecordHeader( rIn, rAtom.aHd ); + rIn + .ReadInt32( rAtom.nLastSlideID ) + .ReadUInt32( rAtom.nVersion ) + .ReadUInt32( rAtom.nOffsetLastEdit ) + .ReadUInt32( rAtom.nOffsetPersistDirectory ) + .ReadUInt32( rAtom.nDocumentRef ) + .ReadUInt32( rAtom.nMaxPersistWritten ) + .ReadInt16( lastViewType ); + rAtom.eLastViewType = static_cast(lastViewType); + rAtom.aHd.SeekToEndOfRecord(rIn); + return rIn; +} + +void PptOEPlaceholderAtom::Clear() +{ + nPlacementId = 0; + nPlaceholderSize = 0; + nPlaceholderId = PptPlaceholder::NONE; +} + +SvStream& ReadPptOEPlaceholderAtom( SvStream& rIn, PptOEPlaceholderAtom& rAtom ) +{ + rIn.ReadUInt32( rAtom.nPlacementId ); + sal_uInt8 nTmp; + rIn.ReadUChar(nTmp); + rAtom.nPlaceholderId = static_cast(nTmp); + rIn.ReadUChar( rAtom.nPlaceholderSize ); + return rIn; +} + +PptSlidePersistEntry::PptSlidePersistEntry() : + nSlidePersistStartOffset( 0 ), + nSlidePersistEndOffset ( 0 ), + nBackgroundOffset ( 0 ), + nDrawingDgId ( 0xffffffff ), + pBObj ( nullptr ), + ePageKind ( PPT_MASTERPAGE ), + bNotesMaster ( false ), + bHandoutMaster ( false ), + bStarDrawFiller ( false ) +{ + HeaderFooterOfs[ 0 ] = HeaderFooterOfs[ 1 ] = HeaderFooterOfs[ 2 ] = HeaderFooterOfs[ 3 ] = 0; +} + +PptSlidePersistEntry::~PptSlidePersistEntry() +{ +} + +SdrEscherImport::SdrEscherImport( PowerPointImportParam& rParam, const OUString& rBaseURL ) : + SvxMSDffManager ( rParam.rDocStream, rBaseURL ), + nStreamLen ( 0 ), + rImportParam ( rParam ) +{ +} + +SdrEscherImport::~SdrEscherImport() +{ +} + +const PptSlideLayoutAtom* SdrEscherImport::GetSlideLayoutAtom() const +{ + return nullptr; +} + +bool SdrEscherImport::ReadString( OUString& rStr ) const +{ + bool bRet = false; + DffRecordHeader aStrHd; + ReadDffRecordHeader( rStCtrl, aStrHd ); + if (aStrHd.nRecType == PPT_PST_TextBytesAtom + || aStrHd.nRecType == PPT_PST_TextCharsAtom + || aStrHd.nRecType == PPT_PST_CString) + { + bool bUniCode = + (aStrHd.nRecType == PPT_PST_TextCharsAtom + || aStrHd.nRecType == PPT_PST_CString); + sal_uLong nBytes = aStrHd.nRecLen; + rStr = MSDFFReadZString( rStCtrl, nBytes, bUniCode ); + bRet = aStrHd.SeekToEndOfRecord( rStCtrl ); + } + else + aStrHd.SeekToBegOfRecord( rStCtrl ); + return bRet; +} + +bool SdrEscherImport::GetColorFromPalette(sal_uInt16 /*nNum*/, Color& /*rColor*/) const +{ + return false; +} + +bool SdrEscherImport::SeekToShape( SvStream& /*rSt*/, SvxMSDffClientData* /*pClientData*/, sal_uInt32 /*nId*/) const +{ + return false; +} + +const PptFontEntityAtom* SdrEscherImport::GetFontEnityAtom( sal_uInt32 nNum ) const +{ + if (m_xFonts && nNum < m_xFonts->size()) + return &(*m_xFonts)[ nNum ]; + return nullptr; +} + +SdrObject* SdrEscherImport::ReadObjText( PPTTextObj* /*pTextObj*/, SdrObject* pObj, SdPageCapsule /*pPage*/) const +{ + return pObj; +} + +void SdrEscherImport::ProcessClientAnchor2( SvStream& rSt, DffRecordHeader& rHd, DffObjData& rObj ) +{ + sal_Int32 l, t, r, b; + if ( rHd.nRecLen == 16 ) + { + rSt.ReadInt32( l ).ReadInt32( t ).ReadInt32( r ).ReadInt32( b ); + } + else + { + sal_Int16 ls, ts, rs, bs; + rSt.ReadInt16( ts ).ReadInt16( ls ).ReadInt16( rs ).ReadInt16( bs ); // the order of coordinates is a bit strange... + l = ls; + t = ts; + r = rs; + b = bs; + } + if (!rSt.good()) + { + SAL_WARN("filter.ms", "ProcessClientAnchor2: short read"); + return; + } + Scale( l ); + Scale( t ); + Scale( r ); + Scale( b ); + rObj.aChildAnchor = tools::Rectangle( l, t, r, b ); + rObj.bChildAnchor = true; +}; + +void SdrEscherImport::RecolorGraphic( SvStream& rSt, sal_uInt32 nRecLen, Graphic& rGraphic ) +{ + if ( rGraphic.GetType() != GraphicType::GdiMetafile ) + return; + + sal_uInt16 nX, nGlobalColorsCount, nFillColorsCount; + + rSt.ReadUInt16( nX ) + .ReadUInt16( nGlobalColorsCount ) + .ReadUInt16( nFillColorsCount ) + .ReadUInt16( nX ) + .ReadUInt16( nX ) + .ReadUInt16( nX ); + + if ( ( nGlobalColorsCount > 64 ) || ( nFillColorsCount > 64 ) ) + return; + + if ( static_cast( ( nGlobalColorsCount + nFillColorsCount ) * 44 + 12 ) != nRecLen ) + return; + + sal_uInt32 OriginalGlobalColors[ 64 ]; + sal_uInt32 NewGlobalColors[ 64 ]; + + sal_uInt32 i, j, nGlobalColorsChanged, nFillColorsChanged; + nGlobalColorsChanged = nFillColorsChanged = 0; + + sal_uInt32* pCurrentOriginal = OriginalGlobalColors; + sal_uInt32* pCurrentNew = NewGlobalColors; + sal_uInt32* pCount = &nGlobalColorsChanged; + i = nGlobalColorsCount; + + for ( j = 0; j < 2; j++ ) + { + for ( ; i > 0; i-- ) + { + sal_uInt64 nPos = rSt.Tell(); + sal_uInt16 nChanged; + rSt.ReadUInt16( nChanged ); + if ( nChanged & 1 ) + { + sal_uInt8 nDummy, nRed, nGreen, nBlue; + sal_uInt32 nColor = 0; + sal_uInt32 nIndex; + rSt.ReadUChar( nDummy ) + .ReadUChar( nRed ) + .ReadUChar( nDummy ) + .ReadUChar( nGreen ) + .ReadUChar( nDummy ) + .ReadUChar( nBlue ) + .ReadUInt32( nIndex ); + + if ( nIndex < 8 ) + { + Color aColor = MSO_CLR_ToColor( nIndex << 24 ); + nRed = aColor.GetRed(); + nGreen = aColor.GetGreen(); + nBlue = aColor.GetBlue(); + } + nColor = nRed | ( nGreen << 8 ) | ( nBlue << 16 ); + *pCurrentNew++ = nColor; + rSt.ReadUChar( nDummy ) + .ReadUChar( nRed ) + .ReadUChar( nDummy ) + .ReadUChar( nGreen ) + .ReadUChar( nDummy ) + .ReadUChar( nBlue ); + nColor = nRed | ( nGreen << 8 ) | ( nBlue << 16 ); + *pCurrentOriginal++ = nColor; + (*pCount)++; + } + rSt.Seek( nPos + 44 ); + } + pCount = &nFillColorsChanged; + i = nFillColorsCount; + } + if ( !(nGlobalColorsChanged || nFillColorsChanged) ) + return; + + std::unique_ptr pSearchColors(new Color[ nGlobalColorsChanged ]); + std::unique_ptr pReplaceColors(new Color[ nGlobalColorsChanged ]); + + for ( j = 0; j < nGlobalColorsChanged; j++ ) + { + sal_uInt32 nSearch = OriginalGlobalColors[ j ]; + sal_uInt32 nReplace = NewGlobalColors[ j ]; + + pSearchColors[ j ].SetRed( static_cast(nSearch) ); + pSearchColors[ j ].SetGreen( static_cast( nSearch >> 8 ) ); + pSearchColors[ j ].SetBlue( static_cast( nSearch >> 16 ) ); + + pReplaceColors[ j ].SetRed( static_cast(nReplace) ); + pReplaceColors[ j ].SetGreen( static_cast( nReplace >> 8 ) ); + pReplaceColors[ j ].SetBlue( static_cast( nReplace >> 16 ) ); + } + GDIMetaFile aGdiMetaFile( rGraphic.GetGDIMetaFile() ); + aGdiMetaFile.ReplaceColors( pSearchColors.get(), pReplaceColors.get(), + nGlobalColorsChanged ); + rGraphic = aGdiMetaFile; +} + +sal_uLong DffPropSet::SanitizeEndPos(SvStream &rIn, sal_uLong nEndRecPos) +{ + auto nStreamLen = rIn.Tell() + rIn.remainingSize(); + if (nEndRecPos > nStreamLen) + { + SAL_WARN("filter.ms", "Parsing error: " << nStreamLen << + " max end pos, but " << nEndRecPos << " claimed, truncating"); + nEndRecPos = nStreamLen; + } + return nEndRecPos; +} + +void ProcessData::NotifyFreeObj(SdrObject* pObj) +{ + if (rPersistEntry.xSolverContainer) + { + for (auto & pPtr : rPersistEntry.xSolverContainer->aCList) + { + if (pPtr->pAObj == pObj) + pPtr->pAObj = nullptr; + if (pPtr->pBObj == pObj) + pPtr->pBObj = nullptr; + if (pPtr->pCObj == pObj) + pPtr->pCObj = nullptr; + } + } +} + +/* ProcessObject is called from ImplSdPPTImport::ProcessObj to handle all application specific things, + such as the import of text, animation effects, header footer and placeholder. + + The parameter pOriginalObj is the object as it was imported by our general escher import, it must either + be deleted or it can be returned to be inserted into the sdr page. +*/ +SdrObject* SdrEscherImport::ProcessObj( SvStream& rSt, DffObjData& rObjData, SvxMSDffClientData& rClientData, tools::Rectangle& rTextRect, SdrObject* pOriginalObj ) +{ + if ( dynamic_cast(pOriginalObj) != nullptr ) + pOriginalObj->SetMergedItem( SdrTextFixedCellHeightItem( true ) ); + + // we are initializing our return value with the object that was imported by our escher import + SdrObject* pRet = pOriginalObj; + + ProcessData& rData = static_cast(rClientData); + PptSlidePersistEntry& rPersistEntry = rData.rPersistEntry; + + if ( ! (rObjData.nSpFlags & ShapeFlag::Group) ) // sj: #114758# ... + { + PptOEPlaceholderAtom aPlaceholderAtom; + + if ( maShapeRecords.SeekToContent( rSt, DFF_msofbtClientData, SEEK_FROM_CURRENT_AND_RESTART ) ) + { + sal_Int16 nHeaderFooterInstance = -1; + DffRecordHeader aClientDataHd; + auto nEndRecPos = SanitizeEndPos(rSt, maShapeRecords.Current()->GetRecEndFilePos()); + while ( ( rSt.GetError() == ERRCODE_NONE ) && ( rSt.Tell() < nEndRecPos ) ) + { + ReadDffRecordHeader( rSt, aClientDataHd ); + switch ( aClientDataHd.nRecType ) + { + // importing header/footer object from master page + case PPT_PST_OEPlaceholderAtom : + { + ReadPptOEPlaceholderAtom( rSt, aPlaceholderAtom ); + if ( nHeaderFooterInstance == -1 ) + { + switch ( aPlaceholderAtom.nPlaceholderId ) + { + case PptPlaceholder::MASTERSLIDENUMBER : nHeaderFooterInstance++; + [[fallthrough]]; + case PptPlaceholder::MASTERFOOTER : nHeaderFooterInstance++; + [[fallthrough]]; + case PptPlaceholder::MASTERHEADER : nHeaderFooterInstance++; + [[fallthrough]]; + case PptPlaceholder::MASTERDATE : nHeaderFooterInstance++; break; + default: break; + + } + if ( ! ( nHeaderFooterInstance & 0xfffc ) ) // is this a valid instance ( 0->3 ) + rPersistEntry.HeaderFooterOfs[ nHeaderFooterInstance ] = rObjData.rSpHd.GetRecBegFilePos(); + } + } + break; + + case PPT_PST_RecolorInfoAtom : + { + if ( auto pSdrGrafObj = dynamic_cast(pRet) ) + if ( pSdrGrafObj->HasGDIMetaFile() ) + { + Graphic aGraphic( pSdrGrafObj->GetGraphic() ); + RecolorGraphic( rSt, aClientDataHd.nRecLen, aGraphic ); + pSdrGrafObj->SetGraphic( aGraphic ); + } + } + break; + } + if (!aClientDataHd.SeekToEndOfRecord(rSt)) + break; + } + } + if ( ( aPlaceholderAtom.nPlaceholderId == PptPlaceholder::NOTESSLIDEIMAGE ) && !rPersistEntry.bNotesMaster ) + { + sal_uInt16 nPageNum = pSdrModel->GetPageCount(); + if ( nPageNum > 0 ) + nPageNum--; + + // replacing the object which we will return with a SdrPageObj + SdrObject::Free( pRet ); + pRet = new SdrPageObj( + *pSdrModel, + rObjData.aBoundRect, + pSdrModel->GetPage(nPageNum - 1)); + } + else + { + // try to load some ppt text + PPTTextObj aTextObj( rSt, static_cast(*this), rPersistEntry, &rObjData ); + if ( aTextObj.Count() || aTextObj.GetOEPlaceHolderAtom() ) + { + bool bVerticalText = false; + // and if the text object is not empty, it must be applied to pRet, the object we + // initially got from our escher import + Degree100 nTextRotationAngle(0); + if ( IsProperty( DFF_Prop_txflTextFlow ) ) + { + auto eTextFlow = GetPropertyValue(DFF_Prop_txflTextFlow, 0) & 0xFFFF; + switch( eTextFlow ) + { + case mso_txflBtoT : // Bottom to Top non-@ + nTextRotationAngle += 9000_deg100; + break; + case mso_txflTtoBA : /* #68110# */ // Top to Bottom @-font + case mso_txflTtoBN : // Top to Bottom non-@ + case mso_txflVertN : // Vertical, non-@, top to bottom + bVerticalText = !bVerticalText; // nTextRotationAngle += 27000; + break; + // case mso_txflHorzN : // Horizontal non-@, normal + // case mso_txflHorzA : // Horizontal @-font, normal + default: break; + } + } + sal_Int32 nFontDirection = GetPropertyValue( DFF_Prop_cdirFont, mso_cdir0 ); + if ( ( nFontDirection == 1 ) || ( nFontDirection == 3 ) ) + { + bVerticalText = !bVerticalText; + } + const bool bFail = o3tl::checked_multiply(nFontDirection, 9000, nFontDirection); + if (!bFail) + nTextRotationAngle -= Degree100(nFontDirection); + else + SAL_WARN("filter.ms", "Parsing error: bad fontdirection: " << nFontDirection); + aTextObj.SetVertical( bVerticalText ); + if ( pRet ) + { + bool bDeleteSource = aTextObj.GetOEPlaceHolderAtom() != nullptr; + if ( bDeleteSource && dynamic_cast(pRet) == nullptr // we are not allowed to get + && dynamic_cast(pRet) == nullptr // grouped placeholder objects + && dynamic_cast(pRet) == nullptr ) + SdrObject::Free( pRet ); + } + sal_uInt32 nTextFlags = aTextObj.GetTextFlags(); + sal_Int32 nTextLeft = GetPropertyValue( DFF_Prop_dxTextLeft, 25 * 3600 ); // 0.25 cm (emu) + sal_Int32 nTextRight = GetPropertyValue( DFF_Prop_dxTextRight, 25 * 3600 ); // 0.25 cm (emu) + sal_Int32 nTextTop = GetPropertyValue( DFF_Prop_dyTextTop, 13 * 3600 ); // 0.13 cm (emu) + sal_Int32 nTextBottom = GetPropertyValue( DFF_Prop_dyTextBottom, 13 * 3600 ); + ScaleEmu( nTextLeft ); + ScaleEmu( nTextRight ); + ScaleEmu( nTextTop ); + ScaleEmu( nTextBottom ); + + sal_Int32 nMinFrameWidth = 0; + sal_Int32 nMinFrameHeight = 0; + bool bAutoGrowWidth, bAutoGrowHeight; + + SdrTextVertAdjust eTVA; + SdrTextHorzAdjust eTHA; + + nTextFlags &= PPT_TEXTOBJ_FLAGS_PARA_ALIGNMENT_USED_LEFT | PPT_TEXTOBJ_FLAGS_PARA_ALIGNMENT_USED_RIGHT + | PPT_TEXTOBJ_FLAGS_PARA_ALIGNMENT_USED_CENTER | PPT_TEXTOBJ_FLAGS_PARA_ALIGNMENT_USED_BLOCK; + + if ( bVerticalText ) + { + eTVA = SDRTEXTVERTADJUST_BLOCK; + eTHA = SDRTEXTHORZADJUST_CENTER; + + // read text anchor + auto eTextAnchor = GetPropertyValue(DFF_Prop_anchorText, mso_anchorTop); + + switch( eTextAnchor ) + { + case mso_anchorTop: + case mso_anchorTopCentered: + case mso_anchorTopBaseline: + case mso_anchorTopCenteredBaseline: + eTHA = SDRTEXTHORZADJUST_RIGHT; + break; + + case mso_anchorMiddle : + case mso_anchorMiddleCentered: + eTHA = SDRTEXTHORZADJUST_CENTER; + break; + + case mso_anchorBottom: + case mso_anchorBottomCentered: + case mso_anchorBottomBaseline: + case mso_anchorBottomCenteredBaseline: + eTHA = SDRTEXTHORZADJUST_LEFT; + break; + } + switch ( eTextAnchor ) + { + case mso_anchorTopCentered : + case mso_anchorMiddleCentered : + case mso_anchorBottomCentered : + case mso_anchorTopCenteredBaseline: + case mso_anchorBottomCenteredBaseline: + { + // check if it is sensible to use the centered alignment + const sal_uInt32 nMask = PPT_TEXTOBJ_FLAGS_PARA_ALIGNMENT_USED_LEFT | PPT_TEXTOBJ_FLAGS_PARA_ALIGNMENT_USED_CENTER | PPT_TEXTOBJ_FLAGS_PARA_ALIGNMENT_USED_RIGHT | PPT_TEXTOBJ_FLAGS_PARA_ALIGNMENT_USED_BLOCK; + switch (nTextFlags & nMask) + { + case PPT_TEXTOBJ_FLAGS_PARA_ALIGNMENT_USED_LEFT: + case PPT_TEXTOBJ_FLAGS_PARA_ALIGNMENT_USED_CENTER: + case PPT_TEXTOBJ_FLAGS_PARA_ALIGNMENT_USED_RIGHT: + case PPT_TEXTOBJ_FLAGS_PARA_ALIGNMENT_USED_BLOCK: + eTVA = SDRTEXTVERTADJUST_CENTER; // If the textobject has only one type of alignment, then the text has not to be displayed using the full width; + break; + } + break; + } + default: + break; + } + nMinFrameWidth = rTextRect.GetWidth() - ( nTextLeft + nTextRight ); + } + else + { + eTVA = SDRTEXTVERTADJUST_CENTER; + eTHA = SDRTEXTHORZADJUST_BLOCK; + + // read text anchor + auto eTextAnchor = GetPropertyValue(DFF_Prop_anchorText, mso_anchorTop); + + switch( eTextAnchor ) + { + case mso_anchorTop: + case mso_anchorTopCentered: + case mso_anchorTopBaseline: + case mso_anchorTopCenteredBaseline: + eTVA = SDRTEXTVERTADJUST_TOP; + break; + + case mso_anchorMiddle : + case mso_anchorMiddleCentered: + eTVA = SDRTEXTVERTADJUST_CENTER; + break; + + case mso_anchorBottom: + case mso_anchorBottomCentered: + case mso_anchorBottomBaseline: + case mso_anchorBottomCenteredBaseline: + eTVA = SDRTEXTVERTADJUST_BOTTOM; + break; + } + switch ( eTextAnchor ) + { + case mso_anchorTopCentered : + case mso_anchorMiddleCentered : + case mso_anchorBottomCentered : + case mso_anchorTopCenteredBaseline: + case mso_anchorBottomCenteredBaseline: + { + // check if it is sensible to use the centered alignment + const sal_uInt32 nMask = PPT_TEXTOBJ_FLAGS_PARA_ALIGNMENT_USED_LEFT | PPT_TEXTOBJ_FLAGS_PARA_ALIGNMENT_USED_CENTER | PPT_TEXTOBJ_FLAGS_PARA_ALIGNMENT_USED_RIGHT | PPT_TEXTOBJ_FLAGS_PARA_ALIGNMENT_USED_BLOCK; + switch (nTextFlags & nMask) + { + case PPT_TEXTOBJ_FLAGS_PARA_ALIGNMENT_USED_LEFT: + case PPT_TEXTOBJ_FLAGS_PARA_ALIGNMENT_USED_CENTER: + case PPT_TEXTOBJ_FLAGS_PARA_ALIGNMENT_USED_RIGHT: + case PPT_TEXTOBJ_FLAGS_PARA_ALIGNMENT_USED_BLOCK: + eTHA = SDRTEXTHORZADJUST_CENTER; // If the textobject has only one type of alignment, then the text has not to be displayed using the full width; + break; + } + break; + } + default: + break; + } + nMinFrameHeight = rTextRect.GetHeight() - ( nTextTop + nTextBottom ); + } + + SdrObjKind eTextKind = SdrObjKind::Rectangle; + if ( ( aPlaceholderAtom.nPlaceholderId == PptPlaceholder::NOTESSLIDEIMAGE ) + || ( aPlaceholderAtom.nPlaceholderId == PptPlaceholder::MASTERNOTESSLIDEIMAGE ) ) + { + aTextObj.SetInstance( TSS_Type::Notes ); + eTextKind = SdrObjKind::TitleText; + } + else if ( ( aPlaceholderAtom.nPlaceholderId == PptPlaceholder::MASTERNOTESBODYIMAGE ) + || ( aPlaceholderAtom.nPlaceholderId == PptPlaceholder::NOTESBODY ) ) + { + aTextObj.SetInstance( TSS_Type::Notes ); + eTextKind = SdrObjKind::Text; + } + + TSS_Type nDestinationInstance = aTextObj.GetInstance(); + if ( rPersistEntry.ePageKind == PPT_MASTERPAGE ) + { + if ( !rPersistEntry.pPresentationObjects ) + { + rPersistEntry.pPresentationObjects.reset( new sal_uInt32[ PPT_STYLESHEETENTRIES ] ); + memset( rPersistEntry.pPresentationObjects.get(), 0, PPT_STYLESHEETENTRIES * 4 ); + } + if ( !rPersistEntry.pPresentationObjects[ static_cast(nDestinationInstance) ] ) + rPersistEntry.pPresentationObjects[ static_cast(nDestinationInstance) ] = rObjData.rSpHd.GetRecBegFilePos(); + } + switch ( nDestinationInstance ) + { + case TSS_Type::PageTitle : + case TSS_Type::Title : + { + if ( GetSlideLayoutAtom()->eLayout == PptSlideLayout::TITLEMASTERSLIDE ) + nDestinationInstance = TSS_Type::Title; + else + nDestinationInstance = TSS_Type::PageTitle; + } + break; + case TSS_Type::Body : + case TSS_Type::HalfBody : + case TSS_Type::QuarterBody : + nDestinationInstance = TSS_Type::Body; + break; + default: break; + } + aTextObj.SetDestinationInstance( nDestinationInstance ); + + bool bAutoFit = false; // auto-scale text into shape box + switch ( aTextObj.GetInstance() ) + { + case TSS_Type::PageTitle : + case TSS_Type::Title : eTextKind = SdrObjKind::TitleText; break; + case TSS_Type::Subtitle : eTextKind = SdrObjKind::Text; break; + case TSS_Type::Body : + case TSS_Type::HalfBody : + case TSS_Type::QuarterBody : eTextKind = SdrObjKind::OutlineText; bAutoFit = true; break; + default: break; + } + if ( aTextObj.GetDestinationInstance() != TSS_Type::TextInShape ) + { + if ( !aTextObj.GetOEPlaceHolderAtom() || aTextObj.GetOEPlaceHolderAtom()->nPlaceholderId == PptPlaceholder::NONE ) + { + aTextObj.SetDestinationInstance( TSS_Type::TextInShape ); + eTextKind = SdrObjKind::Rectangle; + } + } + SdrObject* pTObj = nullptr; + bool bWordWrap = GetPropertyValue(DFF_Prop_WrapText, mso_wrapSquare) != mso_wrapNone; + bool bFitShapeToText = ( GetPropertyValue( DFF_Prop_FitTextToShape, 0 ) & 2 ) != 0; + + if ( dynamic_cast(pRet) != nullptr && ( eTextKind == SdrObjKind::Rectangle ) ) + { + bAutoGrowHeight = bFitShapeToText; + bAutoGrowWidth = !bWordWrap; + pTObj = pRet; + pRet = nullptr; + } + else + { + if ( dynamic_cast(pRet) != nullptr ) + { + SdrObject::Free( pRet ); + pRet = nullptr; + } + pTObj = new SdrRectObj( + *pSdrModel, + eTextKind != SdrObjKind::Rectangle ? eTextKind : SdrObjKind::Text); + SfxItemSet aSet( pSdrModel->GetItemPool() ); + if ( !pRet ) + ApplyAttributes( rSt, aSet, rObjData ); + pTObj->SetMergedItemSet( aSet ); + if ( pRet ) + { + pTObj->SetMergedItem( XLineStyleItem( drawing::LineStyle_NONE ) ); + pTObj->SetMergedItem( XFillStyleItem( drawing::FillStyle_NONE ) ); + } + if ( bVerticalText ) + { + bAutoGrowWidth = bFitShapeToText; + bAutoGrowHeight = false; + } + else + { + bAutoGrowWidth = false; + + // #119885# re-activating bFitShapeToText here, could not find deeper explanations + // for it (it was from 2005). Keeping the old comment here for reference + // old comment: // bFitShapeToText; can't be used, because we cut the text if it is too height, + bAutoGrowHeight = bFitShapeToText; + } + } + pTObj->SetMergedItem( SvxFrameDirectionItem( bVerticalText ? SvxFrameDirection::Vertical_RL_TB : SvxFrameDirection::Horizontal_LR_TB, EE_PARA_WRITINGDIR ) ); + + //Autofit text only if there is no auto grow height and width + //See fdo#41245 + if (bAutoFit && !bAutoGrowHeight && !bAutoGrowWidth) + { + pTObj->SetMergedItem( SdrTextFitToSizeTypeItem(drawing::TextFitToSizeType_AUTOFIT) ); + } + + if ( dynamic_cast(pTObj) == nullptr ) + { + pTObj->SetMergedItem( makeSdrTextAutoGrowWidthItem( bAutoGrowWidth ) ); + pTObj->SetMergedItem( makeSdrTextAutoGrowHeightItem( bAutoGrowHeight ) ); + } + else + { + pTObj->SetMergedItem( makeSdrTextWordWrapItem( bWordWrap ) ); + pTObj->SetMergedItem( makeSdrTextAutoGrowHeightItem( bFitShapeToText ) ); + } + + pTObj->SetMergedItem( SdrTextVertAdjustItem( eTVA ) ); + pTObj->SetMergedItem( SdrTextHorzAdjustItem( eTHA ) ); + + if ( nMinFrameHeight < 0 ) + nMinFrameHeight = 0; + if ( dynamic_cast(pTObj) == nullptr ) + pTObj->SetMergedItem( makeSdrTextMinFrameHeightItem( nMinFrameHeight ) ); + + if ( nMinFrameWidth < 0 ) + nMinFrameWidth = 0; + if ( dynamic_cast(pTObj) == nullptr ) + pTObj->SetMergedItem( makeSdrTextMinFrameWidthItem( nMinFrameWidth ) ); + + // set margins at the borders of the textbox + pTObj->SetMergedItem( makeSdrTextLeftDistItem( nTextLeft ) ); + pTObj->SetMergedItem( makeSdrTextRightDistItem( nTextRight ) ); + pTObj->SetMergedItem( makeSdrTextUpperDistItem( nTextTop ) ); + pTObj->SetMergedItem( makeSdrTextLowerDistItem( nTextBottom ) ); + pTObj->SetMergedItem( SdrTextFixedCellHeightItem( true ) ); + + if ( dynamic_cast(pTObj) == nullptr ) + pTObj->SetSnapRect( rTextRect ); + pTObj = ReadObjText( &aTextObj, pTObj, rData.pPage ); + + if ( pTObj ) + { + /* check if our new snaprect makes trouble, + because we do not display the ADJUST_BLOCK + properly if the textsize is bigger than the + snaprect of the object. Then we will use + ADJUST_CENTER instead of ADJUST_BLOCK. + */ + if ( dynamic_cast(pTObj) == nullptr && !bFitShapeToText && !bWordWrap ) + { + SdrTextObj* pText = dynamic_cast( pTObj ); + if ( pText ) + { + if ( bVerticalText ) + { + if ( eTVA == SDRTEXTVERTADJUST_BLOCK ) + { + Size aTextSize( pText->GetTextSize() ); + aTextSize.AdjustWidth(nTextLeft + nTextRight ); + aTextSize.AdjustHeight(nTextTop + nTextBottom ); + if ( rTextRect.GetHeight() < aTextSize.Height() ) + pTObj->SetMergedItem( SdrTextVertAdjustItem( SDRTEXTVERTADJUST_CENTER ) ); + } + } + else + { + if ( eTHA == SDRTEXTHORZADJUST_BLOCK ) + { + Size aTextSize( pText->GetTextSize() ); + aTextSize.AdjustWidth(nTextLeft + nTextRight ); + aTextSize.AdjustHeight(nTextTop + nTextBottom ); + if ( rTextRect.GetWidth() < aTextSize.Width() ) + pTObj->SetMergedItem( SdrTextHorzAdjustItem( SDRTEXTHORZADJUST_CENTER ) ); + } + } + } + } + // rotate text with shape? + Degree100 nAngle = ( rObjData.nSpFlags & ShapeFlag::FlipV ) ? -mnFix16Angle : mnFix16Angle; // #72116# vertical flip -> rotate by using the other way + nAngle += nTextRotationAngle; + + if ( dynamic_cast< const SdrObjCustomShape* >(pTObj) == nullptr ) + { + if ( rObjData.nSpFlags & ShapeFlag::FlipV ) + { + pTObj->Rotate( rTextRect.Center(), 18000_deg100, 0.0, -1.0 ); + } + if ( rObjData.nSpFlags & ShapeFlag::FlipH ) + nAngle = 36000_deg100 - nAngle; + if ( nAngle ) + pTObj->NbcRotate( rObjData.aBoundRect.Center(), nAngle ); + } + if ( pRet ) + { + SdrObject* pGroup = new SdrObjGroup(*pSdrModel); + pGroup->GetSubList()->NbcInsertObject( pRet ); + pGroup->GetSubList()->NbcInsertObject( pTObj ); + pRet = pGroup; + } + else + pRet = pTObj; + } + } + } + } + else + { + if ( maShapeRecords.SeekToContent( rSt, DFF_msofbtUDefProp, SEEK_FROM_CURRENT_AND_RESTART ) ) + { + maShapeRecords.Current()->SeekToBegOfRecord( rSt ); + DffPropertyReader aSecPropSet( *this ); + aSecPropSet.ReadPropSet( rSt, &rClientData ); + sal_Int32 nTableProperties = aSecPropSet.GetPropertyValue( DFF_Prop_tableProperties, 0 ); + if ( nTableProperties & 3 ) + { + if ( aSecPropSet.SeekToContent( DFF_Prop_tableRowProperties, rSt ) ) + { + sal_Int16 i, nReadRowCount = 0; + rSt.ReadInt16( nReadRowCount ).ReadInt16( i ).ReadInt16( i ); + if (nReadRowCount > 0) + { + const size_t nMinRecordSize = 4; + const size_t nMaxRecords = rSt.remainingSize() / nMinRecordSize; + + auto nRowCount = o3tl::make_unsigned(nReadRowCount); + if (nRowCount > nMaxRecords) + { + SAL_WARN("filter.ms", "Parsing error: " << nMaxRecords << + " max possible entries, but " << nRowCount << " claimed, truncating"); + nRowCount = nMaxRecords; + } + if (nRowCount > 0) + { + std::unique_ptr pTableArry(new sal_uInt32[ nRowCount + 2 ]); + pTableArry[ 0 ] = nTableProperties; + pTableArry[ 1 ] = nRowCount; + for (decltype(nRowCount) nRow = 0; nRow < nRowCount; ++nRow) + rSt.ReadUInt32(pTableArry[nRow + 2]); + rData.pTableRowProperties = std::move(pTableArry); + } + } + } + } + } + } + if ( pRet ) // sj: #i38501#, and taking care of connections to group objects + { + if ( rObjData.nSpFlags & ShapeFlag::Background ) + { + pRet->NbcSetSnapRect( tools::Rectangle( Point(), rData.pPage.page->GetSize() ) ); // set size + } + if (rPersistEntry.xSolverContainer) + { + for (auto & pPtr : rPersistEntry.xSolverContainer->aCList) + { + if ( rObjData.nShapeId == pPtr->nShapeC ) + pPtr->pCObj = pRet; + else + { + SdrObject* pConnectObj = pRet; + if ( pOriginalObj && dynamic_cast< const SdrObjGroup* >(pRet) != nullptr ) + { /* check if the original object from the escherimport is part of the group object, + if this is the case, we will use the original object to connect to */ + SdrObjListIter aIter( *pRet, SdrIterMode::DeepWithGroups ); + while( aIter.IsMore() ) + { + SdrObject* pPartObj = aIter.Next(); + if ( pPartObj == pOriginalObj ) + { + pConnectObj = pPartObj; + break; + } + } + } + if ( rObjData.nShapeId == pPtr->nShapeA ) + { + pPtr->pAObj = pConnectObj; + pPtr->nSpFlagsA = rObjData.nSpFlags; + } + if ( rObjData.nShapeId == pPtr->nShapeB ) + { + pPtr->pBObj = pConnectObj; + pPtr->nSpFlagsB = rObjData.nSpFlags; + } + } + } + } + if ( rPersistEntry.ePageKind == PPT_MASTERPAGE ) + { // maybe the escher clusterlist is not correct, but we have to got the right page by using the + // spMaster property, so we are patching the table + if ( rPersistEntry.nDrawingDgId != 0xffffffff ) + { + sal_uInt32 nSec = ( rObjData.nShapeId >> 10 ) - 1; + if ( !maFidcls.empty() && ( nSec < mnIdClusters ) ) + maFidcls[ nSec ].dgid = rPersistEntry.nDrawingDgId; // insert the correct drawing id; + } + } + if ( GetPropertyValue( DFF_Prop_fNoFillHitTest, 0 ) & 0x10 ) + { + if (GetPropertyValue(DFF_Prop_fillType, mso_fillSolid) == mso_fillBackground) + { + rData.aBackgroundColoredObjects.push_back( pRet ); + } + } + } + return pRet; +} + +SdrPowerPointImport::SdrPowerPointImport( PowerPointImportParam& rParam, const OUString& rBaseURL ) : + SdrEscherImport ( rParam, rBaseURL ), + m_bOk ( rStCtrl.GetErrorCode() == ERRCODE_NONE ), + m_nPersistPtrCnt ( 0 ), + m_pDefaultSheet ( nullptr ), + m_nCurrentPageNum ( 0 ), + m_nDocStreamPos ( 0 ), + m_nPageColorsNum ( 0xFFFF ), + m_ePageColorsKind ( PPT_MASTERPAGE ), + m_eCurrentPageKind ( PPT_MASTERPAGE ) +{ + if ( m_bOk ) + { + nStreamLen = rStCtrl.TellEnd(); + + // try to allocate the UserEditAtom via CurrentUserAtom + sal_uInt32 nCurrentUserEdit = rParam.aCurrentUserAtom.nCurrentUserEdit; + if (nCurrentUserEdit && checkSeek(rStCtrl, nCurrentUserEdit)) + { + ReadPptUserEditAtom( rStCtrl, m_aUserEditAtom ); + } + if ( !m_aUserEditAtom.nOffsetPersistDirectory ) + { // if there is no UserEditAtom try to search the last one + + rStCtrl.Seek( 0 ); + DffRecordManager aPptRecManager; // contains all first level container and atoms + aPptRecManager.Consume( rStCtrl, nStreamLen ); + DffRecordHeader* pHd; + for ( pHd = aPptRecManager.Last(); pHd; pHd = aPptRecManager.Prev() ) + { + if ( pHd->nRecType == PPT_PST_UserEditAtom ) + { + pHd->SeekToBegOfRecord( rStCtrl ); + ReadPptUserEditAtom( rStCtrl, m_aUserEditAtom ); + break; + } + } + if ( !pHd ) + m_bOk = false; + } + } + if ( rStCtrl.GetError() != ERRCODE_NONE ) + m_bOk = false; + + if ( m_bOk ) + { + m_nPersistPtrCnt = m_aUserEditAtom.nMaxPersistWritten + 1; + if ( ( m_nPersistPtrCnt >> 2 ) > nStreamLen ) // sj: at least m_nPersistPtrCnt is not allowed to be greater than filesize + m_bOk = false; // (it should not be greater than the PPT_PST_PersistPtrIncrementalBlock, but + // we are reading this block later, so we do not have access yet) + + if ( m_bOk && ( m_nPersistPtrCnt < ( SAL_MAX_UINT32 / sizeof( sal_uInt32 ) ) -1 ) ) + m_pPersistPtr.reset( new (std::nothrow) sal_uInt32[ m_nPersistPtrCnt + 1 ] ); + if ( !m_pPersistPtr ) + m_bOk = false; + if ( m_bOk ) + { + memset( m_pPersistPtr.get(), 0x00, (m_nPersistPtrCnt+1) * sizeof(sal_uInt32) ); + + // SJ: new search mechanism from bottom to top (Issue 21122) + PptUserEditAtom aCurrentEditAtom( m_aUserEditAtom ); + sal_uInt32 nCurrentEditAtomStrmPos = aCurrentEditAtom.aHd.GetRecEndFilePos(); + while( nCurrentEditAtomStrmPos ) + { + sal_uInt32 nPersistIncPos = aCurrentEditAtom.nOffsetPersistDirectory; + if (nPersistIncPos && checkSeek(rStCtrl, nPersistIncPos)) + { + DffRecordHeader aPersistHd; + ReadDffRecordHeader( rStCtrl, aPersistHd ); + if ( aPersistHd.nRecType == PPT_PST_PersistPtrIncrementalBlock ) + { + sal_uLong nPibLen = aPersistHd.GetRecEndFilePos(); + while (m_bOk && rStCtrl.good() && (rStCtrl.Tell() < nPibLen)) + { + sal_uInt32 nOfs(0); + rStCtrl.ReadUInt32( nOfs ); + sal_uInt32 nCnt = nOfs; + nOfs &= 0x000FFFFF; + nCnt >>= 20; + while (m_bOk && rStCtrl.good() && (nCnt > 0) && (nOfs <= m_nPersistPtrCnt)) + { + sal_uInt32 nPt(0); + rStCtrl.ReadUInt32( nPt ); + if ( !m_pPersistPtr[ nOfs ] ) + { + m_pPersistPtr[ nOfs ] = nPt; + if ( m_pPersistPtr[ nOfs ] > nStreamLen ) + { + m_bOk = false; + OSL_FAIL("SdrPowerPointImport::Ctor(): Invalid Entry in Persist-Directory!"); + } + } + nCnt--; + nOfs++; + } + if ( m_bOk && nCnt > 0 ) + { + OSL_FAIL("SdrPowerPointImport::Ctor(): Not all entries of Persist-Directory read!"); + m_bOk = false; + } + } + } + } + nCurrentEditAtomStrmPos = aCurrentEditAtom.nOffsetLastEdit < nCurrentEditAtomStrmPos ? aCurrentEditAtom.nOffsetLastEdit : 0; + if (nCurrentEditAtomStrmPos && checkSeek(rStCtrl, nCurrentEditAtomStrmPos)) + { + ReadPptUserEditAtom( rStCtrl, aCurrentEditAtom ); + } + } + } + } + if ( rStCtrl.GetError() != ERRCODE_NONE ) + m_bOk = false; + if ( m_bOk ) + { // check Document PersistEntry + m_nDocStreamPos = m_aUserEditAtom.nDocumentRef; + if ( m_nDocStreamPos > m_nPersistPtrCnt ) + { + OSL_FAIL("SdrPowerPointImport::Ctor(): m_aUserEditAtom.nDocumentRef invalid!"); + m_bOk = false; + } + } + if ( m_bOk ) + { // check Document FilePos + m_nDocStreamPos = m_pPersistPtr[ m_nDocStreamPos ]; + if ( m_nDocStreamPos >= nStreamLen ) + { + OSL_FAIL("SdrPowerPointImport::Ctor(): m_nDocStreamPos >= nStreamLen!"); + m_bOk = false; + } + } + if ( m_bOk ) + { + rStCtrl.Seek( m_nDocStreamPos ); + aDocRecManager.Consume( rStCtrl ); + + DffRecordHeader aDocHd; + ReadDffRecordHeader( rStCtrl, aDocHd ); + // read DocumentAtom + DffRecordHeader aDocAtomHd; + ReadDffRecordHeader( rStCtrl, aDocAtomHd ); + if ( aDocHd.nRecType == PPT_PST_Document && aDocAtomHd.nRecType == PPT_PST_DocumentAtom ) + { + aDocAtomHd.SeekToBegOfRecord( rStCtrl ); + ReadPptDocumentAtom( rStCtrl, aDocAtom ); + } + else + m_bOk = false; + + if ( m_bOk ) + { + if (!m_xFonts) + ReadFontCollection(); + + // reading TxPF, TxSI + PPTTextParagraphStyleAtomInterpreter aTxPFStyle; + PPTTextSpecInfoAtomInterpreter aTxSIStyle; // styles (default language setting ... ) + + DffRecordHeader* pEnvHd = aDocRecManager.GetRecordHeader( PPT_PST_Environment ); + if ( pEnvHd ) + { + pEnvHd->SeekToContent( rStCtrl ); + DffRecordHeader aTxPFStyleRecHd; + if ( SeekToRec( rStCtrl, PPT_PST_TxPFStyleAtom, pEnvHd->GetRecEndFilePos(), &aTxPFStyleRecHd ) ) + aTxPFStyle.Read( rStCtrl, aTxPFStyleRecHd ); + + pEnvHd->SeekToContent( rStCtrl ); + DffRecordHeader aTxSIStyleRecHd; + if ( SeekToRec( rStCtrl, PPT_PST_TxSIStyleAtom, pEnvHd->GetRecEndFilePos(), &aTxSIStyleRecHd ) ) + { + aTxSIStyle.Read( rStCtrl, aTxSIStyleRecHd, PPT_PST_TxSIStyleAtom ); +#ifdef DBG_UTIL + if ( !aTxSIStyle.bValid ) + { + if (!(rImportParam.nImportFlags & PPT_IMPORTFLAGS_NO_TEXT_ASSERT )) + { + OSL_FAIL( "SdrTextSpecInfoAtomInterpreter::Ctor(): parsing error, this document needs to be analysed (SJ)" ); + } + } +#endif + } + } + + // TODO:: PPT_PST_TxPFStyleAtom + + // read SlidePersists + m_pMasterPages.reset( new PptSlidePersistList ); + m_pSlidePages.reset( new PptSlidePersistList ); + m_pNotePages.reset( new PptSlidePersistList ); + + // now always creating the handout page, it will be the first in our masterpage list + std::unique_ptr pE(new PptSlidePersistEntry); + pE->aPersistAtom.nPsrReference = aDocAtom.nHandoutMasterPersist; + pE->bHandoutMaster = true; + if ( !aDocAtom.nHandoutMasterPersist ) + pE->bStarDrawFiller = true; // this is a dummy master page + m_pMasterPages->insert(m_pMasterPages->begin(), std::move(pE)); + + DffRecordHeader* pSlideListWithTextHd = aDocRecManager.GetRecordHeader( PPT_PST_SlideListWithText ); + PptSlidePersistEntry* pPreviousPersist = nullptr; + DffRecordHeader* pSlideListHd = aDocRecManager.GetRecordHeader(PPT_PST_List); + sal_uLong nPSTList = 0; + if (pSlideListHd) nPSTList = pSlideListHd->GetRecBegFilePos(); + sal_uInt16 nRealPageNum = 0; + // Normal PPT document has order of Master slides - Presentation slides - Note slides + // for document with the order of Master slides - Note slides - Presentation slides + // we need to swap the later two sections + bool notePresentationSwap = false; + for (sal_uInt16 nPageListNum = 0; + pSlideListWithTextHd && nPageListNum < 3; ++nPageListNum) + { + pSlideListWithTextHd->SeekToContent( rStCtrl ); + PptSlidePersistList* pPageList = nullptr; + sal_uInt32 nSlideListWithTextHdEndOffset = pSlideListWithTextHd->GetRecEndFilePos(); + nRealPageNum = nPageListNum; + while ( SeekToRec( rStCtrl, PPT_PST_SlidePersistAtom, nSlideListWithTextHdEndOffset ) ) + { + if ( pPreviousPersist ) + pPreviousPersist->nSlidePersistEndOffset = rStCtrl.Tell(); + std::unique_ptr pE2(new PptSlidePersistEntry); + ReadPptSlidePersistAtom( rStCtrl, pE2->aPersistAtom ); + pE2->nSlidePersistStartOffset = rStCtrl.Tell(); + // Note/Presentation section swap + if (nPageListNum == 1 && pE2->nSlidePersistStartOffset < nPSTList) + { + notePresentationSwap = true; + } + if (notePresentationSwap) + { + if (nPageListNum == 1) nRealPageNum = 2; + else if (nPageListNum == 2) nRealPageNum = 1; + } + + pE2->ePageKind = PptPageKind(nRealPageNum); + pPreviousPersist = pE2.get(); + if (!pPageList) + { + pPageList = GetPageList(PptPageKind(nRealPageNum)); + } + pPageList->push_back(std::move(pE2)); + } + if ( pPreviousPersist ) + pPreviousPersist->nSlidePersistEndOffset = nSlideListWithTextHdEndOffset; + pSlideListWithTextHd = aDocRecManager.GetRecordHeader( PPT_PST_SlideListWithText, SEEK_FROM_CURRENT ); + } + + // we will ensure that there is at least one master page + if (m_pMasterPages->size() == 1) // -> there is only a handout page available + { + std::unique_ptr pE2(new PptSlidePersistEntry); + pE2->bStarDrawFiller = true; // this is a dummy master page + m_pMasterPages->insert(m_pMasterPages->begin() + 1, std::move(pE2)); + } + + // now we will insert at least one notes master for each master page + sal_uInt16 nMasterPage; + sal_uInt16 nMasterPages = m_pMasterPages->size() - 1; + for ( nMasterPage = 0; nMasterPage < nMasterPages; nMasterPage++ ) + { + std::unique_ptr pE2(new PptSlidePersistEntry); + pE2->bNotesMaster = true; + pE2->bStarDrawFiller = true; // this is a dummy master page + if ( !nMasterPage && aDocAtom.nNotesMasterPersist ) + { // special treatment for the first notes master + pE2->aPersistAtom.nPsrReference = aDocAtom.nNotesMasterPersist; + pE2->bStarDrawFiller = false; // this is a dummy master page + } + m_pMasterPages->insert(m_pMasterPages->begin() + ((nMasterPage + 1) << 1), std::move(pE2)); + } + + // read for each page the SlideAtom respectively the NotesAtom if it exists + for (sal_uInt16 nPageListNum = 0; nPageListNum < 3; ++nPageListNum) + { + PptSlidePersistList* pPageList = GetPageList( PptPageKind( nPageListNum ) ); + for ( size_t nPageNum = 0; nPageNum < pPageList->size(); nPageNum++ ) + { + PptSlidePersistEntry& rE2 = (*pPageList)[ nPageNum ]; + sal_uLong nPersist = rE2.aPersistAtom.nPsrReference; + if ( ( nPersist > 0 ) && ( nPersist < m_nPersistPtrCnt ) ) + { + sal_uLong nFPos = m_pPersistPtr[ nPersist ]; + if ( nFPos < nStreamLen ) + { + rStCtrl.Seek( nFPos ); + DffRecordHeader aSlideHd; + ReadDffRecordHeader( rStCtrl, aSlideHd ); + if ( SeekToRec( rStCtrl, PPT_PST_SlideAtom, aSlideHd.GetRecEndFilePos() ) ) + ReadPptSlideAtom( rStCtrl, rE2.aSlideAtom ); + else if ( SeekToRec( rStCtrl, PPT_PST_NotesAtom, aSlideHd.GetRecEndFilePos() ) ) + ReadPptNotesAtom( rStCtrl, rE2.aNotesAtom ); + aSlideHd.SeekToContent( rStCtrl ); + + DffRecordHeader aPPTDrawingHd; + if ( SeekToRec( rStCtrl, PPT_PST_PPDrawing, aSlideHd.GetRecEndFilePos(), &aPPTDrawingHd ) ) + { + DffRecordHeader aPPTDgContainer; + if ( SeekToRec( rStCtrl, DFF_msofbtDgContainer, aPPTDrawingHd.GetRecEndFilePos(), &aPPTDgContainer ) ) + { + if ( SeekToRec( rStCtrl, DFF_msofbtDg, aPPTDrawingHd.GetRecEndFilePos() ) ) + { + DffRecordHeader aDgRecordHeader; + ReadDffRecordHeader( rStCtrl, aDgRecordHeader ); + rE2.nDrawingDgId = aDgRecordHeader.nRecInstance; + aDgRecordHeader.SeekToEndOfRecord( rStCtrl ); + } + if ( SeekToRec( rStCtrl, DFF_msofbtSolverContainer, aPPTDgContainer.GetRecEndFilePos() ) ) + { + rE2.xSolverContainer.reset(new SvxMSDffSolverContainer); + ReadSvxMSDffSolverContainer(rStCtrl, *rE2.xSolverContainer); + } + aPPTDgContainer.SeekToBegOfRecord( rStCtrl ); + SetDgContainer( rStCtrl ); // set this, so that the escherimport is knowing of our drawings + } + } + // office xp is supporting more than one stylesheet + if ( ( rE2.ePageKind == PPT_MASTERPAGE ) && ( rE2.aSlideAtom.nMasterId == 0 ) && !rE2.bNotesMaster ) + { + PPTTextSpecInfo aTxSI( 0 ); + if ( aTxSIStyle.bValid && !aTxSIStyle.aList.empty() ) + aTxSI = aTxSIStyle.aList[ 0 ]; + + rE2.xStyleSheet = std::make_unique(aSlideHd, rStCtrl, *this, aTxPFStyle, aTxSI); + m_pDefaultSheet = rE2.xStyleSheet.get(); + } + if ( SeekToRec( rStCtrl, PPT_PST_ColorSchemeAtom, aSlideHd.GetRecEndFilePos() ) ) + ReadPptColorSchemeAtom( rStCtrl, rE2.aColorScheme ); + else + { + OSL_FAIL( "SdrPowerPointImport::Ctor(): could not get SlideColorScheme! (SJ)" ); + } + } + else + { + OSL_FAIL("SdrPowerPointImport::Ctor(): Persist entry is flawed! (SJ)"); + } + } + } + } + DffRecordHeader* pHeadersFootersHd = aDocRecManager.GetRecordHeader( PPT_PST_HeadersFooters ); + if ( pHeadersFootersHd ) + { + HeaderFooterEntry aNormalMaster, aNotesMaster; + for ( ; pHeadersFootersHd; pHeadersFootersHd = aDocRecManager.GetRecordHeader( PPT_PST_HeadersFooters, SEEK_FROM_CURRENT ) ) + { + if ( pHeadersFootersHd->nRecInstance == 3 ) // normal master + ImportHeaderFooterContainer( *pHeadersFootersHd, aNormalMaster ); + else if ( pHeadersFootersHd->nRecInstance == 4 ) // notes master + ImportHeaderFooterContainer( *pHeadersFootersHd, aNotesMaster ); + } + for (size_t i = 0; i < m_pMasterPages->size(); i++) + { + if ((*m_pMasterPages)[ i ].bNotesMaster) + (*m_pMasterPages)[ i ].xHeaderFooterEntry.reset(new HeaderFooterEntry(aNotesMaster)); + else + (*m_pMasterPages)[ i ].xHeaderFooterEntry.reset(new HeaderFooterEntry(aNormalMaster)); + } + } + } + } + if ( ( rStCtrl.GetError() != ERRCODE_NONE ) || ( m_pDefaultSheet == nullptr ) ) + m_bOk = false; + m_pPPTStyleSheet = m_pDefaultSheet; + rStCtrl.Seek( 0 ); +} + +SdrPowerPointImport::~SdrPowerPointImport() +{ + m_pMasterPages.reset(); + m_pSlidePages.reset(); + m_pNotePages.reset(); +} + +bool PPTConvertOCXControls::ReadOCXStream( tools::SvRef& rSrc, + css::uno::Reference< css::drawing::XShape > *pShapeRef ) +{ + bool bRes = false; + uno::Reference< form::XFormComponent > xFComp; + if ( mpPPTImporter && mpPPTImporter->ReadFormControl( rSrc, xFComp ) ) + { + if ( xFComp.is() ) + { + css::awt::Size aSz; // not used in import + bRes = InsertControl( xFComp, aSz,pShapeRef, false/*bFloatingCtrl*/); + } + } + return bRes; +} + +bool PPTConvertOCXControls::InsertControl( + const css::uno::Reference< css::form::XFormComponent > &rFComp, + const css::awt::Size& rSize, + css::uno::Reference< css::drawing::XShape > *pShape, + bool /*bFloatingCtrl*/) +{ + bool bRetValue = false; + try + { + css::uno::Reference< css::drawing::XShape > xShape; + + const css::uno::Reference< css::container::XIndexContainer > & rFormComps = + GetFormComps(); + + css::uno::Any aTmp( &rFComp, cppu::UnoType::get() ); + + rFormComps->insertByIndex( rFormComps->getCount(), aTmp ); + + const css::uno::Reference< css::lang::XMultiServiceFactory > & rServiceFactory = + GetServiceFactory(); + if( rServiceFactory.is() ) + { + css::uno::Reference< css::uno::XInterface > xCreate = rServiceFactory + ->createInstance( "com.sun.star.drawing.ControlShape" ); + if( xCreate.is() ) + { + xShape.set(xCreate, css::uno::UNO_QUERY); + if ( xShape.is() ) + { + xShape->setSize(rSize); + // set the Control-Model at the Control-Shape + css::uno::Reference< css::drawing::XControlShape > xControlShape( xShape, + css::uno::UNO_QUERY ); + css::uno::Reference< css::awt::XControlModel > xControlModel( rFComp, + css::uno::UNO_QUERY ); + if ( xControlShape.is() && xControlModel.is() ) + { + xControlShape->setControl( xControlModel ); + if (pShape) + *pShape = xShape; + bRetValue = true; + } + } + } + } + } + catch( ... ) + { + bRetValue = false; + } + return bRetValue; +}; +void PPTConvertOCXControls::GetDrawPage() +{ + if( xDrawPage.is() || !mxModel.is() ) + return; + + css::uno::Reference< css::drawing::XDrawPages > xDrawPages; + switch( ePageKind ) + { + case PPT_SLIDEPAGE : + case PPT_NOTEPAGE : + { + css::uno::Reference< css::drawing::XDrawPagesSupplier > + xDrawPagesSupplier( mxModel, css::uno::UNO_QUERY); + if ( xDrawPagesSupplier.is() ) + xDrawPages = xDrawPagesSupplier->getDrawPages(); + } + break; + + case PPT_MASTERPAGE : + { + css::uno::Reference< css::drawing::XMasterPagesSupplier > + xMasterPagesSupplier( mxModel, css::uno::UNO_QUERY); + if ( xMasterPagesSupplier.is() ) + xDrawPages = xMasterPagesSupplier->getMasterPages(); + } + break; + } + if ( xDrawPages.is() && xDrawPages->getCount() ) + { + xDrawPages->getCount(); + css::uno::Any aAny( xDrawPages->getByIndex( xDrawPages->getCount() - 1 ) ); + aAny >>= xDrawPage; + } +} + +static bool SdrPowerPointOLEDecompress( SvStream& rOutput, SvStream& rInput, sal_uInt32 nInputSize ) +{ + sal_uInt32 nOldPos = rInput.Tell(); + std::unique_ptr pBuf(new char[ nInputSize ]); + rInput.ReadBytes(pBuf.get(), nInputSize); + ZCodec aZCodec( 0x8000, 0x8000 ); + aZCodec.BeginCompression(); + SvMemoryStream aSource( pBuf.get(), nInputSize, StreamMode::READ ); + aZCodec.Decompress( aSource, rOutput ); + const bool bSuccess(0 != aZCodec.EndCompression()); + rInput.Seek( nOldPos ); + return bSuccess; +} + +// #i32596# - add new parameter <_nCalledByGroup> +SdrObject* SdrPowerPointImport::ImportOLE( sal_uInt32 nOLEId, + const Graphic& rGraf, + const tools::Rectangle& rBoundRect, + const tools::Rectangle& rVisArea, + const int /*_nCalledByGroup*/ ) const +{ + SdrObject* pRet = nullptr; + + sal_uInt32 nOldPos = rStCtrl.Tell(); + + Graphic aGraphic( rGraf ); + + if ( const_cast(this)->maShapeRecords.SeekToContent( rStCtrl, DFF_msofbtClientData, SEEK_FROM_CURRENT_AND_RESTART ) ) + { + DffRecordHeader aPlaceHd; + + auto nEndRecPos = SanitizeEndPos(rStCtrl, const_cast(this)->maShapeRecords.Current()->GetRecEndFilePos()); + while ( ( rStCtrl.GetError() == ERRCODE_NONE ) + && ( rStCtrl.Tell() < nEndRecPos ) ) + { + ReadDffRecordHeader( rStCtrl, aPlaceHd ); + if ( aPlaceHd.nRecType == PPT_PST_RecolorInfoAtom ) + { + const_cast(this)->RecolorGraphic( rStCtrl, aPlaceHd.nRecLen, aGraphic ); + break; + } + else + { + if (!aPlaceHd.SeekToEndOfRecord(rStCtrl)) + break; + } + } + } + + for (PPTOleEntry& rOe : const_cast(this)->aOleObjectList) + { + if ( rOe.nId != nOLEId ) + continue; + + rStCtrl.Seek( rOe.nRecHdOfs ); + + DffRecordHeader aHd; + ReadDffRecordHeader( rStCtrl, aHd ); + + sal_uInt32 nLen = aHd.nRecLen - 4; + if ( static_cast(nLen) > 0 ) + { + bool bSuccess = false; + + rStCtrl.SeekRel( 4 ); + + ::utl::TempFile aTmpFile; + aTmpFile.EnableKillingFile(); + + if ( aTmpFile.IsValid() ) + { + SvStream* pDest = aTmpFile.GetStream(StreamMode::TRUNC | StreamMode::WRITE); + if (pDest) + { + bSuccess = SdrPowerPointOLEDecompress( *pDest, rStCtrl, nLen ); + } + aTmpFile.CloseStream(); + } + if ( bSuccess ) + { + SvStream* pDest = aTmpFile.GetStream(StreamMode::READ); + Storage* pObjStor = pDest ? new Storage( *pDest, true ) : nullptr; + if (pObjStor) + { + tools::SvRef xObjStor( new SotStorage( pObjStor ) ); + if ( xObjStor.is() && !xObjStor->GetError() ) + { + if ( xObjStor->GetClassName() == SvGlobalName() ) + { + xObjStor->SetClass( SvGlobalName( pObjStor->GetClassId() ), pObjStor->GetFormat(), pObjStor->GetUserName() ); + } + tools::SvRef xSrcTst = xObjStor->OpenSotStream( "\1Ole" ); + if ( xSrcTst.is() ) + { + sal_uInt8 aTestA[ 10 ]; + bool bGetItAsOle = (sizeof(aTestA) == xSrcTst->ReadBytes(aTestA, sizeof(aTestA))); + if ( !bGetItAsOle ) + { // maybe there is a contents stream in here + xSrcTst = xObjStor->OpenSotStream( "Contents", StreamMode::READWRITE | StreamMode::NOCREATE ); + bGetItAsOle = (xSrcTst.is() && + sizeof(aTestA) == xSrcTst->ReadBytes(aTestA, sizeof(aTestA))); + } + if ( bGetItAsOle ) + { + OUString aNm; + // if ( nSvxMSDffOLEConvFlags ) + { + uno::Reference < embed::XStorage > xDestStorage( rOe.pShell->GetStorage() ); + uno::Reference < embed::XEmbeddedObject > xObj = + CheckForConvertToSOObj(nSvxMSDffOLEConvFlags, *xObjStor, xDestStorage, rGraf, rVisArea, maBaseURL); + if( xObj.is() ) + { + rOe.pShell->getEmbeddedObjectContainer().InsertEmbeddedObject( xObj, aNm ); + + svt::EmbeddedObjectRef aObj( xObj, rOe.nAspect ); + + // TODO/LATER: need MediaType for Graphic + aObj.SetGraphic( rGraf, OUString() ); + pRet = new SdrOle2Obj( + *pSdrModel, + aObj, + aNm, + rBoundRect); + } + } + if ( !pRet && ( rOe.nType == PPT_PST_ExControl ) ) + { + uno::Reference< frame::XModel > xModel( rOe.pShell->GetModel() ); + PPTConvertOCXControls aPPTConvertOCXControls( this, xModel, m_eCurrentPageKind ); + css::uno::Reference< css::drawing::XShape > xShape; + if ( aPPTConvertOCXControls.ReadOCXStream( xObjStor, &xShape ) ) + pRet = SdrObject::getSdrObjectFromXShape(xShape); + + } + if ( !pRet ) + { + aNm = rOe.pShell->getEmbeddedObjectContainer().CreateUniqueObjectName(); + + // object is not an own object + const css::uno::Reference < css::embed::XStorage >& rStorage = rOe.pShell->GetStorage(); + if (rStorage.is()) + { + tools::SvRef xTarget = SotStorage::OpenOLEStorage(rStorage, aNm, StreamMode::READWRITE); + if (xObjStor.is() && xTarget.is()) + { + xObjStor->CopyTo(xTarget.get()); + if (!xTarget->GetError()) + xTarget->Commit(); + } + xTarget.clear(); + } + + uno::Reference < embed::XEmbeddedObject > xObj = + rOe.pShell->getEmbeddedObjectContainer().GetEmbeddedObject( aNm ); + if ( xObj.is() ) + { + if ( rOe.nAspect != embed::Aspects::MSOLE_ICON ) + { + //TODO/LATER: keep on hacking?! + // we don't want to be modified + //xInplaceObj->EnableSetModified( sal_False ); + if ( rVisArea.IsEmpty() ) + { + MapUnit aMapUnit = VCLUnoHelper::UnoEmbed2VCLMapUnit( xObj->getMapUnit( rOe.nAspect ) ); + Size aSize( OutputDevice::LogicToLogic( aGraphic.GetPrefSize(), + aGraphic.GetPrefMapMode(), MapMode( aMapUnit ) ) ); + + awt::Size aSz; + aSz.Width = aSize.Width(); + aSz.Height = aSize.Height(); + xObj->setVisualAreaSize( rOe.nAspect, aSz ); + } + else + { + awt::Size aSize( rVisArea.GetSize().Width(), rVisArea.GetSize().Height() ); + xObj->setVisualAreaSize( rOe.nAspect, aSize ); + } + //xInplaceObj->EnableSetModified( sal_True ); + } + + svt::EmbeddedObjectRef aObj( xObj, rOe.nAspect ); + + // TODO/LATER: need MediaType for Graphic + aObj.SetGraphic( aGraphic, OUString() ); + + pRet = new SdrOle2Obj( + *pSdrModel, + aObj, + aNm, + rBoundRect); + } + } + } + } + } + } + aTmpFile.CloseStream(); + } + } + } + rStCtrl.Seek( nOldPos ); + + return pRet; +} + +std::unique_ptr SdrPowerPointImport::ImportExOleObjStg( sal_uInt32 nPersistPtr, sal_uInt32& nOleId ) const +{ + std::unique_ptr pRet; + if ( nPersistPtr && ( nPersistPtr < m_nPersistPtrCnt ) ) + { + sal_uInt32 nOldPos, nOfs = m_pPersistPtr[ nPersistPtr ]; + nOldPos = rStCtrl.Tell(); + rStCtrl.Seek( nOfs ); + DffRecordHeader aHd; + ReadDffRecordHeader( rStCtrl, aHd ); + if ( aHd.nRecType == DFF_PST_ExOleObjStg ) + { + sal_uInt32 nLen = aHd.nRecLen - 4; + if ( static_cast(nLen) > 0 ) + { + rStCtrl.ReadUInt32( nOleId ); + pRet.reset(new SvMemoryStream); + ZCodec aZCodec( 0x8000, 0x8000 ); + aZCodec.BeginCompression(); + aZCodec.Decompress( rStCtrl, *pRet ); + if ( !aZCodec.EndCompression() ) + { + pRet.reset(); + } + } + } + rStCtrl.Seek( nOldPos ); + } + return pRet; +} + +void SdrPowerPointImport::SeekOle( SfxObjectShell* pShell, sal_uInt32 nFilterOptions ) +{ + if ( !pShell ) + return; + + DffRecordHeader* pHd; + + sal_uInt32 nOldPos = rStCtrl.Tell(); + if ( nFilterOptions & 1 ) + { + pHd = aDocRecManager.GetRecordHeader( PPT_PST_List ); + if ( pHd ) + { + // we try to locate the basic atom + pHd->SeekToContent( rStCtrl ); + if ( SeekToRec( rStCtrl, PPT_PST_VBAInfo, pHd->GetRecEndFilePos(), pHd ) ) + { + if ( SeekToRec( rStCtrl, PPT_PST_VBAInfoAtom, pHd->GetRecEndFilePos(), pHd ) ) + { + sal_uInt32 nPersistPtr, nIDoNotKnow1, nIDoNotKnow2; + rStCtrl.ReadUInt32( nPersistPtr ) + .ReadUInt32( nIDoNotKnow1 ) + .ReadUInt32( nIDoNotKnow2 ); + + sal_uInt32 nOleId; + std::unique_ptr pBas = ImportExOleObjStg( nPersistPtr, nOleId ); + if ( pBas ) + { + tools::SvRef xSource( new SotStorage( pBas.release(), true ) ); + tools::SvRef xDest( new SotStorage( new SvMemoryStream(), true ) ); + if ( xSource.is() && xDest.is() ) + { + // is this a visual basic storage ? + tools::SvRef xSubStorage = xSource->OpenSotStorage( "VBA", + StreamMode::READWRITE | StreamMode::NOCREATE | StreamMode::SHARE_DENYALL ); + if( xSubStorage.is() && ( ERRCODE_NONE == xSubStorage->GetError() ) ) + { + tools::SvRef xMacros = xDest->OpenSotStorage( "MACROS" ); + if ( xMacros.is() ) + { + SvStorageInfoList aList; + xSource->FillInfoList( &aList ); + SvStorageInfoList::size_type i; + + bool bCopied = true; + for ( i = 0; i < aList.size(); i++ ) // copy all entries + { + const SvStorageInfo& rInfo = aList[ i ]; + if ( !xSource->CopyTo( rInfo.GetName(), xMacros.get(), rInfo.GetName() ) ) + bCopied = false; + } + if ( i && bCopied ) + { + uno::Reference < embed::XStorage > xDoc( pShell->GetStorage() ); + if ( xDoc.is() ) + { + tools::SvRef xVBA = SotStorage::OpenOLEStorage( xDoc, SvxImportMSVBasic::GetMSBasicStorageName() ); + if ( xVBA.is() && ( xVBA->GetError() == ERRCODE_NONE ) ) + { + tools::SvRef xSubVBA = xVBA->OpenSotStorage( "_MS_VBA_Overhead" ); + if ( xSubVBA.is() && ( xSubVBA->GetError() == ERRCODE_NONE ) ) + { + tools::SvRef xOriginal = xSubVBA->OpenSotStream( "_MS_VBA_Overhead2" ); + if ( xOriginal.is() && ( xOriginal->GetError() == ERRCODE_NONE ) ) + { + if ( nPersistPtr && ( nPersistPtr < m_nPersistPtrCnt ) ) + { + rStCtrl.Seek( m_pPersistPtr[ nPersistPtr ] ); + ReadDffRecordHeader( rStCtrl, *pHd ); + + xOriginal->WriteUInt32( nIDoNotKnow1 ) + .WriteUInt32( nIDoNotKnow2 ); + + sal_uInt32 nToCopy, nBufSize; + nToCopy = pHd->nRecLen; + std::unique_ptr pBuf(new sal_uInt8[ 0x40000 ]); // 256KB Buffer + while ( nToCopy ) + { + nBufSize = ( nToCopy >= 0x40000 ) ? 0x40000 : nToCopy; + rStCtrl.ReadBytes(pBuf.get(), nBufSize); + xOriginal->WriteBytes(pBuf.get(), nBufSize); + nToCopy -= nBufSize; + } + } + } + } + } + xVBA->Commit(); + } + } + } + } + } + } + } + } + } + } + pHd = aDocRecManager.GetRecordHeader( PPT_PST_ExObjList ); + if ( pHd ) + { + DffRecordHeader* pExEmbed = nullptr; + + pHd->SeekToBegOfRecord( rStCtrl ); + DffRecordManager aExObjListManager( rStCtrl ); + sal_uInt16 i, nRecType(PPT_PST_ExEmbed); + + for ( i = 0; i < 2; i++ ) + { + switch ( i ) + { + case 0 : nRecType = PPT_PST_ExEmbed; break; + case 1 : nRecType = PPT_PST_ExControl; break; + } + for ( pExEmbed = aExObjListManager.GetRecordHeader( nRecType ); + pExEmbed; pExEmbed = aExObjListManager.GetRecordHeader( nRecType, SEEK_FROM_CURRENT ) ) + { + pExEmbed->SeekToContent( rStCtrl ); + + DffRecordHeader aExOleAtHd; + if ( SeekToRec( rStCtrl, PPT_PST_ExOleObjAtom, pExEmbed->GetRecEndFilePos(), &aExOleAtHd ) ) + { + PptExOleObjAtom aAt; + ReadPptExOleObjAtom( rStCtrl, aAt ); + + if ( aAt.nPersistPtr && ( aAt.nPersistPtr < m_nPersistPtrCnt ) ) + { + rStCtrl.Seek( m_pPersistPtr[ aAt.nPersistPtr ] ); + DffRecordHeader aHd; + ReadDffRecordHeader( rStCtrl, aHd ); + if ( aHd.nRecType == DFF_PST_ExOleObjStg ) + { + sal_uInt32 nId; + rStCtrl.ReadUInt32( nId ); + aOleObjectList.emplace_back( + aAt.nId, aHd.nFilePos, pShell, nRecType, aAt.nAspect ); + } + } + } + } + } + } + rStCtrl.Seek( nOldPos ); +} + +bool SdrPowerPointImport::ReadFontCollection() +{ + bool bRet = false; + DffRecordHeader* pEnvHd = aDocRecManager.GetRecordHeader( PPT_PST_Environment ); + if ( pEnvHd ) + { + sal_uInt64 nOldFPos = rStCtrl.Tell(); // remember FilePos for restoring it later + pEnvHd->SeekToContent( rStCtrl ); + DffRecordHeader aListHd; + if ( SeekToRec( rStCtrl, PPT_PST_FontCollection, pEnvHd->GetRecEndFilePos(), &aListHd ) ) + { + sal_uInt16 nCount2 = 0; + while ( SeekToRec( rStCtrl, PPT_PST_FontEntityAtom, aListHd.GetRecEndFilePos() ) ) + { + bRet = true; + if (!m_xFonts) + m_xFonts.emplace(); + PptFontEntityAtom aFontAtom; + ReadPptFontEntityAtom( rStCtrl, aFontAtom ); + + vcl::Font aFont; + aFont.SetCharSet( aFontAtom.eCharSet ); + aFont.SetFamilyName( aFontAtom.aName ); + aFont.SetFamily( aFontAtom.eFamily ); + aFont.SetPitch( aFontAtom.ePitch ); + aFont.SetFontHeight( 100 ); + + // following block is necessary, because our old PowerPoint export did not set the + // correct charset + if ( aFontAtom.aName.equalsIgnoreAsciiCase( "Wingdings" ) || + aFontAtom.aName.equalsIgnoreAsciiCase( "Wingdings 2" ) || + aFontAtom.aName.equalsIgnoreAsciiCase( "Wingdings 3" ) || + aFontAtom.aName.equalsIgnoreAsciiCase( "Monotype Sorts" ) || + aFontAtom.aName.equalsIgnoreAsciiCase( "Monotype Sorts 2" ) || + aFontAtom.aName.equalsIgnoreAsciiCase( "Webdings" ) || + aFontAtom.aName.equalsIgnoreAsciiCase( "StarBats" ) || + aFontAtom.aName.equalsIgnoreAsciiCase( "StarMath" ) || + aFontAtom.aName.equalsIgnoreAsciiCase( "ZapfDingbats" ) ) + { + aFontAtom.eCharSet = RTL_TEXTENCODING_SYMBOL; + }; + m_xFonts->insert(m_xFonts->begin() + nCount2++, std::move(aFontAtom)); + } + } + rStCtrl.Seek( nOldFPos ); // restore FilePos + } + return bRet; +} + +PptSlidePersistList* SdrPowerPointImport::GetPageList(PptPageKind ePageKind) const +{ + switch (ePageKind) + { + case PPT_MASTERPAGE: + return m_pMasterPages.get(); + case PPT_SLIDEPAGE: + return m_pSlidePages.get(); + case PPT_NOTEPAGE: + return m_pNotePages.get(); + } + return nullptr; +} + +SdrOutliner* SdrPowerPointImport::GetDrawOutliner( SdrTextObj const * pSdrText ) +{ + if ( !pSdrText ) + return nullptr; + else + return &pSdrText->ImpGetDrawOutliner(); +} + + +SdrObject* SdrPowerPointImport::ReadObjText( PPTTextObj* pTextObj, SdrObject* pSdrObj, SdPageCapsule pPage ) const +{ + SdrTextObj* pText = dynamic_cast( pSdrObj ); + if ( pText ) + { + if ( !ApplyTextObj( pTextObj, pText, pPage, nullptr, nullptr ) ) + pSdrObj = nullptr; + } + return pSdrObj; +} + +SdrObject* SdrPowerPointImport::ApplyTextObj( PPTTextObj* pTextObj, SdrTextObj* pSdrText, SdPageCapsule /*pPage*/, + SfxStyleSheet* pSheet, SfxStyleSheet** ppStyleSheetAry ) const +{ + SdrTextObj* pText = pSdrText; + if ( pTextObj->Count() ) + { + TSS_Type nDestinationInstance = pTextObj->GetDestinationInstance() ; + SdrOutliner& rOutliner = pText->ImpGetDrawOutliner(); + bool bUndoEnabled = rOutliner.IsUndoEnabled(); + rOutliner.EnableUndo(false); + + if ( ( pText->GetObjInventor() == SdrInventor::Default ) && ( pText->GetObjIdentifier() == SdrObjKind::TitleText ) ) // Outliner-Style for Title-Text object?!? (->of DL) + rOutliner.Init( OutlinerMode::TitleObject ); // Outliner reset + + bool bOldUpdateMode = rOutliner.SetUpdateLayout( false ); + if ( pSheet ) + { + if ( rOutliner.GetStyleSheet( 0 ) != pSheet ) + rOutliner.SetStyleSheet( 0, pSheet ); + } + rOutliner.SetVertical( pTextObj->GetVertical() ); + for ( PPTParagraphObj* pPara = pTextObj->First(); pPara; pPara = pTextObj->Next() ) + { + sal_uInt32 nTextSize = pPara->GetTextSize(); + if ( ! ( nTextSize & 0xffff0000 ) ) + { + PPTPortionObj* pPortion; + std::unique_ptr pParaText(new sal_Unicode[ nTextSize ]); + sal_Int32 nCurrentIndex = 0; + for ( pPortion = pPara->First(); pPortion; pPortion = pPara->Next() ) + { + if ( pPortion->mpFieldItem ) + pParaText[ nCurrentIndex++ ] = ' '; + else + { + sal_Int32 nCharacters = pPortion->Count(); + const sal_Unicode* pSource = pPortion->maString.getStr(); + sal_Unicode* pDest = pParaText.get() + nCurrentIndex; + + sal_uInt32 nFont; + pPortion->GetAttrib( PPT_CharAttr_Font, nFont, pTextObj->GetInstance() ); + const PptFontEntityAtom* pFontEnityAtom = GetFontEnityAtom( nFont ); + if ( pFontEnityAtom && ( pFontEnityAtom->eCharSet == RTL_TEXTENCODING_SYMBOL ) ) + { + sal_Unicode nUnicode; + for (sal_Int32 i = 0; i < nCharacters; i++ ) + { + nUnicode = pSource[ i ]; + if ( ! ( nUnicode & 0xff00 ) ) + nUnicode |= 0xf000; + pDest[ i ] = nUnicode; + } + } + else + memcpy( pDest, pSource, nCharacters << 1 ); + nCurrentIndex += nCharacters; + } + } + sal_Int32 nParaIndex = pTextObj->GetCurrentIndex(); + SfxStyleSheet* pS = ppStyleSheetAry ? ppStyleSheetAry[ pPara->mxParaSet->mnDepth ] : pSheet; + + ESelection aSelection( nParaIndex, 0, nParaIndex, 0 ); + rOutliner.Insert( OUString(), nParaIndex, pPara->mxParaSet->mnDepth ); + rOutliner.QuickInsertText( OUString(pParaText.get(), nCurrentIndex), aSelection ); + rOutliner.SetParaAttribs( nParaIndex, rOutliner.GetEmptyItemSet() ); + if ( pS ) + rOutliner.SetStyleSheet( nParaIndex, pS ); + + for ( pPortion = pPara->First(); pPortion; pPortion = pPara->Next() ) + { + SfxItemSet aPortionAttribs( rOutliner.GetEmptyItemSet() ); + std::unique_ptr pFieldItem(pPortion->GetTextField()); + if ( pFieldItem ) + { + rOutliner.QuickInsertField( *pFieldItem, ESelection( nParaIndex, aSelection.nEndPos, nParaIndex, aSelection.nEndPos + 1 ) ); + aSelection.nEndPos++; + } + else + { + const sal_Unicode *pF, *pPtr = pPortion->maString.getStr(); + const sal_Unicode *pMax = pPtr + pPortion->maString.getLength(); + sal_Int32 nLen; + for ( pF = pPtr; pPtr < pMax; pPtr++ ) + { + if ( *pPtr == 0xb ) + { + nLen = pPtr - pF; + if ( nLen ) + aSelection.nEndPos = + sal::static_int_cast< sal_uInt16 >( + aSelection.nEndPos + nLen ); + pF = pPtr + 1; + rOutliner.QuickInsertLineBreak( ESelection( nParaIndex, aSelection.nEndPos, nParaIndex, aSelection.nEndPos + 1 ) ); + aSelection.nEndPos++; + } + } + nLen = pPtr - pF; + if ( nLen ) + aSelection.nEndPos = sal::static_int_cast< sal_uInt16 >( + aSelection.nEndPos + nLen ); + } + pPortion->ApplyTo( aPortionAttribs, const_cast(*this), nDestinationInstance, pTextObj ); + rOutliner.QuickSetAttribs( aPortionAttribs, aSelection ); + aSelection.nStartPos = aSelection.nEndPos; + } + std::optional< sal_Int16 > oStartNumbering; + SfxItemSet aParagraphAttribs( rOutliner.GetEmptyItemSet() ); + pPara->ApplyTo( aParagraphAttribs, oStartNumbering, *this, nDestinationInstance ); + + sal_uInt32 nIsBullet2 = 0; //, nInstance = nDestinationInstance != 0xffffffff ? nDestinationInstance : pTextObj->GetInstance(); + pPara->GetAttrib( PPT_ParaAttr_BulletOn, nIsBullet2, nDestinationInstance ); + if ( !nIsBullet2 ) + aParagraphAttribs.Put( SfxBoolItem( EE_PARA_BULLETSTATE, false ) ); + + if ( !aSelection.nStartPos ) // in PPT empty paragraphs never gets a bullet + { + aParagraphAttribs.Put( SfxBoolItem( EE_PARA_BULLETSTATE, false ) ); + } + aSelection.nStartPos = 0; + rOutliner.QuickSetAttribs( aParagraphAttribs, aSelection ); + } + } + std::optional pNewText = rOutliner.CreateParaObject(); + rOutliner.Clear(); + rOutliner.SetUpdateLayout( bOldUpdateMode ); + rOutliner.EnableUndo(bUndoEnabled); + pText->SetOutlinerParaObject( std::move(pNewText) ); + } + return pText; +} + +bool SdrPowerPointImport::SeekToDocument( DffRecordHeader* pRecHd ) const +{ + bool bRet; + sal_uInt64 nOldFPos = rStCtrl.Tell(); // remember FilePos for restoring it, if the situation should happen + rStCtrl.Seek( m_nDocStreamPos ); + DffRecordHeader aDocHd; + ReadDffRecordHeader( rStCtrl, aDocHd ); + bRet = aDocHd.nRecType == PPT_PST_Document; + if ( bRet ) + { + if ( pRecHd ) + *pRecHd = aDocHd; + else + aDocHd.SeekToBegOfRecord( rStCtrl ); + } + if ( !bRet ) + rStCtrl.Seek( nOldFPos ); // restore FilePos + return bRet; +} + +bool SdrPowerPointImport::SeekToContentOfProgTag( sal_Int32 nVersion, SvStream& rSt, + const DffRecordHeader& rSourceHd, DffRecordHeader& rContentHd ) +{ + bool bRetValue = false; + sal_uInt32 nOldPos = rSt.Tell(); + + DffRecordHeader aProgTagsHd, aProgTagBinaryDataHd; + rSourceHd.SeekToContent( rSt ); + bool bFound = rSourceHd.nRecType == PPT_PST_ProgTags; + if ( !bFound ) + bFound = SeekToRec( rSt, PPT_PST_ProgTags, rSourceHd.GetRecEndFilePos(), &aProgTagsHd ); + if ( bFound ) + { + while( SeekToRec( rSt, PPT_PST_ProgBinaryTag, aProgTagsHd.GetRecEndFilePos(), &aProgTagBinaryDataHd ) ) + { + ReadDffRecordHeader( rSt, rContentHd ); + if ( rContentHd.nRecType == PPT_PST_CString ) + { + sal_uInt16 n = 6; + sal_uInt32 i = rContentHd.nRecLen >> 1; + if ( i > n ) + { + OUString aPre = read_uInt16s_ToOUString(rSt, n); + n = static_cast( i - 6 ); + OUString aSuf = read_uInt16s_ToOUString(rSt, n); + sal_Int32 nV = aSuf.toInt32(); + if ( ( nV == nVersion ) && ( aPre == "___PPT" ) ) + { + if (!rContentHd.SeekToEndOfRecord(rSt)) + { + break; + } + ReadDffRecordHeader( rSt, rContentHd ); + if ( rContentHd.nRecType == PPT_PST_BinaryTagData ) + { + bRetValue = true; + break; + } + } + } + } + if (!aProgTagBinaryDataHd.SeekToEndOfRecord(rSt)) + break; + } + } + if ( !bRetValue ) + rSt.Seek( nOldPos ); + return bRetValue; +} + +sal_uInt32 SdrPowerPointImport::GetCurrentPageId() +{ + PptSlidePersistList* pList = GetPageList( m_eCurrentPageKind ); + if ( pList && m_nCurrentPageNum < pList->size() ) + return (*pList)[ m_nCurrentPageNum ].aPersistAtom.nSlideId; + return 0; +} + +bool SdrPowerPointImport::SeekToCurrentPage( DffRecordHeader* pRecHd ) const +{ + bool bRet = false; + PptSlidePersistList* pList = GetPageList( m_eCurrentPageKind ); + if ( pList && ( m_nCurrentPageNum < pList->size() ) ) + { + sal_uLong nPersist = (*pList)[ m_nCurrentPageNum ].aPersistAtom.nPsrReference; + if ( nPersist > 0 && nPersist < m_nPersistPtrCnt ) + { + sal_uLong nFPos = m_pPersistPtr[ nPersist ]; + if ( nFPos < nStreamLen ) + { + rStCtrl.Seek( nFPos ); + if ( pRecHd ) + ReadDffRecordHeader( rStCtrl, *pRecHd ); + bRet = true; + } + } + } + return bRet; +} + +sal_uInt16 SdrPowerPointImport::GetPageCount( PptPageKind ePageKind ) const +{ + PptSlidePersistList* pList = GetPageList( ePageKind ); + if ( pList ) + return pList->size(); + return 0; +} + +void SdrPowerPointImport::SetPageNum( sal_uInt16 nPageNum, PptPageKind eKind ) +{ + m_eCurrentPageKind = eKind; + m_nCurrentPageNum = nPageNum; + + m_pPPTStyleSheet = nullptr; + + bool bHasMasterPage = true; + sal_uInt16 nMasterIndex = 0; + + if ( eKind == PPT_MASTERPAGE ) + nMasterIndex = nPageNum; + else + { + if ( HasMasterPage( nPageNum, eKind ) ) + nMasterIndex = GetMasterPageIndex( nPageNum, eKind ); + else + bHasMasterPage = false; + } + if ( bHasMasterPage ) + { + PptSlidePersistList* pPageList = GetPageList( PPT_MASTERPAGE ); + if ( pPageList && nMasterIndex < pPageList->size() ) + { + PptSlidePersistEntry* pMasterPersist = &(*pPageList)[ nMasterIndex ]; + if (!pMasterPersist->xStyleSheet && pMasterPersist->aSlideAtom.nMasterId) + { + nMasterIndex = m_pMasterPages->FindPage( pMasterPersist->aSlideAtom.nMasterId ); + if ( nMasterIndex != PPTSLIDEPERSIST_ENTRY_NOTFOUND ) + pMasterPersist = &(*pPageList)[ nMasterIndex ]; + } + m_pPPTStyleSheet = pMasterPersist->xStyleSheet.get(); + } + } + if ( !m_pPPTStyleSheet ) + m_pPPTStyleSheet = m_pDefaultSheet; +} + +Size SdrPowerPointImport::GetPageSize() const +{ + Size aRet( IsNoteOrHandout( m_nCurrentPageNum ) ? aDocAtom.GetNotesPageSize() : aDocAtom.GetSlidesPageSize() ); + Scale( aRet ); + // PPT works with units of 576 dpi in any case. To avoid inaccuracies + // I do round the last decimal digit away. + if ( nMapMul > 2 * nMapDiv ) + { + MapUnit eMap = pSdrModel->GetScaleUnit(); + bool bInch = IsInch( eMap ); + tools::Long nInchMul = 1, nInchDiv = 1; + if ( bInch ) + { // temporarily convert size (for rounding it) from inch to metric units + Fraction aFact(GetMapFactor(eMap,MapUnit::Map100thMM).X()); + nInchMul = aFact.GetNumerator(); + nInchDiv = aFact.GetDenominator(); + aRet.setWidth( BigMulDiv( aRet.Width(), nInchMul, nInchDiv ) ); + aRet.setHeight( BigMulDiv( aRet.Height(), nInchMul, nInchDiv ) ); + } + aRet.AdjustWidth(5 ); aRet.setWidth( aRet.Width() / 10 ); aRet.setWidth( aRet.Width() * 10 ); + aRet.AdjustHeight(5 ); aRet.setHeight( aRet.Height() / 10 ); aRet.setHeight( aRet.Height() * 10 ); + if ( bInch ) + { + aRet.setWidth( BigMulDiv( aRet.Width(), nInchDiv, nInchMul ) ); + aRet.setHeight( BigMulDiv( aRet.Height(), nInchDiv, nInchMul ) ); + } + } + return aRet; +} + +bool SdrPowerPointImport::GetColorFromPalette( sal_uInt16 nNum, Color& rColor ) const +{ + if ( m_nPageColorsNum != m_nCurrentPageNum || m_ePageColorsKind != m_eCurrentPageKind ) + { + sal_uInt16 nSlideFlags = 0; + PptSlidePersistList* pPageList = GetPageList( m_eCurrentPageKind ); + if ( pPageList && ( m_nCurrentPageNum < pPageList->size() ) ) + { + assert( !pPageList->is_null( m_nCurrentPageNum ) ); + const PptSlidePersistEntry& rE = (*pPageList)[ m_nCurrentPageNum ]; + nSlideFlags = rE.aSlideAtom.nFlags; + if ( ! ( nSlideFlags & 2 ) ) + const_cast(this)->m_aPageColors = rE.aColorScheme; + } + if ( nSlideFlags & 2 ) // follow master colorscheme? + { + PptSlidePersistList* pPageList2 = GetPageList( PPT_MASTERPAGE ); + if ( pPageList2 ) + { + PptSlidePersistEntry* pMasterPersist = nullptr; + if ( m_eCurrentPageKind == PPT_MASTERPAGE ) + pMasterPersist = &(*pPageList2)[ m_nCurrentPageNum ]; + else + { + if ( HasMasterPage( m_nCurrentPageNum, m_eCurrentPageKind ) ) + { + sal_uInt16 nMasterNum = GetMasterPageIndex( m_nCurrentPageNum, m_eCurrentPageKind ); + if ( nMasterNum < pPageList2->size() ) + pMasterPersist = &(*pPageList2)[ nMasterNum ]; + } + } + if ( pMasterPersist ) + { + while( (pMasterPersist->aSlideAtom.nFlags & 2) // it is possible that a masterpage + && pMasterPersist->aSlideAtom.nMasterId ) // itself is following a master colorscheme + { + auto nOrigMasterId = pMasterPersist->aSlideAtom.nMasterId; + sal_uInt16 nNextMaster = m_pMasterPages->FindPage(nOrigMasterId); + if (nNextMaster == PPTSLIDEPERSIST_ENTRY_NOTFOUND) + break; + pMasterPersist = &(*pPageList2)[ nNextMaster ]; + if (pMasterPersist->aSlideAtom.nMasterId == nOrigMasterId) + { + SAL_WARN("filter.ms", "loop in atom chain"); + break; + } + } + const_cast(this)->m_aPageColors = pMasterPersist->aColorScheme; + } + } + } + // register current color scheme + const_cast(this)->m_nPageColorsNum = m_nCurrentPageNum; + const_cast(this)->m_ePageColorsKind = m_eCurrentPageKind; + } + rColor = m_aPageColors.GetColor( nNum ); + return true; +} + +bool SdrPowerPointImport::SeekToShape( SvStream& rSt, SvxMSDffClientData* pClientData, sal_uInt32 nId ) const +{ + bool bRet = SvxMSDffManager::SeekToShape( rSt, pClientData, nId ); + if (!bRet && pClientData) + { + ProcessData& rData = *static_cast(pClientData); + PptSlidePersistEntry& rPersistEntry = rData.rPersistEntry; + if ( rPersistEntry.ePageKind == PPT_SLIDEPAGE ) + { + if ( HasMasterPage( m_nCurrentPageNum, m_eCurrentPageKind ) ) + { + sal_uInt16 nMasterNum = GetMasterPageIndex( m_nCurrentPageNum, m_eCurrentPageKind ); + PptSlidePersistList* pPageList = GetPageList( PPT_MASTERPAGE ); + if ( pPageList && ( nMasterNum < pPageList->size() ) ) + { + assert( !pPageList->is_null( nMasterNum ) ); + const PptSlidePersistEntry& rPersist = (*pPageList)[ nMasterNum ]; // get the masterpage's persistentry + if ( rPersist.pPresentationObjects ) + { + sal_uInt32 nCurrent(0); + DffRecordList* pCList = maShapeRecords.pCList; // we got a backup of the current position + if ( pCList ) + nCurrent = pCList->nCurrent; + if ( const_cast(this)->maShapeRecords.SeekToContent( rSt, DFF_msofbtClientData, SEEK_FROM_CURRENT_AND_RESTART ) ) + { + sal_uInt32 nStreamPos = rSt.Tell(); + PPTTextObj aTextObj( rSt, const_cast(*this), rPersistEntry, nullptr ); + if ( aTextObj.Count() || aTextObj.GetOEPlaceHolderAtom() ) + { + sal_uInt32 nShapePos = 0; + switch ( aTextObj.GetInstance() ) + { + case TSS_Type::Title : + nShapePos = rPersist.pPresentationObjects[ int(TSS_Type::PageTitle) ]; + break; + case TSS_Type::PageTitle : + nShapePos = rPersist.pPresentationObjects[ int(TSS_Type::PageTitle) ]; + break; + case TSS_Type::Subtitle : + case TSS_Type::HalfBody : + case TSS_Type::QuarterBody : + case TSS_Type::Body : + nShapePos = rPersist.pPresentationObjects[ int(TSS_Type::Body) ]; + break; + default: break; + } + if ( nShapePos ) + { + rSt.Seek( nShapePos ); + bRet = true; + } + } + if ( !bRet ) + rSt.Seek( nStreamPos ); + } + if ( pCList ) // restoring + pCList->nCurrent = nCurrent; + const_cast(this)->maShapeRecords.pCList = pCList; + } + } + } + } + } + return bRet; +} + +rtl::Reference SdrPowerPointImport::MakeBlankPage( bool bMaster ) const +{ + rtl::Reference pRet = pSdrModel->AllocPage( bMaster ); + pRet->SetSize( GetPageSize() ); + + return pRet; +} + +static void ImportComment10( SvxMSDffManager const & rMan, SvStream& rStCtrl, SdrPage* pPage, DffRecordHeader const & rComment10Hd ) +{ + OUString sAuthor; + OUString sText; + OUString sInitials; + + sal_Int32 nIndex = 0; + util::DateTime aDateTime; + sal_Int32 nPosX = 0; + sal_Int32 nPosY = 0; + + + auto nEndRecPos = DffPropSet::SanitizeEndPos(rStCtrl, rComment10Hd.GetRecEndFilePos()); + while ( ( rStCtrl.GetError() == ERRCODE_NONE ) && ( rStCtrl.Tell() < nEndRecPos ) ) + { + DffRecordHeader aCommentHd; + ReadDffRecordHeader( rStCtrl, aCommentHd ); + switch( aCommentHd.nRecType ) + { + case PPT_PST_CString : + { + OUString aString = SvxMSDffManager::MSDFFReadZString( rStCtrl, + aCommentHd.nRecLen, true ); + switch ( aCommentHd.nRecInstance ) + { + case 0 : sAuthor = aString; break; + case 1 : sText = aString; break; + case 2 : sInitials = aString; break; + } + } + break; + + case PPT_PST_CommentAtom10 : + { + sal_uInt16 millisec = 0; + rStCtrl.ReadInt32( nIndex ) + .ReadInt16( aDateTime.Year ) + .ReadUInt16( aDateTime.Month ) + .ReadUInt16( aDateTime.Day ) // DayOfWeek + .ReadUInt16( aDateTime.Day ) + .ReadUInt16( aDateTime.Hours ) + .ReadUInt16( aDateTime.Minutes ) + .ReadUInt16( aDateTime.Seconds ) + .ReadUInt16( millisec ) + .ReadInt32( nPosX ) + .ReadInt32( nPosY ); + + aDateTime.NanoSeconds = millisec * ::tools::Time::nanoPerMilli; + } + break; + } + if (!aCommentHd.SeekToEndOfRecord(rStCtrl)) + break; + } + Point aPosition( nPosX, nPosY ); + rMan.Scale( aPosition ); + + try + { + uno::Reference< office::XAnnotationAccess > xAnnotationAccess( pPage->getUnoPage(), UNO_QUERY_THROW ); + uno::Reference< office::XAnnotation > xAnnotation( xAnnotationAccess->createAndInsertAnnotation() ); + xAnnotation->setPosition( geometry::RealPoint2D( aPosition.X() / 100.0, aPosition.Y() / 100.0 ) ); + xAnnotation->setAuthor( sAuthor ); + xAnnotation->setDateTime( aDateTime ); + xAnnotation->setInitials( sInitials ); + uno::Reference< text::XText > xText( xAnnotation->getTextRange() ); + xText->setString( sText ); + } + catch( const uno::Exception& ) + { + + } +} + + +// be sure not to import masterpages with this method +void SdrPowerPointImport::ImportPage( SdrPage* pRet, const PptSlidePersistEntry* pMasterPersist ) +{ + sal_uInt32 nOldPos = rStCtrl.Tell(); + PptSlidePersistList* pList = GetPageList( m_eCurrentPageKind ); + if ( ( !pList ) || ( pList->size() <= m_nCurrentPageNum ) ) + return; + PptSlidePersistEntry& rSlidePersist = (*pList)[ m_nCurrentPageNum ]; + if ( rSlidePersist.bStarDrawFiller ) + return; + + DffRecordHeader aPageHd; + if ( SeekToCurrentPage( &aPageHd ) ) + { + rSlidePersist.xHeaderFooterEntry.reset(new HeaderFooterEntry(pMasterPersist)); + ProcessData aProcessData( rSlidePersist, SdPageCapsule(pRet) ); + auto nEndRecPos = SanitizeEndPos(rStCtrl, aPageHd.GetRecEndFilePos()); + while ( ( rStCtrl.GetError() == ERRCODE_NONE ) && ( rStCtrl.Tell() < nEndRecPos ) ) + { + DffRecordHeader aHd; + ReadDffRecordHeader( rStCtrl, aHd ); + switch ( aHd.nRecType ) + { + case PPT_PST_HeadersFooters : + { + ImportHeaderFooterContainer(aHd, *rSlidePersist.xHeaderFooterEntry); + } + break; + + case PPT_PST_ProgTags : + { + DffRecordHeader aContentDataHd; + if ( SeekToContentOfProgTag( 10, rStCtrl, aHd, aContentDataHd ) ) + { + DffRecordHeader aComment10Hd; + while( ( rStCtrl.GetError() == ERRCODE_NONE ) && SeekToRec( rStCtrl, PPT_PST_Comment10, aContentDataHd.GetRecEndFilePos(), &aComment10Hd ) ) + { + ImportComment10( *this, rStCtrl, pRet, aComment10Hd ); + if (!aComment10Hd.SeekToEndOfRecord(rStCtrl)) + break; + } + } + } + break; + + case PPT_PST_PPDrawing : + { + DffRecordHeader aPPDrawHd; + if ( SeekToRec( rStCtrl, DFF_msofbtDgContainer, aHd.GetRecEndFilePos(), &aPPDrawHd ) ) + { + sal_uInt32 nPPDrawOfs = rStCtrl.Tell(); + + // importing the background object before importing the page + auto nPPEndRecPos = SanitizeEndPos(rStCtrl, aPPDrawHd.GetRecEndFilePos()); + while ( ( rStCtrl.GetError() == ERRCODE_NONE ) && ( rStCtrl.Tell() < nPPEndRecPos ) ) + { + DffRecordHeader aEscherObjListHd; + ReadDffRecordHeader( rStCtrl, aEscherObjListHd ); + switch ( aEscherObjListHd.nRecType ) + { + case DFF_msofbtSpContainer : + { + tools::Rectangle aPageSize( Point(), pRet->GetSize() ); + if ( rSlidePersist.aSlideAtom.nFlags & 4 ) // follow master background? + { + if ( HasMasterPage( m_nCurrentPageNum, m_eCurrentPageKind ) ) + { + sal_uInt16 nMasterNum = GetMasterPageIndex( m_nCurrentPageNum, m_eCurrentPageKind ); + PptSlidePersistList* pPageList = GetPageList( PPT_MASTERPAGE ); + PptSlidePersistEntry* pE = &(*pPageList)[ nMasterNum ]; + while( ( pE->aSlideAtom.nFlags & 4 ) && pE->aSlideAtom.nMasterId ) + { + auto nOrigMasterId = pE->aSlideAtom.nMasterId; + sal_uInt16 nNextMaster = m_pMasterPages->FindPage(nOrigMasterId); + if ( nNextMaster == PPTSLIDEPERSIST_ENTRY_NOTFOUND ) + break; + else + pE = &(*pPageList)[ nNextMaster ]; + if (pE->aSlideAtom.nMasterId == nOrigMasterId) + { + SAL_WARN("filter.ms", "loop in atom chain"); + break; + } + } + if ( pE->nBackgroundOffset ) + { + // do not follow master colorscheme? + sal_uInt32 nPos = rStCtrl.Tell(); + rStCtrl.Seek( pE->nBackgroundOffset ); + rSlidePersist.pBObj = ImportObj( rStCtrl, aProcessData, aPageSize, aPageSize, /*nCalledByGroup*/0, /*pShapeId*/nullptr ); + rStCtrl.Seek( nPos ); + } + } + } + else + { + DffRecordHeader aShapeHd; + ReadDffRecordHeader( rStCtrl, aShapeHd ); + if ( aShapeHd.nRecType == DFF_msofbtSp ) + { + sal_uInt32 nSpFlags; + rStCtrl.ReadUInt32( nSpFlags ).ReadUInt32( nSpFlags ); + if (rStCtrl.good() && ShapeFlag(nSpFlags) & ShapeFlag::Background) + { + aEscherObjListHd.SeekToBegOfRecord( rStCtrl ); + rSlidePersist.pBObj = ImportObj( rStCtrl, aProcessData, aPageSize, aPageSize, /*nCalledByGroup*/0, /*pShapeId*/nullptr ); + } + } + } + } + break; + } + if ( aEscherObjListHd.nRecType == DFF_msofbtSpContainer ) + break; + if (!aEscherObjListHd.SeekToEndOfRecord(rStCtrl)) + break; + } + + // now importing page + rStCtrl.Seek( nPPDrawOfs ); + auto nHdEndRecPos = SanitizeEndPos(rStCtrl, aPPDrawHd.GetRecEndFilePos()); + while ( ( rStCtrl.GetError() == ERRCODE_NONE ) && ( rStCtrl.Tell() < nHdEndRecPos ) ) + { + DffRecordHeader aEscherObjListHd; + ReadDffRecordHeader( rStCtrl, aEscherObjListHd ); + switch ( aEscherObjListHd.nRecType ) + { + case DFF_msofbtSpgrContainer : + { + DffRecordHeader aShapeHd; + if ( SeekToRec( rStCtrl, DFF_msofbtSpContainer, aEscherObjListHd.GetRecEndFilePos(), &aShapeHd ) ) + { + if (!aShapeHd.SeekToEndOfRecord(rStCtrl)) + { + break; + } + auto nListEndRecPos = SanitizeEndPos(rStCtrl, aEscherObjListHd.GetRecEndFilePos()); + while ( ( rStCtrl.GetError() == ERRCODE_NONE ) && ( rStCtrl.Tell() < nListEndRecPos ) ) + { + ReadDffRecordHeader( rStCtrl, aShapeHd ); + if ( ( aShapeHd.nRecType == DFF_msofbtSpContainer ) || ( aShapeHd.nRecType == DFF_msofbtSpgrContainer ) ) + { + tools::Rectangle aEmpty; + aShapeHd.SeekToBegOfRecord( rStCtrl ); + sal_Int32 nShapeId; + aProcessData.pTableRowProperties.reset(); + SdrObject* pObj = ImportObj( rStCtrl, aProcessData, aEmpty, aEmpty, 0, &nShapeId ); + if ( pObj ) + { + if ( aProcessData.pTableRowProperties ) + pObj = CreateTable(pObj, aProcessData.pTableRowProperties.get(), aProcessData.rPersistEntry.xSolverContainer.get(), aProcessData.aBackgroundColoredObjects); + + pRet->NbcInsertObject( pObj ); + + if( nShapeId ) + insertShapeId( nShapeId, pObj ); + } + } + bool bSuccess = aShapeHd.SeekToEndOfRecord(rStCtrl); + if (!bSuccess) + break; + } + } + } + break; + } + if ( aEscherObjListHd.nRecType == DFF_msofbtSpgrContainer ) + break; + if (!aEscherObjListHd.SeekToEndOfRecord(rStCtrl)) + break; + } + + // Handle shapes where the fill matches the background + // fill (mso_fillBackground). + if (rSlidePersist.ePageKind == PPT_SLIDEPAGE) + { + if (!aProcessData.aBackgroundColoredObjects.empty()) + { + if (!rSlidePersist.pBObj) + { + for (auto pObject : aProcessData.aBackgroundColoredObjects) + { + // The shape wants a background, but the slide doesn't have + // one: default to white. + SfxItemSet aNewSet(*pObject->GetMergedItemSet().GetPool()); + aNewSet.Put(XFillStyleItem(css::drawing::FillStyle_SOLID)); + aNewSet.Put(XFillColorItem(OUString(), COL_WHITE)); + pObject->SetMergedItemSet(aNewSet); + } + } + } + } + + if ( rSlidePersist.pBObj ) + { + // #i99386# transfer the attributes from the temporary BackgroundObject + // to the Page and delete it. + pRet->getSdrPageProperties().ClearItem(); + pRet->getSdrPageProperties().PutItemSet(rSlidePersist.pBObj->GetMergedItemSet()); + if (rSlidePersist.xSolverContainer) + { + for (auto & pPtr : rSlidePersist.xSolverContainer->aCList) + { + // check connections to the group object + if (pPtr->pAObj == rSlidePersist.pBObj) + pPtr->pAObj = nullptr; + if (pPtr->pBObj == rSlidePersist.pBObj) + pPtr->pBObj = nullptr; + if (pPtr->pCObj == rSlidePersist.pBObj) + pPtr->pCObj = nullptr; + } + } + SdrObject::Free(rSlidePersist.pBObj); + } + } + } + break; + } + if (!aHd.SeekToEndOfRecord(rStCtrl)) + break; + } + if (rSlidePersist.xSolverContainer) + SolveSolver(*rSlidePersist.xSolverContainer); + } + rStCtrl.Seek( nOldPos ); +} + +const PptSlideLayoutAtom* SdrPowerPointImport::GetSlideLayoutAtom() const +{ + PptSlidePersistList* pPageList = GetPageList( m_eCurrentPageKind ); + if ( pPageList && m_nCurrentPageNum < pPageList->size() ) + { + assert( !pPageList->is_null( m_nCurrentPageNum ) ); + return &(*pPageList)[ m_nCurrentPageNum ].aSlideAtom.aLayout; + } + return nullptr; +} + +bool SdrPowerPointImport::IsNoteOrHandout( sal_uInt16 nPageNum ) const +{ + bool bNote = m_eCurrentPageKind == PPT_NOTEPAGE; + if ( m_eCurrentPageKind == PPT_MASTERPAGE ) + bNote = ( nPageNum & 1 ) == 0; + return bNote; +} + +sal_uInt32 SdrPowerPointImport::GetMasterPageId( sal_uInt16 nPageNum, PptPageKind ePageKind ) const +{ + PptSlidePersistList* pPageList = GetPageList( ePageKind ); + if ( pPageList && nPageNum < pPageList->size() ) + return (*pPageList)[ nPageNum ].aSlideAtom.nMasterId; + return 0; +} + +sal_uInt32 SdrPowerPointImport::GetNotesPageId( sal_uInt16 nPageNum ) const +{ + PptSlidePersistList* pPageList=GetPageList( PPT_SLIDEPAGE ); + if ( pPageList && nPageNum < pPageList->size() ) + return (*pPageList)[ nPageNum ].aSlideAtom.nNotesId; + return 0; +} + +bool SdrPowerPointImport::HasMasterPage( sal_uInt16 nPageNum, PptPageKind ePageKind ) const +{ + if ( ePageKind == PPT_NOTEPAGE ) + return aDocAtom.nNotesMasterPersist != 0; + if ( ePageKind == PPT_MASTERPAGE ) + return false; + return GetMasterPageId( nPageNum, ePageKind ) != 0; +} + +sal_uInt16 SdrPowerPointImport::GetMasterPageIndex( sal_uInt16 nPageNum, PptPageKind ePageKind ) const +{ + sal_uInt16 nIdx = 0; + if ( ePageKind == PPT_NOTEPAGE ) + return 2; + sal_uInt32 nId = GetMasterPageId( nPageNum, ePageKind ); + if (nId && m_pMasterPages) + { + nIdx = m_pMasterPages->FindPage( nId ); + if ( nIdx == PPTSLIDEPERSIST_ENTRY_NOTFOUND ) + nIdx = 0; + } + return nIdx; +} + +SdrObject* SdrPowerPointImport::ImportPageBackgroundObject( const SdrPage& rPage, sal_uInt32& nBgFileOffset ) +{ + SdrObject* pRet = nullptr; + std::optional pSet; + sal_uInt64 nOldFPos = rStCtrl.Tell(); // remember FilePos for restoring it later + DffRecordHeader aPageHd; + if ( SeekToCurrentPage( &aPageHd ) ) + { // and now search for the background attributes of the Page + sal_uLong nPageRecEnd = aPageHd.GetRecEndFilePos(); + DffRecordHeader aPPDrawHd; + if ( SeekToRec( rStCtrl, PPT_PST_PPDrawing, nPageRecEnd, &aPPDrawHd ) ) + { + sal_uLong nPPDrawEnd = aPPDrawHd.GetRecEndFilePos(); + DffRecordHeader aEscherF002Hd; + if ( SeekToRec( rStCtrl, DFF_msofbtDgContainer, nPPDrawEnd, &aEscherF002Hd ) ) + { + sal_uLong nEscherF002End = aEscherF002Hd.GetRecEndFilePos(); + DffRecordHeader aEscherObjectHd; + if ( SeekToRec( rStCtrl, DFF_msofbtSpContainer, nEscherF002End, &aEscherObjectHd ) ) + { + nBgFileOffset = aEscherObjectHd.GetRecBegFilePos(); + //sal_uLong nEscherObjectEnd = aEscherObjectHd.GetRecEndFilePos(); + //DffRecordHeader aEscherPropertiesHd; + if ( SeekToRec( rStCtrl, DFF_msofbtOPT,nEscherF002End ) ) + { + ReadDffPropSet( rStCtrl, static_cast(*this) ); + mnFix16Angle = Fix16ToAngle( GetPropertyValue( DFF_Prop_Rotation, 0 ) ); + sal_uInt32 nColor = GetPropertyValue( DFF_Prop_fillColor, 0xffffff ); + pSet.emplace( pSdrModel->GetItemPool() ); + DffObjData aObjData( aEscherObjectHd, tools::Rectangle( 0, 0, 28000, 21000 ), 0 ); + ApplyAttributes( rStCtrl, *pSet, aObjData ); + Color aColor( MSO_CLR_ToColor( nColor ) ); + pSet->Put( XFillColorItem( OUString(), aColor ) ); + } + } + } + } + } + rStCtrl.Seek( nOldFPos ); // restore FilePos + if ( !pSet ) + { + pSet.emplace( pSdrModel->GetItemPool() ); + pSet->Put( XFillStyleItem( drawing::FillStyle_NONE ) ); + } + pSet->Put( XLineStyleItem( drawing::LineStyle_NONE ) ); + tools::Rectangle aRect( + rPage.GetLeftBorder(), + rPage.GetUpperBorder(), + rPage.GetWidth() - rPage.GetRightBorder(), + rPage.GetHeight() - rPage.GetLowerBorder()); + + pRet = new SdrRectObj( + *pSdrModel, + aRect); + + pRet->SetMergedItemSet(*pSet); + pRet->SetMarkProtect( true ); + pRet->SetMoveProtect( true ); + pRet->SetResizeProtect( true ); + return pRet; +} + +HeaderFooterEntry::HeaderFooterEntry( const PptSlidePersistEntry* pMPE ) : + pMasterPersist ( pMPE ), + nAtom ( 0 ) +{ + if ( pMPE ) + { + HeaderFooterEntry* pMHFE = pMPE->xHeaderFooterEntry.get(); + if ( pMHFE ) + { + nAtom = pMPE->xHeaderFooterEntry->nAtom; + pPlaceholder[ 0 ] = pMHFE->pPlaceholder[ 0 ]; + pPlaceholder[ 1 ] = pMHFE->pPlaceholder[ 1 ]; + pPlaceholder[ 2 ] = pMHFE->pPlaceholder[ 2 ]; + pPlaceholder[ 3 ] = pMHFE->pPlaceholder[ 3 ]; + } + } +} + +sal_uInt32 HeaderFooterEntry::IsToDisplay( sal_uInt32 nInstance ) +{ + sal_uInt32 nMask = 0; + switch ( nInstance ) + { + case 0 : nMask = 0x010000; break; + case 1 : nMask = 0x100000; break; + case 2 : nMask = 0x200000; break; + case 3 : nMask = 0x080000; break; + } + return ( nAtom & nMask ); +} + +// The following method checks if the slide is using a different colorscheme than +// its master, if this is the fact, then the HeaderFooter must probably be +// imported as real sdrobject. In this case, the return value is the offset to the +// master header footer object, so it can be re-loaded with a different color set +sal_uInt32 HeaderFooterEntry::NeedToImportInstance( const sal_uInt32 nInstance, const PptSlidePersistEntry& rSlidePersist ) +{ + sal_uInt32 nRet = 0; + if ( pMasterPersist ) + { + if ( !( rSlidePersist.aSlideAtom.nFlags & 2 ) ) + { // not following the master persist, so we have to check if the colors are changed + if ( memcmp( &rSlidePersist.aColorScheme, &pMasterPersist->aColorScheme, 32 ) ) + { + nRet = pMasterPersist->HeaderFooterOfs[ nInstance ]; + } + } + } + return nRet; +} + +void SdrEscherImport::ImportHeaderFooterContainer( DffRecordHeader const & rHd, HeaderFooterEntry& rE ) +{ + rHd.SeekToContent( rStCtrl ); + auto nEndRecPos = SanitizeEndPos(rStCtrl, rHd.GetRecEndFilePos()); + while ( ( rStCtrl.GetError() == ERRCODE_NONE ) && ( rStCtrl.Tell() < nEndRecPos ) ) + { + DffRecordHeader aHd; + ReadDffRecordHeader( rStCtrl, aHd ); + switch ( aHd.nRecType ) + { + case PPT_PST_HeadersFootersAtom : + rStCtrl.ReadUInt32( rE.nAtom ); + break; + + case PPT_PST_CString : + { + if ( aHd.nRecInstance < 4 ) + { + rE.pPlaceholder[ aHd.nRecInstance ] = MSDFFReadZString( rStCtrl, + aHd.nRecLen, true ); + } + } + break; + } + if (!aHd.SeekToEndOfRecord(rStCtrl)) + break; + } +} + +PPTBuGraEntry::PPTBuGraEntry( Graphic aGraphic, sal_uInt32 nInst ) : + nInstance ( nInst ), + aBuGra (std::move( aGraphic )) {} + +PPTExtParaLevel::PPTExtParaLevel() +: mnExtParagraphMask( 0 ) +, mnBuBlip( 0xffff ) +, mnHasAnm( 0 ) +, mnAnmScheme( 0 ) +, mpfPP10Ext( 0 ) +, mnExtCharacterMask( 0 ) +, mcfPP10Ext( 0 ) +, mbSet( false ) +{} + +SvStream& ReadPPTExtParaLevel( SvStream& rIn, PPTExtParaLevel& rLevel ) +{ + rLevel.mbSet = true; + rIn.ReadUInt32( rLevel.mnExtParagraphMask ); + if ( rLevel.mnExtParagraphMask & 0x00800000 ) + rIn.ReadUInt16( rLevel.mnBuBlip ); + if ( rLevel.mnExtParagraphMask & 0x02000000 ) + rIn.ReadUInt16( rLevel.mnHasAnm ); + if ( rLevel.mnExtParagraphMask & 0x01000000 ) + rIn.ReadUInt32( rLevel.mnAnmScheme ); + if ( rLevel.mnExtParagraphMask & 0x04000000 ) + rIn.ReadUInt32( rLevel.mpfPP10Ext ); + rIn.ReadUInt32( rLevel.mnExtCharacterMask ); + if ( rLevel.mnExtCharacterMask & 0x100000 ) + rIn.ReadUInt32( rLevel.mcfPP10Ext ); + return rIn; +} + +bool PPTExtParaProv::GetGraphic( sal_uInt32 nInstance, Graphic& rGraph ) const +{ + bool bRetValue = false; + PPTBuGraEntry* pPtr = nullptr; + if ( nInstance < aBuGraList.size() ) + { + pPtr = aBuGraList[ nInstance ].get(); + if ( pPtr->nInstance == nInstance ) + bRetValue = true; + } + if ( !bRetValue ) + { + for (std::unique_ptr const & i : aBuGraList) + { + pPtr = i.get(); + if ( pPtr->nInstance == nInstance ) + { + bRetValue = true; + break; + } + } + } + if ( bRetValue ) + rGraph = pPtr->aBuGra; + return bRetValue; +} + +PPTExtParaProv::PPTExtParaProv( SdrPowerPointImport& rMan, SvStream& rSt, const DffRecordHeader* pHd ) : + bStyles ( false ) +{ + sal_uInt32 nOldPos = rSt.Tell(); + + // here we have to get the graphical bullets... + + DffRecordHeader aHd; + DffRecordHeader aContentDataHd; + + const DffRecordHeader* pListHd = rMan.aDocRecManager.GetRecordHeader( PPT_PST_List ); + if( pListHd ) + pListHd->SeekToContent( rSt ); + if ( pListHd && SdrPowerPointImport::SeekToContentOfProgTag( 9, rSt, *pListHd, aContentDataHd ) ) + { + auto nEndRecPos = DffPropSet::SanitizeEndPos(rSt, aContentDataHd.GetRecEndFilePos()); + while ( ( rSt.GetError() == ERRCODE_NONE ) && ( rSt.Tell() < nEndRecPos ) ) + { + ReadDffRecordHeader( rSt, aHd ); + switch ( aHd.nRecType ) + { + case PPT_PST_ExtendedBuGraContainer : + { + auto nHdEndRecPos = DffPropSet::SanitizeEndPos(rSt, aHd.GetRecEndFilePos()); + while ( ( rSt.GetError() == ERRCODE_NONE ) && ( rSt.Tell() < nHdEndRecPos ) ) + { + DffRecordHeader aBuGraAtomHd; + ReadDffRecordHeader( rSt, aBuGraAtomHd ); + if ( aBuGraAtomHd.nRecType == PPT_PST_ExtendedBuGraAtom ) + { + sal_uInt16 nType; + rSt.ReadUInt16( nType ); + Graphic aGraphic; + if ( SvxMSDffManager::GetBLIPDirect( rSt, aGraphic ) ) + { + sal_uInt32 nInstance = aBuGraAtomHd.nRecInstance; + PPTBuGraEntry* pBuGra = new PPTBuGraEntry( std::move(aGraphic), nInstance ); + size_t n = 0; + size_t nBuGraCount = aBuGraList.size(); + if ( nBuGraCount ) + { + if ( aBuGraList[ nBuGraCount - 1 ]->nInstance < nInstance ) + n = nBuGraCount; + else + { // maybe the instances are not sorted, we sort it + for ( n = 0; n < nBuGraCount; n++ ) + { // sorting fields ( hi >> lo ) + if ( aBuGraList[ n ]->nInstance < nInstance ) + break; + } + } + } + if ( n < nBuGraCount ) { + aBuGraList.emplace( aBuGraList.begin() + n, pBuGra ); + } else { + aBuGraList.emplace_back( pBuGra ); + } + } +#ifdef DBG_UTIL + else OSL_FAIL( "PPTExParaProv::PPTExParaProv - bullet graphic is not valid (SJ)" ); +#endif + } +#ifdef DBG_UTIL + else OSL_FAIL( "PPTExParaProv::PPTExParaProv - unknown atom interpreting the PPT_PST_ExtendedBuGraContainer (SJ)" ); +#endif + if (!aBuGraAtomHd.SeekToEndOfRecord(rSt)) + break; + } + } + break; + + case PPT_PST_ExtendedPresRuleContainer : + aExtendedPresRules.Consume( rSt, aHd.GetRecEndFilePos() ); + break; +#ifdef DBG_UTIL + default : + OSL_FAIL( "PPTExParaProv::PPTExParaProv - unknown atom reading ppt2000 num rules (SJ)" ); + break; + case PPT_PST_MasterText : // first seen in: ms-tt02.ppt + case PPT_PST_SrKinsoku : + case PPT_PST_TextDefaults9Atom : + case PPT_PST_PresentationAdvisorFlags9Atom : + case PPT_PST_HtmlDocInfo9Atom : + case PPT_PST_GridSpacing10Atom : + case PPT_PST_CommentIndex10 : + case PPT_PST_DocToolbarStates10Atom : + break; +#endif + } + if (!aHd.SeekToEndOfRecord(rSt)) + break; + } + } + + if ( pHd && SdrPowerPointImport::SeekToContentOfProgTag( 9, rSt, *pHd, aContentDataHd ) ) + { // get the extended paragraph styles on mainmaster ( graphical bullets, num ruling ... ) + auto nEndRecPos = DffPropSet::SanitizeEndPos(rSt, aContentDataHd.GetRecEndFilePos()); + while ( ( rSt.GetError() == ERRCODE_NONE ) && ( rSt.Tell() < nEndRecPos ) ) + { + ReadDffRecordHeader( rSt, aHd ); + switch ( aHd.nRecType ) + { + case PPT_PST_ExtendedParagraphMasterAtom : + { + if ( aHd.nRecInstance < PPT_STYLESHEETENTRIES ) + { + sal_uInt16 nDepth = 0, i = 0; + rSt.ReadUInt16(nDepth); + nDepth = std::min(nDepth, nMaxPPTLevels); + auto nHdEndRecPos = DffPropSet::SanitizeEndPos(rSt, aHd.GetRecEndFilePos()); + while ( ( rSt.GetError() == ERRCODE_NONE ) && ( rSt.Tell() < nHdEndRecPos ) && ( i < nDepth ) ) + { + bStyles = true; + ReadPPTExtParaLevel( rSt, aExtParaSheet[ static_cast(aHd.nRecInstance) ].aExtParaLevel[ i++ ] ); + } +#ifdef DBG_UTIL + if ( rSt.Tell() != aHd.GetRecEndFilePos() ) + OSL_FAIL( "PPTExParaProv::PPTExParaProv - error reading PPT_PST_ExtendedParagraphMasterAtom (SJ)" ); +#endif + } +#ifdef DBG_UTIL + else OSL_FAIL( "PPTExParaProv::PPTExParaProv - instance out of range (SJ)" ); +#endif + } + break; + default : + OSL_FAIL( "PPTExParaProv::PPTExParaProv - unknown atom, assuming PPT_PST_ExtendedParagraphMasterAtom (SJ)" ); + break; + case PPT_PST_HashCodeAtom : + case PPT_PST_BuildList : + case PPT_PST_SlideFlags10Atom : + case PPT_PST_SlideTime10Atom : + case 0xf144 : + break; + } + if (!aHd.SeekToEndOfRecord(rSt)) + break; + } + } + rSt.Seek( nOldPos ); +} + +PPTExtParaProv::~PPTExtParaProv() +{ +} + +PPTNumberFormatCreator::PPTNumberFormatCreator( std::unique_ptr pParaProv ) + : nIsBullet(0) + , nBulletChar(0) + , nBulletFont(0) + , nBulletHeight(0) + , nBulletColor(0) + , nTextOfs(0) + , nBulletOfs(0) + , pExtParaProv(std::move(pParaProv)) +{ +} + +PPTNumberFormatCreator::~PPTNumberFormatCreator() +{ +} + +bool PPTNumberFormatCreator::ImplGetExtNumberFormat( SdrPowerPointImport const & rManager, + SvxNumberFormat& rNumberFormat, sal_uInt32 nLevel, TSS_Type nInstance, TSS_Type nDestinationInstance, + std::optional< sal_Int16 >& rStartNumbering, sal_uInt32 nFontHeight, PPTParagraphObj const * pPara ) +{ + bool bHardAttribute = ( nDestinationInstance == TSS_Type::Unknown ); + + sal_uInt32 nBuFlags = 0; + sal_uInt16 nHasAnm = 0; + sal_uInt32 nAnmScheme = 0xFFFF0003; + sal_uInt16 nBuBlip = 0xffff; + + const PPTExtParaProv* pParaProv = pExtParaProv.get(); + if ( !pExtParaProv ) + pParaProv = pPara ? pPara->mrStyleSheet.pExtParaProv.get() + : rManager.m_pPPTStyleSheet->pExtParaProv.get(); + if ( pPara ) + { + nBuFlags = pPara->mxParaSet->mnExtParagraphMask; + if ( nBuFlags ) + { + if ( nBuFlags & 0x00800000 ) + nBuBlip = pPara->mxParaSet->mnBuBlip; + if ( nBuFlags & 0x01000000 ) + nAnmScheme = pPara->mxParaSet->mnAnmScheme; + if ( nBuFlags & 0x02000000 ) + nHasAnm = pPara->mxParaSet->mnHasAnm; + bHardAttribute = true; + } + } + + if ( ( nBuFlags & 0x03800000 ) != 0x03800000 ) // merge style sheet + { + // we have to read the master attributes + if (pParaProv && nLevel < nMaxPPTLevels) + { + if ( pParaProv->bStyles ) + { + const PPTExtParaLevel& rLev = pParaProv->aExtParaSheet[ nInstance ].aExtParaLevel[ nLevel ]; + if ( rLev.mbSet ) + { + sal_uInt32 nMaBuFlags = rLev.mnExtParagraphMask; + + if ( (!( nBuFlags & 0x00800000)) && ( nMaBuFlags & 0x00800000 ) ) + { + if (!( nBuFlags & 0x02000000)) // if there is a BuStart without BuInstance, + nBuBlip = rLev.mnBuBlip; // then there is no graphical Bullet possible + } + if ( (!( nBuFlags & 0x01000000)) && ( nMaBuFlags & 0x01000000 ) ) + nAnmScheme = rLev.mnAnmScheme; + if ( (!( nBuFlags & 0x02000000)) && ( nMaBuFlags & 0x02000000 ) ) + nHasAnm = rLev.mnHasAnm; + nBuFlags |= nMaBuFlags; + } + } + } + } + if ( nBuBlip != 0xffff ) // set graphical bullet + { + Graphic aGraphic; + if ( pParaProv && pParaProv->GetGraphic( nBuBlip, aGraphic ) ) + { + SvxBrushItem aBrush( aGraphic, GPOS_MM, SID_ATTR_BRUSH ); + rNumberFormat.SetGraphicBrush( &aBrush ); + sal_uInt32 nHeight = static_cast( static_cast(nFontHeight) * 0.2540 * nBulletHeight + 0.5 ); + Size aPrefSize( aGraphic.GetPrefSize() ); + sal_uInt32 nWidth; + if (aPrefSize.Height()) + nWidth = ( nHeight * aPrefSize.Width() ) / aPrefSize.Height(); + else + nWidth = 0; + rNumberFormat.SetGraphicSize( Size( nWidth, nHeight ) ); + rNumberFormat.SetNumberingType ( SVX_NUM_BITMAP ); + } + } + else if ( nHasAnm ) + { + switch( static_cast< sal_uInt16 >( nAnmScheme ) ) + { + default : + case 0 : + { + rNumberFormat.SetNumberingType( SVX_NUM_CHARS_LOWER_LETTER ); + rNumberFormat.SetSuffix( "." ); + } + break; + case 1 : + { + rNumberFormat.SetNumberingType( SVX_NUM_CHARS_UPPER_LETTER ); + rNumberFormat.SetSuffix( "." ); + } + break; + case 2 : + { + rNumberFormat.SetNumberingType( SVX_NUM_ARABIC ); + rNumberFormat.SetSuffix( ")" ); + } + break; + case 3 : + { + rNumberFormat.SetNumberingType( SVX_NUM_ARABIC ); + rNumberFormat.SetSuffix( "." ); + } + break; + case 4 : + { + rNumberFormat.SetNumberingType( SVX_NUM_ROMAN_LOWER ); + rNumberFormat.SetSuffix( ")" ); + rNumberFormat.SetPrefix( "(" ); + } + break; + case 5 : + { + rNumberFormat.SetNumberingType( SVX_NUM_ROMAN_LOWER ); + rNumberFormat.SetSuffix( ")" ); + } + break; + case 6 : + { + rNumberFormat.SetNumberingType( SVX_NUM_ROMAN_LOWER ); + rNumberFormat.SetSuffix( "." ); + } + break; + case 7 : + { + rNumberFormat.SetNumberingType( SVX_NUM_ROMAN_UPPER ); + rNumberFormat.SetSuffix( "." ); + } + break; + case 8 : + { + rNumberFormat.SetNumberingType( SVX_NUM_CHARS_LOWER_LETTER ); + rNumberFormat.SetSuffix( ")" ); + rNumberFormat.SetPrefix( "(" ); + } + break; + case 9 : + { + rNumberFormat.SetNumberingType( SVX_NUM_CHARS_LOWER_LETTER ); + rNumberFormat.SetSuffix( ")" ); + } + break; + case 10 : + { + rNumberFormat.SetNumberingType( SVX_NUM_CHARS_UPPER_LETTER ); + rNumberFormat.SetSuffix( ")" ); + rNumberFormat.SetPrefix( "(" ); + } + break; + case 11 : + { + rNumberFormat.SetNumberingType( SVX_NUM_CHARS_UPPER_LETTER ); + rNumberFormat.SetSuffix( ")" ); + } + break; + case 12 : + { + rNumberFormat.SetNumberingType( SVX_NUM_ARABIC ); + rNumberFormat.SetSuffix( ")" ); + rNumberFormat.SetPrefix( "(" ); + } + break; + case 13 : + { + rNumberFormat.SetNumberingType( SVX_NUM_ARABIC ); + } + break; + case 14 : + { + rNumberFormat.SetNumberingType( SVX_NUM_ROMAN_UPPER ); + rNumberFormat.SetSuffix( ")" ); + rNumberFormat.SetPrefix( "(" ); + } + break; + case 15 : + { + rNumberFormat.SetNumberingType( SVX_NUM_ROMAN_UPPER ); + rNumberFormat.SetSuffix( ")" ); + } + break; + case 16: // Simplified Chinese. + { + rNumberFormat.SetNumberingType( SVX_NUM_NUMBER_UPPER_ZH ); + } + break; + case 17: // Simplified Chinese with single-byte period. + { + rNumberFormat.SetNumberingType( SVX_NUM_NUMBER_UPPER_ZH ); + rNumberFormat.SetSuffix( "." ); + } + break; + case 18: // Double byte circle numbers. + case 19: // Wingdings white circle numbers. + case 20: // Wingdings black circle numbers. + { + rNumberFormat.SetNumberingType( SVX_NUM_CIRCLE_NUMBER ); + } + break; + case 21: // Traditional Chinese. + { + rNumberFormat.SetNumberingType( SVX_NUM_NUMBER_UPPER_ZH_TW ); + } + break; + case 22: // Traditional Chinese with single-byte period. + { + rNumberFormat.SetNumberingType( SVX_NUM_NUMBER_UPPER_ZH_TW ); + rNumberFormat.SetSuffix( "." ); + } + break; + case 25: // Bidi Hebrew 2 with ANSI minus symbol. + { + rNumberFormat.SetNumberingType( SVX_NUM_NUMBER_HEBREW ); + rNumberFormat.SetSuffix( "-" ); + } + break; + case 26: // Japanese/Korean. + { + rNumberFormat.SetNumberingType( SVX_NUM_NUMBER_LOWER_ZH ); + } + break; + case 27: // Japanese/Korean with single-byte period. + { + rNumberFormat.SetNumberingType( SVX_NUM_NUMBER_LOWER_ZH ); + rNumberFormat.SetSuffix( "." ); + } + break; + case 28: // Double-byte Arabic numbers. + { + rNumberFormat.SetNumberingType( SVX_NUM_FULL_WIDTH_ARABIC ); + } + break; + case 29: // Double-byte Arabic numbers with double-byte period. + { + rNumberFormat.SetNumberingType( SVX_NUM_FULL_WIDTH_ARABIC ); + rNumberFormat.SetSuffix( OUString( u'\xff0e' ) ); + } + break; + case 38: // Japanese with double-byte period. + { + rNumberFormat.SetNumberingType( SVX_NUM_NUMBER_LOWER_ZH ); // No such type. Instead with Lower Chinese Number + rNumberFormat.SetSuffix( OUString( u'\xff0e' ) ); + } + break; + } + rStartNumbering = std::optional< sal_Int16 >( nAnmScheme >> 16 ); + sal_Int16 nBuStart = *rStartNumbering; + //The Seventh bit of nBuFlags that specifies whether fBulletHasAutoNumber exists, + //and fBulletHasAutoNumber that specifies whether this paragraph has an automatic numbering scheme. + if ( ( nBuFlags & 0x02000000 ) && ( nBuStart != -1 )) + { + rNumberFormat.SetStart( static_cast(nBuStart) ); + } + } + return bHardAttribute; +} + +void PPTNumberFormatCreator::GetNumberFormat( SdrPowerPointImport const & rManager, SvxNumberFormat& rNumberFormat, sal_uInt32 nLevel, const PPTParaLevel& rParaLevel, const PPTCharLevel& rCharLevel, TSS_Type nInstance ) +{ + nIsBullet = ( rParaLevel.mnBuFlags & ( 1 << PPT_ParaAttr_BulletOn ) ) != 0 ? 1 : 0; + nBulletChar = rParaLevel.mnBulletChar; + + bool bBuHardFont; + bBuHardFont = ( rParaLevel.mnBuFlags & ( 1 << PPT_ParaAttr_BuHardFont ) ) != 0; + if ( bBuHardFont ) + nBulletFont = rParaLevel.mnBulletFont; + else + nBulletFont = rCharLevel.mnFont; + nBulletHeight = rParaLevel.mnBulletHeight; + nBulletColor = rParaLevel.mnBulletColor; + nTextOfs = rParaLevel.mnTextOfs; + nBulletOfs = rParaLevel.mnBulletOfs; + + std::optional< sal_Int16 > oStartNumbering; + ImplGetExtNumberFormat( rManager, rNumberFormat, nLevel, nInstance, TSS_Type::Unknown, oStartNumbering, rCharLevel.mnFontHeight, nullptr ); + if ( ( rNumberFormat.GetNumberingType() != SVX_NUM_BITMAP ) && ( nBulletHeight > 0x7fff ) ) + nBulletHeight = rCharLevel.mnFontHeight ? ((- static_cast(nBulletHeight)) * 100 ) / rCharLevel.mnFontHeight : 100; + ImplGetNumberFormat( rManager, rNumberFormat ); + switch ( rNumberFormat.GetNumberingType() ) + { + case SVX_NUM_CHARS_UPPER_LETTER : + case SVX_NUM_CHARS_LOWER_LETTER : + case SVX_NUM_ROMAN_UPPER : + case SVX_NUM_ROMAN_LOWER : + case SVX_NUM_ARABIC : + case SVX_NUM_CHARS_UPPER_LETTER_N : + case SVX_NUM_CHARS_LOWER_LETTER_N : + { + sal_uInt32 nFont = rCharLevel.mnFont; + const PptFontEntityAtom* pFontEnityAtom = rManager.GetFontEnityAtom( nFont ); + if ( pFontEnityAtom ) + { + vcl::Font aFont; + aFont.SetCharSet( pFontEnityAtom->eCharSet ); + aFont.SetFamilyName( pFontEnityAtom->aName ); + aFont.SetFamily( pFontEnityAtom->eFamily ); + aFont.SetPitch( pFontEnityAtom->ePitch ); + rNumberFormat.SetBulletFont( &aFont ); + } + } + break; + default: break; + } +} + +bool PPTNumberFormatCreator::GetNumberFormat( SdrPowerPointImport const & rManager, SvxNumberFormat& rNumberFormat, PPTParagraphObj* pParaObj, + TSS_Type nDestinationInstance, std::optional< sal_Int16 >& rStartNumbering ) +{ + sal_uInt32 nHardCount = 0; + nHardCount += pParaObj->GetAttrib( PPT_ParaAttr_BulletOn, nIsBullet, nDestinationInstance ) ? 1 : 0; + nHardCount += pParaObj->GetAttrib( PPT_ParaAttr_BulletChar, nBulletChar, nDestinationInstance ) ? 1 : 0; + nHardCount += pParaObj->GetAttrib( PPT_ParaAttr_BulletFont, nBulletFont, nDestinationInstance ) ? 1 : 0; + nHardCount += pParaObj->GetAttrib( PPT_ParaAttr_BulletHeight, nBulletHeight, nDestinationInstance ) ? 1 : 0; + nHardCount += pParaObj->GetAttrib( PPT_ParaAttr_BulletColor, nBulletColor, nDestinationInstance ) ? 1 : 0; + nHardCount += pParaObj->GetAttrib( PPT_ParaAttr_TextOfs, nTextOfs, nDestinationInstance ) ? 1 : 0; + nHardCount += pParaObj->GetAttrib( PPT_ParaAttr_BulletOfs, nBulletOfs, nDestinationInstance ) ? 1 : 0; + + if ( nIsBullet ) + rNumberFormat.SetNumberingType( SVX_NUM_CHAR_SPECIAL ); + + sal_uInt32 nFontHeight = 24; + PPTPortionObj* pPtr = pParaObj->First(); + if ( pPtr ) + pPtr->GetAttrib( PPT_CharAttr_FontHeight, nFontHeight, nDestinationInstance ); + if ( nIsBullet ) + nHardCount += ImplGetExtNumberFormat( rManager, rNumberFormat, pParaObj->mxParaSet->mnDepth, + pParaObj->mnInstance, nDestinationInstance, rStartNumbering, nFontHeight, pParaObj ) ? 1 : 0; + + if ( rNumberFormat.GetNumberingType() != SVX_NUM_BITMAP ) + pParaObj->UpdateBulletRelSize( nBulletHeight ); + if ( nHardCount ) + { + ImplGetNumberFormat( rManager, rNumberFormat ); + switch ( rNumberFormat.GetNumberingType() ) + { + case SVX_NUM_CHARS_UPPER_LETTER : + case SVX_NUM_CHARS_LOWER_LETTER : + case SVX_NUM_ROMAN_UPPER : + case SVX_NUM_ROMAN_LOWER : + case SVX_NUM_ARABIC : + case SVX_NUM_CHARS_UPPER_LETTER_N : + case SVX_NUM_CHARS_LOWER_LETTER_N : + { + if ( pPtr ) + { + sal_uInt32 nFont; + pPtr->GetAttrib( PPT_CharAttr_Font, nFont, nDestinationInstance ); + const PptFontEntityAtom* pFontEnityAtom = rManager.GetFontEnityAtom( nFont ); + if ( pFontEnityAtom ) + { + vcl::Font aFont; + aFont.SetCharSet( pFontEnityAtom->eCharSet ); + aFont.SetFamilyName( pFontEnityAtom->aName ); + aFont.SetFamily( pFontEnityAtom->eFamily ); + aFont.SetPitch( pFontEnityAtom->ePitch ); + rNumberFormat.SetBulletFont( &aFont ); + } + } + } + break; + default: break; + } + } + return nHardCount != 0; +} + +void PPTNumberFormatCreator::ImplGetNumberFormat( SdrPowerPointImport const & rManager, SvxNumberFormat& rNumberFormat ) +{ + vcl::Font aFont; + const PptFontEntityAtom* pAtom = rManager.GetFontEnityAtom( nBulletFont ); + if ( pAtom ) + { + rtl_TextEncoding eCharSet( pAtom->eCharSet ); + aFont.SetFamilyName( pAtom->aName ); + aFont.SetCharSet( eCharSet ); + aFont.SetFamily( pAtom->eFamily ); + aFont.SetPitch( pAtom->ePitch ); + } + Color aCol( rManager.MSO_TEXT_CLR_ToColor( nBulletColor ) ); + aFont.SetColor( aCol ); + + sal_uInt16 nBuChar = static_cast(nBulletChar); + if ( aFont.GetCharSet() == RTL_TEXTENCODING_SYMBOL ) + { + nBuChar &= 0x00ff; + nBuChar |= 0xf000; + } + rNumberFormat.SetBulletFont( &aFont ); + rNumberFormat.SetBulletChar( nBuChar ); + rNumberFormat.SetBulletRelSize( static_cast(nBulletHeight) ); + rNumberFormat.SetBulletColor( aCol ); + sal_uInt32 nAbsLSpace = convertMasterUnitToMm100(nTextOfs); + sal_uInt32 nFirstLineOffset = nAbsLSpace - convertMasterUnitToMm100(nBulletOfs); + rNumberFormat.SetAbsLSpace( nAbsLSpace ); + rNumberFormat.SetFirstLineOffset( -static_cast(nFirstLineOffset) ); +} + +PPTCharSheet::PPTCharSheet( TSS_Type nInstance ) +{ + sal_uInt32 nColor = PPT_COLSCHEME_TEXT_UND_ZEILEN; + sal_uInt16 nFontHeight(0); + switch ( nInstance ) + { + case TSS_Type::PageTitle : + case TSS_Type::Title : + { + nColor = PPT_COLSCHEME_TITELTEXT; + nFontHeight = 44; + } + break; + case TSS_Type::Body : + case TSS_Type::Subtitle : + case TSS_Type::HalfBody : + case TSS_Type::QuarterBody : + nFontHeight = 32; + break; + case TSS_Type::Notes : + nFontHeight = 12; + break; + case TSS_Type::Unused : + case TSS_Type::TextInShape : + nFontHeight = 24; + break; + default: break; + } + for (PPTCharLevel & nDepth : maCharLevel) + { + nDepth.mnFlags = 0; + nDepth.mnFont = 0; + nDepth.mnAsianOrComplexFont = 0xffff; + nDepth.mnFontHeight = nFontHeight; + nDepth.mnFontColor = nColor; + nDepth.mnFontColorInStyleSheet = Color( static_cast(nColor), static_cast( nColor >> 8 ), static_cast( nColor >> 16 ) ); + nDepth.mnEscapement = 0; + } +} + +void PPTCharSheet::Read( SvStream& rIn, sal_uInt32 nLevel) +{ + // character attributes + sal_uInt32 nCMask(0); + sal_uInt16 nVal16; + rIn.ReadUInt32(nCMask); + + if ( nCMask & 0x0000FFFF ) + { + sal_uInt16 nBitAttr(0); + maCharLevel[ nLevel ].mnFlags &= ~static_cast(nCMask); + rIn.ReadUInt16( nBitAttr ); // Bit attributes (bold, underlined, ...) + maCharLevel[ nLevel ].mnFlags |= nBitAttr; + } + if ( nCMask & ( 1 << PPT_CharAttr_Font ) ) // 0x00010000 + rIn.ReadUInt16( maCharLevel[ nLevel ].mnFont ); + if ( nCMask & ( 1 << PPT_CharAttr_AsianOrComplexFont ) ) // 0x00200000 + rIn.ReadUInt16( maCharLevel[ nLevel ].mnAsianOrComplexFont ); + if ( nCMask & ( 1 << PPT_CharAttr_ANSITypeface ) ) // 0x00400000 + rIn.ReadUInt16( nVal16 ); + if ( nCMask & ( 1 << PPT_CharAttr_Symbol ) ) // 0x00800000 + rIn.ReadUInt16( nVal16 ); + if ( nCMask & ( 1 << PPT_CharAttr_FontHeight ) ) // 0x00020000 + rIn.ReadUInt16( maCharLevel[ nLevel ].mnFontHeight ); + if ( nCMask & ( 1 << PPT_CharAttr_FontColor ) ) // 0x00040000 + { + rIn.ReadUInt32( maCharLevel[ nLevel ].mnFontColor ); + if( ! (maCharLevel[ nLevel ].mnFontColor & 0xff000000 ) ) + maCharLevel[ nLevel ].mnFontColor = PPT_COLSCHEME_HINTERGRUND; + } + if ( nCMask & ( 1 << PPT_CharAttr_Escapement ) ) // 0x00080000 + rIn.ReadUInt16( maCharLevel[ nLevel ].mnEscapement ); + if ( nCMask & 0x00100000 ) // 0x00100000 + rIn.ReadUInt16( nVal16 ); + + nCMask >>= 24; + while( nCMask ) + { + if ( nCMask & 1 ) + { + OSL_FAIL( "PPTCharSheet::Read - unknown attribute, send me this document (SJ)" ); + rIn.ReadUInt16( nVal16 ); + } + nCMask >>= 1; + } +} + +PPTParaSheet::PPTParaSheet( TSS_Type nInstance ) +{ + sal_uInt16 nBuFlags = 0; + sal_uInt32 nBulletColor = 0x8000000; + sal_uInt16 nUpperDist = 0; + + switch ( nInstance ) + { + case TSS_Type::PageTitle : + case TSS_Type::Title : + nBulletColor = PPT_COLSCHEME_TITELTEXT; + break; + case TSS_Type::Body : + case TSS_Type::Subtitle : + case TSS_Type::HalfBody : + case TSS_Type::QuarterBody : + { + nBuFlags = 1; + nUpperDist = 0x14; + } + break; + case TSS_Type::Notes : + nUpperDist = 0x1e; + break; + default: break; + } + for (PPTParaLevel & i : maParaLevel) + { + i.mnBuFlags = nBuFlags; + i.mnBulletChar = 0x2022; + i.mnBulletFont = 0; + i.mnBulletHeight = 100; + i.mnBulletColor = nBulletColor; + i.mnAdjust = 0; + i.mnLineFeed = 100; + i.mnLowerDist = 0; + i.mnUpperDist = nUpperDist; + i.mnTextOfs = 0; + i.mnBulletOfs = 0; + i.mnDefaultTab = 0x240; + i.mnAsianLineBreak = 0; + i.mnBiDi = 0; + } +} + +void PPTParaSheet::Read( SdrPowerPointImport const & +#ifdef DBG_UTIL + rManager +#endif + , SvStream& rIn + , sal_uInt32 nLevel, bool bFirst ) +{ + // paragraph attributes + sal_uInt32 nPMask(0); + rIn.ReadUInt32(nPMask); + + sal_uInt16 nMask16 = static_cast(nPMask) & 0xf; + if ( nMask16 ) + { + sal_uInt16 nVal16(0); + rIn.ReadUInt16( nVal16 ); + maParaLevel[ nLevel ].mnBuFlags &=~ nMask16; + nVal16 &= nMask16; + maParaLevel[ nLevel ].mnBuFlags |= nVal16; + } + if ( nPMask & 0x0080 ) + rIn.ReadUInt16( maParaLevel[ nLevel ].mnBulletChar ); + if ( nPMask & 0x0010 ) + rIn.ReadUInt16( maParaLevel[ nLevel ].mnBulletFont ); + if ( nPMask & 0x0040 ) + { + sal_uInt16 nVal16(0); + rIn.ReadUInt16( nVal16 ); + maParaLevel[ nLevel ].mnBulletHeight = nVal16; + } + if ( nPMask & 0x0020 ) + { + sal_uInt32 nVal32(0); + rIn.ReadUInt32(nVal32); + maParaLevel[ nLevel ].mnBulletColor = nVal32; + } + if ( bFirst ) + { + if ( nPMask & 0xF00 ) + { + // AbsJust! + sal_uInt16 nVal16(0); + rIn.ReadUInt16( nVal16 ); + maParaLevel[ nLevel ].mnAdjust = nVal16 & 3; + } + if ( nPMask & 0x1000 ) + rIn.ReadUInt16( maParaLevel[ nLevel ].mnLineFeed ); + if ( nPMask & 0x2000 ) + rIn.ReadUInt16( maParaLevel[ nLevel ].mnUpperDist ); + if ( nPMask & 0x4000 ) + rIn.ReadUInt16( maParaLevel[ nLevel ].mnLowerDist ); + if ( nPMask & 0x8000 ) + rIn.ReadUInt16( maParaLevel[ nLevel ].mnTextOfs ); + if ( nPMask & 0x10000 ) + rIn.ReadUInt16( maParaLevel[ nLevel ].mnBulletOfs ); + if ( nPMask & 0x20000 ) + rIn.ReadUInt16( maParaLevel[ nLevel ].mnDefaultTab ); + if ( nPMask & 0x200000 ) + { + sal_uInt16 nVal16; + sal_uInt32 nVal32; + // number of tabulators + rIn.ReadUInt16( nVal16 ); + if (!rIn.good() || rIn.remainingSize() / sizeof(nVal32) < nVal16) + return; + for (sal_uInt16 i = 0; i < nVal16; ++i) + rIn.ReadUInt32( nVal32 ); // reading the tabulators + } + if ( nPMask & 0x40000 ) + { + sal_uInt16 nVal16; + rIn.ReadUInt16( nVal16 ); + } + if ( nPMask & 0x80000 ) + rIn.ReadUInt16( maParaLevel[ nLevel ].mnAsianLineBreak ); + if ( nPMask & 0x100000 ) + rIn.ReadUInt16( maParaLevel[ nLevel ].mnBiDi ); + } + else + { + if ( nPMask & 0x800 ) + { + sal_uInt16 nVal16(0); + rIn.ReadUInt16( nVal16 ); + maParaLevel[ nLevel ].mnAdjust = nVal16 & 3; + } + if ( nPMask & 0x1000 ) + rIn.ReadUInt16( maParaLevel[ nLevel ].mnLineFeed ); + if ( nPMask & 0x2000 ) + rIn.ReadUInt16( maParaLevel[ nLevel ].mnUpperDist ); + if ( nPMask & 0x4000 ) + rIn.ReadUInt16( maParaLevel[ nLevel ].mnLowerDist ); + if ( nPMask & 0x8000 ) + { + sal_uInt16 nVal16; + rIn.ReadUInt16( nVal16 ); + } + if ( nPMask & 0x100 ) + rIn.ReadUInt16( maParaLevel[ nLevel ].mnTextOfs ); + if ( nPMask & 0x200 ) + { + sal_uInt16 nVal16; + rIn.ReadUInt16( nVal16 ); + } + if ( nPMask & 0x400 ) + rIn.ReadUInt16( maParaLevel[ nLevel ].mnBulletOfs ); + if ( nPMask & 0x10000 ) + { + sal_uInt16 nVal16; + rIn.ReadUInt16( nVal16 ); + } + if ( nPMask & 0xe0000 ) + { + sal_uInt16 nFlagsToModifyMask = static_cast( ( nPMask >> 17 ) & 7 ); + sal_uInt16 nVal16(0); + rIn.ReadUInt16( nVal16 ); + // bits that are not involved to zero + nVal16 &= nFlagsToModifyMask; + // bits that are to change to zero + maParaLevel[ nLevel ].mnAsianLineBreak &=~nFlagsToModifyMask; + // now set the corresponding bits + maParaLevel[ nLevel ].mnAsianLineBreak |= nVal16; + } + if ( nPMask & 0x100000 ) + { + sal_uInt16 nVal16; + sal_uInt32 nVal32; + // number of tabulators + rIn.ReadUInt16( nVal16 ); + if (!rIn.good() || rIn.remainingSize() / sizeof(nVal32) < nVal16) + return; + for (sal_uInt16 i = 0; i < nVal16; ++i) + rIn.ReadUInt32( nVal32 ); // reading the tabulators + } + if ( nPMask & 0x200000 ) + rIn.ReadUInt16( maParaLevel[ nLevel ].mnBiDi ); + } + + nPMask >>= 22; + while( nPMask ) + { + if ( nPMask & 1 ) + { +#ifdef DBG_UTIL + if (!(rManager.rImportParam.nImportFlags & PPT_IMPORTFLAGS_NO_TEXT_ASSERT)) + { + OSL_FAIL( "PPTParaSheet::Read - unknown attribute, send me this document (SJ)" ); + } +#endif + sal_uInt16 nVal16; + rIn.ReadUInt16( nVal16 ); + } + nPMask >>= 1; + } +} + +void PPTParaSheet::UpdateBulletRelSize( sal_uInt32 nLevel, sal_uInt16 nFontHeight ) +{ + if ( maParaLevel[ nLevel ].mnBulletHeight > 0x7fff ) // a negative value is the absolute bullet height + { + sal_Int16 nBulletRelSize = static_cast(maParaLevel[ nLevel ].mnBulletHeight); + nBulletRelSize = nFontHeight ? ((-nBulletRelSize) * 100 ) / nFontHeight : 100; + if ( nBulletRelSize < 0 ) //bullet size over flow + nBulletRelSize = 100; + maParaLevel[ nLevel ].mnBulletHeight = nBulletRelSize; + } +} + +PPTStyleSheet::PPTStyleSheet( const DffRecordHeader& rSlideHd, SvStream& rIn, SdrPowerPointImport& rManager, + const PPTTextParagraphStyleAtomInterpreter& rTxPFStyle, + const PPTTextSpecInfo& rTextSpecInfo ) : + + PPTNumberFormatCreator ( std::make_unique( rManager, rIn, &rSlideHd ) ), + maTxSI ( rTextSpecInfo ) +{ + sal_uInt32 nOldFilePos = rIn.Tell(); + + // default stylesheets + mpCharSheet[ TSS_Type::PageTitle ] = std::make_unique( TSS_Type::PageTitle ); + mpCharSheet[ TSS_Type::Body ] = std::make_unique( TSS_Type::Body ); + mpCharSheet[ TSS_Type::Notes ] = std::make_unique( TSS_Type::Notes ); + mpCharSheet[ TSS_Type::Unused ] = std::make_unique( TSS_Type::Unused ); // this entry is not used by ppt + mpCharSheet[ TSS_Type::TextInShape ] = std::make_unique( TSS_Type::TextInShape ); + mpParaSheet[ TSS_Type::PageTitle ] = std::make_unique( TSS_Type::PageTitle ); + mpParaSheet[ TSS_Type::Body ] = std::make_unique( TSS_Type::Body ); + mpParaSheet[ TSS_Type::Notes ] = std::make_unique( TSS_Type::Notes ); + mpParaSheet[ TSS_Type::Unused ] = std::make_unique( TSS_Type::Unused ); + mpParaSheet[ TSS_Type::TextInShape ] = std::make_unique( TSS_Type::TextInShape ); + // mpCharSheet[ TSS_Type::QuarterBody ], mpCharSheet[ TSS_Type::HalfBody ], mpCharSheet[ TSS_Type::Title ], mpCharSheet[ TSS_Type::Subtitle ] intentionally null + // mpParaSheet[ TSS_Type::QuarterBody ], mpParaSheet[ TSS_Type::HalfBody ], mpParaSheet[ TSS_Type::Title ], mpParaSheet[ TSS_Type::Subtitle ] intentionally null + + /* SJ: try to locate the txMasterStyleAtom in the Environment + + it seems that the environment TextStyle is having a higher priority + than the TextStyle that can be found within the master page + */ + bool bFoundTxMasterStyleAtom04 = false; + DffRecordHeader* pEnvHeader = rManager.aDocRecManager.GetRecordHeader( PPT_PST_Environment ); + if ( pEnvHeader ) + { + pEnvHeader->SeekToContent( rIn ); + DffRecordHeader aTxMasterStyleHd; + auto nEndRecPos = DffPropSet::SanitizeEndPos(rIn, pEnvHeader->GetRecEndFilePos()); + while (rIn.Tell() < nEndRecPos) + { + ReadDffRecordHeader( rIn, aTxMasterStyleHd ); + if ( aTxMasterStyleHd.nRecType == PPT_PST_TxMasterStyleAtom ) + { + sal_uInt16 nLevelCnt(0); + rIn.ReadUInt16(nLevelCnt); + + sal_uInt16 nLev = 0; + bool bFirst = true; + bFoundTxMasterStyleAtom04 = true; + auto nTxEndRecPos = DffPropSet::SanitizeEndPos(rIn, aTxMasterStyleHd.GetRecEndFilePos()); + while (rIn.GetError() == ERRCODE_NONE && rIn.Tell() < nTxEndRecPos && nLev < nLevelCnt && nLev < nMaxPPTLevels) + { + if ( nLev ) + { + mpParaSheet[ TSS_Type::TextInShape ]->maParaLevel[ nLev ] = mpParaSheet[ TSS_Type::TextInShape ]->maParaLevel[ nLev - 1 ]; + mpCharSheet[ TSS_Type::TextInShape ]->maCharLevel[ nLev ] = mpCharSheet[ TSS_Type::TextInShape ]->maCharLevel[ nLev - 1 ]; + } + mpParaSheet[ TSS_Type::TextInShape ]->Read( rManager, rIn, nLev, bFirst ); + if ( !nLev ) + { + // set paragraph defaults for instance 4 (TSS_Type::TextInShape) + if ( rTxPFStyle.bValid ) + { + PPTParaLevel& rParaLevel = mpParaSheet[ TSS_Type::TextInShape ]->maParaLevel[ 0 ]; + rParaLevel.mnAsianLineBreak = 0; + if ( rTxPFStyle.bForbiddenRules ) + rParaLevel.mnAsianLineBreak |= 1; + if ( !rTxPFStyle.bLatinTextWrap ) + rParaLevel.mnAsianLineBreak |= 2; + if ( rTxPFStyle.bHangingPunctuation ) + rParaLevel.mnAsianLineBreak |= 4; + } + } + mpCharSheet[ TSS_Type::TextInShape ]->Read( rIn, nLev ); + mpParaSheet[ TSS_Type::TextInShape ]->UpdateBulletRelSize( nLev, mpCharSheet[ TSS_Type::TextInShape ]->maCharLevel[ nLev ].mnFontHeight ); + bFirst = false; + nLev++; + } + break; + } + else + { + if (!aTxMasterStyleHd.SeekToEndOfRecord(rIn)) + break; + } + } + } + + rSlideHd.SeekToContent( rIn ); + + DffRecordHeader aTxMasterStyleHd; + auto nEndRecPos = DffPropSet::SanitizeEndPos(rIn, rSlideHd.GetRecEndFilePos()); + while (rIn.Tell() < nEndRecPos) + { + ReadDffRecordHeader( rIn, aTxMasterStyleHd ); + if ( aTxMasterStyleHd.nRecType == PPT_PST_TxMasterStyleAtom ) + break; + else + { + if (!aTxMasterStyleHd.SeekToEndOfRecord(rIn)) + break; + } + } + while ( ( aTxMasterStyleHd.nRecType == PPT_PST_TxMasterStyleAtom ) && ( rIn.Tell() < nEndRecPos ) ) //TODO: aTxMasterStyleHd may be used without having been properly initialized + { + TSS_Type nInstance = static_cast(aTxMasterStyleHd.nRecInstance); + if ( ( nInstance <= TSS_Type::LAST ) && + ( ( nInstance != TSS_Type::TextInShape ) || !bFoundTxMasterStyleAtom04 ) ) + { + if ( nInstance > TSS_Type::TextInShape ) + { + mpCharSheet[ nInstance ].reset(); // be sure to delete the old one if this instance comes twice + mpParaSheet[ nInstance ].reset(); + + switch ( nInstance ) + { + case TSS_Type::Subtitle : + { + mpCharSheet[ TSS_Type::Subtitle ] = std::make_unique( *( mpCharSheet[ TSS_Type::Body ] ) ); + mpParaSheet[ TSS_Type::Subtitle ] = std::make_unique( *( mpParaSheet[ TSS_Type::Body ] ) ); + } + break; + case TSS_Type::Title : + { + mpCharSheet[ TSS_Type::Title ] = std::make_unique( *( mpCharSheet[ TSS_Type::PageTitle ] ) ); + mpParaSheet[ TSS_Type::Title ] = std::make_unique( *( mpParaSheet[ TSS_Type::PageTitle ] ) ); + } + break; + case TSS_Type::HalfBody : + { + mpCharSheet[ TSS_Type::HalfBody ] = std::make_unique( *( mpCharSheet[ TSS_Type::Body ] ) ); + mpParaSheet[ TSS_Type::HalfBody ] = std::make_unique( *( mpParaSheet[ TSS_Type::Body ] ) ); + } + break; + + case TSS_Type::QuarterBody : + { + mpCharSheet[ TSS_Type::QuarterBody ] = std::make_unique( *( mpCharSheet[ TSS_Type::Body ] ) ); + mpParaSheet[ TSS_Type::QuarterBody ] = std::make_unique( *( mpParaSheet[ TSS_Type::Body ] ) ); + } + break; + default: break; + } + } + sal_uInt16 nLevelCnt(0); + rIn.ReadUInt16(nLevelCnt); + if (nLevelCnt > nMaxPPTLevels) + { + OSL_FAIL( "PPTStyleSheet::Ppt-TextStylesheet has more than 5 levels! (SJ)" ); + nLevelCnt = nMaxPPTLevels; + } + sal_uInt16 nLev = 0; + bool bFirst = true; + + auto nTxEndRecPos = DffPropSet::SanitizeEndPos(rIn, aTxMasterStyleHd.GetRecEndFilePos()); + while ( rIn.GetError() == ERRCODE_NONE && rIn.Tell() < nTxEndRecPos && nLev < nLevelCnt ) + { + if ( nLev && ( nInstance < TSS_Type::Subtitle ) ) + { + mpParaSheet[ nInstance ]->maParaLevel[ nLev ] = mpParaSheet[ nInstance ]->maParaLevel[ nLev - 1 ]; + mpCharSheet[ nInstance ]->maCharLevel[ nLev ] = mpCharSheet[ nInstance ]->maCharLevel[ nLev - 1 ]; + } + + // Exception: Template 5, 6 (MasterTitle Title and SubTitle) + if ( nInstance >= TSS_Type::Subtitle ) + { + bFirst = false; + + sal_uInt16 nDontKnow; + rIn.ReadUInt16( nDontKnow ); + } + mpParaSheet[ nInstance ]->Read( rManager, rIn, nLev, bFirst ); + mpCharSheet[ nInstance ]->Read( rIn, nLev ); + mpParaSheet[ nInstance ]->UpdateBulletRelSize( nLev, mpCharSheet[ nInstance ]->maCharLevel[ nLev ].mnFontHeight ); + bFirst = false; + nLev++; + } +#ifdef DBG_UTIL + if (!(rManager.rImportParam.nImportFlags & PPT_IMPORTFLAGS_NO_TEXT_ASSERT)) + { + if ( rIn.GetError() == ERRCODE_NONE ) + { + OStringBuffer aMsg; + if ( rIn.Tell() > aTxMasterStyleHd.GetRecEndFilePos() ) + { + aMsg.append("\n reading too many bytes:" + + OString::number(rIn.Tell() - aTxMasterStyleHd.GetRecEndFilePos())); + } + if ( rIn.Tell() < aTxMasterStyleHd.GetRecEndFilePos() ) + { + aMsg.append("\n reading too few bytes:" + + OString::number(aTxMasterStyleHd.GetRecEndFilePos() - rIn.Tell())); + } + if (aMsg.getLength()) + { + aMsg.insert(0, "PptStyleSheet::operator>>[]"); + OSL_FAIL(aMsg.getStr()); + } + } + if ( rIn.Tell() != aTxMasterStyleHd.GetRecEndFilePos() ) + SAL_WARN( "filter.ms", "SJ: Wrong number of bytes read during import of PPT style"); + } +#endif + } + if (!aTxMasterStyleHd.SeekToEndOfRecord(rIn)) + break; + ReadDffRecordHeader( rIn, aTxMasterStyleHd ); + } + if ( !mpCharSheet[ TSS_Type::Subtitle ] ) + { + mpCharSheet[ TSS_Type::Subtitle ] = std::make_unique( *( mpCharSheet[ TSS_Type::Body ] ) ); + mpParaSheet[ TSS_Type::Subtitle ] = std::make_unique( *( mpParaSheet[ TSS_Type::Body ] ) ); + } + if ( !mpCharSheet[ TSS_Type::Title ] ) + { + mpCharSheet[ TSS_Type::Title ] = std::make_unique( *( mpCharSheet[ TSS_Type::PageTitle ] ) ); + mpParaSheet[ TSS_Type::Title ] = std::make_unique( *( mpParaSheet[ TSS_Type::PageTitle ] ) ); + } + if ( !mpCharSheet[ TSS_Type::HalfBody ] ) + { + mpCharSheet[ TSS_Type::HalfBody ] = std::make_unique( *( mpCharSheet[ TSS_Type::Body ] ) ); + mpParaSheet[ TSS_Type::HalfBody ] = std::make_unique( *( mpParaSheet[ TSS_Type::Body ] ) ); + } + if ( !mpCharSheet[ TSS_Type::QuarterBody ] ) + { + mpCharSheet[ TSS_Type::QuarterBody ] = std::make_unique( *( mpCharSheet[ TSS_Type::Body ] ) ); + mpParaSheet[ TSS_Type::QuarterBody ] = std::make_unique( *( mpParaSheet[ TSS_Type::Body ] ) ); + } + if ( !bFoundTxMasterStyleAtom04 ) + { // try to locate the txMasterStyleAtom in the Environment + DffRecordHeader* pEnvHeader2 = rManager.aDocRecManager.GetRecordHeader( PPT_PST_Environment ); + if ( pEnvHeader2 ) + { + pEnvHeader2->SeekToContent( rIn ); + DffRecordHeader aTxMasterStyleHd2; + auto nEnvEndRecPos = DffPropSet::SanitizeEndPos(rIn, pEnvHeader2->GetRecEndFilePos()); + while (rIn.Tell() < nEnvEndRecPos) + { + ReadDffRecordHeader( rIn, aTxMasterStyleHd2 ); + if ( aTxMasterStyleHd2.nRecType == PPT_PST_TxMasterStyleAtom ) + { + sal_uInt16 nLevelCnt; + rIn.ReadUInt16( nLevelCnt ); + + sal_uInt16 nLev = 0; + bool bFirst = true; + auto nTxEndRecPos = DffPropSet::SanitizeEndPos(rIn, aTxMasterStyleHd2.GetRecEndFilePos()); + while ( rIn.GetError() == ERRCODE_NONE && rIn.Tell() < nTxEndRecPos && nLev < nLevelCnt ) + { + if ( nLev ) + { + mpParaSheet[ TSS_Type::TextInShape ]->maParaLevel[ nLev ] = mpParaSheet[ TSS_Type::TextInShape ]->maParaLevel[ nLev - 1 ]; + mpCharSheet[ TSS_Type::TextInShape ]->maCharLevel[ nLev ] = mpCharSheet[ TSS_Type::TextInShape ]->maCharLevel[ nLev - 1 ]; + } + mpParaSheet[ TSS_Type::TextInShape ]->Read( rManager, rIn, nLev, bFirst ); + if ( !nLev ) + { + // set paragraph defaults for instance 4 (TSS_Type::TextInShape) + if ( rTxPFStyle.bValid ) + { + PPTParaLevel& rParaLevel = mpParaSheet[ TSS_Type::TextInShape ]->maParaLevel[ 0 ]; + rParaLevel.mnAsianLineBreak = 0; + if ( rTxPFStyle.bForbiddenRules ) + rParaLevel.mnAsianLineBreak |= 1; + if ( !rTxPFStyle.bLatinTextWrap ) + rParaLevel.mnAsianLineBreak |= 2; + if ( rTxPFStyle.bHangingPunctuation ) + rParaLevel.mnAsianLineBreak |= 4; + } + } + mpCharSheet[ TSS_Type::TextInShape ]->Read( rIn, nLev ); + mpParaSheet[ TSS_Type::TextInShape ]->UpdateBulletRelSize( nLev, mpCharSheet[ TSS_Type::TextInShape ]->maCharLevel[ nLev ].mnFontHeight ); + bFirst = false; + nLev++; + } + break; + } + else + { + if (!aTxMasterStyleHd2.SeekToEndOfRecord(rIn)) + break; + } + } + } + } + rIn.Seek( nOldFilePos ); + + // will create the default numbulletitem for each instance + for ( auto i : o3tl::enumrange() ) + { + sal_uInt16 nLevels, nDepth = 0; + SvxNumRuleType eNumRuleType; + + switch ( i ) + { + case TSS_Type::PageTitle : + case TSS_Type::Title : + nLevels = 1; + eNumRuleType = SvxNumRuleType::NUMBERING; + break; + case TSS_Type::Subtitle : + nLevels = SVX_MAX_NUM; + eNumRuleType = SvxNumRuleType::NUMBERING; + break; + case TSS_Type::Body : + case TSS_Type::HalfBody : + case TSS_Type::QuarterBody : + nLevels = SVX_MAX_NUM; + eNumRuleType = SvxNumRuleType::PRESENTATION_NUMBERING; + break; + default : + case TSS_Type::Notes : + case TSS_Type::Unused : + case TSS_Type::TextInShape : + nLevels = SVX_MAX_NUM; + eNumRuleType = SvxNumRuleType::NUMBERING; + break; + } + SvxNumRule aRule( SvxNumRuleFlags::BULLET_REL_SIZE | SvxNumRuleFlags::BULLET_COLOR, + nLevels, false, eNumRuleType ); + for ( sal_uInt16 nCount = 0; nDepth < nLevels; nCount++ ) + { + const PPTParaLevel& rParaLevel = mpParaSheet[ i ]->maParaLevel[ nCount ]; + const PPTCharLevel& rCharLevel = mpCharSheet[ i ]->maCharLevel[ nCount ]; + SvxNumberFormat aNumberFormat( SVX_NUM_CHAR_SPECIAL ); + aNumberFormat.SetBulletChar( ' ' ); + GetNumberFormat( rManager, aNumberFormat, nCount, rParaLevel, rCharLevel, i ); + aRule.SetLevel( nDepth++, aNumberFormat ); + if ( nCount >= 4 ) + { + for ( ;nDepth < nLevels; nDepth++ ) + aRule.SetLevel( nDepth, aNumberFormat ); + } + } + mpNumBulletItem[ i ] = std::make_unique( std::move(aRule), EE_PARA_NUMBULLET ); + } +} + +PPTStyleSheet::~PPTStyleSheet() +{ + for ( auto i : o3tl::enumrange() ) + { + mpCharSheet[i].reset(); + mpParaSheet[i].reset(); + mpNumBulletItem[i].reset(); + } +} + +PPTParaPropSet::PPTParaPropSet() + : mnOriginalTextPos(0) + , mxParaSet( new ImplPPTParaPropSet ) +{ + mxParaSet->mnHasAnm = 1; +} + +PPTParaPropSet::PPTParaPropSet( PPTParaPropSet const & rParaPropSet ) +{ + mxParaSet = rParaPropSet.mxParaSet; + mnOriginalTextPos = rParaPropSet.mnOriginalTextPos; +} + +PPTParaPropSet::~PPTParaPropSet() +{ +} + +PPTParaPropSet& PPTParaPropSet::operator=( const PPTParaPropSet& rParaPropSet ) +{ + if ( this != &rParaPropSet ) + { + mxParaSet = rParaPropSet.mxParaSet; + mnOriginalTextPos = rParaPropSet.mnOriginalTextPos; + } + return *this; +} + +PPTCharPropSet::PPTCharPropSet(sal_uInt32 nParagraph) + : mnOriginalTextPos(0) + , mnParagraph(nParagraph) +{ + mnHylinkOrigColor = 0; + mbIsHyperlink = false; + mbHardHylinkOrigColor = false; + mnLanguage[ 0 ] = mnLanguage[ 1 ] = mnLanguage[ 2 ] = LANGUAGE_SYSTEM; +} + +PPTCharPropSet::PPTCharPropSet( const PPTCharPropSet& rCharPropSet ) + : mpImplPPTCharPropSet( rCharPropSet.mpImplPPTCharPropSet ) +{ + mnHylinkOrigColor = rCharPropSet.mnHylinkOrigColor; + mbIsHyperlink = rCharPropSet.mbIsHyperlink; + mbHardHylinkOrigColor = rCharPropSet.mbHardHylinkOrigColor; + + mnParagraph = rCharPropSet.mnParagraph; + mnOriginalTextPos = rCharPropSet.mnOriginalTextPos; + maString = rCharPropSet.maString; + mpFieldItem.reset( rCharPropSet.mpFieldItem ? new SvxFieldItem( *rCharPropSet.mpFieldItem ) : nullptr ); + mnLanguage[ 0 ] = rCharPropSet.mnLanguage[ 0 ]; + mnLanguage[ 1 ] = rCharPropSet.mnLanguage[ 1 ]; + mnLanguage[ 2 ] = rCharPropSet.mnLanguage[ 2 ]; +} + +PPTCharPropSet::PPTCharPropSet( const PPTCharPropSet& rCharPropSet, sal_uInt32 nParagraph ) + : mpImplPPTCharPropSet(rCharPropSet.mpImplPPTCharPropSet) +{ + mnHylinkOrigColor = rCharPropSet.mnHylinkOrigColor; + mbIsHyperlink = rCharPropSet.mbIsHyperlink; + mbHardHylinkOrigColor = rCharPropSet.mbHardHylinkOrigColor; + + mnParagraph = nParagraph; + mnOriginalTextPos = rCharPropSet.mnOriginalTextPos; + maString = rCharPropSet.maString; + mpFieldItem.reset( rCharPropSet.mpFieldItem ? new SvxFieldItem( *rCharPropSet.mpFieldItem ) : nullptr ); + mnLanguage[ 0 ] = mnLanguage[ 1 ] = mnLanguage[ 2 ] = LANGUAGE_SYSTEM; +} + +PPTCharPropSet::~PPTCharPropSet() +{ +} + +PPTCharPropSet& PPTCharPropSet::operator=( const PPTCharPropSet& rCharPropSet ) +{ + if ( this != &rCharPropSet ) + { + mpImplPPTCharPropSet = rCharPropSet.mpImplPPTCharPropSet; + mnOriginalTextPos = rCharPropSet.mnOriginalTextPos; + mnParagraph = rCharPropSet.mnParagraph; + maString = rCharPropSet.maString; + mpFieldItem.reset( rCharPropSet.mpFieldItem ? new SvxFieldItem( *rCharPropSet.mpFieldItem ) : nullptr ); + } + return *this; +} + +void PPTCharPropSet::SetFont( sal_uInt16 nFont ) +{ + sal_uInt32 nMask = 1 << PPT_CharAttr_Font; + bool bDoNotMake = (mpImplPPTCharPropSet->mnAttrSet & nMask) != 0; + + if ( bDoNotMake ) + bDoNotMake = nFont == mpImplPPTCharPropSet->mnFont; + + if ( !bDoNotMake ) + { + mpImplPPTCharPropSet->mnFont = nFont; + mpImplPPTCharPropSet->mnAttrSet |= nMask; + } +} + +void PPTCharPropSet::SetColor( sal_uInt32 nColor ) +{ + mpImplPPTCharPropSet->mnColor = nColor; + mpImplPPTCharPropSet->mnAttrSet |= 1 << PPT_CharAttr_FontColor; +} + +PPTRuler::PPTRuler() + : nFlags(0) + , nDefaultTab(0x240) + , nTabCount(0) +{ +} + +PPTRuler::~PPTRuler() +{ +}; + + +PPTTextRulerInterpreter::PPTTextRulerInterpreter() : + mxImplRuler ( new PPTRuler() ) +{ +} + +PPTTextRulerInterpreter::PPTTextRulerInterpreter( PPTTextRulerInterpreter const & rRuler ) +{ + mxImplRuler = rRuler.mxImplRuler; +} + +PPTTextRulerInterpreter::PPTTextRulerInterpreter( sal_uInt32 nFileOfs, DffRecordHeader const & rHeader, SvStream& rIn ) : + mxImplRuler ( new PPTRuler() ) +{ + if ( nFileOfs == 0xffffffff ) + return; + + sal_uInt32 nOldPos = rIn.Tell(); + DffRecordHeader rHd; + if ( nFileOfs ) + { + rIn.Seek( nFileOfs ); + ReadDffRecordHeader( rIn, rHd ); + } + else + { + rHeader.SeekToContent( rIn ); + if ( SvxMSDffManager::SeekToRec( rIn, PPT_PST_TextRulerAtom, rHeader.GetRecEndFilePos(), &rHd ) ) + nFileOfs++; + } + if ( nFileOfs ) + { + bool bRecordOk = true; + + sal_Int16 nTCount(0); + sal_Int32 i; + rIn.ReadInt32( mxImplRuler->nFlags ); + + // number of indent levels, unused now + if ( mxImplRuler->nFlags & 2 ) + rIn.ReadInt16( nTCount ); + if ( mxImplRuler->nFlags & 1 ) + rIn.ReadUInt16( mxImplRuler->nDefaultTab ); + if ( mxImplRuler->nFlags & 4 ) + { + rIn.ReadInt16(nTCount); + + const size_t nMaxPossibleRecords = rIn.remainingSize() / (2*sizeof(sal_uInt16)); + const sal_uInt16 nTabCount(nTCount); + + bRecordOk = nTabCount <= nMaxPossibleRecords; + + if (nTCount && bRecordOk) + { + mxImplRuler->nTabCount = nTabCount; + mxImplRuler->pTab.reset( new PPTTabEntry[ mxImplRuler->nTabCount ] ); + for ( i = 0; i < nTCount; i++ ) + { + rIn.ReadUInt16( mxImplRuler->pTab[ i ].nOffset ) + .ReadUInt16( mxImplRuler->pTab[ i ].nStyle ); + } + } + } + + if (bRecordOk) + { + for ( i = 0; i < 5; i++ ) + { + if ( mxImplRuler->nFlags & ( 8 << i ) ) + rIn.ReadUInt16( mxImplRuler->nTextOfs[ i ] ); + if ( mxImplRuler->nFlags & ( 256 << i ) ) + rIn.ReadUInt16( mxImplRuler->nBulletOfs[ i ] ); + if( mxImplRuler->nBulletOfs[ i ] > 0x7fff) + { + // workaround + // when bullet offset is > 0x7fff, the paragraph should look like + // * first line text + // second line text + + // we add to bullet para indent 0xffff - bullet offset. It looks like + // best we can do for now + mxImplRuler->nTextOfs[ i ] += 0xffff - mxImplRuler->nBulletOfs[ i ]; + mxImplRuler->nBulletOfs[ i ] = 0; + } + } + } + } + rIn.Seek( nOldPos ); +} + +bool PPTTextRulerInterpreter::GetDefaultTab( sal_uInt16& nValue ) const +{ + if ( ! ( mxImplRuler->nFlags & 1 ) ) + return false; + nValue = mxImplRuler->nDefaultTab; + return true; +} + +bool PPTTextRulerInterpreter::GetTextOfs( sal_uInt32 nLevel, sal_uInt16& nValue ) const +{ + if ( ! ( ( nLevel < 5 ) && ( mxImplRuler->nFlags & ( 8 << nLevel ) ) ) ) + return false; + nValue = mxImplRuler->nTextOfs[ nLevel ]; + return true; +} + +bool PPTTextRulerInterpreter::GetBulletOfs( sal_uInt32 nLevel, sal_uInt16& nValue ) const +{ + if ( ! ( ( nLevel < 5 ) && ( mxImplRuler->nFlags & ( 256 << nLevel ) ) ) ) + return false; + nValue = mxImplRuler->nBulletOfs[ nLevel ]; + return true; +} + +PPTTextRulerInterpreter& PPTTextRulerInterpreter::operator=( const PPTTextRulerInterpreter& rRuler ) +{ + if ( this != &rRuler ) + { + mxImplRuler = rRuler.mxImplRuler; + } + return *this; +} + +PPTTextRulerInterpreter::~PPTTextRulerInterpreter() +{ +} + +PPTTextParagraphStyleAtomInterpreter::PPTTextParagraphStyleAtomInterpreter() : + bValid ( false ), + bForbiddenRules ( false ), + bHangingPunctuation ( false ), + bLatinTextWrap ( false ) +{ +} + +bool PPTTextParagraphStyleAtomInterpreter::Read( SvStream& rIn, const DffRecordHeader& rRecHd ) +{ + bValid = false; + rRecHd.SeekToContent( rIn ); + sal_uInt32 nDummy32, nFlags, nRecEndPos = rRecHd.GetRecEndFilePos(); + sal_uInt16 nDummy16; + + rIn.ReadUInt16( nDummy16 ) + .ReadUInt32( nFlags ); + + if ( nFlags & 0xf && ( rIn.Tell() < nRecEndPos ) ) + rIn.ReadUInt16( nDummy16 ); // BuFlags + if ( nFlags & 0x80 && ( rIn.Tell() < nRecEndPos ) ) + rIn.ReadUInt16( nDummy16 ); // BuChar + if ( nFlags & 0x10 && ( rIn.Tell() < nRecEndPos ) ) + rIn.ReadUInt16( nDummy16 ); // nBuFont; + if ( nFlags & 0x40 && ( rIn.Tell() < nRecEndPos ) ) + rIn.ReadUInt16( nDummy16 ); // nBuHeight; + if ( nFlags & 0x0020 && ( rIn.Tell() < nRecEndPos ) ) + rIn.ReadUInt32( nDummy32 ); // nBuColor; + if ( nFlags & 0x800 && ( rIn.Tell() < nRecEndPos ) ) + rIn.ReadUInt16( nDummy16 ); // AbsJust! + if ( nFlags & 0x400 && ( rIn.Tell() < nRecEndPos ) ) + rIn.ReadUInt16( nDummy16 ); + if ( nFlags & 0x200 && ( rIn.Tell() < nRecEndPos ) ) + rIn.ReadUInt16( nDummy16 ); + if ( nFlags & 0x100 && ( rIn.Tell() < nRecEndPos ) ) + rIn.ReadUInt16( nDummy16 ); + if ( nFlags & 0x1000 && ( rIn.Tell() < nRecEndPos ) ) + rIn.ReadUInt16( nDummy16 ); // LineFeed + if ( nFlags & 0x2000 && ( rIn.Tell() < nRecEndPos ) ) + rIn.ReadUInt16( nDummy16 ); // nUpperDist + if ( nFlags & 0x4000 && ( rIn.Tell() < nRecEndPos ) ) + rIn.ReadUInt16( nDummy16 ); // nLowerDist + if ( nFlags & 0x8000 && ( rIn.Tell() < nRecEndPos ) ) + rIn.ReadUInt16( nDummy16 ); + if ( nFlags & 0x10000 && ( rIn.Tell() < nRecEndPos ) ) + rIn.ReadUInt16( nDummy16 ); + if ( nFlags & 0xe0000 && ( rIn.Tell() < nRecEndPos ) ) + { + rIn.ReadUInt16( nDummy16 ); + if ( nFlags & 0x20000 ) + bForbiddenRules = ( nDummy16 & 1 ) == 1; + if ( nFlags & 0x40000 ) + bLatinTextWrap = ( nDummy16 & 2 ) == 0; + if ( nFlags & 0x80000 ) + bHangingPunctuation = ( nDummy16 & 4 ) == 4; + } + nFlags &=~ 0xfffff; + sal_uInt32 nMask = 0x100000; + while ( nFlags && nMask && ( rIn.Tell() < nRecEndPos ) ) + { + if ( nFlags & nMask ) + { + rIn.ReadUInt16( nDummy16 ); + nFlags ^= nMask; + } + nMask <<= 1; + } + bValid = rIn.Tell() == nRecEndPos; + return bValid; +} + +PPTTextSpecInfo::PPTTextSpecInfo( sal_uInt32 _nCharIdx ) : + nCharIdx ( _nCharIdx ), + nDontKnow ( 1 ) +{ + nLanguage[ 0 ] = LANGUAGE_PROCESS_OR_USER_DEFAULT; + nLanguage[ 1 ] = LANGUAGE_SYSTEM; + nLanguage[ 2 ] = LANGUAGE_SYSTEM; +} + +PPTTextSpecInfoAtomInterpreter::PPTTextSpecInfoAtomInterpreter() : + bValid ( false ) +{ +} + +bool PPTTextSpecInfoAtomInterpreter::Read( SvStream& rIn, const DffRecordHeader& rRecHd, + sal_uInt16 nRecordType, const PPTTextSpecInfo* pTextSpecDefault ) +{ + bValid = false; + sal_uInt32 nCharIdx = 0; + rRecHd.SeekToContent( rIn ); + + auto nEndRecPos = DffPropSet::SanitizeEndPos(rIn, rRecHd.GetRecEndFilePos()); + while (rIn.Tell() < nEndRecPos && rIn.good()) + { + if ( nRecordType == PPT_PST_TextSpecInfoAtom ) + { + sal_uInt32 nCharCount(0); + rIn.ReadUInt32( nCharCount ); + nCharIdx += nCharCount; + } + + sal_uInt32 nFlags(0); + rIn.ReadUInt32(nFlags); + + PPTTextSpecInfo aEntry( nCharIdx ); + if ( pTextSpecDefault ) + { + aEntry.nDontKnow = pTextSpecDefault->nDontKnow; + aEntry.nLanguage[ 0 ] = pTextSpecDefault->nLanguage[ 0 ]; + aEntry.nLanguage[ 1 ] = pTextSpecDefault->nLanguage[ 1 ]; + aEntry.nLanguage[ 2 ] = pTextSpecDefault->nLanguage[ 2 ]; + } + for (sal_uInt32 i = 1; nFlags && i ; i <<= 1) + { + sal_uInt16 nLang = 0; + switch( nFlags & i ) + { + case 0 : break; + case 1 : rIn.ReadUInt16( aEntry.nDontKnow ); break; + case 2 : rIn.ReadUInt16( nLang ); break; + case 4 : rIn.ReadUInt16( nLang ); break; + default : + { + rIn.SeekRel( 2 ); + } + } + if ( nLang ) + { + // #i119985#, we could probably handle this better if we have a + // place to override the final language for weak + // characters/fields to fallback to, rather than the current + // application locale. Assuming that we can determine what the + // default fallback language for a given .ppt, etc is during + // load time. + if (i == 2) + { + aEntry.nLanguage[ 0 ] = aEntry.nLanguage[ 1 ] = aEntry.nLanguage[ 2 ] = LanguageType(nLang); + } + } + nFlags &= ~i; + } + aList.push_back( aEntry ); + } + bValid = rIn.Tell() == rRecHd.GetRecEndFilePos(); + return bValid; +} + +PPTTextSpecInfoAtomInterpreter::~PPTTextSpecInfoAtomInterpreter() +{ +} + +void StyleTextProp9::Read( SvStream& rIn ) +{ + rIn.ReadUInt32( mnExtParagraphMask ); + if ( mnExtParagraphMask & 0x800000 ) + rIn.ReadUInt16( mnBuBlip ); + if ( mnExtParagraphMask & 0x2000000 ) + rIn.ReadUInt16( mnHasAnm ); + if ( mnExtParagraphMask & 0x1000000 ) + rIn.ReadUInt32( mnAnmScheme ); + if ( mnExtParagraphMask & 0x4000000 ) + rIn.ReadUInt32( mpfPP10Ext ); + rIn.ReadUInt32( mnExtCharacterMask ); + if ( mnExtCharacterMask & 0x100000 ) + rIn.ReadUInt32( mncfPP10Ext ); + rIn.ReadUInt32( mnSpecialInfoMask ); + if ( mnSpecialInfoMask & 0x20 ) + rIn.ReadUInt32( mnPP10Ext ); + if ( mnSpecialInfoMask & 0x40 ) + rIn.ReadUInt16( mfBidi ); +} + +PPTStyleTextPropReader::PPTStyleTextPropReader( SvStream& rIn, const DffRecordHeader& rTextHeader, + PPTTextRulerInterpreter const & rRuler, const DffRecordHeader& rExtParaHd, TSS_Type nInstance ) +{ + Init(rIn, rTextHeader, rRuler, rExtParaHd, nInstance); +} + +void PPTStyleTextPropReader::ReadParaProps( SvStream& rIn, const DffRecordHeader& rTextHeader, + const OUString& aString, PPTTextRulerInterpreter const & rRuler, + sal_uInt32& nCharCount, bool& bTextPropAtom ) +{ + sal_uInt32 nMask = 0; //TODO: nMask initialized here to suppress warning for now, see corresponding TODO below + sal_uInt32 nCharReadCnt = 0; + sal_uInt16 nDummy16; + + sal_uInt16 nStringLen = aString.getLength(); + + DffRecordHeader aTextHd2; + rTextHeader.SeekToContent( rIn ); + if ( SvxMSDffManager::SeekToRec( rIn, PPT_PST_StyleTextPropAtom, rTextHeader.GetRecEndFilePos(), &aTextHd2 ) ) + bTextPropAtom = true; + while ( nCharReadCnt <= nStringLen ) + { + PPTParaPropSet aParaPropSet; + ImplPPTParaPropSet& aSet = *aParaPropSet.mxParaSet; + if ( bTextPropAtom ) + { + rIn.ReadUInt32( nCharCount ) + .ReadUInt16( aParaPropSet.mxParaSet->mnDepth ); // indent depth + + aParaPropSet.mxParaSet->mnDepth = // taking care of about using not more than 9 outliner levels + std::min(sal_uInt16(8), + aParaPropSet.mxParaSet->mnDepth); + + nCharCount--; + + rIn.ReadUInt32( nMask ); + aSet.mnAttrSet = nMask & 0x207df7; + sal_uInt16 nBulFlg = 0; + if ( nMask & 0xF ) + rIn.ReadUInt16( nBulFlg ); // Bullet-HardAttr-Flags + aSet.mpArry[ PPT_ParaAttr_BulletOn ] = ( nBulFlg & 1 ) ? 1 : 0; + aSet.mpArry[ PPT_ParaAttr_BuHardFont ] = ( nBulFlg & 2 ) ? 1 : 0; + aSet.mpArry[ PPT_ParaAttr_BuHardColor ] = ( nBulFlg & 4 ) ? 1 : 0; + + // NOTE: one might think that the hard-coded numbers here are the + // same as the PPT_ParaAttr_* constants, but it's NOT always true! + if ( nMask & 0x0080 ) // buChar + { + rIn.ReadUInt16( aSet.mpArry[ PPT_ParaAttr_BulletChar ] ); + if (!rIn.good()) + { + aSet.mnAttrSet &= ~(1 << PPT_ParaAttr_BulletChar); + } + } + if ( nMask & 0x0010 ) // buTypeface + { + rIn.ReadUInt16( aSet.mpArry[ PPT_ParaAttr_BulletFont ] ); + if (!rIn.good()) + { + aSet.mnAttrSet &= ~(1 << PPT_ParaAttr_BulletFont); + } + } + if ( nMask & 0x0040 ) // buSize + { + rIn.ReadUInt16( aSet.mpArry[ PPT_ParaAttr_BulletHeight ] ); + if (!rIn.good() + || !((nMask & (1 << PPT_ParaAttr_BuHardHeight)) + && (nBulFlg & (1 << PPT_ParaAttr_BuHardHeight)))) + { + aSet.mnAttrSet &= ~(1 << PPT_ParaAttr_BulletHeight); + } + } + if ( nMask & 0x0020 ) // buColor + { + sal_uInt32 nVal32; + rIn.ReadUInt32( nVal32 ); + if (!rIn.good()) + { + aSet.mnBulletColor = 0; // no flag for this? default it + } + else + { + sal_uInt32 nHiByte; + nHiByte = nVal32 >> 24; + if ( nHiByte <= 8 ) + nVal32 = nHiByte | PPT_COLSCHEME; + aSet.mnBulletColor = nVal32; + } + } + if ( nMask & 0x0800 ) // pfAlignment + { + rIn.ReadUInt16( nDummy16 ); + if (!rIn.good()) + { + aSet.mnAttrSet &= ~(1 << PPT_ParaAttr_Adjust); + } + else + { + aSet.mpArry[ PPT_ParaAttr_Adjust ] = nDummy16 & 3; + } + } + if ( nMask & 0x1000 ) // pfLineSpacing + { + rIn.ReadUInt16( aSet.mpArry[ PPT_ParaAttr_LineFeed ] ); + if (!rIn.good()) + { + aSet.mnAttrSet &= ~(1 << PPT_ParaAttr_LineFeed); + } + } + if ( nMask & 0x2000 ) // pfSpaceBefore + { + rIn.ReadUInt16( aSet.mpArry[ PPT_ParaAttr_UpperDist ] ); + if (!rIn.good()) + { + aSet.mnAttrSet &= ~(1 << PPT_ParaAttr_UpperDist); + } + } + if ( nMask & 0x4000 ) // pfSpaceAfter + { + rIn.ReadUInt16( aSet.mpArry[ PPT_ParaAttr_LowerDist ] ); + if (!rIn.good()) + { + aSet.mnAttrSet &= ~(1 << PPT_ParaAttr_LowerDist); + } + } + if ( nMask & 0x100 ) // pfLeftMargin + { + rIn.ReadUInt16( aSet.mpArry[ PPT_ParaAttr_TextOfs ] ); + if (!rIn.good()) + { + aSet.mnAttrSet &= ~(1 << PPT_ParaAttr_TextOfs); + } + else + { + aSet.mnAttrSet |= 1 << PPT_ParaAttr_TextOfs; + } + } + if ( nMask & 0x400 ) // pfIndent + { + rIn.ReadUInt16( aSet.mpArry[ PPT_ParaAttr_BulletOfs ] ); + if (!rIn.good()) + { + aSet.mnAttrSet &= ~(1 << PPT_ParaAttr_BulletOfs); + } + else + { + aSet.mnAttrSet |= 1 << PPT_ParaAttr_BulletOfs; + } + } + if ( nMask & 0x8000 ) // pfDefaultTabSize + { + rIn.ReadUInt16( nDummy16 ); + if (!rIn.good()) + { + // TODO? + } + } + if ( nMask & 0x100000 ) // pfTabStops + { + sal_uInt16 i, nDistance, nAlignment, nNumberOfTabStops = 0; + rIn.ReadUInt16( nNumberOfTabStops ); + if (!rIn.good()) + { + // TODO? + } + else + { + const size_t nMinRecordSize = 4; + const size_t nMaxRecords = rIn.remainingSize() / nMinRecordSize; + if (nNumberOfTabStops > nMaxRecords) + { + SAL_WARN("filter.ms", "Parsing error: " << nMaxRecords << + " max possible entries, but " << nNumberOfTabStops << " claimed, truncating"); + nNumberOfTabStops = nMaxRecords; + } + for (i = 0; i < nNumberOfTabStops; ++i) + { + rIn.ReadUInt16( nDistance ) + .ReadUInt16( nAlignment ); + } + } + } + if ( nMask & 0x10000 ) // pfBaseLine + { + rIn.ReadUInt16( nDummy16 ); + if (!rIn.good()) + { + // TODO? + } + } + if ( nMask & 0xe0000 ) // pfCharWrap, pfWordWrap, pfOverflow + { + rIn.ReadUInt16( nDummy16 ); + if (!rIn.good()) + { // clear flag to avoid invalid access + aSet.mnAttrSet &= ~((1 << PPT_ParaAttr_AsianLB_1) + | (1 << PPT_ParaAttr_AsianLB_2) + | (1 << PPT_ParaAttr_AsianLB_3)); + } + else + { + if (nMask & 0x20000) + aSet.mpArry[PPT_ParaAttr_AsianLB_1] = nDummy16 & 1; + if (nMask & 0x40000) + aSet.mpArry[PPT_ParaAttr_AsianLB_2] = (nDummy16 >> 1) & 1; + if (nMask & 0x80000) + aSet.mpArry[PPT_ParaAttr_AsianLB_3] = (nDummy16 >> 2) & 1; + aSet.mnAttrSet |= ((nMask >> 17) & 7) << PPT_ParaAttr_AsianLB_1; + } + } + if ( nMask & 0x200000 ) // pfTextDirection + { + rIn.ReadUInt16( aSet.mpArry[ PPT_ParaAttr_BiDi ] ); + if (!rIn.good()) + { // clear flag to avoid invalid access + aSet.mnAttrSet &= ~(1 << PPT_ParaAttr_BiDi); + } + } + } + else + nCharCount = nStringLen; + + //if the textofs attr has been read at above, need not to reset. + if ( ( !( aSet.mnAttrSet & 1 << PPT_ParaAttr_TextOfs ) ) && rRuler.GetTextOfs( aParaPropSet.mxParaSet->mnDepth, aSet.mpArry[ PPT_ParaAttr_TextOfs ] ) ) + aSet.mnAttrSet |= 1 << PPT_ParaAttr_TextOfs; + if ( ( !( aSet.mnAttrSet & 1 << PPT_ParaAttr_BulletOfs ) ) && rRuler.GetBulletOfs( aParaPropSet.mxParaSet->mnDepth, aSet.mpArry[ PPT_ParaAttr_BulletOfs ] ) ) + aSet.mnAttrSet |= 1 << PPT_ParaAttr_BulletOfs; + if ( rRuler.GetDefaultTab( aSet.mpArry[ PPT_ParaAttr_DefaultTab ] ) ) + aSet.mnAttrSet |= 1 << PPT_ParaAttr_DefaultTab; + + if ( ( nCharCount > nStringLen ) || ( nStringLen < nCharReadCnt + nCharCount ) ) + { + bTextPropAtom = false; + nCharCount = nStringLen - nCharReadCnt; + // please fix the right hand side of + // PPTParaPropSet& PPTParaPropSet::operator=(PPTParaPropSet&), + // it should be a const reference + PPTParaPropSet aTmpPPTParaPropSet; + aParaPropSet = aTmpPPTParaPropSet; + OSL_FAIL( "SJ:PPTStyleTextPropReader::could not get this PPT_PST_StyleTextPropAtom by reading the paragraph attributes" ); + } + PPTParaPropSet* pPara = new PPTParaPropSet( aParaPropSet ); + pPara->mnOriginalTextPos = nCharReadCnt; + aParaPropList.emplace_back( pPara ); + if ( nCharCount ) + { + sal_uInt32 nCount; + const sal_Unicode* pDat = aString.getStr() + nCharReadCnt; + for ( nCount = 0; nCount < nCharCount; nCount++ ) + { + if ( pDat[ nCount ] == 0xd ) + { + pPara = new PPTParaPropSet( aParaPropSet ); + pPara->mnOriginalTextPos = nCharReadCnt + nCount + 1; + aParaPropList.emplace_back( pPara ); + } + } + } + nCharReadCnt += nCharCount + 1; + } +} + +void PPTStyleTextPropReader::ReadCharProps( SvStream& rIn, PPTCharPropSet& aCharPropSet, const OUString& aString, + sal_uInt32& nCharCount, sal_uInt32 nCharReadCnt, + bool& bTextPropAtom, sal_uInt32 nExtParaPos, + const std::vector< StyleTextProp9 >& aStyleTextProp9, + sal_uInt32& nExtParaFlags, sal_uInt16& nBuBlip, + sal_uInt16& nHasAnm, sal_uInt32& nAnmScheme ) +{ + sal_uInt16 nStringLen = aString.getLength(); + + sal_uInt16 nDummy16; + rIn.ReadUInt16( nDummy16 ); + nCharCount = (rIn.good()) ? nDummy16 : 0; + rIn.ReadUInt16( nDummy16 ); + + sal_Int32 nCharsToRead = nStringLen - ( nCharReadCnt + nCharCount ); + if ( nCharsToRead < 0 ) + { + nCharCount = nStringLen - nCharReadCnt; + if ( nCharsToRead < -1 ) + { + bTextPropAtom = false; + OSL_FAIL( "SJ:PPTStyleTextPropReader::could not get this PPT_PST_StyleTextPropAtom by reading the character attributes" ); + } + } + ImplPPTCharPropSet& aSet = *aCharPropSet.mpImplPPTCharPropSet; + + // character attributes + sal_uInt32 nMask(0); + rIn.ReadUInt32( nMask ); + if ( static_cast(nMask) ) + { + aSet.mnAttrSet |= static_cast(nMask); + rIn.ReadUInt16( aSet.mnFlags ); + } + if ( nMask & 0x10000 ) // cfTypeface + { + rIn.ReadUInt16( aSet.mnFont ); + aSet.mnAttrSet |= 1 << PPT_CharAttr_Font; + } + if ( nMask & 0x200000 ) // cfFEOldTypeface + { + rIn.ReadUInt16( aSet.mnAsianOrComplexFont ); + aSet.mnAttrSet |= 1 << PPT_CharAttr_AsianOrComplexFont; + } + if ( nMask & 0x400000 ) // cfANSITypeface + { + rIn.ReadUInt16( aSet.mnANSITypeface ); + aSet.mnAttrSet |= 1 << PPT_CharAttr_ANSITypeface; + } + if ( nMask & 0x800000 ) // cfSymbolTypeface + { + rIn.ReadUInt16( aSet.mnSymbolFont ); + aSet.mnAttrSet |= 1 << PPT_CharAttr_Symbol; + } + if ( nMask & 0x20000 ) // cfSize + { + rIn.ReadUInt16( aSet.mnFontHeight ); + aSet.mnAttrSet |= 1 << PPT_CharAttr_FontHeight; + } + if ( nMask & 0x40000 ) // cfColor + { + sal_uInt32 nVal(0); + rIn.ReadUInt32( nVal ); + if ( !( nVal & 0xff000000 ) ) + nVal = PPT_COLSCHEME_HINTERGRUND; + aSet.mnColor = nVal; + aSet.mnAttrSet |= 1 << PPT_CharAttr_FontColor; + } + if ( nMask & 0x80000 ) // cfPosition + { + rIn.ReadUInt16( aSet.mnEscapement ); + aSet.mnAttrSet |= 1 << PPT_CharAttr_Escapement; + } + if ( !nExtParaPos ) + return; + + sal_uInt32 nExtBuInd = nMask & 0x3c00; + if ( nExtBuInd ) + nExtBuInd = ( aSet.mnFlags & 0x3c00 ) >> 10; + if ( nExtBuInd < aStyleTextProp9.size() ) + { + nExtParaFlags = aStyleTextProp9[ nExtBuInd ].mnExtParagraphMask; + nBuBlip = aStyleTextProp9[ nExtBuInd ].mnBuBlip; + nHasAnm = aStyleTextProp9[ nExtBuInd ].mnHasAnm; + nAnmScheme = aStyleTextProp9[ nExtBuInd ].mnAnmScheme; + } +} + +void PPTStyleTextPropReader::Init( SvStream& rIn, const DffRecordHeader& rTextHeader, + PPTTextRulerInterpreter const & rRuler, const DffRecordHeader& rExtParaHd, TSS_Type nInstance ) +{ + sal_uInt32 nOldPos = rIn.Tell(); + sal_uInt32 nExtParaPos = ( rExtParaHd.nRecType == PPT_PST_ExtendedParagraphAtom ) ? rExtParaHd.nFilePos + 8 : 0; + + std::vector< StyleTextProp9 > aStyleTextProp9; + if ( rExtParaHd.nRecType == PPT_PST_ExtendedParagraphAtom ) + { + rIn.Seek( rExtParaHd.nFilePos + 8 ); + + auto nEndRecPos = DffPropSet::SanitizeEndPos(rIn, rExtParaHd.GetRecEndFilePos()); + while( ( rIn.GetError() == ERRCODE_NONE ) && ( rIn.Tell() < nEndRecPos ) ) + { + aStyleTextProp9.emplace_back(); + aStyleTextProp9.back().Read( rIn ); + } + rIn.Seek( nOldPos ); + } + + OUString aString; + DffRecordHeader aTextHd; + ReadDffRecordHeader( rIn, aTextHd ); + sal_uInt32 nMaxLen = aTextHd.nRecLen; + if ( nMaxLen >= 0xFFFF ) + nMaxLen = 0xFFFE; + + if( aTextHd.nRecType == PPT_PST_TextCharsAtom ) + { + std::vector aBuf(( nMaxLen >> 1 ) + 1); + void* pDest = aBuf.data(); + auto nRead = rIn.ReadBytes(pDest, nMaxLen); + if (nRead != nMaxLen) + memset(static_cast(pDest) + nRead, 0, nMaxLen - nRead); + nMaxLen >>= 1; + aBuf[nMaxLen] = 0; + + sal_uInt32 i; + sal_Unicode* pPtr = aBuf.data(); + +#ifdef OSL_BIGENDIAN + sal_Unicode nTemp; + for ( i = 0; i < nMaxLen; i++ ) + { + nTemp = *pPtr; + *pPtr++ = ( nTemp << 8 ) | ( nTemp >> 8 ); + } + pPtr = aBuf.data(); +#endif + + for ( i = 0; i < nMaxLen; pPtr++, i++ ) + { + sal_Unicode nChar = *pPtr; + if ( !nChar ) + break; + if ( ( nChar & 0xff00 ) == 0xf000 ) // in this special case we got a symbol + aSpecMarkerList.push_back( static_cast( i | PPT_SPEC_SYMBOL ) ); + else if ( nChar == 0xd ) + { + if ( nInstance == TSS_Type::PageTitle ) + *pPtr = 0xb; + else + aSpecMarkerList.push_back( static_cast( i | PPT_SPEC_NEWLINE ) ); + } + } + if ( i ) + aString = OUString(aBuf.data(), i); + } + else if( aTextHd.nRecType == PPT_PST_TextBytesAtom ) + { + std::unique_ptr pBuf(new char[ nMaxLen + 1 ]); + nMaxLen = rIn.ReadBytes(pBuf.get(), nMaxLen); + pBuf[ nMaxLen ] = 0; + char* pPtr = pBuf.get(); + for (;;) + { + char cLo = *pPtr; + if ( cLo == 0 ) + break; + if ( cLo == 0xd ) + { + if ( nInstance == TSS_Type::PageTitle ) + *pPtr = 0xb; + else + aSpecMarkerList.push_back( static_cast( (pPtr - pBuf.get()) | PPT_SPEC_NEWLINE ) ); + } + pPtr++; + } + sal_Int32 nLen = pPtr - pBuf.get(); + if ( nLen ) + aString = OUString( pBuf.get(), nLen, RTL_TEXTENCODING_MS_1252 ); + } + else + { + // no chars, but potentially char/para props? + sal_uInt32 nCharCount; + bool bTextPropAtom = false; + ReadParaProps( rIn, rTextHeader, aString, rRuler, nCharCount, bTextPropAtom ); + + if ( bTextPropAtom ) + { + // yeah, StyleTextProp is there, read it all & push to + // aParaPropList + PPTCharPropSet aCharPropSet(0); + aCharPropSet.mnOriginalTextPos = 0; + + sal_uInt32 nExtParaFlags = 0, nAnmScheme = 0; + sal_uInt16 nBuBlip = 0xffff, nHasAnm = 0; + ReadCharProps( rIn, aCharPropSet, aString, nCharCount, 0/*nCharReadCnt*/, + bTextPropAtom, nExtParaPos, aStyleTextProp9, nExtParaFlags, + nBuBlip, nHasAnm, nAnmScheme ); + + aCharPropList.push_back(std::make_unique(aCharPropSet, 0)); + } + } + + if ( !aString.isEmpty() ) + { + sal_uInt32 nCharCount; + bool bTextPropAtom = false; + + ReadParaProps( rIn, rTextHeader, aString, rRuler, nCharCount, bTextPropAtom ); + + bool bEmptyParaPossible = true; + sal_uInt32 nCharReadCnt = 0; + sal_uInt32 nCurrentPara = 0; + size_t i = 1; // points to the next element to process + sal_uInt32 nCurrentSpecMarker = aSpecMarkerList.empty() ? 0 : aSpecMarkerList[0]; + sal_uInt32 nStringLen = aString.getLength(); + + while ( nCharReadCnt < nStringLen ) + { + sal_uInt32 nExtParaFlags = 0, nLatestParaUpdate = 0xffffffff, nAnmScheme = 0; + sal_uInt16 nBuBlip = 0xffff, nHasAnm = 0; + + PPTCharPropSet aCharPropSet( nCurrentPara ); + if ( bTextPropAtom ) + { + ReadCharProps( rIn, aCharPropSet, aString, nCharCount, nCharReadCnt, + bTextPropAtom, nExtParaPos, aStyleTextProp9, nExtParaFlags, + nBuBlip, nHasAnm, nAnmScheme ); + if (!rIn.good()) + break; + } + else + nCharCount = nStringLen; + + sal_uInt32 nLen; + while( nCharCount ) + { + if ( nExtParaPos && ( nLatestParaUpdate != nCurrentPara ) && ( nCurrentPara < aParaPropList.size() ) ) + { + PPTParaPropSet* pPropSet = aParaPropList[ nCurrentPara ].get(); + pPropSet->mxParaSet->mnExtParagraphMask = nExtParaFlags; + if ( nExtParaFlags & 0x800000 ) + pPropSet->mxParaSet->mnBuBlip = nBuBlip; + if ( nExtParaFlags & 0x01000000 ) + pPropSet->mxParaSet->mnAnmScheme = nAnmScheme; + if ( nExtParaFlags & 0x02000000 ) + pPropSet->mxParaSet->mnHasAnm = nHasAnm; + nLatestParaUpdate = nCurrentPara; + } + aCharPropSet.mnOriginalTextPos = nCharReadCnt; + if ( nCurrentSpecMarker && ( ( nCurrentSpecMarker & 0xffff ) < ( nCharReadCnt + nCharCount ) ) ) + { + if ( nCurrentSpecMarker & PPT_SPEC_NEWLINE ) + { + nLen = ( nCurrentSpecMarker & 0xffff ) - nCharReadCnt; + if ( nLen ) + aCharPropSet.maString = aString.copy( nCharReadCnt, nLen ); + else if ( bEmptyParaPossible ) + aCharPropSet.maString.clear(); + if ( nLen || bEmptyParaPossible ) + aCharPropList.push_back( + std::make_unique(aCharPropSet, nCurrentPara)); + nCurrentPara++; + nLen++; + nCharReadCnt += nLen; + nCharCount -= nLen; + bEmptyParaPossible = true; + } + else if ( nCurrentSpecMarker & PPT_SPEC_SYMBOL ) + { + if ( ( nCurrentSpecMarker & 0xffff ) != nCharReadCnt ) + { + nLen = ( nCurrentSpecMarker & 0xffff ) - nCharReadCnt; + aCharPropSet.maString = aString.copy(nCharReadCnt, nLen); + aCharPropList.push_back( + std::make_unique(aCharPropSet, nCurrentPara)); + nCharCount -= nLen; + nCharReadCnt += nLen; + } + PPTCharPropSet* pCPropSet = new PPTCharPropSet( aCharPropSet, nCurrentPara ); + pCPropSet->maString = aString.copy(nCharReadCnt, 1); + if ( aCharPropSet.mpImplPPTCharPropSet->mnAttrSet & ( 1 << PPT_CharAttr_Symbol ) ) + pCPropSet->SetFont( aCharPropSet.mpImplPPTCharPropSet->mnSymbolFont ); + aCharPropList.emplace_back( pCPropSet ); + nCharCount--; + nCharReadCnt++; + bEmptyParaPossible = false; + } + nCurrentSpecMarker = ( i < aSpecMarkerList.size() ) ? aSpecMarkerList[ i++ ] : 0; + } + else + { + if (nCharReadCnt > o3tl::make_unsigned(aString.getLength())) + aCharPropSet.maString = OUString(); + else + { + sal_Int32 nStrLen = nCharCount; + sal_Int32 nMaxStrLen = aString.getLength() - nCharReadCnt; + if (nStrLen > nMaxStrLen) + nStrLen = nMaxStrLen; + aCharPropSet.maString = aString.copy(nCharReadCnt, nStrLen); + } + aCharPropList.push_back( + std::make_unique(aCharPropSet, nCurrentPara)); + nCharReadCnt += nCharCount; + bEmptyParaPossible = false; + break; + } + } + } + if ( !aCharPropList.empty() && ( aCharPropList.back()->mnParagraph != nCurrentPara ) ) + { + PPTCharPropSet* pCharPropSet = new PPTCharPropSet( *aCharPropList.back(), nCurrentPara ); + pCharPropSet->maString.clear(); + pCharPropSet->mnOriginalTextPos = nStringLen - 1; + aCharPropList.emplace_back( pCharPropSet ); + } + } + rIn.Seek( nOldPos ); +} + +PPTStyleTextPropReader::~PPTStyleTextPropReader() +{ +} + +PPTPortionObj::PPTPortionObj( const PPTStyleSheet& rStyleSheet, TSS_Type nInstance, sal_uInt32 nDepth ) : + PPTCharPropSet ( 0 ), + mrStyleSheet ( rStyleSheet ), + mnInstance ( nInstance ), + mnDepth ( std::min( nDepth, 4 ) ) +{ +} + +PPTPortionObj::PPTPortionObj( const PPTCharPropSet& rCharPropSet, const PPTStyleSheet& rStyleSheet, TSS_Type nInstance, sal_uInt32 nDepth ) : + PPTCharPropSet ( rCharPropSet ), + mrStyleSheet ( rStyleSheet ), + mnInstance ( nInstance ), + mnDepth ( std::min( nDepth, 4 ) ) +{ +} + +PPTPortionObj::PPTPortionObj( const PPTPortionObj& rPortionObj ) : + PPTCharPropSet ( rPortionObj ), + mrStyleSheet ( rPortionObj.mrStyleSheet ), + mnInstance ( rPortionObj.mnInstance ), + mnDepth ( rPortionObj.mnDepth ) +{ +} + +PPTPortionObj::~PPTPortionObj() +{ +} + +bool PPTPortionObj::GetAttrib( sal_uInt32 nAttr, sal_uInt32& rRetValue, TSS_Type nDestinationInstance ) const +{ + sal_uInt32 nMask = 1 << nAttr; + rRetValue = 0; + + bool bIsHardAttribute = ( ( mpImplPPTCharPropSet->mnAttrSet & nMask ) != 0 ); + + if ( bIsHardAttribute ) + { + switch ( nAttr ) + { + case PPT_CharAttr_Bold : + case PPT_CharAttr_Italic : + case PPT_CharAttr_Underline : + case PPT_CharAttr_Shadow : + case PPT_CharAttr_Strikeout : + case PPT_CharAttr_Embossed : + rRetValue = ( mpImplPPTCharPropSet->mnFlags & nMask ) ? 1 : 0; + break; + case PPT_CharAttr_Font : + rRetValue = mpImplPPTCharPropSet->mnFont; + break; + case PPT_CharAttr_AsianOrComplexFont : + rRetValue = mpImplPPTCharPropSet->mnAsianOrComplexFont; + break; + case PPT_CharAttr_FontHeight : + rRetValue = mpImplPPTCharPropSet->mnFontHeight; + break; + case PPT_CharAttr_FontColor : + rRetValue = mpImplPPTCharPropSet->mnColor; + break; + case PPT_CharAttr_Escapement : + rRetValue = mpImplPPTCharPropSet->mnEscapement; + break; + default : + OSL_FAIL( "SJ:PPTPortionObj::GetAttrib ( hard attribute does not exist )" ); + } + } + else + { + const PPTCharLevel& rCharLevel = mrStyleSheet.mpCharSheet[ mnInstance ]->maCharLevel[ mnDepth ]; + PPTCharLevel* pCharLevel = nullptr; + if ( ( nDestinationInstance == TSS_Type::Unknown ) + || ( mnDepth && ( ( mnInstance == TSS_Type::Subtitle ) || ( mnInstance == TSS_Type::TextInShape ) ) ) ) + bIsHardAttribute = true; + else if ( nDestinationInstance != mnInstance ) + pCharLevel = &mrStyleSheet.mpCharSheet[ nDestinationInstance ]->maCharLevel[ mnDepth ]; + switch( nAttr ) + { + case PPT_CharAttr_Bold : + case PPT_CharAttr_Italic : + case PPT_CharAttr_Underline : + case PPT_CharAttr_Shadow : + case PPT_CharAttr_Strikeout : + case PPT_CharAttr_Embossed : + { + rRetValue = ( rCharLevel.mnFlags & nMask ) ? 1 : 0; + if ( pCharLevel ) + { + sal_uInt32 nTmp = ( pCharLevel->mnFlags & nMask ) ? 1 : 0; + if ( rRetValue != nTmp ) + bIsHardAttribute = true; + } + } + break; + case PPT_CharAttr_Font : + { + rRetValue = rCharLevel.mnFont; + if ( pCharLevel && ( rRetValue != pCharLevel->mnFont ) ) + bIsHardAttribute = true; + } + break; + case PPT_CharAttr_AsianOrComplexFont : + { + rRetValue = rCharLevel.mnAsianOrComplexFont; + if ( pCharLevel && ( rRetValue != pCharLevel->mnAsianOrComplexFont ) ) + bIsHardAttribute = true; + } + break; + case PPT_CharAttr_FontHeight : + { + rRetValue = rCharLevel.mnFontHeight; + if ( pCharLevel && ( rRetValue != pCharLevel->mnFontHeight ) ) + bIsHardAttribute = true; + } + break; + case PPT_CharAttr_FontColor : + { + rRetValue = rCharLevel.mnFontColor; + if ( pCharLevel && ( rRetValue != pCharLevel->mnFontColor ) ) + bIsHardAttribute = true; + } + break; + case PPT_CharAttr_Escapement : + { + rRetValue = rCharLevel.mnEscapement; + if ( pCharLevel && ( rRetValue != pCharLevel->mnEscapement ) ) + bIsHardAttribute = true; + } + break; + default : + OSL_FAIL( "SJ:PPTPortionObj::GetAttrib ( attribute does not exist )" ); + } + } + return bIsHardAttribute; +} + +void PPTPortionObj::ApplyTo( SfxItemSet& rSet, SdrPowerPointImport& rManager, TSS_Type nDestinationInstance ) +{ + ApplyTo( rSet, rManager, nDestinationInstance, nullptr ); +} + +void PPTPortionObj::ApplyTo( SfxItemSet& rSet, SdrPowerPointImport& rManager, TSS_Type nDestinationInstance, const PPTTextObj* pTextObj ) +{ + sal_uInt32 nVal; + if ( GetAttrib( PPT_CharAttr_Bold, nVal, nDestinationInstance ) ) + { + rSet.Put( SvxWeightItem( nVal != 0 ? WEIGHT_BOLD : WEIGHT_NORMAL, EE_CHAR_WEIGHT ) ); + rSet.Put( SvxWeightItem( nVal != 0 ? WEIGHT_BOLD : WEIGHT_NORMAL, EE_CHAR_WEIGHT_CJK ) ); + rSet.Put( SvxWeightItem( nVal != 0 ? WEIGHT_BOLD : WEIGHT_NORMAL, EE_CHAR_WEIGHT_CTL ) ); + } + if ( GetAttrib( PPT_CharAttr_Italic, nVal, nDestinationInstance ) ) + { + rSet.Put( SvxPostureItem( nVal != 0 ? ITALIC_NORMAL : ITALIC_NONE, EE_CHAR_ITALIC ) ); + rSet.Put( SvxPostureItem( nVal != 0 ? ITALIC_NORMAL : ITALIC_NONE, EE_CHAR_ITALIC_CJK ) ); + rSet.Put( SvxPostureItem( nVal != 0 ? ITALIC_NORMAL : ITALIC_NONE, EE_CHAR_ITALIC_CTL ) ); + } + if ( GetAttrib( PPT_CharAttr_Underline, nVal, nDestinationInstance ) ) + rSet.Put( SvxUnderlineItem( nVal != 0 ? LINESTYLE_SINGLE : LINESTYLE_NONE, EE_CHAR_UNDERLINE ) ); + + if ( GetAttrib( PPT_CharAttr_Shadow, nVal, nDestinationInstance ) ) + rSet.Put( SvxShadowedItem( nVal != 0, EE_CHAR_SHADOW ) ); + + if ( GetAttrib( PPT_CharAttr_Strikeout, nVal, nDestinationInstance ) ) + rSet.Put( SvxCrossedOutItem( nVal != 0 ? STRIKEOUT_SINGLE : STRIKEOUT_NONE, EE_CHAR_STRIKEOUT ) ); + + sal_uInt32 nAsianFontId = 0xffff; + if ( GetAttrib( PPT_CharAttr_AsianOrComplexFont, nAsianFontId, nDestinationInstance ) ) + { + if ( nAsianFontId != 0xffff ) + { + const PptFontEntityAtom* pFontEnityAtom = rManager.GetFontEnityAtom( nAsianFontId ); + if ( pFontEnityAtom ) + { + rSet.Put( SvxFontItem( pFontEnityAtom->eFamily, pFontEnityAtom->aName, + OUString(), pFontEnityAtom->ePitch, pFontEnityAtom->eCharSet, EE_CHAR_FONTINFO_CJK ) ); + rSet.Put( SvxFontItem( pFontEnityAtom->eFamily, pFontEnityAtom->aName, + OUString(), pFontEnityAtom->ePitch, pFontEnityAtom->eCharSet, EE_CHAR_FONTINFO_CTL ) ); + } + } + } + if ( GetAttrib( PPT_CharAttr_Font, nVal, nDestinationInstance ) ) + { + const PptFontEntityAtom* pFontEnityAtom = rManager.GetFontEnityAtom( nVal ); + if ( pFontEnityAtom ) + { + rSet.Put( SvxFontItem( pFontEnityAtom->eFamily, pFontEnityAtom->aName, OUString(), pFontEnityAtom->ePitch, pFontEnityAtom->eCharSet, EE_CHAR_FONTINFO ) ); + + // #i119475# bullet font info for CJK and CTL + if ( RTL_TEXTENCODING_SYMBOL == pFontEnityAtom->eCharSet ) + { + rSet.Put( SvxFontItem( pFontEnityAtom->eFamily, pFontEnityAtom->aName, OUString(), pFontEnityAtom->ePitch, pFontEnityAtom->eCharSet, EE_CHAR_FONTINFO_CJK ) ); + rSet.Put( SvxFontItem( pFontEnityAtom->eFamily, pFontEnityAtom->aName, OUString(), pFontEnityAtom->ePitch, pFontEnityAtom->eCharSet, EE_CHAR_FONTINFO_CTL ) ); + } + } + } + if ( GetAttrib( PPT_CharAttr_FontHeight, nVal, nDestinationInstance ) ) // Schriftgrad in Point + { + sal_uInt32 nHeight = rManager.ScalePoint( nVal ); + rSet.Put( SvxFontHeightItem( nHeight, 100, EE_CHAR_FONTHEIGHT ) ); + rSet.Put( SvxFontHeightItem( nHeight, 100, EE_CHAR_FONTHEIGHT_CJK ) ); + rSet.Put( SvxFontHeightItem( nHeight, 100, EE_CHAR_FONTHEIGHT_CTL ) ); + } + + if ( GetAttrib( PPT_CharAttr_Embossed, nVal, nDestinationInstance ) ) + rSet.Put( SvxCharReliefItem( nVal != 0 ? FontRelief::Embossed : FontRelief::NONE, EE_CHAR_RELIEF ) ); + if ( nVal ) /* if Embossed is set, the font color depends to the fillstyle/color of the object, + if the object has no fillstyle, the font color depends to fillstyle of the background */ + { + Color aDefColor( COL_BLACK ); + sal_uInt32 eFillType = mso_fillSolid; + if ( rManager.GetPropertyValue( DFF_Prop_fNoFillHitTest, 0 ) & 0x10 ) + eFillType = rManager.GetPropertyValue(DFF_Prop_fillType, mso_fillSolid); + else + eFillType = mso_fillBackground; + switch( eFillType ) + { + case mso_fillShade : + case mso_fillShadeCenter : + case mso_fillShadeShape : + case mso_fillShadeScale : + case mso_fillShadeTitle : + case mso_fillSolid : + aDefColor = rManager.MSO_CLR_ToColor( rManager.GetPropertyValue( DFF_Prop_fillColor, 0 ) ); + break; + case mso_fillPattern : + aDefColor = rManager.MSO_CLR_ToColor( rManager.GetPropertyValue( DFF_Prop_fillBackColor, 0 ) ); + break; + case mso_fillTexture : + { + Graphic aGraf; + if ( rManager.GetBLIP( rManager.GetPropertyValue( DFF_Prop_fillBlip, 0 ), aGraf ) ) + { + Bitmap aBmp( aGraf.GetBitmapEx().GetBitmap() ); + Size aSize( aBmp.GetSizePixel() ); + if ( aSize.Width() && aSize.Height() ) + { + if ( aSize.Width () > 64 ) + aSize.setWidth( 64 ); + if ( aSize.Height() > 64 ) + aSize.setHeight( 64 ); + + Bitmap::ScopedReadAccess pAcc(aBmp); + if( pAcc ) + { + sal_uLong nRt = 0, nGn = 0, nBl = 0; + const tools::Long nWidth = aSize.Width(); + const tools::Long nHeight = aSize.Height(); + + if( pAcc->HasPalette() ) + { + for( tools::Long nY = 0; nY < nHeight; nY++ ) + { + Scanline pScanline = pAcc->GetScanline( nY ); + for( tools::Long nX = 0; nX < nWidth; nX++ ) + { + const BitmapColor& rCol = pAcc->GetPaletteColor( pAcc->GetIndexFromData( pScanline, nX ) ); + nRt+=rCol.GetRed(); nGn+=rCol.GetGreen(); nBl+=rCol.GetBlue(); + } + } + } + else + { + for( tools::Long nY = 0; nY < nHeight; nY++ ) + { + Scanline pScanline = pAcc->GetScanline( nY ); + for( tools::Long nX = 0; nX < nWidth; nX++ ) + { + const BitmapColor aCol( pAcc->GetPixelFromData( pScanline, nX ) ); + nRt+=aCol.GetRed(); nGn+=aCol.GetGreen(); nBl+=aCol.GetBlue(); + } + } + } + pAcc.reset(); + sal_uInt32 nC = aSize.Width() * aSize.Height(); + nRt /= nC; + nGn /= nC; + nBl /= nC; + aDefColor = Color(sal_uInt8( nRt ), sal_uInt8( nGn ),sal_uInt8( nBl ) ); + } + } + } + } + break; + case mso_fillBackground : + { + if ( pTextObj ) // the textobject is needed + { + const SfxItemSet* pItemSet = pTextObj->GetBackground(); + if ( pItemSet ) + { + const XFillStyleItem* pFillStyleItem = pItemSet->GetItemIfSet( XATTR_FILLSTYLE, false ); + if ( pFillStyleItem ) + { + drawing::FillStyle eFillStyle = pFillStyleItem->GetValue(); + switch( eFillStyle ) + { + case drawing::FillStyle_SOLID : + { + const XColorItem* pFillColorItem = pItemSet->GetItemIfSet( XATTR_FILLCOLOR, false ); + if ( pFillColorItem ) + aDefColor = pFillColorItem->GetColorValue(); + } + break; + case drawing::FillStyle_GRADIENT : + { + const XFillGradientItem* pGradientItem = pItemSet->GetItemIfSet( XATTR_FILLGRADIENT, false ); + if ( pGradientItem ) + aDefColor = pGradientItem->GetGradientValue().GetStartColor(); + } + break; + case drawing::FillStyle_HATCH : + case drawing::FillStyle_BITMAP : + aDefColor = COL_WHITE; + break; + default: break; + } + } + } + } + } + break; + default: break; + } + rSet.Put( SvxColorItem( aDefColor, EE_CHAR_COLOR ) ); + } + else + { + if ( GetAttrib( PPT_CharAttr_FontColor, nVal, nDestinationInstance ) ) // text color (4Byte-Arg) + { + Color aCol( rManager.MSO_TEXT_CLR_ToColor( nVal ) ); + rSet.Put( SvxColorItem( aCol, EE_CHAR_COLOR ) ); + if ( nDestinationInstance == TSS_Type::Unknown ) + mrStyleSheet.mpCharSheet[ mnInstance ]->maCharLevel[ mnDepth ].mnFontColorInStyleSheet = aCol; + } + else if ( nVal & 0x0f000000 ) // this is not a hard attribute, but maybe the page has a different colorscheme, + { // so that in this case we must use a hard color attribute + Color aCol( rManager.MSO_TEXT_CLR_ToColor( nVal ) ); + Color& aColorInSheet = mrStyleSheet.mpCharSheet[ mnInstance ]->maCharLevel[ mnDepth ].mnFontColorInStyleSheet; + if ( aColorInSheet != aCol ) + rSet.Put( SvxColorItem( aCol, EE_CHAR_COLOR ) ); + } + } + + if ( GetAttrib( PPT_CharAttr_Escapement, nVal, nDestinationInstance ) ) // super-/subscript in % + { + sal_uInt16 nEsc = 0; + sal_uInt8 nProp = 100; + + if ( nVal ) + { + nEsc = static_cast(nVal); + nProp = DFLT_ESC_PROP; + } + SvxEscapementItem aItem( nEsc, nProp, EE_CHAR_ESCAPEMENT ); + rSet.Put( aItem ); + } + if ( mnLanguage[ 0 ] ) + rSet.Put( SvxLanguageItem( mnLanguage[ 0 ], EE_CHAR_LANGUAGE ) ); + if ( mnLanguage[ 1 ] ) + rSet.Put( SvxLanguageItem( mnLanguage[ 1 ], EE_CHAR_LANGUAGE_CJK ) ); + if ( mnLanguage[ 2 ] ) + rSet.Put( SvxLanguageItem( mnLanguage[ 2 ], EE_CHAR_LANGUAGE_CTL ) ); +} + +SvxFieldItem* PPTPortionObj::GetTextField() +{ + if ( mpFieldItem ) + return new SvxFieldItem( *mpFieldItem ); + return nullptr; +} + +namespace +{ + sal_uInt16 sanitizeForMaxPPTLevels(sal_uInt16 nDepth) + { + if (nDepth >= nMaxPPTLevels) + { + SAL_WARN("filter.ms", "Para Style Sheet depth " << nDepth << " but " << nMaxPPTLevels - 1 << " is max possible"); + nDepth = nMaxPPTLevels - 1; + } + return nDepth; + } +} + +PPTParagraphObj::PPTParagraphObj( const PPTStyleSheet& rStyleSheet, TSS_Type nInstance, sal_uInt16 nDepth ) : + PPTNumberFormatCreator ( nullptr ), + mrStyleSheet ( rStyleSheet ), + mnInstance ( nInstance ), + mnCurrentObject ( 0 ) +{ + mxParaSet->mnDepth = sanitizeForMaxPPTLevels(nDepth); +} + +PPTParagraphObj::PPTParagraphObj( PPTStyleTextPropReader& rPropReader, + size_t const nCurParaPos, size_t& rnCurCharPos, + const PPTStyleSheet& rStyleSheet, + TSS_Type nInstance, PPTTextRulerInterpreter const & rRuler ) : + PPTParaPropSet ( *rPropReader.aParaPropList[nCurParaPos] ), + PPTNumberFormatCreator ( nullptr ), + PPTTextRulerInterpreter ( rRuler ), + mrStyleSheet ( rStyleSheet ), + mnInstance ( nInstance ), + mnCurrentObject ( 0 ) +{ + if (rnCurCharPos >= rPropReader.aCharPropList.size()) + return; + + sal_uInt32 const nCurrentParagraph = + rPropReader.aCharPropList[rnCurCharPos]->mnParagraph; + for (; rnCurCharPos < rPropReader.aCharPropList.size() && + rPropReader.aCharPropList[rnCurCharPos]->mnParagraph == nCurrentParagraph; + ++rnCurCharPos) + { + PPTCharPropSet *const pCharPropSet = + rPropReader.aCharPropList[rnCurCharPos].get(); + std::unique_ptr pPPTPortion(new PPTPortionObj( + *pCharPropSet, rStyleSheet, nInstance, mxParaSet->mnDepth)); + m_PortionList.push_back(std::move(pPPTPortion)); + } +} + +PPTParagraphObj::~PPTParagraphObj() +{ +} + +void PPTParagraphObj::AppendPortion( PPTPortionObj& rPPTPortion ) +{ + m_PortionList.push_back( + std::make_unique(rPPTPortion)); +} + +void PPTParagraphObj::UpdateBulletRelSize( sal_uInt32& nBulletRelSize ) const +{ + if ( nBulletRelSize <= 0x7fff ) // a negative value is the absolute bullet height + return; + + sal_uInt16 nFontHeight = 0; + if (!m_PortionList.empty()) + { + PPTPortionObj const& rPortion = *m_PortionList.front(); + if (rPortion.mpImplPPTCharPropSet->mnAttrSet & (1 << PPT_CharAttr_FontHeight)) + { + nFontHeight = rPortion.mpImplPPTCharPropSet->mnFontHeight; + } + } + // if we do not have a hard attributed fontheight, the fontheight is taken from the style + if ( !nFontHeight ) + { + nFontHeight = mrStyleSheet.mpCharSheet[ mnInstance ]->maCharLevel[sanitizeForMaxPPTLevels(mxParaSet->mnDepth)].mnFontHeight; + } + nBulletRelSize = nFontHeight ? ((- static_cast(nBulletRelSize)) * 100 ) / nFontHeight : 100; +} + +bool PPTParagraphObj::GetAttrib( sal_uInt32 nAttr, sal_uInt32& rRetValue, TSS_Type nDestinationInstance ) +{ + sal_uInt32 nMask = 1 << nAttr; + rRetValue = 0; + + if ( nAttr > 21 ) + { + OSL_FAIL( "SJ:PPTParagraphObj::GetAttrib - attribute does not exist" ); + return false; + } + + bool bIsHardAttribute = ( ( mxParaSet->mnAttrSet & nMask ) != 0 ); + + sal_uInt16 nDepth = sanitizeForMaxPPTLevels(mxParaSet->mnDepth); + + if ( bIsHardAttribute ) + { + if ( nAttr == PPT_ParaAttr_BulletColor ) + { + bool bHardBulletColor; + if ( mxParaSet->mnAttrSet & ( 1 << PPT_ParaAttr_BuHardColor ) ) + bHardBulletColor = mxParaSet->mpArry[ PPT_ParaAttr_BuHardColor ] != 0; + else + bHardBulletColor = ( mrStyleSheet.mpParaSheet[ mnInstance ]->maParaLevel[nDepth].mnBuFlags + & ( 1 << PPT_ParaAttr_BuHardColor ) ) != 0; + if ( bHardBulletColor ) + rRetValue = mxParaSet->mnBulletColor; + else + { + rRetValue = PPT_COLSCHEME_TEXT_UND_ZEILEN; + if ((nDestinationInstance != TSS_Type::Unknown) && !m_PortionList.empty()) + { + PPTPortionObj const& rPortion = *m_PortionList.front(); + if (rPortion.mpImplPPTCharPropSet->mnAttrSet & (1 << PPT_CharAttr_FontColor)) + { + rRetValue = rPortion.mpImplPPTCharPropSet->mnColor; + } + else + { + rRetValue = mrStyleSheet.mpCharSheet[ nDestinationInstance ]->maCharLevel[nDepth].mnFontColor; + } + } + } + } + else if ( nAttr == PPT_ParaAttr_BulletFont ) + { + bool bHardBuFont; + if ( mxParaSet->mnAttrSet & ( 1 << PPT_ParaAttr_BuHardFont ) ) + bHardBuFont = mxParaSet->mpArry[ PPT_ParaAttr_BuHardFont ] != 0; + else + bHardBuFont = ( mrStyleSheet.mpParaSheet[ mnInstance ]->maParaLevel[nDepth].mnBuFlags + & ( 1 << PPT_ParaAttr_BuHardFont ) ) != 0; + if ( bHardBuFont ) + rRetValue = mxParaSet->mpArry[ PPT_ParaAttr_BulletFont ]; + else + { + // it is the font used which assigned to the first character of the following text + rRetValue = 0; + if ((nDestinationInstance != TSS_Type::Unknown) && !m_PortionList.empty()) + { + PPTPortionObj const& rPortion = *m_PortionList.front(); + if (rPortion.mpImplPPTCharPropSet->mnAttrSet & ( 1 << PPT_CharAttr_Font ) ) + { + rRetValue = rPortion.mpImplPPTCharPropSet->mnFont; + } + else + { + rRetValue = mrStyleSheet.mpCharSheet[ nDestinationInstance ]->maCharLevel[nDepth].mnFont; + } + } + } + } + else + rRetValue = mxParaSet->mpArry[ nAttr ]; + } + else + { + const PPTParaLevel& rParaLevel = mrStyleSheet.mpParaSheet[ mnInstance ]->maParaLevel[nDepth]; + + PPTParaLevel* pParaLevel = nullptr; + if ( ( nDestinationInstance == TSS_Type::Unknown ) + || ( nDepth && ( ( mnInstance == TSS_Type::Subtitle ) || ( mnInstance == TSS_Type::TextInShape ) ) ) ) + bIsHardAttribute = true; + else if ( nDestinationInstance != mnInstance ) + pParaLevel = &mrStyleSheet.mpParaSheet[ nDestinationInstance ]->maParaLevel[nDepth]; + switch ( nAttr ) + { + case PPT_ParaAttr_BulletOn : + { + rRetValue = rParaLevel.mnBuFlags & ( 1 << PPT_ParaAttr_BulletOn ); + if ( pParaLevel ) + { + if ( rRetValue != ( static_cast(pParaLevel->mnBuFlags) & ( 1 << PPT_ParaAttr_BulletOn ) ) ) + bIsHardAttribute = true; + } + } + break; + case PPT_ParaAttr_BuHardFont : + case PPT_ParaAttr_BuHardColor : + case PPT_ParaAttr_BuHardHeight : + OSL_FAIL( "SJ:PPTParagraphObj::GetAttrib - this attribute does not make sense" ); + break; + case PPT_ParaAttr_BulletChar : + { + rRetValue = rParaLevel.mnBulletChar; + if ( pParaLevel && ( rRetValue != pParaLevel->mnBulletChar ) ) + bIsHardAttribute = true; + } + break; + case PPT_ParaAttr_BulletFont : + { + bool bHardBuFont; + if ( mxParaSet->mnAttrSet & ( 1 << PPT_ParaAttr_BuHardFont ) ) + bHardBuFont = mxParaSet->mpArry[ PPT_ParaAttr_BuHardFont ] != 0; + else + bHardBuFont = ( rParaLevel.mnBuFlags & ( 1 << PPT_ParaAttr_BuHardFont ) ) != 0; + if ( bHardBuFont ) + { + rRetValue = rParaLevel.mnBulletFont; + if ( pParaLevel && ( rRetValue != pParaLevel->mnBulletFont ) ) + bIsHardAttribute = true; + } + else + { + if (!m_PortionList.empty()) + { + PPTPortionObj const& rPortion = *m_PortionList.front(); + bIsHardAttribute = rPortion.GetAttrib( + PPT_CharAttr_Font, rRetValue, nDestinationInstance); + } + else + { + rRetValue = mrStyleSheet.mpCharSheet[ mnInstance ]->maCharLevel[nDepth].mnFont; + bIsHardAttribute = true; + } + } + } + break; + case PPT_ParaAttr_BulletHeight : + { + rRetValue = rParaLevel.mnBulletHeight; + if ( pParaLevel && ( rRetValue != pParaLevel->mnBulletHeight ) ) + bIsHardAttribute = true; + } + break; + case PPT_ParaAttr_BulletColor : + { + bool bHardBulletColor; + if ( mxParaSet->mnAttrSet & ( 1 << PPT_ParaAttr_BuHardColor ) ) + bHardBulletColor = mxParaSet->mpArry[ PPT_ParaAttr_BuHardColor ] != 0; + else + bHardBulletColor = ( rParaLevel.mnBuFlags & ( 1 << PPT_ParaAttr_BuHardColor ) ) != 0; + if ( bHardBulletColor ) + { + rRetValue = rParaLevel.mnBulletColor; + if ( pParaLevel && ( rRetValue != pParaLevel->mnBulletColor ) ) + bIsHardAttribute = true; + } + else + { + if (!m_PortionList.empty()) + { + PPTPortionObj const& rPortion = *m_PortionList.front(); + if (rPortion.mbIsHyperlink ) + { + if( rPortion.mbHardHylinkOrigColor ) + rRetValue = rPortion.mnHylinkOrigColor; + else + rRetValue = mrStyleSheet.mpCharSheet[ mnInstance ]->maCharLevel[nDepth].mnFontColor; + bIsHardAttribute = true; + } + else + { + bIsHardAttribute = rPortion.GetAttrib( PPT_CharAttr_FontColor, rRetValue, nDestinationInstance ); + } + } + else + { + rRetValue = mrStyleSheet.mpCharSheet[ mnInstance ]->maCharLevel[nDepth].mnFontColor; + bIsHardAttribute = true; + } + } + } + break; + case PPT_ParaAttr_Adjust : + { + rRetValue = rParaLevel.mnAdjust; + if ( pParaLevel && ( rRetValue != pParaLevel->mnAdjust ) ) + bIsHardAttribute = true; + } + break; + case PPT_ParaAttr_LineFeed : + { + rRetValue = rParaLevel.mnLineFeed; + if ( pParaLevel && ( rRetValue != pParaLevel->mnLineFeed ) ) + bIsHardAttribute = true; + } + break; + case PPT_ParaAttr_UpperDist : + { + rRetValue = rParaLevel.mnUpperDist; + if ( pParaLevel && ( rRetValue != pParaLevel->mnUpperDist ) ) + bIsHardAttribute = true; + } + break; + case PPT_ParaAttr_LowerDist : + { + rRetValue = rParaLevel.mnLowerDist; + if ( pParaLevel && ( rRetValue != pParaLevel->mnLowerDist ) ) + bIsHardAttribute = true; + } + break; + case PPT_ParaAttr_TextOfs : + { + rRetValue = rParaLevel.mnTextOfs; + if ( pParaLevel && ( rRetValue != pParaLevel->mnTextOfs ) ) + bIsHardAttribute = true; + } + break; + case PPT_ParaAttr_BulletOfs : + { + rRetValue = rParaLevel.mnBulletOfs; + if ( pParaLevel && ( rRetValue != pParaLevel->mnBulletOfs ) ) + bIsHardAttribute = true; + } + break; + case PPT_ParaAttr_DefaultTab : + { + rRetValue = rParaLevel.mnDefaultTab; + if ( pParaLevel && ( rRetValue != pParaLevel->mnDefaultTab ) ) + bIsHardAttribute = true; + } + break; + case PPT_ParaAttr_AsianLB_1 : + { + rRetValue = rParaLevel.mnAsianLineBreak & 1; + if ( pParaLevel && ( rRetValue != ( static_cast(pParaLevel->mnAsianLineBreak) & 1 ) ) ) + bIsHardAttribute = true; + } + break; + case PPT_ParaAttr_AsianLB_2 : + { + rRetValue = ( rParaLevel.mnAsianLineBreak >> 1 ) & 1; + if ( pParaLevel && ( rRetValue != ( ( static_cast(pParaLevel->mnAsianLineBreak) >> 1 ) & 1 ) ) ) + bIsHardAttribute = true; + } + break; + case PPT_ParaAttr_AsianLB_3 : + { + rRetValue = ( rParaLevel.mnAsianLineBreak >> 2 ) & 1; + if ( pParaLevel && ( rRetValue != ( ( static_cast(pParaLevel->mnAsianLineBreak) >> 2 ) & 1 ) ) ) + bIsHardAttribute = true; + } + break; + case PPT_ParaAttr_BiDi : + { + rRetValue = rParaLevel.mnBiDi; + if ( pParaLevel && ( rRetValue != pParaLevel->mnBiDi ) ) + bIsHardAttribute = true; + } + break; + } + } + return bIsHardAttribute; +} + +void PPTParagraphObj::ApplyTo( SfxItemSet& rSet, std::optional< sal_Int16 >& rStartNumbering, SdrPowerPointImport const & rManager, TSS_Type nDestinationInstance ) +{ + sal_Int16 nVal2; + sal_uInt32 nVal, nUpperDist, nLowerDist; + TSS_Type nInstance = nDestinationInstance != TSS_Type::Unknown ? nDestinationInstance : mnInstance; + + if ( ( nDestinationInstance != TSS_Type::Unknown ) || ( mxParaSet->mnDepth <= 1 ) ) + { + SvxNumBulletItem* pNumBulletItem = mrStyleSheet.mpNumBulletItem[ nInstance ].get(); + if ( pNumBulletItem ) + { + SvxNumberFormat aNumberFormat( SVX_NUM_NUMBER_NONE ); + if ( GetNumberFormat( rManager, aNumberFormat, this, nDestinationInstance, rStartNumbering ) ) + { + if ( aNumberFormat.GetNumberingType() == SVX_NUM_NUMBER_NONE ) + { + aNumberFormat.SetAbsLSpace( 0 ); + aNumberFormat.SetFirstLineOffset( 0 ); + aNumberFormat.SetCharTextDistance( 0 ); + aNumberFormat.SetFirstLineIndent( 0 ); + aNumberFormat.SetIndentAt( 0 ); + } + SvxNumBulletItem aNewNumBulletItem( *pNumBulletItem ); + SvxNumRule& rRule = aNewNumBulletItem.GetNumRule(); + rRule.SetLevel( mxParaSet->mnDepth, aNumberFormat ); + for (sal_uInt16 i = 0; i < rRule.GetLevelCount(); ++i) + { + if ( i != mxParaSet->mnDepth ) + { + sal_uInt16 n = sanitizeForMaxPPTLevels(i); + + SvxNumberFormat aNumberFormat2( rRule.GetLevel( i ) ); + const PPTParaLevel& rParaLevel = mrStyleSheet.mpParaSheet[ nInstance ]->maParaLevel[ n ]; + const PPTCharLevel& rCharLevel = mrStyleSheet.mpCharSheet[ nInstance ]->maCharLevel[ n ]; + sal_uInt32 nColor; + if ( rParaLevel.mnBuFlags & ( 1 << PPT_ParaAttr_BuHardColor ) ) + nColor = rParaLevel.mnBulletColor; + else + nColor = rCharLevel.mnFontColor; + aNumberFormat2.SetBulletColor( rManager.MSO_TEXT_CLR_ToColor( nColor ) ); + rRule.SetLevel( i, aNumberFormat2 ); + } + } + rSet.Put( aNewNumBulletItem ); + } + } + } + + sal_uInt32 nIsBullet2, _nTextOfs, _nBulletOfs; + GetAttrib(PPT_ParaAttr_BulletOn, nIsBullet2, nDestinationInstance); + GetAttrib(PPT_ParaAttr_TextOfs, _nTextOfs, nDestinationInstance); + GetAttrib(PPT_ParaAttr_BulletOfs, _nBulletOfs, nDestinationInstance); + if ( !nIsBullet2 ) + { + SvxLRSpaceItem aLRSpaceItem( EE_PARA_LRSPACE ); + auto const nAbsLSpace = convertMasterUnitToMm100(_nTextOfs); + auto const nFirstLineOffset = nAbsLSpace - convertMasterUnitToMm100(_nBulletOfs); + aLRSpaceItem.SetLeft( nAbsLSpace ); + aLRSpaceItem.SetTextFirstLineOffsetValue( -nFirstLineOffset ); + rSet.Put( aLRSpaceItem ); + } + else + { + SvxLRSpaceItem aLRSpaceItem( EE_PARA_LRSPACE ); + aLRSpaceItem.SetLeft( 0 ); + aLRSpaceItem.SetTextFirstLineOffsetValue( 0 ); + rSet.Put( aLRSpaceItem ); + } + if ( GetAttrib( PPT_ParaAttr_Adjust, nVal, nDestinationInstance ) ) + { + if ( nVal <= 3 ) + { // paragraph adjustment + static SvxAdjust const aAdj[ 4 ] = { SvxAdjust::Left, SvxAdjust::Center, SvxAdjust::Right, SvxAdjust::Block }; + rSet.Put( SvxAdjustItem( aAdj[ nVal ], EE_PARA_JUST ) ); + } + } + + if ( GetAttrib( PPT_ParaAttr_AsianLB_1, nVal, nDestinationInstance ) ) + rSet.Put(SvxForbiddenRuleItem(nVal != 0, EE_PARA_FORBIDDENRULES)); + if ( GetAttrib( PPT_ParaAttr_AsianLB_3, nVal, nDestinationInstance ) ) + rSet.Put(SvxHangingPunctuationItem(nVal != 0, EE_PARA_HANGINGPUNCTUATION)); + + if ( GetAttrib( PPT_ParaAttr_BiDi, nVal, nDestinationInstance ) ) + rSet.Put( SvxFrameDirectionItem( nVal == 1 ? SvxFrameDirection::Horizontal_RL_TB : SvxFrameDirection::Horizontal_LR_TB, EE_PARA_WRITINGDIR ) ); + + // LineSpacing + PPTPortionObj* pPortion = First(); + bool bIsHardAttribute = GetAttrib( PPT_ParaAttr_LineFeed, nVal, nDestinationInstance ); + nVal2 = static_cast(nVal); + sal_uInt32 nFont = sal_uInt32(); + if ( pPortion && pPortion->GetAttrib( PPT_CharAttr_Font, nFont, nDestinationInstance ) ) + bIsHardAttribute = true; + + if ( bIsHardAttribute ) + { + SdrTextFixedCellHeightItem aHeightItem(true); + aHeightItem.SetWhich(SDRATTR_TEXT_USEFIXEDCELLHEIGHT); + rSet.Put( aHeightItem ); + SvxLineSpacingItem aItem( 200, EE_PARA_SBL ); + if ( nVal2 <= 0 ) { + aItem.SetLineHeight( static_cast( rManager.ScalePoint( -nVal2 ) / 8 ) ); + aItem.SetLineSpaceRule( SvxLineSpaceRule::Fix ); + aItem.SetInterLineSpaceRule(SvxInterLineSpaceRule::Off); + } else + { + sal_uInt16 nPropLineSpace = static_cast(nVal2); + aItem.SetPropLineSpace( nPropLineSpace ); + aItem.SetLineSpaceRule( SvxLineSpaceRule::Auto ); + } + rSet.Put( aItem ); + } + + // Paragraph Spacing + bIsHardAttribute = ( static_cast(GetAttrib( PPT_ParaAttr_UpperDist, nUpperDist, nDestinationInstance )) + + static_cast(GetAttrib( PPT_ParaAttr_LowerDist, nLowerDist, nDestinationInstance )) ) != 0; + if ( ( nUpperDist > 0 ) || ( nLowerDist > 0 ) ) + { + if (!m_PortionList.empty()) + { + sal_uInt32 nFontHeight = 0; + m_PortionList.back()->GetAttrib( + PPT_CharAttr_FontHeight, nFontHeight, nDestinationInstance); + if ( static_cast(nUpperDist) > 0 ) + nUpperDist = - static_cast( ( nFontHeight * nUpperDist * 100 ) / 1000 ); + if ( static_cast(nLowerDist) > 0 ) + nLowerDist = - static_cast( ( nFontHeight * nLowerDist * 100 ) / 1000 ); + } + bIsHardAttribute = true; + } + if ( bIsHardAttribute ) + { + SvxULSpaceItem aULSpaceItem( EE_PARA_ULSPACE ); + nVal2 = static_cast(nUpperDist); + if ( nVal2 <= 0 ) + aULSpaceItem.SetUpper(static_cast(convertMasterUnitToMm100(-nVal2))); + else + { + aULSpaceItem.SetUpperValue( 0 ); + aULSpaceItem.SetPropUpper( static_cast(nUpperDist) == 100 ? 101 : static_cast(nUpperDist) ); + } + nVal2 = static_cast(nLowerDist); + if ( nVal2 <= 0 ) + aULSpaceItem.SetLower(static_cast(convertMasterUnitToMm100(-nVal2))); + else + { + aULSpaceItem.SetLowerValue( 0 ); + aULSpaceItem.SetPropLower( static_cast(nLowerDist) == 100 ? 101 : static_cast(nLowerDist) ); + } + rSet.Put( aULSpaceItem ); + } + + sal_uInt32 i, nDefaultTab, nTab, nTextOfs2 = 0; + sal_uInt32 nLatestManTab = 0; + GetAttrib( PPT_ParaAttr_TextOfs, nTextOfs2, nDestinationInstance ); + GetAttrib( PPT_ParaAttr_BulletOfs, nTab, nDestinationInstance ); + GetAttrib( PPT_ParaAttr_DefaultTab, nDefaultTab, nDestinationInstance ); + + SvxTabStopItem aTabItem( 0, 0, SvxTabAdjust::Default, EE_PARA_TABS ); + if ( GetTabCount() ) + { + for ( i = 0; i < GetTabCount(); i++ ) + { + SvxTabAdjust eTabAdjust; + nTab = GetTabOffsetByIndex( static_cast(i) ); + switch( GetTabStyleByIndex( static_cast(i) ) ) + { + case 1 : eTabAdjust = SvxTabAdjust::Center; break; + case 2 : eTabAdjust = SvxTabAdjust::Right; break; + case 3 : eTabAdjust = SvxTabAdjust::Decimal; break; + default : eTabAdjust = SvxTabAdjust::Left; + } + aTabItem.Insert(SvxTabStop(convertMasterUnitToMm100(nTab), eTabAdjust)); + } + nLatestManTab = nTab; + } + if ( nIsBullet2 == 0 ) + aTabItem.Insert( SvxTabStop( sal_uInt16(0) ) ); + if ( nDefaultTab ) + { + nTab = std::max( nTextOfs2, nLatestManTab ); + nTab /= nDefaultTab; + nTab = nDefaultTab * ( 1 + nTab ); + for ( i = 0; ( i < 20 ) && ( nTab < 0x1b00 ); i++ ) + { + aTabItem.Insert( SvxTabStop( convertMasterUnitToMm100(nTab))); + nTab += nDefaultTab; + } + } + rSet.Put( aTabItem ); +} + +sal_uInt32 PPTParagraphObj::GetTextSize() +{ + sal_uInt32 nCount, nRetValue = 0; + for (const std::unique_ptr & i : m_PortionList) + { + PPTPortionObj const& rPortionObj = *i; + nCount = rPortionObj.Count(); + if ((!nCount) && rPortionObj.mpFieldItem) + nCount++; + nRetValue += nCount; + } + return nRetValue; +} + +PPTPortionObj* PPTParagraphObj::First() +{ + mnCurrentObject = 0; + if (m_PortionList.empty()) + return nullptr; + return m_PortionList.front().get(); +} + +PPTPortionObj* PPTParagraphObj::Next() +{ + sal_uInt32 i = mnCurrentObject + 1; + if (i >= m_PortionList.size()) + return nullptr; + mnCurrentObject++; + return m_PortionList[i].get(); +} + +PPTFieldEntry::~PPTFieldEntry() +{ +} + +void PPTFieldEntry::GetDateTime( const sal_uInt32 nVal, SvxDateFormat& eDateFormat, SvxTimeFormat& eTimeFormat ) +{ + eDateFormat = SvxDateFormat::AppDefault; + eTimeFormat = SvxTimeFormat::AppDefault; + // evaluate ID + switch( nVal ) + { + case 0: + case 6: + eDateFormat = SvxDateFormat::A; + break; + case 1: + eDateFormat = SvxDateFormat::F; + break; + case 2: + case 3: + eDateFormat = SvxDateFormat::D; + break; + case 4: + case 5: + eDateFormat = SvxDateFormat::C; + break; + case 7: + eDateFormat = SvxDateFormat::A; + [[fallthrough]]; + case 9: + eTimeFormat = SvxTimeFormat::HH24_MM; + break; + case 8: + eDateFormat = SvxDateFormat::A; + [[fallthrough]]; + case 11: + eTimeFormat = SvxTimeFormat::HH12_MM; + break; + case 10: + eTimeFormat = SvxTimeFormat::HH24_MM_SS; + break; + case 12: + eTimeFormat = SvxTimeFormat::HH12_MM_SS; + break; + } +} + +void PPTFieldEntry::SetDateTime( sal_uInt32 nVal ) +{ + SvxDateFormat eDateFormat; + SvxTimeFormat eTimeFormat; + GetDateTime( nVal, eDateFormat, eTimeFormat ); + if ( eDateFormat != SvxDateFormat::AppDefault ) + xField1.reset(new SvxFieldItem(SvxDateField( Date( Date::SYSTEM ), SvxDateType::Var, eDateFormat ), EE_FEATURE_FIELD)); + if ( eTimeFormat != SvxTimeFormat::AppDefault ) + { + std::unique_ptr xFieldItem(new SvxFieldItem(SvxExtTimeField( tools::Time( tools::Time::SYSTEM ), SvxTimeType::Var, eTimeFormat ), EE_FEATURE_FIELD)); + if (xField1) + xField2 = std::move(xFieldItem); + else + xField1 = std::move(xFieldItem); + } +} + +PPTTextObj::PPTTextObj( SvStream& rIn, SdrPowerPointImport& rSdrPowerPointImport, PptSlidePersistEntry& rPersistEntry, DffObjData const * pObjData ) : + mxImplTextObj ( new ImplPPTTextObj( rPersistEntry ) ) +{ + mxImplTextObj->mnShapeId = 0; + mxImplTextObj->mnShapeMaster = 0; + mxImplTextObj->mnDestinationInstance = mxImplTextObj->mnInstance = TSS_Type::TextInShape; + mxImplTextObj->mnCurrentObject = 0; + mxImplTextObj->mnParagraphCount = 0; + mxImplTextObj->mnTextFlags = 0; + mxImplTextObj->meShapeType = ( pObjData && pObjData->bShapeType ) ? pObjData->eShapeType : mso_sptMin; + + DffRecordHeader aExtParaHd; + aExtParaHd.nRecType = 0; // set empty + + + DffRecordHeader aShapeContainerHd; + ReadDffRecordHeader( rIn, aShapeContainerHd ); + + if ( !(( pObjData == nullptr ) || ( pObjData->bShapeType )) ) + return; + + PPTExtParaProv* pExtParaProv = rSdrPowerPointImport.m_pPPTStyleSheet->pExtParaProv.get(); + if ( pObjData ) + { + mxImplTextObj->mnShapeId = pObjData->nShapeId; + if ( pObjData->nSpFlags & ShapeFlag::HaveMaster ) + mxImplTextObj->mnShapeMaster = rSdrPowerPointImport.GetPropertyValue( DFF_Prop_hspMaster, 0 ); + } + // ClientData + if ( rSdrPowerPointImport.maShapeRecords.SeekToContent( rIn, DFF_msofbtClientData, SEEK_FROM_CURRENT_AND_RESTART ) ) + { + sal_uInt32 nOldPos = rIn.Tell(); + DffRecordHeader& aClientDataContainerHd = *rSdrPowerPointImport.maShapeRecords.Current(); + DffRecordHeader aPlaceHolderAtomHd; + if ( SvxMSDffManager::SeekToRec( rIn, PPT_PST_OEPlaceholderAtom, aClientDataContainerHd.GetRecEndFilePos(), &aPlaceHolderAtomHd ) ) + { + mxImplTextObj->mpPlaceHolderAtom.reset( new PptOEPlaceholderAtom ); + ReadPptOEPlaceholderAtom( rIn, *( mxImplTextObj->mpPlaceHolderAtom ) ); + } + rIn.Seek( nOldPos ); + DffRecordHeader aProgTagHd; + if ( SdrPowerPointImport::SeekToContentOfProgTag( 9, rIn, aClientDataContainerHd, aProgTagHd ) ) + { + ReadDffRecordHeader( rIn, aExtParaHd ); + } + } + + // ClientTextBox + if ( !rSdrPowerPointImport.maShapeRecords.SeekToContent( rIn, DFF_msofbtClientTextbox, SEEK_FROM_CURRENT_AND_RESTART ) ) + return; + + bool bStatus = true; + + + DffRecordHeader aClientTextBoxHd( *rSdrPowerPointImport.maShapeRecords.Current() ); + sal_uInt32 nTextRulerAtomOfs = 0; // case of zero -> this atom may be found in aClientDataContainerHd; + // case of -1 -> there is no atom of this kind + // else -> this is the fileofs where we can get it + + // checkout if this is a referenced + // textobj, if so the we will patch + // the ClientTextBoxHd for a + // equivalent one + DffRecordHeader aTextHd; + if ( SvxMSDffManager::SeekToRec( rIn, PPT_PST_OutlineTextRefAtom, aClientTextBoxHd.GetRecEndFilePos(), &aTextHd ) ) + { + sal_uInt32 nRefNum; + rIn.ReadUInt32( nRefNum ); + + if ( SvxMSDffManager::SeekToRec( rIn, PPT_PST_TextRulerAtom, aClientTextBoxHd.GetRecEndFilePos() ) ) + nTextRulerAtomOfs = rIn.Tell(); + else + nTextRulerAtomOfs = 0xffffffff; + + switch( rSdrPowerPointImport.m_eCurrentPageKind ) + { + case PPT_NOTEPAGE : + case PPT_MASTERPAGE : + case PPT_SLIDEPAGE : + break; + default : + bStatus = false; + } + if ( bStatus ) + { + sal_uInt32 nSlideId = rSdrPowerPointImport.GetCurrentPageId(); + if ( !nSlideId ) + bStatus = false; + else + { + if ( !aExtParaHd.nRecType ) + { + sal_uInt32 nOldPos = rIn.Tell(); + // try to locate the referenced ExtendedParaHd + DffRecordHeader* pHd = pExtParaProv-> + aExtendedPresRules.GetRecordHeader( PPT_PST_ExtendedParagraphHeaderAtom, + SEEK_FROM_CURRENT_AND_RESTART ); + DffRecordHeader aPresRuleHd; + DffRecordHeader* pFirst = pHd; + + while ( pHd ) + { + pHd->SeekToContent( rIn ); + sal_uInt32 nTmpSlideId(0), nTmpRef; + rIn.ReadUInt32( nTmpSlideId ) + .ReadUInt32( nTmpRef ); // this seems to be the instance + + if ( ( nTmpSlideId == nSlideId ) && ( pHd->nRecInstance == nRefNum ) ) + { + if (!pHd->SeekToEndOfRecord(rIn)) + break; + ReadDffRecordHeader( rIn, aPresRuleHd ); + if ( aPresRuleHd.nRecType == PPT_PST_ExtendedParagraphAtom ) + { + aExtParaHd = aPresRuleHd; + break; + } + } + pHd = pExtParaProv-> + aExtendedPresRules.GetRecordHeader( PPT_PST_ExtendedParagraphHeaderAtom, + SEEK_FROM_CURRENT_AND_RESTART ); + if ( pHd == pFirst ) + break; + } + rIn.Seek( nOldPos ); + } + // now pHd points to the right SlideListWithText Container + PptSlidePersistList* pPageList = rSdrPowerPointImport.GetPageList( rSdrPowerPointImport.m_eCurrentPageKind ); + PptSlidePersistEntry* pE = nullptr; + if ( pPageList && ( rSdrPowerPointImport.m_nCurrentPageNum < pPageList->size() ) ) + pE = &(*pPageList)[ rSdrPowerPointImport.m_nCurrentPageNum ]; + if ( (!pE) || (!pE->nSlidePersistStartOffset) || ( pE->aPersistAtom.nSlideId != nSlideId ) ) + bStatus = false; + else + { + auto nOffset(pE->nSlidePersistStartOffset); + bStatus = (nOffset == rIn.Seek(nOffset)); + // now we got the right page and are searching for the right + // TextHeaderAtom + auto nEndRecPos = DffPropSet::SanitizeEndPos(rIn, pE->nSlidePersistEndOffset); + while (bStatus && rIn.Tell() < nEndRecPos) + { + ReadDffRecordHeader( rIn, aClientTextBoxHd ); + if ( aClientTextBoxHd.nRecType == PPT_PST_TextHeaderAtom ) + { + if ( aClientTextBoxHd.nRecInstance == nRefNum ) + { + aClientTextBoxHd.SeekToEndOfRecord( rIn ); + break; + } + } + if (!aClientTextBoxHd.SeekToEndOfRecord(rIn)) + break; + } + if ( rIn.Tell() > pE->nSlidePersistEndOffset ) + bStatus = false; + else + { // patching the RecordHeader + aClientTextBoxHd.nFilePos -= DFF_COMMON_RECORD_HEADER_SIZE; + aClientTextBoxHd.nRecLen += DFF_COMMON_RECORD_HEADER_SIZE; + aClientTextBoxHd.nRecType = DFF_msofbtClientTextbox; + aClientTextBoxHd.nRecVer = DFF_PSFLAG_CONTAINER; + + // we have to calculate the correct record len + DffRecordHeader aTmpHd; + nEndRecPos = DffPropSet::SanitizeEndPos(rIn, pE->nSlidePersistEndOffset); + while (rIn.Tell() < nEndRecPos) + { + ReadDffRecordHeader( rIn, aTmpHd ); + if ( ( aTmpHd.nRecType == PPT_PST_SlidePersistAtom ) || ( aTmpHd.nRecType == PPT_PST_TextHeaderAtom ) ) + break; + if (!aTmpHd.SeekToEndOfRecord(rIn)) + break; + aClientTextBoxHd.nRecLen += aTmpHd.nRecLen + DFF_COMMON_RECORD_HEADER_SIZE; + } + aClientTextBoxHd.SeekToContent( rIn ); + } + } + } + } + } + + if ( !bStatus ) + return; + + if ( !SvxMSDffManager::SeekToRec( rIn, PPT_PST_TextHeaderAtom, aClientTextBoxHd.GetRecEndFilePos(), &aTextHd ) ) + return; + + // TextHeaderAtom is always the first Atom + sal_uInt16 nTmp(0); + rIn.ReadUInt16(nTmp); // this number tells us the TxMasterStyleAtom Instance + if (nTmp > 8) + nTmp = 4; + TSS_Type nInstance = static_cast(nTmp); + aTextHd.SeekToEndOfRecord( rIn ); + mxImplTextObj->mnInstance = nInstance; + + sal_uInt32 nFilePos = rIn.Tell(); + if ( !(rSdrPowerPointImport.SeekToRec2( PPT_PST_TextBytesAtom, + PPT_PST_TextCharsAtom, + aClientTextBoxHd.GetRecEndFilePos() ) + || SvxMSDffManager::SeekToRec( rIn, + PPT_PST_StyleTextPropAtom, + aClientTextBoxHd.GetRecEndFilePos() )) ) + return; + + PPTTextRulerInterpreter aTextRulerInterpreter( nTextRulerAtomOfs, aClientTextBoxHd, rIn ); + + PPTStyleTextPropReader aStyleTextPropReader( rIn, aClientTextBoxHd, + aTextRulerInterpreter, aExtParaHd, nInstance ); + sal_uInt32 nParagraphs = mxImplTextObj->mnParagraphCount = aStyleTextPropReader.aParaPropList.size(); + if ( !nParagraphs ) + return; + + // the language settings will be merged into the list of PPTCharPropSet + DffRecordHeader aTextSpecInfoHd; + PPTTextSpecInfoAtomInterpreter aTextSpecInfoAtomInterpreter; + if ( SvxMSDffManager::SeekToRec( rIn, PPT_PST_TextSpecInfoAtom, + aClientTextBoxHd.GetRecEndFilePos(), &aTextSpecInfoHd ) ) + { + if ( aTextSpecInfoAtomInterpreter.Read( rIn, aTextSpecInfoHd, PPT_PST_TextSpecInfoAtom, + &(rSdrPowerPointImport.m_pPPTStyleSheet->maTxSI) ) ) + { + size_t nI = 0; + for (const PPTTextSpecInfo& rSpecInfo : aTextSpecInfoAtomInterpreter.aList) + { + sal_uInt32 nCharIdx = rSpecInfo.nCharIdx; + + // portions and text have to been split in some cases + for ( ; nI < aStyleTextPropReader.aCharPropList.size(); ++nI) + { + PPTCharPropSet* pSet = aStyleTextPropReader.aCharPropList[nI].get(); + if (pSet->mnOriginalTextPos >= nCharIdx) + break; + pSet->mnLanguage[0] = rSpecInfo.nLanguage[0]; + pSet->mnLanguage[1] = rSpecInfo.nLanguage[1]; + pSet->mnLanguage[2] = rSpecInfo.nLanguage[2]; + // test if the current portion needs to be split + if (pSet->maString.getLength() <= 1) + continue; + sal_Int32 nIndexOfNextPortion = pSet->maString.getLength() + pSet->mnOriginalTextPos; + sal_Int32 nNewLen = nIndexOfNextPortion - nCharIdx; + if (nNewLen <= 0) + continue; + sal_Int32 nOldLen = pSet->maString.getLength() - nNewLen; + if (nOldLen <= 0) + continue; + OUString aString(pSet->maString); + PPTCharPropSet* pNew = new PPTCharPropSet(*pSet); + pSet->maString = aString.copy(0, nOldLen); + pNew->maString = aString.copy(nOldLen, nNewLen); + pNew->mnOriginalTextPos += nOldLen; + aStyleTextPropReader.aCharPropList.emplace(aStyleTextPropReader.aCharPropList.begin() + nI + 1, pNew); + } + } + } +#ifdef DBG_UTIL + else + { + if (!(rSdrPowerPointImport.rImportParam.nImportFlags & PPT_IMPORTFLAGS_NO_TEXT_ASSERT)) + { + OSL_FAIL( "SdrTextSpecInfoAtomInterpreter::Ctor(): parsing error, this document needs to be analysed (SJ)" ); + } + } +#endif + } + // now will search for possible textextensions such as date/time fields + // or ParaTabStops and append them on this textobj + rIn.Seek( nFilePos ); + ::std::vector< std::unique_ptr > FieldList; + auto nEndRecPos = DffPropSet::SanitizeEndPos(rIn, aClientTextBoxHd.GetRecEndFilePos()); + while (rIn.Tell() < nEndRecPos) + { + ReadDffRecordHeader( rIn, aTextHd ); + sal_uInt16 nVal = 0; + std::unique_ptr xEntry; + switch ( aTextHd.nRecType ) + { + case PPT_PST_DateTimeMCAtom : + { + xEntry.reset(new PPTFieldEntry); + rIn.ReadUInt16(xEntry->nPos) + .ReadUInt16( nVal ) + .ReadUInt16( nVal ); + xEntry->SetDateTime( nVal & 0xff ); + } + break; + + case PPT_PST_FooterMCAtom : + { + xEntry.reset(new PPTFieldEntry); + rIn.ReadUInt16(xEntry->nPos); + xEntry->xField1.reset(new SvxFieldItem(SvxFooterField(), EE_FEATURE_FIELD)); + } + break; + + case PPT_PST_HeaderMCAtom : + { + xEntry.reset(new PPTFieldEntry); + rIn.ReadUInt16(xEntry->nPos); + xEntry->xField1.reset(new SvxFieldItem(SvxHeaderField(), EE_FEATURE_FIELD)); + } + break; + + case PPT_PST_GenericDateMCAtom : + { + xEntry.reset(new PPTFieldEntry); + rIn.ReadUInt16(xEntry->nPos); + xEntry->xField1.reset(new SvxFieldItem(SvxDateTimeField(), EE_FEATURE_FIELD)); + if (rPersistEntry.xHeaderFooterEntry) // sj: #i34111# on master pages it is possible + { // that there is no HeaderFooterEntry available + if (rPersistEntry.xHeaderFooterEntry->nAtom & 0x20000) // auto date time + xEntry->SetDateTime(rPersistEntry.xHeaderFooterEntry->nAtom & 0xff); + else + xEntry->xString = rPersistEntry.xHeaderFooterEntry->pPlaceholder[nVal]; + } + } + break; + + case PPT_PST_SlideNumberMCAtom : + case PPT_PST_RTFDateTimeMCAtom : + { + xEntry.reset(new PPTFieldEntry); + if ( aTextHd.nRecLen >= 4 ) + { + rIn.ReadUInt16(xEntry->nPos) + .ReadUInt16( nVal ); + + // evaluate ID + //SvxFieldItem* pFieldItem = NULL; + switch( aTextHd.nRecType ) + { + case PPT_PST_SlideNumberMCAtom: + xEntry->xField1.reset(new SvxFieldItem(SvxPageField(), EE_FEATURE_FIELD)); + break; + + case PPT_PST_RTFDateTimeMCAtom: + { + // Rude workaround for one special case reported + // by a customer. (#i75203#) + + // Don't even attempt to handle the general use + // case for PPT_PST_RTFDateTimeMCAtom (a generic + // MS style date/time format string). Just handle + // the special case where the format string + // contains only one or several possibly empty + // quoted strings. I.e. something that doesn't + // expand to any date or time at all, but to a + // fixed string. How on earth somebody manages to + // produce such things in PPT slides I have no + // idea. + if (nVal == 0) + { + OUStringBuffer aStr; + bool inquote = false; + for (int nLen = 0; nLen < 64; ++nLen) + { + sal_Unicode n(0); + rIn.ReadUtf16( n ); + + // Collect quoted characters into aStr + if ( n == '\'') + inquote = !inquote; + else if (!n) + { + // End of format string + xEntry->xString = aStr.makeStringAndClear(); + break; + } + else if (!inquote) + { + // Non-quoted character, i.e. a real + // format specifier. We don't handle + // those. Sorry. + break; + } + else + { + aStr.append(OUStringChar(n)); + } + } + } + if (!xEntry->xString) + { + // Handle as previously + xEntry->xField1.reset(new SvxFieldItem( SvxDateField( Date( Date::SYSTEM ), SvxDateType::Fix ), EE_FEATURE_FIELD )); + } + } + } + } + } + break; + + case PPT_PST_InteractiveInfo : + { + DffRecordHeader aHdInteractiveInfoAtom; + if ( SvxMSDffManager::SeekToRec( rIn, PPT_PST_InteractiveInfoAtom, aTextHd.GetRecEndFilePos(), &aHdInteractiveInfoAtom ) ) + { + PptInteractiveInfoAtom aInteractiveInfoAtom; + ReadPptInteractiveInfoAtom( rIn, aInteractiveInfoAtom ); + for (const SdHyperlinkEntry& rHyperlink : rSdrPowerPointImport.m_aHyperList) + { + if ( rHyperlink.nIndex == aInteractiveInfoAtom.nExHyperlinkId ) + { + if (!aTextHd.SeekToEndOfRecord(rIn)) + { + break; + } + ReadDffRecordHeader( rIn, aTextHd ); + if ( aTextHd.nRecType != PPT_PST_TxInteractiveInfoAtom ) + { + aTextHd.SeekToBegOfRecord( rIn ); + continue; + } + else + { + sal_uInt32 nStartPos, nEndPos; + rIn.ReadUInt32( nStartPos ) + .ReadUInt32( nEndPos ); + if ( nEndPos ) + { + xEntry.reset(new PPTFieldEntry); + xEntry->nPos = static_cast(nStartPos); + xEntry->nTextRangeEnd = static_cast(nEndPos); + OUString aTarget( rHyperlink.aTarget ); + if ( !rHyperlink.aConvSubString.isEmpty() ) + { + aTarget += "#" + rHyperlink.aConvSubString; + } + xEntry->xField1.reset(new SvxFieldItem( SvxURLField( aTarget, OUString(), SvxURLFormat::Repr ), EE_FEATURE_FIELD )); + } + } + break; + } + } + } + } + break; + } + if (!aTextHd.SeekToEndOfRecord(rIn)) + break; + if (xEntry) + { + // sorting fields ( hi >> lo ) + auto it = std::find_if(FieldList.begin(), FieldList.end(), + [&xEntry](const std::unique_ptr& rxField) { + return rxField->nPos < xEntry->nPos; }); + if ( it != FieldList.end() ) { + FieldList.insert(it, std::move(xEntry)); + } else { + FieldList.push_back( std::move(xEntry)); + } + } + } + if ( !FieldList.empty() ) + { + auto FE = FieldList.begin(); + auto& aCharPropList = aStyleTextPropReader.aCharPropList; + + sal_Int32 i = nParagraphs - 1; + sal_Int32 n = aCharPropList.size() - 1; + + // at this point we just have a list of textportions(aCharPropList) + // the next while loop tries to resolve the list of fields(pFieldList) + while( ( FE < FieldList.end() ) && ( n >= 0 ) && ( i >= 0 ) ) + { + PPTCharPropSet* pSet = aCharPropList[n].get(); + OUString aString( pSet->maString ); + sal_uInt32 nCount = aString.getLength(); + sal_uInt32 nPos = pSet->mnOriginalTextPos + nCount; + while ( ( FE < FieldList.end() ) && nCount-- ) + { + nPos--; + FE = std::find_if(FE, FieldList.end(), + [&nPos](const std::unique_ptr& rxField) {return rxField->nPos <= nPos;}); + if (FE == FieldList.end()) + break; + + if ( (*FE)->nPos == nPos ) + { + if ( aString[nCount] == 0x2a ) + { + sal_uInt32 nBehind = aString.getLength() - ( nCount + 1 ); + pSet->maString.clear(); + if ( nBehind ) + { + PPTCharPropSet* pNewCPS = new PPTCharPropSet( *pSet ); + pNewCPS->maString = aString.copy( nCount + 1, nBehind ); + aCharPropList.emplace( aCharPropList.begin() + n + 1, pNewCPS ); + } + if ( (*FE)->xField2 ) + { + PPTCharPropSet* pNewCPS = new PPTCharPropSet( *pSet ); + pNewCPS->mpFieldItem = std::move((*FE)->xField2); + aCharPropList.emplace( aCharPropList.begin() + n + 1, pNewCPS ); + + pNewCPS = new PPTCharPropSet( *pSet ); + pNewCPS->maString = " "; + aCharPropList.emplace( aCharPropList.begin() + n + 1, pNewCPS ); + } + if ( nCount ) + { + PPTCharPropSet* pNewCPS = new PPTCharPropSet( *pSet ); + pNewCPS->maString = aString.copy( 0, nCount ); + aCharPropList.emplace( aCharPropList.begin() + n++, pNewCPS ); + } + if ( (*FE)->xField1 ) + { + pSet->mpFieldItem = std::move((*FE)->xField1); + } + else if ( (*FE)->xString ) + pSet->maString = *(*FE)->xString; + } + else + { + if ( (*FE)->nTextRangeEnd ) // text range hyperlink + { + sal_uInt32 nHyperLen = (*FE)->nTextRangeEnd - nPos; + if ( nHyperLen ) + { + PPTCharPropSet* pBefCPS = nullptr; + if ( nCount ) + { + pBefCPS = new PPTCharPropSet( *pSet ); + pSet->maString = pSet->maString.copy(nCount); + } + sal_uInt32 nIdx = n; + sal_Int32 nHyperLenLeft = nHyperLen; + + while ( ( aCharPropList.size() > nIdx ) && nHyperLenLeft ) + { + // the textrange hyperlink can take more than 1 paragraph + // the solution here is to clone the hyperlink... + + PPTCharPropSet* pCurrent = aCharPropList[ nIdx ].get(); + sal_Int32 nNextStringLen = pCurrent->maString.getLength(); + + DBG_ASSERT( (*FE)->xField1, "missing field!" ); + if (!(*FE)->xField1) + break; + + const SvxURLField* pField = static_cast((*FE)->xField1->GetField()); + + pCurrent->mbIsHyperlink = true; + pCurrent->mnHylinkOrigColor = pCurrent->mpImplPPTCharPropSet->mnColor; + pCurrent->mbHardHylinkOrigColor = ( ( pCurrent->mpImplPPTCharPropSet->mnAttrSet >>PPT_CharAttr_FontColor ) & 1)>0; + + // add missing attribute to show underline property + pCurrent->mpImplPPTCharPropSet->mnAttrSet |= 1 << PPT_CharAttr_Underline; + pCurrent->mpImplPPTCharPropSet->mnFlags = 1 << PPT_CharAttr_Underline; + + if ( pCurrent->mpFieldItem ) + { + pCurrent->SetColor( PPT_COLSCHEME_A_UND_HYPERLINK ); + if ( dynamic_cast< const SvxURLField* >(pCurrent->mpFieldItem->GetField()) != nullptr) + break; + nHyperLenLeft--; + } + else if ( nNextStringLen ) + { + if ( nNextStringLen <= nHyperLenLeft ) + { + pCurrent->mpFieldItem.reset( new SvxFieldItem( SvxURLField( pField->GetURL(), pCurrent->maString, SvxURLFormat::Repr ), EE_FEATURE_FIELD ) ); + nHyperLenLeft -= nNextStringLen; + + if ( nHyperLenLeft ) + { + // if the next portion is in a higher paragraph, + // the textrange is to decrease (because of the LineBreak character) + if ( aCharPropList.size() > ( nIdx + 1 ) ) + { + PPTCharPropSet* pNext = aCharPropList[ nIdx + 1 ].get(); + if ( pNext->mnParagraph > pCurrent->mnParagraph ) + nHyperLenLeft--; + } + } + } + else + { + PPTCharPropSet* pNewCPS = new PPTCharPropSet( *pCurrent ); + pNewCPS->maString = pCurrent->maString.copy( nHyperLenLeft,( nNextStringLen - nHyperLenLeft ) ); + aCharPropList.emplace( aCharPropList.begin() + nIdx + 1, pNewCPS ); + OUString aRepresentation = pCurrent->maString.copy( 0, nHyperLenLeft ); + pCurrent->mpFieldItem.reset( new SvxFieldItem( SvxURLField( pField->GetURL(), aRepresentation, SvxURLFormat::Repr ), EE_FEATURE_FIELD ) ); + nHyperLenLeft = 0; + } + pCurrent->maString.clear(); + pCurrent->SetColor( PPT_COLSCHEME_A_UND_HYPERLINK ); + } + nIdx++; + } + (*FE)->xField1.reset(); + + if ( pBefCPS ) + { + pBefCPS->maString = aString.copy( 0, nCount ); + aCharPropList.emplace( aCharPropList.begin() + n, pBefCPS ); + n++; + } + } + } + } + break; + } + } + n--; + } + } + mxImplTextObj->maParagraphList.resize( nParagraphs ); + for (size_t nCurCharPos = 0, nCurPos = 0; + nCurPos < aStyleTextPropReader.aParaPropList.size(); + ++nCurPos) + { + mxImplTextObj->maParagraphList[ nCurPos ].reset( + new PPTParagraphObj( + aStyleTextPropReader, nCurPos, nCurCharPos, + *rSdrPowerPointImport.m_pPPTStyleSheet, + nInstance, aTextRulerInterpreter ) ); + + sal_uInt32 nParaAdjust, nFlags = 0; + mxImplTextObj->maParagraphList[ nCurPos ]->GetAttrib( PPT_ParaAttr_Adjust, nParaAdjust, GetInstance() ); + + switch ( nParaAdjust ) + { + case 0 : nFlags = PPT_TEXTOBJ_FLAGS_PARA_ALIGNMENT_USED_LEFT; break; + case 1 : nFlags = PPT_TEXTOBJ_FLAGS_PARA_ALIGNMENT_USED_CENTER; break; + case 2 : nFlags = PPT_TEXTOBJ_FLAGS_PARA_ALIGNMENT_USED_RIGHT; break; + case 3 : nFlags = PPT_TEXTOBJ_FLAGS_PARA_ALIGNMENT_USED_BLOCK; break; + } + mxImplTextObj->mnTextFlags |= nFlags; + } +} + +PPTTextObj::PPTTextObj( PPTTextObj const & rTextObj ) +{ + mxImplTextObj = rTextObj.mxImplTextObj; +} + +PPTTextObj::~PPTTextObj() +{ +} + +PPTParagraphObj* PPTTextObj::First() +{ + mxImplTextObj->mnCurrentObject = 0; + if ( !mxImplTextObj->mnParagraphCount ) + return nullptr; + return mxImplTextObj->maParagraphList[ 0 ].get(); +} + +PPTParagraphObj* PPTTextObj::Next() +{ + sal_uInt32 i = mxImplTextObj->mnCurrentObject + 1; + if ( i >= mxImplTextObj->mnParagraphCount ) + return nullptr; + mxImplTextObj->mnCurrentObject++; + return mxImplTextObj->maParagraphList[ i ].get(); +} + +const SfxItemSet* PPTTextObj::GetBackground() const +{ + if ( mxImplTextObj->mrPersistEntry.pBObj ) + return &mxImplTextObj->mrPersistEntry.pBObj->GetMergedItemSet(); + else + return nullptr; +} + +PPTTextObj& PPTTextObj::operator=( const PPTTextObj& rTextObj ) +{ + if ( this != &rTextObj ) + { + mxImplTextObj = rTextObj.mxImplTextObj; + } + return *this; +} + +static bool IsLine( const SdrObject* pObj ) +{ + auto pSdrPathObj = dynamic_cast< const SdrPathObj* >(pObj); + return pSdrPathObj && pSdrPathObj->IsLine() && pSdrPathObj->GetPointCount() == 2; +} + +static bool GetCellPosition( const SdrObject* pObj, const o3tl::sorted_vector< sal_Int32 >& rRows, const o3tl::sorted_vector< sal_Int32 >& rColumns, + sal_Int32& nTableIndex, sal_Int32& nRow, sal_Int32& nRowCount, sal_Int32& nColumn, sal_Int32& nColumnCount ) +{ + tools::Rectangle aSnapRect( pObj->GetSnapRect() ); + bool bCellObject = ( aSnapRect.GetWidth() > 1 ) && ( aSnapRect.GetHeight() > 1 ); + if ( bCellObject ) + { + auto aRowIter = rRows.find( aSnapRect.Top() ); + auto aColumnIter = rColumns.find( aSnapRect.Left() ); + if ( ( aRowIter == rRows.end() ) || ( aColumnIter == rColumns.end() ) ) + bCellObject = false; + else + { + nRowCount = 1; + nRow = std::distance( rRows.begin(), aRowIter ); + while( ++aRowIter != rRows.end() ) + { + if ( *aRowIter >= aSnapRect.Bottom() ) + break; + nRowCount++; + } + nColumnCount = 1; + nColumn = std::distance( rColumns.begin(), aColumnIter ); + while( ++aColumnIter != rColumns.end() ) + { + if ( *aColumnIter >= aSnapRect.Right() ) + break; + nColumnCount++; + } + nTableIndex = nRow * rColumns.size() + nColumn; + } + } + return bCellObject; +} + +#define LinePositionLeft 0x01000000 +#define LinePositionTop 0x02000000 +#define LinePositionRight 0x04000000 +#define LinePositionBottom 0x08000000 +#define LinePositionTLBR 0x10000000 +#define LinePositionBLTR 0x20000000 + + +static void GetRowPositions( const tools::Rectangle& rSnapRect, const o3tl::sorted_vector< sal_Int32 >& rRows, + const o3tl::sorted_vector< sal_Int32 >& rColumns, std::vector< sal_Int32 >& rPositions, sal_Int32 nColumn, sal_Int32 nFlags ) +{ + auto aRow = rRows.find( rSnapRect.Top() ); + if ( aRow == rRows.end() ) + return; + + sal_Int32 nRow = std::distance( rRows.begin(), aRow ); + while( ( aRow != rRows.end() ) && ((*aRow) < rSnapRect.Bottom() ) ) + { + if ( nFlags & LinePositionLeft ) + rPositions.push_back( ( ( nRow * rColumns.size() ) + nColumn ) | LinePositionLeft ); + if ( nFlags & LinePositionRight ) + rPositions.push_back( ( ( nRow * rColumns.size() ) + ( nColumn - 1 ) ) | LinePositionRight ); + + ++nRow; + ++aRow; + } +} + + +static void GetColumnPositions( const tools::Rectangle& rSnapRect, + const o3tl::sorted_vector< sal_Int32 >& rColumns, std::vector< sal_Int32 >& rPositions, sal_Int32 nRow, sal_Int32 nFlags ) +{ + auto aColumn = rColumns.find( rSnapRect.Left() ); + if ( aColumn == rColumns.end() ) + return; + + sal_Int32 nColumn = std::distance( rColumns.begin(), aColumn ); + while( ( aColumn != rColumns.end() ) && ((*aColumn) < rSnapRect.Right() ) ) + { + if ( nFlags & LinePositionTop ) + rPositions.push_back( ( ( nRow * rColumns.size() ) + nColumn ) | LinePositionTop ); + if ( nFlags & LinePositionBottom ) + rPositions.push_back( ( ( ( nRow - 1 ) * rColumns.size() ) + nColumn ) | LinePositionBottom ); + + ++nColumn; + ++aColumn; + } +} + +static void GetLinePositions( const SdrObject* pObj, const o3tl::sorted_vector< sal_Int32 >& rRows, const o3tl::sorted_vector< sal_Int32 >& rColumns, + std::vector< sal_Int32 >& rPositions, const tools::Rectangle& rGroupSnap ) +{ + tools::Rectangle aSnapRect( pObj->GetSnapRect() ); + if ( aSnapRect.Left() == aSnapRect.Right() ) + { + auto aColumn = rColumns.find( aSnapRect.Left() ); + if ( ( aColumn != rColumns.end() ) || ( aSnapRect.Left() == rGroupSnap.Right() ) ) + { + sal_Int32 nColumn, nFlags; + if ( aColumn != rColumns.end() ) + { + nColumn = std::distance( rColumns.begin(), aColumn ); + nFlags = LinePositionLeft; + if ( aColumn != rColumns.begin() ) + nFlags |= LinePositionRight; + } + else + { + nColumn = rColumns.size(); + nFlags = LinePositionRight; + } + GetRowPositions( aSnapRect, rRows, rColumns, rPositions, nColumn, nFlags ); + } + } + else if ( aSnapRect.Top() == aSnapRect.Bottom() ) + { + auto aRow = rRows.find( aSnapRect.Top() ); + if ( ( aRow != rRows.end() ) || ( aSnapRect.Top() == rGroupSnap.Bottom() ) ) + { + sal_Int32 nRow, nFlags; + if ( aRow != rRows.end() ) + { + nRow = std::distance( rRows.begin(), aRow ); + nFlags = LinePositionTop; + if ( aRow != rRows.begin() ) + nFlags |= LinePositionBottom; + } + else + { + nRow = rRows.size(); + nFlags = LinePositionBottom; + } + GetColumnPositions( aSnapRect, rColumns, rPositions, nRow, nFlags ); + } + } + else + { + sal_uInt32 nPosition = 0; + Point aPt1( static_cast(pObj)->GetPoint( 0 ) ); + Point aPt2( static_cast(pObj)->GetPoint( 1 ) ); + if ( aPt1.X() < aPt2.X() ) + nPosition |= aPt1.Y() < aPt2.Y() ? LinePositionTLBR : LinePositionBLTR; + else + nPosition |= aPt1.Y() < aPt2.Y() ? LinePositionBLTR : LinePositionTLBR; + + auto aRow = rRows.find( std::min(aPt1.Y(), aPt2.Y() ) ); + auto aColumn = rColumns.find( std::min(aPt1.X(), aPt2.X() ) ); + if ( ( aRow != rRows.end() ) && ( aColumn != rColumns.end() ) ) + { + nPosition |= ( std::distance( rRows.begin(), aRow ) * rColumns.size() ) + std::distance( rColumns.begin(), aColumn ); + rPositions.push_back( nPosition ); + } + } +} + +static void CreateTableRows( const Reference< XTableRows >& xTableRows, const o3tl::sorted_vector< sal_Int32 >& rRows, sal_Int32 nTableBottom ) +{ + if ( rRows.size() > 1 ) + xTableRows->insertByIndex( 0, rRows.size() - 1 ); + + auto aIter = rRows.begin(); + sal_Int32 nLastPosition( *aIter ); + for ( sal_Int32 n = 0; n < xTableRows->getCount(); n++ ) + { + sal_Int32 nHeight; + if ( ++aIter != rRows.end() ) + { + if (o3tl::checked_sub(*aIter, nLastPosition, nHeight)) + throw lang::IllegalArgumentException(); + nLastPosition = *aIter; + } + else + { + if (o3tl::checked_sub(nTableBottom, nLastPosition, nHeight)) + throw lang::IllegalArgumentException(); + } + + Reference< XPropertySet > xPropSet( xTableRows->getByIndex( n ), UNO_QUERY_THROW ); + xPropSet->setPropertyValue( "Height", Any( nHeight ) ); + } +} + +static void CreateTableColumns( const Reference< XTableColumns >& xTableColumns, const o3tl::sorted_vector< sal_Int32 >& rColumns, sal_Int32 nTableRight ) +{ + if ( rColumns.size() > 1 ) + xTableColumns->insertByIndex( 0, rColumns.size() - 1 ); + + auto aIter = rColumns.begin(); + sal_Int32 nLastPosition( *aIter ); + for ( sal_Int32 n = 0; n < xTableColumns->getCount(); n++ ) + { + sal_Int32 nWidth; + if ( ++aIter != rColumns.end() ) + { + if (o3tl::checked_sub(*aIter, nLastPosition, nWidth)) + throw lang::IllegalArgumentException(); + nLastPosition = *aIter; + } + else + { + if (o3tl::checked_sub(nTableRight, nLastPosition, nWidth)) + throw lang::IllegalArgumentException(); + } + + Reference< XPropertySet > xPropSet( xTableColumns->getByIndex( n ), UNO_QUERY_THROW ); + xPropSet->setPropertyValue( "Width", Any( nWidth ) ); + } +} + +static void MergeCells( const Reference< XTable >& xTable, sal_Int32 nCol, sal_Int32 nRow, sal_Int32 nColSpan, sal_Int32 nRowSpan ) +{ + DBG_ASSERT( (nColSpan > 1) || (nRowSpan > 1), "nonsense parameter!!" ); + DBG_ASSERT( (nCol >= 0) && (nCol < xTable->getColumnCount()) && (nRow >= 0) && (nRow < xTable->getRowCount()), "the cell does not exists!!" ); + DBG_ASSERT( (nColSpan >= 1) && ((nCol + nColSpan - 1) < xTable->getColumnCount()), "nColSpan botch!" ); + DBG_ASSERT( (nRowSpan >= 1) && ((nRow + nRowSpan - 1) < xTable->getRowCount()), "nRowSpan botch!" ); + + if( xTable.is() ) try + { + Reference< XMergeableCellRange > xRange( xTable->createCursorByRange( xTable->getCellRangeByPosition( nCol, nRow,nCol + nColSpan - 1, nRow + nRowSpan - 1 ) ), UNO_QUERY_THROW ); + if( xRange->isMergeable() ) + xRange->merge(); + } + catch( const Exception& ) + { + DBG_UNHANDLED_EXCEPTION("filter.ms"); + } +} + +static void ApplyCellAttributes( const SdrObject* pObj, Reference< XCell > const & xCell ) +{ + try + { + Reference< XPropertySet > xPropSet( xCell, UNO_QUERY_THROW ); + + const sal_Int32 nLeftDist(pObj->GetMergedItem(SDRATTR_TEXT_LEFTDIST).GetValue()); + const sal_Int32 nRightDist(pObj->GetMergedItem(SDRATTR_TEXT_RIGHTDIST).GetValue()); + const sal_Int32 nUpperDist(pObj->GetMergedItem(SDRATTR_TEXT_UPPERDIST).GetValue()); + const sal_Int32 nLowerDist(pObj->GetMergedItem(SDRATTR_TEXT_LOWERDIST).GetValue()); + xPropSet->setPropertyValue( "TextUpperDistance", Any( nUpperDist ) ); + xPropSet->setPropertyValue( "TextRightDistance", Any( nRightDist ) ); + xPropSet->setPropertyValue( "TextLeftDistance", Any( nLeftDist ) ); + xPropSet->setPropertyValue( "TextLowerDistance", Any( nLowerDist ) ); + + const SdrTextVertAdjust eTextVertAdjust(pObj->GetMergedItem(SDRATTR_TEXT_VERTADJUST).GetValue()); + drawing::TextVerticalAdjust eVA( drawing::TextVerticalAdjust_TOP ); + if ( eTextVertAdjust == SDRTEXTVERTADJUST_CENTER ) + eVA = drawing::TextVerticalAdjust_CENTER; + else if ( eTextVertAdjust == SDRTEXTVERTADJUST_BOTTOM ) + eVA = drawing::TextVerticalAdjust_BOTTOM; + xPropSet->setPropertyValue( "TextVerticalAdjust", Any( eVA ) ); + + //set textHorizontalAdjust and TextWritingMode attr + const sal_Int32 eHA(pObj->GetMergedItem(SDRATTR_TEXT_HORZADJUST).GetValue()); + const SvxFrameDirection eDirection = pObj->GetMergedItem(EE_PARA_WRITINGDIR).GetValue(); + xPropSet->setPropertyValue( "TextHorizontalAdjust" , Any( eHA ) ); + if ( eDirection == SvxFrameDirection::Vertical_RL_TB ) + {//vertical writing + xPropSet->setPropertyValue( "TextWritingMode" , Any( css::text::WritingMode_TB_RL ) ); + } + drawing::FillStyle eFillStyle(pObj->GetMergedItem( XATTR_FILLSTYLE ).GetValue()); + css::drawing::FillStyle eFS( css::drawing::FillStyle_NONE ); + switch( eFillStyle ) + { + case drawing::FillStyle_SOLID : + { + eFS = css::drawing::FillStyle_SOLID; + Color aFillColor( pObj->GetMergedItem( XATTR_FILLCOLOR ).GetColorValue() ); + xPropSet->setPropertyValue( "FillColor", Any( aFillColor ) ); + } + break; + case drawing::FillStyle_GRADIENT : + { + eFS = css::drawing::FillStyle_GRADIENT; + XGradient aXGradient(pObj->GetMergedItem(XATTR_FILLGRADIENT).GetGradientValue()); + + css::awt::Gradient aGradient; + aGradient.Style = aXGradient.GetGradientStyle(); + aGradient.StartColor = static_cast(aXGradient.GetStartColor()); + aGradient.EndColor = static_cast(aXGradient.GetEndColor()); + aGradient.Angle = static_cast(aXGradient.GetAngle()); + aGradient.Border = aXGradient.GetBorder(); + aGradient.XOffset = aXGradient.GetXOffset(); + aGradient.YOffset = aXGradient.GetYOffset(); + aGradient.StartIntensity = aXGradient.GetStartIntens(); + aGradient.EndIntensity = aXGradient.GetEndIntens(); + aGradient.StepCount = aXGradient.GetSteps(); + + xPropSet->setPropertyValue( "FillGradient", Any( aGradient ) ); + } + break; + case drawing::FillStyle_HATCH : + eFS = css::drawing::FillStyle_HATCH; + break; + case drawing::FillStyle_BITMAP : + { + eFS = css::drawing::FillStyle_BITMAP; + + const XFillBitmapItem & rXFillBitmapItem(pObj->GetMergedItem( XATTR_FILLBITMAP )); + uno::Reference xGraphic = rXFillBitmapItem.GetGraphicObject().GetGraphic().GetXGraphic(); + uno::Reference xBitmap(xGraphic, uno::UNO_QUERY); + xPropSet->setPropertyValue("FillBitmap", uno::Any(xBitmap)); + + const XFillBmpStretchItem & rStretchItem(pObj->GetMergedItem( XATTR_FILLBMP_STRETCH )); + const XFillBmpTileItem & rTileItem(pObj->GetMergedItem( XATTR_FILLBMP_TILE )); + if( rTileItem.GetValue() ) + xPropSet->setPropertyValue("FillBitmapMode", uno::Any(drawing::BitmapMode_REPEAT)); + else if( rStretchItem.GetValue() ) + xPropSet->setPropertyValue("FillBitmapMode", uno::Any(drawing::BitmapMode_STRETCH)); + else + xPropSet->setPropertyValue("FillBitmapMode", uno::Any(drawing::BitmapMode_NO_REPEAT)); + } + break; + default: + case drawing::FillStyle_NONE : + eFS = css::drawing::FillStyle_NONE; + break; + + } + xPropSet->setPropertyValue( "FillStyle", Any( eFS ) ); + if ( eFillStyle != drawing::FillStyle_NONE ) + { + sal_Int16 nFillTransparence( pObj->GetMergedItem( XATTR_FILLTRANSPARENCE ).GetValue() ); + xPropSet->setPropertyValue( "FillTransparence", Any( nFillTransparence ) ); + } + } + catch( const Exception& ) + { + } +} + +static void ApplyCellLineAttributes( const SdrObject* pLine, Reference< XTable > const & xTable, const std::vector< sal_Int32 >& vPositions, sal_Int32 nColumns ) +{ + try + { + drawing::LineStyle eLineStyle(pLine->GetMergedItem( XATTR_LINESTYLE ).GetValue()); + css::table::BorderLine2 aBorderLine; + switch( eLineStyle ) + { + case drawing::LineStyle_DASH : + case drawing::LineStyle_SOLID : + { + Color aLineColor( pLine->GetMergedItem( XATTR_LINECOLOR ).GetColorValue() ); + aBorderLine.Color = sal_Int32(aLineColor); + // Avoid width = 0, the min value should be 1. + sal_Int32 nLineWidth = std::max(sal_Int32(1), pLine->GetMergedItem(XATTR_LINEWIDTH) .GetValue() / 4); + aBorderLine.LineWidth = static_cast< sal_Int16 >( nLineWidth ); + aBorderLine.LineStyle = eLineStyle == drawing::LineStyle_SOLID ? table::BorderLineStyle::SOLID : table::BorderLineStyle::DASHED; + } + break; + default: + case drawing::LineStyle_NONE : + { + aBorderLine.LineWidth = 0; + aBorderLine.LineStyle = table::BorderLineStyle::NONE; + } + break; + } + for (auto const& vPos : vPositions) + { + sal_Int32 nPosition = vPos & 0xffffff; + sal_Int32 nFlags = vPos &~0xffffff; + sal_Int32 nRow = nPosition / nColumns; + sal_Int32 nColumn = nPosition - ( nRow * nColumns ); + Reference< XCell > xCell( xTable->getCellByPosition( nColumn, nRow ) ); + Reference< XPropertySet > xPropSet( xCell, UNO_QUERY_THROW ); + + if ( nFlags & LinePositionLeft ) + xPropSet->setPropertyValue( "LeftBorder", Any( aBorderLine ) ); + if ( nFlags & LinePositionTop ) + xPropSet->setPropertyValue( "TopBorder", Any( aBorderLine ) ); + if ( nFlags & LinePositionRight ) + xPropSet->setPropertyValue( "RightBorder", Any( aBorderLine ) ); + if ( nFlags & LinePositionBottom ) + xPropSet->setPropertyValue( "BottomBorder", Any( aBorderLine ) ); + if ( nFlags & LinePositionTLBR ) + xPropSet->setPropertyValue( "DiagonalTLBR", Any( true ) ); + if ( nFlags & LinePositionBLTR ) + xPropSet->setPropertyValue( "DiagonalBLTR", Any( true ) ); + } + } + catch( const Exception& ) + { + } +} + +SdrObject* SdrPowerPointImport::CreateTable(SdrObject* pGroup, const sal_uInt32* pTableArry, SvxMSDffSolverContainer* pSolverContainer, std::vector& rBackgroundColoredObjects) +{ + SdrObject* pRet = pGroup; + + sal_uInt32 nRows = pTableArry[ 1 ]; + if (!nRows) + return pRet; + + const SdrObjGroup* pObjGroup = dynamic_cast(pGroup); + if (!pObjGroup) + return pRet; + + SdrObjList* pSubList(pObjGroup->GetSubList()); + if (!pSubList) + return pRet; + + o3tl::sorted_vector< sal_Int32 > aRows; + o3tl::sorted_vector< sal_Int32 > aColumns; + + SdrObjListIter aGroupIter( pSubList, SdrIterMode::DeepNoGroups, false ); + while( aGroupIter.IsMore() ) + { + const SdrObject* pObj( aGroupIter.Next() ); + if ( !IsLine( pObj ) ) + { + tools::Rectangle aSnapRect( pObj->GetSnapRect() ); + aRows.insert( aSnapRect.Top() ); + aColumns.insert( aSnapRect.Left() ); + } + } + + if (aRows.empty()) + return pRet; + + sdr::table::SdrTableObj* pTable = new sdr::table::SdrTableObj(*pSdrModel); + pTable->uno_lock(); + Reference< XTable > xTable( pTable->getTable() ); + + try + { + CreateTableRows( xTable->getRows(), aRows, pGroup->GetSnapRect().Bottom() ); + CreateTableColumns( xTable->getColumns(), aColumns, pGroup->GetSnapRect().Right() ); + + sal_Int32 nCellCount = aRows.size() * aColumns.size(); + std::unique_ptr pMergedCellIndexTable(new sal_Int32[ nCellCount ]); + for ( sal_Int32 i = 0; i < nCellCount; i++ ) + pMergedCellIndexTable[ i ] = i; + + aGroupIter.Reset(); + while( aGroupIter.IsMore() ) + { + SdrObject* pObj( aGroupIter.Next() ); + if ( !IsLine( pObj ) ) + { + sal_Int32 nTableIndex = 0; + sal_Int32 nRow = 0; + sal_Int32 nRowCount = 0; + sal_Int32 nColumn = 0; + sal_Int32 nColumnCount = 0; + if ( GetCellPosition( pObj, aRows, aColumns, nTableIndex, nRow, nRowCount, nColumn, nColumnCount ) ) + { + Reference< XCell > xCell( xTable->getCellByPosition( nColumn, nRow ) ); + + ApplyCellAttributes( pObj, xCell ); + + if ( ( nRowCount > 1 ) || ( nColumnCount > 1 ) ) // cell merging + { + MergeCells( xTable, nColumn, nRow, nColumnCount, nRowCount ); + for ( sal_Int32 nRowIter = 0; nRowIter < nRowCount; nRowIter++ ) + { + for ( sal_Int32 nColumnIter = 0; nColumnIter < nColumnCount; nColumnIter++ ) + { // now set the correct index for the merged cell + pMergedCellIndexTable[ ( ( nRow + nRowIter ) * aColumns.size() ) + nColumn + nColumnIter ] = nTableIndex; + } + } + } + + // applying text + OutlinerParaObject* pParaObject = pObj->GetOutlinerParaObject(); + if ( pParaObject ) + { + SdrText* pSdrText = pTable->getText( nTableIndex ); + if ( pSdrText ) + pSdrText->SetOutlinerParaObject(*pParaObject); + } + } + } + } + aGroupIter.Reset(); + while( aGroupIter.IsMore() ) + { + SdrObject* pObj( aGroupIter.Next() ); + if ( IsLine( pObj ) ) + { + std::vector< sal_Int32 > vPositions; // containing cell indexes + cell position + GetLinePositions( pObj, aRows, aColumns, vPositions, pGroup->GetSnapRect() ); + + // correcting merged cell position + for (auto & vPos : vPositions) + { + sal_Int32 nOldPosition = vPos & 0xffff; + sal_Int32 nOldFlags = vPos & 0xffff0000; + sal_Int32 nNewPosition = pMergedCellIndexTable[ nOldPosition ] | nOldFlags; + vPos = nNewPosition; + } + ApplyCellLineAttributes( pObj, xTable, vPositions, aColumns.size() ); + } + } + pMergedCellIndexTable.reset(); + + // we are replacing the whole group object by a single table object, so + // possibly connections to the group object have to be removed. + if ( pSolverContainer ) + { + for (auto & pPtr : pSolverContainer->aCList) + { + // check connections to the group object + if ( pPtr->pAObj == pGroup ) + pPtr->pAObj = nullptr; + if ( pPtr->pBObj == pGroup ) + pPtr->pBObj = nullptr; + + // check connections to all its subobjects + SdrObjListIter aIter( *pGroup, SdrIterMode::DeepWithGroups ); + while( aIter.IsMore() ) + { + SdrObject* pPartObj = aIter.Next(); + if ( pPtr->pAObj == pPartObj ) + pPtr->pAObj = nullptr; + if ( pPtr->pBObj == pPartObj ) + pPtr->pBObj = nullptr; + } + //In MS, the one_row_one_col table is made up of five + //shape,the connector is connected to some part of a + //table. But for us, the connector is connected to the + //whole group table,so the connector obj is a group + //table when export by us. We should process this + //situation when importing. + if ( pPtr->pAObj == pGroup ) + pPtr->pAObj = pTable; + if ( pPtr->pBObj == pGroup ) + pPtr->pBObj = pTable; + } + } + pTable->uno_unlock(); + pTable->SetSnapRect( pGroup->GetSnapRect() ); + pRet = pTable; + + //Remove Objects from shape map + SdrObjListIter aIter( *pGroup, SdrIterMode::DeepWithGroups ); + while( aIter.IsMore() ) + { + SdrObject* pPartObj = aIter.Next(); + removeShapeId(pPartObj); + // ofz#41510 make sure rBackgroundColoredObjects doesn't contain deleted objects + std::replace(rBackgroundColoredObjects.begin(), rBackgroundColoredObjects.end(), pPartObj, pRet); + } + + SdrObject::Free( pGroup ); + } + catch( const Exception& ) + { + pTable->uno_unlock(); + SdrObject* pObj = pTable; + SdrObject::Free( pObj ); + } + + return pRet; +} + +bool SdrPowerPointImport::IsVerticalText() const +{ + bool bVerticalText = false; + if ( IsProperty( DFF_Prop_txflTextFlow ) ) + { + auto eTextFlow = GetPropertyValue(DFF_Prop_txflTextFlow, 0) & 0xFFFF; + switch( eTextFlow ) + { + case mso_txflTtoBA : // Top to Bottom @-font, above -> below + case mso_txflTtoBN : // Top to Bottom non-@, above -> below + case mso_txflVertN : // Vertical, non-@, above -> below + bVerticalText = !bVerticalText; + break; + default: break; + } + } + + return bVerticalText; +} + +void SdrPowerPointImport::ApplyTextAnchorAttributes( PPTTextObj const & rTextObj, SfxItemSet& rSet ) const +{ + SdrTextVertAdjust eTVA; + SdrTextHorzAdjust eTHA; + + sal_uInt32 nTextFlags = rTextObj.GetTextFlags(); + + nTextFlags &= PPT_TEXTOBJ_FLAGS_PARA_ALIGNMENT_USED_LEFT | PPT_TEXTOBJ_FLAGS_PARA_ALIGNMENT_USED_RIGHT + | PPT_TEXTOBJ_FLAGS_PARA_ALIGNMENT_USED_CENTER | PPT_TEXTOBJ_FLAGS_PARA_ALIGNMENT_USED_BLOCK; + + if ( IsVerticalText() ) + { + eTVA = SDRTEXTVERTADJUST_BLOCK; + eTHA = SDRTEXTHORZADJUST_CENTER; + + // read text anchor + auto eTextAnchor = GetPropertyValue(DFF_Prop_anchorText, mso_anchorTop); + + switch( eTextAnchor ) + { + case mso_anchorTop: + case mso_anchorTopCentered: + eTHA = SDRTEXTHORZADJUST_RIGHT; + break; + + case mso_anchorMiddle : + case mso_anchorMiddleCentered: + eTHA = SDRTEXTHORZADJUST_CENTER; + break; + + case mso_anchorBottom: + case mso_anchorBottomCentered: + eTHA = SDRTEXTHORZADJUST_LEFT; + break; + + default: + break; + } + // if there is a 100% use of following attributes, the textbox can been aligned also in vertical direction + switch ( eTextAnchor ) + { + case mso_anchorTopCentered : + case mso_anchorMiddleCentered : + case mso_anchorBottomCentered : + { + // check if it is sensible to use the centered alignment + sal_uInt32 nMask = PPT_TEXTOBJ_FLAGS_PARA_ALIGNMENT_USED_LEFT | PPT_TEXTOBJ_FLAGS_PARA_ALIGNMENT_USED_RIGHT; + if ( ( nTextFlags & nMask ) != nMask ) // if the textobject has left or also right aligned paragraphs + eTVA = SDRTEXTVERTADJUST_CENTER; // the text has to be displayed using the full width; + } + break; + + default : + { + if ( nTextFlags == PPT_TEXTOBJ_FLAGS_PARA_ALIGNMENT_USED_LEFT ) + eTVA = SDRTEXTVERTADJUST_TOP; + else if ( nTextFlags == PPT_TEXTOBJ_FLAGS_PARA_ALIGNMENT_USED_RIGHT ) + eTVA = SDRTEXTVERTADJUST_BOTTOM; + } + break; + } + } + else + { + eTVA = SDRTEXTVERTADJUST_CENTER; + eTHA = SDRTEXTHORZADJUST_BLOCK; + + // read text anchor + auto eTextAnchor = GetPropertyValue(DFF_Prop_anchorText, mso_anchorTop); + + switch( eTextAnchor ) + { + case mso_anchorTop: + case mso_anchorTopCentered: + eTVA = SDRTEXTVERTADJUST_TOP; + break; + + case mso_anchorMiddle : + case mso_anchorMiddleCentered: + eTVA = SDRTEXTVERTADJUST_CENTER; + break; + + case mso_anchorBottom: + case mso_anchorBottomCentered: + eTVA = SDRTEXTVERTADJUST_BOTTOM; + break; + + default: + break; + } + + // if there is a 100% usage of following attributes, the textbox can be aligned also in horizontal direction + switch ( eTextAnchor ) + { + case mso_anchorTopCentered : + case mso_anchorMiddleCentered : + case mso_anchorBottomCentered : + { + // check if it is sensible to use the centered alignment + sal_uInt32 nMask = PPT_TEXTOBJ_FLAGS_PARA_ALIGNMENT_USED_LEFT | PPT_TEXTOBJ_FLAGS_PARA_ALIGNMENT_USED_RIGHT; + if ( ( nTextFlags & nMask ) != nMask ) // if the textobject has left or also right aligned paragraphs + eTHA = SDRTEXTHORZADJUST_CENTER; // the text has to be displayed using the full width; + } + break; + + default : + { + if ( nTextFlags == PPT_TEXTOBJ_FLAGS_PARA_ALIGNMENT_USED_LEFT ) + eTHA = SDRTEXTHORZADJUST_LEFT; + else if ( nTextFlags == PPT_TEXTOBJ_FLAGS_PARA_ALIGNMENT_USED_RIGHT ) + eTHA = SDRTEXTHORZADJUST_RIGHT; + } + break; + } + } + rSet.Put( SdrTextVertAdjustItem( eTVA ) ); + rSet.Put( SdrTextHorzAdjustItem( eTHA ) ); +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/filter/source/msfilter/svxmsbas2.cxx b/filter/source/msfilter/svxmsbas2.cxx new file mode 100644 index 000000000..8a4ce43f1 --- /dev/null +++ b/filter/source/msfilter/svxmsbas2.cxx @@ -0,0 +1,80 @@ +/* -*- 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 + +#include +#include +#include +#include +#include + +using namespace com::sun::star; + +ErrCode SvxImportMSVBasic::SaveOrDelMSVBAStorage( bool bSaveInto, + const OUString& rStorageName ) +{ + ErrCode nRet = ERRCODE_NONE; + uno::Reference < embed::XStorage > xSrcRoot( rDocSh.GetStorage() ); + OUString aDstStgName( GetMSBasicStorageName() ); + tools::SvRef xVBAStg( SotStorage::OpenOLEStorage( xSrcRoot, aDstStgName, + StreamMode::READWRITE | StreamMode::NOCREATE | StreamMode::SHARE_DENYALL ) ); + if( xVBAStg.is() && !xVBAStg->GetError() ) + { + xVBAStg = nullptr; + if( bSaveInto ) + { +#if HAVE_FEATURE_SCRIPTING + BasicManager *pBasicMan = rDocSh.GetBasicManager(); + if( pBasicMan && pBasicMan->IsBasicModified() ) + nRet = ERRCODE_SVX_MODIFIED_VBASIC_STORAGE; +#endif + tools::SvRef xSrc = SotStorage::OpenOLEStorage( xSrcRoot, aDstStgName, StreamMode::STD_READ ); + tools::SvRef xDst = xRoot->OpenSotStorage( rStorageName, StreamMode::READWRITE | StreamMode::TRUNC ); + xSrc->CopyTo( xDst.get() ); + xDst->Commit(); + ErrCode nError = xDst->GetError(); + if ( nError == ERRCODE_NONE ) + nError = xSrc->GetError(); + if ( nError != ERRCODE_NONE ) + xRoot->SetError( nError ); + } + } + + return nRet; +} + +// check if the MS-VBA-Storage exists in the RootStorage of the DocShell. +// If it exists, then return the WarningId for losing the information. +ErrCode SvxImportMSVBasic::GetSaveWarningOfMSVBAStorage( SfxObjectShell &rDocSh) +{ + uno::Reference < embed::XStorage > xSrcRoot( rDocSh.GetStorage() ); + tools::SvRef xVBAStg( SotStorage::OpenOLEStorage( xSrcRoot, GetMSBasicStorageName(), + StreamMode::READ | StreamMode::NOCREATE | StreamMode::SHARE_DENYALL )); + return ( xVBAStg.is() && !xVBAStg->GetError() ) + ? ERRCODE_SVX_VBASIC_STORAGE_EXIST + : ERRCODE_NONE; +} + +OUString SvxImportMSVBasic::GetMSBasicStorageName() +{ + return "_MS_VBA_Macros"; +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/filter/source/msfilter/util.cxx b/filter/source/msfilter/util.cxx new file mode 100644 index 000000000..aea2f816b --- /dev/null +++ b/filter/source/msfilter/util.cxx @@ -0,0 +1,1340 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace msfilter::util { + +rtl_TextEncoding getBestTextEncodingFromLocale(const css::lang::Locale &rLocale) +{ + // Obviously not comprehensive, feel free to expand these, they're for ultimate fallbacks + // in last-ditch broken-file-format cases to guess the right 8bit encodings + const OUString &rLanguage = rLocale.Language; + if (rLanguage == "cs" || rLanguage == "hu" || rLanguage == "pl") + return RTL_TEXTENCODING_MS_1250; + if (rLanguage == "ru" || rLanguage == "uk") + return RTL_TEXTENCODING_MS_1251; + if (rLanguage == "el") + return RTL_TEXTENCODING_MS_1253; + if (rLanguage == "tr") + return RTL_TEXTENCODING_MS_1254; + if (rLanguage == "lt") + return RTL_TEXTENCODING_MS_1257; + if (rLanguage == "th") + return RTL_TEXTENCODING_MS_874; + if (rLanguage == "vi") + return RTL_TEXTENCODING_MS_1258; + return RTL_TEXTENCODING_MS_1252; +} + +::Color BGRToRGB(sal_uInt32 nColor) +{ + sal_uInt8 + r(static_cast(nColor&0xFF)), + g(static_cast((nColor>>8)&0xFF)), + b(static_cast((nColor>>16)&0xFF)), + t(static_cast((nColor>>24)&0xFF)); + return ::Color(ColorTransparency, t, r, g, b); +} + +DateTime DTTM2DateTime( tools::Long lDTTM ) +{ + /* + mint short :6 0000003F minutes (0-59) + hr short :5 000007C0 hours (0-23) + dom short :5 0000F800 days of month (1-31) + mon short :4 000F0000 months (1-12) + yr short :9 1FF00000 years (1900-2411)-1900 + wdy short :3 E0000000 weekday(Sunday=0 + Monday=1 + ( wdy can be ignored ) Tuesday=2 + Wednesday=3 + Thursday=4 + Friday=5 + Saturday=6) + */ + DateTime aDateTime(Date( 0 ), ::tools::Time( 0 )); + if( lDTTM ) + { + sal_uInt16 lMin = static_cast(lDTTM & 0x0000003F); + lDTTM >>= 6; + sal_uInt16 lHour= static_cast(lDTTM & 0x0000001F); + lDTTM >>= 5; + sal_uInt16 lDay = static_cast(lDTTM & 0x0000001F); + lDTTM >>= 5; + sal_uInt16 lMon = static_cast(lDTTM & 0x0000000F); + lDTTM >>= 4; + sal_uInt16 lYear= static_cast(lDTTM & 0x000001FF) + 1900; + aDateTime = DateTime(Date(lDay, lMon, lYear), tools::Time(lHour, lMin)); + } + return aDateTime; +} + +sal_Unicode bestFitOpenSymbolToMSFont(sal_Unicode cChar, + rtl_TextEncoding& rChrSet, OUString& rFontName) +{ + std::unique_ptr pConvert(CreateStarSymbolToMSMultiFont()); + OUString sFont = pConvert->ConvertChar(cChar); + pConvert.reset(); + if (!sFont.isEmpty()) + { + cChar = static_cast< sal_Unicode >(cChar | 0xF000); + rFontName = sFont; + rChrSet = RTL_TEXTENCODING_SYMBOL; + } + else if (cChar < 0xE000 || cChar > 0xF8FF) + { + /* + Ok we can't fit into a known windows unicode font, but + we are not in the private area, so we are a + standardized symbol, so turn off the symbol bit and + let words own font substitution kick in + */ + rChrSet = RTL_TEXTENCODING_UNICODE; + sal_Int32 nIndex = 0; + rFontName = ::GetNextFontToken(rFontName, nIndex); + } + else + { + /* + Well we don't have an available substitution, and we're + in our private area, so give up and show a standard + bullet symbol + */ + rFontName = "Wingdings"; + cChar = u'\x6C'; + } + return cChar; +} + + +OString ConvertColor( const Color &rColor ) +{ + static constexpr OStringLiteral AUTO( "auto" ); + + if ( rColor == COL_AUTO ) + return AUTO; + + const char pHexDigits[] = "0123456789ABCDEF"; + char pBuffer[] = "000000"; + + pBuffer[0] = pHexDigits[ ( rColor.GetRed() >> 4 ) & 0x0F ]; + pBuffer[1] = pHexDigits[ rColor.GetRed() & 0x0F ]; + pBuffer[2] = pHexDigits[ ( rColor.GetGreen() >> 4 ) & 0x0F ]; + pBuffer[3] = pHexDigits[ rColor.GetGreen() & 0x0F ]; + pBuffer[4] = pHexDigits[ ( rColor.GetBlue() >> 4 ) & 0x0F ]; + pBuffer[5] = pHexDigits[ rColor.GetBlue() & 0x0F ]; + + return OString( pBuffer ); +} + +OUString ConvertColorOU( const Color &rColor ) +{ + static constexpr OUStringLiteral AUTO( u"auto" ); + + if ( rColor == COL_AUTO ) + return AUTO; + + const char pHexDigits[] = "0123456789ABCDEF"; + sal_Unicode pBuffer[] = u"000000"; + + pBuffer[0] = pHexDigits[ ( rColor.GetRed() >> 4 ) & 0x0F ]; + pBuffer[1] = pHexDigits[ rColor.GetRed() & 0x0F ]; + pBuffer[2] = pHexDigits[ ( rColor.GetGreen() >> 4 ) & 0x0F ]; + pBuffer[3] = pHexDigits[ rColor.GetGreen() & 0x0F ]; + pBuffer[4] = pHexDigits[ ( rColor.GetBlue() >> 4 ) & 0x0F ]; + pBuffer[5] = pHexDigits[ rColor.GetBlue() & 0x0F ]; + + return OUString( pBuffer ); +} + +#define IN2MM100( v ) static_cast< sal_Int32 >( (v) * 2540.0 + 0.5 ) +#define MM2MM100( v ) static_cast< sal_Int32 >( (v) * 100.0 + 0.5 ) + +// see XclPaperSize pPaperSizeTable in calc and aDinTab in i18nutil +const ApiPaperSize spPaperSizeTable[] = +{ + { 0, 0 }, // 0 - (undefined) + { IN2MM100( 8.5 ), IN2MM100( 11 ) }, // 1 - Letter paper + { IN2MM100( 8.5 ), IN2MM100( 11 ) }, // 2 - Letter small paper + { IN2MM100( 11 ), IN2MM100( 17 ) }, // 3 - Tabloid paper + { IN2MM100( 17 ), IN2MM100( 11 ) }, // 4 - Ledger paper + { IN2MM100( 8.5 ), IN2MM100( 14 ) }, // 5 - Legal paper + { IN2MM100( 5.5 ), IN2MM100( 8.5 ) }, // 6 - Statement paper + { IN2MM100( 7.25 ), IN2MM100( 10.5 ) }, // 7 - Executive paper + { MM2MM100( 297 ), MM2MM100( 420 ) }, // 8 - A3 paper + { MM2MM100( 210 ), MM2MM100( 297 ) }, // 9 - A4 paper + { MM2MM100( 210 ), MM2MM100( 297 ) }, // 10 - A4 small paper + { MM2MM100( 148 ), MM2MM100( 210 ) }, // 11 - A5 paper + /* for JIS vs ISO B confusion see: + https://docs.microsoft.com/en-us/windows/win32/intl/paper-sizes + http://wiki.openoffice.org/wiki/DefaultPaperSize comments + http://partners.adobe.com/public/developer/en/ps/5003.PPD_Spec_v4.3.pdf */ + { MM2MM100( 257 ), MM2MM100( 364 ) }, // 12 - B4 (JIS) paper + { MM2MM100( 182 ), MM2MM100( 257 ) }, // 13 - B5 (JIS) paper + { IN2MM100( 8.5 ), IN2MM100( 13 ) }, // 14 - Folio paper + { MM2MM100( 215 ), MM2MM100( 275 ) }, // 15 - Quarto paper + { IN2MM100( 10 ), IN2MM100( 14 ) }, // 16 - Standard paper + { IN2MM100( 11 ), IN2MM100( 17 ) }, // 17 - Standard paper + { IN2MM100( 8.5 ), IN2MM100( 11 ) }, // 18 - Note paper + { IN2MM100( 3.875 ), IN2MM100( 8.875 ) }, // 19 - #9 envelope + { IN2MM100( 4.125 ), IN2MM100( 9.5 ) }, // 20 - #10 envelope + { IN2MM100( 4.5 ), IN2MM100( 10.375 ) }, // 21 - #11 envelope + { IN2MM100( 4.75 ), IN2MM100( 11 ) }, // 22 - #12 envelope + { IN2MM100( 5 ), IN2MM100( 11.5 ) }, // 23 - #14 envelope + { IN2MM100( 17 ), IN2MM100( 22 ) }, // 24 - C paper + { IN2MM100( 22 ), IN2MM100( 34 ) }, // 25 - D paper + { IN2MM100( 34 ), IN2MM100( 44 ) }, // 26 - E paper + { MM2MM100( 110 ), MM2MM100( 220 ) }, // 27 - DL envelope + { MM2MM100( 162 ), MM2MM100( 229 ) }, // 28 - C5 envelope + { MM2MM100( 324 ), MM2MM100( 458 ) }, // 29 - C3 envelope + { MM2MM100( 229 ), MM2MM100( 324 ) }, // 30 - C4 envelope + { MM2MM100( 114 ), MM2MM100( 162 ) }, // 31 - C6 envelope + { MM2MM100( 114 ), MM2MM100( 229 ) }, // 32 - C65 envelope + { MM2MM100( 250 ), MM2MM100( 353 ) }, // 33 - B4 envelope + { MM2MM100( 176 ), MM2MM100( 250 ) }, // 34 - B5 envelope + { MM2MM100( 176 ), MM2MM100( 125 ) }, // 35 - B6 envelope + { MM2MM100( 110 ), MM2MM100( 230 ) }, // 36 - Italy envelope + { IN2MM100( 3.875 ), IN2MM100( 7.5 ) }, // 37 - Monarch envelope + { IN2MM100( 3.625 ), IN2MM100( 6.5 ) }, // 38 - 6 3/4 envelope + { IN2MM100( 14.875 ), IN2MM100( 11 ) }, // 39 - US standard fanfold + { IN2MM100( 8.5 ), IN2MM100( 12 ) }, // 40 - German standard fanfold + { IN2MM100( 8.5 ), IN2MM100( 13 ) }, // 41 - German legal fanfold + { MM2MM100( 250 ), MM2MM100( 353 ) }, // 42 - ISO B4 + { MM2MM100( 200 ), MM2MM100( 148 ) }, // 43 - Japanese double postcard + { IN2MM100( 9 ), IN2MM100( 11 ) }, // 44 - Standard paper + { IN2MM100( 10 ), IN2MM100( 11 ) }, // 45 - Standard paper + { IN2MM100( 15 ), IN2MM100( 11 ) }, // 46 - Standard paper + { MM2MM100( 220 ), MM2MM100( 220 ) }, // 47 - Invite envelope + { 0, 0 }, // 48 - (undefined) + { 0, 0 }, // 49 - (undefined) + /* See: https://docs.microsoft.com/en-us/windows/win32/intl/paper-sizes */ + { IN2MM100( 9.5 ), IN2MM100( 12 ) }, // 50 - Letter extra paper + { IN2MM100( 9.5 ), IN2MM100( 15 ) }, // 51 - Legal extra paper + { IN2MM100( 11.69 ), IN2MM100( 18 ) }, // 52 - Tabloid extra paper + { MM2MM100( 235 ), MM2MM100( 322 ) }, // 53 - A4 extra paper + { IN2MM100( 8.5 ), IN2MM100( 11 ) }, // 54 - Letter transverse paper + { MM2MM100( 210 ), MM2MM100( 297 ) }, // 55 - A4 transverse paper + { IN2MM100( 9.5 ), IN2MM100( 12 ) }, // 56 - Letter extra transverse paper + { MM2MM100( 227 ), MM2MM100( 356 ) }, // 57 - SuperA/SuperA/A4 paper + { MM2MM100( 305 ), MM2MM100( 487 ) }, // 58 - SuperB/SuperB/A3 paper + { IN2MM100( 8.5 ), IN2MM100( 12.69 ) }, // 59 - Letter plus paper + { MM2MM100( 210 ), MM2MM100( 330 ) }, // 60 - A4 plus paper + { MM2MM100( 148 ), MM2MM100( 210 ) }, // 61 - A5 transverse paper + { MM2MM100( 182 ), MM2MM100( 257 ) }, // 62 - JIS B5 transverse paper + { MM2MM100( 322 ), MM2MM100( 445 ) }, // 63 - A3 extra paper + { MM2MM100( 174 ), MM2MM100( 235 ) }, // 64 - A5 extra paper + { MM2MM100( 201 ), MM2MM100( 276 ) }, // 65 - ISO B5 extra paper + { MM2MM100( 420 ), MM2MM100( 594 ) }, // 66 - A2 paper + { MM2MM100( 297 ), MM2MM100( 420 ) }, // 67 - A3 transverse paper + { MM2MM100( 322 ), MM2MM100( 445 ) }, // 68 - A3 extra transverse paper + { MM2MM100( 200 ), MM2MM100( 148 ) }, // 69 - Japanese double postcard + { MM2MM100( 105 ), MM2MM100( 148 ), }, // 70 - A6 paper + { 0, 0 }, // 71 - Japanese Envelope Kaku #2 + { 0, 0 }, // 72 - Japanese Envelope Kaku #3 + { 0, 0 }, // 73 - Japanese Envelope Chou #3 + { 0, 0 }, // 74 - Japanese Envelope Chou #4 + { IN2MM100( 11 ), IN2MM100( 8.5 ) }, // 75 - Letter Rotated + { MM2MM100( 420 ), MM2MM100( 297 ) }, // 76 - A3 Rotated + { MM2MM100( 297 ), MM2MM100( 210 ) }, // 77 - A4 Rotated + { MM2MM100( 210 ), MM2MM100( 148 ) }, // 78 - A5 Rotated + { MM2MM100( 364 ), MM2MM100( 257 ) }, // 79 - B4 (JIS) Rotated + { MM2MM100( 257 ), MM2MM100( 182 ) }, // 80 - B5 (JIS) Rotated + { MM2MM100( 148 ), MM2MM100( 100 ) }, // 81 - Japanese Postcard Rotated + { MM2MM100( 148 ), MM2MM100( 200 ) }, // 82 - Double Japanese Postcard Rotated + { MM2MM100( 148 ), MM2MM100( 105 ) }, // 83 - A6 Rotated + { 0, 0 }, // 84 - Japanese Envelope Kaku #2 Rotated + { 0, 0 }, // 85 - Japanese Envelope Kaku #3 Rotated + { 0, 0 }, // 86 - Japanese Envelope Chou #3 Rotated + { 0, 0 }, // 87 - Japanese Envelope Chou #4 Rotated + { MM2MM100( 128 ), MM2MM100( 182 ) }, // 88 - B6 (JIS) + { MM2MM100( 182 ), MM2MM100( 128 ) }, // 89 - B6 (JIS) Rotated + { IN2MM100( 12 ), IN2MM100( 11 ) } // 90 - 12x11 +}; + +sal_Int32 PaperSizeConv::getMSPaperSizeIndex( const css::awt::Size& rSize ) +{ + // Need to find the best match for current size + sal_Int32 nDeltaWidth = 0; + sal_Int32 nDeltaHeight = 0; + + sal_Int32 nPaperSizeIndex = 0; // Undefined + const ApiPaperSize* pItem = spPaperSizeTable; + const ApiPaperSize* pEnd = spPaperSizeTable + SAL_N_ELEMENTS( spPaperSizeTable ); + for ( ; pItem != pEnd; ++pItem ) + { + sal_Int32 nCurDeltaHeight = std::abs( pItem->mnHeight - rSize.Height ); + sal_Int32 nCurDeltaWidth = std::abs( pItem->mnWidth - rSize.Width ); + if ( pItem == spPaperSizeTable ) // initialize delta with first item + { + nDeltaWidth = nCurDeltaWidth; + nDeltaHeight = nCurDeltaHeight; + } + else + { + if ( nCurDeltaWidth < nDeltaWidth && nCurDeltaHeight < nDeltaHeight ) + { + nDeltaWidth = nCurDeltaWidth; + nDeltaHeight = nCurDeltaHeight; + nPaperSizeIndex = (pItem - spPaperSizeTable); + } + } + } + sal_Int32 nTol = 10; // hmm not sure is this the best way + if ( nDeltaWidth <= nTol && nDeltaHeight <= nTol ) + return nPaperSizeIndex; + return 0; +} + +const ApiPaperSize& PaperSizeConv::getApiSizeForMSPaperSizeIndex( sal_Int32 nMSOPaperIndex ) +{ + if ( nMSOPaperIndex < 0 || nMSOPaperIndex > sal_Int32(SAL_N_ELEMENTS( spPaperSizeTable )) - 1 ) + return spPaperSizeTable[ 0 ]; + return spPaperSizeTable[ nMSOPaperIndex ]; +} + +std::u16string_view findQuotedText( std::u16string_view rCommand, + const char* cStartQuote, const sal_Unicode uEndQuote ) +{ + std::u16string_view sRet; + OUString sStartQuote( OUString::createFromAscii(cStartQuote) ); + size_t nStartIndex = rCommand.find( sStartQuote ); + if( nStartIndex != std::u16string_view::npos ) + { + sal_Int32 nStartLength = sStartQuote.getLength(); + size_t nEndIndex = rCommand.find( uEndQuote, nStartIndex + nStartLength); + if( nEndIndex != std::u16string_view::npos && nEndIndex > nStartIndex ) + { + sRet = rCommand.substr( nStartIndex + nStartLength, nEndIndex - nStartIndex - nStartLength); + } + } + return sRet; + +} + +WW8ReadFieldParams::WW8ReadFieldParams( OUString _aData ) + : aData(std::move( _aData )) + , nFnd( 0 ) + , nNext( 0 ) + , nSavPtr( 0 ) +{ + + /* + First look for an opening bracket or a space or a question mark or a backslash, so that the field (i.e. INCLUDEPICTURE or EINFUEGENGRAFIK or...) gets read over + */ + const sal_Int32 nLen = aData.getLength(); + + while ( nNextnFnd) + return OUString(); + else + { + return nSavPtr < nFnd ? aData.copy(nFnd) : aData.copy(nFnd, nSavPtr-nFnd); + } +} + + +bool WW8ReadFieldParams::GoToTokenParam() +{ + const sal_Int32 nOld = nNext; + if( -2 == SkipToNextToken() ) + return GetTokenSttPtr()>=0; + nNext = nOld; + return false; +} + +// ret: -2: NOT a '\' parameter but normal text +sal_Int32 WW8ReadFieldParams::SkipToNextToken() +{ + if ( nNext<0 || nNext>=aData.getLength() ) + return -1; + + nFnd = FindNextStringPiece(nNext); + if ( nFnd<0 ) + return -1; + + nSavPtr = nNext; + + if (nFnd+10 && (aData[nSavPtr-1]=='"' || aData[nSavPtr-1]==0x201d ) ) + { + --nSavPtr; + } + return -2; +} + +// FindNextPara searches the next backslash parameter or the next string +// until the next blank or "\" or closing quotation mark +// or the end of the string of pStr. +// +// Output ppNext (if ppNext != 0) search begin of next parameter resp. 0 +// +// Return value: 0 if end of string reached, +// otherwise beginning of the parameter resp. string +// +sal_Int32 WW8ReadFieldParams::FindNextStringPiece(const sal_Int32 nStart) +{ + const sal_Int32 nLen = aData.getLength(); + sal_Int32 n = nStart<0 ? nFnd : nStart; // start + sal_Int32 n2; // end + + nNext = -1; // if not found -> Default + + while ( n n2) + && (aData[n2] != '"') + && (aData[n2] != 0x201d) + && (aData[n2] != 147) + && (aData[n2] != 0x15) ) + n2++; // search for the end of the paragraph + } + else // no quotation mark + { + n2 = n; // search for the end from here on + while ( n2 OK + else + { + if( n2 > n ) + n2--; + break; // single backslash -> end + } + } + else + n2++; // no backslash -> OK + } + } + if( nLen > n2 ) + { + if (aData[n2]!=' ') ++n2; + nNext = n2; + } + return n; +} + + +// read parameters "1-3" or 1-3 with both values between 1 and nMax +bool WW8ReadFieldParams::GetTokenSttFromTo(sal_Int32* pFrom, sal_Int32* pTo, sal_Int32 nMax) +{ + sal_Int32 nStart = 0; + sal_Int32 nEnd = 0; + if ( GoToTokenParam() ) + { + + const OUString sParams( GetResult() ); + + sal_Int32 nIndex = 0; + const std::u16string_view sStart = o3tl::getToken(sParams, 0, '-', nIndex); + if (nIndex>=0) + { + nStart = o3tl::toInt32(sStart); + nEnd = o3tl::toInt32(sParams.subView(nIndex)); + } + } + if( pFrom ) *pFrom = nStart; + if( pTo ) *pTo = nEnd; + + return nStart && nEnd && (nMax >= nStart) && (nMax >= nEnd); +} + +static EquationResult Read_SubF_Combined(WW8ReadFieldParams& rReadParam) +{ + EquationResult aResult; + + OUString sCombinedCharacters; + WW8ReadFieldParams aOriFldParam = rReadParam; + const sal_Int32 cGetChar = rReadParam.SkipToNextToken(); + switch( cGetChar ) + { + case 'a': + case 'A': + if ( !rReadParam.GetResult().startsWithIgnoreAsciiCase("d") ) + { + break; + } + (void)rReadParam.SkipToNextToken(); + [[fallthrough]]; + case -2: + { + if ( rReadParam.GetResult().startsWithIgnoreAsciiCase("(") ) + { + for (int i=0;i<2;i++) + { + if ('s' == rReadParam.SkipToNextToken()) + { + const sal_Int32 cChar = rReadParam.SkipToNextToken(); + if (-2 != rReadParam.SkipToNextToken()) + break; + const OUString sF = rReadParam.GetResult(); + if ((('u' == cChar) && sF.startsWithIgnoreAsciiCase("p")) + || (('d' == cChar) && sF.startsWithIgnoreAsciiCase("o"))) + { + if (-2 == rReadParam.SkipToNextToken()) + { + OUString sPart = rReadParam.GetResult(); + sal_Int32 nBegin = sPart.indexOf('('); + + // Word disallows brackets in this field, which + // aids figuring out the case of an end of )) vs ) + sal_Int32 nEnd = sPart.indexOf(')'); + + if (nBegin != -1 && nEnd != -1) + { + sCombinedCharacters += + sPart.subView(nBegin+1,nEnd-nBegin-1); + } + } + } + } + } + if (!sCombinedCharacters.isEmpty()) + { + aResult.sType = "CombinedCharacters"; + aResult.sResult = sCombinedCharacters; + } + else + { + const OUString sPart = aOriFldParam.GetResult(); + sal_Int32 nBegin = sPart.indexOf('('); + sal_Int32 nEnd = sPart.indexOf(','); + if ( nEnd == -1 ) + { + nEnd = sPart.indexOf(')'); + } + if ( nBegin != -1 && nEnd != -1 ) + { + // skip certain leading characters + for (int i = nBegin;i < nEnd-1;i++) + { + const sal_Unicode cC = sPart[nBegin+1]; + if ( cC < 32 ) + { + nBegin++; + } + else + break; + } + sCombinedCharacters = sPart.copy( nBegin+1, nEnd-nBegin-1 ); + if ( !sCombinedCharacters.isEmpty() ) + { + aResult.sType = "Input"; + aResult.sResult = sCombinedCharacters; + } + } + } + } + break; + } + default: + break; + } + return aResult; +} + +EquationResult ParseCombinedChars(const OUString& rStr) +{ + EquationResult aResult; + WW8ReadFieldParams aReadParam( rStr ); + const sal_Int32 cChar = aReadParam.SkipToNextToken(); + if ('o' == cChar || 'O' == cChar) + aResult = Read_SubF_Combined(aReadParam); + return aResult; +} + +OString GetOOXMLPresetGeometry( std::u16string_view rShapeType ) +{ + typedef std::unordered_map CustomShapeTypeTranslationHashMap; + static const CustomShapeTypeTranslationHashMap aCustomShapeTypeTranslationHashMap{ + // { "non-primitive", mso_sptMin }, + { u"frame", "frame" }, + { u"rectangle", "rect" }, + { u"round-rectangle", "roundRect" }, + { u"ellipse", "ellipse" }, + { u"diamond", "diamond" }, + { u"isosceles-triangle", "triangle" }, + { u"right-triangle", "rtTriangle" }, + { u"parallelogram", "parallelogram" }, + { u"trapezoid", "trapezoid" }, + { u"hexagon", "hexagon" }, + { u"octagon", "octagon" }, + { u"cross", "plus" }, + { u"star5", "star5" }, + { u"right-arrow", "rightArrow" }, + // { u"mso-spt14", mso_sptThickArrow }, + { u"pentagon-right", "homePlate" }, + { u"cube", "cube" }, + // { u"mso-spt17", mso_sptBalloon }, + // { u"mso-spt18", mso_sptSeal }, + { u"mso-spt19", "arc" }, + { u"mso-spt20", "line" }, + { u"mso-spt21", "plaque" }, + { u"can", "can" }, + { u"ring", "donut" }, + { u"mso-spt24", "textPlain" }, + { u"mso-spt25", "textStop" }, + { u"mso-spt26", "textTriangle" }, + { u"mso-spt27", "textCanDown" }, + { u"mso-spt28", "textWave1" }, + { u"mso-spt29", "textArchUpPour" }, + { u"mso-spt30", "textCanDown" }, + { u"mso-spt31", "textArchUp" }, + { u"mso-spt32", "straightConnector1" }, + { u"mso-spt33", "bentConnector2" }, + { u"mso-spt34", "bentConnector3" }, + { u"mso-spt35", "bentConnector4" }, + { u"mso-spt36", "bentConnector5" }, + { u"mso-spt37", "curvedConnector2" }, + { u"mso-spt38", "curvedConnector3" }, + { u"mso-spt39", "curvedConnector4" }, + { u"mso-spt40", "curvedConnector5" }, + { u"mso-spt41", "callout1" }, + { u"mso-spt42", "callout2" }, + { u"mso-spt43", "callout3" }, + { u"mso-spt44", "accentCallout1" }, + { u"mso-spt45", "accentCallout2" }, + { u"mso-spt46", "accentCallout3" }, + { u"line-callout-1", "borderCallout1" }, + { u"line-callout-2", "borderCallout2" }, + { u"line-callout-3", "borderCallout3" }, + { u"mso-spt49", "borderCallout3" }, + { u"mso-spt50", "accentBorderCallout1" }, + { u"mso-spt51", "accentBorderCallout2" }, + { u"mso-spt52", "accentBorderCallout3" }, + { u"mso-spt53", "ribbon" }, + { u"mso-spt54", "ribbon2" }, + { u"chevron", "chevron" }, + { u"pentagon", "pentagon" }, + { u"forbidden", "noSmoking" }, + { u"star8", "star8" }, + { u"mso-spt59", "star16" }, + { u"mso-spt60", "star32" }, + { u"rectangular-callout", "wedgeRectCallout" }, + { u"round-rectangular-callout", "wedgeRoundRectCallout" }, + { u"round-callout", "wedgeEllipseCallout" }, + { u"mso-spt64", "wave" }, + { u"paper", "foldedCorner" }, + { u"left-arrow", "leftArrow" }, + { u"down-arrow", "downArrow" }, + { u"up-arrow", "upArrow" }, + { u"left-right-arrow", "leftRightArrow" }, + { u"up-down-arrow", "upDownArrow" }, + { u"mso-spt71", "irregularSeal1" }, + { u"bang", "irregularSeal2" }, + { u"lightning", "lightningBolt" }, + { u"heart", "heart" }, + { u"quad-arrow", "quadArrow" }, + { u"left-arrow-callout", "leftArrowCallout" }, + { u"right-arrow-callout", "rightArrowCallout" }, + { u"up-arrow-callout", "upArrowCallout" }, + { u"down-arrow-callout", "downArrowCallout" }, + { u"left-right-arrow-callout", "leftRightArrowCallout" }, + { u"up-down-arrow-callout", "upDownArrowCallout" }, + { u"quad-arrow-callout", "quadArrowCallout" }, + { u"quad-bevel", "bevel" }, + { u"left-bracket", "leftBracket" }, + { u"right-bracket", "rightBracket" }, + { u"left-brace", "leftBrace" }, + { u"right-brace", "rightBrace" }, + { u"mso-spt89", "leftUpArrow" }, + { u"mso-spt90", "bentUpArrow" }, + { u"mso-spt91", "bentArrow" }, + { u"star24", "star24" }, + { u"striped-right-arrow", "stripedRightArrow" }, + { u"notched-right-arrow", "notchedRightArrow" }, + { u"block-arc", "blockArc" }, + { u"smiley", "smileyFace" }, + { u"vertical-scroll", "verticalScroll" }, + { u"horizontal-scroll", "horizontalScroll" }, + { u"circular-arrow", "circularArrow" }, + { u"mso-spt100", "pie" }, // looks like MSO_SPT is wrong here + { u"mso-spt101", "uturnArrow" }, + { u"mso-spt102", "curvedRightArrow" }, + { u"mso-spt103", "curvedLeftArrow" }, + { u"mso-spt104", "curvedUpArrow" }, + { u"mso-spt105", "curvedDownArrow" }, + { u"cloud-callout", "cloudCallout" }, + { u"mso-spt107", "ellipseRibbon" }, + { u"mso-spt108", "ellipseRibbon2" }, + { u"flowchart-process", "flowChartProcess" }, + { u"flowchart-decision", "flowChartDecision" }, + { u"flowchart-data", "flowChartInputOutput" }, + { u"flowchart-predefined-process", "flowChartPredefinedProcess" }, + { u"flowchart-internal-storage", "flowChartInternalStorage" }, + { u"flowchart-document", "flowChartDocument" }, + { u"flowchart-multidocument", "flowChartMultidocument" }, + { u"flowchart-terminator", "flowChartTerminator" }, + { u"flowchart-preparation", "flowChartPreparation" }, + { u"flowchart-manual-input", "flowChartManualInput" }, + { u"flowchart-manual-operation", "flowChartManualOperation" }, + { u"flowchart-connector", "flowChartConnector" }, + { u"flowchart-card", "flowChartPunchedCard" }, + { u"flowchart-punched-tape", "flowChartPunchedTape" }, + { u"flowchart-summing-junction", "flowChartSummingJunction" }, + { u"flowchart-or", "flowChartOr" }, + { u"flowchart-collate", "flowChartCollate" }, + { u"flowchart-sort", "flowChartSort" }, + { u"flowchart-extract", "flowChartExtract" }, + { u"flowchart-merge", "flowChartMerge" }, + { u"mso-spt129", "flowChartOfflineStorage" }, + { u"flowchart-stored-data", "flowChartOnlineStorage" }, + { u"flowchart-sequential-access", "flowChartMagneticTape" }, + { u"flowchart-magnetic-disk", "flowChartMagneticDisk" }, + { u"flowchart-direct-access-storage", "flowChartMagneticDrum" }, + { u"flowchart-display", "flowChartDisplay" }, + { u"flowchart-delay", "flowChartDelay" }, + // { u"fontwork-plain-text", "textPlainText" }, + // { u"fontwork-stop", "textStop" }, + // { u"fontwork-triangle-up", "textTriangle" }, + // { u"fontwork-triangle-down", "textTriangleInverted" }, + // { u"fontwork-chevron-up", "textChevron" }, + // { u"fontwork-chevron-down", "textChevronInverted" }, + // { u"mso-spt142", "textRingInside" }, + // { u"mso-spt143", "textRingOutside" }, + // { u"fontwork-arch-up-curve", "textArchUpCurve" }, + // { u"fontwork-arch-down-curve", "textArchDownCurve" }, + // { u"fontwork-circle-curve", "textCircleCurve" }, + // { u"fontwork-open-circle-curve", "textButtonCurve" }, + // { u"fontwork-arch-up-pour", "textArchUpPour" }, + // { u"fontwork-arch-down-pour", "textArchDownPour" }, + // { u"fontwork-circle-pour", "textCirclePour" }, + // { u"fontwork-open-circle-pour", "textButtonPour" }, + // { u"fontwork-curve-up", "textCurveUp" }, + // { u"fontwork-curve-down", "textCurveDown" }, + // { u"fontwork-fade-up-and-right", "textCascadeUp" }, + // { u"fontwork-fade-up-and-left", "textCascadeDown" }, + // { u"fontwork-wave", "textWave1" }, + // { u"mso-spt157", "textWave2" }, + // { u"mso-spt158", "textWave3" }, + // { u"mso-spt159", "textWave4" }, + // { u"fontwork-inflate", "textInflate" }, + // { u"mso-spt161", "textDeflate" }, + // { u"mso-spt162", "textInflateBottom" }, + // { u"mso-spt163", "textDeflateBottom" }, + // { u"mso-spt164", "textInflateTop" }, + // { u"mso-spt165", "textDeflateTop" }, + // { u"mso-spt166", "textDeflateInflate" }, + // { u"mso-spt167", "textDeflateInflateDeflate" }, + // { u"fontwork-fade-right", "textFadeRight" }, + // { u"fontwork-fade-left", "textFadeLeft" }, + // { u"fontwork-fade-up", "textFadeUp" }, + // { u"fontwork-fade-down", "textFadeDown" }, + // { u"fontwork-slant-up", "textSlantUp" }, + // { u"fontwork-slant-down", "textSlantDown" }, + // { u"mso-spt174", "textCanUp" }, + // { u"mso-spt175", "textCanDown" }, + { u"flowchart-alternate-process", "flowChartAlternateProcess" }, + { u"flowchart-off-page-connector", "flowChartOffpageConnector" }, + { u"mso-spt178", "callout1" }, + { u"mso-spt179", "accentCallout1" }, + { u"mso-spt180", "borderCallout1" }, + { u"mso-spt182", "leftRightUpArrow" }, + { u"sun", "sun" }, + { u"moon", "moon" }, + { u"bracket-pair", "bracketPair" }, + { u"brace-pair", "bracePair" }, + { u"star4", "star4" }, + { u"mso-spt188", "doubleWave" }, + { u"mso-spt189", "actionButtonBlank" }, + { u"mso-spt190", "actionButtonHome" }, + { u"mso-spt191", "actionButtonHelp" }, + { u"mso-spt192", "actionButtonInformation" }, + { u"mso-spt193", "actionButtonForwardNext" }, + { u"mso-spt194", "actionButtonBackPrevious" }, + { u"mso-spt195", "actionButtonEnd" }, + { u"mso-spt196", "actionButtonBeginning" }, + { u"mso-spt197", "actionButtonReturn" }, + { u"mso-spt198", "actionButtonDocument" }, + { u"mso-spt199", "actionButtonSound" }, + { u"mso-spt200", "actionButtonMovie" }, + // { u"mso-spt201", "hostControl" }, + { u"mso-spt202", "rect" }, + { u"ooxml-actionButtonSound", "actionButtonSound" }, + { u"ooxml-borderCallout1", "borderCallout1" }, + { u"ooxml-plaqueTabs", "plaqueTabs" }, + { u"ooxml-curvedLeftArrow", "curvedLeftArrow" }, + { u"ooxml-octagon", "octagon" }, + { u"ooxml-leftRightRibbon", "leftRightRibbon" }, + { u"ooxml-actionButtonInformation", "actionButtonInformation" }, + { u"ooxml-bentConnector5", "bentConnector5" }, + { u"ooxml-circularArrow", "circularArrow" }, + { u"ooxml-downArrowCallout", "downArrowCallout" }, + { u"ooxml-mathMinus", "mathMinus" }, + { u"ooxml-gear9", "gear9" }, + { u"ooxml-round1Rect", "round1Rect" }, + { u"ooxml-sun", "sun" }, + { u"ooxml-plaque", "plaque" }, + { u"ooxml-chevron", "chevron" }, + { u"ooxml-flowChartPreparation", "flowChartPreparation" }, + { u"ooxml-diagStripe", "diagStripe" }, + { u"ooxml-pentagon", "pentagon" }, + { u"ooxml-funnel", "funnel" }, + { u"ooxml-chartStar", "chartStar" }, + { u"ooxml-accentBorderCallout1", "accentBorderCallout1" }, + { u"ooxml-notchedRightArrow", "notchedRightArrow" }, + { u"ooxml-rightBracket", "rightBracket" }, + { u"ooxml-flowChartOffpageConnector", "flowChartOffpageConnector" }, + { u"ooxml-leftRightArrow", "leftRightArrow" }, + { u"ooxml-decagon", "decagon" }, + { u"ooxml-actionButtonHelp", "actionButtonHelp" }, + { u"ooxml-star24", "star24" }, + { u"ooxml-mathDivide", "mathDivide" }, + { u"ooxml-curvedConnector4", "curvedConnector4" }, + { u"ooxml-flowChartOr", "flowChartOr" }, + { u"ooxml-borderCallout3", "borderCallout3" }, + { u"ooxml-upDownArrowCallout", "upDownArrowCallout" }, + { u"ooxml-flowChartDecision", "flowChartDecision" }, + { u"ooxml-leftRightArrowCallout", "leftRightArrowCallout" }, + { u"ooxml-flowChartManualOperation", "flowChartManualOperation" }, + { u"ooxml-snipRoundRect", "snipRoundRect" }, + { u"ooxml-mathPlus", "mathPlus" }, + { u"ooxml-actionButtonForwardNext", "actionButtonForwardNext" }, + { u"ooxml-can", "can" }, + { u"ooxml-foldedCorner", "foldedCorner" }, + { u"ooxml-star32", "star32" }, + { u"ooxml-flowChartInternalStorage", "flowChartInternalStorage" }, + { u"ooxml-upDownArrow", "upDownArrow" }, + { u"ooxml-irregularSeal2", "irregularSeal2" }, + { u"ooxml-mathEqual", "mathEqual" }, + { u"ooxml-star12", "star12" }, + { u"ooxml-uturnArrow", "uturnArrow" }, + { u"ooxml-squareTabs", "squareTabs" }, + { u"ooxml-leftRightUpArrow", "leftRightUpArrow" }, + { u"ooxml-homePlate", "homePlate" }, + { u"ooxml-dodecagon", "dodecagon" }, + { u"ooxml-leftArrowCallout", "leftArrowCallout" }, + { u"ooxml-chord", "chord" }, + { u"ooxml-quadArrowCallout", "quadArrowCallout" }, + { u"ooxml-actionButtonBeginning", "actionButtonBeginning" }, + { u"ooxml-ellipse", "ellipse" }, + { u"ooxml-actionButtonEnd", "actionButtonEnd" }, + { u"ooxml-arc", "arc" }, + { u"ooxml-star16", "star16" }, + { u"ooxml-parallelogram", "parallelogram" }, + { u"ooxml-bevel", "bevel" }, + { u"ooxml-roundRect", "roundRect" }, + { u"ooxml-accentCallout1", "accentCallout1" }, + { u"ooxml-flowChartSort", "flowChartSort" }, + { u"ooxml-star8", "star8" }, + { u"ooxml-flowChartAlternateProcess", "flowChartAlternateProcess" }, + { u"ooxml-moon", "moon" }, + { u"ooxml-star6", "star6" }, + { u"ooxml-round2SameRect", "round2SameRect" }, + { u"ooxml-nonIsoscelesTrapezoid", "nonIsoscelesTrapezoid" }, + { u"ooxml-diamond", "diamond" }, + { u"ooxml-ellipseRibbon", "ellipseRibbon" }, + { u"ooxml-callout2", "callout2" }, + { u"ooxml-pie", "pie" }, + { u"ooxml-star4", "star4" }, + { u"ooxml-flowChartPredefinedProcess", "flowChartPredefinedProcess" }, + { u"ooxml-flowChartPunchedTape", "flowChartPunchedTape" }, + { u"ooxml-curvedConnector2", "curvedConnector2" }, + { u"ooxml-bentConnector3", "bentConnector3" }, + { u"ooxml-cornerTabs", "cornerTabs" }, + { u"ooxml-hexagon", "hexagon" }, + { u"ooxml-flowChartConnector", "flowChartConnector" }, + { u"ooxml-flowChartMagneticDisk", "flowChartMagneticDisk" }, + { u"ooxml-heart", "heart" }, + { u"ooxml-ribbon2", "ribbon2" }, + { u"ooxml-bracePair", "bracePair" }, + { u"ooxml-flowChartExtract", "flowChartExtract" }, + { u"ooxml-actionButtonHome", "actionButtonHome" }, + { u"ooxml-accentBorderCallout3", "accentBorderCallout3" }, + { u"ooxml-flowChartOfflineStorage", "flowChartOfflineStorage" }, + { u"ooxml-irregularSeal1", "irregularSeal1" }, + { u"ooxml-quadArrow", "quadArrow" }, + { u"ooxml-leftBrace", "leftBrace" }, + { u"ooxml-leftBracket", "leftBracket" }, + { u"ooxml-blockArc", "blockArc" }, + { u"ooxml-curvedConnector3", "curvedConnector3" }, + { u"ooxml-wedgeRoundRectCallout", "wedgeRoundRectCallout" }, + { u"ooxml-actionButtonMovie", "actionButtonMovie" }, + { u"ooxml-flowChartOnlineStorage", "flowChartOnlineStorage" }, + { u"ooxml-gear6", "gear6" }, + { u"ooxml-halfFrame", "halfFrame" }, + { u"ooxml-snip2SameRect", "snip2SameRect" }, + { u"ooxml-triangle", "triangle" }, + { u"ooxml-teardrop", "teardrop" }, + { u"ooxml-flowChartDocument", "flowChartDocument" }, + { u"ooxml-rightArrowCallout", "rightArrowCallout" }, + { u"ooxml-rightBrace", "rightBrace" }, + { u"ooxml-chartPlus", "chartPlus" }, + { u"ooxml-flowChartManualInput", "flowChartManualInput" }, + { u"ooxml-flowChartMerge", "flowChartMerge" }, + { u"ooxml-line", "line" }, + { u"ooxml-downArrow", "downArrow" }, + { u"ooxml-upArrow", "upArrow" }, + { u"ooxml-curvedDownArrow", "curvedDownArrow" }, + { u"ooxml-actionButtonReturn", "actionButtonReturn" }, + { u"ooxml-flowChartInputOutput", "flowChartInputOutput" }, + { u"ooxml-bracketPair", "bracketPair" }, + { u"ooxml-smileyFace", "smileyFace" }, + { u"ooxml-actionButtonBlank", "actionButtonBlank" }, + { u"ooxml-wave", "wave" }, + { u"ooxml-swooshArrow", "swooshArrow" }, + { u"ooxml-flowChartSummingJunction", "flowChartSummingJunction" }, + { u"ooxml-lightningBolt", "lightningBolt" }, + { u"ooxml-flowChartDisplay", "flowChartDisplay" }, + { u"ooxml-actionButtonBackPrevious", "actionButtonBackPrevious" }, + { u"ooxml-frame", "frame" }, + { u"ooxml-rtTriangle", "rtTriangle" }, + { u"ooxml-flowChartMagneticTape", "flowChartMagneticTape" }, + { u"ooxml-curvedRightArrow", "curvedRightArrow" }, + { u"ooxml-leftUpArrow", "leftUpArrow" }, + { u"ooxml-wedgeEllipseCallout", "wedgeEllipseCallout" }, + { u"ooxml-doubleWave", "doubleWave" }, + { u"ooxml-bentArrow", "bentArrow" }, + { u"ooxml-star10", "star10" }, + { u"ooxml-leftArrow", "leftArrow" }, + { u"ooxml-curvedUpArrow", "curvedUpArrow" }, + { u"ooxml-snip1Rect", "snip1Rect" }, + { u"ooxml-ellipseRibbon2", "ellipseRibbon2" }, + { u"ooxml-plus", "plus" }, + { u"ooxml-accentCallout3", "accentCallout3" }, + { u"ooxml-leftCircularArrow", "leftCircularArrow" }, + { u"ooxml-rightArrow", "rightArrow" }, + { u"ooxml-flowChartPunchedCard", "flowChartPunchedCard" }, + { u"ooxml-snip2DiagRect", "snip2DiagRect" }, + { u"ooxml-verticalScroll", "verticalScroll" }, + { u"ooxml-star7", "star7" }, + { u"ooxml-chartX", "chartX" }, + { u"ooxml-cloud", "cloud" }, + { u"ooxml-cube", "cube" }, + { u"ooxml-round2DiagRect", "round2DiagRect" }, + { u"ooxml-flowChartMultidocument", "flowChartMultidocument" }, + { u"ooxml-actionButtonDocument", "actionButtonDocument" }, + { u"ooxml-flowChartTerminator", "flowChartTerminator" }, + { u"ooxml-flowChartDelay", "flowChartDelay" }, + { u"ooxml-curvedConnector5", "curvedConnector5" }, + { u"ooxml-horizontalScroll", "horizontalScroll" }, + { u"ooxml-bentConnector4", "bentConnector4" }, + { u"ooxml-leftRightCircularArrow", "leftRightCircularArrow" }, + { u"ooxml-wedgeRectCallout", "wedgeRectCallout" }, + { u"ooxml-accentCallout2", "accentCallout2" }, + { u"ooxml-flowChartMagneticDrum", "flowChartMagneticDrum" }, + { u"ooxml-corner", "corner" }, + { u"ooxml-borderCallout2", "borderCallout2" }, + { u"ooxml-donut", "donut" }, + { u"ooxml-flowChartCollate", "flowChartCollate" }, + { u"ooxml-mathNotEqual", "mathNotEqual" }, + { u"ooxml-bentConnector2", "bentConnector2" }, + { u"ooxml-mathMultiply", "mathMultiply" }, + { u"ooxml-heptagon", "heptagon" }, + { u"ooxml-rect", "rect" }, + { u"ooxml-accentBorderCallout2", "accentBorderCallout2" }, + { u"ooxml-pieWedge", "pieWedge" }, + { u"ooxml-upArrowCallout", "upArrowCallout" }, + { u"ooxml-flowChartProcess", "flowChartProcess" }, + { u"ooxml-star5", "star5" }, + { u"ooxml-lineInv", "lineInv" }, + { u"ooxml-straightConnector1", "straightConnector1" }, + { u"ooxml-stripedRightArrow", "stripedRightArrow" }, + { u"ooxml-callout3", "callout3" }, + { u"ooxml-bentUpArrow", "bentUpArrow" }, + { u"ooxml-noSmoking", "noSmoking" }, + { u"ooxml-trapezoid", "trapezoid" }, + { u"ooxml-cloudCallout", "cloudCallout" }, + { u"ooxml-callout1", "callout1" }, + { u"ooxml-ribbon", "ribbon" }, + { u"ooxml-rect", "rect" }, + }; + auto i(aCustomShapeTypeTranslationHashMap.find(rShapeType)); + return i == aCustomShapeTypeTranslationHashMap.end() ? "rect" : i->second; +} + +MSO_SPT GETVMLShapeType(std::u16string_view aType) +{ + typedef std::unordered_map DMLToVMLTranslationHashMap; + static const DMLToVMLTranslationHashMap aDMLToVMLMap{ + {"notPrimitive", mso_sptNotPrimitive}, + {"rectangle", mso_sptRectangle}, + {"roundRectangle", mso_sptRoundRectangle}, + {"ellipse", mso_sptEllipse}, + {"diamond", mso_sptDiamond}, + {"triangle", mso_sptIsocelesTriangle}, + {"rtTriangle", mso_sptRightTriangle}, + {"parallelogram", mso_sptParallelogram}, + {"trapezoid", mso_sptTrapezoid}, + {"hexagon", mso_sptHexagon}, + {"octagon", mso_sptOctagon}, + {"plus", mso_sptPlus}, + {"star5", mso_sptStar}, + {"rightArrow", mso_sptArrow}, + {"thickArrow", mso_sptThickArrow}, + {"homePlate", mso_sptHomePlate}, + {"cube", mso_sptCube}, + {"wedgeRoundRectCallout", mso_sptBalloon}, + {"star16", mso_sptSeal}, + {"arc", mso_sptArc}, + {"line", mso_sptLine}, + {"plaque", mso_sptPlaque}, + {"can", mso_sptCan}, + {"donut", mso_sptDonut}, + {"textPlain", mso_sptTextSimple}, + {"textStop", mso_sptTextOctagon}, + {"textTriangle", mso_sptTextHexagon}, + {"textCanDown", mso_sptTextCurve}, + {"textWave1", mso_sptTextWave}, + {"textArchUpPour", mso_sptTextRing}, + {"textCanDown", mso_sptTextOnCurve}, + {"textArchUp", mso_sptTextOnRing}, + {"straightConnector1", mso_sptStraightConnector1}, + {"bentConnector2", mso_sptBentConnector2}, + {"bentConnector3", mso_sptBentConnector3}, + {"bentConnector4", mso_sptBentConnector4}, + {"bentConnector5", mso_sptBentConnector5}, + {"curvedConnector2", mso_sptCurvedConnector2}, + {"curvedConnector3", mso_sptCurvedConnector3}, + {"curvedConnector4", mso_sptCurvedConnector4}, + {"curvedConnector5", mso_sptCurvedConnector5}, + {"callout1", mso_sptCallout1}, + {"callout2", mso_sptCallout2}, + {"callout3", mso_sptCallout3}, + {"accentCallout1", mso_sptAccentCallout1}, + {"accentCallout2", mso_sptAccentCallout2}, + {"accentCallout3", mso_sptAccentCallout3}, + {"borderCallout1", mso_sptBorderCallout1}, + {"borderCallout2", mso_sptBorderCallout2}, + {"borderCallout3", mso_sptBorderCallout3}, + {"accentBorderCallout1", mso_sptAccentBorderCallout1}, + {"accentBorderCallout2", mso_sptAccentBorderCallout2}, + {"accentBorderCallout3", mso_sptAccentBorderCallout3}, + {"ribbon", mso_sptRibbon}, + {"ribbon2", mso_sptRibbon2}, + {"chevron", mso_sptChevron}, + {"pentagon", mso_sptPentagon}, + {"noSmoking", mso_sptNoSmoking}, + {"star8", mso_sptSeal8}, + {"star16", mso_sptSeal16}, + {"star32", mso_sptSeal32}, + {"wedgeRectCallout", mso_sptWedgeRectCallout}, + {"wedgeRoundRectCallout", mso_sptWedgeRRectCallout}, + {"wedgeEllipseCallout", mso_sptWedgeEllipseCallout}, + {"wave", mso_sptWave}, + {"foldedCorner", mso_sptFoldedCorner}, + {"leftArrow", mso_sptLeftArrow}, + {"downArrow", mso_sptDownArrow}, + {"upArrow", mso_sptUpArrow}, + {"leftRightArrow", mso_sptLeftRightArrow}, + {"upDownArrow", mso_sptUpDownArrow}, + {"irregularSeal1", mso_sptIrregularSeal1}, + {"irregularSeal2", mso_sptIrregularSeal2}, + {"lightningBolt", mso_sptLightningBolt}, + {"heart", mso_sptHeart}, + {"pictureFrame", mso_sptPictureFrame}, + {"quadArrow", mso_sptQuadArrow}, + {"leftArrowCallout", mso_sptLeftArrowCallout}, + {"rightArrowCallout", mso_sptRightArrowCallout}, + {"upArrowCallout", mso_sptUpArrowCallout}, + {"downArrowCallout", mso_sptDownArrowCallout}, + {"leftRightArrowCallout", mso_sptLeftRightArrowCallout}, + {"upDownArrowCallout", mso_sptUpDownArrowCallout}, + {"quadArrowCallout", mso_sptQuadArrowCallout}, + {"bevel", mso_sptBevel}, + {"leftBracket", mso_sptLeftBracket}, + {"rightBracket", mso_sptRightBracket}, + {"leftBrace", mso_sptLeftBrace}, + {"rightBrace", mso_sptRightBrace}, + {"leftUpArrow", mso_sptLeftUpArrow}, + {"bentUpArrow", mso_sptBentUpArrow}, + {"bentArrow", mso_sptBentArrow}, + {"star24", mso_sptSeal24}, + {"stripedRightArrow", mso_sptStripedRightArrow}, + {"notchedRightArrow", mso_sptNotchedRightArrow}, + {"blockArc", mso_sptBlockArc}, + {"smileyFace", mso_sptSmileyFace}, + {"verticalScroll", mso_sptVerticalScroll}, + {"horizontalScroll", mso_sptHorizontalScroll}, + {"circularArrow", mso_sptCircularArrow}, + {"notchedCircularArrow", mso_sptNotchedCircularArrow}, + {"uturnArrow", mso_sptUturnArrow}, + {"curvedRightArrow", mso_sptCurvedRightArrow}, + {"curvedLeftArrow", mso_sptCurvedLeftArrow}, + {"curvedUpArrow", mso_sptCurvedUpArrow}, + {"curvedDownArrow", mso_sptCurvedDownArrow}, + {"cloudCallout", mso_sptCloudCallout}, + {"ellipseRibbon", mso_sptEllipseRibbon}, + {"ellipseRibbon2", mso_sptEllipseRibbon2}, + {"flowChartProcess", mso_sptFlowChartProcess}, + {"flowChartDecision", mso_sptFlowChartDecision}, + {"flowChartInputOutput", mso_sptFlowChartInputOutput}, + {"flowChartPredefinedProcess", mso_sptFlowChartPredefinedProcess}, + {"flowChartInternalStorage", mso_sptFlowChartInternalStorage}, + {"flowChartDocument", mso_sptFlowChartDocument}, + {"flowChartMultidocument", mso_sptFlowChartMultidocument}, + {"flowChartTerminator", mso_sptFlowChartTerminator}, + {"flowChartPreparation", mso_sptFlowChartPreparation}, + {"flowChartManualInput", mso_sptFlowChartManualInput}, + {"flowChartManualOperation", mso_sptFlowChartManualOperation}, + {"flowChartConnector", mso_sptFlowChartConnector}, + {"flowChartPunchedCard", mso_sptFlowChartPunchedCard}, + {"flowChartPunchedTape", mso_sptFlowChartPunchedTape}, + {"flowChartSummingJunction", mso_sptFlowChartSummingJunction}, + {"flowChartOr", mso_sptFlowChartOr}, + {"flowChartCollate", mso_sptFlowChartCollate}, + {"flowChartSort", mso_sptFlowChartSort}, + {"flowChartExtract", mso_sptFlowChartExtract}, + {"flowChartMerge", mso_sptFlowChartMerge}, + {"flowChartOfflineStorage", mso_sptFlowChartOfflineStorage}, + {"flowChartOnlineStorage", mso_sptFlowChartOnlineStorage}, + {"flowChartMagneticTape", mso_sptFlowChartMagneticTape}, + {"flowChartMagneticDisk", mso_sptFlowChartMagneticDisk}, + {"flowChartMagneticDrum", mso_sptFlowChartMagneticDrum}, + {"flowChartDisplay", mso_sptFlowChartDisplay}, + {"flowChartDelay", mso_sptFlowChartDelay}, + {"textPlain", mso_sptTextPlainText}, + {"textStop", mso_sptTextStop}, + {"textTriangle", mso_sptTextTriangle}, + {"textTriangleInverted", mso_sptTextTriangleInverted}, + {"textChevron", mso_sptTextChevron}, + {"textChevronInverted", mso_sptTextChevronInverted}, + {"textRingInside", mso_sptTextRingInside}, + {"textRingOutside", mso_sptTextRingOutside}, + {"textArchUp", mso_sptTextArchUpCurve}, + {"textArchDown", mso_sptTextArchDownCurve}, + {"textCircle", mso_sptTextCircleCurve}, + {"textButton", mso_sptTextButtonCurve}, + {"textArchUpPour", mso_sptTextArchUpPour}, + {"textArchDownPour", mso_sptTextArchDownPour}, + {"textCirclePour", mso_sptTextCirclePour}, + {"textButtonPour", mso_sptTextButtonPour}, + {"textCurveUp", mso_sptTextCurveUp}, + {"textCurveDown", mso_sptTextCurveDown}, + {"textCascadeUp", mso_sptTextCascadeUp}, + {"textCascadeDown", mso_sptTextCascadeDown}, + {"textWave1", mso_sptTextWave1}, + {"textWave2", mso_sptTextWave2}, + {"textWave3", mso_sptTextWave3}, + {"textWave4", mso_sptTextWave4}, + {"textInflate", mso_sptTextInflate}, + {"textDeflate", mso_sptTextDeflate}, + {"textInflateBottom", mso_sptTextInflateBottom}, + {"textDeflateBottom", mso_sptTextDeflateBottom}, + {"textInflateTop", mso_sptTextInflateTop}, + {"textDeflateTop", mso_sptTextDeflateTop}, + {"textDeflateInflate", mso_sptTextDeflateInflate}, + {"textDeflateInflateDeflate", mso_sptTextDeflateInflateDeflate}, + {"textFadeRight", mso_sptTextFadeRight}, + {"textFadeLeft", mso_sptTextFadeLeft}, + {"textFadeUp", mso_sptTextFadeUp}, + {"textFadeDown", mso_sptTextFadeDown}, + {"textSlantUp", mso_sptTextSlantUp}, + {"textSlantDown", mso_sptTextSlantDown}, + {"textCanUp", mso_sptTextCanUp}, + {"textCanDown", mso_sptTextCanDown}, + {"flowChartAlternateProcess", mso_sptFlowChartAlternateProcess}, + {"flowChartOffpageConnector", mso_sptFlowChartOffpageConnector}, + {"callout1", mso_sptCallout90}, + {"accentCallout1", mso_sptAccentCallout90}, + {"borderCallout1", mso_sptBorderCallout90}, + {"accentBorderCallout1", mso_sptAccentBorderCallout90}, + {"leftRightUpArrow", mso_sptLeftRightUpArrow}, + {"sun", mso_sptSun}, + {"moon", mso_sptMoon}, + {"bracketPair", mso_sptBracketPair}, + {"bracePair", mso_sptBracePair}, + {"star4", mso_sptSeal4}, + {"doubleWave", mso_sptDoubleWave}, + {"actionButtonBlank", mso_sptActionButtonBlank}, + {"actionButtonHome", mso_sptActionButtonHome}, + {"actionButtonHelp", mso_sptActionButtonHelp}, + {"actionButtonInformation", mso_sptActionButtonInformation}, + {"actionButtonForwardNext", mso_sptActionButtonForwardNext}, + {"actionButtonBackPrevious", mso_sptActionButtonBackPrevious}, + {"actionButtonEnd", mso_sptActionButtonEnd}, + {"actionButtonBeginning", mso_sptActionButtonBeginning}, + {"actionButtonReturn", mso_sptActionButtonReturn}, + {"actionButtonDocument", mso_sptActionButtonDocument}, + {"actionButtonSound", mso_sptActionButtonSound}, + {"actionButtonMovie", mso_sptActionButtonMovie}, + {"hostControl", mso_sptHostControl}, + {"textBox", mso_sptTextBox}, + }; + + auto i(aDMLToVMLMap.find(GetOOXMLPresetGeometry(aType))); + return i == aDMLToVMLMap.end() ? mso_sptNil : i->second; +} + +bool HasTextBoxContent(sal_uInt32 nShapeType) +{ + switch (nShapeType) + { + case ESCHER_ShpInst_TextPlainText: + case ESCHER_ShpInst_TextSlantUp: + case ESCHER_ShpInst_TextDeflateInflateDeflate: + return false; + default: + return true; + } +} + +namespace +{ + +// Scheme means pattern of chromatic values. +// [2,2,1] -> red and green are approximately equal and blue is the dominant color (e.g. blue) +// [1,1,1] -> all chromatic values are approximately equal (e.g. white, gray, black) +void CalculateScheme(const BitmapColor& rBitmapColor, std::vector &vScheme, sal_uInt16 nVariance) +{ + vScheme.resize(3,1); + if( rBitmapColor.GetRed() < rBitmapColor.GetGreen() + nVariance ) + ++vScheme[0]; + if( rBitmapColor.GetRed() < rBitmapColor.GetBlue() + nVariance ) + ++vScheme[0]; + if( rBitmapColor.GetGreen() < rBitmapColor.GetRed() + nVariance ) + ++vScheme[1]; + if( rBitmapColor.GetGreen() < rBitmapColor.GetBlue() + nVariance ) + ++vScheme[1]; + if( rBitmapColor.GetBlue() < rBitmapColor.GetRed() + nVariance ) + ++vScheme[2]; + if( rBitmapColor.GetBlue() < rBitmapColor.GetGreen() + nVariance ) + ++vScheme[2]; +} + +bool HasSimilarScheme(const BitmapColor& rBitmapColor1, const BitmapColor& rBitmapColor2, sal_uInt16 nVariance) +{ + std::vector vScheme1, vScheme2; + CalculateScheme(rBitmapColor1, vScheme1, nVariance); + CalculateScheme(rBitmapColor2, vScheme2, nVariance); + for( int i = 0; i < 3; ++i ) + { + if( vScheme1[i] != vScheme2[i] ) + return false; + } + return true; +} + +// Find the best match in the color palette using scheme of the input color +sal_uInt16 GetBestIndex(const BitmapPalette& rPalette, const BitmapColor& rBitmapColor) +{ + sal_uInt16 nReturn = 0; + sal_uInt16 nLastErr = SAL_MAX_UINT16; + bool bFound = false; + + // Prefer those colors which have similar scheme as the input + // Allow bigger and bigger variance of the schemes until we find + // a color in the palette with similar scheme. + for( sal_uInt16 nVariance = 0; nVariance <= 255; ++nVariance ) + { + for( sal_uInt16 i = 0; i < rPalette.GetEntryCount(); ++i ) + { + if( HasSimilarScheme(rBitmapColor, rPalette[i], nVariance) ) + { + const sal_uInt16 nActErr = rBitmapColor.GetColorError( rPalette[i] ); + if( nActErr < nLastErr ) + { + nLastErr = nActErr; + nReturn = i; + bFound = true; + } + } + } + if( bFound ) + return nReturn; + } + return nReturn; +} +} + +sal_uInt8 TransColToIco( const Color& rCol ) +{ + sal_uInt8 nCol = 0; // ->Auto + switch( sal_uInt32(rCol) ) + { + case sal_uInt32(COL_BLACK): nCol = 1; break; + case sal_uInt32(COL_BLUE): nCol = 9; break; + case sal_uInt32(COL_GREEN): nCol = 11; break; + case sal_uInt32(COL_CYAN): nCol = 10; break; + case sal_uInt32(COL_RED): nCol = 13; break; + case sal_uInt32(COL_MAGENTA): nCol = 12; break; + case sal_uInt32(COL_BROWN): nCol = 14; break; + case sal_uInt32(COL_GRAY): nCol = 15; break; + case sal_uInt32(COL_LIGHTGRAY): nCol = 16; break; + case sal_uInt32(COL_LIGHTBLUE): nCol = 2; break; + case sal_uInt32(COL_LIGHTGREEN): nCol = 4; break; + case sal_uInt32(COL_LIGHTCYAN): nCol = 3; break; + case sal_uInt32(COL_LIGHTRED): nCol = 6; break; + case sal_uInt32(COL_LIGHTMAGENTA): nCol = 5; break; + case sal_uInt32(COL_YELLOW): nCol = 7; break; + case sal_uInt32(COL_WHITE): nCol = 8; break; + case sal_uInt32(COL_AUTO): nCol = 0; break; + + default: + static const BitmapPalette aBmpPal { + COL_BLACK, COL_LIGHTBLUE, COL_LIGHTCYAN, COL_LIGHTGREEN, + COL_LIGHTMAGENTA,COL_LIGHTRED, COL_YELLOW, COL_WHITE, + COL_BLUE, COL_CYAN, COL_GREEN, COL_MAGENTA, + COL_RED, COL_BROWN, COL_GRAY, COL_LIGHTGRAY + }; + + nCol = static_cast< sal_uInt8 >(GetBestIndex(aBmpPal, rCol) + 1); + break; + } + return nCol; +} + +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ diff --git a/filter/source/msfilter/viscache.hxx b/filter/source/msfilter/viscache.hxx new file mode 100644 index 000000000..5111969b3 --- /dev/null +++ b/filter/source/msfilter/viscache.hxx @@ -0,0 +1,52 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ +#pragma once + +#include +#include +#include + +class SvStream; + +class Impl_OlePres +{ + SotClipboardFormatId nFormat; + sal_uInt16 nAspect; + std::unique_ptr + pMtf; + + sal_uInt32 nAdvFlags; + Size aSize; // size in 100TH_MM +public: + explicit Impl_OlePres() + : nFormat( SotClipboardFormatId::GDIMETAFILE ) + , nAspect( ASPECT_CONTENT ) + , nAdvFlags( 0x2 ) // found in document + {} + void SetMtf( const GDIMetaFile & rMtf ) + { + pMtf.reset( new GDIMetaFile( rMtf ) ); + } + void SetAspect( sal_uInt16 nAsp ) { nAspect = nAsp; } + void SetAdviseFlags( sal_uLong nAdv ) { nAdvFlags = nAdv; } + void SetSize( const Size & rSize ) { aSize = rSize; } + void Write( SvStream & rStm ); +}; + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ -- cgit v1.2.3