summaryrefslogtreecommitdiffstats
path: root/src/libnrtype
diff options
context:
space:
mode:
authorTavmjong Bah <tavmjong@free.fr>2019-05-06 09:39:24 +0000
committerTavmjong Bah <tavmjong@free.fr>2019-05-06 09:39:24 +0000
commite9916b0ca221016add9268b6d83dffc4be457fac (patch)
tree108bff7a3c6160bf976096f8a667400733e24982 /src/libnrtype
parentFix GdkMotionEvents under Wayland (diff)
downloadinkscape-e9916b0ca221016add9268b6d83dffc4be457fac.tar.gz
inkscape-e9916b0ca221016add9268b6d83dffc4be457fac.zip
Add basic SVG in OpenType support.
Diffstat (limited to 'src/libnrtype')
-rw-r--r--src/libnrtype/FontInstance.cpp69
-rw-r--r--src/libnrtype/OpenTypeUtil.cpp62
-rw-r--r--src/libnrtype/OpenTypeUtil.h13
-rw-r--r--src/libnrtype/font-instance.h16
4 files changed, 158 insertions, 2 deletions
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];