svx/inc/CommonStylePreviewRenderer.hxx           |   29 +-
 svx/source/styles/CommonStylePreviewRenderer.cxx |  326 ++++++++++++++++++-----
 2 files changed, 293 insertions(+), 62 deletions(-)

New commits:
commit 0483fbd1d05d74c394d9274bdee77b3b2aebb495
Author:     Khaled Hosny <[email protected]>
AuthorDate: Sun Aug 21 01:49:57 2022 +0200
Commit:     Caolán McNamara <[email protected]>
CommitDate: Mon Aug 22 15:44:36 2022 +0200

    tdf#87535: Preview styles using CTL/CJK fonts in the sidebar
    
    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 <[email protected]>

diff --git a/svx/inc/CommonStylePreviewRenderer.hxx 
b/svx/inc/CommonStylePreviewRenderer.hxx
index b613f489f7b9..c4d87abf61a0 100644
--- a/svx/inc/CommonStylePreviewRenderer.hxx
+++ b/svx/inc/CommonStylePreviewRenderer.hxx
@@ -10,6 +10,7 @@
 #pragma once
 
 #include <optional>
+#include <vector>
 
 #include <editeng/svxfont.hxx>
 #include <sfx2/objsh.hxx>
@@ -18,21 +19,45 @@
 #include <tools/color.hxx>
 #include <tools/gen.hxx>
 
+#include <com/sun/star/uno/Reference.h>
+#include <com/sun/star/i18n/BreakIterator.hpp>
+
 class OutputDevice;
 class SfxStyleSheetBase;
 
+using namespace css;
+
 namespace svx
 {
 class CommonStylePreviewRenderer final : public sfx2::StylePreviewRenderer
 {
     std::optional<SvxFont> m_oFont;
+    std::optional<SvxFont> m_oCJKFont;
+    std::optional<SvxFont> m_oCTLFont;
     Color maFontColor;
     Color maHighlightColor;
     Color maBackgroundColor;
-    Size maPixelSize;
+    tools::Long mnHeight;
     OUString maStyleName;
+    OUString maScriptText;
+    css::uno::Reference<css::i18n::XBreakIterator> mxBreak;
+    struct ScriptInfo
+    {
+        tools::Long textWidth;
+        sal_uInt16 scriptType;
+        sal_Int32 changePos;
+        ScriptInfo(sal_uInt16 scrptType, sal_Int32 position)
+            : textWidth(0)
+            , scriptType(scrptType)
+            , changePos(position)
+        {
+        }
+    };
+    std::vector<ScriptInfo> maScriptChanges;
 
-    Size getRenderSize() const;
+    bool SetFontSize(const SfxItemSet& rSet, sal_uInt16 nSlot, SvxFont& rFont);
+    void CalcRenderSize();
+    void CheckScript();
 
 public:
     CommonStylePreviewRenderer(const SfxObjectShell& rShell, OutputDevice& 
rOutputDev,
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 <sfx2/objsh.hxx>
 #include <svl/style.hxx>
 #include <svl/itemset.hxx>
+#include <svl/itempool.hxx>
 #include <vcl/outdev.hxx>
 
 #include <com/sun/star/drawing/FillStyle.hpp>
@@ -37,6 +38,9 @@
 
 #include <editeng/editids.hrc>
 
+#include <comphelper/processfactory.hxx>
+#include <com/sun/star/i18n/ScriptType.hpp>
+
 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<const 
SvxFontItem&>(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<const 
SvxFontHeightItem&>(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<SfxItemSet> 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<const SvxWeightItem*>(pItem)->GetWeight());
-    }
+    if ((pItem = pItemSet->GetItem(SID_ATTR_CHAR_CJK_WEIGHT)) != nullptr)
+        aCJKFont.SetWeight(static_cast<const 
SvxWeightItem*>(pItem)->GetWeight());
+    if ((pItem = pItemSet->GetItem(SID_ATTR_CHAR_CTL_WEIGHT)) != nullptr)
+        aCTLFont.SetWeight(static_cast<const 
SvxWeightItem*>(pItem)->GetWeight());
+
     if ((pItem = pItemSet->GetItem(SID_ATTR_CHAR_POSTURE)) != nullptr)
-    {
         aFont.SetItalic(static_cast<const 
SvxPostureItem*>(pItem)->GetPosture());
-    }
+    if ((pItem = pItemSet->GetItem(SID_ATTR_CHAR_CJK_POSTURE)) != nullptr)
+        aCJKFont.SetItalic(static_cast<const 
SvxPostureItem*>(pItem)->GetPosture());
+    if ((pItem = pItemSet->GetItem(SID_ATTR_CHAR_CTL_POSTURE)) != nullptr)
+        aCTLFont.SetItalic(static_cast<const 
SvxPostureItem*>(pItem)->GetPosture());
+
     if ((pItem = pItemSet->GetItem(SID_ATTR_CHAR_CONTOUR)) != nullptr)
     {
-        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);
     }
     if ((pItem = pItemSet->GetItem(SID_ATTR_CHAR_SHADOWED)) != nullptr)
     {
-        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);
     }
     if ((pItem = pItemSet->GetItem(SID_ATTR_CHAR_RELIEF)) != nullptr)
     {
-        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);
     }
     if ((pItem = pItemSet->GetItem(SID_ATTR_CHAR_UNDERLINE)) != nullptr)
     {
-        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);
     }
     if ((pItem = pItemSet->GetItem(SID_ATTR_CHAR_OVERLINE)) != nullptr)
     {
-        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);
     }
     if ((pItem = pItemSet->GetItem(SID_ATTR_CHAR_STRIKEOUT)) != nullptr)
     {
-        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);
     }
     if ((pItem = pItemSet->GetItem(SID_ATTR_CHAR_CASEMAP)) != nullptr)
     {
-        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);
     }
     if ((pItem = pItemSet->GetItem(SID_ATTR_CHAR_EMPHASISMARK)) != nullptr)
     {
-        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);
     }
     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<const SvxFontItem*>(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<const 
SvxFontHeightItem*>(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: */

Reply via email to