svx/source/tbxctrls/tbcontrl.cxx | 386 ++++++++++++++++++++++++++++++++------- 1 file changed, 320 insertions(+), 66 deletions(-)
New commits: commit 044fc5c8b225732d57970d3bf0720c13a5a9e0a4 Author: Khaled Hosny <kha...@aliftype.com> AuthorDate: Sun Aug 21 06:08:01 2022 +0200 Commit: Caolán McNamara <caol...@redhat.com> CommitDate: Mon Aug 22 15:44:06 2022 +0200 tdf#87535: Preview styles using CTL/CJK fonts in the styles menu Code is similar to code in svx/source/styles/CommonStylePreviewRenderer.cxx, etc. Instead of using only the “western” font, it splits the text based on script type and uses the appropriate font for each script type. Change-Id: I1db87748119beac18e5b7e61617419c149400c27 Reviewed-on: https://gerrit.libreoffice.org/c/core/+/138601 Tested-by: Jenkins Tested-by: Caolán McNamara <caol...@redhat.com> Reviewed-by: Caolán McNamara <caol...@redhat.com> diff --git a/svx/source/tbxctrls/tbcontrl.cxx b/svx/source/tbxctrls/tbcontrl.cxx index 70063a04980b..610bb630e55e 100644 --- a/svx/source/tbxctrls/tbcontrl.cxx +++ b/svx/source/tbxctrls/tbcontrl.cxx @@ -26,6 +26,7 @@ #include <svl/numformat.hxx> #include <svl/poolitem.hxx> #include <svl/itemset.hxx> +#include <svl/itempool.hxx> #include <vcl/commandinfoprovider.hxx> #include <vcl/event.hxx> #include <vcl/toolbox.hxx> @@ -103,6 +104,11 @@ #include <comphelper/lok.hxx> #include <tools/json_writer.hxx> +#include <com/sun/star/uno/Reference.h> +#include <com/sun/star/i18n/BreakIterator.hpp> +#include <comphelper/processfactory.hxx> +#include <com/sun/star/i18n/ScriptType.hpp> + #define MAX_MRU_FONTNAME_ENTRIES 5 #define COMBO_WIDTH_IN_CHARS 18 @@ -117,6 +123,19 @@ using namespace ::com::sun::star::lang; namespace { +struct ScriptInfo +{ + tools::Long textWidth; + sal_uInt16 scriptType; + sal_Int32 changePos; + ScriptInfo(sal_uInt16 scrptType, sal_Int32 position) + : textWidth(0) + , scriptType(scrptType) + , changePos(position) + { + } +}; + class SvxStyleBox_Base { public: @@ -186,6 +205,12 @@ public: virtual bool DoKeyInput(const KeyEvent& rKEvt); private: + std::optional<SvxFont> m_oFont; + std::optional<SvxFont> m_oCJKFont; + std::optional<SvxFont> m_oCTLFont; + + css::uno::Reference<css::i18n::XBreakIterator> mxBreak; + DECL_LINK(SelectHdl, weld::ComboBox&, void); DECL_LINK(KeyInputHdl, const KeyEvent&, bool); DECL_LINK(ActivateHdl, weld::ComboBox&, bool); @@ -199,6 +224,9 @@ private: void Select(bool bNonTravelSelect); + std::vector<ScriptInfo> CheckScript(const OUString &rStyleName); + tools::Rectangle CalcBoundRect(vcl::RenderContext& rRenderContext, const OUString &rStyleName, std::vector<ScriptInfo>& rScriptChanges, double fRatio = 1); + protected: SvxStyleToolBoxControl& m_rCtrl; @@ -220,9 +248,8 @@ protected: void ReleaseFocus(); static Color TestColorsVisible(const Color &FontCol, const Color &BackCol); - static void UserDrawEntry(vcl::RenderContext& rRenderContext, const tools::Rectangle& rRect, const OUString &rStyleName); + void UserDrawEntry(vcl::RenderContext& rRenderContext, const tools::Rectangle& rRect, const tools::Rectangle& rTextRect, const OUString &rStyleName, const std::vector<ScriptInfo>& rScriptChanges); void SetupEntry(vcl::RenderContext& rRenderContext, sal_Int32 nItem, const tools::Rectangle& rRect, std::u16string_view rStyleName, bool bIsNotSelected); - static bool AdjustFontForItemHeight(OutputDevice& rDevice, tools::Rectangle const & rTextRect, tools::Long nHeight); DECL_LINK(MenuSelectHdl, const OString&, void); DECL_STATIC_LINK(SvxStyleBox_Base, ShowMoreHdl, void*, void); }; @@ -1088,23 +1115,6 @@ void SvxStyleBox_Impl::DataChanged( const DataChangedEvent& rDCEvt ) InterimItemWindow::DataChanged( rDCEvt ); } -bool SvxStyleBox_Base::AdjustFontForItemHeight(OutputDevice& rDevice, tools::Rectangle const & rTextRect, tools::Long nHeight) -{ - if (rTextRect.Bottom() > nHeight) - { - // the text does not fit, adjust the font size - double ratio = static_cast< double >( nHeight ) / rTextRect.Bottom(); - vcl::Font aFont(rDevice.GetFont()); - Size aPixelSize(aFont.GetFontSize()); - aPixelSize.setWidth( aPixelSize.Width() * ratio ); - aPixelSize.setHeight( aPixelSize.Height() * ratio ); - aFont.SetFontSize(aPixelSize); - rDevice.SetFont(aFont); - return true; - } - return false; -} - void SvxStyleBox_Impl::SetOptimalSize() { // set width in chars low so the size request will not be overridden @@ -1116,23 +1126,232 @@ void SvxStyleBox_Impl::SetOptimalSize() SetSizePixel(get_preferred_size()); } -void SvxStyleBox_Base::UserDrawEntry(vcl::RenderContext& rRenderContext, const tools::Rectangle& rRect, const OUString &rStyleName) +std::vector<ScriptInfo> SvxStyleBox_Base::CheckScript(const OUString &rStyleName) +{ + assert(!rStyleName.isEmpty()); // must have a preview text here! + + std::vector<ScriptInfo> aScriptChanges; + + if (!mxBreak.is()) + { + auto xContext = comphelper::getProcessComponentContext(); + mxBreak = css::i18n::BreakIterator::create(xContext); + } + + sal_Int16 nScript = mxBreak->getScriptType(rStyleName, 0); + sal_Int32 nChg = 0; + if (css::i18n::ScriptType::WEAK == nScript) + { + nChg = mxBreak->endOfScript(rStyleName, nChg, nScript); + if (nChg < rStyleName.getLength()) + nScript = mxBreak->getScriptType(rStyleName, nChg); + else + nScript = css::i18n::ScriptType::LATIN; + } + + while (true) + { + nChg = mxBreak->endOfScript(rStyleName, nChg, nScript); + aScriptChanges.emplace_back(nScript, nChg); + if (nChg >= rStyleName.getLength() || nChg < 0) + break; + nScript = mxBreak->getScriptType(rStyleName, nChg); + } + + return aScriptChanges; +} + +tools::Rectangle SvxStyleBox_Base::CalcBoundRect(vcl::RenderContext& rRenderContext, const OUString &rStyleName, std::vector<ScriptInfo>& rScriptChanges, double fRatio) +{ + tools::Rectangle aTextRect; + + sal_uInt16 nScript; + sal_uInt16 nIdx = 0; + sal_Int32 nStart = 0; + sal_Int32 nEnd; + size_t nCnt = rScriptChanges.size(); + + if (nCnt) + { + nEnd = rScriptChanges[nIdx].changePos; + nScript = rScriptChanges[nIdx].scriptType; + } + else + { + nEnd = rStyleName.getLength(); + nScript = css::i18n::ScriptType::LATIN; + } + + do + { + auto oFont = (nScript == css::i18n::ScriptType::ASIAN) ? + m_oCJKFont : + ((nScript == css::i18n::ScriptType::COMPLEX) ? + m_oCTLFont : + m_oFont); + + rRenderContext.Push(vcl::PushFlags::FONT); + + if (oFont) + rRenderContext.SetFont(*oFont); + + if (fRatio != 1) + { + vcl::Font aFont(rRenderContext.GetFont()); + Size aPixelSize(aFont.GetFontSize()); + aPixelSize.setWidth(aPixelSize.Width() * fRatio); + aPixelSize.setHeight(aPixelSize.Height() * fRatio); + aFont.SetFontSize(aPixelSize); + rRenderContext.SetFont(aFont); + } + + tools::Rectangle aRect; + rRenderContext.GetTextBoundRect(aRect, rStyleName, nStart, nStart, nEnd - nStart); + aTextRect = aTextRect.Union(aRect); + + tools::Long nWidth = rRenderContext.GetTextWidth(rStyleName, nStart, nEnd - nStart); + + rRenderContext.Pop(); + + if (nIdx >= rScriptChanges.size()) + break; + + rScriptChanges[nIdx++].textWidth = nWidth; + + if (nEnd < rStyleName.getLength() && nIdx < nCnt) + { + nStart = nEnd; + nEnd = rScriptChanges[nIdx].changePos; + nScript = rScriptChanges[nIdx].scriptType; + } + else + break; + } + while(true); + + return aTextRect; +} + +void SvxStyleBox_Base::UserDrawEntry(vcl::RenderContext& rRenderContext, const tools::Rectangle& rRect, const tools::Rectangle& rTextRect, const OUString &rStyleName, const std::vector<ScriptInfo>& rScriptChanges) { // IMG_TXT_DISTANCE in ilstbox.hxx is 6, then 1 is added as // nBorder, and we are adding 1 in order to look better when // italics is present const int nLeftDistance = 8; - tools::Rectangle aTextRect; - rRenderContext.GetTextBoundRect(aTextRect, rStyleName); - Point aPos(rRect.TopLeft()); aPos.AdjustX(nLeftDistance ); - if (!AdjustFontForItemHeight(rRenderContext, aTextRect, rRect.GetHeight())) - aPos.AdjustY((rRect.GetHeight() - aTextRect.Bottom() ) / 2); + double fRatio = 1; + if (rTextRect.Bottom() > rRect.GetHeight()) + fRatio = static_cast<double>(rRect.GetHeight()) / rTextRect.Bottom(); + else + aPos.AdjustY((rRect.GetHeight() - rTextRect.Bottom()) / 2); - rRenderContext.DrawText(aPos, rStyleName); + sal_uInt16 nScript; + sal_uInt16 nIdx = 0; + sal_Int32 nStart = 0; + sal_Int32 nEnd; + size_t nCnt = rScriptChanges.size(); + if (nCnt) + { + nEnd = rScriptChanges[nIdx].changePos; + nScript = rScriptChanges[nIdx].scriptType; + } + else + { + nEnd = rStyleName.getLength(); + nScript = css::i18n::ScriptType::LATIN; + } + + do + { + auto oFont = (nScript == css::i18n::ScriptType::ASIAN) + ? m_oCJKFont + : ((nScript == css::i18n::ScriptType::COMPLEX) + ? m_oCTLFont + : m_oFont); + + rRenderContext.Push(vcl::PushFlags::FONT); + + if (oFont) + rRenderContext.SetFont(*oFont); + + if (fRatio != 1) + { + vcl::Font aFont(rRenderContext.GetFont()); + Size aPixelSize(aFont.GetFontSize()); + aPixelSize.setWidth(aPixelSize.Width() * fRatio); + aPixelSize.setHeight(aPixelSize.Height() * fRatio); + aFont.SetFontSize(aPixelSize); + rRenderContext.SetFont(aFont); + } + + rRenderContext.DrawText(aPos, rStyleName, nStart, nEnd - nStart); + + rRenderContext.Pop(); + + aPos.AdjustX(rScriptChanges[nIdx++].textWidth * fRatio); + if (nEnd < rStyleName.getLength() && nIdx < nCnt) + { + nStart = nEnd; + nEnd = rScriptChanges[nIdx].changePos; + nScript = rScriptChanges[nIdx].scriptType; + } + else + break; + } + while(true); +} + +static bool GetWhich(const SfxItemSet& rSet, sal_uInt16 nSlot, sal_uInt16& rWhich) +{ + rWhich = rSet.GetPool()->GetWhich(nSlot); + return rSet.GetItemState(rWhich) >= SfxItemState::DEFAULT; +} + +static bool SetFont(const SfxItemSet& rSet, sal_uInt16 nSlot, SvxFont& rFont) +{ + sal_uInt16 nWhich; + if (GetWhich(rSet, nSlot, nWhich)) + { + const auto& rFontItem = static_cast<const SvxFontItem&>(rSet.Get(nWhich)); + rFont.SetFamilyName(rFontItem.GetFamilyName()); + rFont.SetStyleName(rFontItem.GetStyleName()); + return true; + } + return false; +} + +static bool SetFontSize(vcl::RenderContext& rRenderContext, const SfxItemSet& rSet, sal_uInt16 nSlot, SvxFont& rFont) +{ + sal_uInt16 nWhich; + if (GetWhich(rSet, nSlot, nWhich)) + { + const auto& rFontHeightItem = static_cast<const SvxFontHeightItem&>(rSet.Get(nWhich)); + SfxObjectShell *pShell = SfxObjectShell::Current(); + Size aFontSize(0, rFontHeightItem.GetHeight()); + Size aPixelSize(rRenderContext.LogicToPixel(aFontSize, MapMode(pShell->GetMapUnit()))); + rFont.SetFontSize(aPixelSize); + return true; + } + return false; +} + +static void SetFontStyle(const SfxItemSet& rSet, sal_uInt16 nPosture, sal_uInt16 nWeight, SvxFont& rFont) +{ + sal_uInt16 nWhich; + if (GetWhich(rSet, nPosture, nWhich)) + { + const auto& rItem = static_cast<const SvxPostureItem&>(rSet.Get(nWhich)); + rFont.SetItalic(rItem.GetPosture()); + } + + if (GetWhich(rSet, nWeight, nWhich)) + { + const auto& rItem = static_cast<const SvxWeightItem&>(rSet.Get(nWhich)); + rFont.SetWeight(rItem.GetWeight()); + } } void SvxStyleBox_Base::SetupEntry(vcl::RenderContext& rRenderContext, sal_Int32 nItem, const tools::Rectangle& rRect, std::u16string_view rStyleName, bool bIsNotSelected) @@ -1175,68 +1394,89 @@ void SvxStyleBox_Base::SetupEntry(vcl::RenderContext& rRenderContext, sal_Int32 std::optional<SfxItemSet> const pItemSet(pStyle->GetItemSetForPreview()); if (!pItemSet) return; - const SvxFontItem * const pFontItem = - pItemSet->GetItem<SvxFontItem>(SID_ATTR_CHAR_FONT); - const SvxFontHeightItem * const pFontHeightItem = - pItemSet->GetItem<SvxFontHeightItem>(SID_ATTR_CHAR_FONTHEIGHT); - - if ( !(pFontItem && pFontHeightItem) ) - return; - - Size aFontSize( 0, pFontHeightItem->GetHeight() ); - Size aPixelSize(rRenderContext.LogicToPixel(aFontSize, MapMode(pShell->GetMapUnit()))); - - // setup the font properties SvxFont aFont; - aFont.SetFamilyName(pFontItem->GetFamilyName()); - aFont.SetStyleName(pFontItem->GetStyleName()); - aFont.SetFontSize(aPixelSize); + SvxFont aCJKFont; + SvxFont aCTLFont; - const SfxPoolItem *pItem = pItemSet->GetItem( SID_ATTR_CHAR_WEIGHT ); - if ( pItem ) - aFont.SetWeight( static_cast< const SvxWeightItem* >( pItem )->GetWeight() ); - - pItem = pItemSet->GetItem( SID_ATTR_CHAR_POSTURE ); - if ( pItem ) - aFont.SetItalic( static_cast< const SvxPostureItem* >( pItem )->GetPosture() ); + SetFontStyle(*pItemSet, SID_ATTR_CHAR_POSTURE, SID_ATTR_CHAR_WEIGHT, aFont); + SetFontStyle(*pItemSet, SID_ATTR_CHAR_CJK_POSTURE, SID_ATTR_CHAR_CJK_WEIGHT, aCJKFont); + SetFontStyle(*pItemSet, SID_ATTR_CHAR_CTL_POSTURE, SID_ATTR_CHAR_CTL_WEIGHT, aCTLFont); - pItem = pItemSet->GetItem( SID_ATTR_CHAR_CONTOUR ); + const SfxPoolItem *pItem = pItemSet->GetItem( SID_ATTR_CHAR_CONTOUR ); if ( pItem ) - aFont.SetOutline( static_cast< const SvxContourItem* >( pItem )->GetValue() ); + { + auto aVal = static_cast< const SvxContourItem* >( pItem )->GetValue(); + aFont.SetOutline(aVal); + aCJKFont.SetOutline(aVal); + aCTLFont.SetOutline(aVal); + } pItem = pItemSet->GetItem( SID_ATTR_CHAR_SHADOWED ); if ( pItem ) - aFont.SetShadow( static_cast< const SvxShadowedItem* >( pItem )->GetValue() ); + { + auto aVal = static_cast< const SvxShadowedItem* >( pItem )->GetValue(); + aFont.SetShadow(aVal); + aCJKFont.SetShadow(aVal); + aCTLFont.SetShadow(aVal); + } pItem = pItemSet->GetItem( SID_ATTR_CHAR_RELIEF ); if ( pItem ) - aFont.SetRelief( static_cast< const SvxCharReliefItem* >( pItem )->GetValue() ); + { + auto aVal = static_cast< const SvxCharReliefItem* >( pItem )->GetValue(); + aFont.SetRelief(aVal); + aCJKFont.SetRelief(aVal); + aCTLFont.SetRelief(aVal); + } pItem = pItemSet->GetItem( SID_ATTR_CHAR_UNDERLINE ); if ( pItem ) - aFont.SetUnderline( static_cast< const SvxUnderlineItem* >( pItem )->GetLineStyle() ); + { + auto aVal = static_cast<const SvxUnderlineItem*>(pItem)->GetLineStyle(); + aFont.SetUnderline(aVal); + aCJKFont.SetUnderline(aVal); + aCTLFont.SetUnderline(aVal); + } pItem = pItemSet->GetItem( SID_ATTR_CHAR_OVERLINE ); if ( pItem ) - aFont.SetOverline( static_cast< const SvxOverlineItem* >( pItem )->GetValue() ); + { + auto aVal = static_cast< const SvxOverlineItem* >( pItem )->GetValue(); + aFont.SetOverline(aVal); + aCJKFont.SetOverline(aVal); + aCTLFont.SetOverline(aVal); + } pItem = pItemSet->GetItem( SID_ATTR_CHAR_STRIKEOUT ); if ( pItem ) - aFont.SetStrikeout( static_cast< const SvxCrossedOutItem* >( pItem )->GetStrikeout() ); + { + auto aVal = static_cast< const SvxCrossedOutItem* >( pItem )->GetStrikeout(); + aFont.SetStrikeout(aVal); + aCJKFont.SetStrikeout(aVal); + aCTLFont.SetStrikeout(aVal); + } pItem = pItemSet->GetItem( SID_ATTR_CHAR_CASEMAP ); if ( pItem ) - aFont.SetCaseMap(static_cast<const SvxCaseMapItem*>(pItem)->GetCaseMap()); + { + auto aVal = static_cast<const SvxCaseMapItem*>(pItem)->GetCaseMap(); + aFont.SetCaseMap(aVal); + aCJKFont.SetCaseMap(aVal); + aCTLFont.SetCaseMap(aVal); + } pItem = pItemSet->GetItem( SID_ATTR_CHAR_EMPHASISMARK ); if ( pItem ) - aFont.SetEmphasisMark( static_cast< const SvxEmphasisMarkItem* >( pItem )->GetEmphasisMark() ); + { + auto aVal = static_cast< const SvxEmphasisMarkItem* >( pItem )->GetEmphasisMark(); + aFont.SetEmphasisMark(aVal); + aCJKFont.SetEmphasisMark(aVal); + aCTLFont.SetEmphasisMark(aVal); + } // setup the device & draw Color aFontCol = COL_AUTO, aBackCol = COL_AUTO; - rRenderContext.SetFont(aFont); - pItem = pItemSet->GetItem( SID_ATTR_CHAR_COLOR ); // text color, when nothing is selected if ( (nullptr != pItem) && bIsNotSelected) @@ -1277,6 +1517,18 @@ void SvxStyleBox_Base::SetupEntry(vcl::RenderContext& rRenderContext, sal_Int32 // set text color if ( aFontCol != COL_AUTO ) rRenderContext.SetTextColor(aFontCol); + + if (SetFont(*pItemSet, SID_ATTR_CHAR_FONT, aFont) && + SetFontSize(rRenderContext, *pItemSet, SID_ATTR_CHAR_FONTHEIGHT, aFont)) + m_oFont = aFont; + + if (SetFont(*pItemSet, SID_ATTR_CHAR_CJK_FONT, aCJKFont) && + SetFontSize(rRenderContext, *pItemSet, SID_ATTR_CHAR_CJK_FONTHEIGHT, aCJKFont)) + m_oCJKFont = aCJKFont; + + if (SetFont(*pItemSet, SID_ATTR_CHAR_CTL_FONT, aCTLFont) && + SetFontSize(rRenderContext, *pItemSet, SID_ATTR_CHAR_CTL_FONTHEIGHT, aCTLFont)) + m_oCTLFont = aCTLFont; } IMPL_LINK(SvxStyleBox_Base, CustomRenderHdl, weld::ComboBox::render_args, aPayload, void) @@ -1293,8 +1545,9 @@ IMPL_LINK(SvxStyleBox_Base, CustomRenderHdl, weld::ComboBox::render_args, aPaylo rRenderContext.Push(vcl::PushFlags::FILLCOLOR | vcl::PushFlags::FONT | vcl::PushFlags::TEXTCOLOR); SetupEntry(rRenderContext, nIndex, rRect, aStyleName, !bSelected); - - UserDrawEntry(rRenderContext, rRect, aStyleName); + auto aScriptChanges = CheckScript(aStyleName); + auto aTextRect = CalcBoundRect(rRenderContext, aStyleName, aScriptChanges); + UserDrawEntry(rRenderContext, rRect, aTextRect, aStyleName, aScriptChanges); rRenderContext.Pop(); } @@ -1324,12 +1577,13 @@ void SvxStyleBox_Base::CalcOptimalExtraUserWidth(vcl::RenderContext& rRenderCont rRenderContext.Push(vcl::PushFlags::FILLCOLOR | vcl::PushFlags::FONT | vcl::PushFlags::TEXTCOLOR); SetupEntry(rRenderContext, i, tools::Rectangle(0, 0, RECT_MAX, ITEM_HEIGHT), sStyleName, true); - tools::Rectangle aTextRectForActualFont; - rRenderContext.GetTextBoundRect(aTextRectForActualFont, sStyleName); - if (AdjustFontForItemHeight(rRenderContext, aTextRectForActualFont, ITEM_HEIGHT)) + auto aScriptChanges = CheckScript(sStyleName); + tools::Rectangle aTextRectForActualFont = CalcBoundRect(rRenderContext, sStyleName, aScriptChanges); + if (aTextRectForActualFont.Bottom() > ITEM_HEIGHT) { - //Font didn't fit, so it was changed, refetch with final font size - rRenderContext.GetTextBoundRect(aTextRectForActualFont, sStyleName); + //Font didn't fit, re-calculate with adjustment ratio. + double fRatio = static_cast<double>(ITEM_HEIGHT) / aTextRectForActualFont.Bottom(); + aTextRectForActualFont = CalcBoundRect(rRenderContext, sStyleName, aScriptChanges, fRatio); } rRenderContext.Pop();