vcl/inc/win/salgdi.h      |    6 ++++--
 vcl/inc/win/winlayout.hxx |    7 ++++---
 vcl/win/gdi/salfont.cxx   |   40 ++++++++++++++--------------------------
 vcl/win/gdi/winlayout.cxx |   44 ++++++++++++++++++++------------------------
 4 files changed, 42 insertions(+), 55 deletions(-)

New commits:
commit b06e7814d2edbc369b7c441941c3392df1266423
Author:     Jonathan Clark <jonat...@libreoffice.org>
AuthorDate: Thu Jan 9 04:52:40 2025 -0700
Commit:     Jonathan Clark <jonat...@libreoffice.org>
CommitDate: Thu Jan 9 14:50:23 2025 +0100

    tdf#152515 vcl: Implement Win32 vert CJK printing for all fonts
    
    Previously, vertical CJK printing was implemented on Windows by
    substituting the font with an @-prefixed alternate. This was a naming
    convention once used by Microsoft to indicate that a font is a
    specially-prepared vertical variant containing pre-rotated CJK glyphs.
    However, modern fonts don't ship these variants and cannot be printed
    correctly if this approach is used.
    
    This change deletes the @-prefix substitution. Instead, a secondary
    HFONT is used for printing upright characters, as determined by the
    shaper. This implementation should work with all fonts, and the printed
    output should now match the screen better.
    
    Change-Id: I3c474980f90ade8475073926364ab59dd9310ca0
    Reviewed-on: https://gerrit.libreoffice.org/c/core/+/180014
    Reviewed-by: Jonathan Clark <jonat...@libreoffice.org>
    Tested-by: Jenkins

diff --git a/vcl/inc/win/salgdi.h b/vcl/inc/win/salgdi.h
index f8107a4a2a00..d210dd22bb68 100644
--- a/vcl/inc/win/salgdi.h
+++ b/vcl/inc/win/salgdi.h
@@ -171,9 +171,11 @@ private:
     void DeInitGraphics();
 
 public:
-    // Return HFONT, and whether the font is for vertical writing ( prefixed 
with '@' )
+    // Returns base HFONT, an optional HFONT for non-rotated CJK glyphs,
     // and tmDescent value for adjusting offset in vertical writing mode.
-    std::tuple<HFONT,bool,sal_Int32> ImplDoSetFont(HDC hDC, 
vcl::font::FontSelectPattern const & i_rFont, const vcl::font::PhysicalFontFace 
* i_pFontFace, HFONT& o_rOldFont);
+    std::tuple<HFONT, HFONT, sal_Int32>
+    ImplDoSetFont(HDC hDC, vcl::font::FontSelectPattern const& i_rFont,
+                  const vcl::font::PhysicalFontFace* i_pFontFace, HFONT& 
o_rOldFont);
 
     HDC getHDC() const { return mhLocalDC; }
     // NOTE: this doesn't transfer ownership! See class comment.
diff --git a/vcl/inc/win/winlayout.hxx b/vcl/inc/win/winlayout.hxx
index 6e536d0a8dd8..f03547035e80 100644
--- a/vcl/inc/win/winlayout.hxx
+++ b/vcl/inc/win/winlayout.hxx
@@ -42,9 +42,10 @@ public:
     WinSalGraphics* GetGraphics() const { return m_pGraphics; }
 
     HFONT GetHFONT() const { return m_hFont; }
+    HFONT GetVerticalHFONT() const { return m_hVerticalFont; }
+
     // Return true if the font is for vertical writing.
-    // I.e. the font name of the LOGFONT is prefixed with '@'.
-    bool  IsCJKVerticalFont() const { return m_bIsCJKVerticalFont; }
+    bool IsCJKVerticalFont() const { return m_hVerticalFont != nullptr; }
     sal_Int32 GetTmDescent() const { return m_nTmDescent; }
 
     const WinFontFace * GetFontFace() const { return static_cast<const 
WinFontFace *>(LogicalFontInstance::GetFontFace()); }
@@ -61,7 +62,7 @@ private:
 
     WinSalGraphics *m_pGraphics;
     HFONT m_hFont;
