/* This file is part of dvi2bitmap; see README for copyrights and licence */ #include #include #include #include // for STDIN_FILENO #ifdef HAVE_FCNTL_H #include // for open #endif #ifdef HAVE_CSTD_INCLUDE # include # include // for fabs() # include # include // for isprint # if CCTYPE_IN_STD using std::isprint; # endif #else # include # include # include # include #endif using STD::cerr; using STD::endl; #include #include #include // Static debug switch verbosities DviFile::verbosity_ = normal; verbosities DviFileEvent::verbosity_ = normal; // Mmm, there's something strangely amiss with the exception handling // in this module, or here and in InputByteStream. // // Odd things happen when I run the program with a non-existent file // as argument. What should happen is that InputByteStream throws an // exception, so DviFile throws an exception, which dvi2bitmap.cc // should catch, and so fail gracefully. If, below, I try // to `throw DviError ("DviFile: "+e.problem())', then GCC 2.8.1 (but // no other compiler) dumps core shortly afterwards. In this case, // dvi2bitmap appears _not_ to receive any exception from DviFile. // // I made the problem go away by (a) having the InputByteStreamError // handler rethrow a static string, and (b), altering the eof() // function to check if dvif_ was non-zero before testing it (which is // good anyway). But this isn't really a fix. I suspect this might // be a GCC error, but I can't pin it down accurately enough to // identify a specific fault. // // XXX This note was added in 2000 or so. Is this problem still open? /** * Constructs a new DviFile object. * *

