summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorKhaled Hosny <khaled@aliftype.com>2022-11-21 15:37:40 +0200
committerخالد حسني <khaled@aliftype.com>2022-11-23 21:35:47 +0100
commit164d717530aff8d2581d0a2ff249f83aabb27502 (patch)
treee2b89bb45b3a20b1d165737e9381a6dd42363db6
parentab09eceb3f5eae6a3e6eeb39d99739684e20363b (diff)
tdf#108497: instantiate variable fonts in PDF
Draw the outlines as PDF Type 3 glyphs until we can use HarfBuzz subsetter to instantiate the fonts before embedding. Change-Id: I811eaa8f7e5875db2eeb3755d69616242e263ca2 Reviewed-on: https://gerrit.libreoffice.org/c/core/+/142028 Tested-by: Jenkins Reviewed-by: خالد حسني <khaled@aliftype.com>
-rw-r--r--vcl/inc/font/LogicalFontInstance.hxx9
-rw-r--r--vcl/inc/pdf/pdfwriter_impl.hxx6
-rw-r--r--vcl/source/font/LogicalFontInstance.cxx88
-rw-r--r--vcl/source/gdi/pdfwriter_impl.cxx28
4 files changed, 126 insertions, 5 deletions
diff --git a/vcl/inc/font/LogicalFontInstance.hxx b/vcl/inc/font/LogicalFontInstance.hxx
index 0b40a9c7d4f7..6f4645c82c0c 100644
--- a/vcl/inc/font/LogicalFontInstance.hxx
+++ b/vcl/inc/font/LogicalFontInstance.hxx
@@ -103,6 +103,7 @@ public: // TODO: make data members private
bool GetGlyphBoundRect(sal_GlyphId, tools::Rectangle&, bool) const;
virtual bool GetGlyphOutline(sal_GlyphId, basegfx::B2DPolyPolygon&, bool) const = 0;
+ bool GetGlyphOutlineUntransformed(sal_GlyphId, basegfx::B2DPolyPolygon&) const;
sal_GlyphId GetGlyphIndex(uint32_t, uint32_t = 0) const;
@@ -125,6 +126,8 @@ protected:
virtual void ImplInitHbFont(hb_font_t*) {}
private:
+ hb_font_t* GetHbFontUntransformed() const;
+
struct MapEntry
{
OUString sFontName;
@@ -139,6 +142,7 @@ private:
mutable ImplFontCache* mpFontCache;
const vcl::font::FontSelectPattern m_aFontSelData;
hb_font_t* m_pHbFont;
+ mutable hb_font_t* m_pHbFontUntransformed = nullptr;
double m_nAveWidthFactor;
rtl::Reference<vcl::font::PhysicalFontFace> m_pFontFace;
std::optional<bool> m_xbIsGraphiteFont;
@@ -151,6 +155,11 @@ private:
// The value is initialized and used in NeedOffsetCorrection().
std::optional<FontFamilyEnum> m_xeFontFamilyEnum;
+
+#if HB_VERSION_ATLEAST(4, 0, 0)
+ mutable hb_draw_funcs_t* m_pHbDrawFuncs = nullptr;
+ basegfx::B2DPolygon m_aDrawPolygon;
+#endif
};
inline hb_font_t* LogicalFontInstance::GetHbFont()
diff --git a/vcl/inc/pdf/pdfwriter_impl.hxx b/vcl/inc/pdf/pdfwriter_impl.hxx
index 1459cf16e0d2..b3222f4072fd 100644
--- a/vcl/inc/pdf/pdfwriter_impl.hxx
+++ b/vcl/inc/pdf/pdfwriter_impl.hxx
@@ -299,6 +299,7 @@ class GlyphEmit
std::vector<ColorLayer> m_aColorLayers;
font::RawFontData m_aColorBitmap;
tools::Rectangle m_aRect;
+ basegfx::B2DPolyPolygon m_aOutline;
public:
GlyphEmit() : m_nSubsetGlyphID(0), m_nGlyphWidth(0)
@@ -325,6 +326,9 @@ public:
return m_aColorBitmap;
}
+ void setOutline(basegfx::B2DPolyPolygon aOutline) { m_aOutline = aOutline; }
+ const basegfx::B2DPolyPolygon& getOutline() const { return m_aOutline; }
+
void addCode( sal_Ucs i_cCode )
{
m_CodeUnits.push_back(i_cCode);
@@ -852,7 +856,7 @@ 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 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 LogicalFontInstance* pFont, const std::vector<sal_Ucs>&, sal_Int32, sal_uInt8&, sal_Int32&);
void registerSimpleGlyph(const sal_GlyphId, const vcl::font::PhysicalFontFace*, const std::vector<sal_Ucs>&, sal_Int32, sal_uInt8&, sal_Int32&);
/* emits a text object according to the passed layout */
diff --git a/vcl/source/font/LogicalFontInstance.cxx b/vcl/source/font/LogicalFontInstance.cxx
index 34f05bbe6e33..6923ce6cd87a 100644
--- a/vcl/source/font/LogicalFontInstance.cxx
+++ b/vcl/source/font/LogicalFontInstance.cxx
@@ -50,6 +50,14 @@ LogicalFontInstance::~LogicalFontInstance()
if (m_pHbFont)
hb_font_destroy(m_pHbFont);
+
+ if (m_pHbFontUntransformed)
+ hb_font_destroy(m_pHbFontUntransformed);
+
+#if HB_VERSION_ATLEAST(4, 0, 0)
+ if (m_pHbDrawFuncs)
+ hb_draw_funcs_destroy(m_pHbDrawFuncs);
+#endif
}
hb_font_t* LogicalFontInstance::InitHbFont()
@@ -79,6 +87,26 @@ hb_font_t* LogicalFontInstance::InitHbFont()
return pHbFont;
}
+hb_font_t* LogicalFontInstance::GetHbFontUntransformed() const
+{
+ auto* pHbFont = const_cast<LogicalFontInstance*>(this)->GetHbFont();
+
+#if HB_VERSION_ATLEAST(3, 3, 0)
+ if (NeedsArtificialItalic()) // || NeedsArtificialBold()
+ {
+ if (!m_pHbFontUntransformed)
+ {
+ m_pHbFontUntransformed = hb_font_create_sub_font(pHbFont);
+ // Unset slant set on parent font.
+ // Does not actually work: https://github.com/harfbuzz/harfbuzz/issues/3890
+ hb_font_set_synthetic_slant(m_pHbFontUntransformed, 0);
+ }
+ return m_pHbFontUntransformed;
+ }
+#endif
+ return pHbFont;
+}
+
int LogicalFontInstance::GetKashidaWidth() const
{
sal_GlyphId nGlyph = GetGlyphIndex(0x0640);
@@ -231,4 +259,64 @@ bool LogicalFontInstance::NeedsArtificialItalic() const
return m_aFontSelData.GetItalic() != ITALIC_NONE && m_pFontFace->GetItalic() == ITALIC_NONE;
}
+namespace
+{
+void move_to_func(hb_draw_funcs_t*, void* /*pDrawData*/, hb_draw_state_t*, float to_x, float to_y,
+ void* pUserData)
+{
+ auto pPoly = static_cast<basegfx::B2DPolygon*>(pUserData);
+ pPoly->append(basegfx::B2DPoint(to_x, -to_y));
+}
+
+void line_to_func(hb_draw_funcs_t*, void* /*pDrawData*/, hb_draw_state_t*, float to_x, float to_y,
+ void* pUserData)
+{
+ auto pPoly = static_cast<basegfx::B2DPolygon*>(pUserData);
+ pPoly->append(basegfx::B2DPoint(to_x, -to_y));
+}
+
+void cubic_to_func(hb_draw_funcs_t*, void* /*pDrawData*/, hb_draw_state_t*, float control1_x,
+ float control1_y, float control2_x, float control2_y, float to_x, float to_y,
+ void* pUserData)
+{
+ auto pPoly = static_cast<basegfx::B2DPolygon*>(pUserData);
+ pPoly->appendBezierSegment(basegfx::B2DPoint(control1_x, -control1_y),
+ basegfx::B2DPoint(control2_x, -control2_y),
+ basegfx::B2DPoint(to_x, -to_y));
+}
+
+void close_path_func(hb_draw_funcs_t*, void* pDrawData, hb_draw_state_t*, void* pUserData)
+{
+ auto pPolyPoly = static_cast<basegfx::B2DPolyPolygon*>(pDrawData);
+ auto pPoly = static_cast<basegfx::B2DPolygon*>(pUserData);
+ pPolyPoly->append(*pPoly);
+ pPoly->clear();
+}
+}
+
+bool LogicalFontInstance::GetGlyphOutlineUntransformed(sal_GlyphId nGlyph,
+ basegfx::B2DPolyPolygon& rPolyPoly) const
+{
+#if HB_VERSION_ATLEAST(4, 0, 0)
+ if (!m_pHbDrawFuncs)
+ {
+ m_pHbDrawFuncs = hb_draw_funcs_create();
+ auto pUserData = const_cast<basegfx::B2DPolygon*>(&m_aDrawPolygon);
+ hb_draw_funcs_set_move_to_func(m_pHbDrawFuncs, move_to_func, pUserData, nullptr);
+ hb_draw_funcs_set_line_to_func(m_pHbDrawFuncs, line_to_func, pUserData, nullptr);
+ hb_draw_funcs_set_cubic_to_func(m_pHbDrawFuncs, cubic_to_func, pUserData, nullptr);
+ // B2DPolyPolygon does not support quadratic curves, HarfBuzz will
+ // convert them to cubic curves for us if we don’t set a callback
+ // function.
+ //hb_draw_funcs_set_quadratic_to_func(m_pHbDrawFuncs, quadratic_to_func, pUserData, nullptr);
+ hb_draw_funcs_set_close_path_func(m_pHbDrawFuncs, close_path_func, pUserData, nullptr);
+ }
+
+ hb_font_get_glyph_shape(GetHbFontUntransformed(), nGlyph, m_pHbDrawFuncs, &rPolyPoly);
+ return true;
+#else
+ return false;
+#endif
+}
+
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/vcl/source/gdi/pdfwriter_impl.cxx b/vcl/source/gdi/pdfwriter_impl.cxx
index 57ea2c785b81..c3a4b092e06e 100644
--- a/vcl/source/gdi/pdfwriter_impl.cxx
+++ b/vcl/source/gdi/pdfwriter_impl.cxx
@@ -2645,6 +2645,18 @@ bool PDFWriterImpl::emitType3Font(const vcl::font::PhysicalFontFace* pFace,
aContents.append(" Do Q\n");
}
+ const auto& rOutline = rGlyph.getOutline();
+ if (rOutline.count())
+ {
+ // XXX I have no idea why this transformation matrix is needed.
+ aContents.append("q 10 0 0 10 0 ");
+ appendDouble(m_aPages.back().getHeight() * -10, aContents, 3);
+ aContents.append(" cm\n");
+ m_aPages.back().appendPolyPolygon(rOutline, aContents);
+ aContents.append("f\n");
+ aContents.append("Q\n");
+ }
+
aLine.setLength(0);
aLine.append(nStream);
aLine.append(" 0 obj\n<</Length ");
@@ -4194,7 +4206,7 @@ void PDFWriterImpl::createDefaultCheckBoxAppearance( PDFWidget& rBox, const PDFW
sal_uInt8 nMappedGlyph;
sal_Int32 nMappedFontObject;
- registerGlyph(nGlyphId, pFace, { cMark }, nGlyphWidth, nMappedGlyph, nMappedFontObject);
+ registerGlyph(nGlyphId, pFace, pFontInstance, { cMark }, nGlyphWidth, nMappedGlyph, nMappedFontObject);
appendNonStrokingColor( replaceColor( rWidget.TextColor, rSettings.GetRadioCheckTextColor() ), aDA );
aDA.append( ' ' );
@@ -6137,16 +6149,18 @@ void PDFWriterImpl::registerSimpleGlyph(const sal_GlyphId nFontGlyphId,
void PDFWriterImpl::registerGlyph(const sal_GlyphId nFontGlyphId,
const vcl::font::PhysicalFontFace* pFace,
+ const LogicalFontInstance* pFont,
const std::vector<sal_Ucs>& rCodeUnits, sal_Int32 nGlyphWidth,
sal_uInt8& nMappedGlyph, sal_Int32& nMappedFontObject)
{
- if (pFace->IsColorFont())
+ auto bVariations = !pFace->GetVariations(*pFont).empty();
+ if (pFace->IsColorFont() || bVariations)
{
// Font has colors, check if this glyph has color layers or bitmap.
tools::Rectangle aRect;
auto aLayers = pFace->GetGlyphColorLayers(nFontGlyphId);
auto aBitmap = pFace->GetGlyphColorBitmap(nFontGlyphId, aRect);
- if (!aLayers.empty() || !aBitmap.empty())
+ if (!aLayers.empty() || !aBitmap.empty() || bVariations)
{
auto& rSubset = m_aType3Fonts[pFace];
auto it = rSubset.m_aMapping.find(nFontGlyphId);
@@ -6194,6 +6208,12 @@ void PDFWriterImpl::registerGlyph(const sal_GlyphId nFontGlyphId,
}
else if (!aBitmap.empty())
rNewGlyphEmit.setColorBitmap(aBitmap, aRect);
+ else if (bVariations)
+ {
+ basegfx::B2DPolyPolygon aOutline;
+ if (pFont->GetGlyphOutlineUntransformed(nFontGlyphId, aOutline))
+ rNewGlyphEmit.setOutline(aOutline);
+ }
// add new glyph to font mapping
Glyph& rNewGlyph = rSubset.m_aMapping[nFontGlyphId];
@@ -6646,7 +6666,7 @@ void PDFWriterImpl::drawLayout( SalLayout& rLayout, const OUString& rText, bool
sal_uInt8 nMappedGlyph;
sal_Int32 nMappedFontObject;
- registerGlyph(nGlyphId, pFace, aCodeUnits, nGlyphWidth, nMappedGlyph, nMappedFontObject);
+ registerGlyph(nGlyphId, pFace, pGlyphFont, aCodeUnits, nGlyphWidth, nMappedGlyph, nMappedFontObject);
int nCharPos = -1;
if (bUseActualText || pGlyph->IsInCluster())