-    bool  m_bIsCJKVerticalFont;
+    HFONT m_hVerticalFont = nullptr;
     sal_Int32 m_nTmDescent;
     mutable sal::systools::COMReference<IDWriteFontFace> mxDWFontFace;
 };
diff --git a/vcl/win/gdi/salfont.cxx b/vcl/win/gdi/salfont.cxx
index 28a6dcf4dba5..06a90a361f18 100644
--- a/vcl/win/gdi/salfont.cxx
+++ b/vcl/win/gdi/salfont.cxx
@@ -719,36 +719,15 @@ void ImplGetLogFontFromFontSelect( const 
vcl::font::FontSelectPattern& rFont,
         rLogFont.lfQuality = bAntiAliased ? ANTIALIASED_QUALITY : 
NONANTIALIASED_QUALITY;
 }
 
-std::tuple<HFONT,bool,sal_Int32> WinSalGraphics::ImplDoSetFont(HDC hDC, 
vcl::font::FontSelectPattern const & i_rFont,
-                                    const vcl::font::PhysicalFontFace * 
i_pFontFace,
-                                    HFONT& o_rOldFont)
+std::tuple<HFONT, HFONT, sal_Int32>
+WinSalGraphics::ImplDoSetFont(HDC hDC, vcl::font::FontSelectPattern const& 
i_rFont,
+                              const vcl::font::PhysicalFontFace* i_pFontFace, 
HFONT& o_rOldFont)
 {
     HFONT hNewFont = nullptr;
 
     LOGFONTW aLogFont;
     ImplGetLogFontFromFontSelect( i_rFont, i_pFontFace, aLogFont, 
getAntiAlias());
 
-    bool    bIsCJKVerticalFont = false;
-    // select vertical mode for printing if requested and available
-    if ( i_rFont.mbVertical && mbPrinter )
-    {
-        constexpr size_t nLen = sizeof(aLogFont.lfFaceName) - 
sizeof(aLogFont.lfFaceName[0]);
-        // vertical fonts start with an '@'
-        memmove( &aLogFont.lfFaceName[1], &aLogFont.lfFaceName[0], nLen );
-        aLogFont.lfFaceName[0] = '@';
-        aLogFont.lfFaceName[LF_FACESIZE - 1] = 0;
-
-        // check availability of vertical mode for this font
-        EnumFontFamiliesExW( getHDC(), &aLogFont, SalEnumQueryFontProcExW,
-                reinterpret_cast<LPARAM>(&bIsCJKVerticalFont), 0 );
-        if( !bIsCJKVerticalFont )
-        {
-            // restore non-vertical name if not vertical mode isn't available
-            memcpy( &aLogFont.lfFaceName[0], &aLogFont.lfFaceName[1], nLen );
-            aLogFont.lfFaceName[LF_FACESIZE - 1] = 0;
-        }
-    }
-
     hNewFont = ::CreateFontIndirectW( &aLogFont );
     o_rOldFont = ::SelectFont(hDC, hNewFont);
 
@@ -763,10 +742,19 @@ std::tuple<HFONT,bool,sal_Int32> 
WinSalGraphics::ImplDoSetFont(HDC hDC, vcl::fon
         SelectFont(hDC, hNewFont2);
         DeleteFont( hNewFont );
         hNewFont = hNewFont2;
-        bIsCJKVerticalFont = false;
     }
 
-    return std::make_tuple(hNewFont, bIsCJKVerticalFont, 
static_cast<sal_Int32>(aTextMetricW.tmDescent));
+    // Optionally create a secondary font for non-rotated CJK glyphs in 
vertical context
+    HFONT hNewVerticalFont = nullptr;
+    if (i_rFont.mbVertical && mbPrinter)
+    {
+        aLogFont.lfEscapement = 0;
+        aLogFont.lfOrientation = 0;
+        hNewVerticalFont = ::CreateFontIndirectW(&aLogFont);
+    }
+
+    return std::make_tuple(hNewFont, hNewVerticalFont,
+                           static_cast<sal_Int32>(aTextMetricW.tmDescent));
 }
 
 void WinSalGraphics::SetFont(LogicalFontInstance* pFont, int nFallbackLevel)
