From 0483fbd1d05d74c394d9274bdee77b3b2aebb495 Mon Sep 17 00:00:00 2001 From: Khaled Hosny Date: Sun, 21 Aug 2022 01:49:57 +0200 Subject: tdf#87535: Preview styles using CTL/CJK fonts in the sidebar MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Code is similar to code in svx/source/dialog/fntctrl.cxx and source/ui/chrdlg/drpcps.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: Iacdf92829d4568f2d580b39ca14ffe1218ceaa9c Reviewed-on: https://gerrit.libreoffice.org/c/core/+/138600 Tested-by: Jenkins Reviewed-by: Caolán McNamara --- svx/source/styles/CommonStylePreviewRenderer.cxx | 326 ++++++++++++++++++----- 1 file changed, 266 insertions(+), 60 deletions(-) (limited to 'svx/source/styles') diff --git a/svx/source/styles/CommonStylePreviewRenderer.cxx b/svx/source/styles/CommonStylePreviewRenderer.cxx index df0e3539e769..66fbb9e72603 100644 --- a/svx/source/styles/CommonStylePreviewRenderer.cxx +++ b/svx/source/styles/CommonStylePreviewRenderer.cxx @@ -14,6 +14,7 @@ #include #include #include +#include #include #include @@ -37,6 +38,9 @@ #include +#include +#include + using namespace css; namespace svx @@ -56,57 +60,127 @@ CommonStylePreviewRenderer::CommonStylePreviewRenderer( CommonStylePreviewRenderer::~CommonStylePreviewRenderer() {} +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(rSet.Get(nWhich)); + rFont.SetFamily(rFontItem.GetFamily()); + rFont.SetFamilyName(rFontItem.GetFamilyName()); + rFont.SetPitch(rFontItem.GetPitch()); + rFont.SetCharSet(rFontItem.GetCharSet()); + rFont.SetStyleName(rFontItem.GetStyleName()); + return true; + } + return false; +} + +bool CommonStylePreviewRenderer::SetFontSize(const SfxItemSet& rSet, sal_uInt16 nSlot, SvxFont& rFont) +{ + sal_uInt16 nWhich; + if (GetWhich(rSet, nSlot, nWhich)) + { + const auto& rFontHeightItem = static_cast(rSet.Get(nWhich)); + Size aFontSize(0, rFontHeightItem.GetHeight()); + aFontSize = mrOutputDev.LogicToPixel(aFontSize, MapMode(mrShell.GetMapUnit())); + rFont.SetFontSize(aFontSize); + return true; + } + return false; +} + bool CommonStylePreviewRenderer::recalculate() { m_oFont.reset(); + m_oCJKFont.reset(); + m_oCTLFont.reset(); std::optional pItemSet(mpStyle->GetItemSetForPreview()); if (!pItemSet) return false; SvxFont aFont; + SvxFont aCJKFont; + SvxFont aCTLFont; const SfxPoolItem* pItem; if ((pItem = pItemSet->GetItem(SID_ATTR_CHAR_WEIGHT)) != nullptr) - { aFont.SetWeight(static_cast(pItem)->GetWeight()); - } + if ((pItem = pItemSet->GetItem(SID_ATTR_CHAR_CJK_WEIGHT)) != nullptr) + aCJKFont.SetWeight(static_cast(pItem)->GetWeight()); + if ((pItem = pItemSet->GetItem(SID_ATTR_CHAR_CTL_WEIGHT)) != nullptr) + aCTLFont.SetWeight(static_cast(pItem)->GetWeight()); + if ((pItem = pItemSet->GetItem(SID_ATTR_CHAR_POSTURE)) != nullptr) - { aFont.SetItalic(static_cast(pItem)->GetPosture()); - } + if ((pItem = pItemSet->GetItem(SID_ATTR_CHAR_CJK_POSTURE)) != nullptr) + aCJKFont.SetItalic(static_cast(pItem)->GetPosture()); + if ((pItem = pItemSet->GetItem(SID_ATTR_CHAR_CTL_POSTURE)) != nullptr) + aCTLFont.SetItalic(static_cast(pItem)->GetPosture()); + if ((pItem = pItemSet->GetItem(SID_ATTR_CHAR_CONTOUR)) != nullptr) { - aFont.SetOutline(static_cast< const SvxContourItem*>(pItem)->GetValue()); + auto aVal = static_cast(pItem)->GetValue(); + aFont.SetOutline(aVal); + aCJKFont.SetOutline(aVal); + aCTLFont.SetOutline(aVal); } if ((pItem = pItemSet->GetItem(SID_ATTR_CHAR_SHADOWED)) != nullptr) { - aFont.SetShadow(static_cast(pItem)->GetValue()); + auto aVal = static_cast(pItem)->GetValue(); + aFont.SetShadow(aVal); + aCJKFont.SetShadow(aVal); + aCTLFont.SetShadow(aVal); } if ((pItem = pItemSet->GetItem(SID_ATTR_CHAR_RELIEF)) != nullptr) { - aFont.SetRelief(static_cast(pItem)->GetValue()); + auto aVal = static_cast(pItem)->GetValue(); + aFont.SetRelief(aVal); + aCJKFont.SetRelief(aVal); + aCTLFont.SetRelief(aVal); } if ((pItem = pItemSet->GetItem(SID_ATTR_CHAR_UNDERLINE)) != nullptr) { - aFont.SetUnderline(static_cast< const SvxUnderlineItem*>(pItem)->GetLineStyle()); + auto aVal = static_cast(pItem)->GetLineStyle(); + aFont.SetUnderline(aVal); + aCJKFont.SetUnderline(aVal); + aCTLFont.SetUnderline(aVal); } if ((pItem = pItemSet->GetItem(SID_ATTR_CHAR_OVERLINE)) != nullptr) { - aFont.SetOverline(static_cast(pItem)->GetValue()); + auto aVal = static_cast(pItem)->GetValue(); + aFont.SetOverline(aVal); + aCJKFont.SetOverline(aVal); + aCTLFont.SetOverline(aVal); } if ((pItem = pItemSet->GetItem(SID_ATTR_CHAR_STRIKEOUT)) != nullptr) { - aFont.SetStrikeout(static_cast(pItem)->GetStrikeout()); + auto aVal = static_cast(pItem)->GetStrikeout(); + aFont.SetStrikeout(aVal); + aCJKFont.SetStrikeout(aVal); + aCTLFont.SetStrikeout(aVal); } if ((pItem = pItemSet->GetItem(SID_ATTR_CHAR_CASEMAP)) != nullptr) { - aFont.SetCaseMap(static_cast(pItem)->GetCaseMap()); + auto aVal = static_cast(pItem)->GetCaseMap(); + aFont.SetCaseMap(aVal); + aCJKFont.SetCaseMap(aVal); + aCTLFont.SetCaseMap(aVal); } if ((pItem = pItemSet->GetItem(SID_ATTR_CHAR_EMPHASISMARK)) != nullptr) { - aFont.SetEmphasisMark(static_cast(pItem)->GetEmphasisMark()); + auto aVal = static_cast(pItem)->GetEmphasisMark(); + aFont.SetEmphasisMark(aVal); + aCJKFont.SetEmphasisMark(aVal); + aCTLFont.SetEmphasisMark(aVal); } if ((pItem = pItemSet->GetItem(SID_ATTR_CHAR_COLOR)) != nullptr) { @@ -132,59 +206,117 @@ bool CommonStylePreviewRenderer::recalculate() } } - if ((pItem = pItemSet->GetItem(SID_ATTR_CHAR_FONT)) != nullptr) + if (SetFont(*pItemSet, SID_ATTR_CHAR_FONT, aFont) && + SetFontSize(*pItemSet, SID_ATTR_CHAR_FONTHEIGHT, aFont)) + m_oFont = aFont; + + if (SetFont(*pItemSet, SID_ATTR_CHAR_CJK_FONT, aCJKFont) && + SetFontSize(*pItemSet, SID_ATTR_CHAR_CJK_FONTHEIGHT, aCJKFont)) + m_oCJKFont = aCJKFont; + + if (SetFont(*pItemSet, SID_ATTR_CHAR_CTL_FONT, aCTLFont) && + SetFontSize(*pItemSet, SID_ATTR_CHAR_CTL_FONTHEIGHT, aCTLFont)) + m_oCTLFont = aCTLFont; + + CheckScript(); + CalcRenderSize(); + return true; +} + +void CommonStylePreviewRenderer::CalcRenderSize() +{ + const OUString& rText = maStyleName; + + tools::Rectangle aTextRect; + tools::Long nHeight = 0; + + sal_uInt16 nScript; + sal_uInt16 nIdx = 0; + sal_Int32 nStart = 0; + sal_Int32 nEnd; + size_t nCnt = maScriptChanges.size(); + + if (nCnt) { - const SvxFontItem* pFontItem = static_cast(pItem); - if (IsStarSymbol(pFontItem->GetFamilyName())) - return false; - aFont.SetFamilyName(pFontItem->GetFamilyName()); - aFont.SetStyleName(pFontItem->GetStyleName()); + nEnd = maScriptChanges[nIdx].changePos; + nScript = maScriptChanges[nIdx].scriptType; } else { - return false; + nEnd = rText.getLength(); + nScript = css::i18n::ScriptType::LATIN; } - if ((pItem = pItemSet->GetItem(SID_ATTR_CHAR_FONTHEIGHT)) != nullptr) + do { - const SvxFontHeightItem* pFontHeightItem = static_cast(pItem); - Size aFontSize(0, pFontHeightItem->GetHeight()); - maPixelSize = mrOutputDev.LogicToPixel(aFontSize, MapMode(mrShell.GetMapUnit())); - aFont.SetFontSize(maPixelSize); + auto oFont = (nScript == css::i18n::ScriptType::ASIAN) ? + m_oCJKFont : + ((nScript == css::i18n::ScriptType::COMPLEX) ? + m_oCTLFont : + m_oFont); - vcl::Font aOldFont(mrOutputDev.GetFont()); + mrOutputDev.Push(vcl::PushFlags::FONT); - mrOutputDev.SetFont(aFont); - tools::Rectangle aTextRect; - mrOutputDev.GetTextBoundRect(aTextRect, mpStyle->GetName()); - if (aTextRect.Bottom() > mnMaxHeight) + Size aSize; + if (oFont) { - double ratio = double(mnMaxHeight) / aTextRect.Bottom(); - maPixelSize.setWidth( maPixelSize.Width() * ratio ); - maPixelSize.setHeight( maPixelSize.Height() * ratio ); - aFont.SetFontSize(maPixelSize); + mrOutputDev.SetFont(*oFont); + aSize = oFont->GetTextSize(mrOutputDev, rText, nStart, nEnd - nStart); } - mrOutputDev.SetFont(aOldFont); - } - else - { - return false; - } + else + aSize = Size(mrOutputDev.GetTextWidth(rText, nStart, nEnd - nStart), mrOutputDev.GetFont().GetFontHeight()); - m_oFont = aFont; - maPixelSize = getRenderSize(); - return true; -} + tools::Rectangle aRect; + mrOutputDev.GetTextBoundRect(aRect, rText, nStart, nStart, nEnd - nStart); + aTextRect = aTextRect.Union(aRect); -Size CommonStylePreviewRenderer::getRenderSize() const -{ - assert(m_oFont); - Size aPixelSize = m_oFont->GetTextSize(mrOutputDev, maStyleName); + mrOutputDev.Pop(); + + auto nWidth = aSize.Width(); + nHeight = std::max(nHeight, aSize.Height()); + if (nIdx >= maScriptChanges.size()) + break; - if (aPixelSize.Height() > mnMaxHeight) - aPixelSize.setHeight( mnMaxHeight ); + maScriptChanges[nIdx++].textWidth = nWidth; - return aPixelSize; + if (nEnd < rText.getLength() && nIdx < nCnt) + { + nStart = nEnd; + nEnd = maScriptChanges[nIdx].changePos; + nScript = maScriptChanges[nIdx].scriptType; + } + else + break; + } + while(true); + + double fRatio = 1; + if (aTextRect.Bottom() > mnMaxHeight) + fRatio = double(mnMaxHeight) / aTextRect.Bottom(); + + mnHeight = std::min(tools::Long(nHeight * fRatio), mnMaxHeight); + if (fRatio != 1) + { + Size aFontSize; + if (m_oFont) + { + aFontSize = m_oFont->GetFontSize(); + m_oFont->SetFontSize(Size(aFontSize.Height() * fRatio, aFontSize.Width() * fRatio)); + } + if (m_oCJKFont) + { + aFontSize = m_oCJKFont->GetFontSize(); + m_oCJKFont->SetFontSize(Size(aFontSize.Height() * fRatio, aFontSize.Width() * fRatio)); + } + if (m_oCTLFont) + { + aFontSize = m_oCTLFont->GetFontSize(); + m_oCTLFont->SetFontSize(Size(aFontSize.Height() * fRatio, aFontSize.Width() * fRatio)); + } + + for (auto& aChange : maScriptChanges) + aChange.textWidth *= fRatio; + } } bool CommonStylePreviewRenderer::render(const tools::Rectangle& aRectangle, RenderAlign eRenderAlign) @@ -200,34 +332,108 @@ bool CommonStylePreviewRenderer::render(const tools::Rectangle& aRectangle, Rend mrOutputDev.DrawRect(aRectangle); } - if (m_oFont) - mrOutputDev.SetFont(*m_oFont); - if (maFontColor != COL_AUTO) mrOutputDev.SetTextColor(maFontColor); if (maHighlightColor != COL_AUTO) mrOutputDev.SetTextFillColor(maHighlightColor); - Size aPixelSize(m_oFont ? maPixelSize : mrOutputDev.GetFont().GetFontSize()); - Point aFontDrawPosition = aRectangle.TopLeft(); if (eRenderAlign == RenderAlign::CENTER) { - if (aRectangle.GetHeight() > aPixelSize.Height()) - aFontDrawPosition.AdjustY((aRectangle.GetHeight() - aPixelSize.Height()) / 2 ); + if (aRectangle.GetHeight() > mnHeight) + aFontDrawPosition.AdjustY((aRectangle.GetHeight() - mnHeight) / 2 ); } - if (m_oFont) - m_oFont->QuickDrawText( &mrOutputDev, aFontDrawPosition, rText, 0, rText.getLength(), {} ); + sal_uInt16 nScript; + sal_uInt16 nIdx = 0; + sal_Int32 nStart = 0; + sal_Int32 nEnd; + size_t nCnt = maScriptChanges.size(); + if (nCnt) + { + nEnd = maScriptChanges[nIdx].changePos; + nScript = maScriptChanges[nIdx].scriptType; + } else - mrOutputDev.DrawText(aFontDrawPosition, rText); + { + nEnd = rText.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); + + mrOutputDev.Push(vcl::PushFlags::FONT); + + if (oFont) + { + mrOutputDev.SetFont(*oFont); + oFont->QuickDrawText(&mrOutputDev, aFontDrawPosition, rText, nStart, nEnd - nStart, {}); + } + else + mrOutputDev.DrawText(aFontDrawPosition, rText, nStart, nEnd - nStart); + + mrOutputDev.Pop(); + + aFontDrawPosition.AdjustX(maScriptChanges[nIdx++].textWidth); + if (nEnd < rText.getLength() && nIdx < nCnt) + { + nStart = nEnd; + nEnd = maScriptChanges[nIdx].changePos; + nScript = maScriptChanges[nIdx].scriptType; + } + else + break; + } + while(true); mrOutputDev.Pop(); return true; } +void CommonStylePreviewRenderer::CheckScript() +{ + assert(!maStyleName.isEmpty()); // must have a preview text here! + if (maStyleName == maScriptText) + return; // already initialized + + maScriptText = maStyleName; + maScriptChanges.clear(); + + if (!mxBreak.is()) + { + auto xContext = comphelper::getProcessComponentContext(); + mxBreak = css::i18n::BreakIterator::create(xContext); + } + + sal_Int16 nScript = mxBreak->getScriptType(maStyleName, 0); + sal_Int32 nChg = 0; + if (css::i18n::ScriptType::WEAK == nScript) + { + nChg = mxBreak->endOfScript(maStyleName, nChg, nScript); + if (nChg < maStyleName.getLength()) + nScript = mxBreak->getScriptType(maStyleName, nChg); + else + nScript = css::i18n::ScriptType::LATIN; + } + + while (true) + { + nChg = mxBreak->endOfScript(maStyleName, nChg, nScript); + maScriptChanges.emplace_back(nScript, nChg); + if (nChg >= maStyleName.getLength() || nChg < 0) + break; + nScript = mxBreak->getScriptType(maStyleName, nChg); + } +} + } // end svx namespace /* vim:set shiftwidth=4 softtabstop=4 expandtab: */ -- cgit