summaryrefslogtreecommitdiffstats
path: root/lingucomponent/source/hyphenator
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 /lingucomponent/source/hyphenator
parentInitial commit. (diff)
downloadlibreoffice-upstream/4%7.4.7.tar.xz
libreoffice-upstream/4%7.4.7.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 'lingucomponent/source/hyphenator')
-rw-r--r--lingucomponent/source/hyphenator/hyphen/hyphen.component26
-rw-r--r--lingucomponent/source/hyphenator/hyphen/hyphenimp.cxx805
-rw-r--r--lingucomponent/source/hyphenator/hyphen/hyphenimp.hxx126
3 files changed, 957 insertions, 0 deletions
diff --git a/lingucomponent/source/hyphenator/hyphen/hyphen.component b/lingucomponent/source/hyphenator/hyphen/hyphen.component
new file mode 100644
index 000000000..b9bc8b1f3
--- /dev/null
+++ b/lingucomponent/source/hyphenator/hyphen/hyphen.component
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ -->
+
+<component loader="com.sun.star.loader.SharedLibrary" environment="@CPPU_ENV@"
+ xmlns="http://openoffice.org/2010/uno-components">
+ <implementation name="org.openoffice.lingu.LibHnjHyphenator"
+ constructor="lingucomponent_Hyphenator_get_implementation" single-instance="true">
+ <service name="com.sun.star.linguistic2.Hyphenator"/>
+ </implementation>
+</component>
diff --git a/lingucomponent/source/hyphenator/hyphen/hyphenimp.cxx b/lingucomponent/source/hyphenator/hyphen/hyphenimp.cxx
new file mode 100644
index 000000000..8ac156ef8
--- /dev/null
+++ b/lingucomponent/source/hyphenator/hyphen/hyphenimp.cxx
@@ -0,0 +1,805 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <com/sun/star/uno/Reference.h>
+
+#include <comphelper/sequence.hxx>
+#include <cppuhelper/factory.hxx>
+#include <cppuhelper/supportsservice.hxx>
+#include <cppuhelper/weak.hxx>
+#include <com/sun/star/linguistic2/XLinguProperties.hpp>
+#include <i18nlangtag/languagetag.hxx>
+#include <tools/debug.hxx>
+#include <osl/mutex.hxx>
+#include <osl/thread.h>
+
+#include <hyphen.h>
+#include "hyphenimp.hxx"
+
+#include <linguistic/hyphdta.hxx>
+#include <rtl/ustring.hxx>
+#include <rtl/ustrbuf.hxx>
+#include <rtl/textenc.h>
+#include <sal/log.hxx>
+
+#include <linguistic/misc.hxx>
+#include <svtools/strings.hrc>
+#include <unotools/charclass.hxx>
+#include <unotools/lingucfg.hxx>
+#include <unotools/resmgr.hxx>
+#include <osl/file.hxx>
+
+#include <stdio.h>
+#include <string.h>
+
+#include <cassert>
+#include <numeric>
+#include <vector>
+#include <set>
+#include <memory>
+
+using namespace utl;
+using namespace osl;
+using namespace com::sun::star;
+using namespace com::sun::star::beans;
+using namespace com::sun::star::lang;
+using namespace com::sun::star::uno;
+using namespace com::sun::star::linguistic2;
+using namespace linguistic;
+
+Hyphenator::Hyphenator() :
+ aEvtListeners ( GetLinguMutex() )
+{
+ bDisposing = false;
+}
+
+Hyphenator::~Hyphenator()
+{
+ for (auto & rInfo : mvDicts)
+ {
+ if (rInfo.aPtr)
+ hnj_hyphen_free(rInfo.aPtr);
+ }
+
+ if (pPropHelper)
+ {
+ pPropHelper->RemoveAsPropListener();
+ }
+}
+
+PropertyHelper_Hyphenation& Hyphenator::GetPropHelper_Impl()
+{
+ if (!pPropHelper)
+ {
+ Reference< XLinguProperties > xPropSet = GetLinguProperties();
+
+ pPropHelper.reset( new PropertyHelper_Hyphenation (static_cast<XHyphenator *>(this), xPropSet ) );
+ pPropHelper->AddAsPropListener(); //! after a reference is established
+ }
+ return *pPropHelper;
+}
+
+Sequence< Locale > SAL_CALL Hyphenator::getLocales()
+{
+ MutexGuard aGuard( GetLinguMutex() );
+
+ // this routine should return the locales supported by the installed
+ // dictionaries.
+ if (mvDicts.empty())
+ {
+ SvtLinguConfig aLinguCfg;
+
+ // get list of dictionaries-to-use
+ // (or better speaking: the list of dictionaries using the
+ // new configuration entries).
+ std::vector< SvtLinguConfigDictionaryEntry > aDics;
+ uno::Sequence< OUString > aFormatList;
+ aLinguCfg.GetSupportedDictionaryFormatsFor( "Hyphenators",
+ "org.openoffice.lingu.LibHnjHyphenator", aFormatList );
+ for (const auto& rFormat : std::as_const(aFormatList))
+ {
+ std::vector< SvtLinguConfigDictionaryEntry > aTmpDic(
+ aLinguCfg.GetActiveDictionariesByFormat( rFormat ) );
+ aDics.insert( aDics.end(), aTmpDic.begin(), aTmpDic.end() );
+ }
+
+ //!! for compatibility with old dictionaries (the ones not using extensions
+ //!! or new configuration entries, but still using the dictionary.lst file)
+ //!! Get the list of old style spell checking dictionaries to use...
+ std::vector< SvtLinguConfigDictionaryEntry > aOldStyleDics(
+ GetOldStyleDics( "HYPH" ) );
+
+ // to prefer dictionaries with configuration entries we will only
+ // use those old style dictionaries that add a language that
+ // is not yet supported by the list of new style dictionaries
+ MergeNewStyleDicsAndOldStyleDics( aDics, aOldStyleDics );
+
+ if (!aDics.empty())
+ {
+ // get supported locales from the dictionaries-to-use...
+ std::set<OUString> aLocaleNamesSet;
+ for (auto const& dict : aDics)
+ {
+ for (const auto& rLocaleName : dict.aLocaleNames)
+ {
+ aLocaleNamesSet.insert( rLocaleName );
+ }
+ }
+ // ... and add them to the resulting sequence
+ std::vector<Locale> aLocalesVec;
+ aLocalesVec.reserve(aLocaleNamesSet.size());
+
+ std::transform(aLocaleNamesSet.begin(), aLocaleNamesSet.end(), std::back_inserter(aLocalesVec),
+ [](const OUString& localeName) { return LanguageTag::convertToLocale(localeName); });
+
+ aSuppLocales = comphelper::containerToSequence(aLocalesVec);
+
+ //! For each dictionary and each locale we need a separate entry.
+ //! If this results in more than one dictionary per locale than (for now)
+ //! it is undefined which dictionary gets used.
+ //! In the future the implementation should support using several dictionaries
+ //! for one locale.
+ sal_Int32 numdict = std::accumulate(aDics.begin(), aDics.end(), 0,
+ [](const sal_Int32 nSum, const SvtLinguConfigDictionaryEntry& dict) {
+ return nSum + dict.aLocaleNames.getLength(); });
+
+ // add dictionary information
+ mvDicts.resize(numdict);
+
+ sal_Int32 k = 0;
+ for (auto const& dict : aDics)
+ {
+ if (dict.aLocaleNames.hasElements() &&
+ dict.aLocations.hasElements())
+ {
+ // currently only one language per dictionary is supported in the actual implementation...
+ // Thus here we work-around this by adding the same dictionary several times.
+ // Once for each of its supported locales.
+ for (const auto& rLocaleName : dict.aLocaleNames)
+ {
+ LanguageTag aLanguageTag(rLocaleName);
+ mvDicts[k].aPtr = nullptr;
+ mvDicts[k].eEnc = RTL_TEXTENCODING_DONTKNOW;
+ mvDicts[k].aLoc = aLanguageTag.getLocale();
+ mvDicts[k].apCC.reset( new CharClass( std::move(aLanguageTag) ) );
+ // also both files have to be in the same directory and the
+ // file names must only differ in the extension (.aff/.dic).
+ // Thus we use the first location only and strip the extension part.
+ OUString aLocation = dict.aLocations[0];
+ sal_Int32 nPos = aLocation.lastIndexOf( '.' );
+ aLocation = aLocation.copy( 0, nPos );
+ mvDicts[k].aName = aLocation;
+
+ ++k;
+ }
+ }
+ }
+ DBG_ASSERT( k == numdict, "index mismatch?" );
+ }
+ else
+ {
+ // no dictionary found so register no dictionaries
+ mvDicts.clear();
+ aSuppLocales.realloc(0);
+ }
+ }
+
+ return aSuppLocales;
+}
+
+sal_Bool SAL_CALL Hyphenator::hasLocale(const Locale& rLocale)
+{
+ MutexGuard aGuard( GetLinguMutex() );
+
+ if (!aSuppLocales.hasElements())
+ getLocales();
+
+ return comphelper::findValue(aSuppLocales, rLocale) != -1;
+}
+
+namespace {
+bool LoadDictionary(HDInfo& rDict)
+{
+ OUString DictFN = rDict.aName + ".dic";
+ OUString dictpath;
+
+ osl::FileBase::getSystemPathFromFileURL(DictFN, dictpath);
+
+#if defined(_WIN32)
+ // hnj_hyphen_load expects UTF-8 encoded paths with \\?\ long path prefix.
+ OString sTmp = Win_AddLongPathPrefix(OUStringToOString(dictpath, RTL_TEXTENCODING_UTF8));
+#else
+ OString sTmp(OU2ENC(dictpath, osl_getThreadTextEncoding()));
+#endif
+ HyphenDict *dict = nullptr;
+ if ((dict = hnj_hyphen_load(sTmp.getStr())) == nullptr)
+ {
+ SAL_WARN(
+ "lingucomponent",
+ "Couldn't find file " << dictpath);
+ return false;
+ }
+ rDict.aPtr = dict;
+ rDict.eEnc = getTextEncodingFromCharset(dict->cset);
+ return true;
+}
+}
+
+Reference< XHyphenatedWord > SAL_CALL Hyphenator::hyphenate( const OUString& aWord,
+ const css::lang::Locale& aLocale,
+ sal_Int16 nMaxLeading,
+ const css::uno::Sequence< css::beans::PropertyValue >& aProperties )
+{
+ PropertyHelper_Hyphenation& rHelper = GetPropHelper();
+ rHelper.SetTmpPropVals(aProperties);
+ sal_Int16 minTrail = rHelper.GetMinTrailing();
+ sal_Int16 minLead = rHelper.GetMinLeading();
+ sal_Int16 minLen = rHelper.GetMinWordLength();
+ bool bNoHyphenateCaps = rHelper.IsNoHyphenateCaps();
+
+ rtl_TextEncoding eEnc = RTL_TEXTENCODING_DONTKNOW;
+
+ Reference< XHyphenatedWord > xRes;
+
+ int k = -1;
+ for (size_t j = 0; j < mvDicts.size(); ++j)
+ {
+ if (aLocale == mvDicts[j].aLoc)
+ k = j;
+ }
+
+ // if we have a hyphenation dictionary matching this locale
+ if (k != -1)
+ {
+ int nHyphenationPos = -1;
+ int nHyphenationPosAlt = -1;
+ int nHyphenationPosAltHyph = -1;
+
+ // if this dictionary has not been loaded yet do that
+ if (!mvDicts[k].aPtr)
+ {
+ if (!LoadDictionary(mvDicts[k]))
+ return nullptr;
+ }
+
+ // otherwise hyphenate the word with that dictionary
+ HyphenDict *dict = mvDicts[k].aPtr;
+ eEnc = mvDicts[k].eEnc;
+ CharClass * pCC = mvDicts[k].apCC.get();
+
+ // Don't hyphenate uppercase words if requested
+ if (bNoHyphenateCaps && aWord == makeUpperCase(aWord, pCC))
+ {
+ return nullptr;
+ }
+
+ // we don't want to work with a default text encoding since following incorrect
+ // results may occur only for specific text and thus may be hard to notice.
+ // Thus better always make a clean exit here if the text encoding is in question.
+ // Hopefully something not working at all will raise proper attention quickly. ;-)
+ DBG_ASSERT( eEnc != RTL_TEXTENCODING_DONTKNOW, "failed to get text encoding! (maybe incorrect encoding string in file)" );
+ if (eEnc == RTL_TEXTENCODING_DONTKNOW)
+ return nullptr;
+
+ CapType ct = capitalType(aWord, pCC);
+
+ // first convert any smart quotes or apostrophes to normal ones
+ OUStringBuffer rBuf(aWord);
+ sal_Int32 nc = rBuf.getLength();
+ sal_Unicode ch;
+ for (sal_Int32 ix=0; ix < nc; ix++)
+ {
+ ch = rBuf[ix];
+ if ((ch == 0x201C) || (ch == 0x201D))
+ rBuf[ix] = u'"';
+ if ((ch == 0x2018) || (ch == 0x2019))
+ rBuf[ix] = u'\'';
+ }
+ OUString nWord(rBuf.makeStringAndClear());
+
+ // now convert word to all lowercase for pattern recognition
+ OUString nTerm(makeLowerCase(nWord, pCC));
+
+ // now convert word to needed encoding
+ OString encWord(OU2ENC(nTerm,eEnc));
+
+ int wordlen = encWord.getLength();
+ std::unique_ptr<char[]> lcword(new char[wordlen + 1]);
+ std::unique_ptr<char[]> hyphens(new char[wordlen + 5]);
+
+ char ** rep = nullptr; // replacements of discretionary hyphenation
+ int * pos = nullptr; // array of [hyphenation point] minus [deletion position]
+ int * cut = nullptr; // length of deletions in original word
+
+ // copy converted word into simple char buffer
+ strcpy(lcword.get(),encWord.getStr());
+
+ // now strip off any ending periods
+ int n = wordlen-1;
+ while((n >=0) && (lcword[n] == '.'))
+ n--;
+ n++;
+ if (n > 0)
+ {
+ const bool bFailed = 0 != hnj_hyphen_hyphenate3( dict, lcword.get(), n, hyphens.get(), nullptr,
+ &rep, &pos, &cut, minLead, minTrail,
+ std::max<sal_Int16>(dict->clhmin, std::max<sal_Int16>(dict->clhmin, 2) + std::max(0, minLead - std::max<sal_Int16>(dict->lhmin, 2))),
+ std::max<sal_Int16>(dict->crhmin, std::max<sal_Int16>(dict->crhmin, 2) + std::max(0, minTrail - std::max<sal_Int16>(dict->rhmin, 2))) );
+ if (bFailed)
+ {
+ // whoops something did not work
+ if (rep)
+ {
+ for(int j = 0; j < n; j++)
+ {
+ if (rep[j]) free(rep[j]);
+ }
+ free(rep);
+ }
+ if (pos) free(pos);
+ if (cut) free(cut);
+ return nullptr;
+ }
+ }
+
+ // now backfill hyphens[] for any removed trailing periods
+ for (int c = n; c < wordlen; c++) hyphens[c] = '0';
+ hyphens[wordlen] = '\0';
+
+ sal_Int32 Leading = GetPosInWordToCheck( aWord, nMaxLeading );
+
+ for (sal_Int32 i = 0; i < n; i++)
+ {
+ int leftrep = 0;
+ bool hit = (n >= minLen);
+ if (!rep || !rep[i])
+ {
+ hit = hit && (hyphens[i]&1) && (i < Leading);
+ hit = hit && (i >= (minLead-1) );
+ hit = hit && ((n - i - 1) >= minTrail);
+ }
+ else
+ {
+ // calculate change character length before hyphenation point signed with '='
+ for (char * c = rep[i]; *c && (*c != '='); c++)
+ {
+ if (eEnc == RTL_TEXTENCODING_UTF8)
+ {
+ if (static_cast<unsigned char>(*c) >> 6 != 2)
+ leftrep++;
+ }
+ else
+ leftrep++;
+ }
+ hit = hit && (hyphens[i]&1) && ((i + leftrep - pos[i]) < Leading);
+ hit = hit && ((i + leftrep - pos[i]) >= (minLead-1) );
+ hit = hit && ((n - i - 1 + sal::static_int_cast< sal_sSize >(strlen(rep[i])) - leftrep - 1) >= minTrail);
+ }
+ if (hit)
+ {
+ nHyphenationPos = i;
+ if (rep && rep[i])
+ {
+ nHyphenationPosAlt = i - pos[i];
+ nHyphenationPosAltHyph = i + leftrep - pos[i];
+ }
+ }
+ }
+
+ if (nHyphenationPos == -1)
+ {
+ xRes = nullptr;
+ }
+ else
+ {
+ if (rep && rep[nHyphenationPos])
+ {
+ // remove equal sign
+ char * s = rep[nHyphenationPos];
+ int eq = 0;
+ for (; *s; s++)
+ {
+ if (*s == '=') eq = 1;
+ if (eq) *s = *(s + 1);
+ }
+ OUString repHyphlow(rep[nHyphenationPos], strlen(rep[nHyphenationPos]), eEnc);
+ OUString repHyph;
+ switch (ct)
+ {
+ case CapType::ALLCAP:
+ {
+ repHyph = makeUpperCase(repHyphlow, pCC);
+ break;
+ }
+ case CapType::INITCAP:
+ {
+ if (nHyphenationPosAlt == -1)
+ repHyph = makeInitCap(repHyphlow, pCC);
+ else
+ repHyph = repHyphlow;
+ break;
+ }
+ default:
+ {
+ repHyph = repHyphlow;
+ break;
+ }
+ }
+
+ // handle shortening
+ sal_Int16 nPos = static_cast<sal_Int16>((nHyphenationPosAltHyph < nHyphenationPos) ?
+ nHyphenationPosAltHyph : nHyphenationPos);
+ // discretionary hyphenation
+ xRes = HyphenatedWord::CreateHyphenatedWord( aWord, LinguLocaleToLanguage( aLocale ), nPos,
+ aWord.replaceAt(nHyphenationPosAlt + 1, cut[nHyphenationPos], repHyph),
+ static_cast<sal_Int16>(nHyphenationPosAltHyph));
+ }
+ else
+ {
+ xRes = HyphenatedWord::CreateHyphenatedWord( aWord, LinguLocaleToLanguage( aLocale ),
+ static_cast<sal_Int16>(nHyphenationPos), aWord, static_cast<sal_Int16>(nHyphenationPos));
+ }
+ }
+
+ if (rep)
+ {
+ for(int j = 0; j < n; j++)
+ {
+ if (rep[j]) free(rep[j]);
+ }
+ free(rep);
+ }
+ if (pos) free(pos);
+ if (cut) free(cut);
+ return xRes;
+ }
+ return nullptr;
+}
+
+Reference < XHyphenatedWord > SAL_CALL Hyphenator::queryAlternativeSpelling(
+ const OUString& aWord,
+ const css::lang::Locale& aLocale,
+ sal_Int16 nIndex,
+ const css::uno::Sequence< css::beans::PropertyValue >& aProperties )
+{
+ // Firstly we allow only one plus character before the hyphen to avoid to miss the right break point:
+ for (int extrachar = 1; extrachar <= 2; extrachar++)
+ {
+ Reference< XHyphenatedWord > xRes = hyphenate(aWord, aLocale, nIndex + 1 + extrachar, aProperties);
+ if (xRes.is() && xRes->isAlternativeSpelling() && xRes->getHyphenationPos() == nIndex)
+ return xRes;
+ }
+ return nullptr;
+}
+
+Reference< XPossibleHyphens > SAL_CALL Hyphenator::createPossibleHyphens( const OUString& aWord,
+ const css::lang::Locale& aLocale,
+ const css::uno::Sequence< css::beans::PropertyValue >& aProperties )
+{
+ PropertyHelper_Hyphenation& rHelper = GetPropHelper();
+ rHelper.SetTmpPropVals(aProperties);
+ sal_Int16 minTrail = rHelper.GetMinTrailing();
+ sal_Int16 minLead = rHelper.GetMinLeading();
+ sal_Int16 minLen = rHelper.GetMinWordLength();
+
+ // Resolves: fdo#41083 honour MinWordLength in "createPossibleHyphens" as
+ // well as "hyphenate"
+ if (aWord.getLength() < minLen)
+ {
+ return PossibleHyphens::CreatePossibleHyphens( aWord, LinguLocaleToLanguage( aLocale ),
+ aWord, Sequence< sal_Int16 >() );
+ }
+
+ int k = -1;
+ for (size_t j = 0; j < mvDicts.size(); ++j)
+ {
+ if (aLocale == mvDicts[j].aLoc)
+ k = j;
+ }
+
+ // if we have a hyphenation dictionary matching this locale
+ if (k != -1)
+ {
+ HyphenDict *dict = nullptr;
+ // if this dictionary has not been loaded yet do that
+ if (!mvDicts[k].aPtr)
+ {
+ if (!LoadDictionary(mvDicts[k]))
+ return nullptr;
+ }
+
+ // otherwise hyphenate the word with that dictionary
+ dict = mvDicts[k].aPtr;
+ rtl_TextEncoding eEnc = mvDicts[k].eEnc;
+ CharClass* pCC = mvDicts[k].apCC.get();
+
+ // we don't want to work with a default text encoding since following incorrect
+ // results may occur only for specific text and thus may be hard to notice.
+ // Thus better always make a clean exit here if the text encoding is in question.
+ // Hopefully something not working at all will raise proper attention quickly. ;-)
+ DBG_ASSERT( eEnc != RTL_TEXTENCODING_DONTKNOW, "failed to get text encoding! (maybe incorrect encoding string in file)" );
+ if (eEnc == RTL_TEXTENCODING_DONTKNOW)
+ return nullptr;
+
+ // first handle smart quotes both single and double
+ OUStringBuffer rBuf(aWord);
+ sal_Int32 nc = rBuf.getLength();
+ sal_Unicode ch;
+ for (sal_Int32 ix=0; ix < nc; ix++)
+ {
+ ch = rBuf[ix];
+ if ((ch == 0x201C) || (ch == 0x201D))
+ rBuf[ix] = u'"';
+ if ((ch == 0x2018) || (ch == 0x2019))
+ rBuf[ix] = u'\'';
+ }
+ OUString nWord(rBuf.makeStringAndClear());
+
+ // now convert word to all lowercase for pattern recognition
+ OUString nTerm(makeLowerCase(nWord, pCC));
+
+ // now convert word to needed encoding
+ OString encWord(OU2ENC(nTerm,eEnc));
+
+ sal_Int32 wordlen = encWord.getLength();
+ std::unique_ptr<char[]> lcword(new char[wordlen+1]);
+ std::unique_ptr<char[]> hyphens(new char[wordlen+5]);
+ char ** rep = nullptr; // replacements of discretionary hyphenation
+ int * pos = nullptr; // array of [hyphenation point] minus [deletion position]
+ int * cut = nullptr; // length of deletions in original word
+
+ // copy converted word into simple char buffer
+ strcpy(lcword.get(),encWord.getStr());
+
+ // first remove any trailing periods
+ sal_Int32 n = wordlen-1;
+ while((n >=0) && (lcword[n] == '.'))
+ n--;
+ n++;
+ if (n > 0)
+ {
+ const bool bFailed = 0 != hnj_hyphen_hyphenate3(dict, lcword.get(), n, hyphens.get(), nullptr,
+ &rep, &pos, &cut, minLead, minTrail,
+ std::max<sal_Int16>(dict->clhmin, std::max<sal_Int16>(dict->clhmin, 2) + std::max(0, minLead - std::max<sal_Int16>(dict->lhmin, 2))),
+ std::max<sal_Int16>(dict->crhmin, std::max<sal_Int16>(dict->crhmin, 2) + std::max(0, minTrail - std::max<sal_Int16>(dict->rhmin, 2))) );
+ if (bFailed)
+ {
+ if (rep)
+ {
+ for(int j = 0; j < n; j++)
+ {
+ if (rep[j]) free(rep[j]);
+ }
+ free(rep);
+ }
+ if (pos) free(pos);
+ if (cut) free(cut);
+
+ return nullptr;
+ }
+ }
+ // now backfill hyphens[] for any removed periods
+ for (sal_Int32 c = n; c < wordlen; c++)
+ hyphens[c] = '0';
+ hyphens[wordlen] = '\0';
+
+ sal_Int32 nHyphCount = 0;
+
+ for ( sal_Int32 i = 0; i < encWord.getLength(); i++)
+ {
+ if (hyphens[i]&1)
+ nHyphCount++;
+ }
+
+ Sequence< sal_Int16 > aHyphPos(nHyphCount);
+ sal_Int16 *pPos = aHyphPos.getArray();
+ OUStringBuffer hyphenatedWordBuffer;
+ nHyphCount = 0;
+
+ for (sal_Int32 i = 0; i < nWord.getLength(); i++)
+ {
+ hyphenatedWordBuffer.append(aWord[i]);
+ // hyphenation position
+ if (hyphens[i]&1)
+ {
+ // linguistic::PossibleHyphens is stuck with
+ // css::uno::Sequence<sal_Int16> because of
+ // css.linguistic2.XPossibleHyphens.getHyphenationPositions, so
+ // any further positions need to be ignored:
+ assert(i >= SAL_MIN_INT16);
+ if (i > SAL_MAX_INT16)
+ {
+ SAL_WARN(
+ "lingucomponent",
+ "hyphen pos " << i << " > SAL_MAX_INT16 in \"" << aWord
+ << "\"");
+ continue;
+ }
+ pPos[nHyphCount] = i;
+ hyphenatedWordBuffer.append('=');
+ nHyphCount++;
+ }
+ }
+
+ OUString hyphenatedWord = hyphenatedWordBuffer.makeStringAndClear();
+
+ Reference< XPossibleHyphens > xRes = PossibleHyphens::CreatePossibleHyphens(
+ aWord, LinguLocaleToLanguage( aLocale ), hyphenatedWord, aHyphPos);
+
+ if (rep)
+ {
+ for(int j = 0; j < n; j++)
+ {
+ if (rep[j]) free(rep[j]);
+ }
+ free(rep);
+ }
+ if (pos) free(pos);
+ if (cut) free(cut);
+
+ return xRes;
+ }
+
+ return nullptr;
+}
+
+OUString Hyphenator::makeLowerCase(const OUString& aTerm, CharClass const * pCC)
+{
+ if (pCC)
+ return pCC->lowercase(aTerm);
+ return aTerm;
+}
+
+OUString Hyphenator::makeUpperCase(const OUString& aTerm, CharClass const * pCC)
+{
+ if (pCC)
+ return pCC->uppercase(aTerm);
+ return aTerm;
+}
+
+OUString Hyphenator::makeInitCap(const OUString& aTerm, CharClass const * pCC)
+{
+ sal_Int32 tlen = aTerm.getLength();
+ if (pCC && tlen)
+ {
+ OUString bTemp = aTerm.copy(0,1);
+ if (tlen > 1)
+ return ( pCC->uppercase(bTemp, 0, 1) + pCC->lowercase(aTerm,1,(tlen-1)) );
+
+ return pCC->uppercase(bTemp, 0, 1);
+ }
+ return aTerm;
+}
+
+sal_Bool SAL_CALL Hyphenator::addLinguServiceEventListener(
+ const Reference< XLinguServiceEventListener >& rxLstnr )
+{
+ MutexGuard aGuard( GetLinguMutex() );
+
+ bool bRes = false;
+ if (!bDisposing && rxLstnr.is())
+ {
+ bRes = GetPropHelper().addLinguServiceEventListener( rxLstnr );
+ }
+ return bRes;
+}
+
+sal_Bool SAL_CALL Hyphenator::removeLinguServiceEventListener(
+ const Reference< XLinguServiceEventListener >& rxLstnr )
+{
+ MutexGuard aGuard( GetLinguMutex() );
+
+ bool bRes = false;
+ if (!bDisposing && rxLstnr.is())
+ {
+ bRes = GetPropHelper().removeLinguServiceEventListener( rxLstnr );
+ }
+ return bRes;
+}
+
+OUString SAL_CALL Hyphenator::getServiceDisplayName(const Locale& rLocale)
+{
+ std::locale loc(Translate::Create("svt", LanguageTag(rLocale)));
+ return Translate::get(STR_DESCRIPTION_LIBHYPHEN, loc);
+}
+
+void SAL_CALL Hyphenator::initialize( const Sequence< Any >& rArguments )
+{
+ MutexGuard aGuard( GetLinguMutex() );
+
+ if (pPropHelper)
+ return;
+
+ sal_Int32 nLen = rArguments.getLength();
+ if (2 == nLen)
+ {
+ Reference< XLinguProperties > xPropSet;
+ rArguments.getConstArray()[0] >>= xPropSet;
+ // rArguments.getConstArray()[1] >>= xDicList;
+
+ //! Pointer allows for access of the non-UNO functions.
+ //! And the reference to the UNO-functions while increasing
+ //! the ref-count and will implicitly free the memory
+ //! when the object is no longer used.
+ pPropHelper.reset( new PropertyHelper_Hyphenation( static_cast<XHyphenator *>(this), xPropSet ) );
+ pPropHelper->AddAsPropListener(); //! after a reference is established
+ }
+ else {
+ OSL_FAIL( "wrong number of arguments in sequence" );
+ }
+}
+
+void SAL_CALL Hyphenator::dispose()
+{
+ MutexGuard aGuard( GetLinguMutex() );
+
+ if (!bDisposing)
+ {
+ bDisposing = true;
+ EventObject aEvtObj( static_cast<XHyphenator *>(this) );
+ aEvtListeners.disposeAndClear( aEvtObj );
+ if (pPropHelper)
+ {
+ pPropHelper->RemoveAsPropListener();
+ pPropHelper.reset();
+ }
+ }
+}
+
+void SAL_CALL Hyphenator::addEventListener( const Reference< XEventListener >& rxListener )
+{
+ MutexGuard aGuard( GetLinguMutex() );
+
+ if (!bDisposing && rxListener.is())
+ aEvtListeners.addInterface( rxListener );
+}
+
+void SAL_CALL Hyphenator::removeEventListener( const Reference< XEventListener >& rxListener )
+{
+ MutexGuard aGuard( GetLinguMutex() );
+
+ if (!bDisposing && rxListener.is())
+ aEvtListeners.removeInterface( rxListener );
+}
+
+// Service specific part
+OUString SAL_CALL Hyphenator::getImplementationName()
+{
+ return "org.openoffice.lingu.LibHnjHyphenator";
+}
+
+sal_Bool SAL_CALL Hyphenator::supportsService( const OUString& ServiceName )
+{
+ return cppu::supportsService(this, ServiceName);
+}
+
+Sequence< OUString > SAL_CALL Hyphenator::getSupportedServiceNames()
+{
+ return { SN_HYPHENATOR };
+}
+
+extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface*
+lingucomponent_Hyphenator_get_implementation(
+ css::uno::XComponentContext* , css::uno::Sequence<css::uno::Any> const&)
+{
+ return cppu::acquire(new Hyphenator());
+}
+
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/lingucomponent/source/hyphenator/hyphen/hyphenimp.hxx b/lingucomponent/source/hyphenator/hyphen/hyphenimp.hxx
new file mode 100644
index 000000000..45ebca112
--- /dev/null
+++ b/lingucomponent/source/hyphenator/hyphen/hyphenimp.hxx
@@ -0,0 +1,126 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#ifndef INCLUDED_LINGUCOMPONENT_SOURCE_HYPHENATOR_HYPHEN_HYPHENIMP_HXX
+#define INCLUDED_LINGUCOMPONENT_SOURCE_HYPHENATOR_HYPHEN_HYPHENIMP_HXX
+
+#include <comphelper/interfacecontainer3.hxx>
+#include <cppuhelper/implbase.hxx>
+#include <com/sun/star/lang/XComponent.hpp>
+#include <com/sun/star/lang/XInitialization.hpp>
+#include <com/sun/star/lang/XServiceDisplayName.hpp>
+#include <com/sun/star/beans/XPropertySet.hpp>
+#include <com/sun/star/beans/PropertyValues.hpp>
+#include <com/sun/star/lang/XServiceInfo.hpp>
+#include <com/sun/star/linguistic2/XHyphenator.hpp>
+#include <com/sun/star/linguistic2/XLinguServiceEventBroadcaster.hpp>
+
+#include <unotools/charclass.hxx>
+
+#include <linguistic/misc.hxx>
+#include <linguistic/lngprophelp.hxx>
+
+#include <lingutil.hxx>
+#include <stdio.h>
+
+#include <hyphen.h>
+
+using namespace ::com::sun::star::uno;
+using namespace ::com::sun::star::beans;
+using namespace ::com::sun::star::lang;
+using namespace ::com::sun::star::linguistic2;
+
+struct HDInfo {
+ HyphenDict * aPtr;
+ OUString aName;
+ Locale aLoc;
+ rtl_TextEncoding eEnc;
+ std::unique_ptr<CharClass> apCC;
+};
+
+class Hyphenator :
+ public cppu::WeakImplHelper
+ <
+ XHyphenator,
+ XLinguServiceEventBroadcaster,
+ XInitialization,
+ XComponent,
+ XServiceInfo,
+ XServiceDisplayName
+ >
+{
+ Sequence< Locale > aSuppLocales;
+ std::vector< HDInfo > mvDicts;
+
+ ::comphelper::OInterfaceContainerHelper3<XEventListener> aEvtListeners;
+ std::unique_ptr<linguistic::PropertyHelper_Hyphenation> pPropHelper;
+ bool bDisposing;
+
+ Hyphenator(const Hyphenator &) = delete;
+ Hyphenator & operator = (const Hyphenator &) = delete;
+
+ linguistic::PropertyHelper_Hyphenation& GetPropHelper_Impl();
+ linguistic::PropertyHelper_Hyphenation& GetPropHelper()
+ {
+ return pPropHelper ? *pPropHelper : GetPropHelper_Impl();
+ }
+
+public:
+ Hyphenator();
+
+ virtual ~Hyphenator() override;
+
+ // XSupportedLocales (for XHyphenator)
+ virtual Sequence< Locale > SAL_CALL getLocales() override;
+ virtual sal_Bool SAL_CALL hasLocale( const Locale& rLocale ) override;
+
+ // XHyphenator
+ virtual css::uno::Reference< css::linguistic2::XHyphenatedWord > SAL_CALL hyphenate( const OUString& aWord, const css::lang::Locale& aLocale, sal_Int16 nMaxLeading, const css::uno::Sequence< css::beans::PropertyValue >& aProperties ) override;
+ virtual css::uno::Reference< css::linguistic2::XHyphenatedWord > SAL_CALL queryAlternativeSpelling( const OUString& aWord, const css::lang::Locale& aLocale, sal_Int16 nIndex, const css::uno::Sequence< css::beans::PropertyValue >& aProperties ) override;
+ virtual css::uno::Reference< css::linguistic2::XPossibleHyphens > SAL_CALL createPossibleHyphens( const OUString& aWord, const css::lang::Locale& aLocale, const css::uno::Sequence< css::beans::PropertyValue >& aProperties ) override;
+
+ // XLinguServiceEventBroadcaster
+ virtual sal_Bool SAL_CALL addLinguServiceEventListener( const Reference< XLinguServiceEventListener >& rxLstnr ) override;
+ virtual sal_Bool SAL_CALL removeLinguServiceEventListener( const Reference< XLinguServiceEventListener >& rxLstnr ) override;
+
+ // XServiceDisplayName
+ virtual OUString SAL_CALL getServiceDisplayName( const Locale& rLocale ) override;
+
+ // XInitialization
+ virtual void SAL_CALL initialize( const Sequence< Any >& rArguments ) override;
+
+ // XComponent
+ virtual void SAL_CALL dispose() override;
+ virtual void SAL_CALL addEventListener( const Reference< XEventListener >& rxListener ) override;
+ virtual void SAL_CALL removeEventListener( const Reference< XEventListener >& rxListener ) override;
+
+ // XServiceInfo
+ virtual OUString SAL_CALL getImplementationName() override;
+ virtual sal_Bool SAL_CALL supportsService( const OUString& rServiceName ) override;
+ virtual Sequence< OUString > SAL_CALL getSupportedServiceNames() override;
+
+private:
+ static OUString makeLowerCase(const OUString&, CharClass const *);
+ static OUString makeUpperCase(const OUString&, CharClass const *);
+ static OUString makeInitCap(const OUString&, CharClass const *);
+};
+
+#endif
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */