1
0
mirror of https://github.com/pdf2htmlEX/pdf2htmlEX.git synced 2024-09-19 13:50:06 +00:00
pdf2htmlEX/src/HTMLRenderer/font.cc

329 lines
9.1 KiB
C++
Raw Normal View History

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>
#include <cmath>
#include <algorithm>
2013-02-05 14:00:23 +00:00
#include <sstream>
#include <cctype>
2012-08-14 06:35:55 +00:00
#include <GlobalParams.h>
2012-08-31 09:06:19 +00:00
#include "Param.h"
2012-08-14 06:35:55 +00:00
#include "HTMLRenderer.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:00:23 +00:00
#include "util/base64stream.h"
2012-08-26 15:56:38 +00:00
2012-09-12 15:26:14 +00:00
namespace pdf2htmlEX {
2012-08-31 15:14:05 +00:00
using std::abs;
2012-11-29 10:28:07 +00:00
using std::cerr;
using std::endl;
2012-08-31 15:14:05 +00:00
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
auto iter = font_name_map.find(fn_id);
if(iter != font_name_map.end())
2012-09-04 15:33:15 +00:00
return &(iter->second);
2012-08-14 06:35:55 +00:00
long long new_fn_id = font_name_map.size();
auto cur_info_iter = font_name_map.insert(make_pair(fn_id, FontInfo())).first;
FontInfo & new_font_info = cur_info_iter->second;
new_font_info.id = new_fn_id;
new_font_info.use_tounicode = true;
2012-08-14 06:35:55 +00:00
if(font == nullptr)
{
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);
return &(new_font_info);
2012-08-14 06:35:55 +00:00
}
new_font_info.ascent = font->getAscent();
new_font_info.descent = font->getDescent();
new_font_info.is_type3 = (font->getType() == fontType3);
2012-08-14 06:35:55 +00:00
if(param->debug)
{
2012-09-07 16:38:41 +00:00
cerr << "Install font: (" << (font->getID()->num) << ' ' << (font->getID()->gen) << ") -> " << "f" << hex << new_fn_id << dec << endl;
2012-08-14 06:35:55 +00:00
}
if(font->getType() == fontType3) {
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);
2012-09-04 15:33:15 +00:00
return &(cur_info_iter->second);
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);
2012-09-04 15:33:15 +00:00
return &(cur_info_iter->second);
2012-08-14 06:35:55 +00:00
}
auto * font_loc = font->locateFont(xref, gTrue);
if(font_loc != nullptr)
{
switch(font_loc -> locType)
{
case gfxFontLocEmbedded:
install_embedded_font(font, cur_info_iter->second);
2012-08-14 06:35:55 +00:00
break;
case gfxFontLocExternal:
install_external_font(font, cur_info_iter->second);
2012-08-14 06:35:55 +00:00
break;
case gfxFontLocResident:
install_base_font(font, font_loc, cur_info_iter->second);
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);
}
2012-09-04 15:33:15 +00:00
return &(cur_info_iter->second);
2012-08-14 06:35:55 +00:00
}
void HTMLRenderer::install_embedded_font(GfxFont * font, FontInfo & info)
2012-08-14 06:35:55 +00:00
{
auto path = dump_embedded_font(font, info.id);
2012-09-09 17:27:32 +00:00
if(path != "")
2012-08-26 15:56:38 +00:00
{
embed_font(path, font, info);
export_remote_font(info, param->font_suffix, font);
2012-08-26 15:56:38 +00:00
}
else
{
export_remote_default_font(info.id);
}
}
void HTMLRenderer::install_base_font(GfxFont * font, GfxFontLoc * font_loc, FontInfo & info)
{
string psname(font_loc->path->getCString());
string basename = psname.substr(0, psname.find('-'));
GfxFontLoc * localfontloc = font->locateFont(xref, gFalse);
if(param->embed_base_font)
2012-08-30 15:36:30 +00:00
{
if(localfontloc != nullptr)
{
embed_font(localfontloc->path->getCString(), font, info);
export_remote_font(info, param->font_suffix, font);
delete localfontloc;
return;
}
else
{
2012-09-07 16:38:41 +00:00
cerr << "Cannot embed base font: f" << hex << info.id << dec << ' ' << psname << endl;
// fallback to exporting by name
}
2012-08-30 15:36:30 +00:00
}
2012-08-14 06:35:55 +00:00
string cssfont;
auto iter = BASE_14_FONT_CSS_FONT_MAP.find(basename);
if(iter == BASE_14_FONT_CSS_FONT_MAP.end())
{
2012-08-14 09:13:29 +00:00
cerr << "PS Font: " << basename << " not found in the base 14 font map" << endl;
2012-08-14 06:35:55 +00:00
cssfont = "";
}
else
cssfont = iter->second;
// 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);
delete localfontloc;
}
else
{
info.ascent = font->getAscent();
info.descent = font->getDescent();
}
export_local_font(info, font, psname, cssfont);
2012-08-14 06:35:55 +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
GfxFontLoc * localfontloc = font->locateFont(xref, gFalse);
2012-08-31 09:06:19 +00:00
if(param->embed_external_font)
2012-08-31 09:06:19 +00:00
{
if(localfontloc != nullptr)
{
2012-09-09 16:21:46 +00:00
embed_font(string(localfontloc->path->getCString()), font, info);
export_remote_font(info, param->font_suffix, font);
delete localfontloc;
return;
}
else
{
2012-09-07 16:38:41 +00:00
cerr << "Cannot embed external font: f" << hex << info.id << dec << ' ' << fontname << endl;
// 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);
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
export_local_font(info, font, fontname, "");
2012-08-14 06:35:55 +00:00
}
2013-02-05 14:00:23 +00:00
void HTMLRenderer::export_remote_font(const FontInfo & info, const string & suffix, GfxFont * font)
{
string mime_type, format;
if(suffix == ".ttf")
{
format = "truetype";
mime_type = "application/x-font-ttf";
}
else if(suffix == ".otf")
{
format = "opentype";
mime_type = "application/x-font-otf";
}
else if(suffix == ".woff")
{
format = "woff";
mime_type = "application/font-woff";
}
else if(suffix == ".eot")
{
format = "embedded-opentype";
mime_type = "application/vnd.ms-fontobject";
}
else if(suffix == ".svg")
{
format = "svg";
mime_type = "image/svg+xml";
}
else
{
cerr << "Warning: unknown font suffix: " << suffix << endl;
}
f_css.fs << "@font-face{"
<< "font-family:f" << info.id << ";"
<< "src:url(";
{
auto fn = str_fmt("f%llx%s", info.id, suffix.c_str());
if(param->single_html)
{
auto path = param->tmp_dir + "/" + (char*)fn;
ifstream fin(path, ifstream::binary);
if(!fin)
throw "Cannot locate font file: " + path;
f_css.fs << "'data:font/" + mime_type + ";base64," << base64stream(fin) << "'";
}
else
{
f_css.fs << (char*)fn;
}
}
f_css.fs << ")"
<< "format(\"" << format << "\");"
<< "}" // end of @font-face
<< ".f" << info.id << "{"
<< "font-family:f" << info.id << ";"
<< "line-height:" << round(info.ascent - info.descent) << ";"
<< "font-style:normal;"
<< "font-weight:normal;"
<< "visibility:visible;"
<< "}" // end of .f
<< 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)
{
f_css.fs << ".f" << fn_id << "{font-family:sans-serif;visibility:hidden;}" << endl;
}
void HTMLRenderer::export_local_font(const FontInfo & info, GfxFont * font, const string & original_font_name, const string & cssfont)
{
f_css.fs << ".f" << info.id << "{";
f_css.fs << "font-family:" << ((cssfont == "") ? (original_font_name + "," + general_font_family(font)) : cssfont) << ";";
string fn = original_font_name;
for(auto iter = fn.begin(); iter != fn.end(); ++iter)
*iter = tolower(*iter);
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;
}
} //namespace pdf2hmlEX