/* This file is part of dvi2bitmap; see README for copyrights and licence */ #include #include // for debugging code, written to cerr #include #include //#include #include #include //#include // for auto_ptr #ifdef HAVE_CSTD_INCLUDE # include # include # include #else # include # include # include #endif using STD::cerr; using STD::endl; using STD::ends; using STD::ofstream; using STD::ios; #include #include #include #include #include #include #ifdef ENABLE_KPATHSEA #include #endif string_list break_path (string); // define class static variables verbosities PkRasterdata::verbosity_ = normal; verbosities PkFont::verbosity_ = normal; verbosities PkGlyph::verbosity_ = normal; #ifndef DEFAULT_MFMODE #define DEFAULT_MFMODE "ibmvga" #endif #ifndef DEFAULT_RESOLUTION #define DEFAULT_RESOLUTION 110 #endif string PkFont::missingFontMode_ = DEFAULT_MFMODE; int PkFont::resolution_ = DEFAULT_RESOLUTION; // Font-search strategies: manipulated by setFontSearchPath() and // setFontSearchCommand(). This could be made a lot more // sophisticated, but I can't yet decide in precisely which way. The // initialisation would probably be better done using // setFontSearchStrategy_() in a class initialiser (but I've forgotten // how to do that in C++!) //unsigned int PkFont::fontSearchStrategies_ = ~0; unsigned int PkFont::fontSearchStrategies_ = fontSearchStrategyPath_ #ifdef FONT_SEARCH_SCRIPT | fontSearchStrategyCommand_ #endif #ifdef ENABLE_KPATHSEA | fontSearchStrategyKpse_ #endif ; string PkFont::fontSearchPath_ = ""; #ifdef FONT_SEARCH_SCRIPT string PkFont::fontSearchCommandTemplate_ = FONT_SEARCH_SCRIPT; #else string PkFont::fontSearchCommandTemplate_ = ""; #endif /* defined FONT_SEARCH_SCRIPT */ #ifdef FONT_GEN_TEMPLATE string PkFont::fontgenCommandTemplate_ = FONT_GEN_TEMPLATE; bool PkFont::makeMissingFonts_ = true; #else string PkFont::fontgenCommandTemplate_ = ""; bool PkFont::makeMissingFonts_ = false; #endif /* defined FONT_GEN_TEMPLATE */ /** * Represents a PK font. * *

