summaryrefslogtreecommitdiffstats
path: root/xmloff/source/style/chrlohdl.cxx
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-07 09:06:44 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-07 09:06:44 +0000
commited5640d8b587fbcfed7dd7967f3de04b37a76f26 (patch)
tree7a5f7c6c9d02226d7471cb3cc8fbbf631b415303 /xmloff/source/style/chrlohdl.cxx
parentInitial commit. (diff)
downloadlibreoffice-upstream.tar.xz
libreoffice-upstream.zip
Adding upstream version 4:7.4.7.upstream/4%7.4.7upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to '')
-rw-r--r--xmloff/source/style/chrlohdl.cxx423
1 files changed, 423 insertions, 0 deletions
diff --git a/xmloff/source/style/chrlohdl.cxx b/xmloff/source/style/chrlohdl.cxx
new file mode 100644
index 000000000..05dd6d770
--- /dev/null
+++ b/xmloff/source/style/chrlohdl.cxx
@@ -0,0 +1,423 @@
+/* -*- 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 "chrlohdl.hxx"
+#include <xmloff/xmltoken.hxx>
+#include <xmloff/xmluconv.hxx>
+#include <unotools/saveopt.hxx>
+#include <i18nlangtag/languagetag.hxx>
+#include <sal/log.hxx>
+#include <com/sun/star/uno/Any.hxx>
+#include <com/sun/star/lang/Locale.hpp>
+
+using namespace ::com::sun::star;
+using namespace ::xmloff::token;
+
+/* TODO-BCP47: this fiddling with Locale is quite ugly and fragile, especially
+ * for the fo:script temporarily stored in Variant, it would be better to use
+ * LanguageTagODF but we have that nasty UNO API requirement here.
+ * => make LanguageTagODF (unpublished) API? */
+
+// For runtime performance, instead of converting back and forth between
+// css::Locale and LanguageTag to decide if script or tag are
+// needed, this code takes advantage of knowledge about the internal
+// representation of BCP 47 language tags in a Locale if present as done in a
+// LanguageTag.
+
+XMLCharLanguageHdl::~XMLCharLanguageHdl()
+{
+ // nothing to do
+}
+
+bool XMLCharLanguageHdl::equals( const css::uno::Any& r1, const css::uno::Any& r2 ) const
+{
+ bool bRet = false;
+ lang::Locale aLocale1, aLocale2;
+
+ if( ( r1 >>= aLocale1 ) && ( r2 >>= aLocale2 ) )
+ {
+ bool bEmptyOrScriptVariant1 = (aLocale1.Variant.isEmpty() || aLocale1.Variant[0] == '-');
+ bool bEmptyOrScriptVariant2 = (aLocale2.Variant.isEmpty() || aLocale2.Variant[0] == '-');
+ if (bEmptyOrScriptVariant1 && bEmptyOrScriptVariant2)
+ bRet = ( aLocale1.Language == aLocale2.Language );
+ else
+ {
+ OUString aLanguage1, aLanguage2;
+ if (bEmptyOrScriptVariant1)
+ aLanguage1 = aLocale1.Language;
+ else
+ aLanguage1 = LanguageTag( aLocale1).getLanguage();
+ if (bEmptyOrScriptVariant2)
+ aLanguage2 = aLocale2.Language;
+ else
+ aLanguage2 = LanguageTag( aLocale2).getLanguage();
+ bRet = ( aLanguage1 == aLanguage2 );
+ }
+ }
+
+ return bRet;
+}
+
+bool XMLCharLanguageHdl::importXML( const OUString& rStrImpValue, uno::Any& rValue, const SvXMLUnitConverter& ) const
+{
+ lang::Locale aLocale;
+ rValue >>= aLocale;
+
+ if( !IsXMLToken(rStrImpValue, XML_NONE) )
+ {
+ if (aLocale.Variant.isEmpty())
+ aLocale.Language = rStrImpValue;
+ else
+ {
+ if (!aLocale.Language.isEmpty() || aLocale.Variant[0] != '-')
+ {
+ SAL_WARN_IF( aLocale.Language != I18NLANGTAG_QLT, "xmloff.style",
+ "XMLCharLanguageHdl::importXML - attempt to import language twice");
+ }
+ else
+ {
+ aLocale.Variant = rStrImpValue + aLocale.Variant;
+ if (!aLocale.Country.isEmpty())
+ aLocale.Variant += "-" + aLocale.Country;
+ aLocale.Language = I18NLANGTAG_QLT;
+ }
+ }
+ }
+
+ rValue <<= aLocale;
+ return true;
+}
+
+bool XMLCharLanguageHdl::exportXML( OUString& rStrExpValue, const uno::Any& rValue, const SvXMLUnitConverter& ) const
+{
+ lang::Locale aLocale;
+ if(!(rValue >>= aLocale))
+ return false;
+
+ if (aLocale.Variant.isEmpty())
+ rStrExpValue = aLocale.Language;
+ else
+ {
+ LanguageTag aLanguageTag( aLocale);
+ OUString aScript, aCountry;
+ aLanguageTag.getIsoLanguageScriptCountry( rStrExpValue, aScript, aCountry);
+ // Do not write *:language='none' for a non-ISO language with
+ // *:rfc-language-tag that is written if Variant is not empty. If there
+ // is no match do not write this attribute at all.
+ if (rStrExpValue.isEmpty())
+ return false;
+ }
+
+ if( rStrExpValue.isEmpty() )
+ rStrExpValue = GetXMLToken( XML_NONE );
+
+ return true;
+}
+
+XMLCharScriptHdl::~XMLCharScriptHdl()
+{
+ // nothing to do
+}
+
+bool XMLCharScriptHdl::equals( const css::uno::Any& r1, const css::uno::Any& r2 ) const
+{
+ bool bRet = false;
+ lang::Locale aLocale1, aLocale2;
+
+ if( ( r1 >>= aLocale1 ) && ( r2 >>= aLocale2 ) )
+ {
+ bool bEmptyVariant1 = aLocale1.Variant.isEmpty();
+ bool bEmptyVariant2 = aLocale2.Variant.isEmpty();
+ if (bEmptyVariant1 && bEmptyVariant2)
+ bRet = true;
+ else if (bEmptyVariant1 != bEmptyVariant2)
+ ; // stays false
+ else
+ {
+ OUString aScript1, aScript2;
+ if (aLocale1.Variant[0] == '-')
+ aScript1 = aLocale1.Variant.copy(1);
+ else
+ aScript1 = LanguageTag( aLocale1).getScript();
+ if (aLocale2.Variant[0] == '-')
+ aScript2 = aLocale2.Variant.copy(1);
+ else
+ aScript2 = LanguageTag( aLocale2).getScript();
+ bRet = ( aScript1 == aScript2 );
+ }
+ }
+
+ return bRet;
+}
+
+bool XMLCharScriptHdl::importXML( const OUString& rStrImpValue, uno::Any& rValue, const SvXMLUnitConverter& ) const
+{
+ lang::Locale aLocale;
+ rValue >>= aLocale;
+
+ if( !IsXMLToken( rStrImpValue, XML_NONE ) )
+ {
+ // Import the script only if we don't have a full BCP 47 language tag
+ // in Variant yet.
+ if (aLocale.Variant.isEmpty())
+ {
+ if (aLocale.Language.isEmpty())
+ {
+ SAL_INFO( "xmloff.style", "XMLCharScriptHdl::importXML - script but no language yet");
+ // Temporarily store in Variant and hope the best (we will get
+ // a language later, yes?)
+ aLocale.Variant = "-" + rStrImpValue;
+ }
+ else
+ {
+ aLocale.Variant = aLocale.Language + "-" + rStrImpValue;
+ if (!aLocale.Country.isEmpty())
+ aLocale.Variant += "-" + aLocale.Country;
+ aLocale.Language = I18NLANGTAG_QLT;
+ }
+ }
+ else if (aLocale.Variant[0] == '-')
+ {
+ SAL_WARN( "xmloff.style", "XMLCharScriptHdl::importXML - attempt to insert script twice: "
+ << rStrImpValue << " -> " << aLocale.Variant);
+ }
+ else
+ {
+ // Assume that if there already is a script or anything else BCP 47
+ // it was read by XMLCharRfcLanguageTagHdl() and takes precedence.
+ // On the other hand, an *:rfc-language-tag without script and a
+ // *:script ?!?
+#if OSL_DEBUG_LEVEL > 0 || defined(DBG_UTIL)
+ LanguageTag aLanguageTag( aLocale);
+ if (!aLanguageTag.hasScript())
+ {
+ SAL_WARN( "xmloff.style", "XMLCharScriptHdl::importXML - attempt to insert script over bcp47: "
+ << rStrImpValue << " -> " << aLanguageTag.getBcp47());
+ }
+#endif
+ }
+ }
+
+ rValue <<= aLocale;
+ return true;
+}
+
+bool XMLCharScriptHdl::exportXML(OUString& rStrExpValue,
+ const uno::Any& rValue, const SvXMLUnitConverter& rUnitConv) const
+{
+ lang::Locale aLocale;
+ if(!(rValue >>= aLocale))
+ return false;
+
+ // Do not write script='none' for default script.
+
+ if (aLocale.Variant.isEmpty())
+ return false;
+
+ LanguageTag aLanguageTag( aLocale);
+ if (!aLanguageTag.hasScript())
+ return false;
+
+ if (rUnitConv.getSaneDefaultVersion() < SvtSaveOptions::ODFSVER_012)
+ return false;
+
+ OUString aLanguage, aCountry;
+ aLanguageTag.getIsoLanguageScriptCountry( aLanguage, rStrExpValue, aCountry);
+ // For non-ISO language it does not make sense to write *:script if
+ // *:language is not written either, does it? It's all in
+ // *:rfc-language-tag
+ return !aLanguage.isEmpty() && !rStrExpValue.isEmpty();
+}
+
+XMLCharCountryHdl::~XMLCharCountryHdl()
+{
+ // nothing to do
+}
+
+bool XMLCharCountryHdl::equals( const css::uno::Any& r1, const css::uno::Any& r2 ) const
+{
+ bool bRet = false;
+ lang::Locale aLocale1, aLocale2;
+
+ if( ( r1 >>= aLocale1 ) && ( r2 >>= aLocale2 ) )
+ bRet = ( aLocale1.Country == aLocale2.Country );
+
+ return bRet;
+}
+
+bool XMLCharCountryHdl::importXML( const OUString& rStrImpValue, uno::Any& rValue, const SvXMLUnitConverter& ) const
+{
+ lang::Locale aLocale;
+ rValue >>= aLocale;
+
+ if( !IsXMLToken( rStrImpValue, XML_NONE ) )
+ {
+ if (aLocale.Country.isEmpty())
+ {
+ aLocale.Country = rStrImpValue;
+ if (aLocale.Variant.getLength() >= 7 && aLocale.Language == I18NLANGTAG_QLT)
+ {
+ // already assembled language tag, at least ll-Ssss and not
+ // ll-CC or lll-CC
+ sal_Int32 i = aLocale.Variant.indexOf('-'); // separator to script
+ if (2 <= i && i < aLocale.Variant.getLength())
+ {
+ i = aLocale.Variant.indexOf( '-', i+1);
+ if (i < 0) // no other separator
+ aLocale.Variant += "-" + rStrImpValue; // append country
+ }
+ }
+ }
+ }
+
+ rValue <<= aLocale;
+ return true;
+}
+
+bool XMLCharCountryHdl::exportXML( OUString& rStrExpValue, const uno::Any& rValue, const SvXMLUnitConverter& ) const
+{
+ lang::Locale aLocale;
+ if(!(rValue >>= aLocale))
+ return false;
+
+ if (aLocale.Variant.isEmpty())
+ rStrExpValue = aLocale.Country;
+ else
+ {
+ LanguageTag aLanguageTag( aLocale);
+ OUString aLanguage, aScript;
+ aLanguageTag.getIsoLanguageScriptCountry( aLanguage, aScript, rStrExpValue);
+ // Do not write *:country='none' for a non-ISO country with
+ // *:rfc-language-tag that is written if Variant is not empty. If there
+ // is no match do not write this attribute at all.
+ if (rStrExpValue.isEmpty())
+ return false;
+ }
+
+ if( rStrExpValue.isEmpty() )
+ rStrExpValue = GetXMLToken( XML_NONE );
+
+ return true;
+}
+
+XMLCharRfcLanguageTagHdl::~XMLCharRfcLanguageTagHdl()
+{
+ // nothing to do
+}
+
+bool XMLCharRfcLanguageTagHdl::equals( const css::uno::Any& r1, const css::uno::Any& r2 ) const
+{
+ bool bRet = false;
+ lang::Locale aLocale1, aLocale2;
+
+ if( ( r1 >>= aLocale1 ) && ( r2 >>= aLocale2 ) )
+ bRet = ( aLocale1.Variant == aLocale2.Variant );
+
+ return bRet;
+}
+
+bool XMLCharRfcLanguageTagHdl::importXML( const OUString& rStrImpValue, uno::Any& rValue, const SvXMLUnitConverter& ) const
+{
+ lang::Locale aLocale;
+ rValue >>= aLocale;
+
+ if( !IsXMLToken( rStrImpValue, XML_NONE ) )
+ {
+ // Stored may be a *:rfc-language-tag in violation of ODF v1.3
+ // 19.516 style:rfc-language-tag "It shall only be used if its value
+ // cannot be expressed as a valid combination of the fo:language
+ // 19.871, fo:script 19.242 and fo:country 19.234 attributes".
+ // That could override a more detailed fo:* and we also don't want an
+ // unjustified I18NLANGTAG_QLT extended locale tag, but fetch the
+ // values in case fo:* doesn't follow.
+ // Rule out the obvious.
+ if (rStrImpValue.getLength() < 7)
+ {
+ SAL_WARN("xmloff.style","rfc-language-tag too short: {" << rStrImpValue << "} Set: "
+ << aLocale.Language <<","<< aLocale.Country <<","<< aLocale.Variant);
+ // Ignore empty and keep Ssss or any earlier qlt already set.
+ if (!rStrImpValue.isEmpty() && aLocale.Language != I18NLANGTAG_QLT)
+ {
+ // Shorter than ll-Ssss, so try ll-CC or lll-CC or ll or lll
+ sal_Int32 h = rStrImpValue.indexOf('-');
+ OUString aLang;
+ if (2 <= h && h <= 3)
+ aLang = rStrImpValue.copy(0, h);
+ else if (h < 0 && 2 <= rStrImpValue.getLength() && rStrImpValue.getLength() <= 3)
+ aLang = rStrImpValue;
+ OUString aCoun;
+ if (!aLang.isEmpty() && aLang.getLength() + 3 == rStrImpValue.getLength())
+ aCoun = rStrImpValue.copy( aLang.getLength() + 1);
+ // Ignore identical value or less information.
+ if ((!aLang.isEmpty() && aLang != aLocale.Language) ||
+ (!aCoun.isEmpty() && aCoun != aLocale.Country))
+ {
+ // Do not override existing values.
+ if (aLocale.Language.isEmpty())
+ aLocale.Language = aLang;
+ if (aLocale.Country.isEmpty())
+ aLocale.Country = aCoun;
+ if (aLang != aLocale.Language || aCoun != aLocale.Country)
+ {
+ // No match, so we still need the qlt anyway. Whatever..
+ aLocale.Variant = rStrImpValue;
+ aLocale.Language = I18NLANGTAG_QLT;
+ }
+ }
+ else if (aLang.isEmpty() && aCoun.isEmpty())
+ {
+ // Both empty, some other tag.
+ aLocale.Variant = rStrImpValue;
+ aLocale.Language = I18NLANGTAG_QLT;
+ }
+ }
+ SAL_WARN("xmloff.style","rfc-language-tag too short: now set: "
+ << aLocale.Language <<","<< aLocale.Country <<","<< aLocale.Variant);
+ }
+ else
+ {
+ aLocale.Variant = rStrImpValue;
+ aLocale.Language = I18NLANGTAG_QLT;
+ }
+ }
+
+ rValue <<= aLocale;
+ return true;
+}
+
+bool XMLCharRfcLanguageTagHdl::exportXML(OUString& rStrExpValue,
+ const uno::Any& rValue, const SvXMLUnitConverter& rUnitConv) const
+{
+ lang::Locale aLocale;
+ if(!(rValue >>= aLocale))
+ return false;
+
+ // Do not write rfc-language-tag='none' if BCP 47 is not needed.
+ if (aLocale.Variant.isEmpty())
+ return false;
+
+ if (rUnitConv.getSaneDefaultVersion() < SvtSaveOptions::ODFSVER_012)
+ return false;
+
+ rStrExpValue = aLocale.Variant;
+
+ return true;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */