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 <caolanm@redhat.com>
Reviewed-by: Caolán McNamara <caolanm@redhat.com>
diff --git a/svx/source/tbxctrls/tbcontrl.cxx b/svx/source/tbxctrls/tbcontrl.cxx
index 70063a0..610bb63 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() );
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_POSTURE );
const SfxPoolItem *pItem = pItemSet->GetItem( SID_ATTR_CHAR_CONTOUR );
if ( pItem )
aFont.SetItalic( static_cast< const SvxPostureItem* >( pItem )->GetPosture() );
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();