The constructor arguments are those read from the DVI file font * declaration, after a fnt_def opcode, except for * dvimag, which is the overall DVI magnification * factor, and includes the file and command-line magnification * adjustments. For more details, see the definition of the font * declaration in section `A.4 Font defintions' of the DVI standard, * and the use of these factors in method {@link #magnification}. * * @param dvimag the scale factor (1.0 = no magnification) by which * the font is to be magnified as it is read from the PK file * * @param c the font checksum expected * * @param s fixed-point scale factor applied to the character widths * in the font * * @param d the fixed-point `design size' of the font * * @param name the name of the font */ PkFont::PkFont(double dvimag, unsigned int c, unsigned int s, unsigned int d, string name) : name_(name), pkf_(0), font_loaded_(false), dvimag_(dvimag), seen_in_doc_(false) { font_header_.c = c; font_header_.s = s; font_header_.d = d; path_ = ""; if (verbosity_ > normal) cerr << "PkFont::PkFont " << name << " c=" << c << " s=" << s << " d=" << d << endl; try { string pk_file_path; bool got_path = find_font (pk_file_path); if (! got_path) { if (makeMissingFonts_) { string cmd; cmd = fontgenCommand(); if (verbosity_ >= normal) cerr << "mktexpk: " << cmd << endl; if (cmd.length() == 0) throw InputByteStreamError ("can't generate fontgen command"); PipeStream *PS = new PipeStream(cmd); string fontpath = PS->getResult(); if (PS->getTerminationStatus() != 0) throw InputByteStreamError ("unable to generate font"); delete PS; // Try searching again, rather than relying on the // return value from the pipe (there's no reason // to expect it to be unreliable, but re-using // find_font() seems more consistent). got_path = find_font (pk_file_path); if (! got_path) { // We didn't find it, for some reason, but `fontpath' // returned from the pipe should have the string. Use that // instead, but warn about it. I _think_ there's a // race-condition which sometimes makes the find_font() fail // to find the font if it's called immediately after the // font is successfully generated. if (verbosity_ >= normal) cerr << "Warning: tried (apparently successfully) to create font, but couldn't find it afterwards." << endl << "Consider setting DVI2BITMAP_PK_PATH (see docs, based on " << fontpath << ")" << endl << "I'll use what I think is the correct path to it (fingers crossed)" << endl; pk_file_path = fontpath; } } else throw InputByteStreamError ("can't find font file, and font-generation disabled or impossible"); } path_ = pk_file_path; pkf_ = new FileByteStream (path_, "", true); for (unsigned int i=0; i normal) cerr << "Opened font " << path_ << " successfully" << endl; if (preamble_.cs != c) if (verbosity_ > quiet) cerr << "Warning: Font " << name_ << "found : expected checksum " << c << ", got checksum " << preamble_.cs << endl; font_loaded_ = true; } catch (InputByteStreamError& e) { if (verbosity_ > quiet) cerr << "Warning: Font " << name << " at " << dpiScaled() << "dpi (" << path_ << ") not found (" << e.problem() << ")" << endl; preamble_.cs = 0; glyphs_[0] = new PkGlyph(resolution_, this); // dummy glyph } //quad_ = ((double)dvimag/1000.0) * d; // These next three quantities should be given in DVI units // _without_ the overall DVI magnification. The function // magnification() includes that, so remove it. quad_ = d * magnification(false); //quad_ = d * magnification(true); //quad_ = d * dvimag_; word_space_ = 0.2*quad_; back_space_ = 0.9*quad_; if (verbosity_ > normal) cerr << "Quad="<setFontSearch... methods. The * result of the first one which succeeds is the one returned. * *

Return true if we found a file to open, and return the path in the * argument, which is unchanged otherwise. Return false on error. * Success doesn't guarantee that the file exists, just that it was * constructed without problems (in the current implementation, success * does mean that the file was found, but this is not guaranteed by * this routine). * * @param path reference to a path, which is filled in on output if * the search was successful * @return true on success, false on error */ bool PkFont::find_font (string& path) { bool got_it = false; double scaled_res = resolution_ * magnification(); if (verbosity_ > normal) cerr << "PkFont::find_font: " << name_ << ", checksum=" << font_header_.c << ", res " << resolution_ << '*' << magnification() << " = " << scaled_res << " (fontSearchStrategies_=" << fontSearchStrategies_ << ')' << endl; if (fontSearchStrategies_ & fontSearchStrategyPath_) { string pkpath = ""; if (fontSearchPath_.length() > 0) { pkpath = fontSearchPath_; if (verbosity_ > normal) cerr << "find_font: using fontpath=" << fontSearchPath_ << endl; } else { const char *pkpath_p = getenv ("DVI2BITMAP_PK_PATH"); if (pkpath_p != 0) { pkpath = pkpath_p; if (verbosity_ > normal) cerr << "find_font: using DVI2BITMAP_PK_PATH=" << pkpath << endl; } } if (pkpath.length() != 0) { string& found_file = search_pkpath (pkpath, name_, scaled_res); if (found_file.length() > 0) { path = found_file; got_it = true; } if (verbosity_ > normal) cerr << "PkFont::find_font: search_pkpath produced " << (got_it ? path : "...nothing!") << endl; } } #ifdef ENABLE_KPATHSEA if (! got_it && (fontSearchStrategies_ & fontSearchStrategyKpse_)) { const char *kpse_file; //std::auto_ptr kps = KarlPathSearcher::getInstance(); KarlPathSearcher* kps = KarlPathSearcher::getInstance(); kpse_file = kps->find (name_.c_str(), static_cast(scaled_res)); if (kpse_file != 0) { path = kpse_file; got_it = true; } if (verbosity_ > normal) cerr << "PkFont::find_font: (KarlPathSearcher)->find(" << name_ << "," << static_cast(scaled_res) << ") = " << (got_it ? path : "...nothing!") << endl; } #endif if (! got_it && (fontSearchStrategies_ & fontSearchStrategyCommand_) && (fontSearchCommandTemplate_.length() != 0)) { string& cmd = substitute_font_string (fontSearchCommandTemplate_, missingFontMode_, name_, dpiScaled(), dpiBase(), magnification()); if (verbosity_ > normal) cerr << "PkFont::find_font: running cmd <" << cmd << ">..." << endl; PipeStream *PS = new PipeStream(cmd); string font_found = PS->getResult(); int status = PS->getTerminationStatus(); delete PS; if (verbosity_ > normal) cerr << " ...produced <" << font_found << "> (status=" << status << ')' << endl; // Checking the length of the found font seems more reliable than // checking the exit status, since kpsewhich apparently returns a // non-zero exit status even if it finds the file, which isn't // terribly helpful if (font_found.length() > 0) { path = font_found; got_it = true; } } if (! got_it) { // write font-generation command string to missfont.log string fontgenCmd = fontgenCommand(); if (verbosity_ > normal) cerr << "PkFont::find_font: no font found, writing <" << fontgenCmd << "> to missfont.log" << endl; if (fontgenCmd.length() != 0) { ofstream missfont; missfont.open("./missfont.log", ios::app); if (!missfont) cerr << "Can't open ./missfont.log to write" << endl; else { missfont << fontgenCmd << endl; missfont.close(); } } } return got_it; } /** * Find a file on the font path, with the specified name, * at the specified resolution. * *

