vcl/inc/font/PhysicalFontFace.hxx       |    2 
 vcl/inc/pdf/pdfwriter_impl.hxx          |   20 +
 vcl/source/font/LogicalFontInstance.cxx |   12 
 vcl/source/font/fontmetric.cxx          |    5 
 vcl/source/gdi/pdfwriter_impl.cxx       |  396 +++++++++++++++++++++++++++-----
 vcl/win/gdi/winlayout.cxx               |    4 
 6 files changed, 365 insertions(+), 74 deletions(-)

New commits:
commit bf41b120b7690bb6976daa7aa317b5f3b9f5b19c
Author:     Khaled Hosny <kha...@aliftype.com>
AuthorDate: Sun Sep 18 09:57:23 2022 +0200
Commit:     خالد حسني <kha...@aliftype.com>
CommitDate: Mon Sep 19 13:38:30 2022 +0200

    vcl: tdf#104403 PDF export for layered color fonts
    
    Support color fonts using color layers from OpenType COLR table and
    color palettes from CPAL table.
    
    This uses PDF Type 3 fonts to wrap the layers into individual glyphs
    that can be drawn normally. Type 3 glyphs can use arbitrary PDF code, so
    here we output the layers in the regular font subset and then draw then
    in the Type 3 glyph with colors applied to them.
    
    This supports only version 0 of COLR table (solid colors only), the new
    but more powerful version 1 of the table is not yet supported.
    
    Change-Id: I4bf36f5b608a0ac15411104d8a6af893fb69a5c4
    Reviewed-on: https://gerrit.libreoffice.org/c/core/+/140125
    Tested-by: Jenkins
    Reviewed-by: خالد حسني <kha...@aliftype.com>

diff --git a/vcl/inc/pdf/pdfwriter_impl.hxx b/vcl/inc/pdf/pdfwriter_impl.hxx
index af77b58da544..5e25023fd452 100644
--- a/vcl/inc/pdf/pdfwriter_impl.hxx
+++ b/vcl/inc/pdf/pdfwriter_impl.hxx
@@ -282,12 +282,21 @@ struct TransparencyEmit
 };
 
 // font subsets
