vcl: embed fonts with CFF table as OpenType fonts
Instead of converting the CFF table to Type1 font, embed the full font as OpenType (SFNT with CFF table). Also for such fonts, add an /Encoding dictionary with /Differences array to the PDF font dictionary, mapping character codes to CFF glyph names, which seems to be needed by some PDF viewers. Change-Id: I3dc51dfbcb9cc996c9253d858d166ac9612aede7 Reviewed-on: https://gerrit.libreoffice.org/c/core/+/203441 Tested-by: Jenkins Reviewed-by: Khaled Hosny <khaled@libreoffice.org>
This commit is contained in:
committed by
Khaled Hosny
parent
6c88e551e3
commit
d9a6a3f7f0
@@ -25,6 +25,8 @@
|
||||
|
||||
#include <vcl/dllapi.h>
|
||||
|
||||
#include <vector>
|
||||
|
||||
// Translate units from TT to PS (standard 1/1000)
|
||||
inline int XUnits(int nUPEM, int n) { return (n * 1000) / nUPEM; }
|
||||
|
||||
@@ -50,6 +52,7 @@ public: // TODO: make subsetter results private and provide accessor methods ins
|
||||
int m_nCapHeight = 0;
|
||||
tools::Rectangle m_aFontBBox;
|
||||
FontType m_nFontType = FontType::NO_FONT; ///< font-type of subset result
|
||||
std::vector<OString> m_aGlyphNames; ///< glyph names for CFF encoding
|
||||
};
|
||||
|
||||
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
|
||||
|
||||
@@ -548,13 +548,28 @@ bool PhysicalFontFace::CreateFontSubset(std::vector<sal_uInt8>& rOutBuffer,
|
||||
hb_blob_t* pCFFBlob = hb_face_reference_table(pSubsetFace, HB_TAG('C', 'F', 'F', ' '));
|
||||
comphelper::ScopeGuard aCFFBlobGuard([&]() { hb_blob_destroy(pCFFBlob); });
|
||||
if (pCFFBlob == hb_blob_get_empty())
|
||||
{
|
||||
// This is not a font with CFF table, so we will create a TTF font subset.
|
||||
rInfo.m_nFontType = FontType::SFNT_TTF;
|
||||
else
|
||||
{
|
||||
rInfo.m_nFontType = FontType::SFNT_CFF;
|
||||
|
||||
// HarfBuzz creates a Unicode cmap, but we need a fake cmap based on pEncoding,
|
||||
// so we use face builder construct a new face based in the subset table,
|
||||
// and create a new cmap table and add it to the new face.
|
||||
// Get glyph names from the subset CFF charset, needed for building
|
||||
// the PDF Encoding dictionary that maps character codes to glyph names.
|
||||
rInfo.m_aGlyphNames.resize(nGlyphCount);
|
||||
for (int i = 0; i < nGlyphCount; ++i)
|
||||
{
|
||||
char aName[64];
|
||||
if (hb_font_get_glyph_name(pSubsetFont, pEncoding[i], aName, sizeof(aName)))
|
||||
rInfo.m_aGlyphNames[i] = OString(aName);
|
||||
else
|
||||
rInfo.m_aGlyphNames[i] = ".notdef"_ostr;
|
||||
}
|
||||
}
|
||||
|
||||
// HarfBuzz creates a Unicode cmap, but we need a fake cmap based on pEncoding,
|
||||
// so we use face builder to construct a new face based on the subset tables,
|
||||
// and create a new cmap table and add it to the new face.
|
||||
{
|
||||
hb_face_t* pBuilderFace = hb_face_builder_create();
|
||||
comphelper::ScopeGuard aBuilderFaceGuard([&]() { hb_face_destroy(pBuilderFace); });
|
||||
unsigned int nSubsetTableCount = hb_face_get_table_tags(pSubsetFace, 0, nullptr, nullptr);
|
||||
@@ -611,26 +626,6 @@ bool PhysicalFontFace::CreateFontSubset(std::vector<sal_uInt8>& rOutBuffer,
|
||||
rOutBuffer.assign(reinterpret_cast<const sal_uInt8*>(pSubsetData),
|
||||
reinterpret_cast<const sal_uInt8*>(pSubsetData) + nSubsetLength);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Ideally we should be outputting a CFF (Type1C) font here, but I couldn’t get it to work.
|
||||
// So we oconvert it to Type1 font instead.
|
||||
// TODO: simplify CreateCFFfontSubset() to only do the conversion, since we already
|
||||
// have the subsetted font.
|
||||
rInfo.m_nFontType = FontType::TYPE1_PFB;
|
||||
|
||||
unsigned int nCffLen;
|
||||
const char* pCffData = hb_blob_get_data(pCFFBlob, &nCffLen);
|
||||
if (!pCffData || !nCffLen)
|
||||
return false;
|
||||
|
||||
if (!ConvertCFFfontToType1(reinterpret_cast<const unsigned char*>(pCffData), nCffLen,
|
||||
rOutBuffer, rInfo))
|
||||
{
|
||||
SAL_WARN("vcl.fonts.cff", "Failed to convert CFF data to Type 1 font");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -2040,6 +2040,7 @@ sal_Int32 PDFWriterImpl::emitFontDescriptor( const vcl::font::PhysicalFontFace*
|
||||
aLine.append( '2' );
|
||||
break;
|
||||
case FontType::CFF_FONT:
|
||||
case FontType::SFNT_CFF:
|
||||
aLine.append( "3" );
|
||||
break;
|
||||
case FontType::TYPE1_PFB:
|
||||
@@ -2131,9 +2132,23 @@ bool PDFWriterImpl::emitFonts()
|
||||
if (!writeBufferBytes(aBuffer.data(), aBuffer.size()))
|
||||
return false;
|
||||
}
|
||||
else if( aSubsetInfo.m_nFontType == FontType::SFNT_CFF )
|
||||
{
|
||||
aLine.append("/Length1 "
|
||||
+ OString::number(aBuffer.size())
|
||||
+ "/Subtype/OpenType>>\n"
|
||||
"stream\n" );
|
||||
if ( !writeBuffer( aLine ) ) return false;
|
||||
if ( osl::File::E_None != m_aFile.getPos(nStartPos) ) return false;
|
||||
|
||||
// copy font file
|
||||
beginCompression();
|
||||
checkAndEnableStreamEncryption( nFontStream );
|
||||
if (!writeBufferBytes(aBuffer.data(), aBuffer.size()))
|
||||
return false;
|
||||
}
|
||||
else if( aSubsetInfo.m_nFontType & FontType::CFF_FONT)
|
||||
{
|
||||
// CFF subset is embedded as an SFNT font (same as TrueType)
|
||||
aLine.append("/Subtype/Type1C>>\nstream\n");
|
||||
if ( !writeBuffer( aLine ) ) return false;
|
||||
if ( osl::File::E_None != m_aFile.getPos(nStartPos) ) return false;
|
||||
@@ -2204,7 +2219,7 @@ bool PDFWriterImpl::emitFonts()
|
||||
if ( !updateObject( nFontObject ) ) return false;
|
||||
aLine.setLength( 0 );
|
||||
aLine.append( OString::number(nFontObject) + " 0 obj\n" );
|
||||
aLine.append( (aSubsetInfo.m_nFontType & (FontType::TYPE1_PFB | FontType::CFF_FONT)) ?
|
||||
aLine.append( (aSubsetInfo.m_nFontType & (FontType::SFNT_CFF | FontType::TYPE1_PFB | FontType::CFF_FONT)) ?
|
||||
"<</Type/Font/Subtype/Type1/BaseFont/" :
|
||||
"<</Type/Font/Subtype/TrueType/BaseFont/" );
|
||||
appendSubsetName( s_subset.m_nFontID, aSubsetInfo.m_aPSName, aLine );
|
||||
@@ -2229,6 +2244,17 @@ bool PDFWriterImpl::emitFonts()
|
||||
+ OString::number( nToUnicodeStream )
|
||||
+ " 0 R\n" );
|
||||
}
|
||||
if( aSubsetInfo.m_nFontType == FontType::SFNT_CFF
|
||||
&& !aSubsetInfo.m_aGlyphNames.empty() )
|
||||
{
|
||||
aLine.append( "/Encoding<</Type/Encoding/Differences[" );
|
||||
for (size_t i = 0; i < aSubsetInfo.m_aGlyphNames.size(); i++)
|
||||
{
|
||||
aLine.append( OString::number(sal_Int32(i))
|
||||
+ "/" + aSubsetInfo.m_aGlyphNames[i] + " " );
|
||||
}
|
||||
aLine.append( "]>>\n" );
|
||||
}
|
||||
aLine.append( ">>\n"
|
||||
"endobj\n\n" );
|
||||
if ( !writeBuffer( aLine ) ) return false;
|
||||
|
||||
Reference in New Issue
Block a user