Do font rounding: Check all the integers between 0.998 and 1.002 of * the specified resolution. If, however, this range falls within * (n,n+1) (non-inclusive) (ie, it doesn't span any integers) then * simply round the number. * See the DVI Driver Standard for more details on the font-rounding * algorithm. * * @param path a colon-separated list of directories to search for fonts * @param name the name of the font to look for * @param resolution the required resolution of the font * * @return a reference to a static string; if not found, return a * zero-length string */ string& PkFont::search_pkpath (string path, string name, double resolution) { static string fname; // return value int size_low, size_high; size_low = static_cast(STD::ceil (0.998*resolution)); size_high = static_cast(STD::floor (1.002*resolution)); if (size_low > size_high) // simply round it size_low = size_high = static_cast(STD::floor(resolution+0.5)); if (verbosity_ > normal) cerr << "PkFont::search_pkpath: searching " << size_low << ".." << size_high << endl; string_list pathlist = break_path(path); if (pathlist.size() == 0) { fname = ""; return fname; // ...silently } bool found = false; for (string_list::const_iterator pi = pathlist.begin(); pi != pathlist.end(); pi++) { // Each member of pathlist is a template as defined by // PkFont::substitute_font_string(). Work through each in turn. for (int size=size_low; size<=size_high && !found; size++) { fname = substitute_font_string (*pi, PkFont::missingFontMode_, name, size, dpiBase(), magnification()); if (verbosity_ > normal) cerr << "PkFont::search_pkpath: trying " << *pi << "->" << fname << endl; struct stat S; if (stat (fname.c_str(), &S) == 0) { // file exists #ifdef WIN32 # define S_IRGRP 0 # define S_IROTH 0 #endif // WIN32 if (S_ISREG(S.st_mode) && (S.st_mode & (S_IRUSR|S_IRGRP|S_IROTH))) { found = true; // exit the loop if (verbosity_ > normal) cerr << "PkFont::search_pkpath: " << fname << " found" << endl; } else if (verbosity_ > normal) cerr << "PkFont::search_pkpath: " << fname << " mode " << STD::oct << S.st_mode << STD::dec << " (not readable)" << endl; } else if (verbosity_ > normal) cerr << "PkFont::search_pkpath: " << fname << " not found" << endl; } } if (!found) fname = ""; return fname; } /** * Given a format string, return a reference to a string with format * specifiers replaced by font information. * *

The format specifiers are: * *
%Mmodeibmvga *
%ffont namecmr10 *
%ddpi330 *
%bbase dpi110 *
%mmagnification3 *
%%literal %-character *
* Any other format specifiers constitute an error. There is no * support for any backslash escapes. * * @param fmt the format string, with the format specifiers described above * @param mode a Metafont mode string, such as ibmvga * @param fontname the name of a font, such as cmr10 * @param dpi the requested size of a font in dots-per-inch * @param basedpi the design size of a font * @param magnification of the font * * @returns a reference to the formatted string; this is a static * string, which is overwritten on each call * * @throws PkError if it encounters an illegal format element. */ string& PkFont::substitute_font_string (const string fmt, const string mode, const string fontname, const int dpi, const int basedpi, const double magnification) throw (PkError) { static string retval; SSTREAM newstring; if (verbosity_ > normal) cerr << "PkFont::substitute_font_string (fmt=" << fmt << " mode=" << mode << " fontname=" << fontname << " dpi=" << dpi << " basedpi=" << basedpi << " magnification=" << magnification << endl; for (const char *p = fmt.c_str(); *p != '\0'; p++) { if (*p == '%') switch (*++p) { case 'M': newstring << mode; break; case 'm': newstring << magnification; break; case 'f': newstring << fontname; break; case 'd': newstring << dpi; break; case 'b': newstring << basedpi; break; case '%': newstring << '%'; break; default: { SSTREAM msg; msg << "substitute_font_string: Invalid format character %" << *p << ". Allowed characters {Mmfdb%}." << ends; throw PkError (SS_C_STR(msg)); } /* NOTREACHED */ } else newstring << *p; } retval = SS_STRING(newstring); return retval; } void PkFont::read_font (InputByteStream& pkf) { // read the preamble, and check that the requested parameters match Byte preamble_opcode = pkf.getByte(); if (preamble_opcode != 247) throw DviError ("PK file doesn't start with preamble"); if ((preamble_.id = pkf.getByte()) != 89) throw DviError ("PK file has wrong ID byte"); int comment_length = pkf.getByte(); preamble_.comment = ""; for (;comment_length > 0; comment_length--) preamble_.comment += static_cast(pkf.getByte()); unsigned int i = pkf.getUIU(4); preamble_.designSize = (double)i/(double)two20_; preamble_.cs = pkf.getUIU(4); i = pkf.getUIU(4); preamble_.hppp = (double)i/(double)two16_; i = pkf.getUIU(4); preamble_.vppp = (double)i/(double)two16_; if (verbosity_ > normal) cerr << "PkFont::read_font " << name_ << " '" << preamble_.comment << "\'" << " designSize=" << preamble_.designSize << " cs=" << preamble_.cs << " hppp=" << preamble_.hppp << " vppp=" << preamble_.vppp << "..." << endl; // Now scan through the file, reporting opcodes and character definitions bool end_of_scan = false; while (! end_of_scan) { Byte opcode = pkf.getByte(); if (opcode <= 239) // character definition { bool two_byte = opcode & 4; Byte pl_prefix = static_cast(opcode & 3); unsigned int packet_length; //unsigned int pos; // primarily for debugging output unsigned int g_cc, g_tfmwidth, g_dm, g_dx, g_dy, g_w, g_h; int g_hoff, g_voff; if (two_byte) if (pl_prefix == 3) // long-form character preamble { packet_length = pkf.getUIU(4); g_cc = pkf.getUIU(4); g_tfmwidth = pkf.getUIU(4); g_dx = pkf.getUIU(4); g_dy = pkf.getUIU(4); g_w = pkf.getUIU(4); g_h = pkf.getUIU(4); g_hoff = pkf.getSIS(4); g_voff = pkf.getSIS(4); if (g_cc >= nglyphs_) // g_cc unsigned, so >= 0 throw DviError ("PK file has out-of-range character code"); //pos = pkf.pos(); packet_length -= 7*4; if (verbosity_ > debug) cerr << "chardef L " << static_cast(g_cc) << "=`" << (g_cc>' ' && g_cc<='~' ?static_cast(g_cc) :'?') << "\' " << STD::hex << ": opcode=" << static_cast(opcode) << " pl=" << packet_length << " cc=" << g_cc << " tfmwidth=" << g_tfmwidth << " dx=" << g_dx << " dy=" << g_dy << " w=" << g_w << " h=" << g_h << " hoff=" << g_hoff << " voff=" << g_voff << STD::dec << "(len="<= nglyphs_) // g_cc unsigned, so >= 0 throw DviError ("PK file has out-of-range character code"); //pos = pkf.pos(); packet_length -= 3 + 5*2; if (verbosity_ > debug) cerr << "chardef XS " << static_cast(g_cc) << "=`" << (g_cc>' ' && g_cc<='~' ?static_cast(g_cc) :'?') << "\' " << STD::hex << ": opcode=" << static_cast(opcode) << " pl=" << packet_length << " cc=" << g_cc << " tfmwidth=" << g_tfmwidth << " dm=" << g_dm << " w=" << g_w << " h=" << g_h << " hoff=" << g_hoff << " voff=" << g_voff << STD::dec << "(len="<= nglyphs_) // g_cc unsigned, so >= 0 throw DviError ("PK file has out-of-range character code"); //pos = pkf.pos(); packet_length -= 8; if (verbosity_ > debug) cerr << "chardef S " << static_cast(g_cc) << "=`" << (g_cc>' ' && g_cc<='~' ? static_cast(g_cc) : '?') << "\' " << STD::hex << ": opcode=" << static_cast(opcode) << " pl=" << packet_length << " cc=" << g_cc << " tfmwidth=" << g_tfmwidth << " dm=" << g_dm << " w=" << g_w << " h=" << g_h << " hoff=" << g_hoff << " voff=" << g_voff << STD::dec << "(len="< debug) cerr << "charsizes " << g_cc << " tfm=" << g_tfmwidth << " w=" << g_w << " h=" << g_h << " off=(" << g_hoff << ',' << g_voff //<< ") at " << pos << "(len " << packet_length << ")" << endl; } else // opcode is command { int lenb = 0; switch (opcode) { case 243: // pk_xxx4 lenb++; case 242: // pk_xxx3 lenb++; case 241: // pk_xxx2 lenb++; case 240: // pk_xxx1 lenb++; { string special = ""; for (unsigned int special_len = pkf.getUIU(lenb); special_len > 0; special_len--) special += static_cast(pkf.getByte()); if (verbosity_ > debug) cerr << "Special \'" << special << "\'" << endl; } break; case 244: // pk_yyy for (int ti=0; ti<4; ti++) (void) pkf.getByte(); break; case 245: // pk_post end_of_scan = true; break; case 246: // pk_no_op break; case 247: // pk_pre throw DviError ("Found PK preamble in body of file"); default: throw DviError ("Found unexpected opcode in PK file"); } } } if (verbosity_ > normal) cerr << "PkFont::read_font ...finished reading " << name_ << endl; } /** * Obtain the magnification of this font. This includes both font scaling * and overall DVI file magnification: this number is mag/1000 . * s/d, where s and d are taken from the font * definition in the DVI file, and mag is the total * magnification taking into account DVI preamble magnification and * any command-line overriding. * * @param includeDviMag if true (the default), include the overall * DVI file magnification mag (as set in the * constructor); if false, do not * * @return the font magnification */ double PkFont::magnification(bool includeDviMag) const { double rval = (double)font_header_.s / (double)font_header_.d; if (includeDviMag) rval *= dvimag_; if (verbosity_ > normal) cerr << "PkFont::magnification: " << font_header_.s << '/' << font_header_.d << " * " << dvimag_ << " = " << rval << endl; return rval; } /** * Set the list of directories in which to look for fonts. * @param fp the colon-separated font path */ void PkFont::setFontSearchPath(string fp) { if (fp.length() == 0) // simply enable this fontSearchPath_ = fp; setFontSearchPath(true); } /** * Set the list of directories in which to look for fonts. * @param fp the colon-separated font path */ void PkFont::setFontSearchPath(char *fp) { string s = (fp == 0 ? "" : fp); setFontSearchPath(s); } /** * Enable or disable using the font-path when searching for fonts. * @param usePath if true, use the font path * @see #setFontSearchPath(string) */ void PkFont::setFontSearchPath(bool usePath) { setFontSearchStrategy_(fontSearchStrategyPath_, usePath); } /** * Set the shell command which is used when searching for fonts. The * specified string should be a template string with the formatting * characters managed by {@link #substitute_font_string}, and these * will be substituted with the required values before the command is * run. * * @param cmd the shell command template */ void PkFont::setFontSearchCommand(string cmd) { // if cmd is zero length, simply enable this if (cmd.length() != 0) fontSearchCommandTemplate_ = cmd; setFontSearchCommand(true); } /** * Sets the shell command which is used when searching for fonts. The * specified string should be a template string with the formatting * characters managed by {@link #substitute_font_string}, and these * will be substituted with the required values before the command is * run. * * @param cmd the shell command template */ void PkFont::setFontSearchCommand(char* cmd) { string s = (cmd == 0 ? "" : cmd); setFontSearchCommand(s); } /** * Enable or disable using a font-search command when searching for fonts. * @param useCommand if true, use the command * @see #setFontSearchCommand(string) */ void PkFont::setFontSearchCommand(bool useCommand) { setFontSearchStrategy_(fontSearchStrategyCommand_, useCommand); } /** * Enable or disable using the kpathsea library when * searching for fonts. * @param useKpse if true, use the library */ void PkFont::setFontSearchKpse(bool useKpse) { setFontSearchStrategy_(fontSearchStrategyKpse_, useKpse); } void PkFont::setFontSearchStrategy_(unsigned int strat, bool useit) { if (useit) fontSearchStrategies_ |= strat; else fontSearchStrategies_ &= ~strat; } /** * Represents a single glyph in a font. The parameters here * correspond to the parameters of the same names which are read from * the PK file. * * @param cc the character code of this glyph * @param tfmwidth the width of the character, in DVI units * @param dm the horizontal escapement, in pixels; this is the number * of pixels rightwards (towards increasing x) that the * reference should move after this glyph is set; the vertical * escapement is taken to be zero * @param w width of the bitmap in pixels * @param h height of the bitmap in pixels * @param hoff (hoff,voff) is the position of the glyph * reference point, as an offset from the top-left pixel, in units of pixels, and with right and down being positive * @param voff see parameter hoff * @param rasterdata the raster information for this glyph * @param f the font which this glyph belongs to */ PkGlyph::PkGlyph(unsigned int cc, unsigned int tfmwidth, unsigned int dm, unsigned int w, unsigned int h, int hoff, int voff, PkRasterdata *rasterdata, PkFont *f) : cc_(cc), w_(w), h_(h), hoff_(hoff), voff_(voff), font_(f), rasterdata_(rasterdata), longform_(false), bitmap_(0) { tfmwidth_ = (double)tfmwidth/two20_ * f->designSize(); dx_ = dm; dy_ = 0; } /** * Represents a single glyph in a font. The parameters here * correspond to the parameters of the same names which are read from * the PK file. * * @param cc the character code of this glyph * @param tfmwidth the width of the character, in DVI units * @param dx the horizontal escapement, in pixels times * @manonly 2**16; @endmanonly * @latexonly $2^{16}$; @endlatexonly * @htmlonly 216; @endhtmlonly * this defines the number * of pixels rightwards (towards increasing x) that the * reference should move after this glyph is set * @param dy the vertical escapement, in pixels times * @manonly 2**16 @endmanonly * @latexonly $2^{16}$ @endlatexonly * @htmlonly 216 @endhtmlonly * @param w width of the bitmap in pixels * @param h height of the bitmap in pixels * @param hoff (hoff,voff) is the position of the glyph * reference point, as an offset from the top-left pixel, in units of pixels, and with right and down being positive * @param voff see parameter hoff * @param rasterdata the raster information for this glyph * @param f the font which this glyph belongs to */ PkGlyph::PkGlyph(unsigned int cc, unsigned int tfmwidth, unsigned int dx, unsigned int dy, unsigned int w, unsigned int h, int hoff, int voff, PkRasterdata *rasterdata, PkFont *f) : cc_(cc), w_(w), h_(h), hoff_(hoff), voff_(voff), font_(f), rasterdata_(rasterdata), longform_(true), bitmap_(0) { tfmwidth_ = (double)tfmwidth/(double)two20_ * f->designSize(); dx_ = static_cast(STD::floor(dx / (double)two16_ + 0.5)); dy_ = static_cast(STD::floor(dy / (double)two16_ + 0.5)); } /** * Constructs a dummy glyph for a font. * * @param resolution the resolution which this glyph corresponds to * @param f the font which this glyph is a member of */ PkGlyph::PkGlyph(int resolution, PkFont *f) : font_(f) { tfmwidth_ = f->designSize(); // design size in points w_ = h_ = 0; bitmap_ = 0; // make dx_ = 1 quad in pixels (rather large) dx_ = static_cast(f->designSize() * resolution / 72.27); dy_ = 0; hoff_ = voff_ = 0; } /** * Returns the bitmap which represents this glyph. This runs from * the top-left of the character, with the width and height as given * by methods w() and h(). * @return the bitmap for this glyph */ const Byte *PkGlyph::bitmap() { if (bitmap_ == 0 && w_ != 0 && h_ != 0) bitmap_ = rasterdata_->bitmap(); return bitmap_; } /** * Creates a Rasterdata object representing the provided data. The * raster data read from the PK file needs to be decoded into a * bitmap, and this is the function of this class. * * @param opcode the character code of this glyph * @param rasterdata the rastered glyph, in the format described in * the PK file documentation * @param len the number of bytes in the rasterdata stream * @param w the width of the resulting bitmap * @param h the number of rows in the resulting bitmap */ PkRasterdata::PkRasterdata(Byte opcode, const Byte *rasterdata, unsigned int len, unsigned int w, unsigned int h) : len_(len), w_(w), h_(h), bitmap_(0), highnybble_(false) { dyn_f_ = static_cast(opcode >> 4); start_black_ = opcode&8; rasterdata_ = new Byte[len]; (void) STD::memcpy ((void*)rasterdata_, (void*)rasterdata, len); eob_ = rasterdata_+len_; } /** * Unpacks a PK number. Numbers in the PK file are stored in a * compressed form. This method consumes as many nybbles as necessary * from the input stream and converts them to an unpacked number. * * @return the unsigned integer corresponding to the next number read * from the input stream */ unsigned int PkRasterdata::unpackpk () { unsigned int res = 0; Byte n = nybble(); if (n == 0) { // it's a 'large' number int nzero = 1; while ((n = nybble()) == 0) nzero++; res = n; for (; nzero>0; nzero--) res = res*16 + nybble(); res = res + (13-dyn_f_)*16 + dyn_f_ - 15; } else if (n <= dyn_f_) res = n; else if (n < 14) res = (n-dyn_f_-1)*16+(dyn_f_+1) + nybble(); else { // it's a repeatcount if (repeatcount_ != 0) throw DviError ("found double repeatcount in unpackpk"); if (n == 15) repeatcount_ = 1; else repeatcount_ = unpackpk (); res = unpackpk(); // get the following runcount } if (verbosity_ > debug) cerr << '=' << res << ' ' << static_cast(repeatcount_) << endl; return res; } /** * Returns the next nybble from the input stream * @return the value of the next nybble [0..15], as a Byte */ Byte PkRasterdata::nybble() { highnybble_ = !highnybble_; Byte res; if (highnybble_) { if (rasterdata_ == eob_) throw DviBug ("Run out of nybbles (snackattack!)"); res = static_cast((*rasterdata_)>>4); } else res = static_cast((*rasterdata_++)&0xf); if (verbosity_ > debug) cerr << '<' << static_cast(res) << endl; return res; } /** * Constructs a bitmap from the provided rasterinfo, which has come from * the PK file. Place the resulting bitmap in bitmap_ */ void PkRasterdata::construct_bitmap() { bitmap_ = new Byte[w_ * h_]; if (dyn_f_ == 14) { // rasterinfo is a pure bitmap - no decoding necessary unsigned int nbits_req = w_*h_; Byte *p = bitmap_; // build this up... const Byte *r = rasterdata_; // ...from here Byte b; while (nbits_req >= 8) { for (int i=7, bb=*r; i>=0; i--, bb>>=1) p[i] = static_cast(bb&1); p += 8; r++; nbits_req -= 8; } if (nbits_req > 0) { // get the last few bits b = static_cast(*r >> (8-nbits_req)); for (int i=nbits_req-1; i>=0; i--, b>>=1) p[i] = static_cast(b&1); } } else { // decode the rasterinfo Byte *rowp = bitmap_; Byte *rowstart = bitmap_; unsigned int nrow = 0; unsigned int ncol = 0; Byte pixelcolour = start_black_; repeatcount_ = 0; if (verbosity_ > debug) { cerr << "dyn_f=" << static_cast(dyn_f_) << " h=" << h_ << " w=" << w_ << endl << "rasterdata=" << STD::hex; for (const Byte *p=rasterdata_; p(*p) << ' '; cerr << STD::dec << endl; } while (nrow < h_) { unsigned int runcount = unpackpk(); for (; runcount>0; runcount--) { *rowp++ = pixelcolour; ncol++; if (ncol == w_) // end of row { ncol = 0; nrow++; if (verbosity_ > debug) { cerr << nrow << ':'; for (const Byte *p=rowstart; p 0) { Byte *rowend=rowp; for (; repeatcount_>0; repeatcount_--) { Byte *pt=rowstart; while (pt < rowend) *rowp++ = *pt++; nrow++; } } rowstart = rowp; } } pixelcolour = static_cast(pixelcolour==0 ? 1 : 0); } } } /** * Utility function: break path at colons, and return list. * @param path a colon-separated string * @return a List of strings */ string_list break_path (string path) { string_list l; string tmp = ""; for (unsigned int i=0; isetFontgenCommand, or as a compiled-in * default. If there is no such command, this method will have no effect. * * @param doit if true, font generation is enabled */ void PkFont::setFontgen(bool doit) { if (fontgenCommandTemplate_.length() == 0) makeMissingFonts_ = false; else makeMissingFonts_ = doit; } /** * Set the shell command which is used when generating fonts. The * specified string should be a template string with the formatting * characters managed by {@link #substitute_font_string}, and these * will be substituted with the required values before the command is * run. * *

This also enables font-generation, so that it has the effect of * setFontgen(true). * * @param cmd a font-generation template */ void PkFont::setFontgenCommand(string cmd) { fontgenCommandTemplate_ = cmd; makeMissingFonts_ = true; } /** * Returns a command which can be used to generate fonts. The command * should return a single line containing the path to the generated * font file. Return an empty string on errors, or if such a command * is not supported on a particular platform. * *

Note that if a font-generation * command template is defined but automatic font generation is * disabled, this still returns a font-generation command. * * @return a font generation command, suitable for passing to a shell */ string PkFont::fontgenCommand (void) const { string rval; if (fontgenCommandTemplate_.length() > 0) { try { rval = substitute_font_string (fontgenCommandTemplate_, missingFontMode_, name_, dpiScaled(), dpiBase(), magnification()); if (verbosity_ > normal) cerr << "PkFont:font_gen_string=" << rval << endl; } catch (PkError& e) { if (verbosity_ > quiet) cerr << "Warning: can't generate fontgen command (" << e.problem() << ")" << endl; rval = ""; } } else { rval = ""; } return rval; }