+
+struct ColorLayer
+{
+    sal_Int32 m_nFontID;
+    sal_uInt8 m_nSubsetGlyphID;
+    uint32_t m_nColorIndex;
+};
+
 class GlyphEmit
 {
     // performance: actually this should probably a vector;
     std::vector<sal_Ucs>            m_CodeUnits;
     sal_uInt8                       m_nSubsetGlyphID;
     sal_Int32                       m_nGlyphWidth;
+    std::vector<ColorLayer>         m_aColorLayers;
 
 public:
     GlyphEmit() : m_nSubsetGlyphID(0), m_nGlyphWidth(0)
@@ -300,6 +309,9 @@ public:
     void setGlyphWidth( sal_Int32 nWidth ) { m_nGlyphWidth = nWidth; }
     sal_Int32 getGlyphWidth() const { return m_nGlyphWidth; }
 
+    void addColorLayer(ColorLayer aLayer) { m_aColorLayers.push_back(aLayer); }
+    const std::vector<ColorLayer>& getColorLayers() const { return 
m_aColorLayers; }
+
     void addCode( sal_Ucs i_cCode )
     {
         m_CodeUnits.push_back(i_cCode);
@@ -740,6 +752,7 @@ private:
     /*  contains all font subsets in use */
     std::map<const vcl::font::PhysicalFontFace*, FontSubset> m_aSubsets;
     std::map<const vcl::font::PhysicalFontFace*, EmbedFont> m_aSystemFonts;
+    std::map<const vcl::font::PhysicalFontFace*, FontSubset> m_aType3Fonts;
     sal_Int32                           m_nNextFID;
 
     /// Cache some most recent bitmaps we've exported, in case we encounter 
them again..
@@ -823,7 +836,8 @@ i12626
     void appendLiteralStringEncrypt( std::string_view rInString, const 
sal_Int32 nInObjectNumber, OStringBuffer& rOutBuffer );
 
     /* creates fonts and subsets that will be emitted later */
-    void registerGlyph(const GlyphItem* pGlyph, const 
vcl::font::PhysicalFontFace* pFont, const std::vector<sal_Ucs>& rCodeUnits, 
sal_Int32 nGlyphWidth, sal_uInt8& nMappedGlyph, sal_Int32& nMappedFontObject);
+    void registerGlyph(const sal_GlyphId, const vcl::font::PhysicalFontFace*, 
const std::vector<sal_Ucs>&, sal_Int32, sal_uInt8&, sal_Int32&);
+    void registerGlyph(const sal_GlyphId, const vcl::font::PhysicalFontFace*, 
const std::vector<sal_Ucs>&, sal_Int32, sal_uInt8&, sal_Int32&, bool);
 
     /*  emits a text object according to the passed layout */
     /* TODO: remove rText as soon as SalLayout will change so that rText is 
not necessary anymore */
@@ -869,11 +883,13 @@ i12626
     sal_Int32 emitBuildinFont( const pdf::BuildinFontFace*, sal_Int32 nObject 
);
     /* writes a type1 system font object and returns its mapping from font ids 
to object ids (or 0 in case of failure ) */
     std::map< sal_Int32, sal_Int32 > emitSystemFont(const 
vcl::font::PhysicalFontFace*, EmbedFont const &);
+    /* writes a type3 font object and appends it to the font id mapping, or 
returns false in case of failure */
+    bool emitType3Font(const vcl::font::PhysicalFontFace*, const FontSubset&, 
std::map<sal_Int32, sal_Int32>&);
     /* writes a font descriptor and returns its object id (or 0) */
     sal_Int32 emitFontDescriptor(const vcl::font::PhysicalFontFace*, 
FontSubsetInfo const &, sal_Int32 nSubsetID, sal_Int32 nStream);
     /* writes a ToUnicode cmap, returns the corresponding stream object */
     sal_Int32 createToUnicodeCMap( sal_uInt8 const * pEncoding, const sal_Ucs* 
pCodeUnits, const sal_Int32* pCodeUnitsPerGlyph,
-                                   const sal_Int32* pEncToUnicodeIndex, int 
nGlyphs );
+                                   const sal_Int32* pEncToUnicodeIndex, 
uint32_t nGlyphs );
 
     /* get resource dict object number */
     sal_Int32 getResourceDictObj()
diff --git a/vcl/source/gdi/pdfwriter_impl.cxx 
b/vcl/source/gdi/pdfwriter_impl.cxx
index b9d601b4a20a..61729e7e07b4 100644
--- a/vcl/source/gdi/pdfwriter_impl.cxx
+++ b/vcl/source/gdi/pdfwriter_impl.cxx
@@ -2317,8 +2317,9 @@ std::map< sal_Int32, sal_Int32 > 
PDFWriterImpl::emitSystemFont( const vcl::font:
 {
     std::map< sal_Int32, sal_Int32 > aRet;
 
-    sal_Int32 nFontDescriptor = 0;
-    OString aSubType( "/Type1" );
+    if (g_bDebugDisableCompression)
+        emitComment("PDFWriterImpl::emitSystemFont");
+
     FontSubsetInfo aInfo;
     // fill in dummy values
     aInfo.m_nAscent = 1000;
@@ -2327,8 +2328,6 @@ std::map< sal_Int32, sal_Int32 > 
PDFWriterImpl::emitSystemFont( const vcl::font:
     aInfo.m_aFontBBox = tools::Rectangle( Point( -200, -200 ), Size( 1700, 
1700 ) );
     aInfo.m_aPSName = pFont->GetFamilyName();
 
-    aSubType = OString( "/TrueType" );
-
     sal_Int32 pWidths[256] = { 0 };
     const LogicalFontInstance* pFontInstance = rEmbed.m_pFontInstance;
     for( sal_Ucs c = 32; c < 256; c++ )
@@ -2338,13 +2337,13 @@ std::map< sal_Int32, sal_Int32 > 
PDFWriterImpl::emitSystemFont( const vcl::font:
     }
 
     // We are interested only in filling aInfo
-    sal_GlyphId aGlyphIds[256] = { 0 };
-    sal_uInt8 pEncoding[256] = { 0 };
+    sal_GlyphId aGlyphIds[] = { 0 };
+    sal_uInt8 pEncoding[] = { 0 };
     std::vector<sal_uInt8> aBuffer;
-    pFont->CreateFontSubset(aBuffer, aGlyphIds, pEncoding, 256, aInfo);
+    pFont->CreateFontSubset(aBuffer, aGlyphIds, pEncoding, 1, aInfo);
 
     // write font descriptor
-    nFontDescriptor = emitFontDescriptor( pFont, aInfo, 0, 0 );
+    sal_Int32 nFontDescriptor = emitFontDescriptor( pFont, aInfo, 0, 0 );
     if( nFontDescriptor )
     {
         // write font object
@@ -2354,8 +2353,7 @@ std::map< sal_Int32, sal_Int32 > 
PDFWriterImpl::emitSystemFont( const vcl::font:
             OStringBuffer aLine( 1024 );
             aLine.append( nObject );
             aLine.append( " 0 obj\n"
-                          "<</Type/Font/Subtype" );
-            aLine.append( aSubType );
+                          "<</Type/Font/Subtype/TrueType" );
             aLine.append( "/BaseFont/" );
             appendName( aInfo.m_aPSName, aLine );
             aLine.append( "\n" );
@@ -2382,6 +2380,239 @@ std::map< sal_Int32, sal_Int32 > 
PDFWriterImpl::emitSystemFont( const vcl::font:
     return aRet;
 }
 
+namespace
+{
+uint32_t fillSubsetArrays(const FontEmit& rSubset, sal_GlyphId* pGlyphIds, 
sal_Int32* pWidths,
+                          sal_uInt8* pEncoding, sal_Int32* pEncToUnicodeIndex,
+                          sal_Int32* pCodeUnitsPerGlyph, std::vector<sal_Ucs>& 
rCodeUnits,
+                          sal_Int32& nToUnicodeStream)
+{
+    rCodeUnits.reserve(256);
+
+    // if it gets used then it will appear in s_subset.m_aMapping, otherwise 0 
is fine
+    pWidths[0] = 0;
+
+    uint32_t nGlyphs = 1;
+    for (auto const& item : rSubset.m_aMapping)
+    {
+        sal_uInt8 nEnc = item.second.getGlyphId();
+
+        SAL_WARN_IF(pGlyphIds[nEnc] != 0 || pEncoding[nEnc] != 0, 
"vcl.pdfwriter",
+                    "duplicate glyph");
+        SAL_WARN_IF(nEnc > rSubset.m_aMapping.size(), "vcl.pdfwriter", 
"invalid glyph encoding");
+
+        pGlyphIds[nEnc] = item.first;
+        pEncoding[nEnc] = nEnc;
+        pEncToUnicodeIndex[nEnc] = static_cast<sal_Int32>(rCodeUnits.size());
+        pCodeUnitsPerGlyph[nEnc] = item.second.countCodes();
+        pWidths[nEnc] = item.second.getGlyphWidth();
+        for (sal_Int32 n = 0; n < pCodeUnitsPerGlyph[nEnc]; n++)
+            rCodeUnits.push_back(item.second.getCode(n));
+        if (item.second.getCode(0))
+            nToUnicodeStream = 1;
+        if (nGlyphs < 256)
+            nGlyphs++;
+        else
+            OSL_FAIL("too many glyphs for subset");
+    }
+
+    return nGlyphs;
+}
+}
+
+bool PDFWriterImpl::emitType3Font(const vcl::font::PhysicalFontFace* pFace,
+                                  const FontSubset& rType3Font,
+                                  std::map<sal_Int32, sal_Int32>& 
rFontIDToObject)
+{
+    if (g_bDebugDisableCompression)
+        emitComment("PDFWriterImpl::emitType3Font");
+
+    const auto& aPalette = pFace->GetColorPalette(0);
+
+    FontSubsetInfo aSubsetInfo;
+    sal_GlyphId pTempGlyphIds[] = { 0 };
+    sal_uInt8 pTempEncoding[] = { 0 };
+    std::vector<sal_uInt8> aBuffer;
+    pFace->CreateFontSubset(aBuffer, pTempGlyphIds, pTempEncoding, 1, 
aSubsetInfo);
+
+    for (auto& rSubset : rType3Font.m_aSubsets)
+    {
+        sal_GlyphId pGlyphIds[256] = {};
+        sal_Int32 pWidths[256];
+        sal_uInt8 pEncoding[256] = {};
+        sal_Int32 pEncToUnicodeIndex[256] = {};
+        sal_Int32 pCodeUnitsPerGlyph[256] = {};
+        std::vector<sal_Ucs> aCodeUnits;
+        sal_Int32 nToUnicodeStream = 0;
+
+        // fill arrays and prepare encoding index map
+        auto nGlyphs = fillSubsetArrays(rSubset, pGlyphIds, pWidths, 
pEncoding, pEncToUnicodeIndex,
+                                        pCodeUnitsPerGlyph, aCodeUnits, 
nToUnicodeStream);
+
+        // write font descriptor
+        sal_Int32 nFontDescriptor = 0;
+        if (m_aContext.Version > PDFWriter::PDFVersion::PDF_1_4)
+            nFontDescriptor = emitFontDescriptor(pFace, aSubsetInfo, 0, 0);
+
+        if (nToUnicodeStream)
+            nToUnicodeStream = createToUnicodeCMap(pEncoding, 
aCodeUnits.data(), pCodeUnitsPerGlyph,
+                                                   pEncToUnicodeIndex, 
nGlyphs);
+
+        // write font object
+        sal_Int32 nFontObject = createObject();
+        if (!updateObject(nFontObject))
+            return false;
+
+        OStringBuffer aLine(1024);
+        aLine.append(nFontObject);
+        aLine.append(" 0 obj\n"
+                     "<</Type/Font/Subtype/Type3\n");
+
+        aLine.append("/FontBBox[");
+        // note: Top and Bottom are reversed in VCL and PDF rectangles
+        aLine.append(OString::number(aSubsetInfo.m_aFontBBox.Left()));
+        aLine.append(' ');
+        aLine.append(OString::number(aSubsetInfo.m_aFontBBox.Top()));
+        aLine.append(' ');
+        aLine.append(OString::number(aSubsetInfo.m_aFontBBox.Right()));
+        aLine.append(' ');
+        aLine.append(OString::number(aSubsetInfo.m_aFontBBox.Bottom() + 1));
+        aLine.append("]\n");
+
+        auto nScale = 1. / pFace->UnitsPerEm();
+        aLine.append("/FontMatrix[");
+        aLine.append(nScale);
+        aLine.append(" 0 0 ");
+        aLine.append(nScale);
+        aLine.append(" 0 0]\n");
+
+        sal_Int32 pGlyphStreams[256] = {};
+        aLine.append("/CharProcs<<\n");
+        for (auto i = 1u; i < nGlyphs; i++)
+        {
+            auto nStream = createObject();
+            aLine.append("/g" + OString::number(i) + " ");
+            aLine.append(nStream);
+            aLine.append(" 0 R\n");
+            pGlyphStreams[i] = nStream;
+        }
+        aLine.append(">>\n");
+
+        aLine.append("/Encoding<</Differences[1");
+        for (auto i = 1u; i < nGlyphs; i++)
+            aLine.append(" /g" + OString::number(i));
+        aLine.append("]>>\n");
+
+        aLine.append("/FirstChar 0\n"
+                     "/LastChar ");
+        aLine.append(OString::number(nGlyphs));
+        aLine.append("\n");
+
+        aLine.append("/Widths[");
+        for (auto i = 0u; i < nGlyphs; i++)
+        {
+            aLine.append(pWidths[i]);
+            aLine.append(" ");
+        }
+        aLine.append("]\n");
+
+        if (m_aContext.Version > PDFWriter::PDFVersion::PDF_1_4)
+        {
+            aLine.append("/FontDescriptor ");
+            aLine.append(nFontDescriptor);
+            aLine.append(" 0 R\n");
+        }
+
+        auto nFontResources = createObject();
+        aLine.append("/Resources ");
+        aLine.append(nFontResources);
+        aLine.append(" 0 R\n");
+
+        if (nToUnicodeStream)
+        {
+            aLine.append("/ToUnicode ");
+            aLine.append(nToUnicodeStream);
+            aLine.append(" 0 R\n");
+        }
+
+        aLine.append(">>\n"
+                     "endobj\n\n");
+
+        if (!writeBuffer(aLine.getStr(), aLine.getLength()))
+            return false;
+
+        std::set<sal_Int32> aUsedFonts;
+        for (auto i = 1u; i < nGlyphs; i++)
+        {
+            auto nStream = pGlyphStreams[i];
+            if (!updateObject(nStream))
+                return false;
+            OStringBuffer aContents(1024);
+            aContents.append(pWidths[i]);
+            aContents.append(" 0 d0\n");
+
+            const auto& rGlyph = rSubset.m_aMapping.find(pGlyphIds[i])->second;
+            const auto& rLayers = rGlyph.getColorLayers();
+            for (const auto& rLayer : rLayers)
+            {
+                aUsedFonts.insert(rLayer.m_nFontID);
+
+                // 0xFFFF is a special value means foreground color.
+                aContents.append("q ");
+                if (rLayer.m_nColorIndex != 0xFFFF)
+                {
+                    appendNonStrokingColor(aPalette[rLayer.m_nColorIndex], 
aContents);
+                    aContents.append(" ");
+                }
+                aContents.append("BT ");
+                aContents.append("/F" + OString::number(rLayer.m_nFontID) + " 
");
+                aContents.append(OString::number(pFace->UnitsPerEm()) + " Tf 
");
+                aContents.append("<");
+                appendHex(rLayer.m_nSubsetGlyphID, aContents);
+                aContents.append(">Tj ");
+                aContents.append("ET ");
+                aContents.append("Q\n");
+            }
+
+            aLine.setLength(0);
+            aLine.append(nStream);
+            aLine.append(" 0 obj\n<</Length ");
+            aLine.append(aContents.getLength());
+            aLine.append(">>\nstream\n");
+            if (!writeBuffer(aLine.getStr(), aLine.getLength()))
+                return false;
+            if (!writeBuffer(aContents.getStr(), aContents.getLength()))
+                return false;
+            aLine.setLength(0);
+            aLine.append("endstream\n"
+                         "endobj\n\n");
+            if (!writeBuffer(aLine.getStr(), aLine.getLength()))
+                return false;
+        }
+
+        if (!updateObject(nFontResources))
+            return false;
+        aLine.setLength(0);
+        aLine.append(nFontResources);
+        aLine.append(" 0 obj\n<</Font\n<<\n");
+        for (auto nFontID : aUsedFonts)
+        {
+            aLine.append("/F");
+            aLine.append(nFontID);
+            aLine.append(" ");
+            aLine.append(rFontIDToObject[nFontID]);
+            aLine.append(" 0 R");
+        }
+        aLine.append("\n>>\n>>\nendobj\n\n");
+        if (!writeBuffer(aLine.getStr(), aLine.getLength()))
+            return false;
+
+        rFontIDToObject[rSubset.m_nFontID] = nFontObject;
+    }
+
+    return true;
+}
+
 typedef int ThreeInts[3];
 static bool getPfbSegmentLengths( const unsigned char* pFontBytes, int 
nByteLen,
     ThreeInts& rSegmentLengths )
@@ -2432,10 +2663,10 @@ sal_Int32 PDFWriterImpl::createToUnicodeCMap( sal_uInt8 
const * pEncoding,
                                               const sal_Ucs* pCodeUnits,
                                               const sal_Int32* 
pCodeUnitsPerGlyph,
                                               const sal_Int32* 
pEncToUnicodeIndex,
-                                              int nGlyphs )
+                                              uint32_t nGlyphs )
 {
     int nMapped = 0;
-    for (int n = 0; n < nGlyphs; ++n)
+    for (auto n = 0u; n < nGlyphs; ++n)
         if( pCodeUnits[pEncToUnicodeIndex[n]] && pCodeUnitsPerGlyph[n] )
             nMapped++;
 
@@ -2462,7 +2693,7 @@ sal_Int32 PDFWriterImpl::createToUnicodeCMap( sal_uInt8 
const * pEncoding,
                      "endcodespacerange\n"
                      );
     int nCount = 0;
-    for (int n = 0; n < nGlyphs; ++n)
+    for (auto n = 0u; n < nGlyphs; ++n)
     {
         if( pCodeUnits[pEncToUnicodeIndex[n]] && pCodeUnitsPerGlyph[n] )
         {
@@ -2640,46 +2871,22 @@ bool PDFWriterImpl::emitFonts()
     {
         for (auto & s_subset :subset.second.m_aSubsets)
         {
-            sal_GlyphId aGlyphIds[ 256 ] = {};
+            sal_GlyphId pGlyphIds[ 256 ] = {};
             sal_Int32 pWidths[ 256 ];
             sal_uInt8 pEncoding[ 256 ] = {};
             sal_Int32 pEncToUnicodeIndex[ 256 ] = {};
             sal_Int32 pCodeUnitsPerGlyph[ 256 ] = {};
             std::vector<sal_Ucs> aCodeUnits;
-            aCodeUnits.reserve( 256 );
-            // fill arrays and prepare encoding index map
             sal_Int32 nToUnicodeStream = 0;
 
-            pWidths[0] = 0; // if it gets used then it will appear in 
s_subset.m_aMapping, otherwise 0 is fine
-            int nGlyphs = 1;
+            // fill arrays and prepare encoding index map
+            auto nGlyphs = fillSubsetArrays(s_subset, pGlyphIds, pWidths, 
pEncoding, pEncToUnicodeIndex,
+                                            pCodeUnitsPerGlyph, aCodeUnits, 
nToUnicodeStream);
 
-            for (auto const& item : s_subset.m_aMapping)
-            {
-                sal_uInt8 nEnc = item.second.getGlyphId();
-
-                SAL_WARN_IF( aGlyphIds[nEnc] != 0 || pEncoding[nEnc] != 0, 
"vcl.pdfwriter", "duplicate glyph" );
-                SAL_WARN_IF( nEnc > s_subset.m_aMapping.size(), 
"vcl.pdfwriter", "invalid glyph encoding" );
-
-                aGlyphIds[ nEnc ] = item.first;
-                pEncoding[ nEnc ] = nEnc;
-                pEncToUnicodeIndex[ nEnc ] = 
static_cast<sal_Int32>(aCodeUnits.size());
-                pCodeUnitsPerGlyph[ nEnc ] = item.second.countCodes();
-                pWidths[ nEnc ] = item.second.getGlyphWidth();
-                for( sal_Int32 n = 0; n < pCodeUnitsPerGlyph[ nEnc ]; n++ )
-                    aCodeUnits.push_back( item.second.getCode( n ) );
-                if( item.second.getCode(0) )
-                    nToUnicodeStream = 1;
-                if( nGlyphs < 256 )
-                    nGlyphs++;
-                else
-                {
-                    OSL_FAIL( "too many glyphs for subset" );
-                }
-            }
             std::vector<sal_uInt8> aBuffer;
             FontSubsetInfo aSubsetInfo;
             const auto* pFace = subset.first;
-            if (pFace->CreateFontSubset(aBuffer, aGlyphIds, pEncoding, 
nGlyphs, aSubsetInfo))
+            if (pFace->CreateFontSubset(aBuffer, pGlyphIds, pEncoding, 
nGlyphs, aSubsetInfo))
             {
                 // create font stream
                 if (g_bDebugDisableCompression)
@@ -2795,7 +3002,7 @@ bool PDFWriterImpl::emitFonts()
                 aLine.append( static_cast<sal_Int32>(nGlyphs-1) );
                 aLine.append( "\n"
                              "/Widths[" );
-                for( int i = 0; i < nGlyphs; i++ )
+                for (auto i = 0u; i < nGlyphs; i++)
                 {
                     aLine.append( pWidths[ i ] );
                     aLine.append( ((i & 15) == 15) ? "\n" : " " );
@@ -2818,24 +3025,21 @@ bool PDFWriterImpl::emitFonts()
             }
             else
             {
-                const vcl::font::PhysicalFontFace* pFont = subset.first;
                 OStringBuffer aErrorComment( 256 );
                 aErrorComment.append( "CreateFontSubset failed for font \"" );
-                aErrorComment.append( OUStringToOString( 
pFont->GetFamilyName(), RTL_TEXTENCODING_UTF8 ) );
+                aErrorComment.append( OUStringToOString( 
pFace->GetFamilyName(), RTL_TEXTENCODING_UTF8 ) );
                 aErrorComment.append( '\"' );
-                if( pFont->GetItalic() == ITALIC_NORMAL )
+                if( pFace->GetItalic() == ITALIC_NORMAL )
                     aErrorComment.append( " italic" );
-                else if( pFont->GetItalic() == ITALIC_OBLIQUE )
+                else if( pFace->GetItalic() == ITALIC_OBLIQUE )
                     aErrorComment.append( " oblique" );
                 aErrorComment.append( " weight=" );
-                aErrorComment.append( sal_Int32(pFont->GetWeight()) );
+                aErrorComment.append( sal_Int32(pFace->GetWeight()) );
                 emitComment( aErrorComment.getStr() );
             }
         }
     }
 
-    if (g_bDebugDisableCompression)
-        emitComment( "PDFWriterImpl::emitSystemFonts" );
     // emit system fonts
     for (auto const& systemFont : m_aSystemFonts)
     {
@@ -2847,6 +3051,13 @@ bool PDFWriterImpl::emitFonts()
         }
     }
 
+    // emit Type3 fonts
+    for (auto const& it : m_aType3Fonts)
+    {
+        if (!emitType3Font(it.first, it.second, aFontIDToObject))
+            return false;
+    }
+
     OStringBuffer aFontDict( 1024 );
     aFontDict.append( getFontDictObject() );
     aFontDict.append( " 0 obj\n"
@@ -3860,14 +4071,12 @@ void PDFWriterImpl::createDefaultCheckBoxAppearance( 
PDFWidget& rBox, const PDFW
 
     // make sure OpenSymbol is embedded, and includes our checkmark
     const sal_Unicode cMark=0x2713;
-    const GlyphItem aItem(0, 0, pFontInstance->GetGlyphIndex(cMark),
-                          DevicePoint(), GlyphItemFlags::NONE, 0, 0, 0);
-    const std::vector<sal_Ucs> aCodeUnits={ cMark };
-    auto nGlyphWidth = pFontInstance->GetGlyphWidth(aItem.glyphId(), 
aItem.IsVertical(), true);
+    const auto nGlyphId = pFontInstance->GetGlyphIndex(cMark);
+    const auto nGlyphWidth = pFontInstance->GetGlyphWidth(nGlyphId, false, 
true);
 
     sal_uInt8 nMappedGlyph;
     sal_Int32 nMappedFontObject;
-    registerGlyph(&aItem, pDevFont, aCodeUnits, nGlyphWidth, nMappedGlyph, 
nMappedFontObject);
+    registerGlyph(nGlyphId, pDevFont, { cMark }, nGlyphWidth, nMappedGlyph, 
nMappedFontObject, pDevFont->HasColorLayers());
 
     appendNonStrokingColor( replaceColor( rWidget.TextColor, 
rSettings.GetRadioCheckTextColor() ), aDA );
     aDA.append( ' ' );
@@ -5764,14 +5973,13 @@ sal_Int32 PDFWriterImpl::getSystemFont( const 
vcl::Font& i_rFont )
     return nFontID;
 }
 
-void PDFWriterImpl::registerGlyph(const GlyphItem* pGlyph,
+void PDFWriterImpl::registerGlyph(const sal_GlyphId nFontGlyphId,
                                   const vcl::font::PhysicalFontFace* pFont,
                                   const std::vector<sal_Ucs>& rCodeUnits,
                                   sal_Int32 nGlyphWidth,
                                   sal_uInt8& nMappedGlyph,
                                   sal_Int32& nMappedFontObject)
 {
-    const int nFontGlyphId = pGlyph->glyphId();
     FontSubset& rSubset = m_aSubsets[ pFont ];
     // search for font specific glyphID
     auto it = rSubset.m_aMapping.find( nFontGlyphId );
@@ -5809,6 +6017,72 @@ void PDFWriterImpl::registerGlyph(const GlyphItem* 
pGlyph,
     }
 }
 
+void PDFWriterImpl::registerGlyph(const sal_GlyphId nFontGlyphId,
+                                  const vcl::font::PhysicalFontFace* pFace,
+                                  const std::vector<sal_Ucs>& rCodeUnits, 
sal_Int32 nGlyphWidth,
+                                  sal_uInt8& nMappedGlyph, sal_Int32& 
nMappedFontObject,
+                                  bool bColor)
+{
+    if (bColor)
+    {
+        // Font has color layers, check if this glyph has color layers.
+        auto aLayers = pFace->GetGlyphColorLayers(nFontGlyphId);
+        if (!aLayers.empty())
+        {
+            auto& rSubset = m_aType3Fonts[pFace];
+            auto it = rSubset.m_aMapping.find(nFontGlyphId);
+            if (it != rSubset.m_aMapping.end())
+            {
+                nMappedFontObject = it->second.m_nFontID;
+                nMappedGlyph = it->second.m_nSubsetGlyphID;
+            }
+            else
+            {
+                // create new subset if necessary
+                if (rSubset.m_aSubsets.empty()
+                    || (rSubset.m_aSubsets.back().m_aMapping.size() > 254))
+                {
+                    rSubset.m_aSubsets.emplace_back(m_nNextFID++);
+                }
+
+                // copy font id
+                nMappedFontObject = rSubset.m_aSubsets.back().m_nFontID;
+                // create new glyph in subset
+                sal_uInt8 nNewId = sal::static_int_cast<sal_uInt8>(
+                    rSubset.m_aSubsets.back().m_aMapping.size() + 1);
+                nMappedGlyph = nNewId;
+
+                // add new glyph to emitted font subset
+                auto& rNewGlyphEmit = 
rSubset.m_aSubsets.back().m_aMapping[nFontGlyphId];
+                rNewGlyphEmit.setGlyphId(nNewId);
+                rNewGlyphEmit.setGlyphWidth(nGlyphWidth);
+                for (const auto nCode : rCodeUnits)
+                    rNewGlyphEmit.addCode(nCode);
+
+                // add color layers to the glyphs
+                for (const auto& aLayer : aLayers)
+                {
+                    sal_uInt8 nLayerGlyph;
+                    sal_Int32 nLayerFontID;
+                    registerGlyph(aLayer.nGlyphIndex, pFace, rCodeUnits, 
nGlyphWidth, nLayerGlyph,
+                                  nLayerFontID);
+
+                    rNewGlyphEmit.addColorLayer({ nLayerFontID, nLayerGlyph, 
aLayer.nColorIndex });
+                }
+
+                // add new glyph to font mapping
+                Glyph& rNewGlyph = rSubset.m_aMapping[nFontGlyphId];
+                rNewGlyph.m_nFontID = nMappedFontObject;
+                rNewGlyph.m_nSubsetGlyphID = nNewId;
+            }
+            return;
+        }
+    }
+
+    // If we reach here then the glyph has no color layers.
+    registerGlyph(nFontGlyphId, pFace, rCodeUnits, nGlyphWidth, nMappedGlyph, 
nMappedFontObject);
+}
+
 void PDFWriterImpl::drawRelief( SalLayout& rLayout, const OUString& rText, 
bool bTextLines )
 {
     push( PushFlags::ALL );
@@ -6240,13 +6514,15 @@ void PDFWriterImpl::drawLayout( SalLayout& rLayout, 
const OUString& rText, bool
         if (pGlyph->IsInCluster())
             assert(aCodeUnits.empty());
 
+        const auto nGlyphId = pGlyph->glyphId();
+
         // A glyph can't have more than one ToUnicode entry, use ActualText
         // instead.
         if (!aCodeUnits.empty() && !bUseActualText)
         {
             for (const auto& rSubset : m_aSubsets[pFont].m_aSubsets)
             {
-                const auto& it = rSubset.m_aMapping.find(pGlyph->glyphId());
+                const auto& it = rSubset.m_aMapping.find(nGlyphId);
                 if (it != rSubset.m_aMapping.cend() && it->second.codes() != 
aCodeUnits)
                 {
                     bUseActualText = true;
@@ -6257,11 +6533,11 @@ void PDFWriterImpl::drawLayout( SalLayout& rLayout, 
const OUString& rText, bool
 
         assert(!aCodeUnits.empty() || bUseActualText || pGlyph->IsInCluster());
 
-        auto nGlyphWidth = pGlyphFont->GetGlyphWidth(pGlyph->glyphId(), 
pGlyph->IsVertical(), true);
+        auto nGlyphWidth = pGlyphFont->GetGlyphWidth(nGlyphId, 
pGlyph->IsVertical(), true);
 
         sal_uInt8 nMappedGlyph;
         sal_Int32 nMappedFontObject;
-        registerGlyph(pGlyph, pFont, aCodeUnits, nGlyphWidth, nMappedGlyph, 
nMappedFontObject);
+        registerGlyph(nGlyphId, pFont, aCodeUnits, nGlyphWidth, nMappedGlyph, 
nMappedFontObject, pDevFont->HasColorLayers());
 
         int nCharPos = -1;
         if (bUseActualText || pGlyph->IsInCluster())
commit 8453d7ea1ff5b2b93196ed4b4bf430d6c574a7cb
Author:     Khaled Hosny <kha...@aliftype.com>
AuthorDate: Fri Sep 16 14:46:27 2022 +0200
Commit:     خالد حسني <kha...@aliftype.com>
CommitDate: Mon Sep 19 13:38:17 2022 +0200

    vcl: Add PhysicalFontFace::UnitsPerEm()
    
    Change-Id: I9239085aabe450499193e74ac91d6a16437545c4
    Reviewed-on: https://gerrit.libreoffice.org/c/core/+/140124
    Tested-by: Jenkins
    Reviewed-by: خالد حسني <kha...@aliftype.com>

diff --git a/vcl/inc/font/PhysicalFontFace.hxx 
b/vcl/inc/font/PhysicalFontFace.hxx
index 75133503e8af..303976c59776 100644
--- a/vcl/inc/font/PhysicalFontFace.hxx
+++ b/vcl/inc/font/PhysicalFontFace.hxx
@@ -138,6 +138,8 @@ public:
     const ColorPalette& GetColorPalette(size_t) const;
     std::vector<ColorLayer> GetGlyphColorLayers(sal_GlyphId) const;
 
+    uint32_t UnitsPerEm() const { return hb_face_get_upem(GetHbFace()); }
+
     virtual hb_face_t* GetHbFace() const;
     virtual hb_blob_t* GetHbTable(hb_tag_t) const
     {
diff --git a/vcl/source/font/LogicalFontInstance.cxx 
b/vcl/source/font/LogicalFontInstance.cxx
index ce6ea99ee8f3..b882ce93dfe4 100644
--- a/vcl/source/font/LogicalFontInstance.cxx
+++ b/vcl/source/font/LogicalFontInstance.cxx
@@ -54,10 +54,11 @@ LogicalFontInstance::~LogicalFontInstance()
 
 hb_font_t* LogicalFontInstance::InitHbFont()
 {
-    hb_face_t* pHbFace = GetFontFace()->GetHbFace();
+    auto pFace = GetFontFace();
+    hb_face_t* pHbFace = pFace->GetHbFace();
     assert(pHbFace);
     hb_font_t* pHbFont = hb_font_create(pHbFace);
-    unsigned int nUPEM = hb_face_get_upem(pHbFace);
+    auto nUPEM = pFace->UnitsPerEm();
     hb_font_set_scale(pHbFont, nUPEM, nUPEM);
     hb_ot_font_set_funcs(pHbFont);
     ImplInitHbFont(pHbFont);
@@ -74,9 +75,7 @@ int LogicalFontInstance::GetKashidaWidth() const
 
 void LogicalFontInstance::GetScale(double* nXScale, double* nYScale) const
 {
-    hb_face_t* pHbFace = 
hb_font_get_face(const_cast<LogicalFontInstance*>(this)->GetHbFont());
-    unsigned int nUPEM = hb_face_get_upem(pHbFace);
-
+    double nUPEM = GetFontFace()->UnitsPerEm();
     double nHeight(m_aFontSelData.mnHeight);
 
     // On Windows, mnWidth is relative to average char width not font height,
@@ -162,8 +161,7 @@ double LogicalFontInstance::GetGlyphWidth(sal_GlyphId 
nGlyph, bool bVertical, bo
 
     if (bPDF)
     {
-        unsigned int nUPEM = hb_face_get_upem(hb_font_get_face(pHbFont));
-        return (nWidth * 1000) / nUPEM;
+        return (nWidth * 1000) / GetFontFace()->UnitsPerEm();
     }
     else
     {
diff --git a/vcl/source/font/fontmetric.cxx b/vcl/source/font/fontmetric.cxx
index 2d918d4dca68..f03edd201982 100644
--- a/vcl/source/font/fontmetric.cxx
+++ b/vcl/source/font/fontmetric.cxx
@@ -380,7 +380,7 @@ void 
ImplFontMetricData::ImplCalcLineSpacing(LogicalFontInstance *pFontInstance)
     vcl::TTGlobalFontInfo rInfo = {};
     GetTTFontMetrics(aHhea.data(), aHhea.size(), aOS_2.data(), aOS_2.size(), 
&rInfo);
 
-    double nUPEM = hb_face_get_upem(pFace->GetHbFace());
+    double nUPEM = pFace->UnitsPerEm();
     double fScale = mnHeight / nUPEM;
     double fAscent = 0, fDescent = 0, fExtLeading = 0;
 
@@ -438,8 +438,7 @@ void 
ImplFontMetricData::ImplCalcLineSpacing(LogicalFontInstance *pFontInstance)
 void ImplFontMetricData::ImplInitBaselines(LogicalFontInstance *pFontInstance)
 {
     hb_font_t* pHbFont = pFontInstance->GetHbFont();
-    hb_face_t* pHbFace = hb_font_get_face(pHbFont);
-    double nUPEM = hb_face_get_upem(pHbFace);
+    double nUPEM = pFontInstance->GetFontFace()->UnitsPerEm();
     double fScale = mnHeight / nUPEM;
     hb_position_t nBaseline = 0;
 
diff --git a/vcl/win/gdi/winlayout.cxx b/vcl/win/gdi/winlayout.cxx
index 748e5502ee73..8d6126c2fcfd 100644
--- a/vcl/win/gdi/winlayout.cxx
+++ b/vcl/win/gdi/winlayout.cxx
@@ -165,13 +165,13 @@ float WinFontInstance::getHScale() const
     return nWidth / nHeight;
 }
 
-void WinFontInstance::ImplInitHbFont(hb_font_t* pHbFont)
+void WinFontInstance::ImplInitHbFont(hb_font_t* /*pHbFont*/)
 {
     assert(m_pGraphics);
     // Calculate the AverageWidthFactor, see LogicalFontInstance::GetScale().
     if (GetFontSelectPattern().mnWidth)
     {
-        double nUPEM = hb_face_get_upem(hb_font_get_face(pHbFont));
+        double nUPEM = GetFontFace()->UnitsPerEm();
 
         LOGFONTW aLogFont;
         GetObjectW(m_hFont, sizeof(LOGFONTW), &aLogFont);

Reply via email to