diff options
Diffstat (limited to 'kdvi/dviFile.cpp')
-rw-r--r-- | kdvi/dviFile.cpp | 406 |
1 files changed, 406 insertions, 0 deletions
diff --git a/kdvi/dviFile.cpp b/kdvi/dviFile.cpp new file mode 100644 index 00000000..521fb39f --- /dev/null +++ b/kdvi/dviFile.cpp @@ -0,0 +1,406 @@ +/* + * Copyright (c) 1994 Paul Vojta. All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * NOTE: + * xdvi is based on prior work as noted in the modification history, below. + */ + +/* + * DVI previewer for X. + * + * Eric Cooper, CMU, September 1985. + * + * Code derived from dvi-imagen.c. + * + * Modification history: + * 1/1986 Modified for X.10 --Bob Scheifler, MIT LCS. + * 7/1988 Modified for X.11 --Mark Eichin, MIT + * 12/1988 Added 'R' option, toolkit, magnifying glass + * --Paul Vojta, UC Berkeley. + * 2/1989 Added tpic support --Jeffrey Lee, U of Toronto + * 4/1989 Modified for System V --Donald Richardson, Clarkson Univ. + * 3/1990 Added VMS support --Scott Allendorf, U of Iowa + * 7/1990 Added reflection mode --Michael Pak, Hebrew U of Jerusalem + * 1/1992 Added greyscale code --Till Brychcy, Techn. Univ. Muenchen + * and Lee Hetherington, MIT + * 4/1994 Added DPS support, bounding box + * --Ricardo Telichevesky + * and Luis Miguel Silveira, MIT RLE. + */ + +#include <config.h> + +#include <kdebug.h> +#include <klocale.h> +#include <ktempfile.h> +#include <qdir.h> +#include <qfileinfo.h> +#include <stdlib.h> +#include <kprocio.h> + +extern "C" { +#include "dvi.h" +} + +#include "../kviewshell/pageSize.h" +#include "dviFile.h" +#include "fontpool.h" +#include "xdvi.h" + + +dvifile::dvifile(const dvifile *old, fontPool *fp) +{ + errorMsg = QString::null; + errorCounter = 0; + page_offset = 0; + suggestedPageSize = 0; + numberOfExternalPSFiles = 0; + numberOfExternalNONPSFiles = 0; + sourceSpecialMarker = old->sourceSpecialMarker; + + dviData = old->dviData.copy(); + + filename = old->filename; + size_of_file = old->size_of_file; + end_pointer = dvi_Data()+size_of_file; + if (dvi_Data() == 0) { + kdError(4300) << "Not enough memory to copy the DVI-file." << endl; + return; + } + + font_pool = fp; + filename = old->filename; + generatorString = old->generatorString; + total_pages = old->total_pages; + + + tn_table.clear(); + process_preamble(); + find_postamble(); + read_postamble(); + prepare_pages(); +} + + +void dvifile::process_preamble() +{ + command_pointer = dvi_Data(); + + Q_UINT8 magic_number = readUINT8(); + if (magic_number != PRE) { + errorMsg = i18n("The DVI file does not start with the preamble."); + return; + } + magic_number = readUINT8(); + if (magic_number != 2) { + errorMsg = i18n("The DVI file contains the wrong version of DVI output for this program. " + "Hint: If you use the typesetting system Omega, you have to use a special " + "program, such as oxdvi."); + return; + } + + /** numerator, denominator and the magnification value that describe + how many centimeters there are in one TeX unit, as explained in + section A.3 of the DVI driver standard, Level 0, published by + the TUG DVI driver standards committee. */ + Q_UINT32 numerator = readUINT32(); + Q_UINT32 denominator = readUINT32(); + _magnification = readUINT32(); + + cmPerDVIunit = (double(numerator) / double(denominator)) * (double(_magnification) / 1000.0) * (1.0 / 1e5); + + + // Read the generatorString (such as "TeX output ..." from the + // DVI-File). The variable "magic_number" holds the length of the + // string. + char job_id[300]; + magic_number = readUINT8(); + strncpy(job_id, (char *)command_pointer, magic_number); + job_id[magic_number] = '\0'; + generatorString = job_id; +} + + +/** find_postamble locates the beginning of the postamble and leaves + the file ready to start reading at that location. */ + +void dvifile::find_postamble() +{ + // Move backwards through the TRAILER bytes + command_pointer = dvi_Data() + size_of_file - 1; + while((*command_pointer == TRAILER) && (command_pointer > dvi_Data())) + command_pointer--; + if (command_pointer == dvi_Data()) { + errorMsg = i18n("The DVI file is badly corrupted. KDVI was not able to find the postamble."); + return; + } + + // And this is finally the pointer to the beginning of the postamble + command_pointer -= 4; + beginning_of_postamble = readUINT32(); + command_pointer = dvi_Data() + beginning_of_postamble; +} + + +void dvifile::read_postamble() +{ + Q_UINT8 magic_byte = readUINT8(); + if (magic_byte != POST) { + errorMsg = i18n("The postamble does not begin with the POST command."); + return; + } + last_page_offset = readUINT32(); + + // Skip the numerator, denominator and magnification, the largest + // box height and width and the maximal depth of the stack. These + // are not used at the moment. + command_pointer += 4 + 4 + 4 + 4 + 4 + 2; + + // The number of pages is more interesting for us. + total_pages = readUINT16(); + + // As a next step, read the font definitions. + Q_UINT8 cmnd = readUINT8(); + while (cmnd >= FNTDEF1 && cmnd <= FNTDEF4) { + Q_UINT32 TeXnumber = readUINT(cmnd-FNTDEF1+1); + Q_UINT32 checksum = readUINT32(); // Checksum of the font, as found by TeX in the TFM file + + // Read scale and design factor, and the name of the font. All + // these are explained in section A.4 of the DVI driver standard, + // Level 0, published by the TUG DVI driver standards committee + Q_UINT32 scale = readUINT32(); + Q_UINT32 design = readUINT32(); + Q_UINT16 len = readUINT8() + readUINT8(); // Length of the font name, including the directory name + char *fontname = new char[len + 1]; + strncpy(fontname, (char *)command_pointer, len ); + fontname[len] = '\0'; + command_pointer += len; + +#ifdef DEBUG_FONTS + kdDebug(4300) << "Postamble: define font \"" << fontname << "\" scale=" << scale << " design=" << design << endl; +#endif + + // According to section A.4 of the DVI driver standard, this font + // shall be enlarged by the following factor before it is used. + double enlargement_factor = (double(scale) * double(_magnification))/(double(design) * 1000.0); + + if (font_pool != 0) { + TeXFontDefinition *fontp = font_pool->appendx(fontname, checksum, scale, enlargement_factor); + + // Insert font in dictionary and make sure the dictionary is big + // enough. + if (tn_table.size()-2 <= tn_table.count()) + // Not quite optimal. The size of the dictionary should be a + // prime for optimal performance. I don't care. + tn_table.resize(tn_table.size()*2); + tn_table.insert(TeXnumber, fontp); + } + + // Read the next command + cmnd = readUINT8(); + } + + if (cmnd != POSTPOST) { + errorMsg = i18n("The postamble contained a command other than FNTDEF."); + return; + } + + // Now we remove all those fonts from the memory which are no longer + // in use. + if (font_pool != 0) + font_pool->release_fonts(); +} + + +void dvifile::prepare_pages() +{ +#ifdef DEBUG_DVIFILE + kdDebug(4300) << "prepare_pages" << endl; +#endif + + if (page_offset.resize(total_pages+1) == false) { + kdError(4300) << "No memory for page list!" << endl; + return; + } + for(int i=0; i<=total_pages; i++) + page_offset[i] = 0; + + + page_offset[total_pages] = beginning_of_postamble; + Q_UINT16 i = total_pages-1; + page_offset[i] = last_page_offset; + + // Follow back pointers through pages in the DVI file, storing the + // offsets in the page_offset table. + while (i > 0) { + command_pointer = dvi_Data() + page_offset[i--]; + if (readUINT8() != BOP) { + errorMsg = i18n("The page %1 does not start with the BOP command.").arg(i+1); + return; + } + command_pointer += 10 * 4; + page_offset[i] = readUINT32(); + if ((dvi_Data()+page_offset[i] < dvi_Data())||(dvi_Data()+page_offset[i] > dvi_Data()+size_of_file)) + break; + } +} + + +dvifile::dvifile(const QString& fname, fontPool* pool) +{ +#ifdef DEBUG_DVIFILE + kdDebug(4300) << "init_dvi_file: " << fname << endl; +#endif + + errorMsg = QString::null; + errorCounter = 0; + page_offset = 0; + suggestedPageSize = 0; + numberOfExternalPSFiles = 0; + numberOfExternalNONPSFiles = 0; + font_pool = pool; + sourceSpecialMarker = true; + + QFile file(fname); + filename = file.name(); + file.open( IO_ReadOnly ); + size_of_file = file.size(); + dviData.resize(size_of_file); + // Sets the end pointer for the bigEndianByteReader so that the + // whole memory buffer is readable + end_pointer = dvi_Data()+size_of_file; + if (dvi_Data() == 0) { + kdError() << i18n("Not enough memory to load the DVI-file."); + return; + } + file.readBlock((char *)dvi_Data(), size_of_file); + file.close(); + if (file.status() != IO_Ok) { + kdError() << i18n("Could not load the DVI-file."); + return; + } + + tn_table.clear(); + + process_preamble(); + find_postamble(); + read_postamble(); + prepare_pages(); + + return; +} + + +dvifile::~dvifile() +{ +#ifdef DEBUG_DVIFILE + kdDebug(4300) << "destroy dvi-file" << endl; +#endif + + // Delete converted PDF files + QMap<QString, QString>::Iterator it; + for ( it = convertedFiles.begin(); it != convertedFiles.end(); ++it ) + QFile::remove(it.data()); + + if (suggestedPageSize != 0) + delete suggestedPageSize; + if (font_pool != 0) + font_pool->mark_fonts_as_unused(); +} + + +void dvifile::renumber() +{ + dviData.detach(); + + // Write the page number to the file, taking good care of byte + // orderings. + int wordSize; + bool bigEndian; + qSysInfo (&wordSize, &bigEndian); + + for(Q_UINT32 i=1; i<=total_pages; i++) { + Q_UINT8 *ptr = dviData.data() + page_offset[i-1]+1; + Q_UINT8 *num = (Q_UINT8 *)&i; + for(Q_UINT8 j=0; j<4; j++) + if (bigEndian) { + *(ptr++) = num[0]; + *(ptr++) = num[1]; + *(ptr++) = num[2]; + *(ptr++) = num[3]; + } else { + *(ptr++) = num[3]; + *(ptr++) = num[2]; + *(ptr++) = num[1]; + *(ptr++) = num[0]; + } + } +} + + +QString dvifile::convertPDFtoPS(const QString &PDFFilename) +{ + // Check if the PDFFile is known + QMap<QString, QString>::Iterator it = convertedFiles.find(PDFFilename); + if (it != convertedFiles.end()) { + // PDF-File is known. Good. + return it.data(); + } + + // Get the name of a temporary file + KTempFile tmpfile(QString::null, ".ps"); + QString convertedFileName = tmpfile.name(); + tmpfile.close(); + tmpfile.unlink(); + + // Use pdf2ps to do the conversion + KProcIO proc; + proc << "pdf2ps" << PDFFilename << convertedFileName; + if (proc.start(KProcess::Block) == false) + convertedFileName = QString::null; // Indicates that conversion failed, won't try again. + if (!QFile::exists(convertedFileName)) + convertedFileName = QString::null; // Indicates that conversion failed, won't try again. + + // Save name of converted file to buffer, so PDF file won't be + // converted again, and files can be deleted when *this is + // deconstructed. + convertedFiles[PDFFilename] = convertedFileName; + + return convertedFileName; +} + + +bool dvifile::saveAs(const QString &filename) +{ + if (dvi_Data() == 0) + return false; + + QFile out(filename); + if (out.open( IO_Raw|IO_WriteOnly ) == false) + return false; + if (out.writeBlock ( (char *)(dvi_Data()), size_of_file ) == -1) + return false; + out.close(); + return true; +} |