diff options
Diffstat (limited to 'src')
| -rw-r--r-- | src/display/drawing-text.cpp | 29 | ||||
| -rw-r--r-- | src/libnrtype/FontInstance.cpp | 69 | ||||
| -rw-r--r-- | src/libnrtype/OpenTypeUtil.cpp | 62 | ||||
| -rw-r--r-- | src/libnrtype/OpenTypeUtil.h | 13 | ||||
| -rw-r--r-- | src/libnrtype/font-instance.h | 16 |
5 files changed, 186 insertions, 3 deletions
diff --git a/src/display/drawing-text.cpp b/src/display/drawing-text.cpp index 9d81d736d..b92886691 100644 --- a/src/display/drawing-text.cpp +++ b/src/display/drawing-text.cpp @@ -584,7 +584,34 @@ unsigned DrawingText::_renderItem(DrawingContext &dc, Geom::IntRect const &/*are if (g->_ctm.isSingular()) continue; dc.transform(g->_ctm); if (g->_drawable) { - dc.path(*g->_font->PathVector(g->_glyph)); + if (g->_font->FontHasSVG()) { + Inkscape::Pixbuf* pixbuf = g->_font->PixBuf(g->_glyph); + if (pixbuf) { + // Geom::OptRect box = bounds_exact(*g->_font->PathVector(g->_glyph)); + // if (box) { + // Inkscape::DrawingContext::Save save(dc); + // dc.newPath(); + // dc.rectangle(*box); + // dc.setLineWidth(0.01); + // dc.setSource(0x8080ffff); + // dc.stroke(); + // } + { + // pixbuf is in font design units, scale to embox. + double scale = g->_font->GetDesignUnits(); + if (scale <= 0) scale = 1000; + Inkscape::DrawingContext::Save save(dc); + dc.translate(0, 1); + dc.scale(1.0/scale, -1.0/scale); + dc.setSource(pixbuf->getSurfaceRaw(), 0, 0); + dc.paint(1); + } + } else { + dc.path(*g->_font->PathVector(g->_glyph)); + } + } else { + dc.path(*g->_font->PathVector(g->_glyph)); + } } } diff --git a/src/libnrtype/FontInstance.cpp b/src/libnrtype/FontInstance.cpp index 0c1d0f5ed..f0834ab73 100644 --- a/src/libnrtype/FontInstance.cpp +++ b/src/libnrtype/FontInstance.cpp @@ -35,6 +35,7 @@ #include "libnrtype/font-glyph.h" #include "libnrtype/font-instance.h" +#include "display/cairo-utils.h" // Inkscape::Pixbuf #ifndef USE_PANGO_WIN32 /* @@ -111,7 +112,8 @@ font_instance::font_instance() : nbGlyph(0), maxGlyph(0), glyphs(nullptr), - theFace(nullptr) + theFace(nullptr), + fontHasSVG(false) { //printf("font instance born\n"); _ascent = _ascent_max = 0.8; @@ -214,6 +216,11 @@ void font_instance::InitTheFace() readOpenTypeGsubTable( theFace, openTypeTables ); readOpenTypeFvarAxes( theFace, openTypeVarAxes ); + readOpenTypeSVGTable( theFace, openTypeSVGGlyphs ); + + if (openTypeSVGGlyphs.size() > 0 ) { + fontHasSVG = true; + } #if PANGO_VERSION_CHECK(1,41,1) #if FREETYPE_MAJOR == 2 && FREETYPE_MINOR >= 8 // 2.8 does not seem to work even though it has some support. @@ -669,7 +676,63 @@ Geom::PathVector* font_instance::PathVector(int glyph_id) return glyphs[no].pathvector; } -double font_instance::Advance(int glyph_id,bool vertical) +Inkscape::Pixbuf* font_instance::PixBuf(int glyph_id) +{ + Inkscape::Pixbuf* pixbuf = nullptr; + + auto glyph_iter = openTypeSVGGlyphs.find(glyph_id); + if (glyph_iter != openTypeSVGGlyphs.end()) { + + // Glyphs are layed out in the +x, -y quadrant (assuming viewBox origin is 0,0). + // We need to shift the viewBox by the height inorder to generate pixbuf! + // To do: glyphs must draw overflow so we actually need larger pixbuf! + // To do: cache pixbuf. + // To do: Error handling. + + pixbuf = glyph_iter->second.pixbuf; + if (!pixbuf) { + Glib::ustring svg = glyph_iter->second.svg; + + Glib::RefPtr<Glib::Regex> regex = + Glib::Regex::create("viewBox=\"\\s*(\\d*)\\s*,?\\s*(\\d*)\\s*,?\\s*(\\d*)\\s*,?\\s*(\\d*)\\s*\""); + Glib::MatchInfo matchInfo; + regex->match(svg, matchInfo); + if (matchInfo.matches()) { + int x = std::stod(matchInfo.fetch(1)); + int y = std::stod(matchInfo.fetch(2)); + int w = std::stod(matchInfo.fetch(3)); + int h = std::stod(matchInfo.fetch(4)); + // std::cout << " x: " << x + // << " y: " << y + // << " w: " << w + // << " h: " << h << std::endl; + Glib::ustring replacement("viewBox=\""); + replacement += std::to_string(x); + replacement += " "; + replacement += std::to_string(y-h); + replacement += " "; + replacement += std::to_string(w); + replacement += " "; + replacement += std::to_string(h*2); // Baseline is at y=0 so we need larger box to get decent. + replacement += "\""; + // std::cout << "replacement: |" << replacement << "|" << std::endl; + svg = regex->replace_literal(svg, 0, replacement, static_cast<Glib::RegexMatchFlags >(0)); + } else { + std::cerr << "font_instance::PixBuf: Failed to match!" << std::endl; + } + + // Finally create pixbuf! + pixbuf = Inkscape::Pixbuf::create_from_buffer(svg); + + // And cache it. + glyph_iter->second.pixbuf = pixbuf; + } + } + + return pixbuf; +} + +double font_instance::Advance(int glyph_id, bool vertical) { int no = -1; if ( id_to_no.find(glyph_id) == id_to_no.end() ) { @@ -714,6 +777,7 @@ void font_instance::FindFontMetrics() { _xheight = fabs(otm.otmsXHeight * scale); _ascent_max = fabs(otm.otmAscent * scale); _descent_max = fabs(otm.otmDescent * scale); + _design_units = parent->fontSize; // In CSS em size is ascent + descent... which should be 1. If not, // adjust so it is. @@ -777,6 +841,7 @@ void font_instance::FindFontMetrics() { } _ascent_max = fabs(((double)theFace->ascender) / ((double)theFace->units_per_EM)); _descent_max = fabs(((double)theFace->descender) / ((double)theFace->units_per_EM)); + _design_units = theFace->units_per_EM; // In CSS em size is ascent + descent... which should be 1. If not, // adjust so it is. diff --git a/src/libnrtype/OpenTypeUtil.cpp b/src/libnrtype/OpenTypeUtil.cpp index 08cec5468..eb2c22256 100644 --- a/src/libnrtype/OpenTypeUtil.cpp +++ b/src/libnrtype/OpenTypeUtil.cpp @@ -324,6 +324,68 @@ void readOpenTypeFvarNamed(const FT_Face ft_face, #endif /* FREETYPE Version */ } +#define HB_OT_TAG_SVG HB_TAG('S','V','G',' ') + +// Get SVG glyphs out of an OpenType font. +void readOpenTypeSVGTable(const FT_Face ft_face, + std::map<int, SVGTableEntry>& glyphs) { + + // Harfbuzz has some support for SVG fonts but it is not yet exposed to user. + // We do it the hard way! + hb_face_t *hb_face = hb_ft_face_create_cached (ft_face); + hb_blob_t *hb_blob = hb_face_reference_table (hb_face, HB_OT_TAG_SVG); + unsigned int svg_length = hb_blob_get_length (hb_blob); + + if (!hb_blob) { + // No SVG glyphs in font! + return; + } + + const char* data = hb_blob_get_data(hb_blob, &svg_length); + if (!data) { + std::cerr << "readOpenTypeSVGTable: Failed to get data! " + << (ft_face->family_name?ft_face->family_name:"Unknown family") << std::endl; + return; + } + + // OpenType fonts use Big Endian + uint16_t version = ((data[0] & 0xff) << 8) + (data[1] & 0xff); + // std::cout << "Version: " << version << std::endl; + uint32_t offset = ((data[2] & 0xff) << 24) + ((data[3] & 0xff) << 16) + ((data[4] & 0xff) << 8) + (data[5] & 0xff); + + // std::cout << "Offset: " << offset << std::endl; + // Bytes 6-9 are reserved. + + uint16_t entries = (data[offset] << 8) + data[offset+1]; + // std::cout << "Number of entries: " << entries << std::endl; + + for (int entry = 0; entry < entries; ++entry) { + uint32_t base = offset + 2 + entry * 12; + + uint16_t startGlyphID = ((data[base ] & 0xff) << 8) + (data[base+1] & 0xff); + uint16_t endGlyphID = ((data[base+2] & 0xff) << 8) + (data[base+3] & 0xff); + uint32_t offsetGlyph = ((data[base+4] & 0xff) << 24) + ((data[base+5] & 0xff) << 16) +((data[base+6] & 0xff) << 8) + (data[base+7] & 0xff); + uint32_t lengthGlyph = ((data[base+8] & 0xff) << 24) + ((data[base+9] & 0xff) << 16) +((data[base+10] & 0xff) << 8) + (data[base+11] & 0xff); + + // std::cout << "Entry " << entry << ": Start: " << startGlyphID << " End: " << endGlyphID + // << " Offset: " << offsetGlyph << " Length: " << lengthGlyph << std::endl; + + std::string svg; + for (unsigned c = offsetGlyph; c < offsetGlyph + lengthGlyph; ++c) { + svg += (char) data[offset + c]; + } + + for (uint i = startGlyphID; i < endGlyphID+1; ++i) { + glyphs[i].svg = svg; + } + + // for (auto glyph : glyphs) { + // std::cout << "Glyph: " << glyph.first << std::endl; + // std::cout << glyph.second << std::endl; + // } + } +} + #endif /* !USE_PANGO_WIND32 */ /* diff --git a/src/libnrtype/OpenTypeUtil.h b/src/libnrtype/OpenTypeUtil.h index 5c8ca7d80..e52526b4c 100644 --- a/src/libnrtype/OpenTypeUtil.h +++ b/src/libnrtype/OpenTypeUtil.h @@ -72,6 +72,16 @@ inline FT_Fixed FTDoubleToFixed (double value) { return static_cast<FT_Fixed>(value * 65536); } + +namespace Inkscape { class Pixbuf; } + +class SVGTableEntry { +public: + SVGTableEntry() : pixbuf(nullptr) {}; + std::string svg; + Inkscape::Pixbuf* pixbuf; +}; + // This would be better if one had std::vector<OTSubstitution> instead of OTSubstitution where each // entry corresponded to one substitution (e.g. ff -> ff) but Harfbuzz at the moment cannot return // individual substitutions. See Harfbuzz issue #673. @@ -84,6 +94,9 @@ void readOpenTypeFvarAxes (const FT_Face ft_face, void readOpenTypeFvarNamed (const FT_Face ft_face, std::map<Glib::ustring, OTVarInstance>& named); +void readOpenTypeSVGTable (const FT_Face ft_face, + std::map<int, SVGTableEntry>& glyphs); + #endif /* !USE_PANGO_WIND32 */ #endif /* !SEEN_OPENTYPEUTIL_H */ diff --git a/src/libnrtype/font-instance.h b/src/libnrtype/font-instance.h index 1dd77dfb2..0dc9c40d0 100644 --- a/src/libnrtype/font-instance.h +++ b/src/libnrtype/font-instance.h @@ -21,6 +21,7 @@ #include "font-style.h" #include "OpenTypeUtil.h" +namespace Inkscape { class Pixbuf; } class font_factory; struct font_glyph; @@ -54,6 +55,12 @@ public: // Maps for font variations. std::map<Glib::ustring, OTVarAxis> openTypeVarAxes; // Axes with ranges + // Map of SVG in OpenType glyphs + std::map<int, SVGTableEntry> openTypeSVGGlyphs; + + // Does OpenType font contain SVG glyphs? + bool fontHasSVG; + font_instance(); virtual ~font_instance(); @@ -76,6 +83,13 @@ public: // Return 2geom pathvector for glyph. Deallocated when font instance dies. Geom::PathVector* PathVector(int glyph_id); + // Return font has SVG OpenType enties. + bool FontHasSVG() { return fontHasSVG; }; + + // Return pixbuf of SVG glyph or nullptr if no SVG glyph exists. + Inkscape::Pixbuf* PixBuf(int glyph_id); + + // Horizontal advance if 'vertical' is false, vertical advance if true. double Advance(int glyph_id, bool vertical); @@ -85,6 +99,7 @@ public: double GetMaxAscent() { return _ascent_max; } double GetMaxDescent() { return _descent_max; } const double* GetBaselines() { return _baselines; } + int GetDesignUnits() { return _design_units; } bool FontMetrics(double &ascent, double &descent, double &leading); bool FontDecoration(double &underline_position, double &underline_thickness, @@ -116,6 +131,7 @@ private: double _xheight; // x-height of font. double _ascent_max; // Maximum ascent of all glyphs in font. double _descent_max; // Maximum descent of all glyphs in font. + int _design_units; // Design units, (units per em, typically 1000 or 2048). // Baselines double _baselines[SP_CSS_BASELINE_SIZE]; |
