1

I try to read .dvi file in c#. I have a problem with the 3 word or 24 byte integer.

How do I do the HiLo field swapping on this, .NET is in little endian and i believe .dvi is in big endian. And the 24 byte integer is slightly unique.

My goal is to read it to a 32 byte int type with 2's complement handling.

My goal is to read the dvi and the \special{} as extensions from TeX.

But to get the \special out and handle it in code seems to require to parse the whole dvi binary. To do this I already have tif and gf files in parsed.

3

Your question is more about C# than about the DVI format (and so more appropriate for Stack Overflow), but let me answer anyway. Yes, the integers in the DVI format are indeed big-endian. To read the 24-bit (or 3-byte) integers, you can just read them byte-by-byte and perform the arithmetic yourself: for example, if r is such that r.ReadByte() gives the next byte, in range 0–255 (and advances the position), then you can read a 24-bit integer with:

(r.readByte() * 256 + r.readByte()) * 256 + r.readByte()

I think that answers your question, but just for completeness, and to illustrate how simple the DVI format is (see 1, 2, 3, 4, or 5), here is a complete C# program that parses an entire DVI file and writes out only the DVI specials. (The output has been verified against that of dvitype foo.dvi | grep xxx which is a simple Unix pipeline to get the same.) It can easily be extended to do more if necessary.

using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;

namespace DviSpecials {
  class Program {
    // Reads from |r| an integer (big-endian) that is |length| bytes long.
    static int ReadInt(BinaryReader r, int length) {
      int n = 0; 
      for (int i = 0; i < length; ++i) n = n * 256 + r.ReadByte();
      return n;
    }
    // Reads a DVI special command (xxx1 to xxx4) 
    static byte[] ReadSpecial(Byte op, BinaryReader r) {
      if (!(239 <= op && op <= 242)) throw new Exception("Not a special");
      int k = ReadInt(r, op - 238);
      return r.ReadBytes(k);
    }
    // Returns the list of bytes for one DVI command.
    // (Actually, drops info for fnt_def; interface needs to change.)
    static List<byte> ReadOneCommand(BinaryReader r)
    {
      // Commands that take no (so 0 bytes of) additional parameters.
      byte[] commands0 = { 
        // other than set_char 0 to 127 and fnt_num 171 to 234
        138, 140, 141, 142, 147, 152, 161,
      };
      // Commands that take 1,2,...,44 additional bytes of parameters respectively.
      byte[] commands1 = { 128, 133, 143, 148, 153, 157, 162, 167, 235 };
      byte[] commands2 = { 129, 134, 144, 149, 154, 158, 163, 168, 236 };
      byte[] commands3 = { 130, 135, 145, 150, 155, 159, 164, 169, 237 };
      byte[] commands4 = { 131, 136, 146, 151, 156, 160, 165, 170, 238 };
      byte[] commands8 = { 132, 137 };
      byte[] commands44 = { 139 };
      // Undefined commands.
      byte[] commandsUd = { 250, 251, 252, 253, 254, 255 };

      List<byte> ret = new List<byte> { r.ReadByte() };
      byte b = ret[0];
      if (b <= 127 || (171 <= b && b <= 234) || commands0.Contains(b)) return ret;
      if (commands1.Contains(b)) ret.Add(r.ReadByte());
      if (commands2.Contains(b)) ret.AddRange(r.ReadBytes(2));
      if (commands3.Contains(b)) ret.AddRange(r.ReadBytes(3));
      if (commands4.Contains(b)) ret.AddRange(r.ReadBytes(4));
      if (commands8.Contains(b)) ret.AddRange(r.ReadBytes(8));
      if (commands44.Contains(b)) ret.AddRange(r.ReadBytes(44));
      if (commandsUd.Contains(b)) throw new Exception("Undefined command");
      if (239 <= b && b <= 242) ret.AddRange(ReadSpecial(b, r));
      if (243 <= b && b <= 246) {
        // fnt_def k[1-4] c[4] s[4] d[4] a[1] l[1] n[a + l]
        int k = ReadInt(r, b - 242);
        ret.AddRange(r.ReadBytes(12));
        int a = r.ReadByte();
        int l = r.ReadByte();
        ret.AddRange(r.ReadBytes(a + l));
      }
      if (b == 247) { // pre: i[1] num[4] den[4] mag[4] k[1] x[k]
        ret.AddRange(r.ReadBytes(13));
        int k = r.ReadByte();
        ret.AddRange(r.ReadBytes(k));
      }
      if (b == 248) { // post: p[4] num[4] den[4] mag [4] l[4] u[4] s[2] t[2]
        ret.AddRange(r.ReadBytes(28));
      }
      if (b == 249) { // post_post: q[4] i[1] 223s[>=4]
        ret.AddRange(r.ReadBytes(5));
        if (r.BaseStream.Length < r.BaseStream.Position + 4) {
          throw new Exception("Expecting DF at least 4 times");
        }
        while (r.BaseStream.Position != r.BaseStream.Length) {
          if (r.ReadByte() != 223) throw new Exception("Expecting 0xDF");
        }
      }
      return ret;
    }

    static int Main(string[] args) {
      if (args.Length == 0) {
        System.Console.WriteLine("Please pass a DVI file as argument.");
        return 1;
      }
      String filepath = args[0];
      using (FileStream fs = File.Open(filepath, FileMode.Open)) {
        using (BinaryReader r = new BinaryReader(fs)) {
          while (r.BaseStream.Position != r.BaseStream.Length) {
            long pos = r.BaseStream.Position;
            List<byte> bs = ReadOneCommand(r);
            byte op = bs[0];
            if (239 <= op && op <= 242) {
              // Assumes the special is a UTF8 string. Change for arbitrary bytes.
              var s = System.Text.Encoding.UTF8.GetString(bs.Skip(1).ToArray());
              Console.WriteLine($"{pos}: xxx '{s}'");
            }
          }
        }
      }
      return 0;
    }
  }
}

For more questions about the DVI format, apart from the links above, you can look at the source code of programs like DVItype, dvipng, dvi2bitmap, or dvisvgm (the last may be best, as it's actively maintained and supports many extensions to DVI such as XeTeX's xdv).

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.