/* This file is part of dvi2bitmap; see README for copyrights and licence */ #ifndef DVI_FILE_HEADER_READ #define DVI_FILE_HEADER_READ 1 #include #ifdef HAVE_CSTD_INCLUDE #include #else #include #endif #include #include #include #include #include #include #include #include #include // I think the GCC 2.8.1 stack implementation may be buggy. // Setting this to 1 switches on a home-made version. // This hasn't received _extensive_ testing, but seems to work OK. // This is pretty crappy, though, and I intend to remove it next time // I'm feeling brave. If you have to enable this to get this library // to compile, therefore, tell me (norman@astro.gla.ac.uk) so that I // don't discard this. If you need to enable this, then you should // probably call the DviFile constructors with read_post true, so // that the maximum stack size can be read from the postamble, and // the homemade posstack therefore initialised to the correct length. #ifndef HOMEMADE_POSSTATESTACK #define HOMEMADE_POSSTATESTACK 0 #endif class DviFileEvent; class DviFilePreamble; /** * Represents a DVI file. See the DVI Driver standard [driv-std] for * details of the parameters here. * *

[driv-std] Level-0 DVI Driver Standard, available on-line on CTAN, in * the directory * dviware/driv-standard. */ class DviFile { public: DviFile (string& s, int resolution=0, double magmag=1.0, bool read_postamble=true, bool seekable=true) throw (DviError); ~DviFile(); bool eof(); DviFileEvent *getEvent(); DviFileEvent *getEndOfPage(); /** * Units of length. Used in {@link #currH} and {@link #currV}, * and other conversions are available through method {@link * #convertFromScaledPoints}. */ enum DviUnits { /** * An invalid unit. */ unit_BAD, /** * Traditional printer's point. There are 72.27pt in 1in. */ unit_pt, /** * Pica; 1pc=12pt. */ unit_pc, /** * Inch; 1in=25.4mm. */ unit_in, /** * The big point; 72bp=1in. Postscript points are bigpoints * in this terminology. */ unit_bp, /** * Centimetre; 1cm=10E-2m. */ unit_cm, /** * Millimetre; 1mm=10E-3m. One metre is the distance light * travels, in vacuum, in a time 1/299792458 seconds. * *

Conversion factors: 1mm=2.84527559pt; 1pt=0.3515mm. */ unit_mm, /** * Didot point; 1157dd=1238pt. */ unit_dd, /** * Cicero; 1cc=12dd. */ unit_cc, /** * TeX `scaled points'. These are the dimensions which TeX * itself works in, where 65536sp=1pt, or * @manonly 1sp=5.36434 * 10**-9 @endmanonly * @latexonly 1sp=$5.36434\times 10^{-9}$ @endlatexonly * @htmlonly 1sp=5.36434×10-9 @endhtmlonly * metres. Light at that wavelength is classed * as soft X-rays, and is a health-hazard (so don't examine * your scaled points too closely); it's about the size of 50 atoms. */ unit_sp, /** * Pixel units. The DVI standard calls these `device * units', and refers to them with the notation hh. */ unit_pixels, /** * DVI units. All dimensions within the DVI file are * expressed in these units, written as h in the * standard. The conversion of DVI units to physical units is * governed by the values in the preamble. DVI files written by TeX * (that is, essentially all of them, except those written by * converters such as dtl, which write * compatible ones) have a preamble which ensures that the DVI * units are scaled points, times the overall magnification factor. */ unit_dvi }; static DviUnits unitType(string unitString); static string unitString(DviUnits unit); /** * Obtains the current horizontal position. The position can be * reported in any of #unit_pixels, #unit_dvi or * #unit_sp; it is an error to call this function with * any other of the defined units. * *

The conversion to pixel units includes any drift * correction, and is correctly rounded. Scaled points are * calculated as DVI units times the overall magnification (that * is, we ignore the general case of DVI files with odd preamble * scalings). * * @return the horizontal position, in the chosen units * @throws DviError if we are invoked with an inappropriate unit argument */ int currH(DviUnits units=unit_pixels) const throw (DviError) { int r; switch (units) { case unit_pixels: r = hh_; /* device units */ break; case unit_dvi: r = h_; break; case unit_sp: r = (netmag_ == 1.0 ? h_ : static_cast(h_*netmag_)); break; default: throw DviError("Bad unit in currH"); } return r; } /** * Obtains the current vertical position. See {@link #currH}, to * which this is precisely analogous. */ int currV(DviUnits units=unit_pixels) const throw (DviError) { int r; switch (units) { case unit_pixels: r = vv_; /* device units */ break; case unit_dvi: r = v_; break; case unit_sp: r = (netmag_ == 1.0 ? v_ : static_cast(v_*netmag_)); break; default: throw DviError("Bad unit in currV"); } return r; } int hSize(); int vSize(); static double convertFromScaledPoints(int sp, DviUnits units, DviFile *dvif=0) throw (DviError); static int convertToScaledPoints(double length, DviUnits units, DviFile *dvif=0) throw (DviError); static double convertUnits(double length, DviUnits from_units, DviUnits to_units, DviFile *dvif=0) throw (DviError); static verbosities verbosity(const verbosities level); /** * Return the net magnification factor for the DVI file * @return the overall magnification factor applied to lengths in * the DVI file. A value of 1.0 implies no magnification at all. */ double magnification() const { return netmag_; } /** * Converts a length in points to one in pixels, using the current * magnifications and any other relevant parameters. * * @param npt a length in points * @return the given length, in pixels */ int pt2px(double npt) const { return static_cast (npt * dviu_per_pt_ / dviu_per_px_ + 0.5); } /** * Gets the name of this DVI file. * @return the open file name as a string */ const string *filename () const { return &fileName_; } const PkFont* getFallbackFont(const PkFont* desired); /** * Reports whether the DVI file postamble was read when this file * was opened. This affects the semantics of such methods as * {@link #getFontSet}. Note that this only reports whether the * postamble was read at the start of processing, and it * does not become true when the postamble is discovered at the * end; it is not an end-of-file indicator. For that, see the * {@link #eof} method. * * @return true if the postamble was (successfully) read */ bool haveReadPostamble() const { return have_preread_postamble_; } private: string fileName_; /** Horizontal and other positions. All dimensions within this * class are in DVI units, except where stated. That means that they * are not multiplied by any magnification factors. */ int h_, v_, w_, x_, y_, z_; /** Horizontal and other positions in pixel units. This includes * any magnifications. */ int hh_, vv_; /** Updates to h_ yet to be applied. In DVIunits */ int pending_hupdate_; /** Updates to hh_ yet to be applied. In pixel units */ int pending_hhupdate_; PkFont *current_font_; InputByteStream *dvif_; /** * Conversion between DVI units and points. DVIunits are defined * by the numerator and denominator specified in the DVI preamble. * The definition is 1pt = dviu_per_pt_ * 1dviu, and * thus for a distance d, d/dviu = * dviu_per_pt_ x d/pt. This does not include * any DVI-magnification. */ double dviu_per_pt_; /** * Conversion between DVIunits and pixels. This is * 1/K, where K is the constant referred to in * [driv-std], which multiplies lengths in DVIunits to get * lengths in pixels, or 1px = dviu_per_px_ * * 1dviu. It includes DVI-magnification. */ double dviu_per_px_; /** * Conversion between DVIunits and scaled points. For DVI files * written by TeX82, this will have the value 1 (see [driv-std]). * Definition is 1sp = dviu_per_sp_ * 1dviu. */ double dviu_per_sp_; double dviu_per_(DviUnits unit); /** * The factor by which the file's internal magnification * should be increased (1.0 = no magnification). This is set * externally to the DVI file. */ const double extmag_; /** * Net magnification. The externally-imposed magnification times * the DVI file's own magnification (1.0 = no magnification). */ double netmag_; // tell getEvent to skip this page bool skipPage_; /** * Maximum drift parameter. See [driv-std]. */ int max_drift_; /** * Largest width in pixels. This is either initialised to the * value given in the postamble, scaled to pixels, or is the * maximum value of hh_ encountered so far. */ int widest_page_; /** * Largest value of the height+depth, in pixels. This is either * initialised to the value given in the postamble, scaled to * pixels, or is the maximum value of vv_ encountered * so far. */ int deepest_page_; Byte getByte(); signed int getSIU(int), getSIS(int); unsigned int getUIU(int); struct { unsigned int mag, l, u, s, t; } postamble_; struct { unsigned int i, num, den, mag; string comment; } preamble_; void read_postamble() throw (DviError); bool have_preread_postamble_; /* we sought to it at beginning */ bool have_read_to_postamble_; /* we have read through to end */ void process_preamble(DviFilePreamble *); void fnt_def_(double fontmag, int nbytes); void check_duplicate_font(int); int pixel_round(int); int charWidth_ (int charno); int charEscapement_ (int charno); // updateH/V update the horizontal position by an amount in DVI units void updateH_ (int hup, int hhup); void updateV_ (int y); struct PosState { const int h, v, w, x, y, z, hh, vv; PosState(int h, int v, int w, int x, int y, int z, int hh, int vv) : h(h),v(v),w(w),x(x),y(y),z(z),hh(hh),vv(vv) { } }; #if HOMEMADE_POSSTATESTACK class PosStateStack { // It seems wrong to implement a stack rather than using the standard // one, but either I'm doing something wrong the way // I use the STL stack, or else it's (horrors!) buggy. In any case, // it's reasonable to use a non-extendable stack, since the DVI // postamble specifies the maximum stack size required. public: PosStateStack(int size); void push(const PosState *p); const PosState *pop(); bool empty() const { return i == 0; } void clear(); private: unsigned int size, i; const PosState **s; }; PosStateStack *posStack_; #else STD::stack posStack_; #endif static verbosities verbosity_; public: /** * Represents the set of fonts in a DVI file */ class FontSet { private: FontSet(); ~FontSet(); void add(int fnt_num, PkFont* newfont); PkFont* get(int fnt_num); friend class DviFile; typedef STD::map FontMap; FontMap fontMap_; public: /** * Tests whether the FontSet is empty * @return true if the FontSet is empty */ bool empty() const { return fontMap_.empty(); } /** * Determines the number of fonts in the FontSet * @return the number of fonts available */ size_t size() const { return fontMap_.size(); } /** * Represents an iteration over all the fonts in this DVI file */ class const_iterator; friend class DviFile::FontSet::const_iterator; /* so it sees FontMap */ class const_iterator { public: const PkFont* operator*() const throw (DviError); const_iterator& operator++() throw (DviError); bool operator==(const const_iterator& it) const; bool operator!=(const const_iterator& it) const; ~const_iterator(); private: const_iterator(); const_iterator(FontMap m); STD::list fontlist_; friend class DviFile::FontSet; }; const_iterator begin() const; const_iterator end() const; private: mutable const_iterator* myIter_; }; private: FontSet fontSet_; public: /** * Obtains a representation of the set of fonts contained in this * DVI file. If the postamble was read, then the * FontSet returned by this method will be complete; * if not, it will simply represent the set of fonts read so far * in the file. * * @return a pointer to the FontSet for this file */ const FontSet* getFontSet() const { return &fontSet_; } // Why can't I have 'FontSet& getFontSet() const { return fontSet_; };'? #if 0 public: class const_iterator { public: const PkFont* operator*() const throw (DviBug); const_iterator& operator++(); bool operator==(const const_iterator& it) const; bool operator!=(const const_iterator& it) const; #if 0 bool operator==(const const_iterator& it) const { return finished_ == it.finished_; } bool operator!=(const const_iterator& it) const { return finished_ != it.finished_; } #endif private: /* These should be implementable more compactly, since we're just using map's iterator, but there's some visibility subtlety that escapes me... */ const_iterator(FontMap::const_iterator m, FontMap::const_iterator me) { mapiter_ = m; endmapiter_ = me; finished_ = false; }; const_iterator() : finished_(true) { } FontMap::const_iterator mapiter_; FontMap::const_iterator endmapiter_; bool finished_; friend class DviFile; }; /** * Creates an initialised PkFont iterator. This * allows you to step through the collection of * PkFont instances which are contained within this * DviFile. * @return initialised iterator */ const_iterator begin() { return const_iterator(fontMap_.begin(), fontMap_.end()); } /** * Represents the end of the PkFont sequence begun by * begin() * @return iterator representing the end of sequence * @see #begin */ const_iterator end() const { return const_iterator(); }; friend class const_iterator; #endif }; /* DviFileEvent is what is returned to the client from the DVI reading class. * Declare one derived class for each type of event. * * This is rather bad design - these classes should be subclasses of DviFile * above. * DviFileEvent is a virtual class, so these derived classes should have * non-virtual destructors. * XXX these shouldn't be subclasses of DviFile, I don't think, but * they should have DviFile as a friend (correct?), so that only * DviFile can create instances */ /** * Abstracts the contents of a DVI file. All the features of a DVI * file which calling code might be interested in are represented by * one of the subclasses of this, and these are obtained in order by * calling the {@link DviFile#getEvent} method on * DviFile. * *

The design here is likely to be adjusted in future releases of * this library. Although it is possible now, you should avoid * constructing any of the subtypes DviFileEvent * yourself, but use only those returned to you by {@link DviFile#getEvent}. * *

The documentation here concentrates on how the methods and * variables here relate to the underlying quantities obtained from * the DVI file. For fuller information on these, see the DVI standard. */ class DviFileEvent { public: enum eventTypes { setchar, setrule, fontchange, special, page, preamble, postamble }; /** * Writes a representation of this event on cerr */ virtual void debug() const; /** * Gets the type of this event. This information can also be * obtained in a more object-oriented style by attempting a cast * to the appropriate subtype, using, for example, * dynamic_cast<DviFileSetChar *>(event_ptr). * * @return the type */ eventTypes type() const { return type_; } /** * Gets the underlying opcode which produced this event. * @return the opcode */ const unsigned char opcode() const { return opcode_; } void release(); static verbosities verbosity(const verbosities level); protected: DviFileEvent(unsigned char opcode, eventTypes t, DviFile *dp=0); static verbosities verbosity_; private: const unsigned char opcode_; DviFile *dviFile_; const eventTypes type_; static void releaseEvent(DviFileEvent *e); }; /** An event which requests that a given character be set in place */ class DviFileSetChar : public DviFileEvent { public: /** * A request that a given character be set * @param charno the character to be set * @param dptr the DviFile associated with this character */ DviFileSetChar(int charno, DviFile *dptr); DviFileSetChar(int opcode, int charno, DviFile *dptr); void debug() const; /** * Obtains the character which is to be set. * @return the character as an integer */ const int charno() const { return charno_; } private: const int charno_; }; /** An event which requests that a rule be set */ class DviFileSetRule: public DviFileEvent { public: /** * The height of the rule to be set at this position. Given in * pixels/device units. */ const int h; /** * The width of the rule to be set at this position. Given in * pixels/device units. */ const int w; DviFileSetRule(unsigned char opcode, DviFile *dptr, int h, int w) : DviFileEvent(opcode, setrule, dptr), h(h), w(w) { } void debug() const; }; /** An event which requests a change of font */ class DviFileFontChange : public DviFileEvent { public: DviFileFontChange(unsigned char opcode, PkFont *f) : DviFileEvent(opcode, fontchange), font(f) { } void debug() const; /** The font we are to change to. */ const PkFont *font; }; /** A special embedded within the file */ class DviFileSpecial : public DviFileEvent { public: DviFileSpecial(unsigned char opcode, string str) : DviFileEvent(opcode, special), specialString(str) { } /** The content of the special, as stored in the DVI file. */ const string specialString; void debug() const; }; /** The beginning or end of a page. */ class DviFilePage : public DviFileEvent { public: DviFilePage(unsigned char opcode, bool isStart) : DviFileEvent(opcode, page), isStart(isStart) { } void debug() const; /** * Is this the beginning or end of a page? Member * isStart is true if this event was produced by a * bop even (opcode 139), and false if it came from a * eop (opcode 140) event. */ const bool isStart; /** * If isStart is true, then count[] holds * the ten TeX page counters. */ signed int count[10]; /** * If isStart is true, then previous * holds the offset within the DVI file of the previous page in * the sequence. */ signed int previous; }; /** The preamble to the DVI file */ class DviFilePreamble : public DviFileEvent { public: DviFilePreamble() : DviFileEvent(247, preamble) { } void debug() const; /** * The DVI file identification byte. Always 2 for DVI files * produced by standard TeX. */ unsigned int dviType; /** * The numerator of the fraction defining the standard of measurement. */ unsigned int num; /** * The denominator of the fraction defining the standard of measurement. */ unsigned int den; /** * Magnification specification. 1000 times the desired * magnification factor. */ unsigned int mag; /** * The DVI file comment, as a string. */ string comment; }; /** The postamble to (end of) the DVI file. */ class DviFilePostamble : public DviFileEvent { public: DviFilePostamble() : DviFileEvent(248, postamble) { } }; #endif //#ifndef DVI_FILE_HEADER_READ