diff --git a/vcl/win/gdi/winlayout.cxx b/vcl/win/gdi/winlayout.cxx
index 83c3a653b1ae..6f5538140a2e 100644
--- a/vcl/win/gdi/winlayout.cxx
+++ b/vcl/win/gdi/winlayout.cxx
@@ -88,36 +88,27 @@ bool ExTextOutRenderer::operator()(GenericSalLayout const& 
rLayout, SalGraphics&
     basegfx::B2DPoint aPos;
     const GlyphItem* pGlyph;
     const WinFontInstance* pWinFont = static_cast<const 
WinFontInstance*>(&rLayout.GetFont());
-    UINT nTextAlign = GetTextAlign(hDC);
-    UINT nCurTextAlign = nTextAlign;
-    sal_Int32 nGlyphOffset = -pWinFont->GetTmDescent();
 
-    while (rLayout.GetNextGlyph(&pGlyph, aPos, nStart))
-    {
-        wchar_t glyphWStr = pGlyph->glyphId();
-        UINT32 nNewTextAlign = nCurTextAlign;
-        sal_Int32 nYOffset = 0;
-
-        if (pWinFont->IsCJKVerticalFont() && pGlyph->IsVertical())
+    bool bVertFontSelected = false;
+    auto fnUseVertFont = [&](bool bValue) {
+        if (bVertFontSelected != bValue)
         {
-            nNewTextAlign = VTA_CENTER | TA_BOTTOM;
-            nYOffset = nGlyphOffset;
+            bVertFontSelected = bValue;
+            SelectFont(hDC,
+                       bVertFontSelected ? pWinFont->GetVerticalHFONT() : 
pWinFont->GetHFONT());
         }
-        else
-            nNewTextAlign = nTextAlign;
+    };
 
-        if (nCurTextAlign != nNewTextAlign)
-            SetTextAlign(hDC, nNewTextAlign);
+    while (rLayout.GetNextGlyph(&pGlyph, aPos, nStart))
+    {
+        wchar_t glyphWStr = pGlyph->glyphId();
 
-        ExtTextOutW(hDC, aPos.getX(), aPos.getY() + nYOffset, ETO_GLYPH_INDEX, 
nullptr, &glyphWStr,
-                    1, nullptr);
+        fnUseVertFont(pWinFont->IsCJKVerticalFont() && pGlyph->IsVertical());
 
-        nCurTextAlign = nNewTextAlign;
+        ExtTextOutW(hDC, aPos.getX(), aPos.getY(), ETO_GLYPH_INDEX, nullptr, 
&glyphWStr, 1,
+                    nullptr);
     }
 
-    if (nCurTextAlign != nTextAlign)
-        SetTextAlign(hDC, nTextAlign);
-
     return true;
 }
 
@@ -137,7 +128,7 @@ WinFontInstance::WinFontInstance(const WinFontFace& rPFF, 
const vcl::font::FontS
     : LogicalFontInstance(rPFF, rFSP)
     , m_pGraphics(nullptr)
     , m_hFont(nullptr)
-    , m_bIsCJKVerticalFont(false)
+    , m_hVerticalFont(nullptr)
     , m_nTmDescent(0)
 {
 }
@@ -146,6 +137,11 @@ WinFontInstance::~WinFontInstance()
 {
     if (m_hFont)
         ::DeleteFont(m_hFont);
+
+    if (m_hVerticalFont)
+    {
+        ::DeleteFont(m_hVerticalFont);
+    }
 }
 
 float WinFontInstance::getHScale() const
@@ -191,7 +187,7 @@ void WinFontInstance::SetGraphics(WinSalGraphics* pGraphics)
         return;
     HFONT hOrigFont;
     HDC hDC = m_pGraphics->getHDC();
-    std::tie(m_hFont, m_bIsCJKVerticalFont, m_nTmDescent)
+    std::tie(m_hFont, m_hVerticalFont, m_nTmDescent)
         = m_pGraphics->ImplDoSetFont(hDC, GetFontSelectPattern(), 
GetFontFace(), hOrigFont);
     SelectObject(hDC, hOrigFont);
 }

Reply via email to