Once the DVI file has been opened in this way, its contents can * be processed in an event-based fashion, by repeatedly calling * {@link #getEvent} and handling the returned object. * *

The DVI file may be specified as "-", in which case the DVI * file will be read from stdin. In fact, this is just a * synonym for the expression, <osfd>0, as * understood by {@link InputByteStream} (qv). In this case, the * postamble is not read, irrespective of the value of parameter * read_post, and this is known not to be seekable, so * the value of parameter seekable is ignored. * *

After the DVI file has been opened using this constructor, the * preamble may or may not be read (this may be specified in future * versions). If you need to ensure that the preamble is read (for * example if you wish to call {@link #convertUnits}), then * you should call {@link #getEvent} once to retrieve the {@link * DviFilePreamble} event which it returns the first time it is called. * * @param fn the name of the DVI file. The given name is searched for * both as-is, and with an extension .dvi added. * * @param res the base DPI to be used for processing the file, in * pixels-per-inch. If given as zero, default to the resolution * returned by {@link PkFont#dpiBase}. * * @param externalmag the factor by which the DVI file's internal * magnification factor should itself be magnified, specified * externally to the DVI file (on a command line, for example); * default is 1.0 * * @param read_post if true, then the DVI postamble will be read; if * false, it will be skipped. This is false by default, but if the * postamble is read, then the font declarations there will be read * and acted on, which may speed things up. * * @param seekable if true, the input file is seekable; if false, it * is not seekable, the value of read_post is ignored * (taken to be false), and the file is opened without attempting to * add any .dvi extension. * * @throws DviError if the DVI file cannot be opened */ DviFile::DviFile (string& fn, int res, double externalmag, bool read_post, bool seekable) throw (DviError) : extmag_(externalmag) { // Initialisations fileName_ = fn; pending_hupdate_ = 0; pending_hhupdate_ = 0; current_font_ = 0; dvif_ = 0; netmag_ = 1.0; skipPage_ = false; max_drift_ = 0; widest_page_ = -1; deepest_page_ = -1; have_preread_postamble_ = false; have_read_to_postamble_ = false; dviu_per_pt_ = dviu_per_px_ = dviu_per_sp_ = 0; if (res != 0) PkFont::setResolution(res); try { if (fileName_ == "-") { fileName_ = "0"; seekable = false; } if (seekable) { // ordinary file dvif_ = new FileByteStream(fileName_, ".dvi", false); if (read_post) read_postamble(); } else { dvif_ = new InputByteStream(fileName_); read_post = false; } #if HOMEMADE_POSSTATESTACK posStack_ = new PosStateStack(read_post ? postamble_.s : 100); #endif } catch (InputByteStreamError& e) { string msg = "DviFile: can't open file: "; throw DviError (msg + e.problem()); } } DviFile::~DviFile() { delete dvif_; } /** * Skips to the end of the page. This reads the DVI file at high * speed until it finds the next end-of-page event, which it returns, * leaving the DVI file positioned appropriately. If there are in * fact no more pages -- if the last end-of-page event has already * been returned -- then return either a {@link DviFilePostamble} event * or zero, just like {@link #getEvent}. * * @return the next end-of-page event */ DviFileEvent *DviFile::getEndOfPage() { skipPage_ = true; return getEvent(); } /** * Gets an event from the DVI file. * *

This is the routine which does most of the actual work. It * scans through the file reading opcodes. Most of these it handles * itself, but certain ones it handles by returning an event to the * calling routine. * *

The first event this method will return is the {@link * DviFilePreamble} event, and the last one is a {@link * DviFilePostamble} event, after which eof() will be * true, and this method will return only zero. * *

The events which can be returned are all of the subclasses of * {@link DviFileEvent}, qv. * *

When you are finished with the returned event, you should * release it by a call to the event's {@link DviFileEvent#release * release} method, after which * you should make no further reference to it. * * @return the next event from the DVI file, or zero if * eof() is true */ DviFileEvent *DviFile::getEvent() { // If the flag skipPage_ is true, then keep processing the file, but // without returning an event. The eop opcode (number 140) resets // skipPage_ to false. We can't just scan for opcode 140 because (a) // we need to process font-changing commands, so that current_font is // correct at the start of the next page, and (b) we might just come // across byte 140 in data, which would mess us up big-time. DviFileEvent *gotEvent = 0; // non-zero when we've got an event Byte opcode = 255; // illegal opcode int i1; if (eof()) return 0; // Add in any pending update of the horizontal position. if (pending_hupdate_ != 0) { updateH_ (pending_hupdate_, pending_hhupdate_); pending_hupdate_ = pending_hhupdate_ = 0; } // When we start, assume the next character is an opcode. Keep // looping until we both have an event, and skipPage_ is false. while (skipPage_ || ! gotEvent) { opcode = getByte(); int charno; if (verbosity_ > normal) cerr << 'O' << static_cast(opcode) << endl; if (opcode <= 127) // set character { if (current_font_ == 0) throw DviError ("current_font undefined"); pending_hhupdate_ += charEscapement_(opcode); pending_hupdate_ += charWidth_(opcode); gotEvent = new DviFileSetChar(opcode, this); } else if (opcode >= 171 && opcode <= 234) { // fnt_num_0 to fnt_num_63 current_font_ = fontSet_.get(opcode-171); if (current_font_ == 0) throw DviError ("undefined font requested"); gotEvent = new DviFileFontChange (opcode, current_font_); } else { switch (opcode) { case 128: // set1 charno = getSIU(1); if (current_font_ == 0) throw DviError ("current_font undefined"); pending_hhupdate_ += charEscapement_(charno); pending_hupdate_ += charWidth_(charno); gotEvent = new DviFileSetChar (opcode, charno, this); break; case 129: // set2 charno = getSIU(2); if (current_font_ == 0) throw DviError ("current_font undefined"); pending_hhupdate_ += charEscapement_(charno); pending_hupdate_ += charWidth_(charno); gotEvent = new DviFileSetChar (opcode, charno, this); break; case 130: // set3 charno = getSIU(3); if (current_font_ == 0) throw DviError ("current_font undefined"); pending_hhupdate_ += charEscapement_(charno); pending_hupdate_ += charWidth_(charno); gotEvent = new DviFileSetChar (opcode, charno, this); break; case 131: // set4 charno = getSIS(4); if (current_font_ == 0) throw DviError ("current_font undefined"); pending_hhupdate_ += charEscapement_(charno); pending_hupdate_ += charWidth_(charno); gotEvent = new DviFileSetChar (opcode, charno, this); break; case 132: // set_rule { int a = getSIS(4); int b = getSIS(4); pending_hupdate_ += b; if (a > 0 && b > 0) gotEvent = new DviFileSetRule (opcode, this, pixel_round(a), pixel_round(b)); break; } case 133: // put1 gotEvent = new DviFileSetChar (opcode, getSIU(1), this); break; case 134: // put2 gotEvent = new DviFileSetChar (opcode, getSIU(2), this); break; case 135: // put3 gotEvent = new DviFileSetChar (opcode, getSIU(3), this); break; case 136: // put4 gotEvent = new DviFileSetChar (opcode, getSIS(4), this); break; case 137: // put_rule { int a = getSIS(4); int b = getSIS(4); if (a > 0 && b > 0) gotEvent = new DviFileSetRule (opcode, this, pixel_round(a), pixel_round(b)); break; } case 138: // nop break; case 139: // bop { DviFilePage *pageEvent = new DviFilePage(opcode, true); for (int i=0; i<=9; i++) pageEvent->count[i] = getSIS(4); pageEvent->previous = getSIS(4); h_ = v_ = w_ = x_ = y_ = z_ = hh_ = vv_ = 0; #if HOMEMADE_POSSTATESTACK posStack_->clear(); #else while (posStack_.size() > 0) posStack_.pop(); #endif gotEvent = pageEvent; } break; case 140: // eop #if HOMEMADE_POSSTATESTACK if (!posStack_->empty()) #else if (!posStack_.empty()) #endif throw DviBug("EOP: position stack not empty"); gotEvent = new DviFilePage(opcode, false); skipPage_ = false; break; case 141: // push { #if HOMEMADE_POSSTATESTACK const PosState *ps = new PosState(h_,v_,w_,x_,y_,z_,hh_,vv_); posStack_->push(ps); #else PosState ps = PosState(h_,v_,w_,x_,y_,z_,hh_,vv_); posStack_.push(ps); #endif if (verbosity_ > normal) cerr << ">> "<pop(); h_ = ps->h; v_ = ps->v; hh_ = ps->hh; vv_ = ps->vv; w_ = ps->w; x_ = ps->x; y_ = ps->y; z_ = ps->z; delete ps; #else const PosState& ps = posStack_.top(); posStack_.pop(); h_ = ps.h; v_ = ps.v; w_ = ps.w; x_ = ps.x; y_ = ps.y; z_ = ps.z; hh_ = ps.hh; vv_ = ps.vv; //delete ps; #endif if (verbosity_ > normal) cerr << "<< "<0; len--) str += static_cast(getByte()); gotEvent = new DviFileSpecial(opcode, str); } break; case 240: // xxx2 { string str; for (int len = getSIU(2); len>0; len--) str += static_cast(getByte()); gotEvent = new DviFileSpecial(opcode, str); } break; case 241: // xxx3 { string str; for (int len = getSIU(3); len>0; len--) str += static_cast(getByte()); gotEvent = new DviFileSpecial(opcode, str); } break; case 242: // xxx4 { string str; for (int len = getSIS(4); len>0; len--) str += static_cast(getByte()); gotEvent = new DviFileSpecial(opcode, str); } break; // If the postamble was read, then these font // definitions should be duplicates; otherwise, // they're the first declarations of the fonts. case 243: // fnt_def1 fnt_def_(netmag_, 1); break; case 244: // fnt_def2 fnt_def_(netmag_, 2); break; case 245: // fnt_def3 fnt_def_(netmag_, 3); break; case 246: // fnt_def4 fnt_def_(netmag_, 4); break; case 247: // pre { DviFilePreamble *pre = new DviFilePreamble(); pre->dviType = getUIU(1); pre->num = getUIU(4); pre->den = getUIU(4); pre->mag = getUIU(4); pre->comment = ""; for (int k=getSIU(1); k>0; k--) pre->comment += static_cast(getByte()); process_preamble(pre); gotEvent = pre; } break; case 248: // post // don't process it in any way gotEvent = new DviFilePostamble(); have_read_to_postamble_ = true; skipPage_ = false; break; case 249: // post_post // This shouldn't happen within getEvent throw DviBug("post_post found in getEvent"); default: throw DviBug ("unrecognised opcode %d in getEvent", opcode); } } } assert (gotEvent != 0); return gotEvent; } /** * Returns a fallback font, for use when a requested font is not * available. The font returned depends on whether the DVI postamble * was read or not, on what fonts have already been seen in the file, * and on the font desired. This flexibility will not, however, stray * beyond the liberty given by section 4.4, `Missing fonts', in the * DVI standard. * * @param desired a pointer to the font which was requested (but not, * presumably, loaded), or 0 if this information is not available * @return a pointer to a fallback font, or zero if absolutely no * fonts are available */ const PkFont* DviFile::getFallbackFont(const PkFont* desired) { const FontSet* fs = getFontSet(); const PkFont* best_so_far = 0; for (FontSet::const_iterator ci = fs->begin(); ci != fs->end(); ++ci) { const PkFont* f = *ci; if (desired == 0) return f; double diff = STD::fabs(desired->designSize() - f->designSize()); if (diff == 0.0) return f; if (diff < STD::fabs(desired->designSize()-best_so_far->designSize())) best_so_far = f; } return best_so_far; } // getByte and the get?I? routines are just interfaces to the corresponding // routines in InputByteStream /** * Obtains a byte from the DVI file. * @return the next byte from the input stream */ Byte DviFile::getByte() { if (eof()) throw DviBug ("Tried to getByte when no file open"); else { return dvif_->getByte(); } } /** * Obtains an n-byte unsigned integer from the DVI file, * as a signed int. * * @param n the number of bytes to read * @return the next integer from the input stream, as a signed int * @see InputByteStream#getSIU */ signed int DviFile::getSIU(int n) { if (eof()) throw DviBug ("Tried to getSIU when no file open"); else { return dvif_->getSIU(n); } } /** * Obtains an n-byte signed integer from the DVI file, as a signed * int. * * @param n the number of bytes to read * @return the next integer from the input stream, as a signed int * @see InputByteStream#getSIS */ signed int DviFile::getSIS(int n) { if (eof()) throw DviBug ("Tried to getSIS when no file open"); else { return dvif_->getSIS(n); } } /** * Obtains an n-byte unsigned integer from the DVI file, as an * unsigned int * * @param n the number of bytes to read * @return the next integer from the input stream, as an unsigned int * @see InputByteStream#getUIU */ unsigned int DviFile::getUIU(int n) { if (eof()) throw DviBug ("Tried to getUIU when no file open"); else { return dvif_->getUIU(n); } } /** * Indicates whether we are at the end of the DVI file. This is true * if the underlying file is closed, or if we have * read all the pages and {@link #getEvent} has returned a {@link * DviFilePostamble} event. * @return true if we are at EOF */ bool DviFile::eof() { return (have_read_to_postamble_ || dvif_ == 0 || dvif_->eof()); } /** * Convert a DVI length to a pixel length, rounding correctly. See * the DVI standard for details. * * @param dp a length in DVI units * @return the length in pixel units, with any necessary rounding */ int DviFile::pixel_round(int dp) { if (dp > 0) return static_cast( dp/dviu_per_px_ + 0.5); else return -static_cast(-dp/dviu_per_px_ + 0.5); } /** * Convert a TeX scaled point to another unit. * *

It is possible to convert to pixel units with this method; * however it is generally better to either get pixel positions * directly (through {@link #currH} or {@link #currV} for example). * *

The conversions to DVIunits and pixels are not universal, but * are instead dependent on a particular DVI file; if you wish to * convert to either of these units, you must supply a reference to a * DVI file. If not, any argument here is ignored, and may be zero * (the default). * * @param sp the length in scaled points * @param units the unit to convert it to * @param dvif a DVI file, if pixels or DVIunits are requested * * @return the converted unit * * @throws DviError if pixels or DVIunits were requested and no dvif * parameter was supplied * @see #convertToScaledPoints * @see #convertUnits */ double DviFile::convertFromScaledPoints(int sp, DviUnits units, DviFile *dvif) throw (DviError) { // You should generally convert from DVIunits to pixels using // pixel_round() (the conversion here avoids the specific rounding // algorithm required for that). double ans; switch (units) { case unit_pt: ans = sp / 65536.0; break; case unit_pc: ans = sp / 65536.0 / 12.0; break; case unit_in: ans = sp / 65536.0 / 72.27; break; case unit_bp: ans = sp / 65536.0 / 72.27 * 72.0; break; case unit_cm: ans = sp / 65536.0 / 72.27 * 2.54; break; case unit_mm: ans = sp / 65536.0 / 72.27 * 25.4; break; case unit_dd: ans = sp / 65536.0 / 1238.0 * 1157.0; break; case unit_cc: ans = sp / 65536.0 / 1238.0 * 1157.0 / 12.0; break; case unit_sp: ans = sp; break; case unit_pixels: if (dvif == 0) throw DviError ("Conversion to pixels requested, but no DVI file supplied"); ans = sp * dvif->dviu_per_(unit_sp) // ...to dviu / dvif->dviu_per_(unit_pixels); // ...to px break; case unit_dvi: if (dvif == 0) throw DviError ("Conversion to DVIunits requested, but no DVI file supplied"); ans = sp * dvif->dviu_per_(unit_sp); break; default: assert(false); } return ans; } /** * Convert a length to TeX scaled points. * *

The conversions from DVIunits and pixels are not universal, but * are instead dependent on a particular DVI file; if you wish to * convert from either of these units, you must supply a reference to a * DVI file. If not, any argument here is ignored, and may be zero * (the default). * * @param length the length to be converted * @param units the units in which the length is currently * @param dvif a DVI file, if pixels or DVIunits are requested * * @return the input length as a multiple of the TeX scaled-point * * @throws DviError if pixels or DVIunits were requested and no dvif * parameter was supplied * @see #convertFromScaledPoints * @see #convertUnits */ int DviFile::convertToScaledPoints(double length, DviUnits units, DviFile *dvif) throw (DviError) { double ans; switch (units) { case unit_pt: ans = length * 65536.0; break; case unit_pc: ans = length * 65536 * 12; break; case unit_in: ans = length * 65536.0 * 72.27; break; case unit_bp: ans = length * 65536.0 * 72.27 / 72.0; break; case unit_cm: ans = length * 65536.0 * 72.27 / 2.54; break; case unit_mm: ans = length * 65536.0 * 72.27 / 25.4; break; case unit_dd: ans = length * 65536.0 * 1238.0 / 1157.0; break; case unit_cc: ans = length * 65536.0 * 1238.0 / 1157.0 * 12.0; break; case unit_sp: ans = length; break; case unit_pixels: if (dvif == 0) throw DviError ("Conversion from pixels requested, but no DVI file supplied"); ans = length * dvif->dviu_per_(unit_pixels) // ...to dviu / dvif->dviu_per_(unit_sp); // ...to sp break; case unit_dvi: if (dvif == 0) throw DviError ("Conversion from DVIunits requested, but no DVI file supplied"); ans = length / dvif->dviu_per_(unit_sp); break; default: assert(false); } return static_cast(ans+0.5); } /** * Convert a length from one set of units to another. * *

The conversions to DVIunits and pixels are not universal, but * are instead dependent on a particular DVI file; if you wish to * convert to either of these units, you must supply a reference to a * DVI file. If not, any argument here is ignored, and may be zero * (the default). * * @param length the length to be converted * @param from_units the units in which length is currently expressed * @param to_units the target units * @param dvif a DVI file, if pixels or DVIunits are requested * * @return the length expressed as a multiple of the target unit * * @throws DviError if pixels or DVIunits were requested and no dvif * parameter was supplied * @see #convertFromScaledPoints * @see #convertToScaledPoints */ double DviFile::convertUnits(double length, DviUnits from_units, DviUnits to_units, DviFile *dvif) throw (DviError) { if (from_units == to_units) return length; else return convertFromScaledPoints (convertToScaledPoints(length, from_units, dvif), to_units, dvif); } /** * Convert a string to a unit. * @param unitString one of the strings representing a DVI unit * @return the appropriate member of the {@link #DviUnits} enum, or * {@link #unit_BAD} if the unit string is not recognised */ DviFile::DviUnits DviFile::unitType(string unitString) { // simple linear search struct { string name; DviUnits unit; } u[] = { #define MAP(x) { #x, unit_ ## x } // no unit_BAD MAP(pt), MAP(pc), MAP(in), MAP(bp), MAP(cm), MAP(mm), MAP(dd), MAP(cc), MAP(sp), MAP(pixels), { "px", unit_pixels, }, MAP(dvi), { "dviu", unit_dvi, }, #undef MAP }; int nu = sizeof(u)/sizeof(u[0]); for (int i=0; iconvert... functions. May only be used with argument * unit_pixels and unit_sp, and throws an * assertion error otherwise. * * @param unit the unit we want to relate to DVIu * @return the number of DVIunits in the given unit */ double DviFile::dviu_per_(DviUnits unit) { double ans; switch (unit) { case unit_pixels: ans = dviu_per_px_; break; case unit_sp: ans = dviu_per_sp_; break; default: assert(false); } return ans; } /** * Return width of character in DVIUnits * * @param charno the number of the character to examine * @return the width of the character */ int DviFile::charWidth_ (int charno) { if (current_font_ == 0) throw DviError ("current_font undefined (charWidth)"); return static_cast(current_font_->glyph(charno)->tfmWidth() //points * current_font_->magnification(false) * dviu_per_pt_); } /** * Return escapement of character, in pixel units (thus including * magnification). * * @param charno the number of the character to examine * @return the escapement appropriate for the character */ int DviFile::charEscapement_ (int charno) { if (current_font_ == 0) throw DviError ("current_font undefined (charEscapement)"); return static_cast (current_font_->glyph(charno)->hEscapement() // unmagnified * current_font_->magnification()); } /** * Update the horizontal position. If hhup is non-zero, then the last thing * we did was set a character, and this was its width. If it's zero, then * the last action was some other adjustment of the horizontal position, * so we need to update hh_ based on this. See DVI standard, section * 2.6.2 * * @param hup the horizontal update, in (unmagnified) DVIunits * @param hhup the horizontal update in pixels */ void DviFile::updateH_ (int hup, int hhup) { if (hhup == 0) { if (current_font_ != 0 && hup < current_font_->wordSpace() && hup > -current_font_->backSpace()) hh_ += pixel_round(hup); else hh_ = pixel_round(h_ + hup); } else hh_ += hhup; h_ += hup; // check drift int Kh = pixel_round(h_); int dist = hh_ - Kh; int sdist = 1; if (dist < 0) { dist = -dist; sdist = -1; } if (dist > max_drift_) hh_ = Kh + sdist*max_drift_; if (hh_ > widest_page_) widest_page_ = hh_; if (verbosity_ > normal) cerr << "updateH_ (" << hup << ',' << hhup << ") -> (" << h_ << ',' << hh_ << ") dist=" << (sdist > 0 ? '+' : '-') << dist << endl; } /** * Updates the vertical position. * * @param the vertical update to be applied, in DVIunits * @see #updateH_ */ void DviFile::updateV_ (int vup) { double range; if (current_font_ == 0) // no quad defined range = 0; else range = 0.8 * current_font_->quad(); if (STD::abs(vup) < range) vv_ += pixel_round(vup); else vv_ = pixel_round(v_ + vup); v_ += vup; // check drift int Kv = pixel_round(v_); int dist = vv_ - Kv; int sdist = 1; if (dist < 0) { dist = -dist; sdist = -1; } if (dist > max_drift_) vv_ = Kv + sdist*max_drift_; if (vv_ > deepest_page_) deepest_page_ = vv_; if (verbosity_ > normal) cerr << "updateV_ (" << vup << ") -> (" << v_ << ',' << vv_ << ',' << y_ << ',' << z_ << ") dist=" << (sdist > 0 ? '+' : '-') << dist << endl; } void DviFile::read_postamble() throw (DviError) { const int tailbuflen = 64; FileByteStream* dvifile = dynamic_cast(dvif_); if (dvifile == 0) throw DviError ("DviFile::read_postamble didn't receive a FileByteStream!"); dvifile->seek(-tailbuflen); const Byte *dviBuf = dvifile->getBlock(tailbuflen); const Byte *p; for (p=dviBuf+tailbuflen-1; p>=dviBuf; p--) if (*p != 223) break; if (p < dviBuf+5) // buffer doesn't contain post opcode plus q plus id byte throw DviError ("DviFile::read_postamble: can't find post_post"); if (*p != 2) // should be identification byte, 2 throw DviError ("DviFile::read_postamble: identification byte not 2"); p -= 5; // should now be pointing at post_post opcode if (*p != 249) throw DviError ("DviFile::read_postamble: post_post not in correct place"); p++; unsigned int q = InputByteStream::getUIU(4, p); if (verbosity_ > normal) cerr << "Postamble address=" << q << endl; dvifile->seek(q); if (getByte() != 248) throw DviError ("DviFile::read_postamble: expected post command"); // Read and discard four integers (void) dvifile->getUIU(4); // pointer to final bop (void) dvifile->getUIU(4); // unit-of-measurement numerator... (void) dvifile->getUIU(4); // ...and denominator unsigned int dvimag = dvifile->getUIU(4); // mag postamble_.mag = dvimag; // store dvimag postamble_.l = dvifile->getUIU(4); postamble_.u = dvifile->getUIU(4); postamble_.s = dvifile->getUIU(2); postamble_.t = dvifile->getUIU(2); // Multiply the page sizes by the external magnification. Do not // multiply the postamble sizes by this -- leave them as they were // read from the file. // XXX: I'm not absolutely positive that the logic here is correct. The // postamble information is ignored in dvi2bitmap (apart from // fnt_defs), and so errors here aren't noticed or tested. if (extmag_ != 1.0) { dvimag = static_cast(dvimag*extmag_); // postamble_.mag = dvimag; // postamble_.l = static_cast(postamble_.l // * (double)dvimag / 1000.0); // postamble_.u = static_cast(postamble_.u // * (double)dvimag / 1000.0); } // dvimag/1000 is the font magnification factor double fontmag = dvimag/1000.0; if (verbosity_ > normal) cerr << "Postamble: l=" << postamble_.l << " u=" << postamble_.u << " s=" << postamble_.s << " t=" << postamble_.t << "; fontmag=" << fontmag << endl; // process the following font definitions, building up a map of used fonts bool keepreading = true; while (keepreading) { Byte opcode = getByte(); switch (opcode) { case 243: // fnt_def1 fnt_def_(fontmag, 1); break; case 244: // fnt_def2 fnt_def_(fontmag, 2); break; case 245: // fnt_def3 fnt_def_(fontmag, 3); break; case 246: // fnt_def4 fnt_def_(fontmag, 4); break; case 249: // post_post keepreading = false; break; default: // error throw DviError ("unexpected opcode (%d) in postamble", opcode); break; } } have_preread_postamble_ = true; dvifile->seek(0); } /** * Read a font-definition, either in the postamble on in-line. * We read from one to four bytes of font-number, depending on whether * it was opcode 243..246 (fnt_def1..4) which invoked * this. * *

It is not an error for a font to be defined twice -- the second * definition is simply ignored. This is so that we can invoke * fnt_def_ in the postamble and the run of the file, * without worrying about whether the postamble is being or has been * read. This means that we cannot detect if a font is declared * twice. The DVI standard says that this must not happen, but it * does not require processors to warn if it does, so this behaviour * is both convenient and permissable. * * @param fontmag magnification factor (1.0 = no mag) for the font to * be read * @param nbytes number of bytes of font-number to read */ void DviFile::fnt_def_(double fontmag, int nbytes) { int num; unsigned int c, s, d; string fontdir, fontname; if (nbytes < 1 || nbytes > 4) throw DviBug ("Impossible number of bytes (%d) to read in nbytes", nbytes); if (nbytes == 4) num = getSIS(nbytes); else num = getSIU(nbytes); c = getUIU(4); // checksum (see DVI std, A.4) s = getUIU(4); // scale factor, DVI units d = getUIU(4); // design size fontdir = ""; // to be discarded fontname = ""; for (int a = getSIU(1); a>0; a--) fontdir += static_cast(getByte()); for (int l = getSIU(1); l>0; l--) fontname += static_cast(getByte()); PkFont *f = fontSet_.get(num); if (f == 0) { // font hasn't been seen before fontSet_.add(num, new PkFont(fontmag, c, s, d, fontname)); if (verbosity_ > normal) cerr << "fnt_def_: defined font " << num << ": " << fontname << " size=" << fontSet_.size() << "==? " << getFontSet()->size() << endl; } else { // font has been seen already, probably in the postamble if (verbosity_ > normal) cerr << "fnt_def_: found inline definition of font " << num << "=" << f->name() << "/" << fontname << endl; } return; } /** * Process a DVI file preamble. * *

The preamble dimensions num and den `are positive integers that * define the units of measurement; they are the numerator and * denominator of a fraction by which all dimensions in the DVI file * could be multiplied in order to get lengths in units of 10^{-7} * meters. (For example, there are exactly 7227 \TeX\ points in 254 * centimeters, and \TeX82 works with scaled points where there are * $2^{16}$ sp in a point, so \TeX82 sets num=25400000 and * den=7227.2^{16}=473628672.)' [from the spec]. * *

That is, if u is the length of a DVIunit, then u=(n/d)*1e-7m. We * also know, from their definitions, that 7227*2^16sp/2.54e7=1e-7m. * Thus, if we write u=(r*1sp), then *

 *     u = r * 2.54e7 / (7227*2^16) * 1e-7m
 *       = num/dem * 1e-7m
 * 
* or *
 *     r = num/den * (7227*2^16) / 2.54e7
 * 
* For TeX, therefore, r=1, and 1DVIunit = 1sp. * *

Similarly 1pt = 2.54e7/7227 * 1e-7m = (d/n) * (2.54e7/7227) u. * *

There are R pixels (=`device units') in 1 inch: r*1px = 1in = * 72.27pt = 72.27 * (d/n) * (2.54e5/72.27) u. Thus 1px = * (d/n)*2.54e5 u. It is at this point that we impose the * overall magnification, netmag_, by multiplying R by netmag_. * *

We want to use these numbers to establish a conversion between DVI * units and screen pixels, each of which is nominally 1 TeX point * (1/72.27 inch) in size (for a 72dpi screen). So, how many DVI * units are there in a TeX point? Well, the conversion factor above * says that 1pt = 2.54x10^7/7227 (10^{-7}m), so * DVI unit = sp = num/den x 7227/2.54x10^7 x 1pt. Given the above * values for num and den, this works out as * DVI unit = sp = 1/2^16 x 1pt, which we actually knew as soon as we * were told that TeX's DVI files have (DVI units=sp). * *

The `device units' in this case are pixels, each of which is * (1pt=1/72.27in)/netmag_ in size. This matters, as it * determines the size of the max_drift_ parameter, as described in * the DVI standard, section 2.6.2. * * @param p pointer to the preamble */ void DviFile::process_preamble(DviFilePreamble* p) { preamble_.i = p->dviType; preamble_.num = p->num; preamble_.den = p->den; preamble_.mag = p->mag; preamble_.comment = p->comment; if (preamble_.mag == 1000 && extmag_ == 1.0) netmag_ = 1.0; else netmag_ = (double)preamble_.mag/1000.0 * extmag_; // Note dviu_per_pt_ does not include DVI-magnification // (so `true' dviu_per_pt_ would be this/netmag_) double numden = (double)p->num/(double)p->den; // 1dviu = 1/dviu_per_sp_ * 1sp. // In comments above, dviu_per_sp_ = 1/r. // dviu_per_sp_ = 1/r = 1 for DVI files generated by TeX82 // Calculate dviu_per_sp_ carefully, so it ends up ==1.0 in the // usual case. dviu_per_sp_ = 25400000.0/(double)p->num * (double)p->den/(7227*65536); // 1pt = dviu_per_pt_ * 1dviu dviu_per_pt_ = 2.54e7/7227.0 / numden; // 1px = dviu_per_px_ * 1dviu double resolution = PkFont::dpiBase(); dviu_per_px_ = 2.54e5/(resolution*netmag_) / numden; // pixel_size is the size of one pixel, in inches double pixel_size = dviu_per_px_ / dviu_per_pt_ / 72.27; if (pixel_size > 0.01) max_drift_ = 0; else if (pixel_size > 0.005) max_drift_ = 1; else max_drift_ = 2; if (verbosity_ > normal) cerr << "Preamble: dviu_per_pt_ = " << dviu_per_pt_ << ", dviu_per_px_ = " << dviu_per_px_ << ", dviu_per_sp_ = " << dviu_per_sp_ << ", mag=" << preamble_.mag << ", extmag=" << extmag_ << endl << "Scales: resolution=" << PkFont::dpiBase() << " netmag_=" << netmag_ << " pixel_size=" << pixel_size << "in, =>max_drift_=" << max_drift_ << endl; if (dviu_per_sp_ != 1.0 && verbosity_ >= normal) { cerr << "Note: DVI file has unusual dviu/sp ratio: " << dviu_per_sp_ << ", not 1.0" << endl; } } /** * Obtains the `width of the widest page'. This is either the * value obtained from the postamble of the DVI file, if that was * read, or else the maximum value of the horizontal position (as * returned by currH()), if that is larger. If the * postamble has not been read, then this is initialised to -1. * Note that this isn't the same as the maximum value of {@link * #currH}, any more than 0 is the minimum, but if the origin is * set `appropriately' (ie, at (1in,1in)), then everything should * fit on. It's not a precise figure, but can be useful as a * scale for initialising bitmap sizes, for example. * * @return the horizontal size of the largest `page', in pixels */ int DviFile::hSize() { if (widest_page_ < 0 && have_preread_postamble_) widest_page_ = static_cast(postamble_.u / dviu_per_px_); return widest_page_; } /** * Obtains the `height plus depth of the tallest page'. This is either the * value obtained from the postamble of the DVI file, if that was * read, or else the maximum value of the vertical position (as * returned by currV()), if that is larger. If the * postamble has not been read, then this is initialised to -1. * Note that this isn't the same as the maximum value of {@link * #currV}, any more than 0 is the minimum, but if the origin is * set `appropriately' (ie, at (1in,1in)), then everything should * fit on. It's not a precise figure, but can be useful as a * scale for initialising bitmap sizes, for example. * * @return the vertical size of the largest `page', in pixels */ int DviFile::vSize() { if (deepest_page_ < 0 && have_preread_postamble_) deepest_page_ = static_cast(postamble_.l / dviu_per_px_); return deepest_page_; } /** * Creates a new event. * * @param opcode the DVI opcode which resulted in this event * @param t the type of this event * @param dp the DviFile it is associated with */ DviFileEvent::DviFileEvent(unsigned char opcode, eventTypes t, DviFile *dp) : opcode_(opcode), dviFile_(dp), type_(t) { // nothing } /** * Sets the verbosity of this module. * @param level the required verbosity * @return the previous verbosity level */ verbosities DviFile::verbosity (const verbosities level) { enum verbosities oldv = verbosity_; verbosity_ = level; DviFileEvent::verbosity(level); return oldv; } /** * Constructs a set-character event * * @param charno the character to be set * @param dptr pointer to the DVI file which contained this character */ DviFileSetChar::DviFileSetChar(int charno, DviFile *dptr) : DviFileEvent(charno, setchar, dptr), charno_(charno) { if (verbosity_ > normal) cerr << "Char " << charno << "=" << (isprint(charno) ? static_cast(charno) : '?') << endl; } /** * Constructs a set-character event * * @param opcode the opcode which prompted this event * @param charno the character to be set * @param dptr pointer to the DVI file which contained this character */ DviFileSetChar::DviFileSetChar(int opcode, int charno, DviFile *dptr) : DviFileEvent(opcode, setchar, dptr), charno_(charno) { if (verbosity_ > normal) cerr << "Char " << charno << "=" << (isprint(charno) ? static_cast(charno) : '?') << endl; } /** * Sets the verbosity for DviFileEvent and its subclasses * @param level the desired verbosity * @return the old verbosity */ verbosities DviFileEvent::verbosity(const verbosities level) { enum verbosities oldv = verbosity_; verbosity_ = level; return oldv; } /** * Release this event. Client code which has been given an event by * {@link DviFile#getEvent} should call this method on that event when * it has no further use for it. This releases or reclaims all * resources associated with it. This may call delete on * the associated object, so the client code should assume that it has * done so, and make no further reference to the object. */ void DviFileEvent::release() { releaseEvent(this); } /** * Really release the given event. This is called by {@link #release} * to do the work of releasing the object. In future, we may make * this cleverer, and allow it to release the event back into a pool * * @param event the event to be released */ void DviFileEvent::releaseEvent(DviFileEvent *event) { delete event; } void DviFileEvent::debug () const { cerr << 'E' << static_cast(opcode_) << endl; } void DviFileSetChar::debug () const { cerr << 'C' << static_cast(charno_) << "=\'" << static_cast(charno_) << "\'" << endl; } void DviFileSetRule::debug() const { cerr << 'R' << h << 'x' << w << endl; } void DviFileFontChange::debug() const { cerr << 'f' << font->name() << endl; } void DviFileSpecial::debug() const { cerr << 'S' << " \'" << specialString << "\'" << endl; } void DviFilePage::debug() const { if (isStart) { cerr << "P+"; for (int i=0; i<=9; i++) cerr << ' ' << count[i]; cerr << " : previous " << previous << endl; } else cerr << "P-" << endl; } void DviFilePreamble::debug() const { cerr << '!' << "DVI file: " << num << '/' << den << " : \'" << comment << "\'" << endl; } #if HOMEMADE_POSSTATESTACK void DviFile::PosStateStack::push(const PosState *p) { if (i == size) // call it a bug, though it might be the DVI file at fault, since // the stack size should be set from the s parameter in the postamble throw DviBug("Stack overflow"); s[i++] = p; } const DviFile::PosState *DviFile::PosStateStack::pop() { if (i == 0) // the DVI file's at fault, here throw DviError("Stack underflow"); return s[--i]; } DviFile::PosStateStack::PosStateStack(int size) : size(size),i(0) { s = new (const PosState*)[size]; } void DviFile::PosStateStack::clear() { if (i == 0) return; do delete s[--i]; while (i != 0); } #endif // // Implementation of FontSet DviFile::FontSet::FontSet () : myIter_(0) { // empty } DviFile::FontSet::~FontSet() { if (myIter_ != 0) { delete myIter_; myIter_ = 0; } } void DviFile::FontSet::add(int fnt_num, PkFont* newfont) { fontMap_[fnt_num] = newfont; } PkFont* DviFile::FontSet::get(int fnt_num) { return fontMap_[fnt_num]; } DviFile::FontSet::const_iterator DviFile::FontSet::begin() const { if (myIter_ != 0) { delete myIter_; myIter_ = 0; } myIter_ = new const_iterator(fontMap_); return *myIter_; } DviFile::FontSet::const_iterator DviFile::FontSet::end() const { static const_iterator endIterator_; return endIterator_; } DviFile::FontSet::const_iterator::const_iterator() { // empty } DviFile::FontSet::const_iterator::const_iterator(FontMap m) { for (FontMap::const_iterator ci = m.begin(); ci != m.end(); ++ci) { fontlist_.push_back(ci->second); } } DviFile::FontSet::const_iterator::~const_iterator() { // empty } const PkFont* DviFile::FontSet::const_iterator::operator*() const throw (DviError) { if (fontlist_.size() == 0) throw DviError("Tried to dereference empty iterator"); return fontlist_.front(); } DviFile::FontSet::const_iterator& DviFile::FontSet::const_iterator::operator++() throw (DviError) { if (fontlist_.size() == 0) // empty throw new DviError("Tried to increment empty iterator"); fontlist_.pop_front(); return *this; } bool DviFile::FontSet::const_iterator::operator==(const const_iterator& it) const { return fontlist_.size() == it.fontlist_.size(); } bool DviFile::FontSet::const_iterator::operator!=(const const_iterator& it) const { return fontlist_.size() != it.fontlist_.size(); } #if 0 /** * Dereferences the iterator. * @return pointer to the PkFont currently referenced by * the iterator. * @throws DviBug if the iterator is not initialised or has gone past * the end of its sequence. * @see DviFile#begin */ const PkFont* DviFile::const_iterator::operator*() const throw (DviBug) { if (finished_) throw DviBug("DviFile::const_iterator* : out of bounds"); return mapiter_->second; } /** * Increments the iterator. * @return the iterator itself * @see DviFile#begin */ DviFile::const_iterator& DviFile::const_iterator::operator++() { if (finished_) throw DviBug("DviFile::const_iterator++ : out of bounds"); cerr << "operator++" << endl; ++mapiter_; cerr << "mapiter++" << endl; if (mapiter_ == endmapiter_) finished_ = true; return *this; } bool DviFile::const_iterator::operator==(const const_iterator& it) const { return finished_ == it.finished_; } bool DviFile::const_iterator::operator!=(const const_iterator& it) const { return finished_ != it.finished_; } #endif