1
0
mirror of https://github.com/pdf2htmlEX/pdf2htmlEX.git synced 2024-09-17 21:16:03 +00:00
pdf2htmlEX/src/HTMLRenderer/text.cc

491 lines
13 KiB
C++
Raw Normal View History

2012-08-14 08:23:15 +00:00
/*
* text.cc
2012-08-14 08:23:15 +00:00
*
* Handling text & font, and relative stuffs
2012-08-14 08:23:15 +00:00
*
* by WangLu
* 2012.08.14
*/
#include <iostream>
2012-08-20 21:48:21 +00:00
#include <algorithm>
#include <unordered_set>
2012-09-07 17:18:15 +00:00
#include <cctype>
2012-08-14 08:23:15 +00:00
#include <CharCodeToUnicode.h>
#include <fofi/FoFiTrueType.h>
#include "ff/ff.h"
2012-08-14 08:23:15 +00:00
#include "HTMLRenderer.h"
2012-08-14 09:13:29 +00:00
#include "namespace.h"
#include "config.h"
2012-08-14 08:23:15 +00:00
using std::unordered_set;
2012-09-05 16:07:21 +00:00
using std::min;
2012-09-06 07:09:47 +00:00
using std::all_of;
2012-08-20 21:48:21 +00:00
2012-09-09 16:21:46 +00:00
string HTMLRenderer::dump_embedded_font (GfxFont * font, long long fn_id)
2012-08-14 08:23:15 +00:00
{
Object obj, obj1, obj2;
Object font_obj, font_obj2, fontdesc_obj;
string suffix;
2012-09-09 16:21:46 +00:00
string filepath;
2012-08-14 08:23:15 +00:00
try
{
// mupdf consulted
string subtype;
2012-08-14 08:23:15 +00:00
auto * id = font->getID();
2012-08-14 08:23:15 +00:00
Object ref_obj;
ref_obj.initRef(id->num, id->gen);
ref_obj.fetch(xref, &font_obj);
ref_obj.free();
2012-08-14 08:23:15 +00:00
if(!font_obj.isDict())
2012-08-14 08:23:15 +00:00
{
cerr << "Font object is not a dictionary" << endl;
throw 0;
2012-08-14 08:23:15 +00:00
}
Dict * dict = font_obj.getDict();
if(dict->lookup("DescendantFonts", &font_obj2)->isArray())
{
if(font_obj2.arrayGetLength() == 0)
{
cerr << "Warning: empty DescendantFonts array" << endl;
}
else
2012-08-14 08:23:15 +00:00
{
if(font_obj2.arrayGetLength() > 1)
cerr << "TODO: multiple entries in DescendantFonts array" << endl;
if(font_obj2.arrayGet(0, &obj2)->isDict())
{
dict = obj2.getDict();
}
2012-08-14 08:23:15 +00:00
}
}
if(!dict->lookup("FontDescriptor", &fontdesc_obj)->isDict())
{
cerr << "Cannot find FontDescriptor " << endl;
throw 0;
}
2012-08-14 08:23:15 +00:00
dict = fontdesc_obj.getDict();
if(dict->lookup("FontFile3", &obj)->isStream())
2012-08-14 08:23:15 +00:00
{
if(obj.streamGetDict()->lookup("Subtype", &obj1)->isName())
2012-08-14 08:23:15 +00:00
{
subtype = obj1.getName();
if(subtype == "Type1C")
{
suffix = ".cff";
}
else if (subtype == "CIDFontType0C")
{
suffix = ".cid";
}
else
{
cerr << "Unknown subtype: " << subtype << endl;
throw 0;
}
2012-08-14 08:23:15 +00:00
}
else
{
cerr << "Invalid subtype in font descriptor" << endl;
throw 0;
2012-08-14 08:23:15 +00:00
}
}
else if (dict->lookup("FontFile2", &obj)->isStream())
{
suffix = ".ttf";
}
else if (dict->lookup("FontFile", &obj)->isStream())
{
suffix = ".pfa";
}
2012-08-14 08:23:15 +00:00
else
{
cerr << "Cannot find FontFile for dump" << endl;
throw 0;
2012-08-14 08:23:15 +00:00
}
if(suffix == "")
{
cerr << "Font type unrecognized" << endl;
throw 0;
}
obj.streamReset();
2012-08-14 08:23:15 +00:00
2012-09-09 17:27:32 +00:00
filepath = (char*)str_fmt("%s/f%llx%s", tmp_dir.c_str(), fn_id, suffix.c_str());
add_tmp_file(filepath);
2012-09-09 16:21:46 +00:00
2012-09-09 17:27:32 +00:00
ofstream outf(filepath, ofstream::binary);
2012-08-15 04:27:41 +00:00
char buf[1024];
int len;
while((len = obj.streamGetChars(1024, (Guchar*)buf)) > 0)
{
outf.write(buf, len);
}
outf.close();
obj.streamClose();
}
catch(int)
2012-08-14 08:23:15 +00:00
{
2012-09-07 16:38:41 +00:00
cerr << "Someting wrong when trying to dump font " << hex << fn_id << dec << endl;
2012-08-14 08:23:15 +00:00
}
obj2.free();
obj1.free();
obj.free();
fontdesc_obj.free();
font_obj2.free();
font_obj.free();
return filepath;
}
2012-09-09 16:21:46 +00:00
void HTMLRenderer::embed_font(const string & filepath, GfxFont * font, FontInfo & info, bool get_metric_only)
{
2012-09-09 18:07:35 +00:00
string suffix = get_suffix(filepath);
2012-09-09 16:21:46 +00:00
2012-09-09 06:48:10 +00:00
for(auto iter = suffix.begin(); iter != suffix.end(); ++iter)
*iter = tolower(*iter);
ff_load_font(filepath.c_str());
int * code2GID = nullptr;
int code2GID_len = 0;
int maxcode = 0;
Gfx8BitFont * font_8bit = nullptr;
2012-09-09 18:07:35 +00:00
info.use_tounicode = (is_truetype_suffix(suffix) || (param->tounicode >= 0));
2012-09-06 17:33:00 +00:00
2012-08-31 15:23:03 +00:00
if(!get_metric_only)
{
2012-09-06 17:33:00 +00:00
const char * used_map = font_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
*
* for 8bit + nonTrueType
* re-encoding the font using a PostScript encoding list (glyph id <-> glpyh name)
*
* for 8bit + TrueType
* sort the glpyhs as the original order, and later will map GID (instead of char code) to Unicode
*
* for CID + nonTrueType
* Flatten the font
*
* for CID Truetype
* same as 8bitTrueType, except for that we have to check 65536 charcodes
*/
if(!font->isCIDFont())
{
font_8bit = dynamic_cast<Gfx8BitFont*>(font);
maxcode = 0xff;
2012-09-09 18:07:35 +00:00
if(is_truetype_suffix(suffix))
{
2012-09-05 17:01:47 +00:00
ff_reencode_glyph_order();
FoFiTrueType *fftt = nullptr;
if((fftt = FoFiTrueType::load((char*)filepath.c_str())) != nullptr)
{
code2GID = font_8bit->getCodeToGIDMap(fftt);
code2GID_len = 256;
delete fftt;
}
}
else
{
// move the slot such that it's consistent with the encoding seen in PDF
unordered_set<string> nameset;
bool name_conflict_warned = false;
memset(cur_mapping2, 0, 0x100 * sizeof(char*));
for(int i = 0; i < 256; ++i)
{
2012-09-06 16:58:23 +00:00
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)
{
2012-09-01 18:25:54 +00:00
name_conflict_warned = true;
//TODO: may be resolved using advanced font properties?
2012-09-07 16:38:41 +00:00
cerr << "Warning: encoding confliction detected in font: " << hex << info.id << dec << endl;
}
}
}
}
ff_reencode_raw2(cur_mapping2, 256, 0);
}
}
else
{
maxcode = 0xffff;
2012-09-09 18:07:35 +00:00
if(is_truetype_suffix(suffix))
{
2012-09-05 17:01:47 +00:00
ff_reencode_glyph_order();
GfxCIDFont * _font = dynamic_cast<GfxCIDFont*>(font);
// code2GID has been stored for embedded CID fonts
code2GID = _font->getCIDToGID();
code2GID_len = _font->getCIDToGIDLen();
}
else
{
ff_cidflatten();
}
}
/*
* Step 2
* map charcode (or GID for CID truetype)
* generate an Consortium encoding file and let fontforge handle it.
*
* - 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, unless param->always_apply_tounicode is set
*
*/
{
unordered_set<int> codeset;
bool name_conflict_warned = false;
2012-09-03 13:54:48 +00:00
auto ctu = font->getToUnicode();
memset(cur_mapping, 0, 0x10000 * sizeof(int32_t));
2012-09-05 16:07:21 +00:00
if(code2GID)
2012-09-05 17:01:47 +00:00
maxcode = min(maxcode, code2GID_len - 1);
2012-09-05 16:07:21 +00:00
2012-09-05 17:01:47 +00:00
int max_key = maxcode;
for(int i = 0; i <= maxcode; ++i)
{
2012-09-06 17:33:00 +00:00
if(!used_map[i])
continue;
2012-09-09 18:07:35 +00:00
if(is_truetype_suffix(suffix) && (font_8bit != nullptr) && (font_8bit->getCharName(i) == nullptr))
2012-09-03 13:54:48 +00:00
{
continue;
2012-09-03 13:54:48 +00:00
}
2012-09-05 16:07:21 +00:00
int k = i;
if(code2GID)
{
2012-09-05 17:01:47 +00:00
if((k = code2GID[i]) == 0) continue;
2012-09-05 16:07:21 +00:00
}
2012-09-05 17:01:47 +00:00
if(k > max_key)
max_key = k;
Unicode u, *pu=&u;
if(info.use_tounicode)
{
2012-09-03 13:54:48 +00:00
int n = ctu ? (ctu->mapToUnicode(i, &pu)) : 0;
u = check_unicode(pu, n, i, font);
}
else
{
u = unicode_from_font(i, font);
}
if(codeset.insert(u).second)
{
cur_mapping[k] = u;
}
else
{
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;
}
}
}
2012-09-05 17:01:47 +00:00
ff_reencode_raw(cur_mapping, max_key + 1, 1);
2012-09-03 13:54:48 +00:00
if(ctu)
ctu->decRefCnt();
}
}
{
2012-09-07 17:09:09 +00:00
/*
* [Win|Typo|HHead][Ascent|Descent]
* Firefox & Chrome interprets the values in different ways
* Trying to unify them
*/
// Generate an intermediate ttf font in order to retrieve the metrics
// TODO: see if we can get the values without save/load
2012-09-09 16:21:46 +00:00
auto fn = str_fmt("%s/f%llx_.ttf", tmp_dir.c_str(), info.id);
add_tmp_file((char*)fn);
ff_save((char*)fn);
2012-09-07 17:09:09 +00:00
ff_close();
2012-09-09 16:21:46 +00:00
ff_load_font((char*)fn);
}
2012-09-07 17:09:09 +00:00
{
2012-09-07 17:09:09 +00:00
// read metrics
int em = ff_get_em_size();
int ascent = ff_get_max_ascent();
int descent = ff_get_max_descent();
if(em != 0)
{
info.ascent = ((double)ascent) / em;
info.descent = -((double)descent) / em;
}
else
{
info.ascent = 0;
info.descent = 0;
}
if(param->debug)
{
cerr << "Ascent: " << info.ascent << " Descent: " << info.descent << endl;
}
2012-09-07 17:09:09 +00:00
ff_set_ascent(ascent);
ff_set_descent(descent);
}
{
2012-09-09 16:21:46 +00:00
auto fn = str_fmt("%s/f%llx%s",
(param->single_html ? tmp_dir : dest_dir).c_str(),
info.id, param->font_suffix.c_str());
2012-09-07 17:09:09 +00:00
if(param->single_html)
2012-09-09 16:21:46 +00:00
add_tmp_file((char*)fn);
2012-09-07 17:09:09 +00:00
2012-09-09 16:21:46 +00:00
ff_save((char*)fn);
2012-09-07 17:09:09 +00:00
ff_close();
}
2012-08-14 08:23:15 +00:00
}
void HTMLRenderer::drawString(GfxState * state, GooString * s)
{
if(s->getLength() == 0)
return;
auto font = state->getFont();
if((font == nullptr) || (font->getWMode()))
{
return;
}
//hidden
if((state->getRender() & 3) == 3)
{
return;
}
// see if the line has to be closed due to state change
check_state_change(state);
prepare_line(state);
2012-08-14 08:23:15 +00:00
// Now ready to output
// get the unicodes
char *p = s->getCString();
int len = s->getLength();
double dx = 0;
double dy = 0;
2012-08-23 20:36:27 +00:00
double dxerr = 0;
2012-08-14 08:23:15 +00:00
double dx1,dy1;
double ox, oy;
int nChars = 0;
int nSpaces = 0;
int uLen;
CharCode code;
Unicode *u = nullptr;
while (len > 0) {
auto n = font->getNextChar(p, len, &code, &u, &uLen, &dx1, &dy1, &ox, &oy);
2012-08-24 06:21:20 +00:00
2012-08-14 08:23:15 +00:00
if(!(_equal(ox, 0) && _equal(oy, 0)))
{
2012-08-14 09:13:29 +00:00
cerr << "TODO: non-zero origins" << endl;
2012-08-14 08:23:15 +00:00
}
2012-09-07 00:39:21 +00:00
bool is_space = false;
2012-08-19 20:50:28 +00:00
if (n == 1 && *p == ' ')
{
++nSpaces;
2012-09-07 00:39:21 +00:00
is_space = true;
2012-08-19 20:50:28 +00:00
}
2012-08-24 06:21:20 +00:00
2012-09-07 00:39:21 +00:00
if(is_space && (param->space_as_offset))
2012-09-06 07:09:47 +00:00
{
2012-09-07 00:39:21 +00:00
// ignore horiz_scaling, as it's merged in CTM
line_buf.append_offset((dx1 * cur_font_size + cur_letter_space + cur_word_space) * draw_scale);
2012-09-06 07:09:47 +00:00
}
else
{
2012-09-07 00:39:21 +00:00
if((param->decompose_ligature) && all_of(u, u+uLen, isLegalUnicode))
{
line_buf.append_unicodes(u, uLen);
}
else
{
Unicode uu = (cur_font_info->use_tounicode ? check_unicode(u, uLen, code, font) : unicode_from_font(code, font));
line_buf.append_unicodes(&uu, 1);
}
2012-09-06 07:09:47 +00:00
}
2012-08-14 08:23:15 +00:00
2012-08-23 20:36:27 +00:00
dx += dx1;
dy += dy1;
2012-08-14 08:23:15 +00:00
++nChars;
p += n;
len -= n;
}
2012-09-04 04:54:47 +00:00
double hs = state->getHorizScaling();
2012-08-21 20:34:39 +00:00
// horiz_scaling is merged into ctm now,
2012-08-21 19:44:48 +00:00
// so the coordinate system is ugly
2012-09-04 04:54:47 +00:00
dx = (dx * cur_font_size + nChars * cur_letter_space + nSpaces * cur_word_space) * hs;
2012-08-14 08:23:15 +00:00
2012-09-04 04:54:47 +00:00
dy *= cur_font_size;
2012-08-14 08:23:15 +00:00
cur_tx += dx;
cur_ty += dy;
2012-09-04 04:54:47 +00:00
draw_tx += dx + dxerr * cur_font_size * hs;
2012-08-14 08:23:15 +00:00
draw_ty += dy;
}