From 8fd55422c3fa2279991d93875d912fca4ee89cf5 Mon Sep 17 00:00:00 2001 From: Ebrahim Byagowi Date: Tue, 27 Mar 2018 16:57:09 +0430 Subject: [PATCH] Implement an internal emojis dumper tool (#909) Later to be expanded to a more general tool but for now it only supports CBDT, SVG and CBDT. --- CMakeLists.txt | 2 +- src/Makefile.am | 4 + src/dump-emoji.cc | 141 ++++++++++++++++++++++++++++++++++ src/hb-ot-color-cbdt-table.hh | 71 +++++++++++++++++ src/hb-ot-color-sbix-table.hh | 79 ++++++++++++------- src/hb-ot-color-svg-table.hh | 56 ++++++++++---- 6 files changed, 307 insertions(+), 46 deletions(-) create mode 100644 src/dump-emoji.cc diff --git a/CMakeLists.txt b/CMakeLists.txt index a41afc4ad..da6ae8b39 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -543,7 +543,7 @@ if (UNIX OR MINGW) check_cxx_compiler_flag(-Bsymbolic-functions CXX_SUPPORTS_FLAG_BSYMB_FUNCS) if(CXX_SUPPORTS_FLAG_BSYMB_FUNCS) link_libraries(-Bsymbolic-functions) - endif() + endif() if (CMAKE_CXX_COMPILER_ID STREQUAL "Clang" OR CMAKE_CXX_COMPILER_ID STREQUAL "GNU") # Make sure we don't link to libstdc++ diff --git a/src/Makefile.am b/src/Makefile.am index 2aa3dc4fc..df45f73bc 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -368,11 +368,15 @@ dist_check_SCRIPTS += \ endif check_PROGRAMS += \ + dump-emoji \ dump-indic-data \ dump-khmer-data \ dump-myanmar-data \ dump-use-data \ $(NULL) +dump_emoji_SOURCES = dump-emoji.cc +dump_emoji_CPPFLAGS = $(HBCFLAGS) +dump_emoji_LDADD = libharfbuzz.la $(HBLIBS) dump_indic_data_SOURCES = dump-indic-data.cc hb-ot-shape-complex-indic-table.cc dump_indic_data_CPPFLAGS = $(HBCFLAGS) dump_indic_data_LDADD = libharfbuzz.la $(HBLIBS) diff --git a/src/dump-emoji.cc b/src/dump-emoji.cc new file mode 100644 index 000000000..a9595e41e --- /dev/null +++ b/src/dump-emoji.cc @@ -0,0 +1,141 @@ +/* + * Copyright © 2018 Ebrahim Byagowi + * + * This is part of HarfBuzz, a text shaping library. + * + * Permission is hereby granted, without written agreement and without + * license or royalty fees, to use, copy, modify, and distribute this + * software and its documentation for any purpose, provided that the + * above copyright notice and the following two paragraphs appear in + * all copies of this software. + * + * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR + * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES + * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN + * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH + * DAMAGE. + * + * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, + * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND + * FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS + * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO + * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. + */ + +#include "hb.h" +#include "hb-private.hh" +#include "hb-ot-color-cbdt-table.hh" +#include "hb-ot-color-sbix-table.hh" +#include "hb-ot-color-svg-table.hh" + +#ifdef HAVE_GLIB +#include +#endif +#include +#include + +#ifndef HB_NO_VISIBILITY +const void * const OT::_hb_NullPool[HB_NULL_POOL_SIZE / sizeof (void *)] = {}; +#endif + +void cbdt_callback (const uint8_t* data, unsigned int length, + unsigned int group, unsigned int gid) +{ + char outName[255]; + sprintf (outName, "out/cbdt-%d-%d.png", group, gid); + FILE *f = fopen (outName, "wb"); + fwrite (data, 1, length, f); + fclose (f); +} + +void sbix_callback (const uint8_t* data, unsigned int length, + unsigned int group, unsigned int gid) +{ + char outName[255]; + sprintf (outName, "out/sbix-%d-%d.png", group, gid); + FILE *f = fopen (outName, "wb"); + fwrite (data, 1, length, f); + fclose (f); +} + +void svg_callback (const uint8_t* data, unsigned int length, + unsigned int start_glyph, unsigned int end_glyph) +{ + char outName[255]; + if (start_glyph == end_glyph) + sprintf (outName, "out/svg-%d.svg", start_glyph); + else + sprintf (outName, "out/svg-%d-%d.svg", start_glyph, end_glyph); + + // append "z" if the content is gzipped + if ((data[0] == 0x1F) && (data[1] == 0x8B)) + strcat (outName, "z"); + + FILE *f = fopen (outName, "wb"); + fwrite (data, 1, length, f); + fclose (f); +} + +int main(int argc, char **argv) +{ + if (argc != 2) { + fprintf (stderr, "usage: %s font-file.ttf\n", argv[0]); + exit (1); + } + + hb_blob_t *blob = nullptr; + { + const char *font_data; + unsigned int len; + hb_destroy_func_t destroy; + void *user_data; + hb_memory_mode_t mm; + +#ifdef HAVE_GLIB + GMappedFile *mf = g_mapped_file_new (argv[1], false, nullptr); + font_data = g_mapped_file_get_contents (mf); + len = g_mapped_file_get_length (mf); + destroy = (hb_destroy_func_t) g_mapped_file_unref; + user_data = (void *) mf; + mm = HB_MEMORY_MODE_READONLY_MAY_MAKE_WRITABLE; +#else + FILE *f = fopen (argv[1], "rb"); + fseek (f, 0, SEEK_END); + len = ftell (f); + fseek (f, 0, SEEK_SET); + font_data = (const char *) malloc (len); + if (!font_data) len = 0; + len = fread ((char *) font_data, 1, len, f); + destroy = free; + user_data = (void *) font_data; + fclose (f); + mm = HB_MEMORY_MODE_WRITABLE; +#endif + + blob = hb_blob_create (font_data, len, mm, user_data, destroy); + } + + hb_face_t *face = hb_face_create (blob, 0); + hb_font_t *font = hb_font_create (face); + + OT::CBDT::accelerator_t cbdt; + cbdt.init (face); + cbdt.dump (cbdt_callback); + cbdt.fini (); + + OT::sbix::accelerator_t sbix; + sbix.init (face); + sbix.dump (sbix_callback); + sbix.fini (); + + OT::SVG::accelerator_t svg; + svg.init (face); + svg.dump (svg_callback); + svg.fini (); + + hb_font_destroy (font); + hb_face_destroy (face); + hb_blob_destroy (blob); + + return 0; +} diff --git a/src/hb-ot-color-cbdt-table.hh b/src/hb-ot-color-cbdt-table.hh index b4971bda7..cf1c69c17 100644 --- a/src/hb-ot-color-cbdt-table.hh +++ b/src/hb-ot-color-cbdt-table.hh @@ -223,6 +223,8 @@ struct IndexSubtableRecord struct IndexSubtableArray { + friend struct CBDT; + inline bool sanitize (hb_sanitize_context_t *c, unsigned int count) const { TRACE_SANITIZE (this); @@ -257,6 +259,7 @@ struct IndexSubtableArray struct BitmapSizeTable { friend struct CBLC; + friend struct CBDT; inline bool sanitize (hb_sanitize_context_t *c, const void *base) const { @@ -304,6 +307,21 @@ struct GlyphBitmapDataFormat17 DEFINE_SIZE_ARRAY(9, data); }; +struct GlyphBitmapDataFormat18 +{ + BigGlyphMetrics glyphMetrics; + ArrayOf data; + public: + DEFINE_SIZE_ARRAY(12, data); +}; + +struct GlyphBitmapDataFormat19 +{ + ArrayOf data; + public: + DEFINE_SIZE_ARRAY(4, data); +}; + /* * CBLC -- Color Bitmap Location Table @@ -444,6 +462,59 @@ struct CBDT return true; } + inline void dump (void (*callback) (const uint8_t* data, unsigned int length, + unsigned int group, unsigned int gid)) const + { + if (!cblc) + return; // Not a color bitmap font. + + for (unsigned int i = 0; i < cblc->sizeTables.len; ++i) + { + const BitmapSizeTable &sizeTable = cblc->sizeTables[i]; + const IndexSubtableArray &subtable_array = cblc+sizeTable.indexSubtableArrayOffset; + for (unsigned int j = 0; j < sizeTable.numberOfIndexSubtables; ++j) + { + const IndexSubtableRecord &subtable_record = subtable_array.indexSubtablesZ[j]; + for (unsigned int gid = subtable_record.firstGlyphIndex; + gid <= subtable_record.lastGlyphIndex; ++gid) + { + unsigned int image_offset = 0, image_length = 0, image_format = 0; + + if (!subtable_record.get_image_data (gid, + &image_offset, &image_length, &image_format)) + continue; + + switch (image_format) + { + case 17: { + const GlyphBitmapDataFormat17& glyphFormat17 = + StructAtOffset (this->cbdt, image_offset); + callback ((const uint8_t *) &glyphFormat17.data.array, + glyphFormat17.data.len, i, gid); + } + break; + case 18: { + const GlyphBitmapDataFormat18& glyphFormat18 = + StructAtOffset (this->cbdt, image_offset); + callback ((const uint8_t *) &glyphFormat18.data.array, + glyphFormat18.data.len, i, gid); + } + break; + case 19: { + const GlyphBitmapDataFormat19& glyphFormat19 = + StructAtOffset (this->cbdt, image_offset); + callback ((const uint8_t *) &glyphFormat19.data.array, + glyphFormat19.data.len, i, gid); + } + break; + default: + continue; + } + } + } + } + } + private: hb_blob_t *cblc_blob; hb_blob_t *cbdt_blob; diff --git a/src/hb-ot-color-sbix-table.hh b/src/hb-ot-color-sbix-table.hh index c0d8f9b3d..bc3956d81 100644 --- a/src/hb-ot-color-sbix-table.hh +++ b/src/hb-ot-color-sbix-table.hh @@ -27,7 +27,7 @@ #include "hb-open-type-private.hh" -#define HB_OT_TAG_SBIX HB_TAG('s','b','i','x') +#define HB_OT_TAG_sbix HB_TAG('s','b','i','x') namespace OT { @@ -55,6 +55,8 @@ struct SBIXGlyph struct SBIXStrike { + friend struct sbix; + inline bool sanitize (hb_sanitize_context_t *c) const { TRACE_SANITIZE (this); @@ -77,22 +79,12 @@ struct SBIXStrike /* * sbix -- Standard Bitmap Graphics Table + * https://docs.microsoft.com/en-us/typography/opentype/spec/sbix */ -// It should be called with something like this so it can have -// access to num_glyph while sanitizing. -// -// static inline const OT::sbix* -// _get_sbix (hb_face_t *face) -// { -// OT::Sanitizer sanitizer; -// sanitizer.set_num_glyphs (face->get_num_glyphs ()); -// hb_blob_t *sbix_blob = sanitizer.sanitize (face->reference_table (HB_OT_TAG_SBIX)); -// return OT::Sanitizer::lock_instance (sbix_blob); -// } -// + struct sbix { - static const hb_tag_t tableTag = HB_OT_TAG_SBIX; + static const hb_tag_t tableTag = HB_OT_TAG_sbix; inline bool sanitize (hb_sanitize_context_t *c) const { @@ -100,21 +92,50 @@ struct sbix return_trace (c->check_struct (this) && strikes.sanitize (c, this)); } - // inline void dump (unsigned int num_glyphs, unsigned int group) const - // { - // const SBIXStrike &strike = strikes[group](this); - // for (unsigned int i = 0; i < num_glyphs; ++i) - // if (strike.imageOffsetsZ[i + 1] - strike.imageOffsetsZ[i] > 0) - // { - // const SBIXGlyph &sbixGlyph = strike.imageOffsetsZ[i]((const void *) &strike); - // char outName[255]; - // sprintf (outName, "out/%d-%d.png", group, i); - // FILE *f = fopen (outName, "wb"); - // fwrite (sbixGlyph.data, 1, - // strike.imageOffsetsZ[i + 1] - strike.imageOffsetsZ[i] - 8, f); - // fclose (f); - // } - // } + struct accelerator_t + { + inline void init (hb_face_t *face) + { + num_glyphs = hb_face_get_glyph_count (face); + + OT::Sanitizer sanitizer; + sanitizer.set_num_glyphs (num_glyphs); + sbix_blob = sanitizer.sanitize (face->reference_table (HB_OT_TAG_sbix)); + sbix_len = hb_blob_get_length (sbix_blob); + sbix_table = OT::Sanitizer::lock_instance (sbix_blob); + + } + + inline void fini (void) + { + hb_blob_destroy (sbix_blob); + } + + inline void dump (void (*callback) (const uint8_t* data, unsigned int length, + unsigned int group, unsigned int gid)) const + { + for (unsigned group = 0; group < sbix_table->strikes.len; ++group) + { + const SBIXStrike &strike = sbix_table->strikes[group](sbix_table); + for (unsigned int glyph = 0; glyph < num_glyphs; ++glyph) + if (strike.imageOffsetsZ[glyph + 1] - strike.imageOffsetsZ[glyph] > 0) + { + const SBIXGlyph &sbixGlyph = strike.imageOffsetsZ[glyph]((const void *) &strike); + callback ((const uint8_t*) sbixGlyph.data, + strike.imageOffsetsZ[glyph + 1] - strike.imageOffsetsZ[glyph] - 8, + group, glyph); + } + } + } + + private: + hb_blob_t *sbix_blob; + const sbix *sbix_table; + + unsigned int sbix_len; + unsigned int num_glyphs; + + }; protected: HBUINT16 version; /* Table version number — set to 1 */ diff --git a/src/hb-ot-color-svg-table.hh b/src/hb-ot-color-svg-table.hh index 3dc24e995..6d0d9c4fc 100644 --- a/src/hb-ot-color-svg-table.hh +++ b/src/hb-ot-color-svg-table.hh @@ -39,7 +39,7 @@ namespace OT { struct SVGDocumentIndexEntry { - // friend struct SVGDocumentIndex; + friend struct SVG; inline bool sanitize (hb_sanitize_context_t *c, const void* base) const { @@ -64,27 +64,15 @@ struct SVGDocumentIndexEntry struct SVGDocumentIndex { + friend struct SVG; + inline bool sanitize (hb_sanitize_context_t *c) const { TRACE_SANITIZE (this); - // dump (); return_trace (c->check_struct (this) && entries.sanitize (c, this)); } - // inline void dump () const - // { - // for (unsigned int i = 0; i < entries.len; ++i) - // { - // char outName[255]; - // sprintf (outName, "out/%d.svg", i); - // const SVGDocumentIndexEntry &entry = entries[i]; - // FILE *f = fopen (outName, "wb"); - // fwrite (&entry.svgDoc (this), 1, entry.svgDocLength, f); - // fclose (f); - // } - // } - protected: ArrayOf entries; /* Array of SVG Document Index Entries. */ @@ -100,9 +88,45 @@ struct SVG { TRACE_SANITIZE (this); return_trace (c->check_struct (this) && - svgDocIndex(this).sanitize (c)); + svgDocIndex (this).sanitize (c)); } + struct accelerator_t + { + inline void init (hb_face_t *face) + { + OT::Sanitizer sanitizer; + svg_blob = sanitizer.sanitize (face->reference_table (HB_OT_TAG_SVG)); + svg_len = hb_blob_get_length (svg_blob); + svg = OT::Sanitizer::lock_instance (svg_blob); + + } + + inline void fini (void) + { + hb_blob_destroy (svg_blob); + } + + inline void dump (void (*callback) (const uint8_t* data, unsigned int length, + unsigned int start_glyph, unsigned int end_glyph)) const + { + const SVGDocumentIndex &index = svg->svgDocIndex (svg); + const ArrayOf &entries = index.entries; + for (unsigned int i = 0; i < entries.len; ++i) + { + const SVGDocumentIndexEntry &entry = entries[i]; + callback ((const uint8_t*) &entry.svgDoc (&index), entry.svgDocLength, + entry.startGlyphID, entry.endGlyphID); + } + } + + private: + hb_blob_t *svg_blob; + const SVG *svg; + + unsigned int svg_len; + }; + protected: HBUINT16 version; /* Table version (starting at 0). */ LOffsetTo