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);