2012-08-14 06:35:55 +00:00
|
|
|
/*
|
2013-02-05 14:00:23 +00:00
|
|
|
* font.cc
|
2012-08-14 06:35:55 +00:00
|
|
|
*
|
2013-02-05 14:00:23 +00:00
|
|
|
* Font processing
|
2012-08-14 06:35:55 +00:00
|
|
|
*
|
2013-02-05 06:36:36 +00:00
|
|
|
* Copyright (C) 2012,2013 Lu Wang <coolwanglu@gmail.com>
|
2012-08-14 06:35:55 +00:00
|
|
|
*/
|
|
|
|
|
|
|
|
#include <iostream>
|
2012-08-30 00:39:49 +00:00
|
|
|
#include <cmath>
|
2012-08-30 17:08:31 +00:00
|
|
|
#include <algorithm>
|
2013-02-05 14:00:23 +00:00
|
|
|
#include <sstream>
|
|
|
|
#include <cctype>
|
2013-02-05 14:07:51 +00:00
|
|
|
#include <unordered_set>
|
2012-08-14 06:35:55 +00:00
|
|
|
|
2012-09-27 09:17:08 +00:00
|
|
|
#include <GlobalParams.h>
|
2013-02-05 14:07:51 +00:00
|
|
|
#include <fofi/FoFiTrueType.h>
|
|
|
|
#include <CharCodeToUnicode.h>
|
2012-08-31 09:06:19 +00:00
|
|
|
|
2012-09-27 09:17:08 +00:00
|
|
|
#include "Param.h"
|
2012-08-14 06:35:55 +00:00
|
|
|
#include "HTMLRenderer.h"
|
2013-04-06 15:41:58 +00:00
|
|
|
#include "Base64Stream.h"
|
2013-04-06 08:45:01 +00:00
|
|
|
|
2013-09-18 18:41:00 +00:00
|
|
|
#include "pdf2htmlEX-config.h"
|
|
|
|
|
2012-11-29 09:28:05 +00:00
|
|
|
#include "util/namespace.h"
|
2012-11-29 10:16:05 +00:00
|
|
|
#include "util/math.h"
|
2012-11-29 10:28:07 +00:00
|
|
|
#include "util/misc.h"
|
2013-02-05 14:07:51 +00:00
|
|
|
#include "util/ffw.h"
|
|
|
|
#include "util/path.h"
|
|
|
|
#include "util/unicode.h"
|
2013-02-27 18:52:00 +00:00
|
|
|
#include "util/css_const.h"
|
2012-08-26 15:56:38 +00:00
|
|
|
|
2013-09-18 18:41:00 +00:00
|
|
|
#if ENABLE_SVG
|
|
|
|
#include <cairo.h>
|
|
|
|
#include <cairo-ft.h>
|
|
|
|
#include <cairo-svg.h>
|
2013-09-23 08:37:02 +00:00
|
|
|
#include "CairoFontEngine.h"
|
|
|
|
#include "CairoOutputDev.h"
|
2013-09-19 14:02:12 +00:00
|
|
|
#include <Gfx.h>
|
2013-09-18 18:41:00 +00:00
|
|
|
#endif
|
|
|
|
|
2012-09-12 15:26:14 +00:00
|
|
|
namespace pdf2htmlEX {
|
|
|
|
|
2013-02-05 14:07:51 +00:00
|
|
|
using std::min;
|
|
|
|
using std::unordered_set;
|
2012-11-29 10:28:07 +00:00
|
|
|
using std::cerr;
|
|
|
|
using std::endl;
|
2012-08-31 15:14:05 +00:00
|
|
|
|
2013-09-19 14:02:12 +00:00
|
|
|
string HTMLRenderer::dump_embedded_font (GfxFont * font, FontInfo & info)
|
2013-02-05 14:07:51 +00:00
|
|
|
{
|
2013-09-19 14:02:12 +00:00
|
|
|
if(info.is_type3)
|
|
|
|
return dump_type3_font(font, info);
|
2013-09-18 21:56:57 +00:00
|
|
|
|
2013-02-05 14:07:51 +00:00
|
|
|
Object obj, obj1, obj2;
|
|
|
|
Object font_obj, font_obj2, fontdesc_obj;
|
|
|
|
string suffix;
|
|
|
|
string filepath;
|
|
|
|
|
2013-09-19 14:02:12 +00:00
|
|
|
long long fn_id = info.id;
|
|
|
|
|
2013-02-05 14:07:51 +00:00
|
|
|
try
|
|
|
|
{
|
2013-05-02 17:47:55 +00:00
|
|
|
// inspired by mupdf
|
2013-02-05 14:07:51 +00:00
|
|
|
string subtype;
|
|
|
|
|
|
|
|
auto * id = font->getID();
|
|
|
|
|
|
|
|
Object ref_obj;
|
|
|
|
ref_obj.initRef(id->num, id->gen);
|
|
|
|
ref_obj.fetch(xref, &font_obj);
|
|
|
|
ref_obj.free();
|
|
|
|
|
|
|
|
if(!font_obj.isDict())
|
|
|
|
{
|
|
|
|
cerr << "Font object is not a dictionary" << endl;
|
|
|
|
throw 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
Dict * dict = font_obj.getDict();
|
|
|
|
if(dict->lookup("DescendantFonts", &font_obj2)->isArray())
|
|
|
|
{
|
|
|
|
if(font_obj2.arrayGetLength() == 0)
|
|
|
|
{
|
|
|
|
cerr << "Warning: empty DescendantFonts array" << endl;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
if(font_obj2.arrayGetLength() > 1)
|
|
|
|
cerr << "TODO: multiple entries in DescendantFonts array" << endl;
|
|
|
|
|
|
|
|
if(font_obj2.arrayGet(0, &obj2)->isDict())
|
|
|
|
{
|
|
|
|
dict = obj2.getDict();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if(!dict->lookup("FontDescriptor", &fontdesc_obj)->isDict())
|
|
|
|
{
|
|
|
|
cerr << "Cannot find FontDescriptor " << endl;
|
|
|
|
throw 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
dict = fontdesc_obj.getDict();
|
|
|
|
|
|
|
|
if(dict->lookup("FontFile3", &obj)->isStream())
|
|
|
|
{
|
|
|
|
if(obj.streamGetDict()->lookup("Subtype", &obj1)->isName())
|
|
|
|
{
|
|
|
|
subtype = obj1.getName();
|
|
|
|
if(subtype == "Type1C")
|
|
|
|
{
|
|
|
|
suffix = ".cff";
|
|
|
|
}
|
|
|
|
else if (subtype == "CIDFontType0C")
|
|
|
|
{
|
|
|
|
suffix = ".cid";
|
|
|
|
}
|
2015-05-03 10:40:40 +00:00
|
|
|
else if (subtype == "OpenType")
|
|
|
|
{
|
|
|
|
suffix = ".otf";
|
|
|
|
}
|
2013-02-05 14:07:51 +00:00
|
|
|
else
|
|
|
|
{
|
|
|
|
cerr << "Unknown subtype: " << subtype << endl;
|
|
|
|
throw 0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
cerr << "Invalid subtype in font descriptor" << endl;
|
|
|
|
throw 0;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else if (dict->lookup("FontFile2", &obj)->isStream())
|
|
|
|
{
|
|
|
|
suffix = ".ttf";
|
|
|
|
}
|
|
|
|
else if (dict->lookup("FontFile", &obj)->isStream())
|
|
|
|
{
|
|
|
|
suffix = ".pfa";
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
cerr << "Cannot find FontFile for dump" << endl;
|
|
|
|
throw 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
if(suffix == "")
|
|
|
|
{
|
|
|
|
cerr << "Font type unrecognized" << endl;
|
|
|
|
throw 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
obj.streamReset();
|
|
|
|
|
2013-04-06 09:01:05 +00:00
|
|
|
filepath = (char*)str_fmt("%s/f%llx%s", param.tmp_dir.c_str(), fn_id, suffix.c_str());
|
2013-02-05 14:07:51 +00:00
|
|
|
tmp_files.add(filepath);
|
|
|
|
|
|
|
|
ofstream outf(filepath, ofstream::binary);
|
|
|
|
if(!outf)
|
|
|
|
throw string("Cannot open file ") + filepath + " for writing";
|
|
|
|
|
|
|
|
char buf[1024];
|
|
|
|
int len;
|
|
|
|
while((len = obj.streamGetChars(1024, (Guchar*)buf)) > 0)
|
|
|
|
{
|
|
|
|
outf.write(buf, len);
|
|
|
|
}
|
|
|
|
obj.streamClose();
|
|
|
|
}
|
|
|
|
catch(int)
|
|
|
|
{
|
2014-07-13 23:59:30 +00:00
|
|
|
cerr << "Something wrong when trying to dump font " << hex << fn_id << dec << endl;
|
2013-02-05 14:07:51 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
obj2.free();
|
|
|
|
obj1.free();
|
|
|
|
obj.free();
|
|
|
|
|
|
|
|
fontdesc_obj.free();
|
|
|
|
font_obj2.free();
|
|
|
|
font_obj.free();
|
|
|
|
|
|
|
|
return filepath;
|
|
|
|
}
|
|
|
|
|
2013-09-19 14:02:12 +00:00
|
|
|
string HTMLRenderer::dump_type3_font (GfxFont * font, FontInfo & info)
|
2013-09-18 18:41:00 +00:00
|
|
|
{
|
2013-09-19 14:02:12 +00:00
|
|
|
assert(info.is_type3);
|
2013-09-18 18:41:00 +00:00
|
|
|
|
|
|
|
#if ENABLE_SVG
|
2013-09-19 14:02:12 +00:00
|
|
|
long long fn_id = info.id;
|
|
|
|
|
2013-09-18 18:41:00 +00:00
|
|
|
FT_Library ft_lib;
|
|
|
|
FT_Init_FreeType(&ft_lib);
|
|
|
|
CairoFontEngine font_engine(ft_lib);
|
|
|
|
auto * cur_font = font_engine.getFont(font, cur_doc, true, xref);
|
|
|
|
auto used_map = preprocessor.get_code_map(hash_ref(font->getID()));
|
|
|
|
|
2013-09-20 08:35:19 +00:00
|
|
|
//calculate transformed metrics
|
2013-09-18 18:41:00 +00:00
|
|
|
double * font_bbox = font->getFontBBox();
|
2013-09-19 14:02:12 +00:00
|
|
|
double * font_matrix = font->getFontMatrix();
|
|
|
|
double transformed_bbox[4];
|
|
|
|
memcpy(transformed_bbox, font_bbox, 4 * sizeof(double));
|
2013-09-20 12:06:06 +00:00
|
|
|
/*
|
2013-09-20 08:35:19 +00:00
|
|
|
// add the origin to the bbox
|
|
|
|
if(transformed_bbox[0] > 0) transformed_bbox[0] = 0;
|
|
|
|
if(transformed_bbox[1] > 0) transformed_bbox[1] = 0;
|
|
|
|
if(transformed_bbox[2] < 0) transformed_bbox[2] = 0;
|
|
|
|
if(transformed_bbox[3] < 0) transformed_bbox[3] = 0;
|
2013-09-20 12:06:06 +00:00
|
|
|
*/
|
2013-09-19 14:02:12 +00:00
|
|
|
tm_transform_bbox(font_matrix, transformed_bbox);
|
|
|
|
double transformed_bbox_width = transformed_bbox[2] - transformed_bbox[0];
|
|
|
|
double transformed_bbox_height = transformed_bbox[3] - transformed_bbox[1];
|
2013-09-20 08:35:19 +00:00
|
|
|
info.font_size_scale = std::max(transformed_bbox_width, transformed_bbox_height);
|
2013-09-19 14:02:12 +00:00
|
|
|
|
2013-09-21 05:56:57 +00:00
|
|
|
// we want the glyphs is rendered in a box of size around GLYPH_DUMP_EM_SIZE x GLYPH_DUMP_EM_SIZE
|
|
|
|
// for rectangles, the longer edge should be GLYPH_DUMP_EM_SIZE
|
2013-09-20 12:06:06 +00:00
|
|
|
const double GLYPH_DUMP_EM_SIZE = 100.0;
|
|
|
|
double scale = GLYPH_DUMP_EM_SIZE / info.font_size_scale;
|
2013-09-19 14:02:12 +00:00
|
|
|
|
2013-09-20 08:35:19 +00:00
|
|
|
// we choose ttf as it does not use char names
|
|
|
|
// or actually we don't use char names for ttf (see embed_font)
|
2013-09-18 21:56:57 +00:00
|
|
|
ffw_new_font();
|
2013-09-20 08:35:19 +00:00
|
|
|
// dump each glyph into svg and combine them
|
2013-09-18 21:56:57 +00:00
|
|
|
for(int code = 0; code < 256; ++code)
|
2013-09-18 18:41:00 +00:00
|
|
|
{
|
2013-09-18 21:56:57 +00:00
|
|
|
if(!used_map[code]) continue;
|
2013-09-18 18:41:00 +00:00
|
|
|
|
|
|
|
cairo_surface_t * surface = nullptr;
|
2013-09-18 21:56:57 +00:00
|
|
|
|
|
|
|
string glyph_filename = (char*)str_fmt("%s/f%llx-%x.svg", param.tmp_dir.c_str(), fn_id, code);
|
|
|
|
tmp_files.add(glyph_filename);
|
2013-09-19 14:02:12 +00:00
|
|
|
|
|
|
|
surface = cairo_svg_surface_create(glyph_filename.c_str(), transformed_bbox_width * scale, transformed_bbox_height * scale);
|
2013-09-18 21:56:57 +00:00
|
|
|
|
2013-09-18 18:41:00 +00:00
|
|
|
cairo_svg_surface_restrict_to_version(surface, CAIRO_SVG_VERSION_1_2);
|
|
|
|
cairo_surface_set_fallback_resolution(surface, param.h_dpi, param.v_dpi);
|
|
|
|
cairo_t * cr = cairo_create(surface);
|
|
|
|
|
2014-07-13 23:59:30 +00:00
|
|
|
// track the position of the origin
|
2013-09-20 12:06:06 +00:00
|
|
|
double ox, oy;
|
|
|
|
ox = oy = 0.0;
|
|
|
|
|
2013-09-20 12:30:46 +00:00
|
|
|
auto glyph_width = ((Gfx8BitFont*)font)->getWidth(code);
|
2013-09-20 13:17:39 +00:00
|
|
|
|
2013-09-21 05:56:57 +00:00
|
|
|
#if 1
|
2013-09-19 14:02:12 +00:00
|
|
|
{
|
2013-09-20 13:17:39 +00:00
|
|
|
// pain the glyph
|
|
|
|
cairo_set_font_face(cr, cur_font->getFontFace());
|
|
|
|
|
|
|
|
cairo_matrix_t m1, m2, m3;
|
|
|
|
// set up m1
|
|
|
|
// m1 shift the bottom-left corner of the glyph bbox to the origin
|
|
|
|
// also set font size to scale
|
|
|
|
cairo_matrix_init_translate(&m1, -transformed_bbox[0], transformed_bbox[1]);
|
|
|
|
cairo_matrix_init_scale(&m2, scale, scale);
|
|
|
|
cairo_matrix_multiply(&m1, &m1, &m2);
|
|
|
|
cairo_set_font_matrix(cr, &m1);
|
|
|
|
|
|
|
|
cairo_glyph_t glyph;
|
|
|
|
glyph.index = cur_font->getGlyph(code, nullptr, 0);
|
|
|
|
glyph.x = 0;
|
|
|
|
glyph.y = GLYPH_DUMP_EM_SIZE;
|
|
|
|
cairo_show_glyphs(cr, &glyph, 1);
|
|
|
|
|
|
|
|
|
|
|
|
// apply the type 3 font's font matrix before m1
|
|
|
|
// such that we got the mapping from type 3 font space to user space, then we will be able to calculate mapped position for ox,oy and glyph_width
|
|
|
|
cairo_matrix_init(&m2, font_matrix[0], font_matrix[1], font_matrix[2], font_matrix[3], font_matrix[4], font_matrix[5]);
|
|
|
|
cairo_matrix_init_scale(&m3, 1, -1);
|
|
|
|
cairo_matrix_multiply(&m2, &m2, &m3);
|
|
|
|
cairo_matrix_multiply(&m2, &m2, &m1);
|
|
|
|
|
|
|
|
cairo_matrix_transform_point(&m2, &ox, &oy);
|
|
|
|
double dummy = 0;
|
|
|
|
cairo_matrix_transform_distance(&m2, &glyph_width, &dummy);
|
|
|
|
}
|
2013-09-21 05:56:57 +00:00
|
|
|
#else
|
2013-09-20 13:17:39 +00:00
|
|
|
{
|
|
|
|
// manually draw the char to get the metrics
|
|
|
|
// adapted from _render_type3_glyph of poppler
|
2013-09-20 12:06:06 +00:00
|
|
|
cairo_matrix_t ctm, m, m1;
|
|
|
|
cairo_matrix_init_identity(&ctm);
|
|
|
|
|
|
|
|
// apply font-matrix
|
|
|
|
cairo_matrix_init(&m, font_matrix[0], font_matrix[1], font_matrix[2], font_matrix[3], font_matrix[4], font_matrix[5]);
|
|
|
|
cairo_matrix_multiply(&ctm, &ctm, &m);
|
|
|
|
|
|
|
|
// shift origin
|
|
|
|
cairo_matrix_init_translate(&m1, -transformed_bbox[0], -transformed_bbox[1]);
|
|
|
|
cairo_matrix_multiply(&ctm, &ctm, &m1);
|
|
|
|
|
|
|
|
// make it upside down since the difference between the glyph coordination and cairo coordination
|
|
|
|
cairo_matrix_init_scale(&m1, 1, -1);
|
|
|
|
cairo_matrix_multiply(&ctm, &ctm, &m1);
|
|
|
|
// save m*m1 to m1 for later use
|
|
|
|
cairo_matrix_multiply(&m1, &m, &m1);
|
|
|
|
|
|
|
|
// shift up to the bounding box
|
|
|
|
cairo_matrix_init_translate(&m, 0.0, transformed_bbox_height);
|
|
|
|
cairo_matrix_multiply(&ctm, &ctm, &m);
|
|
|
|
|
|
|
|
// scale up
|
|
|
|
cairo_matrix_init_scale(&m, scale, scale);
|
|
|
|
cairo_matrix_multiply(&ctm, &ctm, &m);
|
2013-09-19 14:02:12 +00:00
|
|
|
|
2013-09-20 12:06:06 +00:00
|
|
|
// set ctm
|
|
|
|
cairo_set_matrix(cr, &ctm);
|
2013-09-19 14:02:12 +00:00
|
|
|
|
2013-09-20 12:30:46 +00:00
|
|
|
// calculate the position of origin
|
|
|
|
cairo_matrix_transform_point(&ctm, &ox, &oy);
|
2013-09-21 05:56:57 +00:00
|
|
|
oy -= transformed_bbox_height * scale;
|
2013-09-20 12:30:46 +00:00
|
|
|
// calculate glyph width
|
|
|
|
double dummy = 0;
|
2013-09-20 13:17:39 +00:00
|
|
|
cairo_matrix_transform_distance(&ctm, &glyph_width, &dummy);
|
2013-09-20 12:30:46 +00:00
|
|
|
|
2013-09-20 12:06:06 +00:00
|
|
|
// draw the glyph
|
2013-09-19 14:02:12 +00:00
|
|
|
auto output_dev = new CairoOutputDev();
|
|
|
|
output_dev->setCairo(cr);
|
|
|
|
output_dev->setPrinting(true);
|
|
|
|
|
|
|
|
PDFRectangle box;
|
|
|
|
box.x1 = font_bbox[0];
|
|
|
|
box.y1 = font_bbox[1];
|
|
|
|
box.x2 = font_bbox[2];
|
|
|
|
box.y2 = font_bbox[3];
|
|
|
|
auto gfx = new Gfx(cur_doc, output_dev,
|
|
|
|
((Gfx8BitFont*)font)->getResources(),
|
|
|
|
&box, nullptr);
|
|
|
|
output_dev->startDoc(cur_doc, &font_engine);
|
|
|
|
output_dev->startPage(1, gfx->getState(), gfx->getXRef());
|
|
|
|
output_dev->setInType3Char(gTrue);
|
|
|
|
auto char_procs = ((Gfx8BitFont*)font)->getCharProcs();
|
|
|
|
Object char_proc_obj;
|
2013-09-20 12:06:06 +00:00
|
|
|
auto glyph_index = cur_font->getGlyph(code, nullptr, 0);
|
|
|
|
gfx->display(char_procs->getVal(glyph_index, &char_proc_obj));
|
2013-09-19 14:02:12 +00:00
|
|
|
|
|
|
|
char_proc_obj.free();
|
|
|
|
delete gfx;
|
|
|
|
delete output_dev;
|
|
|
|
}
|
2013-09-21 05:56:57 +00:00
|
|
|
#endif
|
2013-09-18 18:41:00 +00:00
|
|
|
|
|
|
|
{
|
|
|
|
auto status = cairo_status(cr);
|
|
|
|
cairo_destroy(cr);
|
|
|
|
if(status)
|
|
|
|
throw string("Cairo error: ") + cairo_status_to_string(status);
|
|
|
|
}
|
|
|
|
cairo_surface_finish(surface);
|
|
|
|
{
|
|
|
|
auto status = cairo_surface_status(surface);
|
|
|
|
cairo_surface_destroy(surface);
|
|
|
|
surface = nullptr;
|
|
|
|
if(status)
|
|
|
|
throw string("Error in cairo: ") + cairo_status_to_string(status);
|
|
|
|
}
|
2013-09-18 21:56:57 +00:00
|
|
|
|
2013-09-20 13:17:39 +00:00
|
|
|
ffw_import_svg_glyph(code, glyph_filename.c_str(), ox / GLYPH_DUMP_EM_SIZE, -oy / GLYPH_DUMP_EM_SIZE, glyph_width / GLYPH_DUMP_EM_SIZE);
|
2013-09-18 18:41:00 +00:00
|
|
|
}
|
2013-09-18 21:56:57 +00:00
|
|
|
|
|
|
|
string font_filename = (char*)str_fmt("%s/f%llx.ttf", param.tmp_dir.c_str(), fn_id);
|
|
|
|
tmp_files.add(font_filename);
|
|
|
|
ffw_save(font_filename.c_str());
|
|
|
|
ffw_close();
|
|
|
|
|
|
|
|
return font_filename;
|
2013-09-18 18:41:00 +00:00
|
|
|
#else
|
|
|
|
return "";
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
2013-02-05 14:07:51 +00:00
|
|
|
void HTMLRenderer::embed_font(const string & filepath, GfxFont * font, FontInfo & info, bool get_metric_only)
|
|
|
|
{
|
2013-04-06 09:01:05 +00:00
|
|
|
if(param.debug)
|
2013-02-05 14:07:51 +00:00
|
|
|
{
|
|
|
|
cerr << "Embed font: " << filepath << " " << info.id << endl;
|
|
|
|
}
|
|
|
|
|
|
|
|
ffw_load_font(filepath.c_str());
|
|
|
|
ffw_prepare_font();
|
|
|
|
|
2013-04-06 09:01:05 +00:00
|
|
|
if(param.debug)
|
2013-02-05 14:07:51 +00:00
|
|
|
{
|
2013-09-18 12:24:48 +00:00
|
|
|
auto fn = str_fmt("%s/__raw_font_%llx%s", param.tmp_dir.c_str(), info.id, get_suffix(filepath).c_str());
|
2013-02-05 14:07:51 +00:00
|
|
|
tmp_files.add((char*)fn);
|
|
|
|
ofstream((char*)fn, ofstream::binary) << ifstream(filepath).rdbuf();
|
|
|
|
}
|
|
|
|
|
|
|
|
int * code2GID = nullptr;
|
|
|
|
int code2GID_len = 0;
|
|
|
|
int maxcode = 0;
|
|
|
|
|
|
|
|
Gfx8BitFont * font_8bit = nullptr;
|
|
|
|
GfxCIDFont * font_cid = nullptr;
|
|
|
|
|
|
|
|
string suffix = get_suffix(filepath);
|
2014-11-16 14:04:02 +00:00
|
|
|
for(auto & c : suffix)
|
|
|
|
c = tolower(c);
|
2013-02-05 14:07:51 +00:00
|
|
|
|
|
|
|
/*
|
|
|
|
* if parm->tounicode is 0, try the provided tounicode map first
|
|
|
|
*/
|
2013-05-02 16:51:58 +00:00
|
|
|
info.use_tounicode = (param.tounicode >= 0);
|
2013-02-05 14:07:51 +00:00
|
|
|
bool has_space = false;
|
|
|
|
|
|
|
|
const char * used_map = nullptr;
|
|
|
|
|
|
|
|
info.em_size = ffw_get_em_size();
|
2013-05-03 07:19:09 +00:00
|
|
|
|
|
|
|
if(param.debug)
|
|
|
|
{
|
2013-07-06 01:16:16 +00:00
|
|
|
cerr << "em size: " << info.em_size << endl;
|
2013-05-03 07:19:09 +00:00
|
|
|
}
|
|
|
|
|
2013-03-30 14:37:20 +00:00
|
|
|
info.space_width = 0;
|
2013-02-05 14:07:51 +00:00
|
|
|
|
2013-03-20 15:46:58 +00:00
|
|
|
if(!font->isCIDFont())
|
|
|
|
{
|
2013-03-21 04:18:26 +00:00
|
|
|
font_8bit = dynamic_cast<Gfx8BitFont*>(font);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
font_cid = dynamic_cast<GfxCIDFont*>(font);
|
2013-03-20 15:46:58 +00:00
|
|
|
}
|
|
|
|
|
2013-02-05 14:07:51 +00:00
|
|
|
if(get_metric_only)
|
|
|
|
{
|
2013-09-20 08:35:19 +00:00
|
|
|
ffw_fix_metric();
|
|
|
|
ffw_get_metric(&info.ascent, &info.descent);
|
2013-02-05 14:07:51 +00:00
|
|
|
ffw_close();
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
used_map = preprocessor.get_code_map(hash_ref(font->getID()));
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Step 1
|
|
|
|
* dump the font file directly from the font descriptor and put the glyphs into the correct slots *
|
2013-09-18 21:56:57 +00:00
|
|
|
*
|
2013-02-05 14:07:51 +00:00
|
|
|
* for 8bit + nonTrueType
|
|
|
|
* re-encoding the font by glyph names
|
|
|
|
*
|
|
|
|
* for 8bit + TrueType
|
2016-01-01 11:02:27 +00:00
|
|
|
* sort the glyphs as the original order, and load the code2GID table
|
2013-05-03 18:52:30 +00:00
|
|
|
* later we will map GID (instead of char code) to Unicode
|
2013-02-05 14:07:51 +00:00
|
|
|
*
|
|
|
|
* for CID + nonTrueType
|
|
|
|
* Flatten the font
|
|
|
|
*
|
|
|
|
* for CID Truetype
|
|
|
|
* same as 8bitTrueType, except for that we have to check 65536 charcodes
|
2013-05-03 18:52:30 +00:00
|
|
|
* use the embedded code2GID table if there is, otherwise use the one in the font
|
2013-02-05 14:07:51 +00:00
|
|
|
*/
|
2013-03-21 04:18:26 +00:00
|
|
|
if(font_8bit)
|
2013-02-05 14:07:51 +00:00
|
|
|
{
|
|
|
|
maxcode = 0xff;
|
|
|
|
if(is_truetype_suffix(suffix))
|
|
|
|
{
|
2013-09-18 21:56:57 +00:00
|
|
|
if(info.is_type3)
|
2013-02-05 14:07:51 +00:00
|
|
|
{
|
2013-09-18 22:05:03 +00:00
|
|
|
/*
|
|
|
|
* Type 3 fonts are saved and converted into ttf fonts
|
|
|
|
* encoded based on code points instead of GID
|
|
|
|
*
|
|
|
|
* I thought code2GID would work but it never works, and I don't know why
|
|
|
|
* Anyway we can disable code2GID such that the following procedure will be working based on code points instead of GID
|
|
|
|
*/
|
2013-09-18 21:56:57 +00:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
ffw_reencode_glyph_order();
|
|
|
|
if(FoFiTrueType * fftt = FoFiTrueType::load((char*)filepath.c_str()))
|
|
|
|
{
|
|
|
|
code2GID = font_8bit->getCodeToGIDMap(fftt);
|
|
|
|
code2GID_len = 256;
|
|
|
|
delete fftt;
|
|
|
|
}
|
2013-02-05 14:07:51 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
// move the slot such that it's consistent with the encoding seen in PDF
|
|
|
|
unordered_set<string> nameset;
|
|
|
|
bool name_conflict_warned = false;
|
|
|
|
|
2014-11-16 14:04:02 +00:00
|
|
|
std::fill(cur_mapping2.begin(), cur_mapping2.end(), (char*)nullptr);
|
2013-02-05 14:07:51 +00:00
|
|
|
|
|
|
|
for(int i = 0; i < 256; ++i)
|
|
|
|
{
|
|
|
|
if(!used_map[i]) continue;
|
|
|
|
|
|
|
|
auto cn = font_8bit->getCharName(i);
|
|
|
|
if(cn == nullptr)
|
|
|
|
{
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
if(nameset.insert(string(cn)).second)
|
|
|
|
{
|
|
|
|
cur_mapping2[i] = cn;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
if(!name_conflict_warned)
|
|
|
|
{
|
|
|
|
name_conflict_warned = true;
|
|
|
|
//TODO: may be resolved using advanced font properties?
|
2013-09-18 21:56:57 +00:00
|
|
|
cerr << "Warning: encoding conflict detected in font: " << hex << info.id << dec << endl;
|
2013-02-05 14:07:51 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-11-16 14:04:02 +00:00
|
|
|
ffw_reencode_raw2(cur_mapping2.data(), 256, 0);
|
2013-02-05 14:07:51 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
maxcode = 0xffff;
|
|
|
|
|
|
|
|
if(is_truetype_suffix(suffix))
|
|
|
|
{
|
|
|
|
ffw_reencode_glyph_order();
|
|
|
|
|
|
|
|
GfxCIDFont * _font = dynamic_cast<GfxCIDFont*>(font);
|
|
|
|
|
2013-05-03 18:52:30 +00:00
|
|
|
// To locate CID2GID for the font
|
|
|
|
// as in CairoFontEngine.cc
|
2013-05-03 19:30:18 +00:00
|
|
|
if((code2GID = _font->getCIDToGID()))
|
2013-05-03 18:52:30 +00:00
|
|
|
{
|
|
|
|
// use the mapping stored in _font
|
|
|
|
code2GID_len = _font->getCIDToGIDLen();
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
// use the mapping stored in the file
|
|
|
|
if(FoFiTrueType * fftt = FoFiTrueType::load((char*)filepath.c_str()))
|
|
|
|
{
|
|
|
|
code2GID = _font->getCodeToGIDMap(fftt, &code2GID_len);
|
|
|
|
delete fftt;
|
|
|
|
}
|
|
|
|
}
|
2013-02-05 14:07:51 +00:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2013-05-03 19:29:10 +00:00
|
|
|
// TODO: add an option to load the table?
|
2013-02-05 14:07:51 +00:00
|
|
|
ffw_cidflatten();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Step 2
|
|
|
|
* - map charcode (or GID for CID truetype)
|
|
|
|
*
|
|
|
|
* -> Always map to Unicode for 8bit TrueType fonts and CID fonts
|
|
|
|
*
|
|
|
|
* -> For 8bit nonTruetype fonts:
|
|
|
|
* Try to calculate the correct Unicode value from the glyph names, when collision is detected in ToUnicode Map
|
|
|
|
*
|
|
|
|
* - Fill in the width_list, and set widths accordingly
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
|
|
{
|
2013-05-06 09:25:41 +00:00
|
|
|
string map_filename;
|
2013-05-02 16:51:58 +00:00
|
|
|
ofstream map_outf;
|
|
|
|
if(param.debug)
|
|
|
|
{
|
2013-05-06 09:25:41 +00:00
|
|
|
map_filename = (char*)str_fmt("%s/f%llx.map", param.tmp_dir.c_str(), info.id);
|
|
|
|
tmp_files.add(map_filename);
|
|
|
|
map_outf.open(map_filename);
|
2013-05-02 16:51:58 +00:00
|
|
|
}
|
|
|
|
|
2013-02-05 14:07:51 +00:00
|
|
|
unordered_set<int> codeset;
|
|
|
|
bool name_conflict_warned = false;
|
|
|
|
|
|
|
|
auto ctu = font->getToUnicode();
|
2014-11-16 14:04:02 +00:00
|
|
|
std::fill(cur_mapping.begin(), cur_mapping.end(), -1);
|
|
|
|
std::fill(width_list.begin(), width_list.end(), -1);
|
2013-02-05 14:07:51 +00:00
|
|
|
|
|
|
|
if(code2GID)
|
|
|
|
maxcode = min<int>(maxcode, code2GID_len - 1);
|
|
|
|
|
|
|
|
bool is_truetype = is_truetype_suffix(suffix);
|
|
|
|
int max_key = maxcode;
|
|
|
|
/*
|
|
|
|
* Traverse all possible codes
|
|
|
|
*/
|
|
|
|
bool retried = false; // avoid infinite loop
|
2013-03-30 14:37:20 +00:00
|
|
|
for(int cur_code = 0; cur_code <= maxcode; ++cur_code)
|
2013-02-05 14:07:51 +00:00
|
|
|
{
|
2013-03-30 14:37:20 +00:00
|
|
|
if(!used_map[cur_code])
|
2013-02-05 14:07:51 +00:00
|
|
|
continue;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Skip glyphs without names (only for non-ttf fonts)
|
|
|
|
*/
|
|
|
|
if(!is_truetype && (font_8bit != nullptr)
|
2013-03-30 14:37:20 +00:00
|
|
|
&& (font_8bit->getCharName(cur_code) == nullptr))
|
2013-02-05 14:07:51 +00:00
|
|
|
{
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
2013-03-30 14:37:20 +00:00
|
|
|
int mapped_code = cur_code;
|
2013-02-05 14:07:51 +00:00
|
|
|
if(code2GID)
|
|
|
|
{
|
2013-02-21 08:40:15 +00:00
|
|
|
// for fonts with GID (e.g. TTF) we need to map GIDs instead of codes
|
2013-03-30 14:37:20 +00:00
|
|
|
if((mapped_code = code2GID[cur_code]) == 0) continue;
|
2013-02-05 14:07:51 +00:00
|
|
|
}
|
|
|
|
|
2013-03-30 14:37:20 +00:00
|
|
|
if(mapped_code > max_key)
|
|
|
|
max_key = mapped_code;
|
2013-02-05 14:07:51 +00:00
|
|
|
|
|
|
|
Unicode u, *pu=&u;
|
|
|
|
if(info.use_tounicode)
|
|
|
|
{
|
2013-03-30 14:37:20 +00:00
|
|
|
int n = ctu ? (ctu->mapToUnicode(cur_code, &pu)) : 0;
|
|
|
|
u = check_unicode(pu, n, cur_code, font);
|
2013-02-05 14:07:51 +00:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2013-03-30 14:37:20 +00:00
|
|
|
u = unicode_from_font(cur_code, font);
|
2013-02-05 14:07:51 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
if(codeset.insert(u).second)
|
|
|
|
{
|
2013-03-30 14:37:20 +00:00
|
|
|
cur_mapping[mapped_code] = u;
|
2013-02-05 14:07:51 +00:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
// collision detected
|
2013-04-06 09:01:05 +00:00
|
|
|
if(param.tounicode == 0)
|
2013-02-05 14:07:51 +00:00
|
|
|
{
|
|
|
|
// in auto mode, just drop the tounicode map
|
|
|
|
if(!retried)
|
|
|
|
{
|
|
|
|
cerr << "ToUnicode CMap is not valid and got dropped for font: " << hex << info.id << dec << endl;
|
|
|
|
retried = true;
|
|
|
|
codeset.clear();
|
|
|
|
info.use_tounicode = false;
|
2014-11-16 14:04:02 +00:00
|
|
|
std::fill(cur_mapping.begin(), cur_mapping.end(), -1);
|
|
|
|
std::fill(width_list.begin(), width_list.end(), -1);
|
2013-03-30 14:37:20 +00:00
|
|
|
cur_code = -1;
|
2013-05-02 16:51:58 +00:00
|
|
|
if(param.debug)
|
|
|
|
{
|
2013-05-06 09:25:41 +00:00
|
|
|
map_outf.close();
|
|
|
|
map_outf.open(map_filename);
|
2013-05-02 16:51:58 +00:00
|
|
|
}
|
2013-02-05 14:07:51 +00:00
|
|
|
continue;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if(!name_conflict_warned)
|
|
|
|
{
|
|
|
|
name_conflict_warned = true;
|
|
|
|
//TODO: may be resolved using advanced font properties?
|
|
|
|
cerr << "Warning: encoding confliction detected in font: " << hex << info.id << dec << endl;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
{
|
2013-03-30 14:37:20 +00:00
|
|
|
double cur_width = 0;
|
|
|
|
if(font_8bit)
|
|
|
|
{
|
|
|
|
cur_width = font_8bit->getWidth(cur_code);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
char buf[2];
|
|
|
|
buf[0] = (cur_code >> 8) & 0xff;
|
|
|
|
buf[1] = (cur_code & 0xff);
|
|
|
|
cur_width = font_cid->getWidth(buf, 2) ;
|
|
|
|
}
|
|
|
|
|
2013-09-20 08:35:19 +00:00
|
|
|
cur_width /= info.font_size_scale;
|
2013-09-19 14:02:12 +00:00
|
|
|
|
2013-03-30 14:37:20 +00:00
|
|
|
if(u == ' ')
|
|
|
|
{
|
2013-07-06 02:10:41 +00:00
|
|
|
/*
|
|
|
|
* Internet Explorer will ignore `word-spacing` if
|
|
|
|
* the width of the 'space' glyph is 0
|
|
|
|
*
|
|
|
|
* space_width==0 often means no spaces are used in the PDF
|
|
|
|
* so setting it to be 0.001 should be safe
|
|
|
|
*/
|
|
|
|
if(equal(cur_width, 0))
|
|
|
|
cur_width = 0.001;
|
|
|
|
|
2013-03-30 14:37:20 +00:00
|
|
|
info.space_width = cur_width;
|
2013-07-06 02:10:41 +00:00
|
|
|
has_space = true;
|
2013-03-30 14:37:20 +00:00
|
|
|
}
|
2013-07-06 02:10:41 +00:00
|
|
|
|
|
|
|
width_list[mapped_code] = (int)floor(cur_width * info.em_size + 0.5);
|
2013-02-05 14:07:51 +00:00
|
|
|
}
|
2013-05-02 16:51:58 +00:00
|
|
|
|
|
|
|
if(param.debug)
|
|
|
|
{
|
|
|
|
map_outf << hex << cur_code << ' ' << mapped_code << ' ' << u << endl;
|
|
|
|
}
|
2013-02-05 14:07:51 +00:00
|
|
|
}
|
|
|
|
|
2014-11-16 14:04:02 +00:00
|
|
|
ffw_set_widths(width_list.data(), max_key + 1, param.stretch_narrow_glyph, param.squeeze_wide_glyph);
|
2013-02-05 14:07:51 +00:00
|
|
|
|
2014-11-16 14:04:02 +00:00
|
|
|
ffw_reencode_raw(cur_mapping.data(), max_key + 1, 1);
|
2013-02-05 14:07:51 +00:00
|
|
|
|
2013-02-21 09:55:58 +00:00
|
|
|
// In some space offsets in HTML, we insert a ' ' there in order to improve text copy&paste
|
|
|
|
// We need to make sure that ' ' is in the font, otherwise it would be very ugly if you select the text
|
|
|
|
// Might be a problem if ' ' is in the font, but not empty
|
2013-02-05 14:07:51 +00:00
|
|
|
if(!has_space)
|
|
|
|
{
|
2013-03-30 14:37:20 +00:00
|
|
|
if(font_8bit)
|
|
|
|
{
|
|
|
|
info.space_width = font_8bit->getWidth(' ');
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
char buf[2] = {0, ' '};
|
|
|
|
info.space_width = font_cid->getWidth(buf, 2);
|
|
|
|
}
|
2013-09-20 08:35:19 +00:00
|
|
|
info.space_width /= info.font_size_scale;
|
2013-09-19 14:02:12 +00:00
|
|
|
|
2013-07-06 02:10:41 +00:00
|
|
|
/* See comments above */
|
|
|
|
if(equal(info.space_width,0))
|
|
|
|
info.space_width = 0.001;
|
|
|
|
|
2013-03-21 04:18:26 +00:00
|
|
|
ffw_add_empty_char((int32_t)' ', (int)floor(info.space_width * info.em_size + 0.5));
|
2013-05-01 15:56:20 +00:00
|
|
|
if(param.debug)
|
|
|
|
{
|
2013-05-02 17:47:55 +00:00
|
|
|
cerr << "Missing space width in font " << hex << info.id << ": set to " << dec << info.space_width << endl;
|
2013-05-01 15:56:20 +00:00
|
|
|
}
|
2013-02-05 14:07:51 +00:00
|
|
|
}
|
|
|
|
|
2013-07-01 02:19:30 +00:00
|
|
|
if(param.debug)
|
|
|
|
{
|
|
|
|
cerr << "space width: " << info.space_width << endl;
|
|
|
|
}
|
|
|
|
|
2013-02-05 14:07:51 +00:00
|
|
|
if(ctu)
|
|
|
|
ctu->decRefCnt();
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Step 3
|
|
|
|
* Generate the font as desired
|
|
|
|
*/
|
2013-11-08 05:53:42 +00:00
|
|
|
|
|
|
|
// Reencode to Unicode Full such that FontForge won't ditch unicode values larger than 0xFFFF
|
2013-07-05 02:52:31 +00:00
|
|
|
ffw_reencode_unicode_full();
|
|
|
|
|
2013-11-08 05:53:42 +00:00
|
|
|
// Due to a bug of Fontforge about pfa -> woff conversion
|
|
|
|
// we always generate TTF first, instead of the format specified by user
|
|
|
|
string cur_tmp_fn = (char*)str_fmt("%s/__tmp_font1.%s", param.tmp_dir.c_str(), "ttf");
|
2013-02-05 14:07:51 +00:00
|
|
|
tmp_files.add(cur_tmp_fn);
|
2013-11-08 05:53:42 +00:00
|
|
|
string other_tmp_fn = (char*)str_fmt("%s/__tmp_font2.%s", param.tmp_dir.c_str(), "ttf");
|
2013-02-05 14:07:51 +00:00
|
|
|
tmp_files.add(other_tmp_fn);
|
|
|
|
|
|
|
|
ffw_save(cur_tmp_fn.c_str());
|
|
|
|
|
|
|
|
ffw_close();
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Step 4
|
|
|
|
* Font Hinting
|
|
|
|
*/
|
|
|
|
bool hinted = false;
|
|
|
|
|
|
|
|
// Call external hinting program if specified
|
2013-04-06 09:01:05 +00:00
|
|
|
if(param.external_hint_tool != "")
|
2013-02-05 14:07:51 +00:00
|
|
|
{
|
2013-04-06 09:01:05 +00:00
|
|
|
hinted = (system((char*)str_fmt("%s \"%s\" \"%s\"", param.external_hint_tool.c_str(), cur_tmp_fn.c_str(), other_tmp_fn.c_str())) == 0);
|
2013-02-05 14:07:51 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// Call internal hinting procedure if specified
|
2013-04-06 09:01:05 +00:00
|
|
|
if((!hinted) && (param.auto_hint))
|
2013-02-05 14:07:51 +00:00
|
|
|
{
|
|
|
|
ffw_load_font(cur_tmp_fn.c_str());
|
|
|
|
ffw_auto_hint();
|
|
|
|
ffw_save(other_tmp_fn.c_str());
|
|
|
|
ffw_close();
|
|
|
|
hinted = true;
|
|
|
|
}
|
|
|
|
|
|
|
|
if(hinted)
|
|
|
|
{
|
|
|
|
swap(cur_tmp_fn, other_tmp_fn);
|
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Step 5
|
2014-07-13 23:59:30 +00:00
|
|
|
* Generate the font, load the metrics and set the embedding bits (fstype)
|
2013-02-21 08:40:15 +00:00
|
|
|
*
|
|
|
|
* Ascent/Descent are not used in PDF, and the values in PDF may be wrong or inconsistent (there are 3 sets of them)
|
2013-02-25 16:40:35 +00:00
|
|
|
* We need to reload in order to retrieve/fix accurate ascent/descent, some info won't be written to the font by fontforge until saved.
|
2013-02-05 14:07:51 +00:00
|
|
|
*/
|
2013-09-18 12:24:48 +00:00
|
|
|
string fn = (char*)str_fmt("%s/f%llx.%s",
|
2013-05-26 23:43:26 +00:00
|
|
|
(param.embed_font ? param.tmp_dir : param.dest_dir).c_str(),
|
2013-09-18 12:24:48 +00:00
|
|
|
info.id, param.font_format.c_str());
|
2013-02-05 14:07:51 +00:00
|
|
|
|
2013-05-26 23:43:26 +00:00
|
|
|
if(param.embed_font)
|
2013-02-05 14:07:51 +00:00
|
|
|
tmp_files.add(fn);
|
|
|
|
|
|
|
|
ffw_load_font(cur_tmp_fn.c_str());
|
2013-09-20 08:35:19 +00:00
|
|
|
ffw_fix_metric();
|
|
|
|
ffw_get_metric(&info.ascent, &info.descent);
|
2013-07-02 00:04:20 +00:00
|
|
|
if(param.override_fstype)
|
|
|
|
ffw_override_fstype();
|
2013-02-05 14:07:51 +00:00
|
|
|
ffw_save(fn.c_str());
|
|
|
|
|
|
|
|
ffw_close();
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2012-09-04 15:33:15 +00:00
|
|
|
const FontInfo * HTMLRenderer::install_font(GfxFont * font)
|
2012-08-14 06:35:55 +00:00
|
|
|
{
|
|
|
|
assert(sizeof(long long) == 2*sizeof(int));
|
|
|
|
|
2012-09-06 16:58:23 +00:00
|
|
|
long long fn_id = (font == nullptr) ? 0 : hash_ref(font->getID());
|
2012-08-14 06:35:55 +00:00
|
|
|
|
2013-03-31 04:53:44 +00:00
|
|
|
auto iter = font_info_map.find(fn_id);
|
|
|
|
if(iter != font_info_map.end())
|
2012-09-04 15:33:15 +00:00
|
|
|
return &(iter->second);
|
2012-08-14 06:35:55 +00:00
|
|
|
|
2013-03-31 04:53:44 +00:00
|
|
|
long long new_fn_id = font_info_map.size();
|
2012-08-14 06:35:55 +00:00
|
|
|
|
2013-03-31 04:53:44 +00:00
|
|
|
auto cur_info_iter = font_info_map.insert(make_pair(fn_id, FontInfo())).first;
|
2013-01-19 12:13:31 +00:00
|
|
|
|
|
|
|
FontInfo & new_font_info = cur_info_iter->second;
|
|
|
|
new_font_info.id = new_fn_id;
|
|
|
|
new_font_info.use_tounicode = true;
|
2013-09-20 12:06:06 +00:00
|
|
|
new_font_info.font_size_scale = 1.0;
|
2012-08-14 06:35:55 +00:00
|
|
|
|
|
|
|
if(font == nullptr)
|
|
|
|
{
|
2013-03-20 15:46:58 +00:00
|
|
|
new_font_info.em_size = 0;
|
|
|
|
new_font_info.space_width = 0;
|
2013-01-19 12:13:31 +00:00
|
|
|
new_font_info.ascent = 0;
|
|
|
|
new_font_info.descent = 0;
|
|
|
|
new_font_info.is_type3 = false;
|
|
|
|
|
2012-08-14 06:35:55 +00:00
|
|
|
export_remote_default_font(new_fn_id);
|
2013-01-19 12:13:31 +00:00
|
|
|
|
|
|
|
return &(new_font_info);
|
2012-08-14 06:35:55 +00:00
|
|
|
}
|
|
|
|
|
2013-01-19 12:13:31 +00:00
|
|
|
new_font_info.ascent = font->getAscent();
|
|
|
|
new_font_info.descent = font->getDescent();
|
|
|
|
new_font_info.is_type3 = (font->getType() == fontType3);
|
2012-08-30 16:25:05 +00:00
|
|
|
|
2013-04-06 09:01:05 +00:00
|
|
|
if(param.debug)
|
2012-08-14 06:35:55 +00:00
|
|
|
{
|
2013-07-06 01:16:16 +00:00
|
|
|
cerr << "Install font " << hex << new_fn_id << dec
|
|
|
|
<< ": (" << (font->getID()->num) << ' ' << (font->getID()->gen) << ") "
|
|
|
|
<< (font->getName() ? font->getName()->getCString() : "")
|
|
|
|
<< endl;
|
2012-08-14 06:35:55 +00:00
|
|
|
}
|
|
|
|
|
2013-09-18 21:56:57 +00:00
|
|
|
if(new_font_info.is_type3)
|
2013-09-18 18:41:00 +00:00
|
|
|
{
|
2013-09-18 21:56:57 +00:00
|
|
|
#if ENABLE_SVG
|
|
|
|
if(param.process_type3)
|
|
|
|
{
|
|
|
|
install_embedded_font(font, new_font_info);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
export_remote_default_font(new_fn_id);
|
|
|
|
}
|
|
|
|
#else
|
2012-08-14 09:13:29 +00:00
|
|
|
cerr << "Type 3 fonts are unsupported and will be rendered as Image" << endl;
|
2012-08-14 06:35:55 +00:00
|
|
|
export_remote_default_font(new_fn_id);
|
2013-09-18 21:56:57 +00:00
|
|
|
#endif
|
2013-05-02 17:47:55 +00:00
|
|
|
return &new_font_info;
|
2012-08-14 06:35:55 +00:00
|
|
|
}
|
|
|
|
if(font->getWMode()) {
|
2012-08-14 09:13:29 +00:00
|
|
|
cerr << "Writing mode is unsupported and will be rendered as Image" << endl;
|
2012-08-14 06:35:55 +00:00
|
|
|
export_remote_default_font(new_fn_id);
|
2013-05-02 17:47:55 +00:00
|
|
|
return &new_font_info;
|
2012-08-14 06:35:55 +00:00
|
|
|
}
|
|
|
|
|
2013-05-03 19:29:10 +00:00
|
|
|
/*
|
|
|
|
* The 2nd parameter of locateFont should be true only for PS
|
|
|
|
* which does not make much sense in our case
|
2016-01-01 11:02:27 +00:00
|
|
|
* If we specify gFalse here, font_loc->locType cannot be gfxFontLocResident
|
2013-05-03 19:29:10 +00:00
|
|
|
*/
|
2015-02-16 08:56:43 +00:00
|
|
|
if(auto * font_loc = font->locateFont(xref, nullptr))
|
2012-08-14 06:35:55 +00:00
|
|
|
{
|
|
|
|
switch(font_loc -> locType)
|
|
|
|
{
|
|
|
|
case gfxFontLocEmbedded:
|
2013-05-02 17:47:55 +00:00
|
|
|
install_embedded_font(font, new_font_info);
|
2012-08-14 06:35:55 +00:00
|
|
|
break;
|
2013-05-03 19:29:10 +00:00
|
|
|
case gfxFontLocResident:
|
|
|
|
std::cerr << "Warning: Base 14 fonts should not be specially handled now. Please report a bug!" << std::endl;
|
|
|
|
/* fall through */
|
2012-08-14 06:35:55 +00:00
|
|
|
case gfxFontLocExternal:
|
2013-05-02 17:47:55 +00:00
|
|
|
install_external_font(font, new_font_info);
|
2012-08-14 06:35:55 +00:00
|
|
|
break;
|
|
|
|
default:
|
2012-08-14 09:13:29 +00:00
|
|
|
cerr << "TODO: other font loc" << endl;
|
2012-08-14 06:35:55 +00:00
|
|
|
export_remote_default_font(new_fn_id);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
delete font_loc;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
export_remote_default_font(new_fn_id);
|
|
|
|
}
|
|
|
|
|
2013-09-18 21:56:57 +00:00
|
|
|
return &new_font_info;
|
2012-08-14 06:35:55 +00:00
|
|
|
}
|
|
|
|
|
2012-08-31 07:27:17 +00:00
|
|
|
void HTMLRenderer::install_embedded_font(GfxFont * font, FontInfo & info)
|
2012-08-14 06:35:55 +00:00
|
|
|
{
|
2013-09-19 14:02:12 +00:00
|
|
|
auto path = dump_embedded_font(font, info);
|
2012-09-09 17:27:32 +00:00
|
|
|
|
2012-08-31 07:27:17 +00:00
|
|
|
if(path != "")
|
2012-08-26 15:56:38 +00:00
|
|
|
{
|
2012-08-31 07:27:17 +00:00
|
|
|
embed_font(path, font, info);
|
2013-09-18 12:24:48 +00:00
|
|
|
export_remote_font(info, param.font_format, font);
|
2012-08-26 15:56:38 +00:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2012-08-31 07:27:17 +00:00
|
|
|
export_remote_default_font(info.id);
|
2012-08-27 16:14:11 +00:00
|
|
|
}
|
2012-08-31 07:27:17 +00:00
|
|
|
}
|
2012-08-27 16:14:11 +00:00
|
|
|
|
2012-08-31 07:50:14 +00:00
|
|
|
void HTMLRenderer::install_external_font(GfxFont * font, FontInfo & info)
|
2012-08-14 06:35:55 +00:00
|
|
|
{
|
2012-08-14 09:13:29 +00:00
|
|
|
string fontname(font->getName()->getCString());
|
2012-08-14 06:35:55 +00:00
|
|
|
|
|
|
|
// resolve bad encodings in GB
|
|
|
|
auto iter = GB_ENCODED_FONT_NAME_MAP.find(fontname);
|
|
|
|
if(iter != GB_ENCODED_FONT_NAME_MAP.end())
|
|
|
|
{
|
|
|
|
fontname = iter->second;
|
2012-08-14 09:13:29 +00:00
|
|
|
cerr << "Warning: workaround for font names in bad encodings." << endl;
|
2012-08-14 06:35:55 +00:00
|
|
|
}
|
2012-08-31 09:06:19 +00:00
|
|
|
|
2015-02-16 08:56:43 +00:00
|
|
|
GfxFontLoc * localfontloc = font->locateFont(xref, nullptr);
|
2012-08-31 09:06:19 +00:00
|
|
|
|
2013-04-06 09:01:05 +00:00
|
|
|
if(param.embed_external_font)
|
2012-08-31 09:06:19 +00:00
|
|
|
{
|
2012-08-31 09:47:37 +00:00
|
|
|
if(localfontloc != nullptr)
|
|
|
|
{
|
2012-09-09 16:21:46 +00:00
|
|
|
embed_font(string(localfontloc->path->getCString()), font, info);
|
2013-09-18 12:24:48 +00:00
|
|
|
export_remote_font(info, param.font_format, font);
|
2012-08-31 09:47:37 +00:00
|
|
|
delete localfontloc;
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2012-09-07 16:38:41 +00:00
|
|
|
cerr << "Cannot embed external font: f" << hex << info.id << dec << ' ' << fontname << endl;
|
2012-08-31 09:47:37 +00:00
|
|
|
// fallback to exporting by name
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// still try to get an idea of read ascent/descent
|
|
|
|
if(localfontloc != nullptr)
|
|
|
|
{
|
|
|
|
// fill in ascent/descent only, do not embed
|
2012-09-09 16:21:46 +00:00
|
|
|
embed_font(string(localfontloc->path->getCString()), font, info, true);
|
2012-08-31 09:47:37 +00:00
|
|
|
delete localfontloc;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
info.ascent = font->getAscent();
|
|
|
|
info.descent = font->getDescent();
|
2012-08-31 09:06:19 +00:00
|
|
|
}
|
2012-08-14 06:35:55 +00:00
|
|
|
|
2012-08-31 07:50:14 +00:00
|
|
|
export_local_font(info, font, fontname, "");
|
2012-08-14 06:35:55 +00:00
|
|
|
}
|
2013-02-05 14:00:23 +00:00
|
|
|
|
2013-09-18 12:24:48 +00:00
|
|
|
void HTMLRenderer::export_remote_font(const FontInfo & info, const string & format, GfxFont * font)
|
2013-02-05 14:00:23 +00:00
|
|
|
{
|
2013-09-18 12:24:48 +00:00
|
|
|
string css_font_format;
|
|
|
|
if(format == "ttf")
|
2013-02-05 14:00:23 +00:00
|
|
|
{
|
2013-09-18 12:24:48 +00:00
|
|
|
css_font_format = "truetype";
|
2013-02-05 14:00:23 +00:00
|
|
|
}
|
2013-09-18 12:24:48 +00:00
|
|
|
else if(format == "otf")
|
2013-02-05 14:00:23 +00:00
|
|
|
{
|
2013-09-18 12:24:48 +00:00
|
|
|
css_font_format = "opentype";
|
2013-02-05 14:00:23 +00:00
|
|
|
}
|
2013-09-18 12:24:48 +00:00
|
|
|
else if(format == "woff")
|
2013-02-05 14:00:23 +00:00
|
|
|
{
|
2013-09-18 12:24:48 +00:00
|
|
|
css_font_format = "woff";
|
2013-02-05 14:00:23 +00:00
|
|
|
}
|
2013-09-18 12:24:48 +00:00
|
|
|
else if(format == "eot")
|
2013-02-05 14:00:23 +00:00
|
|
|
{
|
2013-09-18 12:24:48 +00:00
|
|
|
css_font_format = "embedded-opentype";
|
2013-02-05 14:00:23 +00:00
|
|
|
}
|
2013-09-18 12:24:48 +00:00
|
|
|
else if(format == "svg")
|
2013-02-05 14:00:23 +00:00
|
|
|
{
|
2013-09-18 12:24:48 +00:00
|
|
|
css_font_format = "svg";
|
2013-02-05 14:00:23 +00:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2013-09-18 12:24:48 +00:00
|
|
|
throw string("Warning: unknown font format: ") + format;
|
2013-02-05 14:00:23 +00:00
|
|
|
}
|
2013-09-18 12:24:48 +00:00
|
|
|
auto iter = FORMAT_MIME_TYPE_MAP.find(format);
|
|
|
|
if(iter == FORMAT_MIME_TYPE_MAP.end())
|
|
|
|
{
|
|
|
|
throw string("Warning: unknown font format: ") + format;
|
|
|
|
}
|
|
|
|
string mime_type = iter->second;
|
2013-02-05 14:00:23 +00:00
|
|
|
|
|
|
|
f_css.fs << "@font-face{"
|
2013-02-28 07:59:14 +00:00
|
|
|
<< "font-family:" << CSS::FONT_FAMILY_CN << info.id << ";"
|
2013-02-05 14:00:23 +00:00
|
|
|
<< "src:url(";
|
|
|
|
|
|
|
|
{
|
2013-09-18 12:24:48 +00:00
|
|
|
auto fn = str_fmt("f%llx.%s", info.id, format.c_str());
|
2013-05-26 23:43:26 +00:00
|
|
|
if(param.embed_font)
|
2013-02-05 14:00:23 +00:00
|
|
|
{
|
2013-04-06 09:01:05 +00:00
|
|
|
auto path = param.tmp_dir + "/" + (char*)fn;
|
2013-02-05 14:00:23 +00:00
|
|
|
ifstream fin(path, ifstream::binary);
|
|
|
|
if(!fin)
|
|
|
|
throw "Cannot locate font file: " + path;
|
2013-04-06 15:41:58 +00:00
|
|
|
f_css.fs << "'data:" + mime_type + ";base64," << Base64Stream(fin) << "'";
|
2013-02-05 14:00:23 +00:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
f_css.fs << (char*)fn;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
f_css.fs << ")"
|
2013-09-18 12:24:48 +00:00
|
|
|
<< "format(\"" << css_font_format << "\");"
|
2013-02-05 14:00:23 +00:00
|
|
|
<< "}" // end of @font-face
|
2013-02-28 07:59:14 +00:00
|
|
|
<< "." << CSS::FONT_FAMILY_CN << info.id << "{"
|
|
|
|
<< "font-family:" << CSS::FONT_FAMILY_CN << info.id << ";"
|
2013-02-05 14:00:23 +00:00
|
|
|
<< "line-height:" << round(info.ascent - info.descent) << ";"
|
|
|
|
<< "font-style:normal;"
|
|
|
|
<< "font-weight:normal;"
|
|
|
|
<< "visibility:visible;"
|
2013-02-27 18:52:00 +00:00
|
|
|
<< "}"
|
2013-02-05 14:00:23 +00:00
|
|
|
<< endl;
|
|
|
|
}
|
|
|
|
|
|
|
|
static string general_font_family(GfxFont * font)
|
|
|
|
{
|
|
|
|
if(font->isFixedWidth())
|
|
|
|
return "monospace";
|
|
|
|
else if (font->isSerif())
|
|
|
|
return "serif";
|
|
|
|
else
|
|
|
|
return "sans-serif";
|
|
|
|
}
|
|
|
|
|
|
|
|
// TODO: this function is called when some font is unable to process, may use the name there as a hint
|
|
|
|
void HTMLRenderer::export_remote_default_font(long long fn_id)
|
|
|
|
{
|
2013-02-28 07:59:14 +00:00
|
|
|
f_css.fs << "." << CSS::FONT_FAMILY_CN << fn_id << "{font-family:sans-serif;visibility:hidden;}" << endl;
|
2013-02-05 14:00:23 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void HTMLRenderer::export_local_font(const FontInfo & info, GfxFont * font, const string & original_font_name, const string & cssfont)
|
|
|
|
{
|
2013-02-28 07:59:14 +00:00
|
|
|
f_css.fs << "." << CSS::FONT_FAMILY_CN << info.id << "{";
|
2013-02-05 14:00:23 +00:00
|
|
|
f_css.fs << "font-family:" << ((cssfont == "") ? (original_font_name + "," + general_font_family(font)) : cssfont) << ";";
|
|
|
|
|
|
|
|
string fn = original_font_name;
|
2014-11-16 14:04:02 +00:00
|
|
|
for(auto & c : fn)
|
|
|
|
c = tolower(c);
|
2013-02-05 14:00:23 +00:00
|
|
|
|
|
|
|
if(font->isBold() || (fn.find("bold") != string::npos))
|
|
|
|
f_css.fs << "font-weight:bold;";
|
|
|
|
else
|
|
|
|
f_css.fs << "font-weight:normal;";
|
|
|
|
|
|
|
|
if(fn.find("oblique") != string::npos)
|
|
|
|
f_css.fs << "font-style:oblique;";
|
|
|
|
else if(font->isItalic() || (fn.find("italic") != string::npos))
|
|
|
|
f_css.fs << "font-style:italic;";
|
|
|
|
else
|
|
|
|
f_css.fs << "font-style:normal;";
|
|
|
|
|
|
|
|
f_css.fs << "line-height:" << round(info.ascent - info.descent) << ";";
|
|
|
|
|
|
|
|
f_css.fs << "visibility:visible;";
|
|
|
|
|
|
|
|
f_css.fs << "}" << endl;
|
|
|
|
}
|
|
|
|
|
2013-02-27 18:52:00 +00:00
|
|
|
} //namespace pdf2htmlEX
|