2 * Copyright © 2007,2008,2009 Red Hat, Inc.
3 * Copyright © 2018,2019,2020 Ebrahim Byagowi
4 * Copyright © 2018 Khaled Hosny
6 * This is part of HarfBuzz, a text shaping library.
8 * Permission is hereby granted, without written agreement and without
9 * license or royalty fees, to use, copy, modify, and distribute this
10 * software and its documentation for any purpose, provided that the
11 * above copyright notice and the following two paragraphs appear in
12 * all copies of this software.
14 * IN NO EVENT SHALL THE COPYRIGHT HOLDER BE LIABLE TO ANY PARTY FOR
15 * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES
16 * ARISING OUT OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN
17 * IF THE COPYRIGHT HOLDER HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH
20 * THE COPYRIGHT HOLDER SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING,
21 * BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
22 * FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS
23 * ON AN "AS IS" BASIS, AND THE COPYRIGHT HOLDER HAS NO OBLIGATION TO
24 * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
26 * Red Hat Author(s): Behdad Esfahbod
42 #define hb_blob_create_from_file_or_fail(x) hb_blob_get_empty ()
45 #if !defined(HB_NO_COLOR) && !defined(HB_NO_DRAW)
47 svg_dump (hb_face_t
*face
, unsigned face_index
)
49 unsigned glyph_count
= hb_face_get_glyph_count (face
);
51 for (unsigned glyph_id
= 0; glyph_id
< glyph_count
; ++glyph_id
)
53 hb_blob_t
*blob
= hb_ot_color_glyph_reference_svg (face
, glyph_id
);
55 if (hb_blob_get_length (blob
) == 0) continue;
58 const char *data
= hb_blob_get_data (blob
, &length
);
60 char output_path
[255];
61 snprintf (output_path
, sizeof output_path
,
62 "out/svg-%u-%u.svg%s",
65 // append "z" if the content is gzipped, https://stackoverflow.com/a/6059405
66 (length
> 2 && (data
[0] == '\x1F') && (data
[1] == '\x8B')) ? "z" : "");
68 FILE *f
= fopen (output_path
, "wb");
69 fwrite (data
, 1, length
, f
);
72 hb_blob_destroy (blob
);
76 /* _png API is so easy to use unlike the below code, don't get confused */
78 png_dump (hb_face_t
*face
, unsigned face_index
)
80 unsigned glyph_count
= hb_face_get_glyph_count (face
);
81 hb_font_t
*font
= hb_font_create (face
);
83 /* scans the font for strikes */
84 unsigned sample_glyph_id
;
85 /* we don't care about different strikes for different glyphs at this point */
86 for (sample_glyph_id
= 0; sample_glyph_id
< glyph_count
; ++sample_glyph_id
)
88 hb_blob_t
*blob
= hb_ot_color_glyph_reference_png (font
, sample_glyph_id
);
89 unsigned blob_length
= hb_blob_get_length (blob
);
90 hb_blob_destroy (blob
);
95 unsigned upem
= hb_face_get_upem (face
);
96 unsigned blob_length
= 0;
98 for (unsigned ppem
= 1; ppem
< upem
; ++ppem
)
100 hb_font_set_ppem (font
, ppem
, ppem
);
101 hb_blob_t
*blob
= hb_ot_color_glyph_reference_png (font
, sample_glyph_id
);
102 unsigned new_blob_length
= hb_blob_get_length (blob
);
103 hb_blob_destroy (blob
);
104 if (new_blob_length
!= blob_length
)
106 for (unsigned glyph_id
= 0; glyph_id
< glyph_count
; ++glyph_id
)
108 hb_blob_t
*blob
= hb_ot_color_glyph_reference_png (font
, glyph_id
);
110 if (hb_blob_get_length (blob
) == 0) continue;
113 const char *data
= hb_blob_get_data (blob
, &length
);
115 char output_path
[255];
116 snprintf (output_path
, sizeof output_path
, "out/png-%u-%u-%u.png", glyph_id
, strike
, face_index
);
118 FILE *f
= fopen (output_path
, "wb");
119 fwrite (data
, 1, length
, f
);
122 hb_blob_destroy (blob
);
126 blob_length
= new_blob_length
;
130 hb_font_destroy (font
);
136 hb_position_t ascender
;
140 move_to (hb_draw_funcs_t
*, draw_data_t
*draw_data
,
142 float to_x
, float to_y
,
145 fprintf (draw_data
->f
, "M%g,%g", to_x
, draw_data
->ascender
- to_y
);
149 line_to (hb_draw_funcs_t
*, draw_data_t
*draw_data
,
151 float to_x
, float to_y
,
154 fprintf (draw_data
->f
, "L%g,%g", to_x
, draw_data
->ascender
- to_y
);
158 quadratic_to (hb_draw_funcs_t
*, draw_data_t
*draw_data
,
160 float control_x
, float control_y
,
161 float to_x
, float to_y
,
164 fprintf (draw_data
->f
, "Q%g,%g %g,%g", control_x
, draw_data
->ascender
- control_y
,
165 to_x
, draw_data
->ascender
- to_y
);
169 cubic_to (hb_draw_funcs_t
*, draw_data_t
*draw_data
,
171 float control1_x
, float control1_y
,
172 float control2_x
, float control2_y
,
173 float to_x
, float to_y
,
176 fprintf (draw_data
->f
, "C%g,%g %g,%g %g,%g", control1_x
, draw_data
->ascender
- control1_y
,
177 control2_x
, draw_data
->ascender
- control2_y
,
178 to_x
, draw_data
->ascender
- to_y
);
182 close_path (hb_draw_funcs_t
*, draw_data_t
*draw_data
,
186 fprintf (draw_data
->f
, "Z");
190 layered_glyph_dump (hb_font_t
*font
, hb_draw_funcs_t
*funcs
, unsigned face_index
)
192 hb_face_t
*face
= hb_font_get_face (font
);
193 unsigned palette_count
= hb_ot_color_palette_get_count (face
);
194 for (unsigned palette
= 0; palette
< palette_count
; ++palette
)
196 unsigned num_colors
= hb_ot_color_palette_get_colors (face
, palette
, 0, nullptr, nullptr);
197 if (!num_colors
) continue;
199 hb_color_t
*colors
= (hb_color_t
*) calloc (num_colors
, sizeof (hb_color_t
));
200 hb_ot_color_palette_get_colors (face
, palette
, 0, &num_colors
, colors
);
207 unsigned num_glyphs
= hb_face_get_glyph_count (face
);
208 for (hb_codepoint_t gid
= 0; gid
< num_glyphs
; ++gid
)
210 unsigned num_layers
= hb_ot_color_glyph_get_layers (face
, gid
, 0, nullptr, nullptr);
211 if (!num_layers
) continue;
213 hb_ot_color_layer_t
*layers
= (hb_ot_color_layer_t
*) malloc (num_layers
* sizeof (hb_ot_color_layer_t
));
215 hb_ot_color_glyph_get_layers (face
, gid
, 0, &num_layers
, layers
);
218 hb_font_extents_t font_extents
;
219 hb_font_get_extents_for_direction (font
, HB_DIRECTION_LTR
, &font_extents
);
220 hb_glyph_extents_t extents
= {0};
221 if (!hb_font_get_glyph_extents (font
, gid
, &extents
))
223 printf ("Skip gid: %u\n", gid
);
227 char output_path
[255];
228 snprintf (output_path
, sizeof output_path
, "out/colr-%u-%u-%u.svg", gid
, palette
, face_index
);
229 FILE *f
= fopen (output_path
, "wb");
230 fprintf (f
, "<svg xmlns=\"http://www.w3.org/2000/svg\""
231 " viewBox=\"%d %d %d %d\">\n",
232 extents
.x_bearing
, 0,
233 extents
.x_bearing
+ extents
.width
, -extents
.height
);
234 draw_data_t draw_data
;
235 draw_data
.ascender
= extents
.y_bearing
;
238 for (unsigned layer
= 0; layer
< num_layers
; ++layer
)
240 hb_color_t color
= 0x000000FF;
241 if (layers
[layer
].color_index
!= 0xFFFF)
242 color
= colors
[layers
[layer
].color_index
];
243 fprintf (f
, "<path fill=\"#%02X%02X%02X\" ",
244 hb_color_get_red (color
), hb_color_get_green (color
), hb_color_get_green (color
));
245 if (hb_color_get_alpha (color
) != 255)
246 fprintf (f
, "fill-opacity=\"%.3f\"", (double) hb_color_get_alpha (color
) / 255.);
248 hb_font_draw_glyph (font
, layers
[layer
].glyph
, funcs
, &draw_data
);
249 fprintf (f
, "\"/>\n");
252 fprintf (f
, "</svg>");
263 dump_glyphs (hb_font_t
*font
, hb_draw_funcs_t
*funcs
, unsigned face_index
)
265 unsigned num_glyphs
= hb_face_get_glyph_count (hb_font_get_face (font
));
266 for (unsigned gid
= 0; gid
< num_glyphs
; ++gid
)
268 hb_font_extents_t font_extents
;
269 hb_font_get_extents_for_direction (font
, HB_DIRECTION_LTR
, &font_extents
);
270 hb_glyph_extents_t extents
= {0};
271 if (!hb_font_get_glyph_extents (font
, gid
, &extents
))
273 printf ("Skip gid: %u\n", gid
);
277 char output_path
[255];
278 snprintf (output_path
, sizeof output_path
, "out/%u-%u.svg", face_index
, gid
);
279 FILE *f
= fopen (output_path
, "wb");
280 fprintf (f
, "<svg xmlns=\"http://www.w3.org/2000/svg\""
281 " viewBox=\"%d %d %d %d\"><path d=\"",
282 extents
.x_bearing
, 0,
283 extents
.x_bearing
+ extents
.width
, font_extents
.ascender
- font_extents
.descender
);
284 draw_data_t draw_data
;
285 draw_data
.ascender
= font_extents
.ascender
;
287 hb_font_draw_glyph (font
, gid
, funcs
, &draw_data
);
288 fprintf (f
, "\"/></svg>");
294 dump_glyphs (hb_blob_t
*blob
, const char *font_name
)
296 FILE *font_name_file
= fopen ("out/.dumped_font_name", "r");
299 fprintf (stderr
, "Purge or rename ./out folder if you like to run a glyph dump,\n"
300 "run it like `rm -rf out && mkdir out && src/main font-file.ttf`\n");
304 font_name_file
= fopen ("out/.dumped_font_name", "w");
307 fprintf (stderr
, "./out is not accessible as a folder, create it please\n");
310 fwrite (font_name
, 1, strlen (font_name
), font_name_file
);
311 fclose (font_name_file
);
313 hb_draw_funcs_t
*funcs
= hb_draw_funcs_create ();
314 hb_draw_funcs_set_move_to_func (funcs
, (hb_draw_move_to_func_t
) move_to
, nullptr, nullptr);
315 hb_draw_funcs_set_line_to_func (funcs
, (hb_draw_line_to_func_t
) line_to
, nullptr, nullptr);
316 hb_draw_funcs_set_quadratic_to_func (funcs
, (hb_draw_quadratic_to_func_t
) quadratic_to
, nullptr, nullptr);
317 hb_draw_funcs_set_cubic_to_func (funcs
, (hb_draw_cubic_to_func_t
) cubic_to
, nullptr, nullptr);
318 hb_draw_funcs_set_close_path_func (funcs
, (hb_draw_close_path_func_t
) close_path
, nullptr, nullptr);
320 unsigned num_faces
= hb_face_count (blob
);
321 for (unsigned face_index
= 0; face_index
< num_faces
; ++face_index
)
323 hb_face_t
*face
= hb_face_create (blob
, face_index
);
324 hb_font_t
*font
= hb_font_create (face
);
326 if (hb_ot_color_has_png (face
))
327 printf ("Dumping png (CBDT/sbix)...\n");
328 png_dump (face
, face_index
);
330 if (hb_ot_color_has_svg (face
))
331 printf ("Dumping svg (SVG )...\n");
332 svg_dump (face
, face_index
);
334 if (hb_ot_color_has_layers (face
) && hb_ot_color_has_palettes (face
))
335 printf ("Dumping layered color glyphs (COLR/CPAL)...\n");
336 layered_glyph_dump (font
, funcs
, face_index
);
338 dump_glyphs (font
, funcs
, face_index
);
340 hb_font_destroy (font
);
341 hb_face_destroy (face
);
344 hb_draw_funcs_destroy (funcs
);
348 #ifndef MAIN_CC_NO_PRIVATE_API
349 /* Only this part of this mini app uses private API */
350 #include "hb-static.cc"
351 #include "hb-open-file.hh"
352 #include "hb-ot-layout-gdef-table.hh"
353 #include "hb-ot-layout-gsubgpos.hh"
358 print_layout_info_using_private_api (hb_blob_t
*blob
)
360 const char *font_data
= hb_blob_get_data (blob
, nullptr);
361 hb_blob_t
*font_blob
= hb_sanitize_context_t ().sanitize_blob
<OpenTypeFontFile
> (blob
);
362 const OpenTypeFontFile
* sanitized
= font_blob
->as
<OpenTypeFontFile
> ();
363 if (!font_blob
->data
)
365 printf ("Sanitization of the file wasn't successful. Exit");
368 const OpenTypeFontFile
& ot
= *sanitized
;
370 switch (ot
.get_tag ())
372 case OpenTypeFontFile::TrueTypeTag
:
373 printf ("OpenType font with TrueType outlines\n");
375 case OpenTypeFontFile::CFFTag
:
376 printf ("OpenType font with CFF (Type1) outlines\n");
378 case OpenTypeFontFile::TTCTag
:
379 printf ("TrueType Collection of OpenType fonts\n");
381 case OpenTypeFontFile::TrueTag
:
382 printf ("Obsolete Apple TrueType font\n");
384 case OpenTypeFontFile::Typ1Tag
:
385 printf ("Obsolete Apple Type1 font in SFNT container\n");
387 case OpenTypeFontFile::DFontTag
:
388 printf ("DFont Mac Resource Fork\n");
391 printf ("Unknown font format\n");
395 unsigned num_faces
= hb_face_count (blob
);
396 printf ("%u font(s) found in file\n", num_faces
);
397 for (unsigned n_font
= 0; n_font
< num_faces
; ++n_font
)
399 const OpenTypeFontFace
&font
= ot
.get_face (n_font
);
400 printf ("Font %u of %u:\n", n_font
, num_faces
);
402 unsigned num_tables
= font
.get_table_count ();
403 printf (" %u table(s) found in font\n", num_tables
);
404 for (unsigned n_table
= 0; n_table
< num_tables
; ++n_table
)
406 const OpenTypeTable
&table
= font
.get_table (n_table
);
407 printf (" Table %2u of %2u: %.4s (0x%08x+0x%08x)\n", n_table
, num_tables
,
408 (const char *) table
.tag
,
409 (unsigned) table
.offset
,
410 (unsigned) table
.length
);
419 const GSUBGPOS
&g
= *reinterpret_cast<const GSUBGPOS
*> (font_data
+ table
.offset
);
421 unsigned num_scripts
= g
.get_script_count ();
422 printf (" %u script(s) found in table\n", num_scripts
);
423 for (unsigned n_script
= 0; n_script
< num_scripts
; ++n_script
)
425 const Script
&script
= g
.get_script (n_script
);
426 printf (" Script %2u of %2u: %.4s\n", n_script
, num_scripts
,
427 (const char *) g
.get_script_tag (n_script
));
429 if (!script
.has_default_lang_sys ())
430 printf (" No default language system\n");
431 int num_langsys
= script
.get_lang_sys_count ();
432 printf (" %d language system(s) found in script\n", num_langsys
);
433 for (int n_langsys
= script
.has_default_lang_sys () ? -1 : 0; n_langsys
< num_langsys
; ++n_langsys
)
435 const LangSys
&langsys
= n_langsys
== -1
436 ? script
.get_default_lang_sys ()
437 : script
.get_lang_sys (n_langsys
);
439 printf (" Default Language System\n");
441 printf (" Language System %2d of %2d: %.4s\n", n_langsys
, num_langsys
,
442 (const char *) script
.get_lang_sys_tag (n_langsys
));
443 if (!langsys
.has_required_feature ())
444 printf (" No required feature\n");
446 printf (" Required feature index: %u\n",
447 langsys
.get_required_feature_index ());
449 unsigned num_features
= langsys
.get_feature_count ();
450 printf (" %u feature(s) found in language system\n", num_features
);
451 for (unsigned n_feature
= 0; n_feature
< num_features
; ++n_feature
)
453 printf (" Feature index %2u of %2u: %u\n", n_feature
, num_features
,
454 langsys
.get_feature_index (n_feature
));
459 unsigned num_features
= g
.get_feature_count ();
460 printf (" %u feature(s) found in table\n", num_features
);
461 for (unsigned n_feature
= 0; n_feature
< num_features
; ++n_feature
)
463 const Feature
&feature
= g
.get_feature (n_feature
);
464 unsigned num_lookups
= feature
.get_lookup_count ();
465 printf (" Feature %2u of %2u: %c%c%c%c\n", n_feature
, num_features
,
466 HB_UNTAG (g
.get_feature_tag (n_feature
)));
468 printf (" %u lookup(s) found in feature\n", num_lookups
);
469 for (unsigned n_lookup
= 0; n_lookup
< num_lookups
; ++n_lookup
) {
470 printf (" Lookup index %2u of %2u: %u\n", n_lookup
, num_lookups
,
471 feature
.get_lookup_index (n_lookup
));
475 unsigned num_lookups
= g
.get_lookup_count ();
476 printf (" %u lookup(s) found in table\n", num_lookups
);
477 for (unsigned n_lookup
= 0; n_lookup
< num_lookups
; ++n_lookup
)
479 const Lookup
&lookup
= g
.get_lookup (n_lookup
);
480 printf (" Lookup %2u of %2u: type %u, props 0x%04X\n", n_lookup
, num_lookups
,
481 lookup
.get_type (), lookup
.get_props ());
490 const GDEF
&gdef
= *reinterpret_cast<const GDEF
*> (font_data
+ table
.offset
);
492 printf (" Has %sglyph classes\n",
493 gdef
.has_glyph_classes () ? "" : "no ");
494 printf (" Has %smark attachment types\n",
495 gdef
.has_mark_attachment_types () ? "" : "no ");
496 printf (" Has %sattach list\n",
497 gdef
.has_attach_list () ? "" : "no ");
498 printf (" Has %slig carets\n",
499 gdef
.has_lig_carets () ? "" : "no ");
500 printf (" Has %smark glyph sets\n",
501 gdef
.has_mark_glyph_sets () ? "" : "no ");
508 /* end of private API use */
512 main (int argc
, char **argv
)
516 fprintf (stderr
, "usage: %s font-file.ttf\n", argv
[0]);
520 hb_blob_t
*blob
= hb_blob_create_from_file_or_fail (argv
[1]);
522 printf ("Opened font file %s: %u bytes long\n", argv
[1], hb_blob_get_length (blob
));
523 #ifndef MAIN_CC_NO_PRIVATE_API
524 print_layout_info_using_private_api (blob
);
526 #if !defined(HB_NO_COLOR) && !defined(HB_NO_DRAW)
527 dump_glyphs (blob
, argv
[1]);
529 hb_blob_destroy (blob
);