diff options
Diffstat (limited to 'backend/dvi/mdvi-lib')
37 files changed, 13751 insertions, 0 deletions
diff --git a/backend/dvi/mdvi-lib/Makefile.am b/backend/dvi/mdvi-lib/Makefile.am new file mode 100644 index 00000000..e976aa60 --- /dev/null +++ b/backend/dvi/mdvi-lib/Makefile.am @@ -0,0 +1,43 @@ +INCLUDES = $(WARN_CFLAGS) +noinst_LTLIBRARIES = libmdvi.la + +libmdvi_la_SOURCES = \ + afmparse.c \ + afmparse.h \ + bitmap.c \ + bitmap.h \ + color.c \ + color.h \ + common.c \ + common.h \ + defaults.h \ + dviopcodes.h \ + dviread.c \ + files.c \ + font.c \ + fontmap.c \ + fontmap.h \ + fontsrch.c \ + gf.c \ + hash.c \ + hash.h \ + list.c \ + mdvi.h \ + pagesel.c \ + paper.c \ + paper.h \ + pk.c \ + private.h \ + setup.c \ + special.c \ + sp-epsf.c \ + sysdeps.h \ + t1.c \ + tfm.c \ + tfmfile.c \ + tt.c \ + util.c \ + vf.c + + +-include $(top_srcdir)/git.mk diff --git a/backend/dvi/mdvi-lib/afmparse.c b/backend/dvi/mdvi-lib/afmparse.c new file mode 100644 index 00000000..164366b0 --- /dev/null +++ b/backend/dvi/mdvi-lib/afmparse.c @@ -0,0 +1,1303 @@ +/* + * (C) 1988, 1989, 1990 by Adobe Systems Incorporated. All rights reserved. + * + * This file may be freely copied and redistributed as long as: + * 1) This entire notice continues to be included in the file, + * 2) If the file has been modified in any way, a notice of such + * modification is conspicuously indicated. + * + * PostScript, Display PostScript, and Adobe are registered trademarks of + * Adobe Systems Incorporated. + * + * ************************************************************************ + * THE INFORMATION BELOW IS FURNISHED AS IS, IS SUBJECT TO CHANGE WITHOUT + * NOTICE, AND SHOULD NOT BE CONSTRUED AS A COMMITMENT BY ADOBE SYSTEMS + * INCORPORATED. ADOBE SYSTEMS INCORPORATED ASSUMES NO RESPONSIBILITY OR + * LIABILITY FOR ANY ERRORS OR INACCURACIES, MAKES NO WARRANTY OF ANY + * KIND (EXPRESS, IMPLIED OR STATUTORY) WITH RESPECT TO THIS INFORMATION, + * AND EXPRESSLY DISCLAIMS ANY AND ALL WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR PARTICULAR PURPOSES AND NONINFRINGEMENT OF THIRD PARTY RIGHTS. + * ************************************************************************ + */ + +/* + * modified for MDVI: + * - some names changed to avoid conflicts with T1lib + * - changed to ANSI C prototypes, as used by MDVI + * mal - 3/01 + */ + +/* parseAFM.c + * + * This file is used in conjuction with the parseAFM.h header file. + * This file contains several procedures that are used to parse AFM + * files. It is intended to work with an application program that needs + * font metric information. The program can be used as is by making a + * procedure call to "parseFile" (passing in the expected parameters) + * and having it fill in a data structure with the data from the + * AFM file, or an application developer may wish to customize this + * code. + * + * There is also a file, parseAFMclient.c, that is a sample application + * showing how to call the "parseFile" procedure and how to use the data + * after "parseFile" has returned. + * + * Please read the comments in parseAFM.h and parseAFMclient.c. + * + * History: + * original: DSM Thu Oct 20 17:39:59 PDT 1988 + * modified: DSM Mon Jul 3 14:17:50 PDT 1989 + * - added 'storageProblem' return code + * - fixed bug of not allocating extra byte for string duplication + * - fixed typos + * modified: DSM Tue Apr 3 11:18:34 PDT 1990 + * - added free(ident) at end of parseFile routine + * modified: DSM Tue Jun 19 10:16:29 PDT 1990 + * - changed (width == 250) to (width = 250) in initializeArray + */ + +#include <config.h> +#include "sysdeps.h" + +#ifdef WITH_AFM_FILES + +#include <stdlib.h> /* added for MDVI */ +#include <string.h> /* added for MDVI */ + +#include <stdio.h> +#include <errno.h> +#include <sys/file.h> +#include <math.h> +#include "afmparse.h" +#undef VERSION + +#define lineterm EOL /* line terminating character */ +#define normalEOF 1 /* return code from parsing routines used only */ + /* in this module */ +#define Space "space" /* used in string comparison to look for the width */ + /* of the space character to init the widths array */ +#define False "false" /* used in string comparison to check the value of */ + /* boolean keys (e.g. IsFixedPitch) */ + +#define MATCH(A,B) (strncmp((A),(B), MAX_NAME) == 0) + + + +/*************************** GLOBALS ***********************/ + +static char *ident = NULL; /* storage buffer for keywords */ + + +/* "shorts" for fast case statement + * The values of each of these enumerated items correspond to an entry in the + * table of strings defined below. Therefore, if you add a new string as + * new keyword into the keyStrings table, you must also add a corresponding + * parseKey AND it MUST be in the same position! + * + * IMPORTANT: since the sorting algorithm is a binary search, the strings of + * keywords must be placed in lexicographical order, below. [Therefore, the + * enumerated items are not necessarily in lexicographical order, depending + * on the name chosen. BUT, they must be placed in the same position as the + * corresponding key string.] The NOPE shall remain in the last position, + * since it does not correspond to any key string, and it is used in the + * "recognize" procedure to calculate how many possible keys there are. + */ + +enum parseKey { + ASCENDER, CHARBBOX, CODE, COMPCHAR, CAPHEIGHT, COMMENT, + DESCENDER, ENCODINGSCHEME, ENDCHARMETRICS, ENDCOMPOSITES, + ENDFONTMETRICS, ENDKERNDATA, ENDKERNPAIRS, ENDTRACKKERN, + FAMILYNAME, FONTBBOX, FONTNAME, FULLNAME, ISFIXEDPITCH, + ITALICANGLE, KERNPAIR, KERNPAIRXAMT, LIGATURE, CHARNAME, + NOTICE, COMPCHARPIECE, STARTCHARMETRICS, STARTCOMPOSITES, + STARTFONTMETRICS, STARTKERNDATA, STARTKERNPAIRS, + STARTTRACKKERN, TRACKKERN, UNDERLINEPOSITION, + UNDERLINETHICKNESS, VERSION, XYWIDTH, XWIDTH, WEIGHT, XHEIGHT, + NOPE } ; + +/* keywords for the system: + * This a table of all of the current strings that are vaild AFM keys. + * Each entry can be referenced by the appropriate parseKey value (an + * enumerated data type defined above). If you add a new keyword here, + * a corresponding parseKey MUST be added to the enumerated data type + * defined above, AND it MUST be added in the same position as the + * string is in this table. + * + * IMPORTANT: since the sorting algorithm is a binary search, the keywords + * must be placed in lexicographical order. And, NULL should remain at the + * end. + */ + +static char *keyStrings[] = { + "Ascender", "B", "C", "CC", "CapHeight", "Comment", + "Descender", "EncodingScheme", "EndCharMetrics", "EndComposites", + "EndFontMetrics", "EndKernData", "EndKernPairs", "EndTrackKern", + "FamilyName", "FontBBox", "FontName", "FullName", "IsFixedPitch", + "ItalicAngle", "KP", "KPX", "L", "N", + "Notice", "PCC", "StartCharMetrics", "StartComposites", + "StartFontMetrics", "StartKernData", "StartKernPairs", + "StartTrackKern", "TrackKern", "UnderlinePosition", + "UnderlineThickness", "Version", "W", "WX", "Weight", "XHeight", + NULL }; + +/*************************** PARSING ROUTINES **************/ + +/*************************** token *************************/ + +/* A "AFM File Conventions" tokenizer. That means that it will + * return the next token delimited by white space. See also + * the `linetoken' routine, which does a similar thing but + * reads all tokens until the next end-of-line. + */ + +static char *token(FILE *stream) +{ + int ch, idx; + + /* skip over white space */ + while ((ch = fgetc(stream)) == ' ' || ch == lineterm || + ch == ',' || ch == '\t' || ch == ';'); + + idx = 0; + while (ch != EOF && ch != ' ' && ch != lineterm + && ch != '\t' && ch != ':' && ch != ';') + { + ident[idx++] = ch; + ch = fgetc(stream); + } /* while */ + + if (ch == EOF && idx < 1) return ((char *)NULL); + if (idx >= 1 && ch != ':' ) ungetc(ch, stream); + if (idx < 1 ) ident[idx++] = ch; /* single-character token */ + ident[idx] = 0; + + return(ident); /* returns pointer to the token */ + +} /* token */ + + +/*************************** linetoken *************************/ + +/* "linetoken" will get read all tokens until the EOL character from + * the given stream. This is used to get any arguments that can be + * more than one word (like Comment lines and FullName). + */ + +static char *linetoken(FILE *stream) +{ + int ch, idx; + + while ((ch = fgetc(stream)) == ' ' || ch == '\t' ); + + idx = 0; + while (ch != EOF && ch != lineterm) + { + ident[idx++] = ch; + ch = fgetc(stream); + } /* while */ + + ungetc(ch, stream); + ident[idx] = 0; + + return(ident); /* returns pointer to the token */ + +} /* linetoken */ + + +/*************************** recognize *************************/ + +/* This function tries to match a string to a known list of + * valid AFM entries (check the keyStrings array above). + * "ident" contains everything from white space through the + * next space, tab, or ":" character. + * + * The algorithm is a standard Knuth binary search. + */ + +static enum parseKey recognize(char *ident) +{ + int lower = 0, upper = (int) NOPE, midpoint, cmpvalue; + BOOL found = FALSE; + + while ((upper >= lower) && !found) + { + midpoint = (lower + upper)/2; + if (keyStrings[midpoint] == NULL) break; + cmpvalue = strncmp(ident, keyStrings[midpoint], MAX_NAME); + if (cmpvalue == 0) found = TRUE; + else if (cmpvalue < 0) upper = midpoint - 1; + else lower = midpoint + 1; + } /* while */ + + if (found) return (enum parseKey) midpoint; + else return NOPE; + +} /* recognize */ + + +/************************* parseGlobals *****************************/ + +/* This function is called by "parseFile". It will parse the AFM File + * up to the "StartCharMetrics" keyword, which essentially marks the + * end of the Global Font Information and the beginning of the character + * metrics information. + * + * If the caller of "parseFile" specified that it wanted the Global + * Font Information (as defined by the "AFM File Specification" + * document), then that information will be stored in the returned + * data structure. + * + * Any Global Font Information entries that are not found in a + * given file, will have the usual default initialization value + * for its type (i.e. entries of type int will be 0, etc). + * + * This function returns an error code specifying whether there was + * a premature EOF or a parsing error. This return value is used by + * parseFile to determine if there is more file to parse. + */ + +static BOOL parseGlobals(FILE *fp, GlobalFontInfo *gfi) +{ + BOOL cont = TRUE, save = (gfi != NULL); + int error = ok; + register char *keyword; + + while (cont) + { + keyword = token(fp); + + if (keyword == NULL) + /* Have reached an early and unexpected EOF. */ + /* Set flag and stop parsing */ + { + error = earlyEOF; + break; /* get out of loop */ + } + if (!save) + /* get tokens until the end of the Global Font info section */ + /* without saving any of the data */ + switch (recognize(keyword)) + { + case STARTCHARMETRICS: + cont = FALSE; + break; + case ENDFONTMETRICS: + cont = FALSE; + error = normalEOF; + break; + default: + break; + } /* switch */ + else + /* otherwise parse entire global font info section, */ + /* saving the data */ + switch(recognize(keyword)) + { + case STARTFONTMETRICS: + keyword = token(fp); + gfi->afmVersion = (char *) malloc(strlen(keyword) + 1); + strcpy(gfi->afmVersion, keyword); + break; + case COMMENT: + keyword = linetoken(fp); + break; + case FONTNAME: + keyword = token(fp); + gfi->fontName = (char *) malloc(strlen(keyword) + 1); + strcpy(gfi->fontName, keyword); + break; + case ENCODINGSCHEME: + keyword = token(fp); + gfi->encodingScheme = (char *) + malloc(strlen(keyword) + 1); + strcpy(gfi->encodingScheme, keyword); + break; + case FULLNAME: + keyword = linetoken(fp); + gfi->fullName = (char *) malloc(strlen(keyword) + 1); + strcpy(gfi->fullName, keyword); + break; + case FAMILYNAME: + keyword = linetoken(fp); + gfi->familyName = (char *) malloc(strlen(keyword) + 1); + strcpy(gfi->familyName, keyword); + break; + case WEIGHT: + keyword = token(fp); + gfi->weight = (char *) malloc(strlen(keyword) + 1); + strcpy(gfi->weight, keyword); + break; + case ITALICANGLE: + keyword = token(fp); + gfi->italicAngle = atof(keyword); + if (errno == ERANGE) error = parseError; + break; + case ISFIXEDPITCH: + keyword = token(fp); + if (MATCH(keyword, False)) + gfi->isFixedPitch = 0; + else + gfi->isFixedPitch = 1; + break; + case UNDERLINEPOSITION: + keyword = token(fp); + gfi->underlinePosition = atoi(keyword); + break; + case UNDERLINETHICKNESS: + keyword = token(fp); + gfi->underlineThickness = atoi(keyword); + break; + case VERSION: + keyword = token(fp); + gfi->version = (char *) malloc(strlen(keyword) + 1); + strcpy(gfi->version, keyword); + break; + case NOTICE: + keyword = linetoken(fp); + gfi->notice = (char *) malloc(strlen(keyword) + 1); + strcpy(gfi->notice, keyword); + break; + case FONTBBOX: + keyword = token(fp); + gfi->fontBBox.llx = atoi(keyword); + keyword = token(fp); + gfi->fontBBox.lly = atoi(keyword); + keyword = token(fp); + gfi->fontBBox.urx = atoi(keyword); + keyword = token(fp); + gfi->fontBBox.ury = atoi(keyword); + break; + case CAPHEIGHT: + keyword = token(fp); + gfi->capHeight = atoi(keyword); + break; + case XHEIGHT: + keyword = token(fp); + gfi->xHeight = atoi(keyword); + break; + case DESCENDER: + keyword = token(fp); + gfi->descender = atoi(keyword); + break; + case ASCENDER: + keyword = token(fp); + gfi->ascender = atoi(keyword); + break; + case STARTCHARMETRICS: + cont = FALSE; + break; + case ENDFONTMETRICS: + cont = FALSE; + error = normalEOF; + break; + case NOPE: + default: + error = parseError; + break; + } /* switch */ + } /* while */ + + return(error); + +} /* parseGlobals */ + + + +#if 0 /* this function does not seem to be used anywhere */ +/************************* initializeArray ************************/ + +/* Unmapped character codes are (at Adobe Systems) assigned the + * width of the space character (if one exists) else they get the + * value of 250 ems. This function initializes all entries in the + * char widths array to have this value. Then any mapped character + * codes will be replaced with the width of the appropriate character + * when parsing the character metric section. + + * This function parses the Character Metrics Section looking + * for a space character (by comparing character names). If found, + * the width of the space character will be used to initialize the + * values in the array of character widths. + * + * Before returning, the position of the read/write pointer of the + * file is reset to be where it was upon entering this function. + */ + +static int initializeArray(FILE *fp, int *cwi) +{ + BOOL cont = TRUE, found = FALSE; + long opos = ftell(fp); + int code = 0, width = 0, i = 0, error = 0; + register char *keyword; + + while (cont) + { + keyword = token(fp); + if (keyword == NULL) + { + error = earlyEOF; + break; /* get out of loop */ + } + switch(recognize(keyword)) + { + case COMMENT: + keyword = linetoken(fp); + break; + case CODE: + code = atoi(token(fp)); + break; + case XWIDTH: + width = atoi(token(fp)); + break; + case CHARNAME: + keyword = token(fp); + if (MATCH(keyword, Space)) + { + cont = FALSE; + found = TRUE; + } + break; + case ENDCHARMETRICS: + cont = FALSE; + break; + case ENDFONTMETRICS: + cont = FALSE; + error = normalEOF; + break; + case NOPE: + default: + error = parseError; + break; + } /* switch */ + } /* while */ + + if (!found) + width = 250; + + for (i = 0; i < 256; ++i) + cwi[i] = width; + + fseek(fp, opos, 0); + + return(error); + +} /* initializeArray */ +#endif /* unused */ + +/************************* parseCharWidths **************************/ + +/* This function is called by "parseFile". It will parse the AFM File + * up to the "EndCharMetrics" keyword. It will save the character + * width info (as opposed to all of the character metric information) + * if requested by the caller of parseFile. Otherwise, it will just + * parse through the section without saving any information. + * + * If data is to be saved, parseCharWidths is passed in a pointer + * to an array of widths that has already been initialized by the + * standard value for unmapped character codes. This function parses + * the Character Metrics section only storing the width information + * for the encoded characters into the array using the character code + * as the index into that array. + * + * This function returns an error code specifying whether there was + * a premature EOF or a parsing error. This return value is used by + * parseFile to determine if there is more file to parse. + */ + +static int parseCharWidths(FILE *fp, int *cwi) +{ + BOOL cont = TRUE, save = (cwi != NULL); + int pos = 0, error = ok; + register char *keyword; + + while (cont) + { + keyword = token(fp); + /* Have reached an early and unexpected EOF. */ + /* Set flag and stop parsing */ + if (keyword == NULL) + { + error = earlyEOF; + break; /* get out of loop */ + } + if (!save) + /* get tokens until the end of the Char Metrics section without */ + /* saving any of the data*/ + switch (recognize(keyword)) + { + case ENDCHARMETRICS: + cont = FALSE; + break; + case ENDFONTMETRICS: + cont = FALSE; + error = normalEOF; + break; + default: + break; + } /* switch */ + else + /* otherwise parse entire char metrics section, saving */ + /* only the char x-width info */ + switch(recognize(keyword)) + { + case COMMENT: + keyword = linetoken(fp); + break; + case CODE: + keyword = token(fp); + pos = atoi(keyword); + break; + case XYWIDTH: + /* PROBLEM: Should be no Y-WIDTH when doing "quick & dirty" */ + keyword = token(fp); keyword = token(fp); /* eat values */ + error = parseError; + break; + case XWIDTH: + keyword = token(fp); + if (pos >= 0) /* ignore unmapped chars */ + cwi[pos] = atoi(keyword); + break; + case ENDCHARMETRICS: + cont = FALSE; + break; + case ENDFONTMETRICS: + cont = FALSE; + error = normalEOF; + break; + case CHARNAME: /* eat values (so doesn't cause parseError) */ + keyword = token(fp); + break; + case CHARBBOX: + keyword = token(fp); keyword = token(fp); + keyword = token(fp); keyword = token(fp); + break; + case LIGATURE: + keyword = token(fp); keyword = token(fp); + break; + case NOPE: + default: + error = parseError; + break; + } /* switch */ + } /* while */ + + return(error); + +} /* parseCharWidths */ + + +/************************* parseCharMetrics ************************/ + +/* This function is called by parseFile if the caller of parseFile + * requested that all character metric information be saved + * (as opposed to only the character width information). + * + * parseCharMetrics is passed in a pointer to an array of records + * to hold information on a per character basis. This function + * parses the Character Metrics section storing all character + * metric information for the ALL characters (mapped and unmapped) + * into the array. + * + * This function returns an error code specifying whether there was + * a premature EOF or a parsing error. This return value is used by + * parseFile to determine if there is more file to parse. + */ + +static int parseCharMetrics(FILE *fp, FontInfo *fi) +{ + BOOL cont = TRUE, firstTime = TRUE; + int error = ok, count = 0; + register CharMetricInfo *temp = fi->cmi; + register char *keyword; + + while (cont) + { + keyword = token(fp); + if (keyword == NULL) + { + error = earlyEOF; + break; /* get out of loop */ + } + switch(recognize(keyword)) + { + case COMMENT: + keyword = linetoken(fp); + break; + case CODE: + if (count < fi->numOfChars) + { + if (firstTime) firstTime = FALSE; + else temp++; + temp->code = atoi(token(fp)); + count++; + } + else + { + error = parseError; + cont = FALSE; + } + break; + case XYWIDTH: + temp->wx = atoi(token(fp)); + temp->wy = atoi(token(fp)); + break; + case XWIDTH: + temp->wx = atoi(token(fp)); + break; + case CHARNAME: + keyword = token(fp); + temp->name = (char *) malloc(strlen(keyword) + 1); + strcpy(temp->name, keyword); + break; + case CHARBBOX: + temp->charBBox.llx = atoi(token(fp)); + temp->charBBox.lly = atoi(token(fp)); + temp->charBBox.urx = atoi(token(fp)); + temp->charBBox.ury = atoi(token(fp)); + break; + case LIGATURE: { + Ligature **tail = &(temp->ligs); + Ligature *node = *tail; + + if (*tail != NULL) + { + while (node->next != NULL) + node = node->next; + tail = &(node->next); + } + + *tail = (Ligature *) calloc(1, sizeof(Ligature)); + keyword = token(fp); + (*tail)->succ = (char *) malloc(strlen(keyword) + 1); + strcpy((*tail)->succ, keyword); + keyword = token(fp); + (*tail)->lig = (char *) malloc(strlen(keyword) + 1); + strcpy((*tail)->lig, keyword); + break; } + case ENDCHARMETRICS: + cont = FALSE;; + break; + case ENDFONTMETRICS: + cont = FALSE; + error = normalEOF; + break; + case NOPE: + default: + error = parseError; + break; + } /* switch */ + } /* while */ + + if ((error == ok) && (count != fi->numOfChars)) + error = parseError; + + return(error); + +} /* parseCharMetrics */ + + + +/************************* parseTrackKernData ***********************/ + +/* This function is called by "parseFile". It will parse the AFM File + * up to the "EndTrackKern" or "EndKernData" keywords. It will save the + * track kerning data if requested by the caller of parseFile. + * + * parseTrackKernData is passed in a pointer to the FontInfo record. + * If data is to be saved, the FontInfo record will already contain + * a valid pointer to storage for the track kerning data. + * + * This function returns an error code specifying whether there was + * a premature EOF or a parsing error. This return value is used by + * parseFile to determine if there is more file to parse. + */ + +static int parseTrackKernData(FILE *fp, FontInfo *fi) +{ + BOOL cont = TRUE, save = (fi->tkd != NULL); + int pos = 0, error = ok, tcount = 0; + register char *keyword; + + while (cont) + { + keyword = token(fp); + + if (keyword == NULL) + { + error = earlyEOF; + break; /* get out of loop */ + } + if (!save) + /* get tokens until the end of the Track Kerning Data */ + /* section without saving any of the data */ + switch(recognize(keyword)) + { + case ENDTRACKKERN: + case ENDKERNDATA: + cont = FALSE; + break; + case ENDFONTMETRICS: + cont = FALSE; + error = normalEOF; + break; + default: + break; + } /* switch */ + else + /* otherwise parse entire Track Kerning Data section, */ + /* saving the data */ + switch(recognize(keyword)) + { + case COMMENT: + keyword = linetoken(fp); + break; + case TRACKKERN: + if (tcount < fi->numOfTracks) + { + keyword = token(fp); + fi->tkd[pos].degree = atoi(keyword); + keyword = token(fp); + fi->tkd[pos].minPtSize = atof(keyword); + if (errno == ERANGE) error = parseError; + keyword = token(fp); + fi->tkd[pos].minKernAmt = atof(keyword); + if (errno == ERANGE) error = parseError; + keyword = token(fp); + fi->tkd[pos].maxPtSize = atof(keyword); + if (errno == ERANGE) error = parseError; + keyword = token(fp); + fi->tkd[pos++].maxKernAmt = atof(keyword); + if (errno == ERANGE) error = parseError; + tcount++; + } + else + { + error = parseError; + cont = FALSE; + } + break; + case ENDTRACKKERN: + case ENDKERNDATA: + cont = FALSE; + break; + case ENDFONTMETRICS: + cont = FALSE; + error = normalEOF; + break; + case NOPE: + default: + error = parseError; + break; + } /* switch */ + } /* while */ + + if (error == ok && tcount != fi->numOfTracks) + error = parseError; + + return(error); + +} /* parseTrackKernData */ + + +/************************* parsePairKernData ************************/ + +/* This function is called by "parseFile". It will parse the AFM File + * up to the "EndKernPairs" or "EndKernData" keywords. It will save + * the pair kerning data if requested by the caller of parseFile. + * + * parsePairKernData is passed in a pointer to the FontInfo record. + * If data is to be saved, the FontInfo record will already contain + * a valid pointer to storage for the pair kerning data. + * + * This function returns an error code specifying whether there was + * a premature EOF or a parsing error. This return value is used by + * parseFile to determine if there is more file to parse. + */ + +static int parsePairKernData(FILE *fp, FontInfo *fi) +{ + BOOL cont = TRUE, save = (fi->pkd != NULL); + int pos = 0, error = ok, pcount = 0; + register char *keyword; + + while (cont) + { + keyword = token(fp); + + if (keyword == NULL) + { + error = earlyEOF; + break; /* get out of loop */ + } + if (!save) + /* get tokens until the end of the Pair Kerning Data */ + /* section without saving any of the data */ + switch(recognize(keyword)) + { + case ENDKERNPAIRS: + case ENDKERNDATA: + cont = FALSE; + break; + case ENDFONTMETRICS: + cont = FALSE; + error = normalEOF; + break; + default: + break; + } /* switch */ + else + /* otherwise parse entire Pair Kerning Data section, */ + /* saving the data */ + switch(recognize(keyword)) + { + case COMMENT: + keyword = linetoken(fp); + break; + case KERNPAIR: + if (pcount < fi->numOfPairs) + { + keyword = token(fp); + fi->pkd[pos].name1 = (char *) + malloc(strlen(keyword) + 1); + strcpy(fi->pkd[pos].name1, keyword); + keyword = token(fp); + fi->pkd[pos].name2 = (char *) + malloc(strlen(keyword) + 1); + strcpy(fi->pkd[pos].name2, keyword); + keyword = token(fp); + fi->pkd[pos].xamt = atoi(keyword); + keyword = token(fp); + fi->pkd[pos++].yamt = atoi(keyword); + pcount++; + } + else + { + error = parseError; + cont = FALSE; + } + break; + case KERNPAIRXAMT: + if (pcount < fi->numOfPairs) + { + keyword = token(fp); + fi->pkd[pos].name1 = (char *) + malloc(strlen(keyword) + 1); + strcpy(fi->pkd[pos].name1, keyword); + keyword = token(fp); + fi->pkd[pos].name2 = (char *) + malloc(strlen(keyword) + 1); + strcpy(fi->pkd[pos].name2, keyword); + keyword = token(fp); + fi->pkd[pos++].xamt = atoi(keyword); + pcount++; + } + else + { + error = parseError; + cont = FALSE; + } + break; + case ENDKERNPAIRS: + case ENDKERNDATA: + cont = FALSE; + break; + case ENDFONTMETRICS: + cont = FALSE; + error = normalEOF; + break; + case NOPE: + default: + error = parseError; + break; + } /* switch */ + } /* while */ + + if (error == ok && pcount != fi->numOfPairs) + error = parseError; + + return(error); + +} /* parsePairKernData */ + + +/************************* parseCompCharData **************************/ + +/* This function is called by "parseFile". It will parse the AFM File + * up to the "EndComposites" keyword. It will save the composite + * character data if requested by the caller of parseFile. + * + * parseCompCharData is passed in a pointer to the FontInfo record, and + * a boolean representing if the data should be saved. + * + * This function will create the appropriate amount of storage for + * the composite character data and store a pointer to the storage + * in the FontInfo record. + * + * This function returns an error code specifying whether there was + * a premature EOF or a parsing error. This return value is used by + * parseFile to determine if there is more file to parse. + */ + +static int parseCompCharData(FILE *fp, FontInfo *fi) +{ + BOOL cont = TRUE, firstTime = TRUE, save = (fi->ccd != NULL); + int pos = 0, j = 0, error = ok, ccount = 0, pcount = 0; + register char *keyword; + + while (cont) + { + keyword = token(fp); + if (keyword == NULL) + /* Have reached an early and unexpected EOF. */ + /* Set flag and stop parsing */ + { + error = earlyEOF; + break; /* get out of loop */ + } + if (ccount > fi->numOfComps) + { + error = parseError; + break; /* get out of loop */ + } + if (!save) + /* get tokens until the end of the Composite Character info */ + /* section without saving any of the data */ + switch(recognize(keyword)) + { + case ENDCOMPOSITES: + cont = FALSE; + break; + case ENDFONTMETRICS: + cont = FALSE; + error = normalEOF; + break; + default: + break; + } /* switch */ + else + /* otherwise parse entire Composite Character info section, */ + /* saving the data */ + switch(recognize(keyword)) + { + case COMMENT: + keyword = linetoken(fp); + break; + case COMPCHAR: + if (ccount < fi->numOfComps) + { + keyword = token(fp); + if (pcount != fi->ccd[pos].numOfPieces) + error = parseError; + pcount = 0; + if (firstTime) firstTime = FALSE; + else pos++; + fi->ccd[pos].ccName = (char *) + malloc(strlen(keyword) + 1); + strcpy(fi->ccd[pos].ccName, keyword); + keyword = token(fp); + fi->ccd[pos].numOfPieces = atoi(keyword); + fi->ccd[pos].pieces = (Pcc *) + calloc(fi->ccd[pos].numOfPieces, sizeof(Pcc)); + j = 0; + ccount++; + } + else + { + error = parseError; + cont = FALSE; + } + break; + case COMPCHARPIECE: + if (pcount < fi->ccd[pos].numOfPieces) + { + keyword = token(fp); + fi->ccd[pos].pieces[j].pccName = (char *) + malloc(strlen(keyword) + 1); + strcpy(fi->ccd[pos].pieces[j].pccName, keyword); + keyword = token(fp); + fi->ccd[pos].pieces[j].deltax = atoi(keyword); + keyword = token(fp); + fi->ccd[pos].pieces[j++].deltay = atoi(keyword); + pcount++; + } + else + error = parseError; + break; + case ENDCOMPOSITES: + cont = FALSE; + break; + case ENDFONTMETRICS: + cont = FALSE; + error = normalEOF; + break; + case NOPE: + default: + error = parseError; + break; + } /* switch */ + } /* while */ + + if (error == ok && ccount != fi->numOfComps) + error = parseError; + + return(error); + +} /* parseCompCharData */ + + + + +/*************************** 'PUBLIC' FUNCTION ********************/ + + +/*************************** parseFile *****************************/ + +/* parseFile is the only 'public' procedure available. It is called + * from an application wishing to get information from an AFM file. + * The caller of this function is responsible for locating and opening + * an AFM file and handling all errors associated with that task. + * + * parseFile expects 3 parameters: a vaild file pointer, a pointer + * to a (FontInfo *) variable (for which storage will be allocated and + * the data requested filled in), and a mask specifying which + * data from the AFM File should be saved in the FontInfo structure. + * + * The file will be parsed and the requested data will be stored in + * a record of type FontInfo (refer to ParseAFM.h). + * + * parseFile returns an error code as defined in parseAFM.h. + * + * The position of the read/write pointer associated with the file + * pointer upon return of this function is undefined. + */ + +extern int afm_parse_file(FILE *fp, FontInfo **fi, FLAGS flags) +{ + + int code = ok; /* return code from each of the parsing routines */ + int error = ok; /* used as the return code from this function */ + + register char *keyword; /* used to store a token */ + + + /* storage data for the global variable ident */ + ident = (char *) calloc(MAX_NAME, sizeof(char)); + if (ident == NULL) {error = storageProblem; return(error);} + + (*fi) = (FontInfo *) calloc(1, sizeof(FontInfo)); + if ((*fi) == NULL) {error = storageProblem; return(error);} + + if (flags & P_G) + { + (*fi)->gfi = (GlobalFontInfo *) calloc(1, sizeof(GlobalFontInfo)); + if ((*fi)->gfi == NULL) {error = storageProblem; return(error);} + } + + /* The AFM File begins with Global Font Information. This section */ + /* will be parsed whether or not information should be saved. */ + code = parseGlobals(fp, (*fi)->gfi); + + if (code < 0) error = code; + + /* The Global Font Information is followed by the Character Metrics */ + /* section. Which procedure is used to parse this section depends on */ + /* how much information should be saved. If all of the metrics info */ + /* is wanted, parseCharMetrics is called. If only the character widths */ + /* is wanted, parseCharWidths is called. parseCharWidths will also */ + /* be called in the case that no character data is to be saved, just */ + /* to parse through the section. */ + + if ((code != normalEOF) && (code != earlyEOF)) + { + (*fi)->numOfChars = atoi(token(fp)); + if (flags & (P_M ^ P_W)) + { + (*fi)->cmi = (CharMetricInfo *) + calloc((*fi)->numOfChars, sizeof(CharMetricInfo)); + if ((*fi)->cmi == NULL) {error = storageProblem; return(error);} + code = parseCharMetrics(fp, *fi); + } + else + { + if (flags & P_W) + { + (*fi)->cwi = (int *) calloc(256, sizeof(int)); + if ((*fi)->cwi == NULL) + { + error = storageProblem; + return(error); + } + } + /* parse section regardless */ + code = parseCharWidths(fp, (*fi)->cwi); + } /* else */ + } /* if */ + + if ((error != earlyEOF) && (code < 0)) error = code; + + /* The remaining sections of the AFM are optional. This code will */ + /* look at the next keyword in the file to determine what section */ + /* is next, and then allocate the appropriate amount of storage */ + /* for the data (if the data is to be saved) and call the */ + /* appropriate parsing routine to parse the section. */ + + while ((code != normalEOF) && (code != earlyEOF)) + { + keyword = token(fp); + if (keyword == NULL) + /* Have reached an early and unexpected EOF. */ + /* Set flag and stop parsing */ + { + code = earlyEOF; + break; /* get out of loop */ + } + switch(recognize(keyword)) + { + case STARTKERNDATA: + break; + case ENDKERNDATA: + break; + case STARTTRACKKERN: + keyword = token(fp); + if (flags & P_T) + { + (*fi)->numOfTracks = atoi(keyword); + (*fi)->tkd = (TrackKernData *) + calloc((*fi)->numOfTracks, sizeof(TrackKernData)); + if ((*fi)->tkd == NULL) + { + error = storageProblem; + return(error); + } + } /* if */ + code = parseTrackKernData(fp, *fi); + break; + case STARTKERNPAIRS: + keyword = token(fp); + if (flags & P_P) + { + (*fi)->numOfPairs = atoi(keyword); + (*fi)->pkd = (PairKernData *) + calloc((*fi)->numOfPairs, sizeof(PairKernData)); + if ((*fi)->pkd == NULL) + { + error = storageProblem; + return(error); + } + } /* if */ + code = parsePairKernData(fp, *fi); + break; + case STARTCOMPOSITES: + keyword = token(fp); + if (flags & P_C) + { + (*fi)->numOfComps = atoi(keyword); + (*fi)->ccd = (CompCharData *) + calloc((*fi)->numOfComps, sizeof(CompCharData)); + if ((*fi)->ccd == NULL) + { + error = storageProblem; + return(error); + } + } /* if */ + code = parseCompCharData(fp, *fi); + break; + case ENDFONTMETRICS: + code = normalEOF; + break; + case NOPE: + default: + code = parseError; + break; + } /* switch */ + + if ((error != earlyEOF) && (code < 0)) error = code; + + } /* while */ + + if ((error != earlyEOF) && (code < 0)) error = code; + + if (ident != NULL) { free(ident); ident = NULL; } + + return(error); + +} /* parseFile */ + +/* added for MDVI: this function was copied from `parseAFMclient.c' */ + +void afm_free_fontinfo(FontInfo *fi) +{ + if (fi != NULL) + { + if (fi->gfi != NULL) + { + free(fi->gfi->afmVersion); fi->gfi->afmVersion = NULL; + free(fi->gfi->fontName); fi->gfi->fontName = NULL; + free(fi->gfi->fullName); fi->gfi->fullName = NULL; + free(fi->gfi->familyName); fi->gfi->familyName = NULL; + free(fi->gfi->weight); fi->gfi->weight = NULL; + free(fi->gfi->version); fi->gfi->version = NULL; + free(fi->gfi->notice); fi->gfi->notice = NULL; + free(fi->gfi->encodingScheme); fi->gfi->encodingScheme = NULL; + free(fi->gfi); fi->gfi = NULL; + } + + if (fi->cwi != NULL) + { free(fi->cwi); fi->cwi = NULL; } + + if (fi->cmi != NULL) + { + int i = 0; + CharMetricInfo *temp = fi->cmi; + Ligature *node = temp->ligs; + + for (i = 0; i < fi->numOfChars; ++i) + { + for (node = temp->ligs; node != NULL; node = node->next) + { + free(node->succ); node->succ = NULL; + free(node->lig); node->lig = NULL; + } + + free(temp->name); temp->name = NULL; + temp++; + } + + free(fi->cmi); fi->cmi = NULL; + } + + if (fi->tkd != NULL) + { free(fi->tkd); fi->tkd = NULL; } + + if (fi->pkd != NULL) + { + free(fi->pkd->name1); fi->pkd->name1 = NULL; + free(fi->pkd->name2); fi->pkd->name2 = NULL; + free(fi->pkd); fi->pkd = NULL; + } + + if (fi->ccd != NULL) + { + int i = 0, j = 0; + CompCharData *ccd = fi->ccd; + + for (i = 0; i < fi->numOfComps; ++i) + { + for (j = 0; j < ccd[i].numOfPieces; ++j) + { + free(ccd[i].pieces[j].pccName); + ccd[i].pieces[j].pccName = NULL; + } + + free(ccd[i].ccName); ccd[i].ccName = NULL; + } + + free(fi->ccd); fi->ccd = NULL; + } + + free(fi); + + } /* if */ + +} /* afm_free_fontinfo */ + +#endif /* WITH_AFM_FILES */ diff --git a/backend/dvi/mdvi-lib/afmparse.h b/backend/dvi/mdvi-lib/afmparse.h new file mode 100644 index 00000000..6cce780c --- /dev/null +++ b/backend/dvi/mdvi-lib/afmparse.h @@ -0,0 +1,307 @@ +/* modified for MDVI -- some names changed to avoid conflicts with T1lib */ +/* + * (C) 1988, 1989 by Adobe Systems Incorporated. All rights reserved. + * + * This file may be freely copied and redistributed as long as: + * 1) This entire notice continues to be included in the file, + * 2) If the file has been modified in any way, a notice of such + * modification is conspicuously indicated. + * + * PostScript, Display PostScript, and Adobe are registered trademarks of + * Adobe Systems Incorporated. + * + * ************************************************************************ + * THE INFORMATION BELOW IS FURNISHED AS IS, IS SUBJECT TO CHANGE WITHOUT + * NOTICE, AND SHOULD NOT BE CONSTRUED AS A COMMITMENT BY ADOBE SYSTEMS + * INCORPORATED. ADOBE SYSTEMS INCORPORATED ASSUMES NO RESPONSIBILITY OR + * LIABILITY FOR ANY ERRORS OR INACCURACIES, MAKES NO WARRANTY OF ANY + * KIND (EXPRESS, IMPLIED OR STATUTORY) WITH RESPECT TO THIS INFORMATION, + * AND EXPRESSLY DISCLAIMS ANY AND ALL WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR PARTICULAR PURPOSES AND NONINFRINGEMENT OF THIRD PARTY RIGHTS. + * ************************************************************************ + */ + +/* ParseAFM.h + * + * This header file is used in conjuction with the parseAFM.c file. + * Together these files provide the functionality to parse Adobe Font + * Metrics files and store the information in predefined data structures. + * It is intended to work with an application program that needs font metric + * information. The program can be used as is by making a procedure call to + * parse an AFM file and have the data stored, or an application developer + * may wish to customize the code. + * + * This header file defines the data structures used as well as the key + * strings that are currently recognized by this version of the AFM parser. + * This program is based on the document "Adobe Font Metrics Files, + * Specification Version 2.0". + * + * AFM files are separated into distinct sections of different data. Because + * of this, the parseAFM program can parse a specified file to only save + * certain sections of information based on the application's needs. A record + * containing the requested information will be returned to the application. + * + * AFM files are divided into five sections of data: + * 1) The Global Font Information + * 2) The Character Metrics Information + * 3) The Track Kerning Data + * 4) The Pair-Wise Kerning Data + * 5) The Composite Character Data + * + * Basically, the application can request any of these sections independent + * of what other sections are requested. In addition, in recognizing that + * many applications will want ONLY the x-width of characters and not all + * of the other character metrics information, there is a way to receive + * only the width information so as not to pay the storage cost for the + * unwanted data. An application should never request both the + * "quick and dirty" char metrics (widths only) and the Character Metrics + * Information since the Character Metrics Information will contain all + * of the character widths as well. + * + * There is a procedure in parseAFM.c, called parseFile, that can be + * called from any application wishing to get information from the AFM File. + * This procedure expects 3 parameters: a vaild file descriptor, a pointer + * to a (FontInfo *) variable (for which space will be allocated and then + * will be filled in with the data requested), and a mask specifying + * which data from the AFM File should be saved in the FontInfo structure. + * + * The flags that can be used to set the appropriate mask are defined below. + * In addition, several commonly used masks have already been defined. + * + * History: + * original: DSM Thu Oct 20 17:39:59 PDT 1988 + * modified: DSM Mon Jul 3 14:17:50 PDT 1989 + * - added 'storageProblem' return code + * - fixed typos + */ +#ifndef _MDVI_PARSEAFM_H +#define _MDVI_PARSEAFM_H 1 + +#include "sysdeps.h" + +#include <stdio.h> + +/* your basic constants */ +#define TRUE 1 +#define FALSE 0 +#define EOL '\n' /* end-of-line indicator */ +#define MAX_NAME 4096 /* max length for identifiers */ +#define BOOL int +#define FLAGS int + +/* Flags that can be AND'ed together to specify exactly what + * information from the AFM file should be saved. */ +#define P_G 0x01 /* 0000 0001 */ /* Global Font Info */ +#define P_W 0x02 /* 0000 0010 */ /* Character Widths ONLY */ +#define P_M 0x06 /* 0000 0110 */ /* All Char Metric Info */ +#define P_P 0x08 /* 0000 1000 */ /* Pair Kerning Info */ +#define P_T 0x10 /* 0001 0000 */ /* Track Kerning Info */ +#define P_C 0x20 /* 0010 0000 */ /* Composite Char Info */ + +/* Commonly used flags */ +#define P_GW (P_G | P_W) +#define P_GM (P_G | P_M) +#define P_GMP (P_G | P_M | P_P) +#define P_GMK (P_G | P_M | P_P | P_T) +#define P_ALL (P_G | P_M | P_P | P_T | P_C) + +/* Possible return codes from the parseFile procedure. + * + * ok means there were no problems parsing the file. + * + * parseError means that there was some kind of parsing error, but the + * parser went on. This could include problems like the count for any given + * section does not add up to how many entries there actually were, or + * there was a key that was not recognized. The return record may contain + * vaild data or it may not. + * + * earlyEOF means that an End of File was encountered before expected. This + * may mean that the AFM file had been truncated, or improperly formed. + * + * storageProblem means that there were problems allocating storage for + * the data structures that would have contained the AFM data. + */ +#define ok 0 +#define parseError -1 +#define earlyEOF -2 +#define storageProblem -3 + +/************************* TYPES *********************************/ +/* Below are all of the data structure definitions. These structures + * try to map as closely as possible to grouping and naming of data + * in the AFM Files. + */ + +/* Bounding box definition. Used for the Font BBox as well as the + * Character BBox. + */ +typedef struct +{ + int llx; /* lower left x-position */ + int lly; /* lower left y-position */ + int urx; /* upper right x-position */ + int ury; /* upper right y-position */ +} BBox; + +/* Global Font information. + * The key that each field is associated with is in comments. For an + * explanation about each key and its value please refer to the AFM + * documentation (full title & version given above). + */ +typedef struct +{ + char *afmVersion; /* key: StartFontMetrics */ + char *fontName; /* key: FontName */ + char *fullName; /* key: FullName */ + char *familyName; /* key: FamilyName */ + char *weight; /* key: Weight */ + float italicAngle; /* key: ItalicAngle */ + BOOL isFixedPitch; /* key: IsFixedPitch */ + BBox fontBBox; /* key: FontBBox */ + int underlinePosition; /* key: UnderlinePosition */ + int underlineThickness; /* key: UnderlineThickness */ + char *version; /* key: Version */ + char *notice; /* key: Notice */ + char *encodingScheme; /* key: EncodingScheme */ + int capHeight; /* key: CapHeight */ + int xHeight; /* key: XHeight */ + int ascender; /* key: Ascender */ + int descender; /* key: Descender */ +} GlobalFontInfo; + +/* Ligature definition is a linked list since any character can have + * any number of ligatures. + */ +typedef struct _t_ligature +{ + char *succ, *lig; + struct _t_ligature *next; +} Ligature; + +/* Character Metric Information. This structure is used only if ALL + * character metric information is requested. If only the character + * widths is requested, then only an array of the character x-widths + * is returned. + * + * The key that each field is associated with is in comments. For an + * explanation about each key and its value please refer to the + * Character Metrics section of the AFM documentation (full title + * & version given above). + */ +typedef struct +{ + int code, /* key: C */ + wx, /* key: WX */ + wy; /* together wx and wy are associated with key: W */ + char *name; /* key: N */ + BBox charBBox; /* key: B */ + Ligature *ligs; /* key: L (linked list; not a fixed number of Ls */ +} CharMetricInfo; + +/* Track kerning data structure. + * The fields of this record are the five values associated with every + * TrackKern entry. + * + * For an explanation about each value please refer to the + * Track Kerning section of the AFM documentation (full title + * & version given above). + */ +typedef struct +{ + int degree; + float minPtSize, + minKernAmt, + maxPtSize, + maxKernAmt; +} TrackKernData; + +/* Pair Kerning data structure. + * The fields of this record are the four values associated with every + * KP entry. For KPX entries, the yamt will be zero. + * + * For an explanation about each value please refer to the + * Pair Kerning section of the AFM documentation (full title + * & version given above). + */ +typedef struct +{ + char *name1; + char *name2; + int xamt, + yamt; +} PairKernData; + +/* PCC is a piece of a composite character. This is a sub structure of a + * compCharData described below. + * These fields will be filled in with the values from the key PCC. + * + * For an explanation about each key and its value please refer to the + * Composite Character section of the AFM documentation (full title + * & version given above). + */ +typedef struct +{ + char *pccName; + int deltax, + deltay; +} Pcc; + +/* Composite Character Information data structure. + * The fields ccName and numOfPieces are filled with the values associated + * with the key CC. The field pieces points to an array (size = numOfPieces) + * of information about each of the parts of the composite character. That + * array is filled in with the values from the key PCC. + * + * For an explanation about each key and its value please refer to the + * Composite Character section of the AFM documentation (full title + * & version given above). + */ +typedef struct +{ + char *ccName; + int numOfPieces; + Pcc *pieces; +} CompCharData; + +/* FontInfo + * Record type containing pointers to all of the other data + * structures containing information about a font. + * A a record of this type is filled with data by the + * parseFile function. + */ +typedef struct +{ + GlobalFontInfo *gfi; /* ptr to a GlobalFontInfo record */ + int *cwi; /* ptr to 256 element array of just char widths */ + int numOfChars; /* number of entries in char metrics array */ + CharMetricInfo *cmi; /* ptr to char metrics array */ + int numOfTracks; /* number to entries in track kerning array */ + TrackKernData *tkd; /* ptr to track kerning array */ + int numOfPairs; /* number to entries in pair kerning array */ + PairKernData *pkd; /* ptr to pair kerning array */ + int numOfComps; /* number to entries in comp char array */ + CompCharData *ccd; /* ptr to comp char array */ +} FontInfo; + +/************************* PROCEDURES ****************************/ + +/* Call this procedure to do the grunt work of parsing an AFM file. + * + * "fp" should be a valid file pointer to an AFM file. + * + * "fi" is a pointer to a pointer to a FontInfo record sturcture + * (defined above). Storage for the FontInfo structure will be + * allocated in parseFile and the structure will be filled in + * with the requested data from the AFM File. + * + * "flags" is a mask with bits set representing what data should + * be saved. Defined above are valid flags that can be used to set + * the mask, as well as a few commonly used masks. + * + * The possible return codes from parseFile are defined above. + */ + +extern int afm_parse_file __PROTO((FILE *, FontInfo **, FLAGS)); +extern void afm_free_fontinfo __PROTO((FontInfo *)); + +#endif /* _MDVI_PARSEAFM_H */ diff --git a/backend/dvi/mdvi-lib/bitmap.c b/backend/dvi/mdvi-lib/bitmap.c new file mode 100644 index 00000000..53f21207 --- /dev/null +++ b/backend/dvi/mdvi-lib/bitmap.c @@ -0,0 +1,1035 @@ +/* + * Copyright (C) 2000, Matias Atria + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +/* Bitmap manipulation routines */ + +#include <config.h> +#include <stdlib.h> + +#include "mdvi.h" +#include "color.h" + +/* bit_masks[n] contains a BmUnit with `n' contiguous bits */ + +static BmUnit bit_masks[] = { + 0x0, 0x1, 0x3, 0x7, + 0xf, 0x1f, 0x3f, 0x7f, + 0xff, +#if BITMAP_BYTES > 1 + 0x1ff, 0x3ff, 0x7ff, + 0xfff, 0x1fff, 0x3fff, 0x7fff, + 0xffff, +#if BITMAP_BYTES > 2 + 0x1ffff, 0x3ffff, 0x7ffff, + 0xfffff, 0x1fffff, 0x3fffff, 0x7fffff, + 0xffffff, 0x1ffffff, 0x3ffffff, 0x7ffffff, + 0xfffffff, 0x1fffffff, 0x3fffffff, 0x7fffffff, + 0xffffffff +#endif /* BITMAP_BYTES > 2 */ +#endif /* BITMAP_BYTES > 1 */ +}; + +#ifndef NODEBUG +#define SHOW_OP_DATA (DEBUGGING(BITMAP_OPS) && DEBUGGING(BITMAP_DATA)) +#endif + +/* + * Some useful macros to manipulate bitmap data + * SEGMENT(m,n) = bit mask for a segment of `m' contiguous bits + * starting at column `n'. These macros assume that + * m + n <= BITMAP_BITS, 0 <= m, n. + */ +#ifdef WORD_BIG_ENDIAN +#define SEGMENT(m,n) (bit_masks[m] << (BITMAP_BITS - (m) - (n))) +#else +#define SEGMENT(m,n) (bit_masks[m] << (n)) +#endif + +/* sampling and shrinking routines shamelessly stolen from xdvi */ + +static int sample_count[] = { + 0, 1, 1, 2, 1, 2, 2, 3, 1, 2, 2, 3, 2, 3, 3, 4, + 1, 2, 2, 3, 2, 3, 3, 4, 2, 3, 3, 4, 3, 4, 4, 5, + 1, 2, 2, 3, 2, 3, 3, 4, 2, 3, 3, 4, 3, 4, 4, 5, + 2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6, + 1, 2, 2, 3, 2, 3, 3, 4, 2, 3, 3, 4, 3, 4, 4, 5, + 2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6, + 2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6, + 3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7, + 1, 2, 2, 3, 2, 3, 3, 4, 2, 3, 3, 4, 3, 4, 4, 5, + 2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6, + 2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6, + 3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7, + 2, 3, 3, 4, 3, 4, 4, 5, 3, 4, 4, 5, 4, 5, 5, 6, + 3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7, + 3, 4, 4, 5, 4, 5, 5, 6, 4, 5, 5, 6, 5, 6, 6, 7, + 4, 5, 5, 6, 5, 6, 6, 7, 5, 6, 6, 7, 6, 7, 7, 8 +}; + +/* bit_swap[j] = j with all bits inverted (i.e. msb -> lsb) */ +static Uchar bit_swap[] = { + 0x00, 0x80, 0x40, 0xc0, 0x20, 0xa0, 0x60, 0xe0, + 0x10, 0x90, 0x50, 0xd0, 0x30, 0xb0, 0x70, 0xf0, + 0x08, 0x88, 0x48, 0xc8, 0x28, 0xa8, 0x68, 0xe8, + 0x18, 0x98, 0x58, 0xd8, 0x38, 0xb8, 0x78, 0xf8, + 0x04, 0x84, 0x44, 0xc4, 0x24, 0xa4, 0x64, 0xe4, + 0x14, 0x94, 0x54, 0xd4, 0x34, 0xb4, 0x74, 0xf4, + 0x0c, 0x8c, 0x4c, 0xcc, 0x2c, 0xac, 0x6c, 0xec, + 0x1c, 0x9c, 0x5c, 0xdc, 0x3c, 0xbc, 0x7c, 0xfc, + 0x02, 0x82, 0x42, 0xc2, 0x22, 0xa2, 0x62, 0xe2, + 0x12, 0x92, 0x52, 0xd2, 0x32, 0xb2, 0x72, 0xf2, + 0x0a, 0x8a, 0x4a, 0xca, 0x2a, 0xaa, 0x6a, 0xea, + 0x1a, 0x9a, 0x5a, 0xda, 0x3a, 0xba, 0x7a, 0xfa, + 0x06, 0x86, 0x46, 0xc6, 0x26, 0xa6, 0x66, 0xe6, + 0x16, 0x96, 0x56, 0xd6, 0x36, 0xb6, 0x76, 0xf6, + 0x0e, 0x8e, 0x4e, 0xce, 0x2e, 0xae, 0x6e, 0xee, + 0x1e, 0x9e, 0x5e, 0xde, 0x3e, 0xbe, 0x7e, 0xfe, + 0x01, 0x81, 0x41, 0xc1, 0x21, 0xa1, 0x61, 0xe1, + 0x11, 0x91, 0x51, 0xd1, 0x31, 0xb1, 0x71, 0xf1, + 0x09, 0x89, 0x49, 0xc9, 0x29, 0xa9, 0x69, 0xe9, + 0x19, 0x99, 0x59, 0xd9, 0x39, 0xb9, 0x79, 0xf9, + 0x05, 0x85, 0x45, 0xc5, 0x25, 0xa5, 0x65, 0xe5, + 0x15, 0x95, 0x55, 0xd5, 0x35, 0xb5, 0x75, 0xf5, + 0x0d, 0x8d, 0x4d, 0xcd, 0x2d, 0xad, 0x6d, 0xed, + 0x1d, 0x9d, 0x5d, 0xdd, 0x3d, 0xbd, 0x7d, 0xfd, + 0x03, 0x83, 0x43, 0xc3, 0x23, 0xa3, 0x63, 0xe3, + 0x13, 0x93, 0x53, 0xd3, 0x33, 0xb3, 0x73, 0xf3, + 0x0b, 0x8b, 0x4b, 0xcb, 0x2b, 0xab, 0x6b, 0xeb, + 0x1b, 0x9b, 0x5b, 0xdb, 0x3b, 0xbb, 0x7b, 0xfb, + 0x07, 0x87, 0x47, 0xc7, 0x27, 0xa7, 0x67, 0xe7, + 0x17, 0x97, 0x57, 0xd7, 0x37, 0xb7, 0x77, 0xf7, + 0x0f, 0x8f, 0x4f, 0xcf, 0x2f, 0xaf, 0x6f, 0xef, + 0x1f, 0x9f, 0x5f, 0xdf, 0x3f, 0xbf, 0x7f, 0xff +}; + + +/* + * next we have three bitmap functions to convert bitmaps in LSB bit order + * with 8, 16 and 32 bits per unit, to our internal format. The differences + * are minimal, but writing a generic function to handle all unit sizes is + * hopelessly slow. + */ + +BITMAP *bitmap_convert_lsb8(Uchar *bits, int w, int h, int stride) +{ + BITMAP *bm; + int i; + Uchar *unit; + register Uchar *curr; + Uchar *end; + int bytes; + + DEBUG((DBG_BITMAP_OPS, "convert LSB %dx%d@8 -> bitmap\n", w, h)); + + bm = bitmap_alloc_raw(w, h); + + /* this is the number of bytes in the original bitmap */ + bytes = ROUND(w, 8); + unit = (Uchar *)bm->data; + end = unit + bm->stride; + curr = bits; + /* we try to do this as fast as we can */ + for(i = 0; i < h; i++) { +#ifdef WORD_LITTLE_ENDIAN + memcpy(unit, curr, bytes); + curr += stride; +#else + int j; + + for(j = 0; j < bytes; curr++, j++) + unit[j] = bit_swap[*curr]; + cur += stride - bytes; +#endif + memzero(unit + bytes, bm->stride - bytes); + unit += bm->stride; + } + if(SHOW_OP_DATA) + bitmap_print(stderr, bm); + return bm; +} + +BITMAP *bitmap_convert_msb8(Uchar *data, int w, int h, int stride) +{ + BITMAP *bm; + Uchar *unit; + Uchar *curr; + int i; + int bytes; + + bm = bitmap_alloc(w, h); + bytes = ROUND(w, 8); + unit = (Uchar *)bm->data; + curr = data; + for(i = 0; i < h; i++) { +#ifdef WORD_LITTLE_ENDIAN + int j; + + for(j = 0; j < bytes; curr++, j++) + unit[j] = bit_swap[*curr]; + curr += stride - bytes; +#else + memcpy(unit, curr, bytes); + curr += stride; +#endif + memzero(unit + bytes, bm->stride - bytes); + unit += bm->stride; + } + if(SHOW_OP_DATA) + bitmap_print(stderr, bm); + return bm; +} + + +BITMAP *bitmap_copy(BITMAP *bm) +{ + BITMAP *nb = bitmap_alloc(bm->width, bm->height); + + DEBUG((DBG_BITMAP_OPS, "copy %dx%d\n", bm->width, bm->height)); + memcpy(nb->data, bm->data, bm->height * bm->stride); + return nb; +} + +BITMAP *bitmap_alloc(int w, int h) +{ + BITMAP *bm; + + bm = xalloc(BITMAP); + bm->width = w; + bm->height = h; + bm->stride = BM_BYTES_PER_LINE(bm); + if(h && bm->stride) + bm->data = (BmUnit *)mdvi_calloc(h, bm->stride); + else + bm->data = NULL; + + return bm; +} + +BITMAP *bitmap_alloc_raw(int w, int h) +{ + BITMAP *bm; + + bm = xalloc(BITMAP); + bm->width = w; + bm->height = h; + bm->stride = BM_BYTES_PER_LINE(bm); + if(h && bm->stride) + bm->data = (BmUnit *)mdvi_malloc(h * bm->stride); + else + bm->data = NULL; + + return bm; +} + +void bitmap_destroy(BITMAP *bm) +{ + if(bm->data) + free(bm->data); + free(bm); +} + +void bitmap_print(FILE *out, BITMAP *bm) +{ + int i, j; + BmUnit *a, mask; + static const char labels[] = { + '1', '2', '3', '4', '5', '6', '7', '8', '9', '0' + }; + int sub; + + a = bm->data; + fprintf(out, " "); + if(bm->width > 10) { + putchar('0'); + sub = 0; + for(j = 2; j <= bm->width; j++) + if((j %10) == 0) { + if((j % 100) == 0) { + fprintf(out, "*"); + sub += 100; + } else + fprintf(out, "%d", (j - sub)/10); + } else + putc(' ', out); + fprintf(out, "\n "); + } + for(j = 0; j < bm->width; j++) + putc(labels[j % 10], out); + putchar('\n'); + for(i = 0; i < bm->height; i++) { + mask = FIRSTMASK; + a = (BmUnit *)((char *)bm->data + i * bm->stride); + fprintf(out, "%3d ", i+1); + for(j = 0; j < bm->width; j++) { + if(*a & mask) + putc('#', out); + else + putc('.', out); + if(mask == LASTMASK) { + a++; + mask = FIRSTMASK; + } else + NEXTMASK(mask); + } + putchar('\n'); + } +} + +void bitmap_set_col(BITMAP *bm, int row, int col, int count, int state) +{ + BmUnit *ptr; + BmUnit mask; + + ptr = __bm_unit_ptr(bm, col, row); + mask = FIRSTMASKAT(col); + + while(count-- > 0) { + if(state) + *ptr |= mask; + else + *ptr &= ~mask; + /* move to next row */ + ptr = bm_offset(ptr, bm->stride); + } +} + +/* + * to use this function you should first make sure that + * there is room for `count' bits in the scanline + * + * A general-purpose (but not very efficient) function to paint `n' pixels + * on a bitmap, starting at position (x, y) would be: + * + * bitmap_paint_bits(__bm_unit_ptr(bitmap, x, y), x % BITMAP_BITS, n) + * + */ +void bitmap_paint_bits(BmUnit *ptr, int n, int count) +{ + /* paint the head */ + if(n + count > BITMAP_BITS) { + *ptr |= SEGMENT(BITMAP_BITS - n, n); + count -= BITMAP_BITS - n; + ptr++; + } else { + *ptr |= SEGMENT(count, n); + return; + } + + /* paint the middle */ + for(; count >= BITMAP_BITS; count -= BITMAP_BITS) + *ptr++ = bit_masks[BITMAP_BITS]; + + /* paint the tail */ + if(count > 0) + *ptr |= SEGMENT(count, 0); +} + +/* + * same as paint_bits but clears pixels instead of painting them. Written + * as a separate function for efficiency reasons. + */ +void bitmap_clear_bits(BmUnit *ptr, int n, int count) +{ + if(n + count > BITMAP_BITS) { + *ptr &= ~SEGMENT(BITMAP_BITS - n, n); + count -= BITMAP_BITS; + ptr++; + } else { + *ptr &= ~SEGMENT(count, n); + return; + } + + for(; count >= BITMAP_BITS; count -= BITMAP_BITS) + *ptr++ = 0; + + if(count > 0) + *ptr &= ~SEGMENT(count, 0); +} + +/* the general function to paint rows. Still used by the PK reader, but that + * will change soon (The GF reader already uses bitmap_paint_bits()). + */ +void bitmap_set_row(BITMAP *bm, int row, int col, int count, int state) +{ + BmUnit *ptr; + + ptr = __bm_unit_ptr(bm, col, row); + if(state) + bitmap_paint_bits(ptr, col & (BITMAP_BITS-1), count); + else + bitmap_clear_bits(ptr, col & (BITMAP_BITS-1), count); +} + +/* + * Now several `flipping' operations + */ + +void bitmap_flip_horizontally(BITMAP *bm) +{ + BITMAP nb; + BmUnit *fptr, *tptr; + BmUnit fmask, tmask; + int w, h; + + nb.width = bm->width; + nb.height = bm->height; + nb.stride = bm->stride; + nb.data = mdvi_calloc(bm->height, bm->stride); + + fptr = bm->data; + tptr = __bm_unit_ptr(&nb, nb.width-1, 0); + for(h = 0; h < bm->height; h++) { + BmUnit *fline, *tline; + + fline = fptr; + tline = tptr; + fmask = FIRSTMASK; + tmask = FIRSTMASKAT(nb.width-1); + for(w = 0; w < bm->width; w++) { + if(*fline & fmask) + *tline |= tmask; + if(fmask == LASTMASK) { + fmask = FIRSTMASK; + fline++; + } else + NEXTMASK(fmask); + if(tmask == FIRSTMASK) { + tmask = LASTMASK; + tline--; + } else + PREVMASK(tmask); + } + fptr = bm_offset(fptr, bm->stride); + tptr = bm_offset(tptr, bm->stride); + } + DEBUG((DBG_BITMAP_OPS, "flip_horizontally (%d,%d) -> (%d,%d)\n", + bm->width, bm->height, nb.width, nb.height)); + mdvi_free(bm->data); + bm->data = nb.data; + if(SHOW_OP_DATA) + bitmap_print(stderr, bm); +} + +void bitmap_flip_vertically(BITMAP *bm) +{ + BITMAP nb; + BmUnit *fptr, *tptr; + BmUnit fmask; + int w, h; + + nb.width = bm->width; + nb.height = bm->height; + nb.stride = bm->stride; + nb.data = mdvi_calloc(bm->height, bm->stride); + + fptr = bm->data; + tptr = __bm_unit_ptr(&nb, 0, nb.height-1); + for(h = 0; h < bm->height; h++) { + BmUnit *fline, *tline; + + fline = fptr; + tline = tptr; + fmask = FIRSTMASK; + for(w = 0; w < bm->width; w++) { + if(*fline & fmask) + *tline |= fmask; + if(fmask == LASTMASK) { + fmask = FIRSTMASK; + fline++; + tline++; + } else + NEXTMASK(fmask); + } + fptr = bm_offset(fptr, bm->stride); + tptr = (BmUnit *)((char *)tptr - bm->stride); + } + DEBUG((DBG_BITMAP_OPS, "flip_vertically (%d,%d) -> (%d,%d)\n", + bm->width, bm->height, nb.width, nb.height)); + mdvi_free(bm->data); + bm->data = nb.data; + if(SHOW_OP_DATA) + bitmap_print(stderr, bm); +} + +void bitmap_flip_diagonally(BITMAP *bm) +{ + BITMAP nb; + BmUnit *fptr, *tptr; + BmUnit fmask, tmask; + int w, h; + + nb.width = bm->width; + nb.height = bm->height; + nb.stride = bm->stride; + nb.data = mdvi_calloc(bm->height, bm->stride); + + fptr = bm->data; + tptr = __bm_unit_ptr(&nb, nb.width-1, nb.height-1); + for(h = 0; h < bm->height; h++) { + BmUnit *fline, *tline; + + fline = fptr; + tline = tptr; + fmask = FIRSTMASK; + tmask = FIRSTMASKAT(nb.width-1); + for(w = 0; w < bm->width; w++) { + if(*fline & fmask) + *tline |= tmask; + if(fmask == LASTMASK) { + fmask = FIRSTMASK; + fline++; + } else + NEXTMASK(fmask); + if(tmask == FIRSTMASK) { + tmask = LASTMASK; + tline--; + } else + PREVMASK(tmask); + } + fptr = bm_offset(fptr, bm->stride); + tptr = bm_offset(tptr, -nb.stride); + } + DEBUG((DBG_BITMAP_OPS, "flip_diagonally (%d,%d) -> (%d,%d)\n", + bm->width, bm->height, nb.width, nb.height)); + mdvi_free(bm->data); + bm->data = nb.data; + if(SHOW_OP_DATA) + bitmap_print(stderr, bm); +} + +void bitmap_rotate_clockwise(BITMAP *bm) +{ + BITMAP nb; + BmUnit *fptr, *tptr; + BmUnit fmask, tmask; + int w, h; + + nb.width = bm->height; + nb.height = bm->width; + nb.stride = BM_BYTES_PER_LINE(&nb); + nb.data = mdvi_calloc(nb.height, nb.stride); + + fptr = bm->data; + tptr = __bm_unit_ptr(&nb, nb.width - 1, 0); + + tmask = FIRSTMASKAT(nb.width-1); + for(h = 0; h < bm->height; h++) { + BmUnit *fline, *tline; + + fmask = FIRSTMASK; + fline = fptr; + tline = tptr; + for(w = 0; w < bm->width; w++) { + if(*fline & fmask) + *tline |= tmask; + if(fmask == LASTMASK) { + fmask = FIRSTMASK; + fline++; + } else + NEXTMASK(fmask); + /* go to next row */ + tline = bm_offset(tline, nb.stride); + } + fptr = bm_offset(fptr, bm->stride); + if(tmask == FIRSTMASK) { + tmask = LASTMASK; + tptr--; + } else + PREVMASK(tmask); + } + + DEBUG((DBG_BITMAP_OPS, "rotate_clockwise (%d,%d) -> (%d,%d)\n", + bm->width, bm->height, nb.width, nb.height)); + mdvi_free(bm->data); + bm->data = nb.data; + bm->width = nb.width; + bm->height = nb.height; + bm->stride = nb.stride; + if(SHOW_OP_DATA) + bitmap_print(stderr, bm); +} + +void bitmap_rotate_counter_clockwise(BITMAP *bm) +{ + BITMAP nb; + BmUnit *fptr, *tptr; + BmUnit fmask, tmask; + int w, h; + + nb.width = bm->height; + nb.height = bm->width; + nb.stride = BM_BYTES_PER_LINE(&nb); + nb.data = mdvi_calloc(nb.height, nb.stride); + + fptr = bm->data; + tptr = __bm_unit_ptr(&nb, 0, nb.height - 1); + + tmask = FIRSTMASK; + for(h = 0; h < bm->height; h++) { + BmUnit *fline, *tline; + + fmask = FIRSTMASK; + fline = fptr; + tline = tptr; + for(w = 0; w < bm->width; w++) { + if(*fline & fmask) + *tline |= tmask; + if(fmask == LASTMASK) { + fmask = FIRSTMASK; + fline++; + } else + NEXTMASK(fmask); + /* go to previous row */ + tline = bm_offset(tline, -nb.stride); + } + fptr = bm_offset(fptr, bm->stride); + if(tmask == LASTMASK) { + tmask = FIRSTMASK; + tptr++; + } else + NEXTMASK(tmask); + } + + DEBUG((DBG_BITMAP_OPS, "rotate_counter_clockwise (%d,%d) -> (%d,%d)\n", + bm->width, bm->height, nb.width, nb.height)); + mdvi_free(bm->data); + bm->data = nb.data; + bm->width = nb.width; + bm->height = nb.height; + bm->stride = nb.stride; + if(SHOW_OP_DATA) + bitmap_print(stderr, bm); +} + +void bitmap_flip_rotate_clockwise(BITMAP *bm) +{ + BITMAP nb; + BmUnit *fptr, *tptr; + BmUnit fmask, tmask; + int w, h; + + nb.width = bm->height; + nb.height = bm->width; + nb.stride = BM_BYTES_PER_LINE(&nb); + nb.data = mdvi_calloc(nb.height, nb.stride); + + fptr = bm->data; + tptr = __bm_unit_ptr(&nb, nb.width-1, nb.height-1); + + tmask = FIRSTMASKAT(nb.width-1); + for(h = 0; h < bm->height; h++) { + BmUnit *fline, *tline; + + fmask = FIRSTMASK; + fline = fptr; + tline = tptr; + for(w = 0; w < bm->width; w++) { + if(*fline & fmask) + *tline |= tmask; + if(fmask == LASTMASK) { + fmask = FIRSTMASK; + fline++; + } else + NEXTMASK(fmask); + /* go to previous line */ + tline = bm_offset(tline, -nb.stride); + } + fptr = bm_offset(fptr, bm->stride); + if(tmask == FIRSTMASK) { + tmask = LASTMASK; + tptr--; + } else + PREVMASK(tmask); + } + DEBUG((DBG_BITMAP_OPS, "flip_rotate_clockwise (%d,%d) -> (%d,%d)\n", + bm->width, bm->height, nb.width, nb.height)); + mdvi_free(bm->data); + bm->data = nb.data; + bm->width = nb.width; + bm->height = nb.height; + bm->stride = nb.stride; + if(SHOW_OP_DATA) + bitmap_print(stderr, bm); +} + +void bitmap_flip_rotate_counter_clockwise(BITMAP *bm) +{ + BITMAP nb; + BmUnit *fptr, *tptr; + BmUnit fmask, tmask; + int w, h; + + nb.width = bm->height; + nb.height = bm->width; + nb.stride = BM_BYTES_PER_LINE(&nb); + nb.data = mdvi_calloc(nb.height, nb.stride); + + fptr = bm->data; + tptr = nb.data; + tmask = FIRSTMASK; + + for(h = 0; h < bm->height; h++) { + BmUnit *fline, *tline; + + fmask = FIRSTMASK; + fline = fptr; + tline = tptr; + for(w = 0; w < bm->width; w++) { + if(*fline & fmask) + *tline |= tmask; + if(fmask == LASTMASK) { + fmask = FIRSTMASK; + fline++; + } else + NEXTMASK(fmask); + /* go to next line */ + tline = bm_offset(tline, nb.stride); + } + fptr = bm_offset(fptr, bm->stride); + if(tmask == LASTMASK) { + tmask = FIRSTMASK; + tptr++; + } else + NEXTMASK(tmask); + } + + DEBUG((DBG_BITMAP_OPS, "flip_rotate_counter_clockwise (%d,%d) -> (%d,%d)\n", + bm->width, bm->height, nb.width, nb.height)); + mdvi_free(bm->data); + bm->data = nb.data; + bm->width = nb.width; + bm->height = nb.height; + bm->stride = nb.stride; + if(SHOW_OP_DATA) + bitmap_print(stderr, bm); +} + +#if 0 +void bitmap_transform(BITMAP *map, DviOrientation orient) +{ + switch(orient) { + case MDVI_ORIENT_TBLR: + break; + case MDVI_ORIENT_TBRL: + bitmap_flip_horizontally(map); + break; + case MDVI_ORIENT_BTLR: + bitmap_flip_vertically(map); + break; + case MDVI_ORIENT_BTRL: + bitmap_flip_diagonally(map); + break; + case MDVI_ORIENT_RP90: + bitmap_rotate_counter_clockwise(map); + break; + case MDVI_ORIENT_RM90: + bitmap_rotate_clockwise(map); + break; + case MDVI_ORIENT_IRP90: + bitmap_flip_rotate_counter_clockwise(map); + break; + case MDVI_ORIENT_IRM90: + bitmap_flip_rotate_clockwise(map); + break; + } +} +#endif + +/* + * Count the number of non-zero bits in a box of dimensions w x h, starting + * at column `step' in row `data'. + * + * Shamelessly stolen from xdvi. + */ +static int do_sample(BmUnit *data, int stride, int step, int w, int h) +{ + BmUnit *ptr, *end, *cp; + int shift, n; + int bits_left; + int wid; + + ptr = data + step / BITMAP_BITS; + end = bm_offset(data, h * stride); + shift = FIRSTSHIFTAT(step); + bits_left = w; + n = 0; + while(bits_left) { +#ifndef WORD_BIG_ENDIAN + wid = BITMAP_BITS - shift; +#else + wid = shift; +#endif + if(wid > bits_left) + wid = bits_left; + if(wid > 8) + wid = 8; +#ifdef WORD_BIG_ENDIAN + shift -= wid; +#endif + for(cp = ptr; cp < end; cp = bm_offset(cp, stride)) + n += sample_count[(*cp >> shift) & bit_masks[wid]]; +#ifndef WORD_BIG_ENDIAN + shift += wid; +#endif +#ifdef WORD_BIG_ENDIAN + if(shift == 0) { + shift = BITMAP_BITS; + ptr++; + } +#else + if(shift == BITMAP_BITS) { + shift = 0; + ptr++; + } +#endif + bits_left -= wid; + } + return n; +} + +void mdvi_shrink_box(DviContext *dvi, DviFont *font, + DviFontChar *pk, DviGlyph *dest) +{ + int x, y, z; + DviGlyph *glyph; + int hs, vs; + + hs = dvi->params.hshrink; + vs = dvi->params.vshrink; + glyph = &pk->glyph; + + x = (int)glyph->x / hs; + if((int)glyph->x - x * hs > 0) + x++; + dest->w = x + ROUND((int)glyph->w - glyph->x, hs); + + z = (int)glyph->y + 1; + y = z / vs; + if(z - y * vs <= 0) + y--; + dest->h = y + ROUND((int)glyph->h - z, vs) + 1; + dest->x = x; + dest->y = glyph->y / vs; + dest->data = MDVI_GLYPH_EMPTY; + DEBUG((DBG_BITMAPS, "shrink_box: (%dw,%dh,%dx,%dy) -> (%dw,%dh,%dx,%dy)\n", + glyph->w, glyph->h, glyph->x, glyph->y, + dest->w, dest->h, dest->x, dest->y)); +} + +void mdvi_shrink_glyph(DviContext *dvi, DviFont *font, + DviFontChar *pk, DviGlyph *dest) +{ + int rows_left, rows, init_cols; + int cols_left, cols; + BmUnit *old_ptr, *new_ptr; + BITMAP *oldmap, *newmap; + BmUnit m, *cp; + DviGlyph *glyph; + int sample, min_sample; + int old_stride; + int new_stride; + int x, y; + int w, h; + int hs, vs; + + hs = dvi->params.hshrink; + vs = dvi->params.vshrink; + + min_sample = vs * hs * dvi->params.density / 100; + + glyph = &pk->glyph; + oldmap = (BITMAP *)glyph->data; + + x = (int)glyph->x / hs; + init_cols = (int)glyph->x - x * hs; + if(init_cols <= 0) + init_cols += hs; + else + x++; + w = x + ROUND((int)glyph->w - glyph->x, hs); + + cols = (int)glyph->y + 1; + y = cols / vs; + rows = cols - y * vs; + if(rows <= 0) { + rows += vs; + y--; + } + h = y + ROUND((int)glyph->h - cols, vs) + 1; + + /* create the new glyph */ + newmap = bitmap_alloc(w, h); + dest->data = newmap; + dest->x = x; + dest->y = glyph->y / vs; + dest->w = w; + dest->h = h; + + old_ptr = oldmap->data; + old_stride = oldmap->stride; + new_ptr = newmap->data; + new_stride = newmap->stride; + rows_left = glyph->h; + + while(rows_left) { + if(rows > rows_left) + rows = rows_left; + cols_left = glyph->w; + m = FIRSTMASK; + cp = new_ptr; + cols = init_cols; + while(cols_left > 0) { + if(cols > cols_left) + cols = cols_left; + sample = do_sample(old_ptr, old_stride, + glyph->w - cols_left, cols, rows); + if(sample >= min_sample) + *cp |= m; + if(m == LASTMASK) { + m = FIRSTMASK; + cp++; + } else + NEXTMASK(m); + cols_left -= cols; + cols = hs; + } + new_ptr = bm_offset(new_ptr, new_stride); + old_ptr = bm_offset(old_ptr, rows * old_stride); + rows_left -= rows; + rows = vs; + } + DEBUG((DBG_BITMAPS, "shrink_glyph: (%dw,%dh,%dx,%dy) -> (%dw,%dh,%dx,%dy)\n", + glyph->w, glyph->h, glyph->x, glyph->y, + dest->w, dest->h, dest->x, dest->y)); + if(DEBUGGING(BITMAP_DATA)) + bitmap_print(stderr, newmap); +} + +void mdvi_shrink_glyph_grey(DviContext *dvi, DviFont *font, + DviFontChar *pk, DviGlyph *dest) +{ + int rows_left, rows; + int cols_left, cols, init_cols; + long sampleval, samplemax; + BmUnit *old_ptr; + void *image; + int w, h; + int x, y; + DviGlyph *glyph; + BITMAP *map; + Ulong *pixels; + int npixels; + Ulong colortab[2]; + int hs, vs; + DviDevice *dev; + + hs = dvi->params.hshrink; + vs = dvi->params.vshrink; + dev = &dvi->device; + + glyph = &pk->glyph; + map = (BITMAP *)glyph->data; + + x = (int)glyph->x / hs; + init_cols = (int)glyph->x - x * hs; + if(init_cols <= 0) + init_cols += hs; + else + x++; + w = x + ROUND((int)glyph->w - glyph->x, hs); + + cols = (int)glyph->y + 1; + y = cols / vs; + rows = cols - y * vs; + if(rows <= 0) { + rows += vs; + y--; + } + h = y + ROUND((int)glyph->h - cols, vs) + 1; + ASSERT(w && h); + + /* before touching anything, do this */ + image = dev->create_image(dev->device_data, w, h, BITMAP_BITS); + if(image == NULL) { + mdvi_shrink_glyph(dvi, font, pk, dest); + return; + } + + /* save these colors */ + pk->fg = MDVI_CURRFG(dvi); + pk->bg = MDVI_CURRBG(dvi); + + samplemax = vs * hs; + npixels = samplemax + 1; + pixels = get_color_table(&dvi->device, npixels, pk->fg, pk->bg, + dvi->params.gamma, dvi->params.density); + if(pixels == NULL) { + npixels = 2; + colortab[0] = pk->fg; + colortab[1] = pk->bg; + pixels = &colortab[0]; + } + + /* setup the new glyph */ + dest->data = image; + dest->x = x; + dest->y = glyph->y / vs; + dest->w = w; + dest->h = h; + + y = 0; + old_ptr = map->data; + rows_left = glyph->h; + + while(rows_left && y < h) { + x = 0; + if(rows > rows_left) + rows = rows_left; + cols_left = glyph->w; + cols = init_cols; + while(cols_left && x < w) { + if(cols > cols_left) + cols = cols_left; + sampleval = do_sample(old_ptr, map->stride, + glyph->w - cols_left, cols, rows); + /* scale the sample value by the number of grey levels */ + if(npixels - 1 != samplemax) + sampleval = ((npixels-1) * sampleval) / samplemax; + ASSERT(sampleval < npixels); + dev->put_pixel(image, x, y, pixels[sampleval]); + cols_left -= cols; + cols = hs; + x++; + } + for(; x < w; x++) + dev->put_pixel(image, x, y, pixels[0]); + old_ptr = bm_offset(old_ptr, rows * map->stride); + rows_left -= rows; + rows = vs; + y++; + } + + for(; y < h; y++) { + for(x = 0; x < w; x++) + dev->put_pixel(image, x, y, pixels[0]); + } + DEBUG((DBG_BITMAPS, "shrink_glyph_grey: (%dw,%dh,%dx,%dy) -> (%dw,%dh,%dx,%dy)\n", + glyph->w, glyph->h, glyph->x, glyph->y, + dest->w, dest->h, dest->x, dest->y)); +} + diff --git a/backend/dvi/mdvi-lib/bitmap.h b/backend/dvi/mdvi-lib/bitmap.h new file mode 100644 index 00000000..4d98fecd --- /dev/null +++ b/backend/dvi/mdvi-lib/bitmap.h @@ -0,0 +1,145 @@ +/* + * Copyright (C) 2000, Matias Atria + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +#ifndef _BITMAP_H +#define _BITMAP_H 1 + +#include "sysdeps.h" + +/* Structures and functions to manipulate bitmaps */ + +/* bitmap unit (as in X's docs) */ +typedef Uint32 BmUnit; + +/* size (in bytes) of a bitmap atom */ +#define BITMAP_BYTES 4 + +/* size (in bits) of a bitmap atom */ +#define BITMAP_BITS (BITMAP_BYTES << 3) + +typedef struct { + int width; + int height; + int stride; + BmUnit *data; +} BITMAP; + +#define BM_BYTES_PER_LINE(b) \ + (ROUND((b)->width, BITMAP_BITS) * BITMAP_BYTES) +#define BM_WIDTH(b) (((BITMAP *)(b))->width) +#define BM_HEIGHT(b) (((BITMAP *)(b))->height) + +#define BMBIT(n) ((BmUnit)1 << (n)) + +/* Macros to manipulate individual pixels in a bitmap + * (they are slow, don't use them) + */ + +#define bm_offset(b,o) (BmUnit *)((Uchar *)(b) + (o)) + +#define __bm_unit_ptr(b,x,y) \ + bm_offset((b)->data, (y) * (b)->stride + \ + ((x) / BITMAP_BITS) * BITMAP_BYTES) + +#define __bm_unit(b,x,y) __bm_unit_ptr((b), (x), (y))[0] + +#define BM_GETPIXEL(b,x,y) __bm_unit((b), (x), (y)) +#define BM_SETPIXEL(b,x,y) (__bm_unit((b), (x), (y)) |= FIRSTMASKAT(x)) +#define BM_CLRPIXEL(b,x,y) (__bm_unit((b), (x), (y)) &= ~FIRSTMASKAT(x)) + +/* + * These macros are used to access pixels in a bitmap. They are supposed + * to be used like this: + */ +#if 0 + BmUnit *row, mask; + + mask = FIRSTMASK; + + /* position `unit' at coordinates (column_number, row_number) */ + unit = (BmUnit *)((char *)bitmap->data + row_number * bitmap->stride + + (column_number / BITMAP_BITS); + /* loop over all pixels IN THE SAME ROW */ + for(i = 0; i < number_of_pixels; i++) { + /* to test if a pixel is set */ + if(*unit & mask) { + /* yes, it is, do something with it */ + } + /* to set/clear a pixel */ + if(painting) + *unit |= mask; /* now you see it */ + else + *unit &= ~mask; /* now you don't */ + /* move to next pixel */ + if(mask == LASTMASK) { + unit++; + UPDATEMASK(mask); + } + } +/* end of sample code */ +#endif + +/* bitmaps are stored in native byte order */ +#ifdef WORD_BIG_ENDIAN +#define FIRSTSHIFT (BITMAP_BITS - 1) +#define LASTSHIFT 0 +#define NEXTMASK(m) ((m) >>= 1) +#define PREVMASK(m) ((m) <<= 1) +#define FIRSTSHIFTAT(c) (BITMAP_BITS - ((c) % BITMAP_BITS) - 1) +#else +#define FIRSTSHIFT 0 +#define LASTSHIFT (BITMAP_BITS - 1) +#define NEXTMASK(m) ((m) <<= 1) +#define PREVMASK(m) ((m) >>= 1) +#define FIRSTSHIFTAT(c) ((c) % BITMAP_BITS) +#endif + +#define FIRSTMASK BMBIT(FIRSTSHIFT) +#define FIRSTMASKAT(c) BMBIT(FIRSTSHIFTAT(c)) +#define LASTMASK BMBIT(LASTSHIFT) + +extern BITMAP *bitmap_alloc __PROTO((int, int)); +extern BITMAP *bitmap_alloc_raw __PROTO((int, int)); +extern void bitmap_destroy __PROTO((BITMAP *)); + +/* + * set_row(bm, row, col, count, state): + * sets `count' pixels to state `onoff', starting from pixel + * at position (col, row). All pixels must lie in the same + * row. + */ +extern void bitmap_set_col __PROTO((BITMAP *, int, int, int, int)); +extern void bitmap_set_row __PROTO((BITMAP *, int, int, int, int)); + +extern void bitmap_paint_bits __PROTO((BmUnit *, int, int)); +extern void bitmap_clear_bits __PROTO((BmUnit *, int, int)); + +extern BITMAP *bitmap_copy __PROTO((BITMAP *)); +extern void bitmap_flip_horizontally __PROTO((BITMAP *)); +extern void bitmap_flip_vertically __PROTO((BITMAP *)); +extern void bitmap_flip_diagonally __PROTO((BITMAP *)); +extern void bitmap_rotate_clockwise __PROTO((BITMAP *)); +extern void bitmap_rotate_counter_clockwise __PROTO((BITMAP *)); +extern void bitmap_flip_rotate_clockwise __PROTO((BITMAP *)); +extern void bitmap_flip_rotate_counter_clockwise __PROTO((BITMAP *)); +extern BITMAP *bitmap_convert_lsb8 __PROTO((Uchar *, int, int, int)); +extern BITMAP *bitmap_convert_msb8 __PROTO((Uchar *, int, int, int)); + +#include <stdio.h> +extern void bitmap_print __PROTO((FILE *, BITMAP *)); + +#endif /* _BITMAP_H */ diff --git a/backend/dvi/mdvi-lib/color.c b/backend/dvi/mdvi-lib/color.c new file mode 100644 index 00000000..c28107fd --- /dev/null +++ b/backend/dvi/mdvi-lib/color.c @@ -0,0 +1,136 @@ +/* + * Copyright (C) 2000, Matias Atria + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#include <config.h> +#include "mdvi.h" +#include "color.h" + +void mdvi_set_color(DviContext *dvi, Ulong fg, Ulong bg) +{ + if(dvi->curr_fg != fg || dvi->curr_bg != bg) { + DEBUG((DBG_DEVICE, "setting color to (%lu,%lu)\n", fg, bg)); + if(dvi->device.set_color) + dvi->device.set_color(dvi->device.device_data, fg, bg); + dvi->curr_fg = fg; + dvi->curr_bg = bg; + } +} + +void mdvi_push_color(DviContext *dvi, Ulong fg, Ulong bg) +{ + if(dvi->color_top == dvi->color_size) { + dvi->color_size += 32; + dvi->color_stack = mdvi_realloc(dvi->color_stack, + dvi->color_size * sizeof(DviColorPair)); + } + dvi->color_stack[dvi->color_top].fg = dvi->curr_fg; + dvi->color_stack[dvi->color_top].bg = dvi->curr_bg; + dvi->color_top++; + mdvi_set_color(dvi, fg, bg); +} + +void mdvi_pop_color(DviContext *dvi) +{ + Ulong fg, bg; + + if(dvi->color_top == 0) + return; + dvi->color_top--; + fg = dvi->color_stack[dvi->color_top].fg; + bg = dvi->color_stack[dvi->color_top].bg; + mdvi_set_color(dvi, fg, bg); +} + +void mdvi_reset_color(DviContext *dvi) +{ + dvi->color_top = 0; + mdvi_set_color(dvi, dvi->params.fg, dvi->params.bg); +} + +/* cache for color tables, to avoid creating them for every glyph */ +typedef struct { + Ulong fg; + Ulong bg; + Uint nlevels; + Ulong *pixels; + int density; + double gamma; + Uint hits; +} ColorCache; + +#define CCSIZE 256 +static ColorCache color_cache[CCSIZE]; +static int cc_entries; + +#define GAMMA_DIFF 0.005 + + +/* create a color table */ +Ulong *get_color_table(DviDevice *dev, + int nlevels, Ulong fg, Ulong bg, double gamma, int density) +{ + ColorCache *cc, *tofree; + int lohits; + Ulong *pixels; + int status; + + lohits = color_cache[0].hits; + tofree = &color_cache[0]; + /* look in the cache and see if we have one that matches this request */ + for(cc = &color_cache[0]; cc < &color_cache[cc_entries]; cc++) { + if(cc->hits < lohits) { + lohits = cc->hits; + tofree = cc; + } + if(cc->fg == fg && cc->bg == bg && cc->density == density && + cc->nlevels == nlevels && fabs(cc->gamma - gamma) <= GAMMA_DIFF) + break; + } + + if(cc < &color_cache[cc_entries]) { + cc->hits++; + return cc->pixels; + } + + DEBUG((DBG_DEVICE, "Adding color table to cache (fg=%lu, bg=%lu, n=%d)\n", + fg, bg, nlevels)); + + /* no entry was found in the cache, create a new one */ + if(cc_entries < CCSIZE) { + cc = &color_cache[cc_entries++]; + cc->pixels = NULL; + } else { + cc = tofree; + mdvi_free(cc->pixels); + } + pixels = xnalloc(Ulong, nlevels); + status = dev->alloc_colors(dev->device_data, + pixels, nlevels, fg, bg, gamma, density); + if(status < 0) { + mdvi_free(pixels); + return NULL; + } + cc->fg = fg; + cc->bg = bg; + cc->gamma = gamma; + cc->density = density; + cc->nlevels = nlevels; + cc->pixels = pixels; + cc->hits = 1; + return pixels; +} diff --git a/backend/dvi/mdvi-lib/color.h b/backend/dvi/mdvi-lib/color.h new file mode 100644 index 00000000..35b2f923 --- /dev/null +++ b/backend/dvi/mdvi-lib/color.h @@ -0,0 +1,34 @@ +/* + * Copyright (C) 2000, Matias Atria + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + + +#ifndef _COLOR_H_ +#define _COLOR_H_ + +#include "common.h" + +extern Ulong *get_color_table(DviDevice *dev, + int nlevels, Ulong fg, Ulong bg, double gamma, int density); + +extern void mdvi_set_color __PROTO((DviContext *, Ulong, Ulong)); +extern void mdvi_push_color __PROTO((DviContext *, Ulong, Ulong)); +extern void mdvi_pop_color __PROTO((DviContext *)); +extern void mdvi_reset_color __PROTO((DviContext *)); + +#endif /* _COLOR_H_ */ + diff --git a/backend/dvi/mdvi-lib/common.c b/backend/dvi/mdvi-lib/common.c new file mode 100644 index 00000000..97b34b56 --- /dev/null +++ b/backend/dvi/mdvi-lib/common.c @@ -0,0 +1,168 @@ +/* + * Copyright (C) 2000, Matias Atria + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#include <config.h> +#include <stdlib.h> +#include <string.h> + +#include "common.h" + +long fsgetn(FILE *p, size_t n) +{ + long v; + + v = fgetbyte(p); + if(v & 0x80) + v -= 0x100; + while(--n > 0) + v = (v << 8) | fgetbyte(p); + return v; +} + +Ulong fugetn(FILE *p, size_t n) +{ + Ulong v; + + v = fgetbyte(p); + while(--n > 0) + v = (v << 8) | fgetbyte(p); + return v; +} + +long msgetn(const Uchar *p, size_t n) +{ + long v = (long)*p++; + + if(v & 0x80) + v -= 0x100; + while(--n > 0) + v = (v << 8) | *p++; + return v; +} + +Ulong mugetn(const Uchar *p, size_t n) +{ + Ulong v = (Ulong)*p++; + + while(--n > 0) + v = (v << 8) | *p++; + return v; +} + +char *read_string(FILE *in, int s, char *buffer, size_t len) +{ + int n; + char *str; + + n = fugetn(in, s ? s : 1); + if((str = buffer) == NULL || n + 1 > len) + str = mdvi_malloc(n + 1); + if(fread(str, 1, n, in) != n) { + if(str != buffer) mdvi_free(str); + return NULL; + } + str[n] = 0; + return str; +} + +size_t read_bcpl(FILE *in, char *buffer, size_t maxlen, size_t wanted) +{ + size_t i; + + i = (int)fuget1(in); + if(maxlen && i > maxlen) + i = maxlen; + if(fread(buffer, i, 1, in) != 1) + return -1; + buffer[i] = '\0'; + while(wanted-- > i) + (void)fgetc(in); + return i; +} + +char *read_alloc_bcpl(FILE *in, size_t maxlen, size_t *size) +{ + size_t i; + char *buffer; + + i = (size_t)fuget1(in); + if(maxlen && i > maxlen) + i = maxlen; + buffer = (char *)malloc(i + 1); + if(buffer == NULL) + return NULL; + if(fread(buffer, i, 1, in) != 1) { + free(buffer); + return NULL; + } + buffer[i] = '\0'; + if(size) *size = i; + return buffer; +} + +/* buffers */ + +void buff_free(Buffer *buf) +{ + if(buf->data) + mdvi_free(buf->data); + buff_init(buf); +} + +void buff_init(Buffer *buf) +{ + buf->data = NULL; + buf->size = 0; + buf->length = 0; +} + +size_t buff_add(Buffer *buf, const char *data, size_t len) +{ + if(!len && data) + len = strlen(data); + if(buf->length + len + 1 > buf->size) { + buf->size = buf->length + len + 256; + buf->data = mdvi_realloc(buf->data, buf->size); + } + memcpy(buf->data + buf->length, data, len); + buf->length += len; + return buf->length; +} + +char *buff_gets(Buffer *buf, size_t *length) +{ + char *ptr; + char *ret; + size_t len; + + ptr = strchr(buf->data, '\n'); + if(ptr == NULL) + return NULL; + ptr++; /* include newline */ + len = ptr - buf->data; + ret = mdvi_malloc(len + 1); + if(len > 0) { + memcpy(ret, buf->data, len); + memmove(buf->data, buf->data + len, buf->length - len); + buf->length -= len; + } + ret[len] = 0; + if(length) *length = len; + return ret; +} + diff --git a/backend/dvi/mdvi-lib/common.h b/backend/dvi/mdvi-lib/common.h new file mode 100644 index 00000000..27a7d8f9 --- /dev/null +++ b/backend/dvi/mdvi-lib/common.h @@ -0,0 +1,285 @@ +/* + * Copyright (C) 2000, Matias Atria + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +#ifndef _MDVI_COMMON_H +#define _MDVI_COMMON_H 1 + +#include <stdio.h> +#include <sys/types.h> +#include <math.h> + +#include "sysdeps.h" + +#if STDC_HEADERS +# include <string.h> +#endif + +#if !defined(STDC_HEADERS) || defined(__STRICT_ANSI__) +# ifndef HAVE_STRCHR +# define strchr index +# define strrchr rindex +# endif +# ifndef HAVE_MEMCPY +# define memcpy(a,b,n) bcopy((b), (a), (n)) +# define memmove(a,b,n) bcopy((b), (a), (n)) +# endif +#endif + +#if defined(STDC_HEADERS) || defined(HAVE_MEMCPY) +#define memzero(a,n) memset((a), 0, (n)) +#else +#define memzero(a,n) bzero((a), (n)) +#endif + +typedef struct _List { + struct _List *next; + struct _List *prev; +} List; +#define LIST(x) ((List *)(x)) + +typedef struct { + char *data; + size_t size; + size_t length; +} Buffer; + +typedef struct { + List *head; + List *tail; + int count; +} ListHead; +#define MDVI_EMPTY_LIST_HEAD {NULL, NULL, 0} + +typedef struct { + char *data; + size_t size; + size_t length; +} Dstring; + +/* Functions to read numbers from streams and memory */ + +#define fgetbyte(p) ((unsigned)getc(p)) + +extern char *program_name; + +extern Ulong fugetn __PROTO((FILE *, size_t)); +extern long fsgetn __PROTO((FILE *, size_t)); +extern Ulong mugetn __PROTO((const Uchar *, size_t)); +extern long msgetn __PROTO((const Uchar *, size_t)); + +/* To read from a stream (fu: unsigned, fs: signed) */ +#define fuget4(p) fugetn((p), 4) +#define fuget3(p) fugetn((p), 3) +#define fuget2(p) fugetn((p), 2) +#define fuget1(p) fgetbyte(p) +#define fsget4(p) fsgetn((p), 4) +#define fsget3(p) fsgetn((p), 3) +#define fsget2(p) fsgetn((p), 2) +#define fsget1(p) fsgetn((p), 1) + +/* To read from memory (mu: unsigned, ms: signed) */ +#define MUGETN(p,n) ((p) += (n), mugetn((p)-(n), (n))) +#define MSGETN(p,n) ((p) += (n), msgetn((p)-(n), (n))) +#define muget4(p) MUGETN((p), 4) +#define muget3(p) MUGETN((p), 3) +#define muget2(p) MUGETN((p), 2) +#define muget1(p) MUGETN((p), 1) +#define msget4(p) MSGETN((p), 4) +#define msget3(p) MSGETN((p), 3) +#define msget2(p) MSGETN((p), 2) +#define msget1(p) MSGETN((p), 1) + +#define ROUND(x,y) (((x) + (y) - 1) / (y)) +#define FROUND(x) (int)((x) + 0.5) +#define SFROUND(x) (int)((x) >= 0 ? floor((x) + 0.5) : ceil((x) + 0.5)) + +#define Max(a,b) (((a) > (b)) ? (a) : (b)) +#define Min(a,b) (((a) < (b)) ? (a) : (b)) + +/* make 2byte number from 2 8bit quantities */ +#define HALFWORD(a,b) ((((a) << 8) & 0xf) | (b)) +#define FULLWORD(a,b,c,d) \ + ((((Int8)(a) << 24) & 0xff000000) | \ + (((Uint8)(b) << 16) & 0x00ff0000) | \ + (((Uint8)(c) << 8) & 0x0000ff00) | \ + ((Uint8)(d) & 0xff)) + +#if defined(__GNUC__) && !defined(__STRICT_ANSI__) +#define SWAPINT(a,b) \ + ({ int _s = a; a = b; b = _s; }) +#else +#define SWAPINT(a,b) do { int _s = a; a = b; b = _s; } while(0) +#endif + +#define STREQ(a,b) (strcmp((a), (b)) == 0) +#define STRNEQ(a,b,n) (strncmp((a), (b), (n)) == 0) +#define STRCEQ(a,b) (strcasecmp((a), (b)) == 0) +#define STRNCEQ(a,b,n) (strncasecmp((a), (b), (n)) == 0) + +extern char *read_string __PROTO((FILE *, int, char *, size_t)); +extern size_t read_bcpl __PROTO((FILE *, char *, size_t, size_t)); +extern char *read_alloc_bcpl __PROTO((FILE *, size_t, size_t *)); + +/* miscellaneous */ + +extern void mdvi_message __PROTO((const char *, ...)); +extern void mdvi_crash __PROTO((const char *, ...)); +extern void mdvi_fatal __PROTO((const char *, ...)); +extern void mdvi_error __PROTO((const char *, ...)); +extern void mdvi_warning __PROTO((const char *, ...)); +extern int unit2pix __PROTO((int, const char *)); +extern double unit2pix_factor __PROTO((const char *)); + +#define LOG_NONE -1 +#define LOG_INFO 0 +#define LOG_WARN 1 +#define LOG_ERROR 2 +#define LOG_DEBUG 3 + +#define DBG_OPCODE (1 << 0) +#define DBG_FONTS (1 << 1) +#define DBG_FILES (1 << 2) +#define DBG_DVI (1 << 3) +#define DBG_PARAMS (1 << 4) +#define DBG_SPECIAL (1 << 5) +#define DBG_DEVICE (1 << 6) +#define DBG_GLYPHS (1 << 7) +#define DBG_BITMAPS (1 << 8) +#define DBG_PATHS (1 << 9) +#define DBG_SEARCH (1 << 10) +#define DBG_VARS (1 << 11) +#define DBG_BITMAP_OPS (1 << 12) +#define DBG_BITMAP_DATA (1 << 13) +#define DBG_TYPE1 (1 << 14) +#define DBG_TT (1 << 15) +#define DBG_FT2 (1 << 16) +#define DBG_FMAP (1 << 17) + +#define DBG_SILENT (1 << 31) + +#ifdef NODEBUG +#define DEBUGGING(x) 0 +#else +#define DEBUGGING(x) (_mdvi_debug_mask & DBG_##x) +#endif + +#ifndef NODEBUG +extern Uint32 _mdvi_debug_mask; +extern void __debug __PROTO((int, const char *, ...)); +#define DEBUG(x) __debug x +#define ASSERT(x) do { \ + if(!(x)) mdvi_crash("%s:%d: Assertion %s failed\n", \ + __FILE__, __LINE__, #x); \ + } while(0) +#define ASSERT_VALUE(x,y) do { \ + if((x) != (y)) \ + mdvi_crash("%s:%d: Assertion failed (%d = %s != %s)\n", \ + __FILE__, __LINE__, (x), #x, #x); \ + } while(0) +#else +#define DEBUG(x) do { } while(0) +#define ASSERT(x) do { } while(0) +#define ASSERT_VALUE(x,y) do { } while(0) +#endif + +#define set_debug_mask(m) (_mdvi_debug_mask = (Uint32)(m)) +#define add_debug_mask(m) (_mdvi_debug_mask |= (Uint32)(m)) +#define get_debug_mask() _mdvi_debug_mask + +/* memory allocation */ + +extern void mdvi_free __PROTO((void *)); +extern void *mdvi_malloc __PROTO((size_t)); +extern void *mdvi_realloc __PROTO((void *, size_t)); +extern void *mdvi_calloc __PROTO((size_t, size_t)); +extern char *mdvi_strncpy __PROTO((char *, const char *, size_t)); +extern char *mdvi_strdup __PROTO((const char *)); +extern char *mdvi_strndup __PROTO((const char *, size_t)); +extern void *mdvi_memdup __PROTO((const void *, size_t)); +extern char *mdvi_build_path_from_cwd __PROTO((const char *)); +extern char *mdvi_strrstr __PROTO((const char *, const char *)); + +/* macros to make memory allocation nicer */ +#define xalloc(t) (t *)mdvi_malloc(sizeof(t)) +#define xnalloc(t,n) (t *)mdvi_calloc((n), sizeof(t)) +#define xresize(p,t,n) (t *)mdvi_realloc((p), (n) * sizeof(t)) + +extern char *xstradd __PROTO((char *, size_t *, size_t, const char *, size_t)); + +extern Ulong get_mtime __PROTO((int)); + +/* lists */ +extern void listh_init __PROTO((ListHead *)); +extern void listh_prepend __PROTO((ListHead *, List *)); +extern void listh_append __PROTO((ListHead *, List *)); +extern void listh_add_before __PROTO((ListHead *, List *, List *)); +extern void listh_add_after __PROTO((ListHead *, List *, List *)); +extern void listh_remove __PROTO((ListHead *, List *)); +extern void listh_concat __PROTO((ListHead *, ListHead *)); +extern void listh_catcon __PROTO((ListHead *, ListHead *)); + +extern void buff_init __PROTO((Buffer *)); +extern size_t buff_add __PROTO((Buffer *, const char *, size_t)); +extern char *buff_gets __PROTO((Buffer *, size_t *)); +extern void buff_free __PROTO((Buffer *)); + +extern char *getword __PROTO((char *, const char *, char **)); +extern char *getstring __PROTO((char *, const char *, char **)); + +extern void dstring_init __PROTO((Dstring *)); +extern int dstring_new __PROTO((Dstring *, const char *, int)); +extern int dstring_append __PROTO((Dstring *, const char *, int)); +extern int dstring_copy __PROTO((Dstring *, int, const char *, int)); +extern int dstring_insert __PROTO((Dstring *, int, const char *, int)); +extern void dstring_reset __PROTO((Dstring *)); + +#define dstring_length(d) ((d)->length) +#define dstring_strcat(d,s) dstring_append((d), (s), -1) + +extern char *dgets __PROTO((Dstring *, FILE *)); +extern int file_readable __PROTO((const char *)); +extern int file_exists __PROTO((const char *)); +extern const char *file_basename __PROTO((const char *)); +extern const char *file_extension __PROTO((const char *)); + +/* + * Miscellaneous macros + */ + +#define LIST_FOREACH(ptr, type, list) \ + for(ptr = (type *)(list)->head; ptr; ptr = (ptr)->next) + +#define Size(x) (sizeof(x) / sizeof((x)[0])) + +/* multiply a fix_word by a 32bit number */ +#define B0(x) ((x) & 0xff) +#define B1(x) B0((x) >> 8) +#define B2(x) B0((x) >> 16) +#define B3(x) B0((x) >> 24) +#define __tfm_mul(z,t) \ + (((((B0(t) * (z)) >> 8) + (B1(t) * (z))) >> 8) + B2(t) * (z)) +#define TFMSCALE(z,t,a,b) \ + ((B3(t) == 255) ? \ + __tfm_mul((z), (t)) / (b) - (a) : \ + __tfm_mul((z), (t)) / (b)) +#define TFMPREPARE(x,z,a,b) do { \ + a = 16; z = (x); \ + while(z > 040000000L) { z >>= 1; a <<= 1; } \ + b = 256 / a; a *= z; \ + } while(0) + +#endif /* _MDVI_COMMON_H */ diff --git a/backend/dvi/mdvi-lib/defaults.h b/backend/dvi/mdvi-lib/defaults.h new file mode 100644 index 00000000..7e63f81e --- /dev/null +++ b/backend/dvi/mdvi-lib/defaults.h @@ -0,0 +1,71 @@ +/* + * Copyright (C) 2000, Matias Atria + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +#ifndef _MDVI_DEFAULTS_H +#define _MDVI_DEFAULTS_H 1 + +/* resolution */ +#define MDVI_DPI 600 +#define MDVI_VDPI MDVI_DPI + +/* horizontal margins */ +#define MDVI_HMARGIN "1in" + +/* vertical margins */ +#define MDVI_VMARGIN "1in" + +/* rulers */ +#define MDVI_HRUNITS "1in" +#define MDVI_VRUNITS "1in" + +/* paper */ +#define MDVI_PAPERNAME "letter" + +/* magnification */ +#define MDVI_MAGNIFICATION 1.0 + +/* fallback font */ +#define MDVI_FALLBACK_FONT "cmr10" + +/* metafont mode */ +#define MDVI_MFMODE NULL + +/* default shrinking factor */ +#define MDVI_DEFAULT_SHRINKING -1 /* based on resolution */ + +/* default pixel density */ +#define MDVI_DEFAULT_DENSITY 50 + +/* default gamma correction */ +#define MDVI_DEFAULT_GAMMA 1.0 + +/* default window geometry */ +#define MDVI_GEOMETRY NULL + +/* default orientation */ +#define MDVI_ORIENTATION "tblr" + +/* colors */ +#define MDVI_FOREGROUND "black" +#define MDVI_BACKGROUND "white" + +/* flags */ +#define MDVI_DEFAULT_FLAGS MDVI_ANTIALIASED + +#define MDVI_DEFAULT_CONFIG "mdvi.conf" + +#endif /* _MDVI_DEAFAULTS_H */ diff --git a/backend/dvi/mdvi-lib/dviopcodes.h b/backend/dvi/mdvi-lib/dviopcodes.h new file mode 100644 index 00000000..f99af05e --- /dev/null +++ b/backend/dvi/mdvi-lib/dviopcodes.h @@ -0,0 +1,72 @@ +#ifndef _MDVI_DVIOPCODES_H +#define _MDVI_DVIOPCODES_H 1 + +#define DVI_SET_CHAR0 0 +#define DVI_SET_CHAR1 1 +#define DVI_SET_CHAR_MAX 127 +#define DVI_SET1 128 +#define DVI_SET2 129 +#define DVI_SET3 130 +#define DVI_SET4 131 +#define DVI_SET_RULE 132 +#define DVI_PUT1 133 +#define DVI_PUT2 134 +#define DVI_PUT3 135 +#define DVI_PUT4 136 +#define DVI_PUT_RULE 137 +#define DVI_NOOP 138 +#define DVI_BOP 139 +#define DVI_EOP 140 +#define DVI_PUSH 141 +#define DVI_POP 142 +#define DVI_RIGHT1 143 +#define DVI_RIGHT2 144 +#define DVI_RIGHT3 145 +#define DVI_RIGHT4 146 +#define DVI_W0 147 +#define DVI_W1 148 +#define DVI_W2 149 +#define DVI_W3 150 +#define DVI_W4 151 +#define DVI_X0 152 +#define DVI_X1 153 +#define DVI_X2 154 +#define DVI_X3 155 +#define DVI_X4 156 +#define DVI_DOWN1 157 +#define DVI_DOWN2 158 +#define DVI_DOWN3 159 +#define DVI_DOWN4 160 +#define DVI_Y0 161 +#define DVI_Y1 162 +#define DVI_Y2 163 +#define DVI_Y3 164 +#define DVI_Y4 165 +#define DVI_Z0 166 +#define DVI_Z1 167 +#define DVI_Z2 168 +#define DVI_Z3 169 +#define DVI_Z4 170 +#define DVI_FNT_NUM0 171 +#define DVI_FNT_NUM1 172 +#define DVI_FNT_NUM_MAX 234 +#define DVI_FNT1 235 +#define DVI_FNT2 236 +#define DVI_FNT3 237 +#define DVI_FNT4 238 +#define DVI_XXX1 239 +#define DVI_XXX2 240 +#define DVI_XXX3 241 +#define DVI_XXX4 242 +#define DVI_FNT_DEF1 243 +#define DVI_FNT_DEF2 244 +#define DVI_FNT_DEF3 245 +#define DVI_FNT_DEF4 246 +#define DVI_PRE 247 +#define DVI_POST 248 +#define DVI_POST_POST 249 + +#define DVI_ID 2 +#define DVI_TRAILER 223 + +#endif /* _MDVI_DVIOPCODES_H */ diff --git a/backend/dvi/mdvi-lib/dviread.c b/backend/dvi/mdvi-lib/dviread.c new file mode 100644 index 00000000..97b7b844 --- /dev/null +++ b/backend/dvi/mdvi-lib/dviread.c @@ -0,0 +1,1585 @@ +/* + * Copyright (C) 2000, Matias Atria + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#include <config.h> +#include <stdio.h> +#include <stdlib.h> +#include <stdarg.h> +#include <string.h> +#include <math.h> +#include <errno.h> +#include <unistd.h> +#include <fcntl.h> + +#include "mdvi.h" +#include "private.h" +#include "color.h" + +typedef int (*DviCommand) __PROTO((DviContext *, int)); + +#define DVICMDDEF(x) static int x __PROTO((DviContext *, int)) + +DVICMDDEF(set_char); +DVICMDDEF(set_rule); +DVICMDDEF(no_op); +DVICMDDEF(push); +DVICMDDEF(pop); +DVICMDDEF(move_right); +DVICMDDEF(move_down); +DVICMDDEF(move_w); +DVICMDDEF(move_x); +DVICMDDEF(move_y); +DVICMDDEF(move_z); +DVICMDDEF(sel_font); +DVICMDDEF(sel_fontn); +DVICMDDEF(special); +DVICMDDEF(def_font); +DVICMDDEF(undefined); +DVICMDDEF(unexpected); + +static const DviCommand dvi_commands[] = { + set_char, set_char, set_char, set_char, + set_char, set_char, set_char, set_char, + set_char, set_char, set_char, set_char, + set_char, set_char, set_char, set_char, + set_char, set_char, set_char, set_char, + set_char, set_char, set_char, set_char, + set_char, set_char, set_char, set_char, + set_char, set_char, set_char, set_char, + set_char, set_char, set_char, set_char, + set_char, set_char, set_char, set_char, + set_char, set_char, set_char, set_char, + set_char, set_char, set_char, set_char, + set_char, set_char, set_char, set_char, + set_char, set_char, set_char, set_char, + set_char, set_char, set_char, set_char, + set_char, set_char, set_char, set_char, + set_char, set_char, set_char, set_char, + set_char, set_char, set_char, set_char, + set_char, set_char, set_char, set_char, + set_char, set_char, set_char, set_char, + set_char, set_char, set_char, set_char, + set_char, set_char, set_char, set_char, + set_char, set_char, set_char, set_char, + set_char, set_char, set_char, set_char, + set_char, set_char, set_char, set_char, + set_char, set_char, set_char, set_char, + set_char, set_char, set_char, set_char, + set_char, set_char, set_char, set_char, + set_char, set_char, set_char, set_char, + set_char, set_char, set_char, set_char, + set_char, set_char, set_char, set_char, + set_char, set_char, set_char, set_char, /* 0 - 127 */ + set_char, set_char, set_char, set_char, /* 128 - 131 */ + set_rule, /* 132 */ + set_char, set_char, set_char, set_char, /* 133 - 136 */ + set_rule, /* 137 */ + no_op, /* 138 */ + unexpected, /* 139 (BOP) */ + unexpected, /* 140 (EOP) */ + push, /* 141 */ + pop, /* 142 */ + move_right, move_right, move_right, move_right, /* 143 - 146 */ + move_w, move_w, move_w, move_w, move_w, /* 147 - 151 */ + move_x, move_x, move_x, move_x, move_x, /* 152 - 156 */ + move_down, move_down, move_down, move_down, /* 157 - 160 */ + move_y, move_y, move_y, move_y, move_y, /* 161 - 165 */ + move_z, move_z, move_z, move_z, move_z, /* 166 - 170 */ + sel_font, sel_font, sel_font, sel_font, + sel_font, sel_font, sel_font, sel_font, + sel_font, sel_font, sel_font, sel_font, + sel_font, sel_font, sel_font, sel_font, + sel_font, sel_font, sel_font, sel_font, + sel_font, sel_font, sel_font, sel_font, + sel_font, sel_font, sel_font, sel_font, + sel_font, sel_font, sel_font, sel_font, + sel_font, sel_font, sel_font, sel_font, + sel_font, sel_font, sel_font, sel_font, + sel_font, sel_font, sel_font, sel_font, + sel_font, sel_font, sel_font, sel_font, + sel_font, sel_font, sel_font, sel_font, + sel_font, sel_font, sel_font, sel_font, + sel_font, sel_font, sel_font, sel_font, + sel_font, sel_font, sel_font, sel_font, /* 171 - 234 */ + sel_fontn, sel_fontn, sel_fontn, sel_fontn, /* 235 - 238 */ + special, special, special, special, /* 239 - 242 */ + def_font, def_font, def_font, def_font, /* 243 - 246 */ + unexpected, /* 247 (PRE) */ + unexpected, /* 248 (POST) */ + unexpected, /* 249 (POST_POST) */ + undefined, undefined, undefined, + undefined, undefined, undefined /* 250 - 255 */ +}; + +#define DVI_BUFLEN 4096 + +static int mdvi_run_macro(DviContext *dvi, Uchar *macro, size_t len); + +static void dummy_draw_glyph(DviContext *dvi, DviFontChar *ch, int x, int y) +{ +} + +static void dummy_draw_rule(DviContext *dvi, int x, int y, Uint w, Uint h, int f) +{ +} + +static int dummy_alloc_colors(void *a, Ulong *b, int c, Ulong d, Ulong e, double f, int g) +{ + return -1; +} + +static void *dummy_create_image(void *a, Uint b, Uint c, Uint d) +{ + return NULL; +} + +static void dummy_free_image(void *a) +{ +} + +static void dummy_dev_destroy(void *a) +{ +} + +static void dummy_dev_putpixel(void *a, int x, int y, Ulong c) +{ +} + +static void dummy_dev_refresh(DviContext *a, void *b) +{ +} + +static void dummy_dev_set_color(void *a, Ulong b, Ulong c) +{ +} + +/* functions to report errors */ +static void dvierr(DviContext *dvi, const char *format, ...) +{ + va_list ap; + + va_start(ap, format); + fprintf(stderr, "%s[%d]: Error: ", + dvi->filename, dvi->currpage); + vfprintf(stderr, format, ap); + va_end(ap); +} + +static void dviwarn(DviContext *dvi, const char *format, ...) +{ + va_list ap; + + fprintf(stderr, "%s[%d]: Warning: ", + dvi->filename, dvi->currpage); + va_start(ap, format); + vfprintf(stderr, format, ap); + va_end(ap); +} + +#define NEEDBYTES(d,n) \ + ((d)->buffer.pos + (n) > (d)->buffer.length) + +static int get_bytes(DviContext *dvi, size_t n) +{ + /* + * caller wants to read `n' bytes from dvi->buffer + dvi->pos. + * Make sure there is enough data to satisfy the request + */ + if(NEEDBYTES(dvi, n)) { + size_t required; + int newlen; + + if(dvi->buffer.frozen || dvi->in == NULL || feof(dvi->in)) { + /* this is EOF */ + dviwarn(dvi, _("unexpected EOF\n")); + return -1; + } + /* get more data */ + if(dvi->buffer.data == NULL) { + /* first allocation */ + dvi->buffer.size = Max(DVI_BUFLEN, n); + dvi->buffer.data = (Uchar *)mdvi_malloc(dvi->buffer.size); + dvi->buffer.length = 0; + dvi->buffer.frozen = 0; + } else if(dvi->buffer.pos < dvi->buffer.length) { + /* move whatever we want to keep */ + dvi->buffer.length -= dvi->buffer.pos; + memmove(dvi->buffer.data, + dvi->buffer.data + dvi->buffer.pos, + dvi->buffer.length); + } else { + /* we can discard all the data in this buffer */ + dvi->buffer.length = 0; + } + + required = n - dvi->buffer.length; + if(required > dvi->buffer.size - dvi->buffer.length) { + /* need to allocate more memory */ + dvi->buffer.size = dvi->buffer.length + required + 128; + dvi->buffer.data = (Uchar *)xresize(dvi->buffer.data, + char, dvi->buffer.size); + } + /* now read into the buffer */ + newlen = fread(dvi->buffer.data + dvi->buffer.length, + 1, dvi->buffer.size - dvi->buffer.length, dvi->in); + if(newlen == -1) { + mdvi_error("%s: %s\n", dvi->filename, strerror(errno)); + return -1; + } + dvi->buffer.length += newlen; + dvi->buffer.pos = 0; + } + return 0; +} + +/* only relative forward seeks are supported by this function */ +static int dskip(DviContext *dvi, long offset) +{ + ASSERT(offset > 0); + + if(NEEDBYTES(dvi, offset) && get_bytes(dvi, offset) == -1) + return -1; + dvi->buffer.pos += offset; + return 0; +} + +/* DVI I/O functions (note: here `n' must be <= 4) */ +static long dsgetn(DviContext *dvi, size_t n) +{ + long val; + + if(NEEDBYTES(dvi, n) && get_bytes(dvi, n) == -1) + return -1; + val = msgetn(dvi->buffer.data + dvi->buffer.pos, n); + dvi->buffer.pos += n; + return val; +} + +static int dread(DviContext *dvi, char *buffer, size_t len) +{ + if(NEEDBYTES(dvi, len) && get_bytes(dvi, len) == -1) + return -1; + memcpy(buffer, dvi->buffer.data + dvi->buffer.pos, len); + dvi->buffer.pos += len; + return 0; +} + +static long dugetn(DviContext *dvi, size_t n) +{ + long val; + + if(NEEDBYTES(dvi, n) && get_bytes(dvi, n) == -1) + return -1; + val = mugetn(dvi->buffer.data + dvi->buffer.pos, n); + dvi->buffer.pos += n; + return val; +} + +static long dtell(DviContext *dvi) +{ + return dvi->depth ? + dvi->buffer.pos : + ftell(dvi->in) - dvi->buffer.length + dvi->buffer.pos; +} + +static void dreset(DviContext *dvi) +{ + if(!dvi->buffer.frozen && dvi->buffer.data) + mdvi_free(dvi->buffer.data); + dvi->buffer.data = NULL; + dvi->buffer.size = 0; + dvi->buffer.length = 0; + dvi->buffer.pos = 0; +} + +#define dsget1(d) dsgetn((d), 1) +#define dsget2(d) dsgetn((d), 2) +#define dsget3(d) dsgetn((d), 3) +#define dsget4(d) dsgetn((d), 4) +#define duget1(d) dugetn((d), 1) +#define duget2(d) dugetn((d), 2) +#define duget3(d) dugetn((d), 3) +#define duget4(d) dugetn((d), 4) + +#ifndef NODEBUG +static void dviprint(DviContext *dvi, const char *command, int sub, const char *fmt, ...) +{ + int i; + va_list ap; + + printf("%s: ", dvi->filename); + for(i = 0; i < dvi->depth; i++) + printf(" "); + printf("%4lu: %s", dtell(dvi), command); + if(sub >= 0) printf("%d", sub); + if(*fmt) printf(": "); + va_start(ap, fmt); + vprintf(fmt, ap); + va_end(ap); +} +#define SHOWCMD(x) \ + if(_mdvi_debug_mask & DBG_OPCODE) do { dviprint x; } while(0) +#else +#define SHOWCMD(x) do { } while(0) +#endif + +int mdvi_find_tex_page(DviContext *dvi, int tex_page) +{ + int i; + + for(i = 0; i < dvi->npages; i++) + if(dvi->pagemap[i][1] == tex_page) + return i; + return -1; +} + +/* page sorting functions */ +static int sort_up(const void *p1, const void *p2) +{ + return ((long *)p1)[1] - ((long *)p2)[1]; +} +static int sort_down(const void *p1, const void *p2) +{ + return ((long *)p2)[1] - ((long *)p1)[1]; +} +static int sort_random(const void *p1, const void *p2) +{ + return (rand() % 1) ? -1 : 1; +} +static int sort_dvi_up(const void *p1, const void *p2) +{ + return ((long *)p1)[0] - ((long *)p2)[0]; +} +static int sort_dvi_down(const void *p1, const void *p2) +{ + return ((long *)p1)[0] - ((long *)p2)[0]; +} + +void mdvi_sort_pages(DviContext *dvi, DviPageSort type) +{ + int (*sortfunc) __PROTO((const void *, const void *)); + + switch(type) { + case MDVI_PAGE_SORT_UP: + sortfunc = sort_up; + break; + case MDVI_PAGE_SORT_DOWN: + sortfunc = sort_down; + break; + case MDVI_PAGE_SORT_RANDOM: + sortfunc = sort_random; + break; + case MDVI_PAGE_SORT_DVI_UP: + sortfunc = sort_dvi_up; + break; + case MDVI_PAGE_SORT_DVI_DOWN: + sortfunc = sort_dvi_down; + break; + case MDVI_PAGE_SORT_NONE: + default: + sortfunc = NULL; + break; + } + + if(sortfunc) + qsort(dvi->pagemap, dvi->npages, sizeof(PageNum), sortfunc); +} + +static DviFontRef *define_font(DviContext *dvi, int op) +{ + Int32 arg; + Int32 scale; + Int32 dsize; + Int32 checksum; + int hdpi; + int vdpi; + int n; + char *name; + DviFontRef *ref; + + arg = dugetn(dvi, op - DVI_FNT_DEF1 + 1); + checksum = duget4(dvi); + scale = duget4(dvi); + dsize = duget4(dvi); + hdpi = FROUND(dvi->params.mag * dvi->params.dpi * scale / dsize); + vdpi = FROUND(dvi->params.mag * dvi->params.vdpi * scale / dsize); + n = duget1(dvi) + duget1(dvi); + name = mdvi_malloc(n + 1); + dread(dvi, name, n); + name[n] = 0; + DEBUG((DBG_FONTS, "requesting font %d = `%s' at %.1fpt (%dx%d dpi)\n", + arg, name, (double)scale / (dvi->params.tfm_conv * 0x100000), + hdpi, vdpi)); + ref = font_reference(&dvi->params, arg, name, checksum, hdpi, vdpi, scale); + if(ref == NULL) { + mdvi_error(_("could not load font `%s'\n"), name); + mdvi_free(name); + return NULL; + } + mdvi_free(name); + return ref; +} + +static char *opendvi(const char *name) +{ + int len; + char *file; + + len = strlen(name); + /* if file ends with .dvi and it exists, that's it */ + if(len >= 4 && STREQ(name+len-4, ".dvi")) { + DEBUG((DBG_DVI|DBG_FILES, "opendvi: Trying `%s'\n", name)); + if(access(name, R_OK) == 0) + return mdvi_strdup(name); + } + + /* try appending .dvi */ + file = mdvi_malloc(len + 5); + strcpy(file, name); + strcpy(file+len, ".dvi"); + DEBUG((DBG_DVI|DBG_FILES, "opendvi: Trying `%s'\n", file)); + if(access(file, R_OK) == 0) + return file; + /* try the given name */ + file[len] = 0; + DEBUG((DBG_DVI|DBG_FILES, "opendvi: Trying `%s'\n", file)); + if(access(file, R_OK) == 0) + return file; + mdvi_free(file); + return NULL; +} + +int mdvi_reload(DviContext *dvi, DviParams *np) +{ + DviContext *newdvi; + DviParams *pars; + + /* close our file */ + if(dvi->in) { + fclose(dvi->in); + dvi->in = NULL; + } + + pars = np ? np : &dvi->params; + DEBUG((DBG_DVI, "%s: reloading\n", dvi->filename)); + + /* load it again */ + newdvi = mdvi_init_context(pars, dvi->pagesel, dvi->filename); + if(newdvi == NULL) { + mdvi_warning(_("could not reload `%s'\n"), dvi->filename); + return -1; + } + + /* drop all our font references */ + font_drop_chain(dvi->fonts); + /* destroy our font map */ + if(dvi->fontmap) + mdvi_free(dvi->fontmap); + dvi->currfont = NULL; + + /* and use the ones we just loaded */ + dvi->fonts = newdvi->fonts; + dvi->fontmap = newdvi->fontmap; + dvi->nfonts = newdvi->nfonts; + + /* copy the new information */ + dvi->params = newdvi->params; + dvi->num = newdvi->num; + dvi->den = newdvi->den; + dvi->dvimag = newdvi->dvimag; + dvi->dviconv = newdvi->dviconv; + dvi->dvivconv = newdvi->dvivconv; + dvi->modtime = newdvi->modtime; + + if(dvi->fileid) mdvi_free(dvi->fileid); + dvi->fileid = newdvi->fileid; + + dvi->dvi_page_w = newdvi->dvi_page_w; + dvi->dvi_page_h = newdvi->dvi_page_h; + + mdvi_free(dvi->pagemap); + dvi->pagemap = newdvi->pagemap; + dvi->npages = newdvi->npages; + if(dvi->currpage > dvi->npages-1) + dvi->currpage = 0; + + mdvi_free(dvi->stack); + dvi->stack = newdvi->stack; + dvi->stacksize = newdvi->stacksize; + + /* remove fonts that are not being used anymore */ + font_free_unused(&dvi->device); + + mdvi_free(newdvi->filename); + mdvi_free(newdvi); + + DEBUG((DBG_DVI, "%s: reload successful\n", dvi->filename)); + if(dvi->device.refresh) + dvi->device.refresh(dvi, dvi->device.device_data); + + return 0; +} + +/* function to change parameters ia DVI context + * The DVI context is modified ONLY if this function is successful */ +int mdvi_configure(DviContext *dvi, DviParamCode option, ...) +{ + va_list ap; + int reset_all; + int reset_font; + DviParams np; + + va_start(ap, option); + + reset_font = 0; + reset_all = 0; + np = dvi->params; /* structure copy */ + while(option != MDVI_PARAM_LAST) { + switch(option) { + case MDVI_SET_DPI: + np.dpi = np.vdpi = va_arg(ap, Uint); + reset_all = 1; + break; + case MDVI_SET_XDPI: + np.dpi = va_arg(ap, Uint); + reset_all = 1; + break; + case MDVI_SET_YDPI: + np.vdpi = va_arg(ap, Uint); + break; + case MDVI_SET_SHRINK: + np.hshrink = np.vshrink = va_arg(ap, Uint); + reset_font = MDVI_FONTSEL_GREY|MDVI_FONTSEL_BITMAP; + break; + case MDVI_SET_XSHRINK: + np.hshrink = va_arg(ap, Uint); + reset_font = MDVI_FONTSEL_GREY|MDVI_FONTSEL_BITMAP; + break; + case MDVI_SET_YSHRINK: + np.vshrink = va_arg(ap, Uint); + reset_font = MDVI_FONTSEL_GREY|MDVI_FONTSEL_BITMAP; + break; + case MDVI_SET_ORIENTATION: + np.orientation = va_arg(ap, DviOrientation); + reset_font = MDVI_FONTSEL_GLYPH; + break; + case MDVI_SET_GAMMA: + np.gamma = va_arg(ap, double); + reset_font = MDVI_FONTSEL_GREY; + break; + case MDVI_SET_DENSITY: + np.density = va_arg(ap, Uint); + reset_font = MDVI_FONTSEL_BITMAP; + break; + case MDVI_SET_MAGNIFICATION: + np.mag = va_arg(ap, double); + reset_all = 1; + break; + case MDVI_SET_DRIFT: + np.hdrift = np.vdrift = va_arg(ap, int); + break; + case MDVI_SET_HDRIFT: + np.hdrift = va_arg(ap, int); + break; + case MDVI_SET_VDRIFT: + np.vdrift = va_arg(ap, int); + break; + case MDVI_SET_FOREGROUND: + np.fg = va_arg(ap, Ulong); + reset_font = MDVI_FONTSEL_GREY; + break; + case MDVI_SET_BACKGROUND: + np.bg = va_arg(ap, Ulong); + reset_font = MDVI_FONTSEL_GREY; + break; + default: + break; + } + option = va_arg(ap, DviParamCode); + } + va_end(ap); + + /* check that all values make sense */ + if(np.dpi <= 0 || np.vdpi <= 0) + return -1; + if(np.mag <= 0.0) + return -1; + if(np.density < 0) + return -1; + if(np.hshrink < 1 || np.vshrink < 1) + return -1; + if(np.hdrift < 0 || np.vdrift < 0) + return -1; + if(np.fg == np.bg) + return -1; + + /* + * If the dpi or the magnification change, we basically have to reload + * the DVI file again from scratch. + */ + + if(reset_all) + return (mdvi_reload(dvi, &np) == 0); + + if(np.hshrink != dvi->params.hshrink) { + np.conv = dvi->dviconv; + if(np.hshrink) + np.conv /= np.hshrink; + } + if(np.vshrink != dvi->params.vshrink) { + np.vconv = dvi->dvivconv; + if(np.vshrink) + np.vconv /= np.vshrink; + } + + if(reset_font) { + font_reset_chain_glyphs(&dvi->device, dvi->fonts, reset_font); + } + dvi->params = np; + if((reset_font & MDVI_FONTSEL_GLYPH) && dvi->device.refresh) { + dvi->device.refresh(dvi, dvi->device.device_data); + return 0; + } + + return 1; +} +/* + * Read the initial data from the DVI file. If something is wrong with the + * file, we just spit out an error message and refuse to load the file, + * without giving any details. This makes sense because DVI files are ok + * 99.99% of the time, and dvitype(1) can be used to check the other 0.01%. + */ +DviContext *mdvi_init_context(DviParams *par, DviPageSpec *spec, const char *file) +{ + FILE *p; + Int32 arg; + int op; + long offset; + int n; + DviContext *dvi; + char *filename; + int pagecount; + + /* + * 1. Open the file and initialize the DVI context + */ + + filename = opendvi(file); + if(filename == NULL) { + perror(file); + return NULL; + } + p = fopen(filename, "rb"); + if(p == NULL) { + perror(file); + mdvi_free(filename); + return NULL; + } + dvi = xalloc(DviContext); + memzero(dvi, sizeof(DviContext)); + dvi->pagemap = NULL; + dvi->filename = filename; + dvi->stack = NULL; + dvi->modtime = get_mtime(fileno(p)); + dvi->buffer.data = NULL; + dvi->pagesel = spec; + dvi->in = p; /* now we can use the dget*() functions */ + + /* + * 2. Read the preamble, extract scaling information, and + * setup the DVI parameters. + */ + + if(fuget1(p) != DVI_PRE) + goto bad_dvi; + if((arg = fuget1(p)) != DVI_ID) { + mdvi_error(_("%s: unsupported DVI format (version %u)\n"), + file, arg); + goto error; /* jump to the end of this routine, + * where we handle errors */ + } + /* get dimensions */ + dvi->num = fuget4(p); + dvi->den = fuget4(p); + dvi->dvimag = fuget4(p); + + /* check that these numbers make sense */ + if(!dvi->num || !dvi->den || !dvi->dvimag) + goto bad_dvi; + + dvi->params.mag = + (par->mag > 0 ? par->mag : (double)dvi->dvimag / 1000.0); + dvi->params.hdrift = par->hdrift; + dvi->params.vdrift = par->vdrift; + dvi->params.dpi = par->dpi ? par->dpi : MDVI_DPI; + dvi->params.vdpi = par->vdpi ? par->vdpi : par->dpi; + dvi->params.hshrink = par->hshrink; + dvi->params.vshrink = par->vshrink; + dvi->params.density = par->density; + dvi->params.gamma = par->gamma; + dvi->params.conv = (double)dvi->num / dvi->den; + dvi->params.conv *= (dvi->params.dpi / 254000.0) * dvi->params.mag; + dvi->params.vconv = (double)dvi->num / dvi->den; + dvi->params.vconv *= (dvi->params.vdpi / 254000.0) * dvi->params.mag; + dvi->params.tfm_conv = (25400000.0 / dvi->num) * + ((double)dvi->den / 473628672) / 16.0; + dvi->params.flags = par->flags; + dvi->params.orientation = par->orientation; + dvi->params.fg = par->fg; + dvi->params.bg = par->bg; + + /* initialize colors */ + dvi->curr_fg = par->fg; + dvi->curr_bg = par->bg; + dvi->color_stack = NULL; + dvi->color_top = 0; + dvi->color_size = 0; + + /* pixel conversion factors */ + dvi->dviconv = dvi->params.conv; + dvi->dvivconv = dvi->params.vconv; + if(dvi->params.hshrink) + dvi->params.conv /= dvi->params.hshrink; + if(dvi->params.vshrink) + dvi->params.vconv /= dvi->params.vshrink; + + /* get the comment from the preamble */ + n = fuget1(p); + dvi->fileid = mdvi_malloc(n + 1); + fread(dvi->fileid, 1, n, p); + dvi->fileid[n] = 0; + DEBUG((DBG_DVI, "%s: %s\n", filename, dvi->fileid)); + + /* + * 3. Read postamble, extract page information (number of + * pages, dimensions) and stack depth. + */ + + /* jump to the end of the file */ + if(fseek(p, (long)-1, SEEK_END) == -1) + goto error; + for(n = 0; (op = fuget1(p)) == DVI_TRAILER; n++) + if(fseek(p, (long)-2, SEEK_CUR) < 0) + break; + if(op != arg || n < 4) + goto bad_dvi; + /* get the pointer to postamble */ + fseek(p, (long)-5, SEEK_CUR); + arg = fuget4(p); + /* jump to it */ + fseek(p, (long)arg, SEEK_SET); + if(fuget1(p) != DVI_POST) + goto bad_dvi; + offset = fuget4(p); + if(dvi->num != fuget4(p) || dvi->den != fuget4(p) || + dvi->dvimag != fuget4(p)) + goto bad_dvi; + dvi->dvi_page_h = fuget4(p); + dvi->dvi_page_w = fuget4(p); + dvi->stacksize = fuget2(p); + dvi->npages = fuget2(p); + DEBUG((DBG_DVI, "%s: from postamble: stack depth %d, %d page%s\n", + filename, dvi->stacksize, dvi->npages, dvi->npages > 1 ? "s" : "")); + + /* + * 4. Process font definitions. + */ + + /* process font definitions */ + dvi->nfonts = 0; + dvi->fontmap = NULL; + /* + * CAREFUL: here we need to use the dvi->buffer, but it might leave the + * the file cursor in the wrong position after reading fonts (because of + * buffering). It's ok, though, because after the font definitions we read + * the page offsets, and we fseek() to the relevant part of the file with + * SEEK_SET. Nothing is read after the page offsets. + */ + while((op = duget1(dvi)) != DVI_POST_POST) { + DviFontRef *ref; + + if(op == DVI_NOOP) + continue; + else if(op < DVI_FNT_DEF1 || op > DVI_FNT_DEF4) + goto error; + ref = define_font(dvi, op); + if(ref == NULL) + goto error; + ref->next = dvi->fonts; + dvi->fonts = ref; + dvi->nfonts++; + } + /* we don't need the buffer anymore */ + dreset(dvi); + + if(op != DVI_POST_POST) + goto bad_dvi; + font_finish_definitions(dvi); + DEBUG((DBG_DVI, "%s: %d font%s required by this job\n", + filename, dvi->nfonts, dvi->nfonts > 1 ? "s" : "")); + dvi->findref = font_find_mapped; + + /* + * 5. Build the page map. + */ + + dvi->pagemap = xnalloc(PageNum, dvi->npages); + memzero(dvi->pagemap, sizeof(PageNum) * dvi->npages); + + n = dvi->npages - 1; + pagecount = n; + while(offset != -1) { + int i; + PageNum page; + + fseek(p, offset, SEEK_SET); + op = fuget1(p); + if(op != DVI_BOP || n < 0) + goto bad_dvi; + for(i = 1; i <= 10; i++) + page[i] = fsget4(p); + page[0] = offset; + offset = fsget4(p); + /* check if the page is selected */ + if(spec && mdvi_page_selected(spec, page, n) == 0) { + DEBUG((DBG_DVI, "Page %d (%ld.%ld.%ld.%ld.%ld.%ld.%ld.%ld.%ld.%ld) ignored by request\n", + n, page[1], page[2], page[3], page[4], page[5], + page[6], page[7], page[8], page[9], page[10])); + } else { + memcpy(&dvi->pagemap[pagecount], page, sizeof(PageNum)); + pagecount--; + } + n--; + } + pagecount++; + if(pagecount >= dvi->npages) { + mdvi_error(_("no pages selected\n")); + goto error; + } + if(pagecount) { + DEBUG((DBG_DVI, "%d of %d pages selected\n", + dvi->npages - pagecount, dvi->npages)); + dvi->npages -= pagecount; + memmove(dvi->pagemap, &dvi->pagemap[pagecount], + dvi->npages * sizeof(PageNum)); + } + + /* + * 6. Setup stack, initialize device functions + */ + + dvi->curr_layer = 0; + dvi->stack = xnalloc(DviState, dvi->stacksize + 8); + + dvi->device.draw_glyph = dummy_draw_glyph; + dvi->device.draw_rule = dummy_draw_rule; + dvi->device.alloc_colors = dummy_alloc_colors; + dvi->device.create_image = dummy_create_image; + dvi->device.free_image = dummy_free_image; + dvi->device.dev_destroy = dummy_dev_destroy; + dvi->device.put_pixel = dummy_dev_putpixel; + dvi->device.refresh = dummy_dev_refresh; + dvi->device.set_color = dummy_dev_set_color; + dvi->device.device_data = NULL; + + DEBUG((DBG_DVI, "%s read successfully\n", filename)); + return dvi; + +bad_dvi: + mdvi_error(_("%s: File corrupted, or not a DVI file\n"), file); +error: + /* if we came from the font definitions, this will be non-trivial */ + dreset(dvi); + mdvi_destroy_context(dvi); + return NULL; +} + +void mdvi_destroy_context(DviContext *dvi) +{ + if(dvi->device.dev_destroy) + dvi->device.dev_destroy(dvi->device.device_data); + /* release all fonts */ + if(dvi->fonts) { + font_drop_chain(dvi->fonts); + font_free_unused(&dvi->device); + } + if(dvi->fontmap) + mdvi_free(dvi->fontmap); + if(dvi->filename) + mdvi_free(dvi->filename); + if(dvi->stack) + mdvi_free(dvi->stack); + if(dvi->pagemap) + mdvi_free(dvi->pagemap); + if(dvi->fileid) + mdvi_free(dvi->fileid); + if(dvi->in) + fclose(dvi->in); + if(dvi->buffer.data && !dvi->buffer.frozen) + mdvi_free(dvi->buffer.data); + if(dvi->color_stack) + mdvi_free(dvi->color_stack); + + mdvi_free(dvi); +} + +void mdvi_setpage(DviContext *dvi, int pageno) +{ + if(pageno < 0) + pageno = 0; + if(pageno > dvi->npages-1) + pageno = dvi->npages - 1; + dvi->currpage = pageno; +} + +static int mdvi_run_macro(DviContext *dvi, Uchar *macro, size_t len) +{ + DviFontRef *curr, *fonts; + DviBuffer saved_buffer; + FILE *saved_file; + int opcode; + int oldtop; + + dvi->depth++; + push(dvi, DVI_PUSH); + dvi->pos.w = 0; + dvi->pos.x = 0; + dvi->pos.y = 0; + dvi->pos.z = 0; + + /* save our state */ + curr = dvi->currfont; + fonts = dvi->fonts; + saved_buffer = dvi->buffer; + saved_file = dvi->in; + dvi->currfont = curr->ref->subfonts; + dvi->fonts = curr->ref->subfonts; + dvi->buffer.data = macro; + dvi->buffer.pos = 0; + dvi->buffer.length = len; + dvi->buffer.frozen = 1; + dvi->in = NULL; + oldtop = dvi->stacktop; + + /* execute commands */ + while((opcode = duget1(dvi)) != DVI_EOP) { + if(dvi_commands[opcode](dvi, opcode) < 0) + break; + } + if(opcode != DVI_EOP) + dviwarn(dvi, _("%s: vf macro had errors\n"), + curr->ref->fontname); + if(dvi->stacktop != oldtop) + dviwarn(dvi, _("%s: stack not empty after vf macro\n"), + curr->ref->fontname); + + /* restore things */ + pop(dvi, DVI_POP); + dvi->currfont = curr; + dvi->fonts = fonts; + dvi->buffer = saved_buffer; + dvi->in = saved_file; + dvi->depth--; + + return (opcode != DVI_EOP ? -1 : 0); +} + +int mdvi_dopage(DviContext *dvi, int pageno) +{ + int op; + int ppi; + int reloaded = 0; + +again: + if(dvi->in == NULL) { + /* try reopening the file */ + dvi->in = fopen(dvi->filename, "rb"); + if(dvi->in == NULL) { + mdvi_warning(_("%s: could not reopen file (%s)\n"), + dvi->filename, + strerror(errno)); + return -1; + } + DEBUG((DBG_FILES, "reopen(%s) -> Ok\n", dvi->filename)); + } + + /* check if we need to reload the file */ + if(!reloaded && get_mtime(fileno(dvi->in)) > dvi->modtime) { + mdvi_reload(dvi, &dvi->params); + /* we have to reopen the file, again */ + reloaded = 1; + goto again; + } + + if(pageno < 0 || pageno > dvi->npages-1) { + mdvi_error(_("%s: page %d out of range\n"), + dvi->filename, pageno); + return -1; + } + + fseek(dvi->in, (long)dvi->pagemap[pageno][0], SEEK_SET); + if((op = fuget1(dvi->in)) != DVI_BOP) { + mdvi_error(_("%s: bad offset at page %d\n"), + dvi->filename, pageno+1); + return -1; + } + + /* skip bop */ + fseek(dvi->in, (long)44, SEEK_CUR); + + /* reset state */ + dvi->currfont = NULL; + memzero(&dvi->pos, sizeof(DviState)); + dvi->stacktop = 0; + dvi->currpage = pageno; + dvi->curr_layer = 0; + + if(dvi->buffer.data && !dvi->buffer.frozen) + mdvi_free(dvi->buffer.data); + + /* reset our buffer */ + dvi->buffer.data = NULL; + dvi->buffer.length = 0; + dvi->buffer.pos = 0; + dvi->buffer.frozen = 0; + +#if 0 /* make colors survive page breaks */ + /* reset color stack */ + mdvi_reset_color(dvi); +#endif + + /* set max horizontal and vertical drift (from dvips) */ + if(dvi->params.hdrift < 0) { + ppi = dvi->params.dpi / dvi->params.hshrink; /* shrunk pixels per inch */ + if(ppi < 600) + dvi->params.hdrift = ppi / 100; + else if(ppi < 1200) + dvi->params.hdrift = ppi / 200; + else + dvi->params.hdrift = ppi / 400; + } + if(dvi->params.vdrift < 0) { + ppi = dvi->params.vdpi / dvi->params.vshrink; /* shrunk pixels per inch */ + if(ppi < 600) + dvi->params.vdrift = ppi / 100; + else if(ppi < 1200) + dvi->params.vdrift = ppi / 200; + else + dvi->params.vdrift = ppi / 400; + } + + dvi->params.thinsp = FROUND(0.025 * dvi->params.dpi / dvi->params.conv); + dvi->params.vsmallsp = FROUND(0.025 * dvi->params.vdpi / dvi->params.vconv); + + /* execute all the commands in the page */ + while((op = duget1(dvi)) != DVI_EOP) { + if(dvi_commands[op](dvi, op) < 0) + break; + } + + fflush(stdout); + fflush(stderr); + if(op != DVI_EOP) + return -1; + if(dvi->stacktop) + dviwarn(dvi, _("stack not empty at end of page\n")); + return 0; +} + +static int inline move_vertical(DviContext *dvi, int amount) +{ + int rvv; + + dvi->pos.v += amount; + rvv = vpixel_round(dvi, dvi->pos.v); + if(!dvi->params.vdrift) + return rvv; + if(amount > dvi->params.vsmallsp || amount <= -dvi->params.vsmallsp) + return rvv; + else { + int newvv; + + newvv = dvi->pos.vv + vpixel_round(dvi, amount); + if(rvv - newvv > dvi->params.vdrift) + return rvv - dvi->params.vdrift; + else if(newvv - rvv > dvi->params.vdrift) + return rvv + dvi->params.vdrift; + else + return newvv; + } +} + +static int inline move_horizontal(DviContext *dvi, int amount) +{ + int rhh; + + dvi->pos.h += amount; + rhh = pixel_round(dvi, dvi->pos.h); + if(!dvi->params.hdrift) + return rhh; + else if(amount > dvi->params.thinsp || amount <= -6 * dvi->params.thinsp) + return rhh; + else { + int newhh; + + newhh = dvi->pos.hh + pixel_round(dvi, amount); + if(rhh - newhh > dvi->params.hdrift) + return rhh - dvi->params.hdrift; + else if(newhh - rhh > dvi->params.hdrift) + return rhh + dvi->params.hdrift; + else + return newhh; + } +} + +static void inline fix_after_horizontal(DviContext *dvi) +{ + int rhh; + + rhh = pixel_round(dvi, dvi->pos.h); + if(!dvi->params.hdrift) + dvi->pos.hh = rhh; + else if(rhh - dvi->pos.hh > dvi->params.hdrift) + dvi->pos.hh = rhh - dvi->params.hdrift; + else if(dvi->pos.hh - rhh > dvi->params.hdrift) + dvi->pos.hh = rhh + dvi->params.hdrift; +} + +/* commands */ + +#define DBGSUM(a,b,c) \ + (a), (b) > 0 ? '+' : '-', \ + (b) > 0 ? (b) : -(b), (c) + +/* + * Draw rules with some sort of antialias support. Usefult for high-rate + * scale factors. + */ + +static void draw_shrink_rule (DviContext *dvi, int x, int y, Uint w, Uint h, int f) +{ + int hs, vs, npixels; + Ulong fg, bg; + Ulong *pixels; + + hs = dvi->params.hshrink; + vs = dvi->params.vshrink; + fg = dvi->curr_fg; + bg = dvi->curr_bg; + + if (MDVI_ENABLED(dvi, MDVI_PARAM_ANTIALIASED)) { + npixels = vs * hs + 1; + pixels = get_color_table(&dvi->device, npixels, bg, fg, + dvi->params.gamma, dvi->params.density); + + if (pixels) { + int color; + + /* Lines with width 1 should be perfectly visible + * in shrink about 15. That is the reason of constant + */ + + color = (pow (vs / h * hs, 2) + pow (hs / w * vs, 2)) / 225; + if (color < npixels) { + fg = pixels[color]; + } else { + fg = pixels[npixels - 1]; + } + } + } + + mdvi_push_color (dvi, fg, bg); + dvi->device.draw_rule(dvi, x, y, w, h, f); + mdvi_pop_color (dvi); + + return; +} + +/* + * The only commands that actually draw something are: + * set_char, set_rule + */ + +static void draw_box(DviContext *dvi, DviFontChar *ch) +{ + DviGlyph *glyph = NULL; + int x, y, w, h; + + if(!MDVI_GLYPH_UNSET(ch->shrunk.data)) + glyph = &ch->shrunk; + else if(!MDVI_GLYPH_UNSET(ch->grey.data)) + glyph = &ch->grey; + else if(!MDVI_GLYPH_UNSET(ch->glyph.data)) + glyph = &ch->glyph; + if(glyph == NULL) + return; + x = glyph->x; + y = glyph->y; + w = glyph->w; + h = glyph->h; + /* this is bad -- we have to undo the orientation */ + switch(dvi->params.orientation) { + case MDVI_ORIENT_TBLR: + break; + case MDVI_ORIENT_TBRL: + x = w - x; + break; + case MDVI_ORIENT_BTLR: + y = h - y; + break; + case MDVI_ORIENT_BTRL: + x = w - x; + y = h - y; + break; + case MDVI_ORIENT_RP90: + SWAPINT(w, h); + SWAPINT(x, y); + x = w - x; + break; + case MDVI_ORIENT_RM90: + SWAPINT(w, h); + SWAPINT(x, y); + y = h - y; + break; + case MDVI_ORIENT_IRP90: + SWAPINT(w, h); + SWAPINT(x, y); + break; + case MDVI_ORIENT_IRM90: + SWAPINT(w, h); + SWAPINT(x, y); + x = w - x; + y = h - y; + break; + } + + draw_shrink_rule(dvi, dvi->pos.hh - x, dvi->pos.vv - y, w, h, 1); +} + +int set_char(DviContext *dvi, int opcode) +{ + int num; + int h; + int hh; + DviFontChar *ch; + DviFont *font; + + if(opcode < 128) + num = opcode; + else + num = dugetn(dvi, opcode - DVI_SET1 + 1); + if(dvi->currfont == NULL) { + dvierr(dvi, _("no default font set yet\n")); + return -1; + } + font = dvi->currfont->ref; + ch = font_get_glyph(dvi, font, num); + if(ch == NULL || ch->missing) { + /* try to display something anyway */ + ch = FONTCHAR(font, num); + if(!glyph_present(ch)) { + dviwarn(dvi, + _("requested character %d does not exist in `%s'\n"), + num, font->fontname); + return 0; + } + draw_box(dvi, ch); + } else if(dvi->curr_layer <= dvi->params.layer) { + if(ISVIRTUAL(font)) + mdvi_run_macro(dvi, (Uchar *)font->private + + ch->offset, ch->width); + else if(ch->width && ch->height) + dvi->device.draw_glyph(dvi, ch, + dvi->pos.hh, dvi->pos.vv); + } + if(opcode >= DVI_PUT1 && opcode <= DVI_PUT4) { + SHOWCMD((dvi, "putchar", opcode - DVI_PUT1 + 1, + "char %d (%s)\n", + num, dvi->currfont->ref->fontname)); + } else { + h = dvi->pos.h + ch->tfmwidth; + hh = dvi->pos.hh + pixel_round(dvi, ch->tfmwidth); + SHOWCMD((dvi, "setchar", num, "(%d,%d) h:=%d%c%d=%d, hh:=%d (%s)\n", + dvi->pos.hh, dvi->pos.vv, + DBGSUM(dvi->pos.h, ch->tfmwidth, h), hh, + font->fontname)); + dvi->pos.h = h; + dvi->pos.hh = hh; + fix_after_horizontal(dvi); + } + + return 0; +} + +int set_rule(DviContext *dvi, int opcode) +{ + Int32 a, b; + int h, w; + + a = dsget4(dvi); + b = dsget4(dvi); w = rule_round(dvi, b); + if(a > 0 && b > 0) { + h = vrule_round(dvi, a); + SHOWCMD((dvi, opcode == DVI_SET_RULE ? "setrule" : "putrule", -1, + "width %d, height %d (%dx%d pixels)\n", + b, a, w, h)); + /* the `draw' functions expect the origin to be at the top left + * corner of the rule, not the bottom left, as in DVI files */ + if(dvi->curr_layer <= dvi->params.layer) { + draw_shrink_rule(dvi, + dvi->pos.hh, dvi->pos.vv - h + 1, w, h, 1); + } + } else { + SHOWCMD((dvi, opcode == DVI_SET_RULE ? "setrule" : "putrule", -1, + "(moving left only, by %d)\n", b)); + } + + if(opcode == DVI_SET_RULE) { + dvi->pos.h += b; + dvi->pos.hh += w; + fix_after_horizontal(dvi); + } + return 0; +} + +int no_op(DviContext *dvi, int opcode) +{ + SHOWCMD((dvi, "noop", -1, "")); + return 0; +} + +int push(DviContext *dvi, int opcode) +{ + if(dvi->stacktop == dvi->stacksize) { + if(!dvi->depth) + dviwarn(dvi, _("enlarging stack\n")); + dvi->stacksize += 8; + dvi->stack = xresize(dvi->stack, + DviState, dvi->stacksize); + } + memcpy(&dvi->stack[dvi->stacktop], &dvi->pos, sizeof(DviState)); + SHOWCMD((dvi, "push", -1, + "level %d: (h=%d,v=%d,w=%d,x=%d,y=%d,z=%d,hh=%d,vv=%d)\n", + dvi->stacktop, + dvi->pos.h, dvi->pos.v, dvi->pos.w, dvi->pos.x, + dvi->pos.y, dvi->pos.z, dvi->pos.hh, dvi->pos.vv)); + dvi->stacktop++; + return 0; +} + +int pop(DviContext *dvi, int opcode) +{ + if(dvi->stacktop == 0) { + dvierr(dvi, _("stack underflow\n")); + return -1; + } + memcpy(&dvi->pos, &dvi->stack[dvi->stacktop-1], sizeof(DviState)); + SHOWCMD((dvi, "pop", -1, + "level %d: (h=%d,v=%d,w=%d,x=%d,y=%d,z=%d,hh=%d,vv=%d)\n", + dvi->stacktop, + dvi->pos.h, dvi->pos.v, dvi->pos.w, dvi->pos.x, + dvi->pos.y, dvi->pos.z, dvi->pos.hh, dvi->pos.vv)); + dvi->stacktop--; + return 0; +} + +int move_right(DviContext *dvi, int opcode) +{ + Int32 arg; + int h, hh; + + arg = dsgetn(dvi, opcode - DVI_RIGHT1 + 1); + h = dvi->pos.h; + hh = move_horizontal(dvi, arg); + SHOWCMD((dvi, "right", opcode - DVI_RIGHT1 + 1, + "%d h:=%d%c%d=%d, hh:=%d\n", + arg, DBGSUM(h, arg, dvi->pos.h), hh)); + dvi->pos.hh = hh; + return 0; +} + +int move_down(DviContext *dvi, int opcode) +{ + Int32 arg; + int v, vv; + + arg = dsgetn(dvi, opcode - DVI_DOWN1 + 1); + v = dvi->pos.v; + vv = move_vertical(dvi, arg); + SHOWCMD((dvi, "down", opcode - DVI_DOWN1 + 1, + "%d v:=%d%c%d=%d, vv:=%d\n", + arg, DBGSUM(v, arg, dvi->pos.v), vv)); + dvi->pos.vv = vv; + return 0; +} + +int move_w(DviContext *dvi, int opcode) +{ + int h, hh; + + if(opcode != DVI_W0) + dvi->pos.w = dsgetn(dvi, opcode - DVI_W0); + h = dvi->pos.h; + hh = move_horizontal(dvi, dvi->pos.w); + SHOWCMD((dvi, "w", opcode - DVI_W0, + "%d h:=%d%c%d=%d, hh:=%d\n", + dvi->pos.w, DBGSUM(h, dvi->pos.w, dvi->pos.h), hh)); + dvi->pos.hh = hh; + return 0; +} + +int move_x(DviContext *dvi, int opcode) +{ + int h, hh; + + if(opcode != DVI_X0) + dvi->pos.x = dsgetn(dvi, opcode - DVI_X0); + h = dvi->pos.h; + hh = move_horizontal(dvi, dvi->pos.x); + SHOWCMD((dvi, "x", opcode - DVI_X0, + "%d h:=%d%c%d=%d, hh:=%d\n", + dvi->pos.x, DBGSUM(h, dvi->pos.x, dvi->pos.h), hh)); + dvi->pos.hh = hh; + return 0; +} + +int move_y(DviContext *dvi, int opcode) +{ + int v, vv; + + if(opcode != DVI_Y0) + dvi->pos.y = dsgetn(dvi, opcode - DVI_Y0); + v = dvi->pos.v; + vv = move_vertical(dvi, dvi->pos.y); + SHOWCMD((dvi, "y", opcode - DVI_Y0, + "%d h:=%d%c%d=%d, hh:=%d\n", + dvi->pos.y, DBGSUM(v, dvi->pos.y, dvi->pos.v), vv)); + dvi->pos.vv = vv; + return 0; +} + +int move_z(DviContext *dvi, int opcode) +{ + int v, vv; + + if(opcode != DVI_Z0) + dvi->pos.z = dsgetn(dvi, opcode - DVI_Z0); + v = dvi->pos.v; + vv = move_vertical(dvi, dvi->pos.z); + SHOWCMD((dvi, "z", opcode - DVI_Z0, + "%d h:=%d%c%d=%d, hh:=%d\n", + dvi->pos.z, DBGSUM(v, dvi->pos.z, dvi->pos.v), vv)); + dvi->pos.vv = vv; + return 0; +} + +int sel_font(DviContext *dvi, int opcode) +{ + DviFontRef *ref; + int ndx; + + ndx = opcode - DVI_FNT_NUM0; + if(dvi->depth) + ref = font_find_flat(dvi, ndx); + else + ref = dvi->findref(dvi, ndx); + if(ref == NULL) { + dvierr(dvi, _("font %d is not defined\n"), + opcode - DVI_FNT_NUM0); + return -1; + } + SHOWCMD((dvi, "fntnum", opcode - DVI_FNT_NUM0, + "current font is %s\n", + ref->ref->fontname)); + dvi->currfont = ref; + return 0; +} + +int sel_fontn(DviContext *dvi, int opcode) +{ + Int32 arg; + DviFontRef *ref; + + arg = dugetn(dvi, opcode - DVI_FNT1 + 1); + if(dvi->depth) + ref = font_find_flat(dvi, arg); + else + ref = dvi->findref(dvi, arg); + if(ref == NULL) { + dvierr(dvi, _("font %d is not defined\n"), arg); + return -1; + } + SHOWCMD((dvi, "fnt", opcode - DVI_FNT1 + 1, + "current font is %s (id %d)\n", + ref->ref->fontname, arg)); + dvi->currfont = ref; + return 0; +} + +int special(DviContext *dvi, int opcode) +{ + char *s; + Int32 arg; + + arg = dugetn(dvi, opcode - DVI_XXX1 + 1); + s = mdvi_malloc(arg + 1); + dread(dvi, s, arg); + s[arg] = 0; + mdvi_do_special(dvi, s); + SHOWCMD((dvi, "XXXX", opcode - DVI_XXX1 + 1, + "[%s]", s)); + mdvi_free(s); + return 0; +} + +int def_font(DviContext *dvi, int opcode) +{ + DviFontRef *ref; + Int32 arg; + + arg = dugetn(dvi, opcode - DVI_FNT_DEF1 + 1); + if(dvi->depth) + ref = font_find_flat(dvi, arg); + else + ref = dvi->findref(dvi, arg); + /* skip the rest */ + dskip(dvi, 12); + dskip(dvi, duget1(dvi) + duget1(dvi)); + if(ref == NULL) { + dvierr(dvi, _("font %d is not defined in postamble\n"), arg); + return -1; + } + SHOWCMD((dvi, "fntdef", opcode - DVI_FNT_DEF1 + 1, + "%d -> %s (%d links)\n", + ref->fontid, ref->ref->fontname, + ref->ref->links)); + return 0; +} + +int unexpected(DviContext *dvi, int opcode) +{ + dvierr(dvi, _("unexpected opcode %d\n"), opcode); + return -1; +} + +int undefined(DviContext *dvi, int opcode) +{ + dvierr(dvi, _("undefined opcode %d\n"), opcode); + return -1; +} + diff --git a/backend/dvi/mdvi-lib/files.c b/backend/dvi/mdvi-lib/files.c new file mode 100644 index 00000000..b7065068 --- /dev/null +++ b/backend/dvi/mdvi-lib/files.c @@ -0,0 +1,80 @@ +/* + * Copyright (C) 2000, Matias Atria + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#include <config.h> +#include <unistd.h> +#include <fcntl.h> +#include <sys/stat.h> + +#include "common.h" + +char *dgets(Dstring *dstr, FILE *in) +{ + char buffer[256]; + + dstr->length = 0; + if(feof(in)) + return NULL; + while(fgets(buffer, 256, in) != NULL) { + int len = strlen(buffer); + + if(buffer[len-1] == '\n') { + dstring_append(dstr, buffer, len - 1); + break; + } + dstring_append(dstr, buffer, len); + } + if(dstr->data) + dstr->data[dstr->length] = 0; + return dstr->data; +} + +/* some simple helper functions to manipulate file names */ + +const char *file_basename(const char *filename) +{ + const char *ptr = strrchr(filename, '/'); + + return (ptr ? ptr + 1 : filename); +} + +const char *file_extension(const char *filename) +{ + const char *ptr = strchr(file_basename(filename), '.'); + + return (ptr ? ptr + 1 : NULL); +} + +int file_readable(const char *filename) +{ + int status = (access(filename, R_OK) == 0); + + DEBUG((DBG_FILES, "file_redable(%s) -> %s\n", + filename, status ? "Yes" : "No")); + return status; +} + +int file_exists(const char *filename) +{ + int status = (access(filename, F_OK) == 0); + + DEBUG((DBG_FILES, "file_exists(%s) -> %s\n", + filename, status ? "Yes" : "No")); + return status; +} + diff --git a/backend/dvi/mdvi-lib/font.c b/backend/dvi/mdvi-lib/font.c new file mode 100644 index 00000000..2f655df0 --- /dev/null +++ b/backend/dvi/mdvi-lib/font.c @@ -0,0 +1,519 @@ +/* + * Copyright (C) 2000, Matias Atria + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#include <config.h> +#include <stdlib.h> + +#include "mdvi.h" +#include "private.h" + +static ListHead fontlist; + +extern char *_mdvi_fallback_font; + +extern void vf_free_macros(DviFont *); + +#define finfo search.info +#define TYPENAME(font) \ + ((font)->finfo ? (font)->finfo->name : "none") + +int font_reopen(DviFont *font) +{ + if(font->in) + fseek(font->in, (long)0, SEEK_SET); + else if((font->in = fopen(font->filename, "rb")) == NULL) { + DEBUG((DBG_FILES, "reopen(%s) -> Error\n", font->filename)); + return -1; + } + DEBUG((DBG_FILES, "reopen(%s) -> Ok.\n", font->filename)); + return 0; +} + +/* used from context: params and device */ +static int load_font_file(DviParams *params, DviFont *font) +{ + int status; + + if(SEARCH_DONE(font->search)) + return -1; + if(font->in == NULL && font_reopen(font) < 0) + return -1; + DEBUG((DBG_FONTS, "%s: loading %s font from `%s'\n", + font->fontname, + font->finfo->name, font->filename)); + do { + status = font->finfo->load(params, font); + } while(status < 0 && mdvi_font_retry(params, font) == 0); + if(status < 0) + return -1; + if(font->in) { + fclose(font->in); + font->in = NULL; + } + DEBUG((DBG_FONTS, "reload_font(%s) -> %s\n", + font->fontname, status < 0 ? "Error" : "Ok")); + return 0; +} + +void font_drop_one(DviFontRef *ref) +{ + DviFont *font; + + font = ref->ref; + mdvi_free(ref); + /* drop all children */ + for(ref = font->subfonts; ref; ref = ref->next) { + /* just adjust the reference counts */ + ref->ref->links--; + } + if(--font->links == 0) { + /* + * this font doesn't have any more references, but + * we still keep it around in case a virtual font + * requests it. + */ + if(font->in) { + fclose(font->in); + font->in = NULL; + } + if(LIST(font) != fontlist.tail) { + /* move it to the end of the list */ + listh_remove(&fontlist, LIST(font)); + listh_append(&fontlist, LIST(font)); + } + } + DEBUG((DBG_FONTS, "%s: reference dropped, %d more left\n", + font->fontname, font->links)); +} + +void font_drop_chain(DviFontRef *head) +{ + DviFontRef *ptr; + + for(; (ptr = head); ) { + head = ptr->next; + font_drop_one(ptr); + } +} + +int font_free_unused(DviDevice *dev) +{ + DviFont *font, *next; + int count = 0; + + DEBUG((DBG_FONTS, "destroying unused fonts\n")); + for(font = (DviFont *)fontlist.head; font; font = next) { + DviFontRef *ref; + + next = font->next; + if(font->links) + continue; + count++; + DEBUG((DBG_FONTS, "removing unused %s font `%s'\n", + TYPENAME(font), font->fontname)); + listh_remove(&fontlist, LIST(font)); + if(font->in) + fclose(font->in); + /* get rid of subfonts (but can't use `drop_chain' here) */ + for(; (ref = font->subfonts); ) { + font->subfonts = ref->next; + mdvi_free(ref); + } + /* remove this font */ + font_reset_font_glyphs(dev, font, MDVI_FONTSEL_GLYPH); + /* let the font destroy its private data */ + if(font->finfo->freedata) + font->finfo->freedata(font); + /* destroy characters */ + if(font->chars) + mdvi_free(font->chars); + mdvi_free(font->fontname); + mdvi_free(font->filename); + mdvi_free(font); + } + DEBUG((DBG_FONTS, "%d unused fonts removed\n", count)); + return count; +} + +/* used from context: params and device */ +DviFontRef * +font_reference( + DviParams *params, /* rendering parameters */ + Int32 id, /* external id number */ + const char *name, /* font name */ + Int32 sum, /* checksum (from DVI of VF) */ + int hdpi, /* resolution */ + int vdpi, + Int32 scale) /* scaling factor (from DVI or VF) */ +{ + DviFont *font; + DviFontRef *ref; + DviFontRef *subfont_ref; + + /* see if there is a font with the same characteristics */ + for(font = (DviFont *)fontlist.head; font; font = font->next) { + if(strcmp(name, font->fontname) == 0 + && (!sum || !font->checksum || font->checksum == sum) + && font->hdpi == hdpi + && font->vdpi == vdpi + && font->scale == scale) + break; + } + /* try to load the font */ + if(font == NULL) { + font = mdvi_add_font(name, sum, hdpi, vdpi, scale); + if(font == NULL) + return NULL; + listh_append(&fontlist, LIST(font)); + } + if(!font->links && !font->chars && load_font_file(params, font) < 0) { + DEBUG((DBG_FONTS, "font_reference(%s) -> Error\n", name)); + return NULL; + } + ref = xalloc(DviFontRef); + ref->ref = font; + + font->links++; + for(subfont_ref = font->subfonts; subfont_ref; subfont_ref = subfont_ref->next) { + /* just adjust the reference counts */ + subfont_ref->ref->links++; + } + + ref->fontid = id; + + if(LIST(font) != fontlist.head) { + listh_remove(&fontlist, LIST(font)); + listh_prepend(&fontlist, LIST(font)); + } + + DEBUG((DBG_FONTS, "font_reference(%s) -> %d links\n", + font->fontname, font->links)); + return ref; +} + +void font_transform_glyph(DviOrientation orient, DviGlyph *g) +{ + BITMAP *map; + int x, y; + + map = (BITMAP *)g->data; + if(MDVI_GLYPH_ISEMPTY(map)) + map = NULL; + + /* put the glyph in the right orientation */ + switch(orient) { + case MDVI_ORIENT_TBLR: + break; + case MDVI_ORIENT_TBRL: + g->x = g->w - g->x; + if(map) bitmap_flip_horizontally(map); + break; + case MDVI_ORIENT_BTLR: + g->y = g->h - g->y; + if(map) bitmap_flip_vertically(map); + break; + case MDVI_ORIENT_BTRL: + g->x = g->w - g->x; + g->y = g->h - g->y; + if(map) bitmap_flip_diagonally(map); + break; + case MDVI_ORIENT_RP90: + if(map) bitmap_rotate_counter_clockwise(map); + y = g->y; + x = g->w - g->x; + g->x = y; + g->y = x; + SWAPINT(g->w, g->h); + break; + case MDVI_ORIENT_RM90: + if(map) bitmap_rotate_clockwise(map); + y = g->h - g->y; + x = g->x; + g->x = y; + g->y = x; + SWAPINT(g->w, g->h); + break; + case MDVI_ORIENT_IRP90: + if(map) bitmap_flip_rotate_counter_clockwise(map); + y = g->y; + x = g->x; + g->x = y; + g->y = x; + SWAPINT(g->w, g->h); + break; + case MDVI_ORIENT_IRM90: + if(map) bitmap_flip_rotate_clockwise(map); + y = g->h - g->y; + x = g->w - g->x; + g->x = y; + g->y = x; + SWAPINT(g->w, g->h); + break; + } +} + +static int load_one_glyph(DviContext *dvi, DviFont *font, int code) +{ + BITMAP *map; + DviFontChar *ch; + int status; + +#ifndef NODEBUG + ch = FONTCHAR(font, code); + DEBUG((DBG_GLYPHS, "loading glyph code %d in %s (at %u)\n", + code, font->fontname, ch->offset)); +#endif + if(font->finfo->getglyph == NULL) { + /* font type does not need to load glyphs (e.g. vf) */ + return 0; + } + + status = font->finfo->getglyph(&dvi->params, font, code); + if(status < 0) + return -1; + /* get the glyph again (font->chars may have changed) */ + ch = FONTCHAR(font, code); +#ifndef NODEBUG + map = (BITMAP *)ch->glyph.data; + if(DEBUGGING(BITMAP_DATA)) { + DEBUG((DBG_BITMAP_DATA, + "%s: new %s bitmap for character %d:\n", + font->fontname, TYPENAME(font), code)); + if(MDVI_GLYPH_ISEMPTY(map)) + DEBUG((DBG_BITMAP_DATA, "blank bitmap\n")); + else + bitmap_print(stderr, map); + } +#endif + /* check if we have to scale it */ + if(!font->finfo->scalable && font->hdpi != font->vdpi) { + int hs, vs, d; + + /* we scale it ourselves */ + d = Max(font->hdpi, font->vdpi); + hs = d / font->hdpi; + vs = d / font->vdpi; + if(ch->width && ch->height && (hs > 1 || vs > 1)) { + int h, v; + DviGlyph glyph; + + DEBUG((DBG_FONTS, + "%s: scaling glyph %d to resolution %dx%d\n", + font->fontname, code, font->hdpi, font->vdpi)); + h = dvi->params.hshrink; + v = dvi->params.vshrink; + d = dvi->params.density; + dvi->params.hshrink = hs; + dvi->params.vshrink = vs; + dvi->params.density = 50; + /* shrink it */ + font->finfo->shrink0(dvi, font, ch, &glyph); + /* restore parameters */ + dvi->params.hshrink = h; + dvi->params.vshrink = v; + dvi->params.density = d; + /* update glyph data */ + if(!MDVI_GLYPH_ISEMPTY(ch->glyph.data)) + bitmap_destroy((BITMAP *)ch->glyph.data); + ch->glyph.data = glyph.data; + ch->glyph.x = glyph.x; + ch->glyph.y = glyph.y; + ch->glyph.w = glyph.w; + ch->glyph.h = glyph.h; + } + + } + font_transform_glyph(dvi->params.orientation, &ch->glyph); + + return 0; +} + +DviFontChar *font_get_glyph(DviContext *dvi, DviFont *font, int code) +{ + DviFontChar *ch; + +again: + /* if we have not loaded the font yet, do so now */ + if(!font->chars && load_font_file(&dvi->params, font) < 0) + return NULL; + + /* get the unscaled glyph, maybe loading it from disk */ + ch = FONTCHAR(font, code); + if(!ch || !glyph_present(ch)) + return NULL; + if(!ch->loaded && load_one_glyph(dvi, font, code) == -1) { + if(font->chars == NULL) { + /* we need to try another font class */ + goto again; + } + return NULL; + } + /* yes, we have to do this again */ + ch = FONTCHAR(font, code); + + /* Got the glyph. If we also have the right scaled glyph, do no more */ + if(!ch->width || !ch->height || + font->finfo->getglyph == NULL || + (dvi->params.hshrink == 1 && dvi->params.vshrink == 1)) + return ch; + + /* If the glyph is empty, we just need to shrink the box */ + if(ch->missing || MDVI_GLYPH_ISEMPTY(ch->glyph.data)) { + if(MDVI_GLYPH_UNSET(ch->shrunk.data)) + mdvi_shrink_box(dvi, font, ch, &ch->shrunk); + return ch; + } else if(MDVI_ENABLED(dvi, MDVI_PARAM_ANTIALIASED)) { + if(ch->grey.data && + !MDVI_GLYPH_ISEMPTY(ch->grey.data) && + ch->fg == dvi->curr_fg && + ch->bg == dvi->curr_bg) + return ch; + if(ch->grey.data && + !MDVI_GLYPH_ISEMPTY(ch->grey.data)) { + if(dvi->device.free_image) + dvi->device.free_image(ch->grey.data); + ch->grey.data = NULL; + } + font->finfo->shrink1(dvi, font, ch, &ch->grey); + } else if(!ch->shrunk.data) + font->finfo->shrink0(dvi, font, ch, &ch->shrunk); + + return ch; +} + +void font_reset_one_glyph(DviDevice *dev, DviFontChar *ch, int what) +{ + if(!glyph_present(ch)) + return; + if(what & MDVI_FONTSEL_BITMAP) { + if(MDVI_GLYPH_NONEMPTY(ch->shrunk.data)) + bitmap_destroy((BITMAP *)ch->shrunk.data); + ch->shrunk.data = NULL; + } + if(what & MDVI_FONTSEL_GREY) { + if(MDVI_GLYPH_NONEMPTY(ch->grey.data)) { + if(dev->free_image) + dev->free_image(ch->grey.data); + } + ch->grey.data = NULL; + } + if(what & MDVI_FONTSEL_GLYPH) { + if(MDVI_GLYPH_NONEMPTY(ch->glyph.data)) + bitmap_destroy((BITMAP *)ch->glyph.data); + ch->glyph.data = NULL; + ch->loaded = 0; + } +} + +void font_reset_font_glyphs(DviDevice *dev, DviFont *font, int what) +{ + int i; + DviFontChar *ch; + + if(what & MDVI_FONTSEL_GLYPH) + what |= MDVI_FONTSEL_BITMAP|MDVI_FONTSEL_GREY; + if(font->subfonts) { + DviFontRef *ref; + + for(ref = font->subfonts; ref; ref = ref->next) + font_reset_font_glyphs(dev, ref->ref, what); + } + if(font->in) { + DEBUG((DBG_FILES, "close(%s)\n", font->filename)); + fclose(font->in); + font->in = NULL; + } + if(font->finfo->getglyph == NULL) + return; + DEBUG((DBG_FONTS, "resetting glyphs in font `%s'\n", font->fontname)); + for(ch = font->chars, i = font->loc; i <= font->hic; ch++, i++) { + if(glyph_present(ch)) + font_reset_one_glyph(dev, ch, what); + } + if((what & MDVI_FONTSEL_GLYPH) && font->finfo->reset) + font->finfo->reset(font); +} + +void font_reset_chain_glyphs(DviDevice *dev, DviFontRef *head, int what) +{ + DviFontRef *ref; + + for(ref = head; ref; ref = ref->next) + font_reset_font_glyphs(dev, ref->ref, what); +} + +static int compare_refs(const void *p1, const void *p2) +{ + return ((*(DviFontRef **)p1)->fontid - (*(DviFontRef **)p2)->fontid); +} + +void font_finish_definitions(DviContext *dvi) +{ + int count; + DviFontRef **map, *ref; + + /* first get rid of unused fonts */ + font_free_unused(&dvi->device); + + if(dvi->fonts == NULL) { + mdvi_warning(_("%s: no fonts defined\n"), dvi->filename); + return; + } + map = xnalloc(DviFontRef *, dvi->nfonts); + for(count = 0, ref = dvi->fonts; ref; ref = ref->next) + map[count++] = ref; + /* sort the array by font id */ + qsort(map, dvi->nfonts, sizeof(DviFontRef *), compare_refs); + dvi->fontmap = map; +} + +DviFontRef *font_find_flat(DviContext *dvi, Int32 id) +{ + DviFontRef *ref; + + for(ref = dvi->fonts; ref; ref = ref->next) + if(ref->fontid == id) + break; + return ref; +} + +DviFontRef *font_find_mapped(DviContext *dvi, Int32 id) +{ + int lo, hi, n; + DviFontRef **map; + + /* do a binary search */ + lo = 0; hi = dvi->nfonts; + map = dvi->fontmap; + while(lo < hi) { + int sign; + + n = (hi + lo) >> 1; + sign = (map[n]->fontid - id); + if(sign == 0) + break; + else if(sign < 0) + lo = n; + else + hi = n; + } + if(lo >= hi) + return NULL; + return map[n]; +} + diff --git a/backend/dvi/mdvi-lib/fontmap.c b/backend/dvi/mdvi-lib/fontmap.c new file mode 100644 index 00000000..c3c3a8d3 --- /dev/null +++ b/backend/dvi/mdvi-lib/fontmap.c @@ -0,0 +1,1174 @@ +/* encoding.c - functions to manipulate encodings and fontmaps */ +/* + * Copyright (C) 2000, Matias Atria + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#include <config.h> +#include <string.h> +#include <stdlib.h> +#include <stdio.h> +#include <errno.h> +#include <sys/stat.h> + +#include "mdvi.h" +#include "private.h" + +#include <kpathsea/expand.h> +#include <kpathsea/pathsearch.h> + +typedef struct _DviFontMap DviFontMap; + +struct _DviFontMap { + ListHead entries; + DviHashTable fonts; +}; + +typedef struct _PSFontMap { + struct _PSFontMap *next; + struct _PSFontMap *prev; + char *psname; + char *mapname; + char *fullname; +} PSFontMap; + +/* these variables control PS font maps */ +static char *pslibdir = NULL; /* path where we look for PS font maps */ +static char *psfontdir = NULL; /* PS font search path */ +static int psinitialized = 0; /* did we expand the path already? */ + +static ListHead psfonts = MDVI_EMPTY_LIST_HEAD; +static DviHashTable pstable = MDVI_EMPTY_HASH_TABLE; + +static ListHead fontmaps; +static DviHashTable maptable; +static int fontmaps_loaded = 0; + +#define MAP_HASH_SIZE 57 +#define ENC_HASH_SIZE 31 +#define PSMAP_HASH_SIZE 57 + +/* this hash table should be big enough to + * hold (ideally) one glyph name per bucket */ +#define ENCNAME_HASH_SIZE 131 /* most TeX fonts have 128 glyphs */ + +static ListHead encodings = MDVI_EMPTY_LIST_HEAD; +static DviEncoding *tex_text_encoding = NULL; +static DviEncoding *default_encoding = NULL; + +/* we keep two hash tables for encodings: one for their base files (e.g. + * "8r.enc"), and another one for their names (e.g. "TeXBase1Encoding") */ +static DviHashTable enctable = MDVI_EMPTY_HASH_TABLE; +static DviHashTable enctable_file = MDVI_EMPTY_HASH_TABLE; + +/* the TeX text encoding, from dvips */ +static char *tex_text_vector[256] = { + "Gamma", "Delta", "Theta", "Lambda", "Xi", "Pi", "Sigma", "Upsilon", + "Phi", "Psi", "Omega", "arrowup", "arrowdown", "quotesingle", + "exclamdown", "questiondown", "dotlessi", "dotlessj", "grave", + "acute", "caron", "breve", "macron", "ring", "cedilla", + "germandbls", "ae", "oe", "oslash", "AE", "OE", "Oslash", "space", + "exclam", "quotedbl", "numbersign", "dollar", "percent", + "ampersand", "quoteright", "parenleft", "parenright", "asterisk", + "plus", "comma", "hyphen", "period", "slash", "zero", "one", "two", + "three", "four", "five", "six", "seven", "eight", "nine", "colon", + "semicolon", "less", "equal", "greater", "question", "at", "A", "B", + "C", "D", "E", "F", "G", "H", "I", "J", "K", "L", "M", "N", "O", + "P", "Q", "R", "S", "T", "U", "V", "W", "X", "Y", "Z", + "bracketleft", "backslash", "bracketright", "circumflex", + "underscore", "quoteleft", "a", "b", "c", "d", "e", "f", "g", "h", + "i", "j", "k", "l", "m", "n", "o", "p", "q", "r", "s", "t", "u", + "v", "w", "x", "y", "z", "braceleft", "bar", "braceright", "tilde", + "dieresis", 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 +}; + +static void ps_init_default_paths __PROTO((void)); +static int mdvi_set_default_encoding __PROTO((const char *name)); +static int mdvi_init_fontmaps __PROTO((void)); + +/* + * What we do here is allocate one block large enough to hold the entire + * file (these files are small) minus the leading comments. This is much + * better than allocating up to 256 tiny strings per encoding vector. */ +static int read_encoding(DviEncoding *enc) +{ + FILE *in; + int curr; + char *line; + char *name; + char *next; + struct stat st; + + ASSERT(enc->private == NULL); + + in = fopen(enc->filename, "rb"); + if(in == NULL) { + DEBUG((DBG_FMAP, "%s: could not read `%s' (%s)\n", + enc->name, enc->filename, strerror(errno))); + return -1; + } + if(fstat(fileno(in), &st) < 0) { + /* should not happen */ + fclose(in); + return -1; + } + st.st_size -= enc->offset; + + /* this will be one big string */ + enc->private = (char *)malloc(st.st_size + 1); + /* setup the hash table */ + mdvi_hash_create(&enc->nametab, ENCNAME_HASH_SIZE); + /* setup the encoding vector */ + enc->vector = (char **)mdvi_malloc(256 * sizeof(char *)); + + /* jump to the beginning of the interesting part */ + fseek(in, enc->offset, SEEK_SET); + /* and read everything */ + if(fread(enc->private, st.st_size, 1, in) != 1) { + fclose(in); + mdvi_free(enc->private); + enc->private = NULL; + return -1; + } + /* we don't need this anymore */ + fclose(in); + curr = 0; + + next = name = NULL; + DEBUG((DBG_FMAP, "%s: reading encoding vector\n", enc->name)); + for(line = enc->private; *line && curr < 256; line = next) { + SKIPSP(line); + if(*line == ']') { + line++; SKIPSP(line); + if(STRNEQ(line, "def", 3)) + break; + } + name = getword(line, " \t\n", &next); + if(name == NULL) + break; + /* next > line */ + if(*name < ' ') + continue; + if(*name == '%') { + while(*next && *next != '\n') + next++; + if(*next) next++; /* skip \n */ + continue; + } + + /* got a name */ + if(*next) *next++ = 0; + + if(*name == '/') + name++; + enc->vector[curr] = name; + /* add it to the hash table */ + if(!STREQ(name, ".notdef")) { + mdvi_hash_add(&enc->nametab, MDVI_KEY(name), + Int2Ptr(curr + 1), MDVI_HASH_REPLACE); + } + curr++; + } + if(curr == 0) { + mdvi_hash_reset(&enc->nametab, 0); + mdvi_free(enc->private); + mdvi_free(enc); + return -1; + } + while(curr < 256) + enc->vector[curr++] = NULL; + return 0; +} + +static DviEncoding *find_encoding(const char *name) +{ + return (DviEncoding *)(encodings.count ? + mdvi_hash_lookup(&enctable, MDVI_KEY(name)) : NULL); +} + +static void destroy_encoding(DviEncoding *enc) +{ + if(enc == default_encoding) { + default_encoding = tex_text_encoding; + /* now we use reference counts again */ + mdvi_release_encoding(enc, 1); + } + if(enc != tex_text_encoding) { + mdvi_hash_reset(&enc->nametab, 0); + if(enc->private) { + mdvi_free(enc->private); + mdvi_free(enc->vector); + } + if(enc->name) + mdvi_free(enc->name); + if(enc->filename) + mdvi_free(enc->filename); + mdvi_free(enc); + } +} + +/* this is used for the `enctable_file' hash table */ +static void file_hash_free(DviHashKey key, void *data) +{ + mdvi_free(key); +} + +static DviEncoding *register_encoding(const char *basefile, int replace) +{ + DviEncoding *enc; + FILE *in; + char *filename; + char *name; + Dstring input; + char *line; + long offset; + + DEBUG((DBG_FMAP, "register_encoding(%s)\n", basefile)); + + if(encodings.count) { + enc = mdvi_hash_lookup(&enctable_file, MDVI_KEY(basefile)); + if(enc != NULL) { + DEBUG((DBG_FMAP, "%s: already there\n", basefile)); + return enc; /* no error */ + } + } + + /* try our own files first */ + filename = kpse_find_file(basefile, + kpse_program_text_format, 0); + + /* then try the system-wide ones */ + if(filename == NULL) + filename = kpse_find_file(basefile, + kpse_tex_ps_header_format, 0); + if(filename == NULL) + filename = kpse_find_file(basefile, + kpse_dvips_config_format, 0); + + /* finally try the given name */ + if(filename == NULL) + filename = mdvi_strdup(basefile); + + in = fopen(filename, "rb"); + if(in == NULL) { + mdvi_free(filename); + return NULL; + } + + /* just lookup the name of the encoding */ + name = NULL; + dstring_init(&input); + while((line = dgets(&input, in)) != NULL) { + if(STRNEQ(line, "Encoding=", 9)) { + name = getword(line + 9, " \t", &line); + if(*line) *line++ = 0; + break; + } else if(*line == '/') { + char *label = getword(line + 1, " \t", &line); + if(*line) { + *line++ = 0; + SKIPSP(line); + if(*line == '[') { + *line = 0; + name = label; + break; + } + } + } + } + offset = ftell(in); + fclose(in); + if(name == NULL || *name == 0) { + DEBUG((DBG_FMAP, + "%s: could not determine name of encoding\n", + basefile)); + mdvi_free(filename); + return NULL; + } + + /* check if the encoding is already there */ + enc = find_encoding(name); + if(enc == tex_text_encoding) { + /* A special case: if the vector we found is the static one, + * allow the user to override it with an external file */ + listh_remove(&encodings, LIST(enc)); + mdvi_hash_remove(&enctable, MDVI_KEY(enc->name)); + if(enc == default_encoding) + default_encoding = NULL; + } else if(enc) { + /* if the encoding is being used, refuse to remove it */ + if(enc->links) { + mdvi_free(filename); + dstring_reset(&input); + return NULL; + } + if(replace) { + mdvi_hash_remove(&enctable, MDVI_KEY(name)); + mdvi_hash_remove(&enctable_file, MDVI_KEY(basefile)); + listh_remove(&encodings, LIST(enc)); + if(enc == default_encoding) { + default_encoding = NULL; + mdvi_release_encoding(enc, 1); + } + DEBUG((DBG_FMAP, "%s: overriding encoding\n", name)); + destroy_encoding(enc); + } else { + mdvi_free(filename); + dstring_reset(&input); + return enc; /* no error */ + } + } + enc = xalloc(DviEncoding); + enc->name = mdvi_strdup(name); + enc->filename = filename; + enc->links = 0; + enc->offset = offset; + enc->private = NULL; + enc->vector = NULL; + mdvi_hash_init(&enc->nametab); + dstring_reset(&input); + if(default_encoding == NULL) + default_encoding = enc; + mdvi_hash_add(&enctable, MDVI_KEY(enc->name), + enc, MDVI_HASH_UNCHECKED); + mdvi_hash_add(&enctable_file, MDVI_KEY(mdvi_strdup(basefile)), + enc, MDVI_HASH_REPLACE); + listh_prepend(&encodings, LIST(enc)); + DEBUG((DBG_FMAP, "%s: encoding `%s' registered\n", + basefile, enc->name)); + return enc; +} + +DviEncoding *mdvi_request_encoding(const char *name) +{ + DviEncoding *enc = find_encoding(name); + + if(enc == NULL) { + DEBUG((DBG_FMAP, "%s: encoding not found, returning default `%s'\n", + name, default_encoding->name)); + return default_encoding; + } + /* we don't keep reference counts for this */ + if(enc == tex_text_encoding) + return enc; + if(!enc->private && read_encoding(enc) < 0) + return NULL; + enc->links++; + + /* if the hash table is empty, rebuild it */ + if(enc->nametab.nkeys == 0) { + int i; + + DEBUG((DBG_FMAP, "%s: rehashing\n", enc->name)); + for(i = 0; i < 256; i++) { + if(enc->vector[i] == NULL) + continue; + mdvi_hash_add(&enc->nametab, + MDVI_KEY(enc->vector[i]), + (DviHashKey)Int2Ptr(i), + MDVI_HASH_REPLACE); + } + } + return enc; +} + +void mdvi_release_encoding(DviEncoding *enc, int should_free) +{ + /* ignore our static encoding */ + if(enc == tex_text_encoding) + return; + if(!enc->links || --enc->links > 0 || !should_free) + return; + DEBUG((DBG_FMAP, "%s: resetting encoding vector\n", enc->name)); + mdvi_hash_reset(&enc->nametab, 1); /* we'll reuse it */ +} + +int mdvi_encode_glyph(DviEncoding *enc, const char *name) +{ + void *data; + + data = mdvi_hash_lookup(&enc->nametab, MDVI_KEY(name)); + if(data == NULL) + return -1; + /* we added +1 to the hashed index just to distinguish + * a failed lookup from a zero index. Adjust it now. */ + return (Ptr2Int(data) - 1); +} + +/**************** + * Fontmaps * + ****************/ + +static void parse_spec(DviFontMapEnt *ent, char *spec) +{ + char *arg, *command; + + /* this is a ridiculously simple parser, and recognizes only + * things of the form <argument> <command>. Of these, only + * command=SlantFont, ExtendFont and ReEncodeFont are handled */ + while(*spec) { + arg = getword(spec, " \t", &spec); + if(*spec) *spec++ = 0; + command = getword(spec, " \t", &spec); + if(*spec) *spec++ = 0; + if(!arg || !command) + continue; + if(STREQ(command, "SlantFont")) { + double x = 10000 * strtod(arg, 0); + + /* SFROUND evaluates arguments twice */ + ent->slant = SFROUND(x); + } else if(STREQ(command, "ExtendFont")) { + double x = 10000 * strtod(arg, 0); + + ent->extend = SFROUND(x); + } else if(STREQ(command, "ReEncodeFont")) { + if(ent->encoding) + mdvi_free(ent->encoding); + ent->encoding = mdvi_strdup(arg); + } + } +} + +#if 0 +static void print_ent(DviFontMapEnt *ent) +{ + printf("Entry for `%s':\n", ent->fontname); + printf(" PS name: %s\n", ent->psname ? ent->psname : "(none)"); + printf(" Encoding: %s\n", ent->encoding ? ent->encoding : "(default)"); + printf(" EncFile: %s\n", ent->encfile ? ent->encfile : "(none)"); + printf(" FontFile: %s\n", ent->fontfile ? ent->fontfile : "(same)"); + printf(" Extend: %ld\n", ent->extend); + printf(" Slant: %ld\n", ent->slant); +} +#endif + +DviFontMapEnt *mdvi_load_fontmap(const char *file) +{ + char *ptr; + FILE *in; + int lineno = 1; + Dstring input; + ListHead list; + DviFontMapEnt *ent; + DviEncoding *last_encoding; + char *last_encfile; + + ptr = kpse_find_file(file, kpse_program_text_format, 0); + if(ptr == NULL) + ptr = kpse_find_file(file, kpse_tex_ps_header_format, 0); + if(ptr == NULL) + ptr = kpse_find_file(file, kpse_dvips_config_format, 0); + if(ptr == NULL) + in = fopen(file, "rb"); + else { + in = fopen(ptr, "rb"); + mdvi_free(ptr); + } + if(in == NULL) + return NULL; + + ent = NULL; + listh_init(&list); + dstring_init(&input); + last_encoding = NULL; + last_encfile = NULL; + + while((ptr = dgets(&input, in)) != NULL) { + char *font_file; + char *tex_name; + char *ps_name; + char *vec_name; + int is_encoding; + DviEncoding *enc; + + lineno++; + SKIPSP(ptr); + + /* we skip what dvips does */ + if(*ptr <= ' ' || *ptr == '*' || *ptr == '#' || + *ptr == ';' || *ptr == '%') + continue; + + font_file = NULL; + tex_name = NULL; + ps_name = NULL; + vec_name = NULL; + is_encoding = 0; + + if(ent == NULL) { + ent = xalloc(DviFontMapEnt); + ent->encoding = NULL; + ent->slant = 0; + ent->extend = 0; + } + while(*ptr) { + char *hdr_name = NULL; + + while(*ptr && *ptr <= ' ') + ptr++; + if(*ptr == 0) + break; + if(*ptr == '"') { + char *str; + + str = getstring(ptr, " \t", &ptr); + if(*ptr) *ptr++ = 0; + parse_spec(ent, str); + continue; + } else if(*ptr == '<') { + ptr++; + if(*ptr == '<') + ptr++; + else if(*ptr == '[') { + is_encoding = 1; + ptr++; + } + SKIPSP(ptr); + hdr_name = ptr; + } else if(!tex_name) + tex_name = ptr; + else if(!ps_name) + ps_name = ptr; + else + hdr_name = ptr; + + /* get next word */ + getword(ptr, " \t", &ptr); + if(*ptr) *ptr++ = 0; + + if(hdr_name) { + const char *ext = file_extension(hdr_name); + + if(is_encoding || (ext && STRCEQ(ext, "enc"))) + vec_name = hdr_name; + else + font_file = hdr_name; + } + } + + if(tex_name == NULL) + continue; + ent->fontname = mdvi_strdup(tex_name); + ent->psname = ps_name ? mdvi_strdup(ps_name) : NULL; + ent->fontfile = font_file ? mdvi_strdup(font_file) : NULL; + ent->encfile = vec_name ? mdvi_strdup(vec_name) : NULL; + ent->fullfile = NULL; + enc = NULL; /* we don't have this yet */ + + /* if we have an encoding file, register it */ + if(ent->encfile) { + /* register_encoding is smart enough not to load the + * same file twice */ + if(!last_encfile || !STREQ(last_encfile, ent->encfile)) { + last_encfile = ent->encfile; + last_encoding = register_encoding(ent->encfile, 1); + } + enc = last_encoding; + } + if(ent->encfile && enc){ + if(ent->encoding && !STREQ(ent->encoding, enc->name)) { + mdvi_warning( + _("%s: %d: [%s] requested encoding `%s' does not match vector `%s'\n"), + file, lineno, ent->encfile, + ent->encoding, enc->name); + } else if(!ent->encoding) + ent->encoding = mdvi_strdup(enc->name); + } + + /* add it to the list */ + /*print_ent(ent);*/ + listh_append(&list, LIST(ent)); + ent = NULL; + } + dstring_reset(&input); + fclose(in); + + return (DviFontMapEnt *)list.head; +} + +static void free_ent(DviFontMapEnt *ent) +{ + ASSERT(ent->fontname != NULL); + mdvi_free(ent->fontname); + if(ent->psname) + mdvi_free(ent->psname); + if(ent->fontfile) + mdvi_free(ent->fontfile); + if(ent->encoding) + mdvi_free(ent->encoding); + if(ent->encfile) + mdvi_free(ent->encfile); + if(ent->fullfile) + mdvi_free(ent->fullfile); + mdvi_free(ent); +} + +void mdvi_install_fontmap(DviFontMapEnt *head) +{ + DviFontMapEnt *ent, *next; + + for(ent = head; ent; ent = next) { + /* add all the entries, overriding old ones */ + DviFontMapEnt *old; + + old = (DviFontMapEnt *) + mdvi_hash_remove(&maptable, MDVI_KEY(ent->fontname)); + if(old != NULL) { + DEBUG((DBG_FMAP, "%s: overriding fontmap entry\n", + old->fontname)); + listh_remove(&fontmaps, LIST(old)); + free_ent(old); + } + next = ent->next; + mdvi_hash_add(&maptable, MDVI_KEY(ent->fontname), + ent, MDVI_HASH_UNCHECKED); + listh_append(&fontmaps, LIST(ent)); + } +} + +static void init_static_encoding() +{ + DviEncoding *encoding; + int i; + + DEBUG((DBG_FMAP, "installing static TeX text encoding\n")); + encoding = xalloc(DviEncoding); + encoding->private = ""; + encoding->filename = ""; + encoding->name = "TeXTextEncoding"; + encoding->vector = tex_text_vector; + encoding->links = 1; + encoding->offset = 0; + mdvi_hash_create(&encoding->nametab, ENCNAME_HASH_SIZE); + for(i = 0; i < 256; i++) { + if(encoding->vector[i]) { + mdvi_hash_add(&encoding->nametab, + MDVI_KEY(encoding->vector[i]), + (DviHashKey)Int2Ptr(i), + MDVI_HASH_UNCHECKED); + } + } + ASSERT_VALUE(encodings.count, 0); + mdvi_hash_create(&enctable, ENC_HASH_SIZE); + mdvi_hash_create(&enctable_file, ENC_HASH_SIZE); + enctable_file.hash_free = file_hash_free; + mdvi_hash_add(&enctable, MDVI_KEY(encoding->name), + encoding, MDVI_HASH_UNCHECKED); + listh_prepend(&encodings, LIST(encoding)); + tex_text_encoding = encoding; + default_encoding = tex_text_encoding; +} + +static int mdvi_set_default_encoding(const char *name) +{ + DviEncoding *enc, *old; + + enc = find_encoding(name); + if(enc == NULL) + return -1; + if(enc == default_encoding) + return 0; + /* this will read it from file if necessary, + * but it can fail if the file is corrupted */ + enc = mdvi_request_encoding(name); + if(enc == NULL) + return -1; + old = default_encoding; + default_encoding = enc; + if(old != tex_text_encoding) + mdvi_release_encoding(old, 1); + return 0; +} + +static int mdvi_init_fontmaps(void) +{ + char *file; + char *line; + FILE *in; + Dstring input; + int count = 0; + char *config; + + if(fontmaps_loaded) + return 0; + /* we will only try this once */ + fontmaps_loaded = 1; + + DEBUG((DBG_FMAP, "reading fontmaps\n")); + + /* make sure the static encoding is there */ + init_static_encoding(); + + /* create the fontmap hash table */ + mdvi_hash_create(&maptable, MAP_HASH_SIZE); + + /* get the name of our configuration file */ + config = kpse_cnf_get("mdvi-config"); + if(config == NULL) + config = MDVI_DEFAULT_CONFIG; + /* let's ask kpathsea for the file first */ + file = kpse_find_file(config, kpse_program_text_format, 0); + if(file == NULL) + in = fopen(config, "rb"); + else { + in = fopen(file, "rb"); + mdvi_free(file); + } + if(in == NULL) + return -1; + dstring_init(&input); + while((line = dgets(&input, in)) != NULL) { + char *arg; + + SKIPSP(line); + if(*line < ' ' || *line == '#' || *line == '%') + continue; + if(STRNEQ(line, "fontmap", 7)) { + DviFontMapEnt *ent; + + arg = getstring(line + 7, " \t", &line); *line = 0; + DEBUG((DBG_FMAP, "%s: loading fontmap\n", arg)); + ent = mdvi_load_fontmap(arg); + if(ent == NULL) + mdvi_warning(_("%s: could not load fontmap\n"), arg); + else { + DEBUG((DBG_FMAP, + "%s: installing fontmap\n", arg)); + mdvi_install_fontmap(ent); + count++; + } + } else if(STRNEQ(line, "encoding", 8)) { + arg = getstring(line + 8, " \t", &line); *line = 0; + if(arg && *arg) + register_encoding(arg, 1); + } else if(STRNEQ(line, "default-encoding", 16)) { + arg = getstring(line + 16, " \t", &line); *line = 0; + if(mdvi_set_default_encoding(arg) < 0) + mdvi_warning(_("%s: could not set as default encoding\n"), + arg); + } else if(STRNEQ(line, "psfontpath", 10)) { + arg = getstring(line + 11, " \t", &line); *line = 0; + if(!psinitialized) + ps_init_default_paths(); + if(psfontdir) + mdvi_free(psfontdir); + psfontdir = kpse_path_expand(arg); + } else if(STRNEQ(line, "pslibpath", 9)) { + arg = getstring(line + 10, " \t", &line); *line = 0; + if(!psinitialized) + ps_init_default_paths(); + if(pslibdir) + mdvi_free(pslibdir); + pslibdir = kpse_path_expand(arg); + } else if(STRNEQ(line, "psfontmap", 9)) { + arg = getstring(line + 9, " \t", &line); *line = 0; + if(mdvi_ps_read_fontmap(arg) < 0) + mdvi_warning("%s: %s: could not read PS fontmap\n", + config, arg); + } + } + fclose(in); + dstring_reset(&input); + fontmaps_loaded = 1; + DEBUG((DBG_FMAP, "%d files installed, %d fontmaps\n", + count, fontmaps.count)); + return count; +} + +int mdvi_query_fontmap(DviFontMapInfo *info, const char *fontname) +{ + DviFontMapEnt *ent; + + if(!fontmaps_loaded && mdvi_init_fontmaps() < 0) + return -1; + ent = (DviFontMapEnt *)mdvi_hash_lookup(&maptable, MDVI_KEY(fontname)); + + if(ent == NULL) + return -1; + info->psname = ent->psname; + info->encoding = ent->encoding; + info->fontfile = ent->fontfile; + info->extend = ent->extend; + info->slant = ent->slant; + info->fullfile = ent->fullfile; + + return 0; +} + +int mdvi_add_fontmap_file(const char *name, const char *fullpath) +{ + DviFontMapEnt *ent; + + if(!fontmaps_loaded && mdvi_init_fontmaps() < 0) + return -1; + ent = (DviFontMapEnt *)mdvi_hash_lookup(&maptable, MDVI_KEY(name)); + if(ent == NULL) + return -1; + if(ent->fullfile) + mdvi_free(ent->fullfile); + ent->fullfile = mdvi_strdup(fullpath); + return 0; +} + + +void mdvi_flush_encodings(void) +{ + DviEncoding *enc; + + if(enctable.nbucks == 0) + return; + + DEBUG((DBG_FMAP, "flushing %d encodings\n", encodings.count)); + /* asked to remove all encodings */ + for(; (enc = (DviEncoding *)encodings.head); ) { + encodings.head = LIST(enc->next); + if((enc != tex_text_encoding && enc->links) || enc->links > 1) { + mdvi_warning(_("encoding vector `%s' is in use\n"), + enc->name); + } + destroy_encoding(enc); + } + /* destroy the static encoding */ + if(tex_text_encoding->nametab.buckets) + mdvi_hash_reset(&tex_text_encoding->nametab, 0); + mdvi_hash_reset(&enctable, 0); + mdvi_hash_reset(&enctable_file, 0); +} + +void mdvi_flush_fontmaps(void) +{ + DviFontMapEnt *ent; + + if(!fontmaps_loaded) + return; + + DEBUG((DBG_FMAP, "flushing %d fontmaps\n", fontmaps.count)); + for(; (ent = (DviFontMapEnt *)fontmaps.head); ) { + fontmaps.head = LIST(ent->next); + free_ent(ent); + } + mdvi_hash_reset(&maptable, 0); + fontmaps_loaded = 0; +} + +/* reading of PS fontmaps */ + +void ps_init_default_paths(void) +{ + char *kppath; + char *kfpath; + + ASSERT(psinitialized == 0); + + kppath = getenv("GS_LIB"); + kfpath = getenv("GS_FONTPATH"); + + if(kppath != NULL) + pslibdir = kpse_path_expand(kppath); + if(kfpath != NULL) + psfontdir = kpse_path_expand(kfpath); + + listh_init(&psfonts); + mdvi_hash_create(&pstable, PSMAP_HASH_SIZE); + psinitialized = 1; +} + +int mdvi_ps_read_fontmap(const char *name) +{ + char *fullname; + FILE *in; + Dstring dstr; + char *line; + int count = 0; + + if(!psinitialized) + ps_init_default_paths(); + if(pslibdir) + fullname = kpse_path_search(pslibdir, name, 1); + else + fullname = (char *)name; + in = fopen(fullname, "rb"); + if(in == NULL) { + if(fullname != name) + mdvi_free(fullname); + return -1; + } + dstring_init(&dstr); + + while((line = dgets(&dstr, in)) != NULL) { + char *name; + char *mapname; + const char *ext; + PSFontMap *ps; + + SKIPSP(line); + /* we're looking for lines of the form + * /FONT-NAME (fontfile) + * /FONT-NAME /FONT-ALIAS + */ + if(*line != '/') + continue; + name = getword(line + 1, " \t", &line); + if(*line) *line++ = 0; + mapname = getword(line, " \t", &line); + if(*line) *line++ = 0; + + if(!name || !mapname || !*name) + continue; + if(*mapname == '(') { + char *end; + + mapname++; + for(end = mapname; *end && *end != ')'; end++); + *end = 0; + } + if(!*mapname) + continue; + /* dont add `.gsf' fonts, which require a full blown + * PostScript interpreter */ + ext = file_extension(mapname); + if(ext && STREQ(ext, "gsf")) { + DEBUG((DBG_FMAP, "(ps) %s: font `%s' ignored\n", + name, mapname)); + continue; + } + ps = (PSFontMap *)mdvi_hash_lookup(&pstable, MDVI_KEY(name)); + if(ps != NULL) { + if(STREQ(ps->mapname, mapname)) + continue; + DEBUG((DBG_FMAP, + "(ps) replacing font `%s' (%s) by `%s'\n", + name, ps->mapname, mapname)); + mdvi_free(ps->mapname); + ps->mapname = mdvi_strdup(mapname); + if(ps->fullname) { + mdvi_free(ps->fullname); + ps->fullname = NULL; + } + } else { + DEBUG((DBG_FMAP, "(ps) adding font `%s' as `%s'\n", + name, mapname)); + ps = xalloc(PSFontMap); + ps->psname = mdvi_strdup(name); + ps->mapname = mdvi_strdup(mapname); + ps->fullname = NULL; + listh_append(&psfonts, LIST(ps)); + mdvi_hash_add(&pstable, MDVI_KEY(ps->psname), + ps, MDVI_HASH_UNCHECKED); + count++; + } + } + fclose(in); + dstring_reset(&dstr); + + DEBUG((DBG_FMAP, "(ps) %s: %d PostScript fonts registered\n", + fullname, count)); + return 0; +} + +void mdvi_ps_flush_fonts(void) +{ + PSFontMap *map; + + if(!psinitialized) + return; + DEBUG((DBG_FMAP, "(ps) flushing PS font map (%d) entries\n", + psfonts.count)); + mdvi_hash_reset(&pstable, 0); + for(; (map = (PSFontMap *)psfonts.head); ) { + psfonts.head = LIST(map->next); + mdvi_free(map->psname); + mdvi_free(map->mapname); + if(map->fullname) + mdvi_free(map->fullname); + mdvi_free(map); + } + listh_init(&psfonts); + if(pslibdir) { + mdvi_free(pslibdir); + pslibdir = NULL; + } + if(psfontdir) { + mdvi_free(psfontdir); + psfontdir = NULL; + } + psinitialized = 0; +} + +char *mdvi_ps_find_font(const char *psname) +{ + PSFontMap *map, *smap; + char *filename; + int recursion_limit = 32; + + DEBUG((DBG_FMAP, "(ps) resolving PS font `%s'\n", psname)); + if(!psinitialized) + return NULL; + map = (PSFontMap *)mdvi_hash_lookup(&pstable, MDVI_KEY(psname)); + if(map == NULL) + return NULL; + if(map->fullname) + return mdvi_strdup(map->fullname); + + /* is it an alias? */ + smap = map; + while(recursion_limit-- > 0 && smap && *smap->mapname == '/') + smap = (PSFontMap *)mdvi_hash_lookup(&pstable, + MDVI_KEY(smap->mapname + 1)); + if(smap == NULL) { + if(recursion_limit == 0) + DEBUG((DBG_FMAP, + "(ps) %s: possible loop in PS font map\n", + psname)); + return NULL; + } + + if(psfontdir) + filename = kpse_path_search(psfontdir, smap->mapname, 1); + else if(file_exists(map->mapname)) + filename = mdvi_strdup(map->mapname); + else + filename = NULL; + if(filename) + map->fullname = mdvi_strdup(filename); + + return filename; +} + +/* + * To get metric info for a font, we proceed as follows: + * - We try to find NAME.<tfm,ofm,afm>. + * - We query the fontmap for NAME. + * - We get back a PSNAME, and use to find the file in the PS font map. + * - We get the PSFONT file name, replace its extension by "afm" and + * lookup the file in GS's font search path. + * - We finally read the data, transform it as specified in our font map, + * and return it to the caller. The new data is left in the font metrics + * cache, so the next time it will be found at the first step (when we look + * up NAME.afm). + * + * The name `_ps_' in this function is not meant to imply that it can be + * used for Type1 fonts only. It should be usable for TrueType fonts as well. + * + * The returned metric info is subjected to the same caching mechanism as + * all the other metric data, as returned by get_font_metrics(). One should + * not modify the returned data at all, and it should be disposed with + * free_font_metrics(). + */ +TFMInfo *mdvi_ps_get_metrics(const char *fontname) +{ + TFMInfo *info; + DviFontMapInfo map; + char buffer[64]; /* to avoid mallocs */ + char *psfont; + char *basefile; + char *afmfile; + char *ext; + int baselen; + int nc; + TFMChar *ch; + double efactor; + double sfactor; + + DEBUG((DBG_FMAP, "(ps) %s: looking for metric data\n", fontname)); + info = get_font_metrics(fontname, DviFontAny, NULL); + if(info != NULL) + return info; + + /* query the fontmap */ + if(mdvi_query_fontmap(&map, fontname) < 0 || !map.psname) + return NULL; + + /* get the PS font */ + psfont = mdvi_ps_find_font(map.psname); + if(psfont == NULL) + return NULL; + DEBUG((DBG_FMAP, "(ps) %s: found as PS font `%s'\n", + fontname, psfont)); + /* replace its extension */ + basefile = strrchr(psfont, '/'); + if(basefile == NULL) + basefile = psfont; + baselen = strlen(basefile); + ext = strrchr(basefile, '.'); + if(ext != NULL) + *ext = 0; + if(baselen + 4 < 64) + afmfile = &buffer[0]; + else + afmfile = mdvi_malloc(baselen + 5); + strcpy(afmfile, basefile); + strcpy(afmfile + baselen, ".afm"); + /* we don't need this anymore */ + mdvi_free(psfont); + DEBUG((DBG_FMAP, "(ps) %s: looking for `%s'\n", + fontname, afmfile)); + /* lookup the file */ + psfont = kpse_path_search(psfontdir, afmfile, 1); + /* don't need this anymore */ + if(afmfile != &buffer[0]) + mdvi_free(afmfile); + if(psfont != NULL) { + info = get_font_metrics(fontname, DviFontAFM, psfont); + mdvi_free(psfont); + } else + info = NULL; + if(info == NULL || (!map.extend && !map.slant)) + return info; + + /* + * transform the data as prescribed -- keep in mind that `info' + * points to CACHED data, so we're modifying the metric cache + * in place. + */ + +#define DROUND(x) ((x) >= 0 ? floor((x) + 0.5) : ceil((x) - 0.5)) +#define TRANSFORM(x,y) DROUND(efactor * (x) + sfactor * (y)) + + efactor = (double)map.extend / 10000.0; + sfactor = (double)map.slant / 10000.0; + DEBUG((DBG_FMAP, "(ps) %s: applying extend=%f, slant=%f\n", + efactor, sfactor)); + + nc = info->hic - info->loc + 1; + for(ch = info->chars; ch < info->chars + nc; ch++) { + /* the AFM bounding box is: + * wx = ch->advance + * llx = ch->left + * lly = -ch->depth + * urx = ch->right + * ury = ch->height + * what we do here is transform wx, llx, and urx by + * newX = efactor * oldX + sfactor * oldY + * where for `wx' oldY = 0. Also, these numbers are all in + * TFM units (i.e. TFM's fix-words, which is just the actual + * number times 2^20, no need to do anything to it). + */ + if(ch->present) { + ch->advance = TRANSFORM(ch->advance, 0); + ch->left = TRANSFORM(ch->left, -ch->depth); + ch->right = TRANSFORM(ch->right, ch->height); + } + } + + return info; +} diff --git a/backend/dvi/mdvi-lib/fontmap.h b/backend/dvi/mdvi-lib/fontmap.h new file mode 100644 index 00000000..bb5a944d --- /dev/null +++ b/backend/dvi/mdvi-lib/fontmap.h @@ -0,0 +1,82 @@ +/* + * Copyright (C) 2000, Matias Atria + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +#ifndef _MDVI_FONTMAP_H +#define _MDVI_FONTMAP_H 1 + +typedef struct _DviFontMapEnt DviFontMapEnt; +typedef struct _DviEncoding DviEncoding; + +typedef struct { + const char *psname; + const char *encoding; + const char *fontfile; + const char *fullfile; + const char *fmfile; + int fmtype; + long extend; + long slant; +} DviFontMapInfo; + +struct _DviEncoding { + DviEncoding *next; + DviEncoding *prev; + char *private; + char *filename; + char *name; + char **vector; /* table with exactly 256 strings */ + int links; + long offset; + DviHashTable nametab; +}; + +struct _DviFontMapEnt { + DviFontMapEnt *next; + DviFontMapEnt *prev; + char *private; + char *fontname; + char *psname; + char *encoding; + char *encfile; + char *fontfile; + char *fullfile; + long extend; + long slant; +}; + +#define MDVI_FMAP_SLANT(x) ((double)(x)->slant / 10000.0) +#define MDVI_FMAP_EXTEND(x) ((double)(x)->extend / 10000.0) + +extern DviEncoding *mdvi_request_encoding __PROTO((const char *)); +extern void mdvi_release_encoding __PROTO((DviEncoding *, int)); +extern int mdvi_encode_glyph __PROTO((DviEncoding *, const char *)); +extern DviFontMapEnt *mdvi_load_fontmap __PROTO((const char *)); +extern void mdvi_install_fontmap __PROTO((DviFontMapEnt *)); +extern int mdvi_load_fontmaps __PROTO((void)); +extern int mdvi_query_fontmap __PROTO((DviFontMapInfo *, const char *)); +extern void mdvi_flush_encodings __PROTO((void)); +extern void mdvi_flush_fontmaps __PROTO((void)); + +extern int mdvi_add_fontmap_file __PROTO((const char *, const char *)); + +/* PS font maps */ +extern int mdvi_ps_read_fontmap __PROTO((const char *)); +extern char *mdvi_ps_find_font __PROTO((const char *)); +extern TFMInfo *mdvi_ps_get_metrics __PROTO((const char *)); +extern void mdvi_ps_flush_fonts __PROTO((void)); + +#endif /* _MDVI_FONTMAP_H */ diff --git a/backend/dvi/mdvi-lib/fontsrch.c b/backend/dvi/mdvi-lib/fontsrch.c new file mode 100644 index 00000000..8ebaa5ca --- /dev/null +++ b/backend/dvi/mdvi-lib/fontsrch.c @@ -0,0 +1,371 @@ +/* fontsearch.c -- implements the font lookup mechanism in MDVI */ +/* + * Copyright (C) 2000, Matias Atria + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +/* + * How this works: + * Fonts are divided into MAX_CLASS priority classes. The first + * MAX_CLASS-1 ones correspond to `real' fonts (pk, gf, vf, type1, truetype, + * etc). The last one corresponds to `metric' fonts that are used as a last + * resort (tfm, afm, ofm, ...). When a font is looked up, it is tried in a + * `high' priority class (0 being the highest priority). The priority is + * lowered until it reaches MAX_CLASS-1. Then the whole thing is repeated + * for the fallback font. When the search reaches MAX_CLASS-1, we lookup the + * original font, and then the fallback font. The search can be done + * incrementally, with several calls to mdvi_lookup_font(). If this function + * is called again to continue a search, the function assumes the previous + * font it returned was not valid, and it goes on to the next step. + * + * Reason for this: + * Some font types are quite expensive to load (e.g. Type1), so loading + * them is deferred until the last possible moment. This means that a font that + * was supposed to exist may have to be discarded. Until now, MDVI had no ability to + * "resume" a search, so in this case it would have produced an error, regardless + * of whether the offending font existed in other formats. + * Also, given the large number of font types supported by MDVI, some mechanism + * was necessary to bring some order into the chaos. + * + * This mechanism fixes these two problems. For the first one, a search can + * be "resumed" and all the font formats tried for the missing font, and + * again for the fallback font (see above). As for the second, the + * hierarchical division in classes gives a lot of flexibility in how the + * fonts are configured. + */ + +#include <config.h> +#include "mdvi.h" + +#define HAVE_PROTOTYPES 1 +#include <kpathsea/tex-file.h> +#include <kpathsea/tex-glyph.h> + +struct _DviFontClass { + DviFontClass *next; + DviFontClass *prev; + DviFontInfo info; + int links; + int id; +}; + +char *_mdvi_fallback_font = MDVI_FALLBACK_FONT; + +/* this leaves classes 0 and 1 for `real' fonts */ +#define MAX_CLASS 3 +static ListHead font_classes[MAX_CLASS]; +static int initialized = 0; + +static void init_font_classes(void) +{ + int i; + + for(i = 0; i < MAX_CLASS; i++) + listh_init(&font_classes[i]); + initialized = 1; +} + +int mdvi_get_font_classes(void) +{ + return (MAX_CLASS - 2); +} + +char **mdvi_list_font_class(int klass) +{ + char **list; + int i, n; + DviFontClass *fc; + + if(klass == -1) + klass = MAX_CLASS-1; + if(klass < 0 || klass >= MAX_CLASS) + return NULL; + n = font_classes[klass].count; + list = xnalloc(char *, n + 1); + fc = (DviFontClass *)font_classes[klass].head; + for(i = 0; i < n; fc = fc->next, i++) { + list[i] = mdvi_strdup(fc->info.name); + } + list[i] = NULL; + return list; +} + +int mdvi_register_font_type(DviFontInfo *info, int klass) +{ + DviFontClass *fc; + + if(klass == -1) + klass = MAX_CLASS-1; + if(klass < 0 || klass >= MAX_CLASS) + return -1; + if(!initialized) + init_font_classes(); + fc = xalloc(struct _DviFontClass); + fc->links = 0; + fc->id = klass; + fc->info.name = mdvi_strdup(info->name); + fc->info.scalable = info->scalable; + fc->info.load = info->load; + fc->info.getglyph = info->getglyph; + fc->info.shrink0 = info->shrink0; + fc->info.shrink1 = info->shrink1; + fc->info.freedata = info->freedata; + fc->info.reset = info->reset; + fc->info.lookup = info->lookup; + fc->info.kpse_type = info->kpse_type; + listh_append(&font_classes[klass], LIST(fc)); + return 0; +} + +int mdvi_unregister_font_type(const char *name, int klass) +{ + DviFontClass *fc; + int k; + + if(klass == -1) + klass = MAX_CLASS - 1; + + if(klass >= 0 && klass < MAX_CLASS) { + k = klass; + LIST_FOREACH(fc, DviFontClass, &font_classes[k]) { + if(STREQ(fc->info.name, name)) + break; + } + } else if(klass < 0) { + for(k = 0; k < MAX_CLASS; k++) { + LIST_FOREACH(fc, DviFontClass, &font_classes[k]) { + if(STREQ(fc->info.name, name)) + break; + } + if(fc) break; + } + } else + return -1; + + if(fc == NULL || fc->links) + return -1; + /* remove it */ + listh_remove(&font_classes[k], LIST(fc)); + + /* and destroy it */ + mdvi_free(fc->info.name); + mdvi_free(fc); + return 0; +} + +static char *lookup_font(DviFontClass *ptr, const char *name, Ushort *h, Ushort *v) +{ + char *filename; + + /* + * If the font type registered a function to do the lookup, use that. + * Otherwise we use kpathsea. + */ + if(ptr->info.lookup) + filename = ptr->info.lookup(name, h, v); + else if(ptr->info.kpse_type <= kpse_any_glyph_format) { + kpse_glyph_file_type type; + + filename = kpse_find_glyph(name, Max(*h, *v), + ptr->info.kpse_type, &type); + /* if kpathsea returned a fallback font, reject it */ + if(filename && type.source == kpse_glyph_source_fallback) { + mdvi_free(filename); + filename = NULL; + } else if(filename) + *h = *v = type.dpi; + } else + filename = kpse_find_file(name, ptr->info.kpse_type, 1); + return filename; +} + +/* + * Class MAX_CLASS-1 is special: it consists of `metric' fonts that should + * be tried as a last resort + */ +char *mdvi_lookup_font(DviFontSearch *search) +{ + int kid; + int k; + DviFontClass *ptr; + DviFontClass *last; + char *filename = NULL; + const char *name; + Ushort hdpi, vdpi; + + if(search->id < 0) + return NULL; + + if(search->curr == NULL) { + /* this is the initial search */ + name = search->wanted_name; + hdpi = search->hdpi; + vdpi = search->vdpi; + kid = 0; + last = NULL; + } else { + name = search->actual_name; + hdpi = search->actual_hdpi; + vdpi = search->actual_vdpi; + kid = search->id; + last = search->curr; + } + + ptr = NULL; +again: + /* try all classes except MAX_CLASS-1 */ + for(k = kid; !filename && k < MAX_CLASS-1; k++) { + if(last == NULL) + ptr = (DviFontClass *)font_classes[k].head; + else + ptr = last->next; + while(ptr) { + DEBUG((DBG_FONTS, "%d: trying `%s' at (%d,%d)dpi as `%s'\n", + k, name, hdpi, vdpi, ptr->info.name)); + /* lookup the font in this class */ + filename = lookup_font(ptr, name, &hdpi, &vdpi); + if(filename) + break; + ptr = ptr->next; + } + last = NULL; + } + if(filename != NULL) { + search->id = k-1; + search->curr = ptr; + search->actual_name = name; + search->actual_hdpi = hdpi; + search->actual_vdpi = vdpi; + search->info = &ptr->info; + ptr->links++; + return filename; + } + + if(kid < MAX_CLASS - 1 && !STREQ(name, _mdvi_fallback_font)) { + mdvi_warning("font `%s' at %dx%d not found, trying `%s' instead\n", + name, hdpi, vdpi, _mdvi_fallback_font); + name = _mdvi_fallback_font; + kid = 0; + goto again; + } + + /* we tried the fallback font, and all the `real' classes. Let's + * try the `metric' class now */ + name = search->wanted_name; + hdpi = search->hdpi; + vdpi = search->vdpi; + if(kid == MAX_CLASS-1) { + /* we were looking into this class from the beginning */ + if(last == NULL) { + /* no more fonts to try */ + return NULL; + } + ptr = last->next; + } else { + mdvi_warning("font `%s' not found, trying metric files instead\n", + name); + ptr = (DviFontClass *)font_classes[MAX_CLASS-1].head; + } + +metrics: + while(ptr) { + DEBUG((DBG_FONTS, "metric: trying `%s' at (%d,%d)dpi as `%s'\n", + name, hdpi, vdpi, ptr->info.name)); + filename = lookup_font(ptr, name, &hdpi, &vdpi); + if(filename) + break; + ptr = ptr->next; + } + if(filename != NULL) { + if(STREQ(name, _mdvi_fallback_font)) + search->id = MAX_CLASS; + else + search->id = MAX_CLASS - 1; + search->curr = ptr; + search->actual_name = name; + search->actual_hdpi = hdpi; + search->actual_vdpi = vdpi; + search->info = &ptr->info; + ptr->links++; + return filename; + } + if(!STREQ(name, _mdvi_fallback_font)) { + mdvi_warning("metric file for `%s' not found, trying `%s' instead\n", + name, _mdvi_fallback_font); + name = _mdvi_fallback_font; + ptr = (DviFontClass *)font_classes[MAX_CLASS-1].head; + goto metrics; + } + + search->id = -1; + search->actual_name = NULL; + + /* tough luck, nothing found */ + return NULL; +} + +/* called by `font_reference' to do the initial lookup */ +DviFont *mdvi_add_font(const char *name, Int32 sum, + int hdpi, int vdpi, Int32 scale) +{ + DviFont *font; + + font = xalloc(DviFont); + font->fontname = mdvi_strdup(name); + SEARCH_INIT(font->search, font->fontname, hdpi, vdpi); + font->filename = mdvi_lookup_font(&font->search); + if(font->filename == NULL) { + /* this answer is final */ + mdvi_free(font->fontname); + mdvi_free(font); + return NULL; + } + font->hdpi = font->search.actual_hdpi; + font->vdpi = font->search.actual_vdpi; + font->scale = scale; + font->design = 0; + font->checksum = sum; + font->type = 0; + font->links = 0; + font->loc = 0; + font->hic = 0; + font->in = NULL; + font->chars = NULL; + font->subfonts = NULL; + + return font; +} + +int mdvi_font_retry(DviParams *params, DviFont *font) +{ + /* try the search again */ + char *filename; + + ASSERT(font->search.curr != NULL); + /* we won't be using this class anymore */ + font->search.curr->links--; + + filename = mdvi_lookup_font(&font->search); + if(filename == NULL) + return -1; + mdvi_free(font->filename); + font->filename = filename; + /* copy the new information */ + font->hdpi = font->search.actual_hdpi; + font->vdpi = font->search.actual_vdpi; + + return 0; +} diff --git a/backend/dvi/mdvi-lib/gf.c b/backend/dvi/mdvi-lib/gf.c new file mode 100644 index 00000000..00607ff6 --- /dev/null +++ b/backend/dvi/mdvi-lib/gf.c @@ -0,0 +1,395 @@ +/* gf.c - GF font support */ +/* + * Copyright (C) 2000, Matias Atria + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +/* functions to read GF fonts */ + +#include <config.h> +#include <string.h> +#include "common.h" +#include "mdvi.h" +#include "private.h" + +/* opcodes */ + +#define GF_PAINT0 0 +#define GF_PAINT1 64 +#define GF_PAINT2 65 +#define GF_PAINT3 66 +#define GF_BOC 67 +#define GF_BOC1 68 +#define GF_EOC 69 +#define GF_SKIP0 70 +#define GF_SKIP1 71 +#define GF_SKIP2 72 +#define GF_SKIP3 73 +#define GF_NEW_ROW_0 74 +#define GF_NEW_ROW_1 75 +#define GF_NEW_ROW_MAX 238 +#define GF_XXX1 239 +#define GF_XXX2 240 +#define GF_XXX3 241 +#define GF_XXX4 242 +#define GF_YYY 243 +#define GF_NOOP 244 +#define GF_LOC 245 +#define GF_LOC0 246 +#define GF_PRE 247 +#define GF_POST 248 +#define GF_POST_POST 249 + +#define GF_ID 131 +#define GF_TRAILER 223 + +#define BLACK 1 +#define WHITE 0 + +static int gf_load_font __PROTO((DviParams *, DviFont *)); +static int gf_font_get_glyph __PROTO((DviParams *, DviFont *, int)); + +/* only symbol exported by this file */ +DviFontInfo gf_font_info = { + "GF", + 0, /* scaling not supported natively */ + gf_load_font, + gf_font_get_glyph, + mdvi_shrink_glyph, + mdvi_shrink_glyph_grey, + NULL, /* free */ + NULL, /* reset */ + NULL, /* lookup */ + kpse_gf_format, + NULL +}; + +static int gf_read_bitmap(FILE *p, DviFontChar *ch) +{ + int op; + int min_n, max_n; + int min_m, max_m; + int paint_switch; + int x, y; + int bpl; + Int32 par; + BmUnit *line; + BITMAP *map; + + fseek(p, (long)ch->offset, SEEK_SET); + op = fuget1(p); + if(op == GF_BOC) { + /* skip character code */ + fuget4(p); + /* skip pointer */ + fuget4(p); + min_m = fsget4(p); + max_m = fsget4(p); + min_n = fsget4(p); + max_n = fsget4(p); + } else if(op == GF_BOC1) { + /* skip character code */ + fuget1(p); + min_m = fuget1(p); /* this is max_m - min_m */ + max_m = fuget1(p); + min_n = fuget1(p); /* this is max_n - min_n */ + max_n = fuget1(p); + min_m = max_m - min_m; + min_n = max_n - min_n; + } else { + mdvi_error(_("GF: invalid opcode %d in character %d\n"), + op, ch->code); + return -1; + } + + ch->x = -min_m; + ch->y = max_n; + ch->width = max_m - min_m + 1; + ch->height = max_n - min_n + 1; + map = bitmap_alloc(ch->width, ch->height); + + ch->glyph.data = map; + ch->glyph.x = ch->x; + ch->glyph.y = ch->y; + ch->glyph.w = ch->width; + ch->glyph.h = ch->height; + +#define COLOR(x) ((x) ? "BLACK" : "WHITE") + + paint_switch = WHITE; + x = y = 0; + line = map->data; + bpl = map->stride; + DEBUG((DBG_BITMAPS, "(gf) reading character %d\n", ch->code)); + while((op = fuget1(p)) != GF_EOC) { + Int32 n; + + if(feof(p)) + break; + if(op == GF_PAINT0) { + DEBUG((DBG_BITMAPS, "(gf) Paint0 %s -> %s\n", + COLOR(paint_switch), COLOR(!paint_switch))); + paint_switch = !paint_switch; + } else if(op <= GF_PAINT3) { + if(op < GF_PAINT1) + par = op; + else + par = fugetn(p, op - GF_PAINT1 + 1); + if(y >= ch->height || x + par >= ch->width) + goto toobig; + /* paint everything between columns x and x + par - 1 */ + DEBUG((DBG_BITMAPS, "(gf) Paint %d %s from (%d,%d)\n", + par, COLOR(paint_switch), x, y)); + if(paint_switch == BLACK) + bitmap_paint_bits(line + (x / BITMAP_BITS), + x % BITMAP_BITS, par); + paint_switch = !paint_switch; + x += par; + } else if(op >= GF_NEW_ROW_0 && op <= GF_NEW_ROW_MAX) { + y++; + line = bm_offset(line, bpl); + x = op - GF_NEW_ROW_0; + paint_switch = BLACK; + DEBUG((DBG_BITMAPS, "(gf) new_row_%d\n", x)); + } else switch(op) { + case GF_SKIP0: + y++; + line = bm_offset(line, bpl); + x = 0; + paint_switch = WHITE; + DEBUG((DBG_BITMAPS, "(gf) skip_0\n")); + break; + case GF_SKIP1: + case GF_SKIP2: + case GF_SKIP3: + par = fugetn(p, op - GF_SKIP1 + 1); + y += par + 1; + line = bm_offset(line, (par + 1) * bpl); + x = 0; + paint_switch = WHITE; + DEBUG((DBG_BITMAPS, "(gf) skip_%d\n", op - GF_SKIP1)); + break; + case GF_XXX1: + case GF_XXX2: + case GF_XXX3: + case GF_XXX4: { +#ifndef NODEBUG + char *s; + + s = read_string(p, op - GF_XXX1 + 1, NULL, 0); + DEBUG((DBG_SPECIAL, "(gf) Character %d: Special \"%s\"\n", + ch->code, s)); + mdvi_free(s); +#else + n = fugetn(p, op - GF_XXX1 + 1); + fseek(p, (long)n, SEEK_CUR); +#endif + break; + } + case GF_YYY: + n = fuget4(p); + DEBUG((DBG_SPECIAL, "(gf) Character %d: MF special %u\n", + ch->code, n)); + break; + case GF_NOOP: + DEBUG((DBG_BITMAPS, "(gf) no_op\n")); + break; + default: + mdvi_error(_("(gf) Character %d: invalid opcode %d\n"), + ch->code, op); + goto error; + } + /* chech that we're still inside the bitmap */ + if(x > ch->width || y > ch->height) + goto toobig; + DEBUG((DBG_BITMAPS, "(gf) curr_loc @ (%d,%d)\n", x, y)); + } + + if(op != GF_EOC) + goto error; + DEBUG((DBG_BITMAPS, "(gf) end of character %d\n", ch->code)); + return 0; + +toobig: + mdvi_error(_("(gf) character %d has an incorrect bounding box\n"), + ch->code); +error: + bitmap_destroy(map); + ch->glyph.data = NULL; + return -1; +} + +static int gf_load_font(DviParams *unused, DviFont *font) +{ + int i; + int n; + int loc; + int hic; + FILE *p; + Int32 word; + int op; + long alpha, beta, z; +#ifndef NODEBUG + char s[256]; +#endif + + p = font->in; + + /* check preamble */ + loc = fuget1(p); hic = fuget1(p); + if(loc != GF_PRE || hic != GF_ID) + goto badgf; + loc = fuget1(p); +#ifndef NODEBUG + for(i = 0; i < loc; i++) + s[i] = fuget1(p); + s[i] = 0; + DEBUG((DBG_FONTS, "(gf) %s: %s\n", font->fontname, s)); +#else + fseek(p, (long)loc, SEEK_CUR); +#endif + /* now read character locators in postamble */ + if(fseek(p, (long)-1, SEEK_END) == -1) + return -1; + + n = 0; + while((op = fuget1(p)) == GF_TRAILER) { + if(fseek(p, (long)-2, SEEK_CUR) < 0) + break; + n++; + } + if(op != GF_ID || n < 4) + goto badgf; + /* get the pointer to the postamble */ + fseek(p, (long)-5, SEEK_CUR); + op = fuget4(p); + /* jump to it */ + fseek(p, (long)op, SEEK_SET); + if(fuget1(p) != GF_POST) + goto badgf; + /* skip pointer to last EOC */ + fuget4(p); + /* get the design size */ + font->design = fuget4(p); + /* the checksum */ + word = fuget4(p); + if(word && font->checksum && font->checksum != word) { + mdvi_warning(_("%s: bad checksum (expected %u, found %u)\n"), + font->fontname, font->checksum, word); + } else if(!font->checksum) + font->checksum = word; + /* skip pixels per point ratio */ + fuget4(p); + fuget4(p); + font->chars = xnalloc(DviFontChar, 256); + for(loc = 0; loc < 256; loc++) + font->chars[loc].offset = 0; + /* skip glyph "bounding box" */ + fseek(p, (long)16, SEEK_CUR); + loc = 256; + hic = -1; + TFMPREPARE(font->scale, z, alpha, beta); + while((op = fuget1(p)) != GF_POST_POST) { + DviFontChar *ch; + int cc; + + /* get the character code */ + cc = fuget1(p); + if(cc < loc) + loc = cc; + if(cc > hic) + hic = cc; + ch = &font->chars[cc]; + switch(op) { + case GF_LOC: + fsget4(p); /* skip dx */ + fsget4(p); /* skip dy */ + break; + case GF_LOC0: + fuget1(p); /* skip dx */ + /* dy assumed 0 */ + break; + default: + mdvi_error(_("%s: junk in postamble\n"), font->fontname); + goto error; + } + ch->code = cc; + ch->tfmwidth = fuget4(p); + ch->tfmwidth = TFMSCALE(ch->tfmwidth, z, alpha, beta); + ch->offset = fuget4(p); + if(ch->offset == -1) + ch->offset = 0; + /* initialize the rest of the glyph information */ + ch->x = 0; + ch->y = 0; + ch->width = 0; + ch->height = 0; + ch->glyph.data = NULL; + ch->shrunk.data = NULL; + ch->grey.data = NULL; + ch->flags = 0; + ch->loaded = 0; + } + + if(op != GF_POST_POST) + goto badgf; + + if(loc > 0 || hic < 255) { + /* shrink to optimal size */ + memmove(font->chars, font->chars + loc, + (hic - loc + 1) * sizeof(DviFontChar)); + font->chars = xresize(font->chars, + DviFontChar, hic - loc + 1); + } + font->loc = loc; + font->hic = hic; + + return 0; + +badgf: + mdvi_error(_("%s: File corrupted, or not a GF file\n"), font->fontname); +error: + if(font->chars) { + mdvi_free(font->chars); + font->chars = NULL; + } + font->loc = font->hic = 0; + return -1; +} + +static int gf_font_get_glyph(DviParams *params, DviFont *font, int code) +{ + DviFontChar *ch; + + if(code < font->loc || code > font->hic || !font->chars) + return -1; + ch = &font->chars[code - font->loc]; + + if(!ch->loaded) { + if(ch->offset == 0) + return -1; + DEBUG((DBG_GLYPHS, "(gf) %s: loading GF glyph for character %d\n", + font->fontname, code)); + if(font->in == NULL && font_reopen(font) < 0) + return -1; + if(fseek(font->in, ch->offset, SEEK_SET) == -1) + return -1; + if(gf_read_bitmap(font->in, ch) < 0) + return -1; + ch->loaded = 1; + } + return 0; +} diff --git a/backend/dvi/mdvi-lib/hash.c b/backend/dvi/mdvi-lib/hash.c new file mode 100644 index 00000000..d359e3c8 --- /dev/null +++ b/backend/dvi/mdvi-lib/hash.c @@ -0,0 +1,224 @@ +/* + * Copyright (C) 2000, Matias Atria + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#include <config.h> +#include "mdvi.h" + +/* simple hash tables for MDVI */ + + +struct _DviHashBucket { + DviHashBucket *next; + DviHashKey key; + Ulong hvalue; + void *data; +}; + +static Ulong hash_string(DviHashKey key) +{ + Uchar *p; + Ulong h, g; + + for(h = 0, p = (Uchar *)key; *p; p++) { + h = (h << 4UL) + *p; + if((g = h & 0xf0000000L) != 0) { + h ^= (g >> 24UL); + h ^= g; + } + } + + return h; +} + +static int hash_compare(DviHashKey k1, DviHashKey k2) +{ + return strcmp((char *)k1, (char *)k2); +} + +void mdvi_hash_init(DviHashTable *hash) +{ + hash->buckets = NULL; + hash->nbucks = 0; + hash->nkeys = 0; + hash->hash_func = NULL; + hash->hash_comp = NULL; + hash->hash_free = NULL; +} + +void mdvi_hash_create(DviHashTable *hash, int size) +{ + int i; + + hash->nbucks = size; + hash->buckets = xnalloc(DviHashBucket *, size); + for(i = 0; i < size; i++) + hash->buckets[i] = NULL; + hash->hash_func = hash_string; + hash->hash_comp = hash_compare; + hash->hash_free = NULL; + hash->nkeys = 0; +} + +static DviHashBucket *hash_find(DviHashTable *hash, DviHashKey key) +{ + Ulong hval; + DviHashBucket *buck; + + hval = (hash->hash_func(key) % hash->nbucks); + + for(buck = hash->buckets[hval]; buck; buck = buck->next) + if(hash->hash_comp(buck->key, key) == 0) + break; + return buck; +} + +/* Neither keys nor data are duplicated */ +int mdvi_hash_add(DviHashTable *hash, DviHashKey key, void *data, int rep) +{ + DviHashBucket *buck = NULL; + Ulong hval; + + if(rep != MDVI_HASH_UNCHECKED) { + buck = hash_find(hash, key); + if(buck != NULL) { + if(buck->data == data) + return 0; + if(rep == MDVI_HASH_UNIQUE) + return -1; + if(hash->hash_free != NULL) + hash->hash_free(buck->key, buck->data); + } + } + if(buck == NULL) { + buck = xalloc(DviHashBucket); + buck->hvalue = hash->hash_func(key); + hval = (buck->hvalue % hash->nbucks); + buck->next = hash->buckets[hval]; + hash->buckets[hval] = buck; + hash->nkeys++; + } + + /* save key and data */ + buck->key = key; + buck->data = data; + + return 0; +} + +void *mdvi_hash_lookup(DviHashTable *hash, DviHashKey key) +{ + DviHashBucket *buck = hash_find(hash, key); + + return buck ? buck->data : NULL; +} + +static DviHashBucket *hash_remove(DviHashTable *hash, DviHashKey key) +{ + DviHashBucket *buck, *last; + Ulong hval; + + hval = hash->hash_func(key); + hval %= hash->nbucks; + + for(last = NULL, buck = hash->buckets[hval]; buck; buck = buck->next) { + if(hash->hash_comp(buck->key, key) == 0) + break; + last = buck; + } + if(buck == NULL) + return NULL; + if(last) + last->next = buck->next; + else + hash->buckets[hval] = buck->next; + hash->nkeys--; + return buck; +} + +void *mdvi_hash_remove(DviHashTable *hash, DviHashKey key) +{ + DviHashBucket *buck = hash_remove(hash, key); + void *data = NULL; + + if(buck) { + data = buck->data; + mdvi_free(buck); + } + return data; +} + +void *mdvi_hash_remove_ptr(DviHashTable *hash, DviHashKey key) +{ + DviHashBucket *buck, *last; + Ulong hval; + void *ptr; + + hval = hash->hash_func(key); + hval %= hash->nbucks; + + for(last = NULL, buck = hash->buckets[hval]; buck; buck = buck->next) { + if(buck->key == key) + break; + last = buck; + } + if(buck == NULL) + return NULL; + if(last) + last->next = buck->next; + else + hash->buckets[hval] = buck->next; + hash->nkeys--; + /* destroy the bucket */ + ptr = buck->data; + mdvi_free(buck); + return ptr; +} + +int mdvi_hash_destroy_key(DviHashTable *hash, DviHashKey key) +{ + DviHashBucket *buck = hash_remove(hash, key); + + if(buck == NULL) + return -1; + if(hash->hash_free) + hash->hash_free(buck->key, buck->data); + mdvi_free(buck); + return 0; +} + +void mdvi_hash_reset(DviHashTable *hash, int reuse) +{ + int i; + DviHashBucket *buck; + + /* remove all keys in the hash table */ + for(i = 0; i < hash->nbucks; i++) { + for(; (buck = hash->buckets[i]); ) { + hash->buckets[i] = buck->next; + if(hash->hash_free) + hash->hash_free(buck->key, buck->data); + mdvi_free(buck); + } + } + hash->nkeys = 0; + if(!reuse && hash->buckets) { + mdvi_free(hash->buckets); + hash->buckets = NULL; + hash->nbucks = 0; + } /* otherwise, it is left empty, ready to be reused */ +} diff --git a/backend/dvi/mdvi-lib/hash.h b/backend/dvi/mdvi-lib/hash.h new file mode 100644 index 00000000..b10afd60 --- /dev/null +++ b/backend/dvi/mdvi-lib/hash.h @@ -0,0 +1,49 @@ +#ifndef MDVI_HASH +#define MDVI_HASH + +/* Hash tables */ + + +typedef struct _DviHashBucket DviHashBucket; +typedef struct _DviHashTable DviHashTable; + +/* + * Hash tables + */ + +typedef Uchar *DviHashKey; +#define MDVI_KEY(x) ((DviHashKey)(x)) + +typedef Ulong (*DviHashFunc) __PROTO((DviHashKey key)); +typedef int (*DviHashComp) __PROTO((DviHashKey key1, DviHashKey key2)); +typedef void (*DviHashFree) __PROTO((DviHashKey key, void *data)); + + +struct _DviHashTable { + DviHashBucket **buckets; + int nbucks; + int nkeys; + DviHashFunc hash_func; + DviHashComp hash_comp; + DviHashFree hash_free; +}; +#define MDVI_EMPTY_HASH_TABLE {NULL, 0, 0, NULL, NULL, NULL} + +#define MDVI_HASH_REPLACE 0 +#define MDVI_HASH_UNIQUE 1 +#define MDVI_HASH_UNCHECKED 2 + +extern void mdvi_hash_init __PROTO((DviHashTable *)); +extern void mdvi_hash_create __PROTO((DviHashTable *, int)); +extern int mdvi_hash_add __PROTO((DviHashTable *, DviHashKey, void *, int)); +extern int mdvi_hash_destroy_key __PROTO((DviHashTable *, DviHashKey)); +extern void mdvi_hash_reset __PROTO((DviHashTable *, int)); +extern void *mdvi_hash_lookup __PROTO((DviHashTable *, DviHashKey)); +extern void *mdvi_hash_remove __PROTO((DviHashTable *, DviHashKey)); +extern void *mdvi_hash_remove_ptr __PROTO((DviHashTable *, DviHashKey)); + +#define mdvi_hash_flush(h) mdvi_hash_reset((h), 1) +#define mdvi_hash_destroy(h) mdvi_hash_reset((h), 0) + +#endif + diff --git a/backend/dvi/mdvi-lib/list.c b/backend/dvi/mdvi-lib/list.c new file mode 100644 index 00000000..eb73a178 --- /dev/null +++ b/backend/dvi/mdvi-lib/list.c @@ -0,0 +1,120 @@ +/* + * Copyright (C) 2000, Matias Atria + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#include <config.h> +#include "common.h" + +void listh_init(ListHead *head) +{ + head->head = head->tail = NULL; + head->count = 0; +} + +void listh_prepend(ListHead *head, List *list) +{ + list->prev = NULL; + list->next = head->head; + if(head->head) + head->head->prev = list; + head->head = list; + if(!head->tail) + head->tail = list; + head->count++; +} + +void listh_append(ListHead *head, List *list) +{ + list->next = NULL; + list->prev = head->tail; + if(head->tail) + head->tail->next = list; + else + head->head = list; + head->tail = list; + head->count++; +} + +void listh_add_before(ListHead *head, List *at, List *list) +{ + if(at == head->head || head->head == NULL) + listh_prepend(head, list); + else { + list->next = at; + list->prev = at->prev; + at->prev = list; + head->count++; + } +} + +void listh_add_after(ListHead *head, List *at, List *list) +{ + if(at == head->tail || !head->tail) + listh_append(head, list); + else { + list->prev = at; + list->next = at->next; + at->next = list; + head->count++; + } +} + +void listh_remove(ListHead *head, List *list) +{ + if(list == head->head) { + head->head = list->next; + if(head->head) + head->head->prev = NULL; + } else if(list == head->tail) { + head->tail = list->prev; + if(head->tail) + head->tail->next = NULL; + } else { + list->next->prev = list->prev; + list->prev->next = list->next; + } + if(--head->count == 0) + head->head = head->tail = NULL; +} + +void listh_concat(ListHead *h1, ListHead *h2) +{ + if(h2->head == NULL) + ; /* do nothing */ + else if(h1->tail == NULL) + h1->head = h2->head; + else { + h1->tail->next = h2->head; + h2->head->prev = h1->tail; + } + h1->tail = h2->tail; + h1->count += h2->count; +} + +void listh_catcon(ListHead *h1, ListHead *h2) +{ + if(h2->head == NULL) + ; /* do nothing */ + else if(h1->head == NULL) + h1->tail = h2->tail; + else { + h1->head->prev = h2->tail; + h2->tail->next = h1->head; + } + h1->head = h2->head; + h1->count += h2->count; +} diff --git a/backend/dvi/mdvi-lib/mdvi.h b/backend/dvi/mdvi-lib/mdvi.h new file mode 100644 index 00000000..327e61fe --- /dev/null +++ b/backend/dvi/mdvi-lib/mdvi.h @@ -0,0 +1,623 @@ +/* + * Copyright (C) 2000, Matias Atria + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +#ifndef _MDVI_DVI_H +#define _MDVI_DVI_H 1 + +#include <stdio.h> +#include <sys/types.h> +#include <math.h> + +#include "sysdeps.h" +#include "bitmap.h" +#include "common.h" +#include "defaults.h" +#include "dviopcodes.h" + +typedef struct _DviGlyph DviGlyph; +typedef struct _DviDevice DviDevice; +typedef struct _DviFontChar DviFontChar; +typedef struct _DviFontRef DviFontRef; +typedef struct _DviFontInfo DviFontInfo; +typedef struct _DviFont DviFont; +typedef struct _DviState DviState; +typedef struct _DviPageSpec *DviPageSpec; +typedef struct _DviParams DviParams; +typedef struct _DviBuffer DviBuffer; +typedef struct _DviContext DviContext; +typedef struct _DviRange DviRange; +typedef struct _DviColorPair DviColorPair; +typedef struct _DviSection DviSection; +typedef struct _TFMChar TFMChar; +typedef struct _TFMInfo TFMInfo; +typedef struct _DviFontSearch DviFontSearch; +/* this is an opaque type */ +typedef struct _DviFontClass DviFontClass; + +typedef void (*DviFreeFunc) __PROTO((void *)); +typedef void (*DviFree2Func) __PROTO((void *, void *)); + +typedef Ulong DviColor; + +#ifdef TRUE +#undef TRUE +#endif +#ifdef FALSE +#undef FALSE +#endif + +typedef enum { + FALSE = 0, + TRUE = 1 +} DviBool; + +#include "hash.h" +#include "paper.h" + +/* + * information about a page: + * pagenum[0] = offset to BOP + * pagenum[1], ..., pagenum[10] = TeX \counters + */ +typedef long PageNum[11]; + +/* this structure contains the platform-specific information + * required to interpret a DVI file */ + +typedef void (*DviGlyphDraw) __PROTO((DviContext *context, + DviFontChar *glyph, + int x, int y)); + +typedef void (*DviRuleDraw) __PROTO((DviContext *context, + int x, int y, + Uint width, Uint height, int fill)); + +typedef int (*DviColorScale) __PROTO((void *device_data, + Ulong *pixels, + int npixels, + Ulong foreground, + Ulong background, + double gamma, + int density)); +typedef void *(*DviCreateImage) __PROTO((void *device_data, + Uint width, + Uint height, + Uint bpp)); +typedef void (*DviFreeImage) __PROTO((void *image)); +typedef void (*DviPutPixel) __PROTO((void *image, int x, int y, Ulong color)); +typedef void (*DviDevDestroy) __PROTO((void *data)); +typedef void (*DviRefresh) __PROTO((DviContext *dvi, void *device_data)); +typedef void (*DviSetColor) __PROTO((void *device_data, Ulong, Ulong)); +typedef void (*DviPSDraw) __PROTO((DviContext *context, + const char *filename, + int x, int y, + Uint width, Uint height)); + +struct _DviDevice { + DviGlyphDraw draw_glyph; + DviRuleDraw draw_rule; + DviColorScale alloc_colors; + DviCreateImage create_image; + DviFreeImage free_image; + DviPutPixel put_pixel; + DviDevDestroy dev_destroy; + DviRefresh refresh; + DviSetColor set_color; + DviPSDraw draw_ps; + void * device_data; +}; + +/* + * Fonts + */ + +#include "fontmap.h" + +struct _TFMChar { + Int32 present; + Int32 advance; /* advance */ + Int32 height; /* ascent */ + Int32 depth; /* descent */ + Int32 left; /* leftSideBearing */ + Int32 right; /* rightSideBearing */ +}; + +struct _TFMInfo { + int type; /* DviFontAFM, DviFontTFM, DviFontOFM */ + Uint32 checksum; + Uint32 design; + int loc; + int hic; + char coding[64]; + char family[64]; + TFMChar *chars; +}; + +struct _DviGlyph { + short x, y; /* origin */ + Uint w, h; /* dimensions */ + void *data; /* bitmap or XImage */ +}; + +typedef void (*DviFontShrinkFunc) + __PROTO((DviContext *, DviFont *, DviFontChar *, DviGlyph *)); +typedef int (*DviFontLoadFunc) __PROTO((DviParams *, DviFont *)); +typedef int (*DviFontGetGlyphFunc) __PROTO((DviParams *, DviFont *, int)); +typedef void (*DviFontFreeFunc) __PROTO((DviFont *)); +typedef void (*DviFontResetFunc) __PROTO((DviFont *)); +typedef char *(*DviFontLookupFunc) __PROTO((const char *, Ushort *, Ushort *)); +typedef int (*DviFontEncodeFunc) __PROTO((DviParams *, DviFont *, DviEncoding *)); + +struct _DviFontInfo { + char *name; /* human-readable format identifying string */ + int scalable; /* does it support scaling natively? */ + DviFontLoadFunc load; + DviFontGetGlyphFunc getglyph; + DviFontShrinkFunc shrink0; + DviFontShrinkFunc shrink1; + DviFontFreeFunc freedata; + DviFontResetFunc reset; + DviFontLookupFunc lookup; + int kpse_type; + void * private; +}; + +struct _DviFontChar { + Uint32 offset; + Int16 code; /* format-dependent, not used by MDVI */ + Int16 width; + Int16 height; + Int16 x; + Int16 y; + Int32 tfmwidth; + Ushort flags; +#ifdef __STRICT_ANSI__ + Ushort loaded; + Ushort missing; +#else + Ushort loaded : 1, + missing : 1; +#endif + Ulong fg; + Ulong bg; + BITMAP *glyph_data; + /* data for shrunk bitimaps */ + DviGlyph glyph; + DviGlyph shrunk; + DviGlyph grey; +}; + +struct _DviFontRef { + DviFontRef *next; + DviFont *ref; + Int32 fontid; +}; + +typedef enum { + DviFontAny = -1, + DviFontPK = 0, + DviFontGF = 1, + DviFontVF = 2, + DviFontTFM = 3, + DviFontT1 = 4, + DviFontTT = 5, + DviFontAFM = 6, + DviFontOFM = 7 +} DviFontType; + +struct _DviFontSearch { + int id; + Ushort hdpi; + Ushort vdpi; + Ushort actual_hdpi; + Ushort actual_vdpi; + const char *wanted_name; + const char *actual_name; + DviFontClass *curr; + DviFontInfo *info; +}; + +/* this is a kludge, I know */ +#define ISVIRTUAL(font) ((font)->search.info->getglyph == NULL) +#define SEARCH_DONE(s) ((s).id < 0) +#define SEARCH_INIT(s, name, h, v) do { \ + (s).id = 0; \ + (s).curr = NULL; \ + (s).hdpi = (h); \ + (s).vdpi = (v); \ + (s).wanted_name = (name); \ + (s).actual_name = NULL; \ + } while(0) + +struct _DviFont { + DviFont *next; + DviFont *prev; + int type; + Int32 checksum; + int hdpi; + int vdpi; + Int32 scale; + Int32 design; + FILE *in; + char *fontname; + char *filename; + int links; + int loc; + int hic; + Uint flags; + DviFontSearch search; + DviFontChar *chars; + DviFontRef *subfonts; + void *private; +}; + +/* + * Dvi context + */ + +typedef enum { + MDVI_ORIENT_TBLR = 0, /* top to bottom, left to right */ + MDVI_ORIENT_TBRL = 1, /* top to bottom, right to left */ + MDVI_ORIENT_BTLR = 2, /* bottom to top, left to right */ + MDVI_ORIENT_BTRL = 3, /* bottom to top, right to left */ + MDVI_ORIENT_RP90 = 4, /* rotated +90 degrees (counter-clockwise) */ + MDVI_ORIENT_RM90 = 5, /* rotated -90 degrees (clockwise) */ + MDVI_ORIENT_IRP90 = 6, /* flip horizontally, then rotate by +90 */ + MDVI_ORIENT_IRM90 = 7 /* rotate by -90, then flip horizontally */ +} DviOrientation; + +typedef enum { + MDVI_PAGE_SORT_UP, /* up, using \counter0 */ + MDVI_PAGE_SORT_DOWN, /* down, using \counter0 */ + MDVI_PAGE_SORT_RANDOM, /* randomly */ + MDVI_PAGE_SORT_DVI_UP, /* up, by location in DVI file */ + MDVI_PAGE_SORT_DVI_DOWN, /* down, by location in DVI file */ + MDVI_PAGE_SORT_NONE /* don't sort */ +} DviPageSort; + +struct _DviParams { + double mag; /* magnification */ + double conv; /* horizontal DVI -> pixel */ + double vconv; /* vertical DVI -> pixel */ + double tfm_conv; /* TFM -> DVI */ + double gamma; /* gamma correction factor */ + Uint dpi; /* horizontal resolution */ + Uint vdpi; /* vertical resolution */ + int hshrink; /* horizontal shrinking factor */ + int vshrink; /* vertical shrinking factor */ + Uint density; /* pixel density */ + Uint flags; /* flags (see MDVI_PARAM macros) */ + int hdrift; /* max. horizontal drift */ + int vdrift; /* max. vertical drift */ + int vsmallsp; /* small vertical space */ + int thinsp; /* small horizontal space */ + int layer; /* visible layer (for layered DVI files) */ + Ulong fg; /* foreground color */ + Ulong bg; /* background color */ + DviOrientation orientation; /* page orientation */ + int base_x; + int base_y; +}; + +typedef enum { + MDVI_PARAM_LAST = 0, + MDVI_SET_DPI = 1, + MDVI_SET_XDPI = 2, + MDVI_SET_YDPI = 3, + MDVI_SET_SHRINK = 4, + MDVI_SET_XSHRINK = 5, + MDVI_SET_YSHRINK = 6, + MDVI_SET_GAMMA = 7, + MDVI_SET_DENSITY = 8, + MDVI_SET_MAGNIFICATION = 9, + MDVI_SET_DRIFT = 10, + MDVI_SET_HDRIFT = 11, + MDVI_SET_VDRIFT = 12, + MDVI_SET_ORIENTATION = 13, + MDVI_SET_FOREGROUND = 14, + MDVI_SET_BACKGROUND = 15 +} DviParamCode; + +struct _DviBuffer { + Uchar *data; + size_t size; /* allocated size */ + size_t length; /* amount of data buffered */ + size_t pos; /* current position in buffer */ + int frozen; /* can we free this data? */ +}; + +/* DVI registers */ +struct _DviState { + int h; + int v; + int hh; + int vv; + int w; + int x; + int y; + int z; +}; + +struct _DviColorPair { + Ulong fg; + Ulong bg; +}; + +struct _DviContext { + char *filename; /* name of the DVI file */ + FILE *in; /* from here we read */ + char *fileid; /* from preamble */ + int npages; /* number of pages */ + int currpage; /* currrent page (0 based) */ + int depth; /* recursion depth */ + DviBuffer buffer; /* input buffer */ + DviParams params; /* parameters */ + DviPaper paper; /* paper type */ + Int32 num; /* numerator */ + Int32 den; /* denominator */ + DviFontRef *fonts; /* fonts used in this file */ + DviFontRef **fontmap; /* for faster id lookups */ + DviFontRef *currfont; /* current font */ + int nfonts; /* # of fonts used in this job */ + Int32 dvimag; /* original magnification */ + double dviconv; /* unshrunk scaling factor */ + double dvivconv; /* unshrunk scaling factor (vertical) */ + int dvi_page_w; /* unscaled page width */ + int dvi_page_h; /* unscaled page height */ + Ulong modtime; /* file modification time */ + PageNum *pagemap; /* page table */ + DviState pos; /* registers */ + DviPageSpec *pagesel; /* page selection data */ + int curr_layer; /* current layer */ + DviState *stack; /* DVI stack */ + int stacksize; /* stack depth */ + int stacktop; /* stack pointer */ + DviDevice device; /* device-specific routines */ + Ulong curr_fg; /* rendering color */ + Ulong curr_bg; + + DviColorPair *color_stack; + int color_top; + int color_size; + + DviFontRef *(*findref) __PROTO((DviContext *, Int32)); + void *user_data; /* client data attached to this context */ +}; + +typedef enum { + MDVI_RANGE_BOUNDED, /* range is finite */ + MDVI_RANGE_LOWER, /* range has a lower bound */ + MDVI_RANGE_UPPER, /* range has an upper bound */ + MDVI_RANGE_UNBOUNDED /* range has no bounds at all */ +} DviRangeType; + +struct _DviRange { + DviRangeType type; /* one of the above */ + int from; /* lower bound */ + int to; /* upper bound */ + int step; /* step */ +}; + + +typedef void (*DviSpecialHandler) + __PROTO((DviContext *dvi, const char *prefix, const char *arg)); + +#define RANGE_HAS_LOWER(x) \ + ((x) == MDVI_RANGE_BOUNDED || (x) == MDVI_RANGE_LOWER) +#define RANGE_HAS_UPPER(x) \ + ((x) == MDVI_RANGE_BOUNDED || (x) == MDVI_RANGE_UPPER) + +/* + * Macros and prototypes + */ + +#define MDVI_PARAM_ANTIALIASED 1 +#define MDVI_PARAM_MONO 2 +#define MDVI_PARAM_CHARBOXES 4 +#define MDVI_PARAM_SHOWUNDEF 8 +#define MDVI_PARAM_DELAYFONTS 16 + +/* + * The FALLBACK priority class is reserved for font formats that + * contain no glyph information and are to be used as a last + * resort (e.g. TFM, AFM) + */ +#define MDVI_FONTPRIO_FALLBACK -3 +#define MDVI_FONTPRIO_LOWEST -2 +#define MDVI_FONTPRIO_LOW -1 +#define MDVI_FONTPRIO_NORMAL 0 +#define MDVI_FONTPRIO_HIGH 1 +#define MDVI_FONTPRIO_HIGHEST 2 + +#define MDVI_FONT_ENCODED (1 << 0) + +#define MDVI_GLYPH_EMPTY ((void *)1) +/* does the glyph have a non-empty bitmap/image? */ +#define MDVI_GLYPH_NONEMPTY(x) ((x) && (x) != MDVI_GLYPH_EMPTY) +/* has the glyph been loaded from disk? */ +#define MDVI_GLYPH_UNSET(x) ((x) == NULL) +/* do we have only a bounding box for this glyph? */ +#define MDVI_GLYPH_ISEMPTY(x) ((x) == MDVI_GLYPH_EMPTY) + +#define MDVI_ENABLED(d,x) ((d)->params.flags & (x)) +#define MDVI_DISABLED(d,x) !MDVI_ENABLED((d), (x)) + +#define MDVI_LASTPAGE(d) ((d)->npages - 1) +#define MDVI_NPAGES(d) (d)->npages +#define MDVI_VALIDPAGE(d,p) ((p) >= 0 && (p) <= MDVI_LASTPAGE(d)) +#define MDVI_FLAGS(d) (d)->params.flags +#define MDVI_SHRINK_FROM_DPI(d) Max(1, (d) / 75) +#define MDVI_CURRFG(d) (d)->curr_fg +#define MDVI_CURRBG(d) (d)->curr_bg + +#define pixel_round(d,v) (int)((d)->params.conv * (v) + 0.5) +#define vpixel_round(d,v) (int)((d)->params.vconv * (v) + 0.5) +#define rule_round(d,v) (int)((d)->params.conv * (v) + 0.99999) /*9999999)*/ +#define vrule_round(d,v) (int)((d)->params.vconv * (v) + 0.99999) + +extern int mdvi_reload __PROTO((DviContext *, DviParams *)); +extern void mdvi_setpage __PROTO((DviContext *, int)); +extern int mdvi_dopage __PROTO((DviContext *, int)); +extern void mdvi_shrink_glyph __PROTO((DviContext *, DviFont *, DviFontChar *, DviGlyph *)); +extern void mdvi_shrink_box __PROTO((DviContext *, DviFont *, DviFontChar *, DviGlyph *)); +extern void mdvi_shrink_glyph_grey __PROTO((DviContext *, DviFont *, DviFontChar *, DviGlyph *)); +extern int mdvi_find_tex_page __PROTO((DviContext *, int)); +extern int mdvi_configure __PROTO((DviContext *, DviParamCode, ...)); + +extern int get_tfm_chars __PROTO((DviParams *, DviFont *, TFMInfo *, int)); +extern int tfm_load_file __PROTO((const char *, TFMInfo *)); +extern int afm_load_file __PROTO((const char *, TFMInfo *)); +extern TFMInfo *get_font_metrics __PROTO((const char *, int, const char *)); +extern char *lookup_font_metrics __PROTO((const char *, int *)); +extern void free_font_metrics __PROTO((TFMInfo *)); +extern void flush_font_metrics __PROTO((void)); + +#define get_metrics(name) get_font_metrics((name), DviFontAny, NULL) + +extern void mdvi_sort_pages __PROTO((DviContext *, DviPageSort)); + +extern void mdvi_init_kpathsea __PROTO((const char *, const char *, const char *, int, const char *)); + +extern DviContext* mdvi_init_context __PROTO((DviParams *, DviPageSpec *, const char *)); +extern void mdvi_destroy_context __PROTO((DviContext *)); + +/* helper macros that call mdvi_configure() */ +#define mdvi_config_one(d,x,y) mdvi_configure((d), (x), (y), MDVI_PARAM_LAST) +#define mdvi_set_dpi(d,x) mdvi_config_one((d), MDVI_SET_DPI, (x)) +#define mdvi_set_xdpi(d,x) mdvi_config_one((d), MDVI_SET_XDPI, (x)) +#define mdvi_set_ydpi(d,x) mdvi_config_one((d), MDVI_SET_YDPI, (x)) +#define mdvi_set_hshrink(d,h) mdvi_config_one((d), MDVI_SET_XSHRINK, (h)) +#define mdvi_set_vshrink(d,h) mdvi_config_one((d), MDVI_SET_YSHRINK, (h)) +#define mdvi_set_gamma(d,g) mdvi_config_one((d), MDVI_SET_GAMMA, (g)) +#define mdvi_set_density(d,x) mdvi_config_one((d), MDVI_SET_DENSITY, (x)) +#define mdvi_set_drift(d,x) mdvi_config_one((d), MDVI_SET_DRIFT, (x)) +#define mdvi_set_hdrift(d,h) mdvi_config_one((d), MDVI_SET_HDRIFT, (h)) +#define mdvi_set_vdrift(d,v) mdvi_config_one((d), MDVI_SET_VDRIFT, (v)) +#define mdvi_set_mag(d,m) \ + mdvi_config_one((d), MDVI_SET_MAGNIFICATION, (m)) +#define mdvi_set_foreground(d,x) \ + mdvi_config_one((d), MDVI_SET_FOREGROUND, (x)) +#define mdvi_set_background(d,x) \ + mdvi_config_one((d), MDVI_SET_BACKGROUND, (x)) +#define mdvi_set_orientation(d,x) \ + mdvi_config_one((d), MDVI_SET_ORIENTATION, (x)) +#define mdvi_set_shrink(d,h,v) \ + mdvi_configure((d), MDVI_SET_XSHRINK, (h), \ + MDVI_SET_YSHRINK, (v), MDVI_PARAM_LAST) + +extern DviRange* mdvi_parse_range __PROTO((const char *, DviRange *, int *, char **)); +extern DviPageSpec* mdvi_parse_page_spec __PROTO((const char *)); +extern void mdvi_free_page_spec __PROTO((DviPageSpec *)); +extern int mdvi_in_range __PROTO((DviRange *, int, int)); +extern int mdvi_range_length __PROTO((DviRange *, int)); +extern int mdvi_page_selected __PROTO((DviPageSpec *, PageNum, int)); + +/* Specials */ +extern int mdvi_register_special __PROTO(( + const char *label, + const char *prefix, + const char *regex, + DviSpecialHandler handler, + int replace)); +extern int mdvi_unregister_special __PROTO((const char *prefix)); +extern int mdvi_do_special __PROTO((DviContext *dvi, char *dvi_special)); +extern void mdvi_flush_specials __PROTO((void)); + +/* Fonts */ + +#define MDVI_FONTSEL_BITMAP (1 << 0) +#define MDVI_FONTSEL_GREY (1 << 1) +#define MDVI_FONTSEL_GLYPH (1 << 2) + +#define FONTCHAR(font, code) \ + (((code) < font->loc || (code) > font->hic || !(font)->chars) ? \ + NULL : &font->chars[(code) - (font)->loc]) +#define FONT_GLYPH_COUNT(font) ((font)->hic - (font)->loc + 1) + +#define glyph_present(x) ((x) && (x)->offset) + +/* create a reference to a font */ +extern DviFontRef *font_reference __PROTO((DviParams *params, + Int32 dvi_id, + const char *font_name, + Int32 checksum, + int xdpi, + int ydpi, + Int32 scale_factor)); + +/* drop a reference to a font */ +extern void font_drop_one __PROTO((DviFontRef *)); + +/* drop a chain of references */ +extern void font_drop_chain __PROTO((DviFontRef *)); + +/* destroy selected information for a glyph */ +extern void font_reset_one_glyph __PROTO((DviDevice *, DviFontChar *, int)); + +/* destroy selected information for all glyphs in a font */ +extern void font_reset_font_glyphs __PROTO((DviDevice *, DviFont *, int)); + +/* same for a chain of font references */ +extern void font_reset_chain_glyphs __PROTO((DviDevice *, DviFontRef *, int)); + +extern void font_finish_definitions __PROTO((DviContext *)); + +/* lookup an id # in a reference chain */ +extern DviFontRef* font_find_flat __PROTO((DviContext *, Int32)); +extern DviFontRef* font_find_mapped __PROTO((DviContext *, Int32)); + +/* called to reopen (or rewind) a font file */ +extern int font_reopen __PROTO((DviFont *)); + +/* reads a glyph from a font, and makes all necessary transformations */ +extern DviFontChar* font_get_glyph __PROTO((DviContext *, DviFont *, int)); + +/* transform a glyph according to the given orientation */ +extern void font_transform_glyph __PROTO((DviOrientation, DviGlyph *)); + +/* destroy all fonts that are not being used, returns number of fonts freed */ +extern int font_free_unused __PROTO((DviDevice *)); + +#define font_free_glyph(dev, font, code) \ + font_reset_one_glyph((dev), \ + FONTCHAR((font), (code)), MDVI_FONTSEL_GLYPH) + +extern int mdvi_encode_font __PROTO((DviParams *, DviFont *)); + + +/* font lookup functions */ +extern int mdvi_register_font_type __PROTO((DviFontInfo *, int)); +extern char **mdvi_list_font_class __PROTO((int)); +extern int mdvi_get_font_classes __PROTO((void)); +extern int mdvi_unregister_font_type __PROTO((const char *, int)); +extern char *mdvi_lookup_font __PROTO((DviFontSearch *)); +extern DviFont *mdvi_add_font __PROTO((const char *, Int32, int, int, Int32)); +extern int mdvi_font_retry __PROTO((DviParams *, DviFont *)); + +/* Miscellaneous */ + +extern int mdvi_set_logfile __PROTO((const char *)); +extern int mdvi_set_logstream __PROTO((FILE *)); +extern int mdvi_set_loglevel __PROTO((int)); + +#define mdvi_stop_logging(x) mdvi_set_logstream(NULL) + +/* this will check the environment and then `texmf.cnf' for + * the given name changed to lowercase, and `_' changed to `-' */ +extern char* mdvi_getenv __PROTO((const char *)); + +#endif /* _MDVI_DVI_H */ diff --git a/backend/dvi/mdvi-lib/pagesel.c b/backend/dvi/mdvi-lib/pagesel.c new file mode 100644 index 00000000..5a153955 --- /dev/null +++ b/backend/dvi/mdvi-lib/pagesel.c @@ -0,0 +1,491 @@ +/* pagesel.c -- Page selection mechanism */ +/* + * Copyright (C) 2000, Matias Atria + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#include <config.h> +#include <stdlib.h> +#include <ctype.h> +#include <string.h> +#include <limits.h> + +#include "mdvi.h" +#include "private.h" + +char *program_name = "page"; + +struct _DviPageSpec { + DviRange *ranges; + int nranges; +}; + +DviRange *mdvi_parse_range(const char *format, DviRange *limit, int *nitems, char **endptr) +{ + int quoted; + int size; + int curr; + int done; + int lower; + int upper; + int type; + char * cp; + char * copy; + char * text; + DviRange one; + DviRange *range; + + quoted = (*format == '{'); + if(quoted) format++; + + size = 0; + curr = 0; + range = NULL; + copy = mdvi_strdup(format); + done = 0; + lower = 0; + upper = 0; + type = MDVI_RANGE_UNBOUNDED; + + if(limit) { + switch(limit->type) { + case MDVI_RANGE_BOUNDED: + lower = limit->from; + upper = limit->to; + break; + case MDVI_RANGE_UPPER: + lower = INT_MIN; + upper = limit->to; + break; + case MDVI_RANGE_LOWER: + lower = limit->from; + upper = INT_MAX; + break; + case MDVI_RANGE_UNBOUNDED: + lower = INT_MIN; + upper = INT_MAX; + break; + } + type = limit->type; + } else { + lower = INT_MIN; + upper = INT_MAX; + type = MDVI_RANGE_UNBOUNDED; + } + one.type = type; + one.from = lower; + one.to = upper; + one.step = 1; + for(cp = text = copy; !done; cp++) { + char *p; + int f, t, s; + int ch; + int this_type; + int lower_given = 0; + int upper_given = 0; + + if(*cp == 0 || *cp == '.' || (*cp == '}' && quoted)) + done = 1; + else if(*cp != ',') + continue; + + if(text == cp) + continue; + ch = *cp; + *cp = 0; + f = lower; + t = upper; + s = 1; + + p = strchr(text, ':'); + if(p) *p++ = 0; + if(*text) { + lower_given = 1; + f = strtol(text, NULL, 0); + } + if(p == NULL) { + if(lower_given) { + upper_given = 1; + t = f; s = 1; + } + goto finish; + } + text = p; + p = strchr(text, ':'); + if(p) *p++ = 0; + if(*text) { + upper_given = 1; + t = strtol(text, NULL, 0); + } + if(p == NULL) + goto finish; + text = p; + if(*text) + s = strtol(text, NULL, 0); +finish: + if(lower_given && upper_given) + this_type = MDVI_RANGE_BOUNDED; + else if(lower_given) { + if(!RANGE_HAS_UPPER(type)) + this_type = MDVI_RANGE_LOWER; + else + this_type = MDVI_RANGE_BOUNDED; + t = upper; + } else if(upper_given) { + if(RANGE_HAS_UPPER(one.type)) { + one.to++; + this_type = MDVI_RANGE_BOUNDED; + } else { + one.to = lower; + if(!RANGE_HAS_LOWER(type)) + this_type = MDVI_RANGE_UPPER; + else + this_type = MDVI_RANGE_BOUNDED; + } + f = one.to; + } else { + this_type = type; + f = lower; + t = upper; + } + one.type = this_type; + one.to = t; + one.from = f; + one.step = s; + + if(curr == size) { + size += 8; + range = mdvi_realloc(range, size * sizeof(DviRange)); + } + memcpy(&range[curr++], &one, sizeof(DviRange)); + *cp = ch; + text = cp + 1; + } + if(done) + cp--; + if(quoted && *cp == '}') + cp++; + if(endptr) + *endptr = (char *)format + (cp - copy); + if(curr && curr < size) + range = mdvi_realloc(range, curr * sizeof(DviRange)); + *nitems = curr; + mdvi_free(copy); + return range; +} + +DviPageSpec *mdvi_parse_page_spec(const char *format) +{ + /* + * a page specification looks like this: + * '{'RANGE_SPEC'}' for a DVI spec + * '{'RANGE_SPEC'}' '.' ... for a TeX spec + */ + DviPageSpec *spec; + DviRange *range; + int count; + int i; + char *ptr; + + spec = xnalloc(struct _DviPageSpec *, 11); + for(i = 0; i < 11; i++) + spec[i] = NULL; + + /* check what kind of spec we're parsing */ + if(*format != '*') { + range = mdvi_parse_range(format, NULL, &count, &ptr); + if(ptr == format) { + if(range) mdvi_free(range); + mdvi_error(_("invalid page specification `%s'\n"), format); + return NULL; + } + } else + range = NULL; + + if(*format == 'D' || *format == 'd' || *ptr != '.') + i = 0; + else + i = 1; + + if(range) { + spec[i] = xalloc(struct _DviPageSpec); + spec[i]->ranges = range; + spec[i]->nranges = count; + } else + spec[i] = NULL; + + if(*ptr != '.') { + if(*ptr) + mdvi_warning(_("garbage after DVI page specification ignored\n")); + return spec; + } + + for(i++; *ptr == '.' && i <= 10; i++) { + ptr++; + if(*ptr == '*') { + ptr++; + range = NULL; + } else { + char *end; + + range = mdvi_parse_range(ptr, NULL, &count, &end); + if(end == ptr) { + if(range) mdvi_free(range); + range = NULL; + } else + ptr = end; + } + if(range != NULL) { + spec[i] = xalloc(struct _DviPageSpec); + spec[i]->ranges = range; + spec[i]->nranges = count; + } else + spec[i] = NULL; + } + + if(i > 10) + mdvi_warning(_("more than 10 counters in page specification\n")); + else if(*ptr) + mdvi_warning(_("garbage after TeX page specification ignored\n")); + + return spec; +} + +/* returns non-zero if the given page is included by `spec' */ +int mdvi_page_selected(DviPageSpec *spec, PageNum page, int dvipage) +{ + int i; + int not_found; + + if(spec == NULL) + return 1; + if(spec[0]) { + not_found = mdvi_in_range(spec[0]->ranges, + spec[0]->nranges, dvipage); + if(not_found < 0) + return 0; + } + for(i = 1; i <= 10; i++) { + if(spec[i] == NULL) + continue; + not_found = mdvi_in_range(spec[i]->ranges, + spec[i]->nranges, (int)page[i]); + if(not_found < 0) + return 0; + } + return 1; +} + +void mdvi_free_page_spec(DviPageSpec *spec) +{ + int i; + + for(i = 0; i < 11; i++) + if(spec[i]) { + mdvi_free(spec[i]->ranges); + mdvi_free(spec[i]); + } + mdvi_free(spec); +} + +int mdvi_in_range(DviRange *range, int nitems, int value) +{ + DviRange *r; + + for(r = range; r < range + nitems; r++) { + int cond; + + switch(r->type) { + case MDVI_RANGE_BOUNDED: + if(value == r->from) + return (r - range); + if(r->step < 0) + cond = (value <= r->from) && (value >= r->to); + else + cond = (value <= r->to) && (value >= r->from); + if(cond && ((value - r->from) % r->step) == 0) + return (r - range); + break; + case MDVI_RANGE_LOWER: + if(value == r->from) + return (r - range); + if(r->step < 0) + cond = (value < r->from); + else + cond = (value > r->from); + if(cond && ((value - r->from) % r->step) == 0) + return (r - range); + break; + case MDVI_RANGE_UPPER: + if(value == r->to) + return (r - range); + if(r->step < 0) + cond = (value > r->to); + else + cond = (value < r->to); + if(cond && ((value - r->to) % r->step) == 0) + return (r - range); + break; + case MDVI_RANGE_UNBOUNDED: + if((value % r->step) == 0) + return (r - range); + break; + } + } + return -1; +} + +int mdvi_range_length(DviRange *range, int nitems) +{ + int count = 0; + DviRange *r; + + for(r = range; r < range + nitems; r++) { + int n; + + if(r->type != MDVI_RANGE_BOUNDED) + return -2; + n = (r->to - r->from) / r->step; + if(n < 0) + n = 0; + count += n + 1; + } + return count; +} + +#ifdef TEST + +void print_range(DviRange *range) +{ + switch(range->type) { + case MDVI_RANGE_BOUNDED: + printf("From %d to %d, step %d\n", + range->from, range->to, range->step); + break; + case MDVI_RANGE_LOWER: + printf("From %d, step %d\n", + range->from, range->step); + break; + case MDVI_RANGE_UPPER: + printf("From %d, step -%d\n", + range->to, range->step); + break; + case MDVI_RANGE_UNBOUNDED: + printf("From 0, step %d and %d\n", + range->step, -range->step); + break; + } +} + +int main() +{ +#if 0 + char buf[256]; + DviRange limit; + + limit.from = 0; + limit.to = 100; + limit.step = 2; + limit.type = MDVI_RANGE_UNBOUNDED; + while(1) { + DviRange *range; + char *end; + int count; + int i; + + printf("Range> "); fflush(stdout); + if(fgets(buf, 256, stdin) == NULL) + break; + if(buf[strlen(buf)-1] == '\n') + buf[strlen(buf)-1] = 0; + if(buf[0] == 0) + continue; + end = NULL; + range = mdvi_parse_range(buf, &limit, &count, &end); + if(range == NULL) { + printf("range is empty\n"); + continue; + } + + for(i = 0; i < count; i++) { + printf("Range %d (%d elements):\n", + i, mdvi_range_length(&range[i], 1)); + print_range(&range[i]); + } + if(end && *end) + printf("Tail: [%s]\n", end); + printf("range has %d elements\n", + mdvi_range_length(range, count)); +#if 1 + while(1) { + int v; + + printf("Value: "); fflush(stdout); + if(fgets(buf, 256, stdin) == NULL) + break; + if(buf[strlen(buf)-1] == '\n') + buf[strlen(buf)-1] = 0; + if(buf[0] == 0) + break; + v = atoi(buf); + i = mdvi_in_range(range, count, v); + if(i == -1) + printf("%d not in range\n", v); + else { + printf("%d in range: ", v); + print_range(&range[i]); + } + } +#endif + if(range) mdvi_free(range); + } +#else + DviPageSpec *spec; + char buf[256]; + + while(1) { + printf("Spec> "); fflush(stdout); + if(fgets(buf, 256, stdin) == NULL) + break; + if(buf[strlen(buf)-1] == '\n') + buf[strlen(buf)-1] = 0; + if(buf[0] == 0) + continue; + spec = mdvi_parse_page_spec(buf); + if(spec == NULL) + printf("no spec parsed\n"); + else { + int i; + + printf("spec = "); + for(i = 0; i < 11; i++) { + printf("Counter %d:\n", i); + if(spec[i]) { + int k; + + for(k = 0; k < spec[i]->nranges; k++) + print_range(&spec[i]->ranges[k]); + } else + printf("\t*\n"); + } + mdvi_free_page_spec(spec); + } + } +#endif + exit(0); + +} +#endif /* TEST */ diff --git a/backend/dvi/mdvi-lib/paper.c b/backend/dvi/mdvi-lib/paper.c new file mode 100644 index 00000000..42cbcd3f --- /dev/null +++ b/backend/dvi/mdvi-lib/paper.c @@ -0,0 +1,171 @@ +/* + * Copyright (C) 2000, Matias Atria + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#include <config.h> +#include <string.h> + +#include "common.h" +#include "mdvi.h" +#include "private.h" + +static const DviPaperSpec papers[] = { + {"ISO", 0, 0}, + {"4A0", "1682mm", "2378mm"}, + {"2A0", "1189mm", "1682mm"}, + {"A0", "841mm", "1189mm"}, + {"A1", "594mm", "841mm"}, + {"A2", "420mm", "594mm"}, + {"A3", "297mm", "420mm"}, + {"A4", "210mm", "297mm"}, + {"A5", "148mm", "210mm"}, + {"A6", "105mm", "148mm"}, + {"A7", "74mm", "105mm"}, + {"A8", "52mm", "74mm"}, + {"A9", "37mm", "52mm"}, + {"A10", "26mm", "37mm"}, + {"B0", "1000mm", "1414mm"}, + {"B1", "707mm", "1000mm"}, + {"B2", "500mm", "707mm"}, + {"B3", "353mm", "500mm"}, + {"B4", "250mm", "353mm"}, + {"B5", "176mm", "250mm"}, + {"B6", "125mm", "176mm"}, + {"B7", "88mm", "125mm"}, + {"B8", "62mm", "88mm"}, + {"B9", "44mm", "62mm"}, + {"B10", "31mm", "44mm"}, + {"C0", "917mm", "1297mm"}, + {"C1", "648mm", "917mm"}, + {"C2", "458mm", "648mm"}, + {"C3", "324mm", "458mm"}, + {"C4", "229mm", "324mm"}, + {"C5", "162mm", "229mm"}, + {"C6", "114mm", "162mm"}, + {"C7", "81mm", "114mm"}, + {"C8", "57mm", "81mm"}, + {"C9", "40mm", "57mm"}, + {"C10", "28mm", "40mm"}, + {"US", 0, 0}, + {"archA", "9in", "12in"}, + {"archB", "12in", "18in"}, + {"archC", "18in", "24in"}, + {"archD", "24in", "36in"}, + {"archE", "36in", "48in"}, + {"executive", "7.5in", "10in"}, + {"flsa", "8.5in", "13in"}, + {"flse", "8.5in", "13in"}, + {"halfletter", "5.5in", "8.5in"}, + {"letter", "8.5in", "11in"}, + {"legal", "8.5in", "14in"}, + {"ledger", "17in", "11in"}, + {"note", "7.5in", "10in"}, + {"tabloid", "11in", "17in"}, + {"statement", "5.5in", "8.5in"}, + {0, 0, 0} +}; + +static DviPaperClass str2class(const char *name) +{ + if(STRCEQ(name, "ISO")) + return MDVI_PAPER_CLASS_ISO; + else if(STRCEQ(name, "US")) + return MDVI_PAPER_CLASS_US; + return MDVI_PAPER_CLASS_CUSTOM; +} + +int mdvi_get_paper_size(const char *name, DviPaper *paper) +{ + const DviPaperSpec *sp; + double a, b; + char c, d, e, f; + char buf[32]; + + paper->pclass = MDVI_PAPER_CLASS_CUSTOM; + if(sscanf(name, "%lfx%lf%c%c", &a, &b, &c, &d) == 4) { + sprintf(buf, "%12.16f%c%c", a, c, d); + paper->inches_wide = unit2pix_factor(buf); + sprintf(buf, "%12.16f%c%c", b, c, d); + paper->inches_tall = unit2pix_factor(buf); + paper->name = _("custom"); + return 0; + } else if(sscanf(name, "%lf%c%c,%lf%c%c", &a, &c, &d, &b, &e, &f) == 6) { + sprintf(buf, "%12.16f%c%c", a, c, d); + paper->inches_wide = unit2pix_factor(buf); + sprintf(buf, "%12.16f%c%c", b, e, f); + paper->inches_tall = unit2pix_factor(buf); + paper->name = _("custom"); + return 0; + } + + for(sp = &papers[0]; sp->name; sp++) { + if(!sp->width || !sp->height) { + paper->pclass = str2class(sp->name); + continue; + } + if(strcasecmp(sp->name, name) == 0) { + paper->inches_wide = unit2pix_factor(sp->width); + paper->inches_tall = unit2pix_factor(sp->height); + paper->name = sp->name; + return 0; + } + } + return -1; +} + +DviPaperSpec *mdvi_get_paper_specs(DviPaperClass pclass) +{ + int i; + int first, count; + DviPaperSpec *spec, *ptr; + + first = -1; + count = 0; + if(pclass == MDVI_PAPER_CLASS_ANY || + pclass == MDVI_PAPER_CLASS_CUSTOM) { + first = 0; + count = (sizeof(papers) / sizeof(papers[0])) - 3; + } else for(i = 0; papers[i].name; i++) { + if(papers[i].width == NULL) { + if(str2class(papers[i].name) == pclass) + first = i; + else if(first >= 0) + break; + } else if(first >= 0) + count++; + } + ptr = spec = xnalloc(DviPaperSpec, count + 1); + for(i = first; papers[i].name&& count > 0; i++) { + if(papers[i].width) { + ptr->name = papers[i].name; + ptr->width = papers[i].width; + ptr->height = papers[i].height; + ptr++; + count--; + } + } + ptr->name = NULL; + ptr->width = NULL; + ptr->height = NULL; + + return spec; +} + +void mdvi_free_paper_specs(DviPaperSpec *spec) +{ + mdvi_free(spec); +} diff --git a/backend/dvi/mdvi-lib/paper.h b/backend/dvi/mdvi-lib/paper.h new file mode 100644 index 00000000..d42ee079 --- /dev/null +++ b/backend/dvi/mdvi-lib/paper.h @@ -0,0 +1,32 @@ +#ifndef MDVI_PAPER +#define MDVI_PAPER + +typedef struct _DviPaper DviPaper; +typedef struct _DviPaperSpec DviPaperSpec; + +typedef enum { + MDVI_PAPER_CLASS_ISO, + MDVI_PAPER_CLASS_US, + MDVI_PAPER_CLASS_ANY, + MDVI_PAPER_CLASS_CUSTOM +} DviPaperClass; + +struct _DviPaper { + DviPaperClass pclass; + const char *name; + double inches_wide; + double inches_tall; +}; + +struct _DviPaperSpec { + const char *name; + const char *width; + const char *height; +}; + + +extern int mdvi_get_paper_size __PROTO((const char *, DviPaper *)); +extern DviPaperSpec* mdvi_get_paper_specs __PROTO((DviPaperClass)); +extern void mdvi_free_paper_specs __PROTO((DviPaperSpec *)); + +#endif diff --git a/backend/dvi/mdvi-lib/pk.c b/backend/dvi/mdvi-lib/pk.c new file mode 100644 index 00000000..a5791869 --- /dev/null +++ b/backend/dvi/mdvi-lib/pk.c @@ -0,0 +1,570 @@ + +/* Copyright (C) 2000, Matias Atria + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +/* + * History: + * + * 11/3/2000: + * - First working version + * 11/4/2000: + * - FIXED: entirely white/black rows were missed. + * 11/8/2000: + * - TESTED: Glyphs are rendered correctly in different byte orders. + * - Made bitmap code much more efficient and compact. + */ + +#include <config.h> +#include <stdio.h> +#include <stdlib.h> +#include <unistd.h> +#include <string.h> +#include <math.h> + +#include "mdvi.h" +#include "private.h" + +#define PK_ID 89 +#define PK_CMD_START 240 +#define PK_X1 240 +#define PK_X2 241 +#define PK_X3 242 +#define PK_X4 243 +#define PK_Y 244 +#define PK_POST 245 +#define PK_NOOP 246 +#define PK_PRE 247 + +#define PK_DYN_F(x) (((x) >> 4) & 0xf) +#define PK_PACKED(x) (PK_DYN_F(x) != 14) + +static int pk_load_font __PROTO((DviParams *, DviFont *)); +static int pk_font_get_glyph __PROTO((DviParams *, DviFont *, int)); + +static int pk_auto_generate = 1; /* this is ON by default */ + +typedef struct { + char currbyte; + char nybpos; + int dyn_f; +} pkread; + +static char *pk_lookup __PROTO((const char *, Ushort *, Ushort *)); +static char *pk_lookupn __PROTO((const char *, Ushort *, Ushort *)); + +/* only symbols exported by this file */ +DviFontInfo pk_font_info = { + "PK", + 0, /* scaling not supported natively */ + pk_load_font, + pk_font_get_glyph, + mdvi_shrink_glyph, + mdvi_shrink_glyph_grey, + NULL, /* free */ + NULL, /* reset */ + pk_lookup, /* lookup */ + kpse_pk_format, + NULL +}; + +DviFontInfo pkn_font_info = { + "PKN", + 0, /* scaling not supported natively */ + pk_load_font, + pk_font_get_glyph, + mdvi_shrink_glyph, + mdvi_shrink_glyph_grey, + NULL, /* free */ + NULL, /* reset */ + pk_lookupn, /* lookup */ + kpse_pk_format, + NULL +}; + +static char *pk_lookup(const char *name, Ushort *hdpi, Ushort *vdpi) +{ + kpse_glyph_file_type type; + char *filename; + + if(pk_auto_generate == 0) { + kpse_set_program_enabled(kpse_pk_format, 1, kpse_src_cmdline); + pk_auto_generate = 1; + } + filename = kpse_find_glyph(name, Max(*hdpi, *vdpi), + kpse_pk_format, &type); + if(filename && type.source == kpse_glyph_source_fallback) { + mdvi_free(filename); + filename = NULL; + } else if(filename) { + *hdpi = *vdpi = type.dpi; + } + return filename; +} + +static char *pk_lookupn(const char *name, Ushort *hdpi, Ushort *vdpi) +{ + kpse_glyph_file_type type; + char *filename; + + if(pk_auto_generate) { + kpse_set_program_enabled(kpse_pk_format, 0, kpse_src_cmdline); + pk_auto_generate = 0; + } + filename = kpse_find_glyph(name, Max(*hdpi, *vdpi), + kpse_pk_format, &type); + if(filename && type.source == kpse_glyph_source_fallback) { + mdvi_free(filename); + filename = NULL; + } else if(filename) { + *hdpi = *vdpi = type.dpi; + } + return filename; +} + +static inline int pk_get_nyb(FILE *p, pkread *pk) +{ + unsigned t; + int nb; + char c; + + t = c = pk->currbyte; + nb = pk->nybpos; + + switch(nb) { + case 0: + c = pk->currbyte = fuget1(p); + t = (c >> 4); + break; + case 1: + t = c; + break; + } + pk->nybpos = !nb; + return (t & 0xf); +} + +/* + * this is a bit cumbersome because we have to pass around + * the `pkread' data... + */ +static int pk_packed_num(FILE *p, pkread *pkr, int *repeat) +{ + int i, j; + int dyn_f = pkr->dyn_f; + + i = pk_get_nyb(p, pkr); + if(i == 0) { + do { + j = pk_get_nyb(p, pkr); + i++; + } while(j == 0); + while(i-- > 0) + j = (j << 4) + pk_get_nyb(p, pkr); + return (j - 15 + ((13 - dyn_f) << 4) + + dyn_f); + } else if(i <= dyn_f) + return i; + else if(i < 14) + return ((i - dyn_f - 1) << 4) + + pk_get_nyb(p, pkr) + dyn_f + 1; + else { + *repeat = 1; + if(i == 14) + *repeat = pk_packed_num(p, pkr, repeat); + return pk_packed_num(p, pkr, repeat); + } +} + +#define ROUND(x,y) (((x) + (y) - 1) / (y)) + +static BITMAP *get_bitmap(FILE *p, int w, int h, int flags) +{ + int i, j; + BmUnit *ptr; + BITMAP *bm; + int bitpos; + int currch; + + flags = 0; /* shut up that compiler */ + bitpos = -1; + if((bm = bitmap_alloc(w, h)) == NULL) + return NULL; + DEBUG((DBG_BITMAPS, "get_bitmap(%d,%d,%d): reading raw bitmap\n", + w, h, flags)); + ptr = bm->data; + currch = 0; + for(i = 0; i < h; i++) { + BmUnit mask; + + mask = FIRSTMASK; + for(j = 0; j < w; j++) { + if(bitpos < 0) { + currch = fuget1(p); + bitpos = 7; + } + if(currch & (1 << bitpos)) + *ptr |= mask; + bitpos--; + if(mask == LASTMASK) { + ptr++; + mask = FIRSTMASK; + } else + NEXTMASK(mask); + } + ptr = bm_offset(ptr, bm->stride); + } + return bm; +} + +static BITMAP *get_packed(FILE *p, int w, int h, int flags) +{ + int inrow, count; + int row; + BITMAP *bm; + int repeat_count; + int paint; + pkread pkr; + + pkr.nybpos = 0; + pkr.currbyte = 0; + pkr.dyn_f = PK_DYN_F(flags); + paint = !!(flags & 0x8); + + repeat_count = 0; + row = 0; + inrow = w; + if((bm = bitmap_alloc(w, h)) == NULL) + return NULL; + DEBUG((DBG_BITMAPS, "get_packed(%d,%d,%d): reading packed glyph\n", + w, h, flags)); + while(row < h) { + int i = 0; + + count = pk_packed_num(p, &pkr, &i); + if(i > 0) { + if(repeat_count) + fprintf(stderr, "second repeat count for this row (had %d and got %d)\n", + repeat_count, i); + repeat_count = i; + } + + if(count >= inrow) { + Uchar *r, *t; + BmUnit *a, mask; + + /* first finish current row */ + if(paint) + bitmap_set_row(bm, row, w - inrow, inrow, paint); + /* now copy it as many times as required */ + r = (Uchar *)bm->data + row * bm->stride; + while(repeat_count-- > 0) { + t = r + bm->stride; + /* copy entire lines */ + memcpy(t, r, bm->stride); + r = t; + row++; + } + repeat_count = 0; + /* count first row we drew */ + row++; + /* update run count */ + count -= inrow; + /* now r points to the beginning of the last row we finished */ + if(paint) + mask = ~((BmUnit)0); + else + mask = 0; + /* goto next row */ + a = (BmUnit *)(r + bm->stride); + /* deal with entirely with/black rows */ + while(count >= w) { + /* count number of atoms in a row */ + i = ROUND(w, BITMAP_BITS); + while(i-- > 0) + *a++ = mask; + count -= w; + row++; + } + inrow = w; + } + if(count > 0) + bitmap_set_row(bm, row, w - inrow, count, paint); + inrow -= count; + paint = !paint; + } + if(row != h || inrow != w) { + mdvi_error(_("Bad PK file: More bits than required\n")); + bitmap_destroy(bm); + return NULL; + } + return bm; +} + +static BITMAP *get_char(FILE *p, int w, int h, int flags) +{ + /* check if dyn_f == 14 */ + if(((flags >> 4) & 0xf) == 14) + return get_bitmap(p, w, h, flags); + else + return get_packed(p, w, h, flags); +} + +/* supports any number of characters in a font */ +static int pk_load_font(DviParams *unused, DviFont *font) +{ + int i; + int flag_byte; + int loc, hic, maxch; + Int32 checksum; + FILE *p; +#ifndef NODEBUG + char s[256]; +#endif + long alpha, beta, z; + + font->chars = xnalloc(DviFontChar, 256); + p = font->in; + memzero(font->chars, 256 * sizeof(DviFontChar)); + for(i = 0; i < 256; i++) + font->chars[i].offset = 0; + + /* check the preamble */ + loc = fuget1(p); hic = fuget1(p); + if(loc != PK_PRE || hic != PK_ID) + goto badpk; + i = fuget1(p); +#ifndef NODEBUG + for(loc = 0; loc < i; loc++) + s[loc] = fuget1(p); + s[loc] = 0; + DEBUG((DBG_FONTS, "(pk) %s: %s\n", font->fontname, s)); +#else + fseek(in, (long)i, SEEK_CUR); +#endif + /* get the design size */ + font->design = fuget4(p); + /* get the checksum */ + checksum = fuget4(p); + if(checksum && font->checksum && font->checksum != checksum) { + mdvi_warning(_("%s: checksum mismatch (expected %u, got %u)\n"), + font->fontname, font->checksum, checksum); + } else if(!font->checksum) + font->checksum = checksum; + /* skip pixel per point ratios */ + fuget4(p); + fuget4(p); + if(feof(p)) + goto badpk; + + /* now start reading the font */ + loc = 256; hic = -1; maxch = 256; + + /* initialize alpha and beta for TFM width computation */ + TFMPREPARE(font->scale, z, alpha, beta); + + while((flag_byte = fuget1(p)) != PK_POST) { + if(feof(p)) + break; + if(flag_byte >= PK_CMD_START) { + switch(flag_byte) { + case PK_X1: + case PK_X2: + case PK_X3: + case PK_X4: { +#ifndef NODEBUG + char *t; + int n; + + i = fugetn(p, flag_byte - PK_X1 + 1); + if(i < 256) + t = &s[0]; + else + t = mdvi_malloc(i + 1); + for(n = 0; n < i; n++) + t[n] = fuget1(p); + t[n] = 0; + DEBUG((DBG_SPECIAL, "(pk) %s: Special \"%s\"\n", + font->fontname, t)); + if(t != &s[0]) + mdvi_free(t); +#else + i = fugetn(p, flag_byte - PK_X1 + 1); + while(i-- > 0) + fuget1(p); +#endif + break; + } + case PK_Y: + i = fuget4(p); + DEBUG((DBG_SPECIAL, "(pk) %s: MF special %u\n", + font->fontname, (unsigned)i)); + break; + case PK_POST: + case PK_NOOP: + break; + case PK_PRE: + mdvi_error(_("%s: unexpected preamble\n"), font->fontname); + goto error; + } + } else { + int pl; + int cc; + int w, h; + int x, y; + int offset; + long tfm; + + switch(flag_byte & 0x7) { + case 7: + pl = fuget4(p); + cc = fuget4(p); + offset = ftell(p) + pl; + tfm = fuget4(p); + fsget4(p); /* skip dx */ + fsget4(p); /* skip dy */ + w = fuget4(p); + h = fuget4(p); + x = fsget4(p); + y = fsget4(p); + break; + case 4: + case 5: + case 6: + pl = (flag_byte % 4) * 65536 + fuget2(p); + cc = fuget1(p); + offset = ftell(p) + pl; + tfm = fuget3(p); + fsget2(p); /* skip dx */ + /* dy assumed 0 */ + w = fuget2(p); + h = fuget2(p); + x = fsget2(p); + y = fsget2(p); + break; + default: + pl = (flag_byte % 4) * 256 + fuget1(p); + cc = fuget1(p); + offset = ftell(p) + pl; + tfm = fuget3(p); + fsget1(p); /* skip dx */ + /* dy assumed 0 */ + w = fuget1(p); + h = fuget1(p); + x = fsget1(p); + y = fsget1(p); + } + if(feof(p)) + break; + if(cc < loc) + loc = cc; + if(cc > hic) + hic = cc; + if(cc > maxch) { + font->chars = xresize(font->chars, + DviFontChar, cc + 16); + for(i = maxch; i < cc + 16; i++) + font->chars[i].offset = 0; + maxch = cc + 16; + } + font->chars[cc].code = cc; + font->chars[cc].flags = flag_byte; + font->chars[cc].offset = ftell(p); + font->chars[cc].width = w; + font->chars[cc].height = h; + font->chars[cc].glyph.data = NULL; + font->chars[cc].x = x; + font->chars[cc].y = y; + font->chars[cc].glyph.x = x; + font->chars[cc].glyph.y = y; + font->chars[cc].glyph.w = w; + font->chars[cc].glyph.h = h; + font->chars[cc].grey.data = NULL; + font->chars[cc].shrunk.data = NULL; + font->chars[cc].tfmwidth = TFMSCALE(z, tfm, alpha, beta); + font->chars[cc].loaded = 0; + fseek(p, (long)offset, SEEK_SET); + } + } + if(flag_byte != PK_POST) { + mdvi_error(_("%s: unexpected end of file (no postamble)\n"), + font->fontname); + goto error; + } + while((flag_byte = fuget1(p)) != EOF) { + if(flag_byte != PK_NOOP) { + mdvi_error(_("invalid PK file! (junk in postamble)\n")); + goto error; + } + } + + /* resize font char data */ + if(loc > 0 || hic < maxch-1) { + memmove(font->chars, font->chars + loc, + (hic - loc + 1) * sizeof(DviFontChar)); + font->chars = xresize(font->chars, + DviFontChar, hic - loc + 1); + } + font->loc = loc; + font->hic = hic; + return 0; + +badpk: + mdvi_error(_("%s: File corrupted, or not a PK file\n"), font->fontname); +error: + mdvi_free(font->chars); + font->chars = NULL; + font->loc = font->hic = 0; + return -1; +} + +static int pk_font_get_glyph(DviParams *params, DviFont *font, int code) +{ + DviFontChar *ch; + + if((ch = FONTCHAR(font, code)) == NULL) + return -1; + + if(ch->offset == 0) + return -1; + DEBUG((DBG_GLYPHS, "(pk) loading glyph for character %d (%dx%d) in font `%s'\n", + code, ch->width, ch->height, font->fontname)); + if(font->in == NULL && font_reopen(font) < 0) + return -1; + if(!ch->width || !ch->height) { + /* this happens for ` ' (ASCII 32) in some fonts */ + ch->glyph.x = ch->x; + ch->glyph.y = ch->y; + ch->glyph.w = ch->width; + ch->glyph.h = ch->height; + ch->glyph.data = NULL; + return 0; + } + if(fseek(font->in, ch->offset, SEEK_SET) == -1) + return -1; + ch->glyph.data = get_char(font->in, + ch->width, ch->height, ch->flags); + if(ch->glyph.data) { + /* restore original settings */ + ch->glyph.x = ch->x; + ch->glyph.y = ch->y; + ch->glyph.w = ch->width; + ch->glyph.h = ch->height; + } else + return -1; + ch->loaded = 1; + return 0; +} diff --git a/backend/dvi/mdvi-lib/private.h b/backend/dvi/mdvi-lib/private.h new file mode 100644 index 00000000..9f89dc70 --- /dev/null +++ b/backend/dvi/mdvi-lib/private.h @@ -0,0 +1,65 @@ +/* + * Copyright (C) 2000, Matias Atria + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +#ifndef _MDVI_PRIVATE_H +#define _MDVI_PRIVATE_H 1 + +#define HAVE_PROTOTYPES 1 + +#if STDC_HEADERS +# /* kpathsea's headers (wrongly!) redefine strchr() and strrchr() to +# non ANSI C functions if HAVE_STRCHR and HAVE_STRRCHR are not defined. +# */ +# ifndef HAVE_STRCHR +# define HAVE_STRCHR +# endif +# ifndef HAVE_STRRCHR +# define HAVE_STRRCHR +# endif +#endif + +#include <kpathsea/debug.h> +#include <kpathsea/tex-file.h> +#include <kpathsea/tex-glyph.h> +#include <kpathsea/cnf.h> +#include <kpathsea/proginit.h> +#include <kpathsea/progname.h> +#include <kpathsea/tex-make.h> +#include <kpathsea/lib.h> + +#define ISSP(p) (*(p) == ' ' || *(p) == '\t') +#define SKIPSP(p) while(ISSP(p)) p++ +#define SKIPNSP(p) while(*(p) && !ISSP(p)) p++ + +#ifdef ENABLE_NLS +#include <libintl.h> +#define _(x) gettext(x) +#define _G(x) x +#else +#define _(x) x +#define _G(x) x +#endif /* ENABLE_NLS */ + +#if defined (__i386__) && defined (__GNUC__) && __GNUC__ >= 2 +#define _BREAKPOINT() do { __asm__ __volatile__ ("int $03"); } while(0) +#elif defined (__alpha__) && defined (__GNUC__) && __GNUC__ >= 2 +#define _BREAKPOINT() do { __asm__ __volatile__ ("bpt"); } while(0) +#else /* !__i386__ && !__alpha__ */ +#define _BREAKPOINT() +#endif /* __i386__ */ + +#endif /* _MDVI_PRIVATE_H */ diff --git a/backend/dvi/mdvi-lib/setup.c b/backend/dvi/mdvi-lib/setup.c new file mode 100644 index 00000000..ba0c545d --- /dev/null +++ b/backend/dvi/mdvi-lib/setup.c @@ -0,0 +1,48 @@ +/* + * Copyright (C) 2000, Matias Atria + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#include <config.h> +#include <stdlib.h> +#include <string.h> +#include <errno.h> +#include <ctype.h> + +#include "mdvi.h" +#include "private.h" + +void mdvi_init_kpathsea(const char *program, + const char *mfmode, const char *font, int dpi, + const char *texmfcnf) +{ + const char *p; + + /* Stop meaningless output generation. */ + kpse_make_tex_discard_errors = FALSE; + + p = strrchr(program, '/'); + p = (p ? p + 1 : program); + kpse_set_program_name(program, p); + kpse_init_prog(p, dpi, mfmode, font); + kpse_set_program_enabled(kpse_any_glyph_format, 1, kpse_src_compile); + kpse_set_program_enabled(kpse_pk_format, 1, kpse_src_compile); + kpse_set_program_enabled(kpse_tfm_format, 1, kpse_src_compile); + kpse_set_program_enabled(kpse_ofm_format, 1, kpse_src_compile); + if (texmfcnf != NULL) + xputenv("TEXMFCNF", texmfcnf); +} + diff --git a/backend/dvi/mdvi-lib/sp-epsf.c b/backend/dvi/mdvi-lib/sp-epsf.c new file mode 100644 index 00000000..703a9c8c --- /dev/null +++ b/backend/dvi/mdvi-lib/sp-epsf.c @@ -0,0 +1,311 @@ +/* + * Copyright (C) 2000, Matias Atria + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +/* postscript specials */ + +#include <config.h> +#include <stdlib.h> +#include <string.h> +#include <sys/stat.h> +#include <unistd.h> + +#include "mdvi.h" +#include "private.h" + +typedef struct { + double ox; + double oy; + double bw; + double bh; + double angle; +} EpsfBox; + +#define LLX 0 +#define LLY 1 +#define URX 2 +#define URY 3 +#define RWI 4 +#define RHI 5 +#define HOFF 6 +#define VOFF 7 +#define HSIZE 8 +#define VSIZE 9 +#define HSCALE 10 +#define VSCALE 11 +#define ANGLE 12 +#define CLIP 13 + +void epsf_special __PROTO((DviContext *dvi, char *prefix, char *arg)); + +/* Note: the given strings are modified in place */ +static char *parse_epsf_special(EpsfBox *box, char **ret, + char *prefix, char *arg) +{ + static struct { + char *name; + int has_arg; + char *value; + } keys[] = { + {"llx", 1, "0"}, + {"lly", 1, "0"}, + {"urx", 1, "0"}, + {"ury", 1, "0"}, + {"rwi", 1, "0"}, + {"rhi", 1, "0"}, + {"hoffset", 1, "0"}, + {"voffset", 1, "0"}, + {"hsize", 1, "612"}, + {"vsize", 1, "792"}, + {"hscale", 1, "100"}, + {"vscale", 1, "100"}, + {"angle", 1, "0"}, + {"clip", 0, "0"} + }; +#define NKEYS (sizeof(keys) / sizeof(keys[0])) + char *ptr; + char *filename; + int quoted; + double value[NKEYS]; + Uchar present[NKEYS]; + Buffer buffer; + char *name; + int i; + double originx; + double originy; + double hsize; + double vsize; + double hscale; + double vscale; + + /* this special has the form + * ["]file.ps["] [key=valye]* + */ + + /* scan the filename */ + while(*arg == ' ' || *arg == '\t') + arg++; + + /* make a copy of the string */ + ptr = arg; + + if(*ptr == '"') + for(name = ++ptr; *ptr && *ptr != '"'; ptr++); + else + for(name = ptr; *ptr && *ptr != ' ' && *ptr != '\t'; ptr++); + if(ptr == name) + return NULL; + *ptr++ = 0; + filename = name; + + /* reset values to defaults */ + for(i = 0; i < NKEYS; i++) { + value[i] = atof(keys[i].value); + present[i] = 0; + } + + buff_init(&buffer); + buff_add(&buffer, "@beginspecial ", 0); + + quoted = 0; + while(*ptr) { + const char *keyname; + char *val; + char *p; + + while(*ptr == ' ' || *ptr == '\t') + ptr++; + keyname = ptr; + + /* get the whole key=value pair */ + for(; *ptr && *ptr != ' ' && *ptr != '\t'; ptr++); + + if(*ptr) *ptr++ = 0; + /* now we shouldn't touch `ptr' anymore */ + + /* now work on this pair */ + p = strchr(keyname, '='); + if(p == NULL) + val = NULL; + else { + *p++ = 0; + if(*p == '"') { + val = ++p; + /* skip until closing quote */ + while(*p && *p != '"') + p++; + if(*p != '"') + mdvi_warning( + _("%s: malformed value for key `%s'\n"), + filename, keyname); + } else + val = p; + } + + /* lookup the key */ + for(i = 0; i < NKEYS; i++) + if(STRCEQ(keys[i].name, keyname)) + break; + if(i == NKEYS) { + mdvi_warning(_("%s: unknown key `%s' ignored\n"), + filename, keyname); + continue; + } + if(keys[i].has_arg && val == NULL) { + mdvi_warning(_("%s: no argument for key `%s', using defaults\n"), + filename, keyname); + val = keys[i].value; + } else if(!keys[i].has_arg && val) { + mdvi_warning(_("%s: argument `%s' ignored for key `%s'\n"), + filename, val, keyname); + val = NULL; + } + if(val) + value[i] = atof(val); + + /* put the argument */ + buff_add(&buffer, val, 0); + buff_add(&buffer, " @", 2); + buff_add(&buffer, keyname, 0); + buff_add(&buffer, " ", 1); + + /* remember that this option was given */ + present[i] = 0xff; + } + buff_add(&buffer, " @setspecial", 0); + + /* now compute the bounding box (code comes from dvips) */ + originx = 0; + originy = 0; + hscale = 1; + vscale = 1; + hsize = 0; + vsize = 0; + + if(present[HSIZE]) + hsize = value[HSIZE]; + if(present[VSIZE]) + vsize = value[VSIZE]; + if(present[HOFF]) + originx = value[HOFF]; + if(present[VOFF]) + originy = value[VOFF]; + if(present[HSCALE]) + hscale = value[HSCALE] / 100.0; + if(present[VSCALE]) + vscale = value[VSCALE] / 100.0; + if(present[URX] && present[LLX]) + hsize = value[URX] - value[LLX]; + if(present[URY] && present[LLY]) + vsize = value[URY] - value[LLY]; + if(present[RWI] || present[RHI]) { + if(present[RWI] && !present[RHI]) + hscale = vscale = value[RWI] / (10.0 * hsize); + else if(present[RHI] && !present[RWI]) + hscale = vscale = value[RHI] / (10.0 * vsize); + else { + hscale = value[RWI] / (10.0 * hsize); + vscale = value[RHI] / (10.0 * vsize); + } + } + + box->ox = originx; + box->oy = originy; + box->bw = hsize * hscale; + box->bh = vsize * vscale; + box->angle = value[ANGLE]; + + *ret = buffer.data; + + return filename; +} + +void epsf_special(DviContext *dvi, char *prefix, char *arg) +{ + char *file; + char *special; + char *psfile; + char *tmp; + EpsfBox box = {0, 0, 0, 0}; + int x, y; + int w, h; + double xf, vf; + struct stat buf; + + file = parse_epsf_special(&box, &special, prefix, arg); + if (file != NULL) + mdvi_free (special); + + xf = dvi->params.dpi * dvi->params.mag / (72.0 * dvi->params.hshrink); + vf = dvi->params.vdpi * dvi->params.mag / (72.0 * dvi->params.vshrink); + w = FROUND(box.bw * xf); + h = FROUND(box.bh * vf); + x = FROUND(box.ox * xf) + dvi->pos.hh; + y = FROUND(box.oy * vf) + dvi->pos.vv - h + 1; + + if (!file || !dvi->device.draw_ps) { + dvi->device.draw_rule (dvi, x, y, w, h, 0); + return; + } + + if (file[0] == '/') { /* Absolute path */ + if (stat (file, &buf) == 0) + dvi->device.draw_ps (dvi, file, x, y, w, h); + else + dvi->device.draw_rule (dvi, x, y, w, h, 0); + return; + } + + tmp = mdvi_strrstr (dvi->filename, "/"); + if (tmp) { /* Document directory */ + int path_len = strlen (dvi->filename) - strlen (tmp + 1); + int file_len = strlen (file); + + psfile = mdvi_malloc (path_len + file_len + 1); + psfile[0] = '\0'; + strncat (psfile, dvi->filename, path_len); + strncat (psfile, file, file_len); + + if (stat (psfile, &buf) == 0) { + dvi->device.draw_ps (dvi, psfile, x, y, w, h); + mdvi_free (psfile); + + return; + } + + mdvi_free (psfile); + } + + psfile = mdvi_build_path_from_cwd (file); + if (stat (psfile, &buf) == 0) { /* Current working dir */ + dvi->device.draw_ps (dvi, psfile, x, y, w, h); + mdvi_free (psfile); + + return; + } + + mdvi_free (psfile); + + psfile = kpse_find_pict (file); + if (psfile) { /* kpse */ + dvi->device.draw_ps (dvi, psfile, x, y, w, h); + } else { + dvi->device.draw_rule(dvi, x, y, w, h, 0); + } + + free (psfile); +} diff --git a/backend/dvi/mdvi-lib/special.c b/backend/dvi/mdvi-lib/special.c new file mode 100644 index 00000000..e4832544 --- /dev/null +++ b/backend/dvi/mdvi-lib/special.c @@ -0,0 +1,249 @@ +/* + * Copyright (C) 2000, Matias Atria + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#include <config.h> +#include <ctype.h> +#include <string.h> + +#include "mdvi.h" +#include "private.h" + +#if defined(WITH_REGEX_SPECIALS) && defined(HAVE_REGEX_H) +#include <regex.h> +#endif + +typedef struct _DviSpecial { + struct _DviSpecial *next; + struct _DviSpecial *prev; + char *label; + char *prefix; + size_t plen; +#ifdef WITH_REGEX_SPECIALS + regex_t reg; + int has_reg; +#endif + DviSpecialHandler handler; +} DviSpecial; + +static ListHead specials = {NULL, NULL, 0}; + +#define SPECIAL(x) \ + void x __PROTO((DviContext *, const char *, const char *)) + +static SPECIAL(sp_layer); +extern SPECIAL(epsf_special); +extern SPECIAL(do_color_special); + +static struct { + char *label; + char *prefix; + char *regex; + DviSpecialHandler handler; +} builtins[] = { + {"Layers", "layer", NULL, sp_layer}, + {"EPSF", "psfile", NULL, epsf_special} +}; +#define NSPECIALS (sizeof(builtins) / sizeof(builtins[0])) +static int registered_builtins = 0; + +static void register_builtin_specials(void) +{ + int i; + + ASSERT(registered_builtins == 0); + registered_builtins = 1; + for(i = 0; i < NSPECIALS; i++) + mdvi_register_special( + builtins[i].label, + builtins[i].prefix, + builtins[i].regex, + builtins[i].handler, + 1 /* replace if exists */); +} + +static DviSpecial *find_special_prefix(const char *prefix) +{ + DviSpecial *sp; + + /* should have a hash table here, but I'm so lazy */ + for(sp = (DviSpecial *)specials.head; sp; sp = sp->next) { + if(STRCEQ(sp->prefix, prefix)) + break; + } + return sp; +} + +int mdvi_register_special(const char *label, const char *prefix, + const char *regex, DviSpecialHandler handler, int replace) +{ + DviSpecial *sp; + int newsp = 0; + + if(!registered_builtins) + register_builtin_specials(); + + sp = find_special_prefix(prefix); + if(sp == NULL) { + sp = xalloc(DviSpecial); + sp->prefix = mdvi_strdup(prefix); + newsp = 1; + } else if(!replace) + return -1; + else { + mdvi_free(sp->label); + sp->label = NULL; + } + +#ifdef WITH_REGEX_SPECIALS + if(!newsp && sp->has_reg) { + regfree(&sp->reg); + sp->has_reg = 0; + } + if(regex && regcomp(&sp->reg, regex, REG_NOSUB) != 0) { + if(newsp) { + mdvi_free(sp->prefix); + mdvi_free(sp); + } + return -1; + } + sp->has_reg = (regex != NULL); +#endif + sp->handler = handler; + sp->label = mdvi_strdup(label); + sp->plen = strlen(prefix); + if(newsp) + listh_prepend(&specials, LIST(sp)); + DEBUG((DBG_SPECIAL, + "New \\special handler `%s' with prefix `%s'\n", + label, prefix)); + return 0; +} + +int mdvi_unregister_special(const char *prefix) +{ + DviSpecial *sp; + + sp = find_special_prefix(prefix); + if(sp == NULL) + return -1; + mdvi_free(sp->prefix); +#ifdef WITH_REGEX_SPECIALS + if(sp->has_reg) + regfree(&sp->reg); +#endif + listh_remove(&specials, LIST(sp)); + mdvi_free(sp); + return 0; +} + +#define IS_PREFIX_DELIMITER(x) (strchr(" \t\n:=", (x)) != NULL) + +int mdvi_do_special(DviContext *dvi, char *string) +{ + char *prefix; + char *ptr; + DviSpecial *sp; + + if(!registered_builtins) { + } + + if(!string || !*string) + return 0; + + /* skip leading spaces */ + while(*string && isspace(*string)) + string++; + + DEBUG((DBG_SPECIAL, "Looking for a handler for `%s'\n", string)); + + /* now try to find a match */ + ptr = string; + for(sp = (DviSpecial *)specials.head; sp; sp = sp->next) { +#ifdef WITH_REGEX_SPECIALS + if(sp->has_reg && !regexec(&sp->reg, ptr, 0, 0, 0)) + break; +#endif + /* check the prefix */ + if(STRNCEQ(sp->prefix, ptr, sp->plen)) { + ptr += sp->plen; + break; + } + } + + if(sp == NULL) { + DEBUG((DBG_SPECIAL, "None found\n")); + return -1; + } + + /* extract the prefix */ + if(ptr == string) { + prefix = NULL; + DEBUG((DBG_SPECIAL, + "REGEX match with `%s' (arg `%s')\n", + sp->label, ptr)); + } else { + if(*ptr) *ptr++ = 0; + prefix = string; + DEBUG((DBG_SPECIAL, + "PREFIX match with `%s' (prefix `%s', arg `%s')\n", + sp->label, prefix, ptr)); + } + + /* invoke the handler */ + sp->handler(dvi, prefix, ptr); + + return 0; +} + +void mdvi_flush_specials(void) +{ + DviSpecial *sp, *list; + + + for(list = (DviSpecial *)specials.head; (sp = list); ) { + list = sp->next; + if(sp->prefix) mdvi_free(sp->prefix); + if(sp->label) mdvi_free(sp->label); +#ifdef WITH_REGEX_SPECIALS + if(sp->has_reg) + regfree(&sp->reg); +#endif + mdvi_free(sp); + } + specials.head = NULL; + specials.tail = NULL; + specials.count = 0; +} + +/* some builtin specials */ + +void sp_layer(DviContext *dvi, const char *prefix, const char *arg) +{ + if(STREQ("push", arg)) + dvi->curr_layer++; + else if(STREQ("pop", arg)) { + if(dvi->curr_layer) + dvi->curr_layer--; + else + mdvi_warning(_("%s: tried to pop top level layer\n"), + dvi->filename); + } else if(STREQ("reset", arg)) + dvi->curr_layer = 0; + DEBUG((DBG_SPECIAL, "Layer level: %d\n", dvi->curr_layer)); +} + diff --git a/backend/dvi/mdvi-lib/sysdeps.h b/backend/dvi/mdvi-lib/sysdeps.h new file mode 100644 index 00000000..c77d7651 --- /dev/null +++ b/backend/dvi/mdvi-lib/sysdeps.h @@ -0,0 +1,116 @@ +/* + * Copyright (C) 2000, Matias Atria + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ +#ifndef _SYSDEP_H +#define _SYSDEP_H 1 + +/* + * The purpose of this file is to define symbols that describe the + * system-dependent features we use. Namely, byte order, native integer + * types of various sizes, and safe pointer<->integer conversion. + */ + +#include "config.h" + +#ifdef WORDS_BIGENDIAN +#define WORD_BIG_ENDIAN 1 +#else +#define WORD_LITTLE_ENDIAN 1 +#endif + +typedef unsigned long Ulong; +typedef unsigned int Uint; +typedef unsigned short Ushort; +typedef unsigned char Uchar; + +/* this one's easy */ +typedef unsigned char Uint8; +typedef char Int8; + +/* define a datatype for 32bit integers (either int or long) */ +#if SIZEOF_LONG == 4 +typedef unsigned long Uint32; +typedef long Int32; +#else /* SIZEOF_LONG != 4 */ +#if SIZEOF_INT == 4 +typedef unsigned int Uint32; +typedef int Int32; +#else /* SIZEOF_INT != 4 */ +#ifdef __cplusplus +#include "No.appropriate.32bit.native.type.found.Fix.sysdeps.h" +#else +#error No appropriate 32bit native type found. Fix sysdeps.h +#endif /* ! __cplusplus */ +#endif /* SIZEOF_INT != 4 */ +#endif /* SIZEOF_LONG != 4 */ + +/* now 16bit integers (one of long, int or short) */ +#if SIZEOF_SHORT == 2 +typedef unsigned short Uint16; +typedef short Int16; +#else /* SIZEOF_SHORT != 2 */ +#if SIZEOF_INT == 2 +typedef unsigned int Uint16; +typedef short Int16; +#else /* SIZEOF_INT != 2 */ +#ifdef __cplusplus +#include "No.appropriate.16bit.native.type.found.Fix.sysdeps.h" +#else +#error No appropriate 16bit native type found. Fix sysdeps.h +#endif /* ! __cplusplus */ +#endif /* SIZEOF_INT != 2 */ +#endif /* SIZEOF_SHORT != 2 */ + +/* + * An integer type to convert to and from pointers safely. All we do here is + * look for an integer type with the same size as a pointer. + */ +#if SIZEOF_LONG == SIZEOF_VOID_P +typedef unsigned long UINT; +typedef long INT; +#else +#if SIZEOF_INT == SIZEOF_VOID_P +typedef unsigned int UINT; +typedef int INT; +#else +#if SIZEOF_SHORT == SIZEOF_VOID_P +typedef unsigned short UINT; +typedef short INT; +#else +#ifdef __cplusplus +#include "No.native.pointer-compatible.integer.type.found.Fix.sysdeps.h" +#else +#error No native pointer-compatible integer type found. Fix sysdeps.h +#endif +#endif +#endif +#endif + +/* nice, uh? */ +typedef void *Pointer; + +/* macros to do the safe pointer <-> integer conversions */ +#define Ptr2Int(x) ((INT)((Pointer)(x))) +#define Int2Ptr(x) ((Pointer)((INT)(x))) + +#ifdef _NO_PROTO +#define __PROTO(x) () +#else +#define __PROTO(x) x +#endif + +#endif /* _SYSDEP_H */ diff --git a/backend/dvi/mdvi-lib/t1.c b/backend/dvi/mdvi-lib/t1.c new file mode 100644 index 00000000..bc910ba6 --- /dev/null +++ b/backend/dvi/mdvi-lib/t1.c @@ -0,0 +1,630 @@ +/* + * Copyright (C) 2000, Matias Atria + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +/* + * Type1 font support for MDVI + * + * We use T1lib only as a rasterizer, not to draw glyphs. + */ + +#include <config.h> +#include "mdvi.h" + +#ifdef WITH_TYPE1_FONTS + +#include <stdio.h> +#include <t1lib.h> +#include "private.h" + +static int t1lib_initialized = 0; + +typedef struct t1info { + struct t1info *next; + struct t1info *prev; + char *fontname; /* (short) name of this font */ + int t1id; /* T1lib's id for this font */ + int hasmetrics; /* have we processed this font? */ + TFMInfo *tfminfo; /* TFM data is shared */ + DviFontMapInfo mapinfo; + DviEncoding *encoding; +} T1Info; + +static void t1_font_remove __PROTO((T1Info *)); +static int t1_load_font __PROTO((DviParams *, DviFont *)); +static int t1_font_get_glyph __PROTO((DviParams *, DviFont *, int)); +static void t1_font_shrink_glyph + __PROTO((DviContext *, DviFont *, DviFontChar *, DviGlyph *)); +static void t1_free_data __PROTO((DviFont *)); +static void t1_reset_font __PROTO((DviFont *)); +static char *t1_lookup_font __PROTO((const char *, Ushort *, Ushort *)); + +/* only symbol exported by this file */ +DviFontInfo t1_font_info = { + "Type1", + 1, /* scaling supported by format */ + t1_load_font, + t1_font_get_glyph, + t1_font_shrink_glyph, + mdvi_shrink_glyph_grey, + t1_free_data, + t1_reset_font, + t1_lookup_font, /* lookup */ + kpse_type1_format, + NULL +}; + +/* this seems good enough for most DVI files */ +#define T1_HASH_SIZE 31 + +/* If these parameters change, we must delete all size information + * in all fonts, and reset the device resolutions in T1lib */ +static int t1lib_xdpi = -1; +static int t1lib_ydpi = -1; + +static ListHead t1fonts = {NULL, NULL, 0}; +static DviHashTable t1hash; + +/* Type1 fonts need their own `lookup' function. Here is how it works: + * First we try to find the font by its given name. If that fails, we + * query the font maps. A typical font map entry may contain the line + * + * ptmr8rn Times-Roman ".82 ExtendFont TeXBase1Encoding ReEncodeFont" <8r.enc <ptmr + * + * which means: If you're looking for the font `ptmr8rn' load `Times-Roman' + * which is in `ptmr' instead, and extend it by 0.82 points, then reencode + * it with the vector TeXBase1Encoding from the file `8r.enc'. This will + * fail if the entry looks like this: + * + * ptmr8rn Times-Roman ".82 ExtendFont TeXBase1Encoding ReEncodeFont" <8r.enc + * + * because to deal with this we would need to be able to locate the font file + * for the `Times-Roman' font ourselves, and that's beyond the scope of mdvi. + * But hey, we tried hard. + */ +char *t1_lookup_font(const char *name, Ushort *hdpi, Ushort *vdpi) +{ + char *filename; + char *newname; + const char *ext; + DviFontMapInfo info; + + DEBUG((DBG_TYPE1, "(t1) looking for `%s'\n", name)); + + /* first let's try the font we were asked for */ + filename = kpse_find_file(name, kpse_type1_format, 1); + if(filename != NULL) { + /* we got it */ + return filename; + } + + DEBUG((DBG_TYPE1, "(t1) %s: not found, querying font maps\n", name)); + /* now query the fontmap */ + if(mdvi_query_fontmap(&info, name) < 0) { + /* it's not there either */ + return NULL; + } + + /* check what we got */ + if(info.fullfile) { + DEBUG((DBG_TYPE1, "(t1) %s: found `%s' (cached)\n", + name, info.fullfile)); + /* this is a cached lookup */ + return mdvi_strdup(info.fullfile); + } + + /* no file associated to this font? */ + if(info.fontfile == NULL) + return info.psname ? mdvi_ps_find_font(info.psname) : NULL; + + /* let's extract the extension */ + ext = file_extension(info.fontfile); + if(ext && !STREQ(ext, "pfa") && !STREQ(ext, "pfb")) { + DEBUG((DBG_TYPE1, + "(t1) %s: associated name `%s' is not Type1\n", + name, info.fontfile)); + /* it's not a Type1 font */ + return NULL; + } + + /* get the `base' name */ + if(ext) { + newname = mdvi_strdup(name); + newname[ext - info.fontfile - 1] = 0; + } else + newname = (char *)name; /* we don't modify this */ + + /* look it up */ + DEBUG((DBG_TYPE1, "(t1) looking for `%s' on behalf of `%s'\n", + newname, name)); + filename = kpse_find_file(newname, kpse_type1_format, 1); + + /* we don't need this anymore */ + if(newname != name) + mdvi_free(newname); + if(filename == NULL) { + DEBUG((DBG_TYPE1, "(t1) %s: not found\n", name)); + return NULL; + } + + DEBUG((DBG_TYPE1, "(t1) %s: found as `%s'\n", name, filename)); + /* got it! let's remember this */ + mdvi_add_fontmap_file(name, filename); + return filename; +} + +static void t1_reset_resolution(int xdpi, int ydpi) +{ + int i; + int nfonts; + + DEBUG((DBG_TYPE1, "(t1) resetting device resolution (current: (%d,%d))\n", + t1lib_xdpi, t1lib_ydpi)); +#if T1LIB_VERSION < 5 + nfonts = T1_Get_no_fonts(); +#else + nfonts = T1_GetNoFonts(); +#endif + + for(i = 0; i < nfonts; i++) + T1_DeleteAllSizes(i); + /* reset device resolutions */ + if(T1_SetDeviceResolutions((float)xdpi, (float)ydpi) < 0) + mdvi_warning(_("(t1) failed to reset device resolution\n")); + else + DEBUG((DBG_TYPE1, + "(t1) reset successful, new resolution is (%d, %d)\n", + xdpi, ydpi)); + t1lib_xdpi = xdpi; + t1lib_ydpi = ydpi; +} + +static void t1_reset_font(DviFont *font) +{ + T1Info *info = (T1Info *)font->private; + + if(info == NULL) + return; + DEBUG((DBG_FONTS, "(t1) resetting font `%s'\n", font->fontname)); + /* just mark the font as not having metric info. It will be reset + * automatically later */ + info->hasmetrics = 0; +} + +static void t1_transform_font(T1Info *info) +{ + if(!info->hasmetrics && info->encoding != NULL) { + DEBUG((DBG_TYPE1, "(t1) %s: encoding with vector `%s'\n", + info->fontname, info->encoding->name)); + T1_DeleteAllSizes(info->t1id); + if(T1_ReencodeFont(info->t1id, info->encoding->vector) < 0) + mdvi_warning(_("%s: could not encode font\n"), info->fontname); + } + if(info->mapinfo.slant) { + DEBUG((DBG_TYPE1, "(t1) %s: slanting by %.3f\n", + info->fontname, + MDVI_FMAP_SLANT(&info->mapinfo))); + T1_SlantFont(info->t1id, + MDVI_FMAP_SLANT(&info->mapinfo)); + } + if(info->mapinfo.extend) { + DEBUG((DBG_TYPE1, "(t1) %s: extending by %.3f\n", + info->fontname, + MDVI_FMAP_EXTEND(&info->mapinfo))); + T1_ExtendFont(info->t1id, + MDVI_FMAP_EXTEND(&info->mapinfo)); + } +} + +/* if this function is called, we really need this font */ +static int t1_really_load_font(DviParams *params, DviFont *font, T1Info *info) +{ + int i; + T1Info *old; + int t1id; + int copied; + int status; + + DEBUG((DBG_TYPE1, "(t1) really_load_font(%s)\n", info->fontname)); + + /* if the parameters changed, reset T1lib */ + if(t1lib_xdpi != params->dpi || t1lib_ydpi != params->vdpi) + t1_reset_resolution(params->dpi, params->vdpi); + + /* if we already have a T1lib id, do nothing */ + if(info->t1id != -1) { + info->hasmetrics = 1; + /* apply slant and extend again */ + t1_transform_font(info); + return 0; + } + + /* before we even attempt to load the font, make sure we have metric + * data for it */ + info->tfminfo = mdvi_ps_get_metrics(info->fontname); + if(info->tfminfo == NULL) { + DEBUG((DBG_FONTS, + "(t1) %s: no metric data, font ignored\n", + info->fontname)); + goto t1_error; + } + /* fix this */ + font->design = info->tfminfo->design; + + /* check if we have a font with this name (maybe at a different size) */ + old = (T1Info *)mdvi_hash_lookup(&t1hash, (unsigned char *)info->fontname); + if(old == info) { + /* let's avoid confusion */ + old = NULL; + } + if(old && old->t1id != -1) { + /* let's take advantage of T1lib's font sharing */ + t1id = T1_CopyFont(old->t1id); + DEBUG((DBG_TYPE1, "(t1) %s -> %d (CopyFont)\n", + info->fontname, t1id)); + copied = 1; + } else { + t1id = T1_AddFont(font->filename); + DEBUG((DBG_TYPE1, "(t1) %s -> %d (AddFont)\n", + info->fontname, t1id)); + copied = 0; + } + if(t1id < 0) + goto t1_error; + info->t1id = t1id; + + /* + * a minor optimization: If the old font in the hash table has + * not been loaded yet, replace it by this one, so we can use + * CopyFont later. + */ + if(old && old->t1id == -1) { + DEBUG((DBG_TYPE1, "(t1) font `%s' exchanged in hash table\n", + info->fontname)); + mdvi_hash_remove(&t1hash, (unsigned char *)old->fontname); + mdvi_hash_add(&t1hash, (unsigned char *)info->fontname, + info, MDVI_HASH_UNCHECKED); + } + + /* now let T1lib load it */ + if(!copied && T1_LoadFont(info->t1id) < 0) { + DEBUG((DBG_TYPE1, "(t1) T1_LoadFont(%d) failed with error %d\n", + info->t1id, T1_errno)); + goto t1_error; + } + DEBUG((DBG_TYPE1, "(t1) T1_LoadFont(%d) -> Ok\n", info->t1id)); + + /* get information from the fontmap */ + status = mdvi_query_fontmap(&info->mapinfo, info->fontname); + if(!status && info->mapinfo.encoding) + info->encoding = mdvi_request_encoding(info->mapinfo.encoding); + t1_transform_font(info); + + i = info->tfminfo->hic - info->tfminfo->loc + 1; + if(i != font->hic - font->loc + 1) { + /* reset to optimal size */ + font->chars = mdvi_realloc(font->chars, i * sizeof(DviFontChar)); + } + + /* get the scaled characters metrics */ + get_tfm_chars(params, font, info->tfminfo, 0); + info->hasmetrics = 1; + + DEBUG((DBG_TYPE1, "(t1) font `%s' really-loaded\n", info->fontname)); + return 0; + +t1_error: + /* some error does not allows us to use this font. We need to reset + * the font structure, so the font system can try to read this + * font in a different class */ + + /* first destroy the private data */ + t1_font_remove(info); + /* now reset all chars -- this is the important part */ + mdvi_free(font->chars); + font->chars = NULL; + font->loc = font->hic = 0; + return -1; +} + +static int init_t1lib(DviParams *params) +{ + int t1flags; + +#ifdef WORD_LITTLE_ENDIAN + /* try making T1lib use bitmaps in our format, but if this + * fails we'll convert the bitmap ourselves */ + T1_SetBitmapPad(BITMAP_BITS); +#endif + T1_SetDeviceResolutions((float)params->dpi, (float)params->vdpi); + t1flags = IGNORE_CONFIGFILE|IGNORE_FONTDATABASE|T1_NO_AFM; + if(DEBUGGING(TYPE1)) + t1flags |= LOGFILE; + if(T1_InitLib(t1flags) == NULL) + return (t1lib_initialized = -1); + if(DEBUGGING(TYPE1)) { + DEBUG((DBG_TYPE1, "T1lib debugging output saved in t1lib.log\n")); + T1_SetLogLevel(T1LOG_DEBUG); + } + /* initialize our hash table, but don't allocate memory for it + * until we use it */ + mdvi_hash_init(&t1hash); + DEBUG((DBG_TYPE1, "(t1) t1lib %s initialized -- resolution is (%d, %d), pad is %d bits\n", + T1_GetLibIdent(), params->dpi, params->vdpi, T1_GetBitmapPad())); + t1lib_initialized = 1; + t1lib_xdpi = params->dpi; + t1lib_ydpi = params->vdpi; + return 0; +} + +static int t1_load_font(DviParams *params, DviFont *font) +{ + T1Info *info; + int i; + + if(t1lib_initialized < 0) + return -1; + else if(t1lib_initialized == 0 && init_t1lib(params) < 0) + return -1; + + if(font->in != NULL) { + /* we don't need this */ + fclose(font->in); + font->in = NULL; + } + + info = xalloc(T1Info); + + /* + * mark the font as `unregistered' with T1lib. It will + * be added when we actually use it + */ + info->t1id = -1; + + /* add the font to our list */ + info->fontname = font->fontname; + info->hasmetrics = 0; + info->encoding = NULL; + info->mapinfo.psname = NULL; + info->mapinfo.encoding = NULL; + info->mapinfo.fontfile = NULL; + info->mapinfo.extend = 0; + info->mapinfo.slant = 0; + info->encoding = NULL; + + /* create the hash table if we have not done so yet */ + if(t1hash.nbucks == 0) + mdvi_hash_create(&t1hash, T1_HASH_SIZE); + mdvi_hash_add(&t1hash, (unsigned char *) info->fontname, info, MDVI_HASH_UNIQUE); + listh_append(&t1fonts, LIST(info)); + + font->private = info; + + /* reset everything */ + font->chars = xnalloc(DviFontChar, 256); + font->loc = 0; + font->hic = 255; + for(i = 0; i < 256; i++) { + font->chars[i].code = i; + font->chars[i].offset = 1; + font->chars[i].loaded = 0; + font->chars[i].glyph.data = NULL; + font->chars[i].shrunk.data = NULL; + font->chars[i].grey.data = NULL; + } + + return 0; +} + +#define GLYPH_WIDTH(g) \ + ((g)->metrics.rightSideBearing - (g)->metrics.leftSideBearing) +#define GLYPH_HEIGHT(g) \ + ((g)->metrics.ascent - (g)->metrics.descent) + +static inline BITMAP *t1_glyph_bitmap(GLYPH *glyph) +{ + int w, h, pad; + + w = GLYPH_WIDTH(glyph); + h = GLYPH_HEIGHT(glyph); + + if(!w || !h) + return MDVI_GLYPH_EMPTY; + + pad = T1_GetBitmapPad(); + return bitmap_convert_lsb8((unsigned char *)glyph->bits, w, h, ROUND(w, pad) * (pad >> 3)); +} + +static void t1_font_shrink_glyph(DviContext *dvi, DviFont *font, DviFontChar *ch, DviGlyph *dest) +{ + double size; + GLYPH *glyph; + T1Info *info; + T1_TMATRIX matrix; + + info = (T1Info *)font->private; + ASSERT(info != NULL); + + DEBUG((DBG_TYPE1, "(t1) shrinking glyph for character %d in `%s' (%d,%d)\n", + ch->code, font->fontname, ch->width, ch->height)); + size = (double)font->scale / (dvi->params.tfm_conv * 0x100000); + size = 72.0 * size / 72.27; + matrix.cxx = 1.0/(double)dvi->params.hshrink; + matrix.cyy = 1.0/(double)dvi->params.vshrink; + matrix.cxy = 0.0; + matrix.cyx = 0.0; + glyph = T1_SetChar(info->t1id, ch->code, (float)size, &matrix); + + dest->data = t1_glyph_bitmap(glyph); + dest->x = -glyph->metrics.leftSideBearing; + dest->y = glyph->metrics.ascent; + dest->w = GLYPH_WIDTH(glyph); + dest->h = GLYPH_HEIGHT(glyph); + +#ifndef NODEBUG + if(DEBUGGING(BITMAP_DATA)) { + DEBUG((DBG_BITMAP_DATA, + "(t1) %s: t1_shrink_glyph(%d): (%dw,%dh,%dx,%dy) -> (%dw,%dh,%dx,%dy)\n", + ch->glyph.w, ch->glyph.h, ch->glyph.x, ch->glyph.y, + dest->w, dest->h, dest->x, dest->y)); + bitmap_print(stderr, (BITMAP *)dest->data); + } +#endif + /* transform the glyph - we could do this with t1lib, but we do + * it ourselves for now */ + font_transform_glyph(dvi->params.orientation, dest); +} + +static int t1_font_get_glyph(DviParams *params, DviFont *font, int code) +{ + T1Info *info = (T1Info *)font->private; + GLYPH *glyph; + DviFontChar *ch; + double size; + T1_TMATRIX matrix; + int dpi; + + ASSERT(info != NULL); + if(!info->hasmetrics && t1_really_load_font(params, font, info) < 0) + return -1; + ch = FONTCHAR(font, code); + if(!ch || !glyph_present(ch)) + return -1; + ch->loaded = 1; + if(!ch->width || !ch->height) { + ch->glyph.x = ch->x; + ch->glyph.y = ch->y; + ch->glyph.w = ch->width; + ch->glyph.h = ch->height; + ch->glyph.data = NULL; + return 0; + } + + /* load the glyph with T1lib (this is done only once for each glyph) */ + + /* get size in TeX points (tfm_conv includes dpi and magnification) */ + size = (double)font->scale / (params->tfm_conv * 0x100000); + /* and transform into PostScript points */ + size = 72.0 * size / 72.27; + + dpi = Max(font->hdpi, font->vdpi); + /* we don't want the glyph to be cached twice (once by us, another by + * T1lib), so we use an identity matrix to tell T1lib not to keep the + * glyph around */ + matrix.cxx = (double)font->hdpi / dpi; + matrix.cyy = (double)font->vdpi / dpi; + matrix.cxy = matrix.cyx = 0.0; + glyph = T1_SetChar(info->t1id, ch->code, (float)size, &matrix); + if(glyph == NULL) { + ch->glyph.x = ch->x; + ch->glyph.y = ch->y; + ch->glyph.w = ch->width; + ch->glyph.h = ch->height; + ch->glyph.data = NULL; + ch->missing = 1; + return 0; + } + /* and make it a bitmap */ + ch->glyph.data = t1_glyph_bitmap(glyph); + ch->glyph.x = -glyph->metrics.leftSideBearing; + ch->glyph.y = glyph->metrics.ascent; + ch->glyph.w = GLYPH_WIDTH(glyph); + ch->glyph.h = GLYPH_HEIGHT(glyph); + + /* let's also fix the glyph's origin + * (which is not contained in the TFM) */ + ch->x = ch->glyph.x; + ch->y = ch->glyph.y; + /* let's fix these too */ + ch->width = ch->glyph.w; + ch->height = ch->glyph.h; + + return 0; +} + +static void t1_font_remove(T1Info *info) +{ + T1Info *old; + + /* first remove it from our list */ + listh_remove(&t1fonts, LIST(info)); + + /* it it's in the hash table, we may need to replace this by another font */ + old = (T1Info *)mdvi_hash_lookup(&t1hash, (unsigned char *)info->fontname); + if(old == info) { + mdvi_hash_remove(&t1hash, (unsigned char *) info->fontname); + /* go through the list and see if there is another + * font with this name */ + for(old = (T1Info *)t1fonts.head; old; old = old->next) + if(STREQ(old->fontname, info->fontname)) + break; + if(old != NULL) + mdvi_hash_add(&t1hash, (unsigned char *) old->fontname, old, + MDVI_HASH_UNCHECKED); + } + /* release our encoding vector */ + if(info->encoding) { + DEBUG((DBG_TYPE1, "(t1) %s: releasing vector `%s'\n", + info->fontname, info->encoding->name)); + mdvi_release_encoding(info->encoding, 1); + } + + /* now get rid of it */ + if(info->t1id != -1) { + DEBUG((DBG_TYPE1, "(t1) %s: T1_DeleteFont(%d)\n", + info->fontname, info->t1id)); + T1_DeleteFont(info->t1id); + } else + DEBUG((DBG_TYPE1, "(t1) %s: not loaded yet, DeleteFont skipped\n", + info->fontname)); + + if(info->tfminfo) + free_font_metrics(info->tfminfo); + /*mdvi_free(info->fontname);*/ + mdvi_free(info); +} + +static void t1_free_data(DviFont *font) +{ + /* called after all the glyphs are destroyed */ + + if(font->private == NULL) { + /* this is perfectly normal, it just means the font has + * not been requested by MDVI yet */ + return; + } + + /* destroy this data */ + + t1_font_remove((T1Info *)font->private); + font->private = NULL; + + /* + * if this is the last T1 font, reset the T1 library + * It is important that we do this, because this is will be called + * when the resolution or the magnification changes. + */ + if(t1fonts.count == 0) { + DEBUG((DBG_TYPE1, "(t1) last font removed -- closing T1lib\n")); + T1_CloseLib(); + t1lib_initialized = 0; + t1lib_xdpi = -1; + t1lib_ydpi = -1; + } +} + +#endif /* WITH_TYPE1_FONTS */ diff --git a/backend/dvi/mdvi-lib/tfm.c b/backend/dvi/mdvi-lib/tfm.c new file mode 100644 index 00000000..f37de0be --- /dev/null +++ b/backend/dvi/mdvi-lib/tfm.c @@ -0,0 +1,214 @@ +/* + * Copyright (C) 2000, Matias Atria + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#include <config.h> +#include <stdlib.h> +#include <stdarg.h> +#include <string.h> +#include <sys/stat.h> +#include <unistd.h> + +#include "mdvi.h" +#include "private.h" + +static int tfm_load_font __PROTO((DviParams *, DviFont *)); +static int tfm_font_get_glyph __PROTO((DviParams *, DviFont *, int)); + +DviFontInfo tfm_font_info = { + "TFM", + 0, /* scaling not supported by format */ + tfm_load_font, + tfm_font_get_glyph, + mdvi_shrink_box, + mdvi_shrink_box, + NULL, /* free */ + NULL, /* reset */ + NULL, /* lookup */ + kpse_tfm_format, + NULL +}; + +DviFontInfo ofm_font_info = { + "OFM", + 0, /* scaling not supported by format */ + tfm_load_font, + tfm_font_get_glyph, + mdvi_shrink_box, + mdvi_shrink_box, + NULL, /* free */ + NULL, /* reset */ + NULL, /* lookup */ + kpse_ofm_format, + NULL +}; + +DviFontInfo afm_font_info = { + "AFM", + 0, /* scaling not supported by format */ + tfm_load_font, + tfm_font_get_glyph, + mdvi_shrink_box, + mdvi_shrink_box, + NULL, /* free */ + NULL, /* reset */ + NULL, /* lookup */ + kpse_afm_format, + NULL +}; + +#define TYPENAME(font) \ + ((font)->search.info ? (font)->search.info : "none") + +/* + * Although it does not seem that way, this conversion is independent of the + * shrinking factors, within roundoff (that's because `conv' and `vconv' + * have already been scaled by hshrink and vshrink, repsectively). We + * should really use `dviconv' and `dvivconv', but I'm not so sure those + * should be moved to the DviParams structure. + */ +#define XCONV(x) FROUND(params->conv * (x) * params->hshrink) +#define YCONV(y) FROUND(params->vconv * (y) * params->vshrink) + +/* this is used quite often in several places, so I made it standalone */ +int get_tfm_chars(DviParams *params, DviFont *font, TFMInfo *info, int loaded) +{ + Int32 z, alpha, beta; + int n; + DviFontChar *ch; + TFMChar *ptr; + + n = info->hic - info->loc + 1; + if(n != FONT_GLYPH_COUNT(font)) { + font->chars = mdvi_realloc(font->chars, + n * sizeof(DviFontChar)); + } + font->loc = info->loc; + font->hic = info->hic; + ch = font->chars; + ptr = info->chars; + + /* Prepare z, alpha and beta for TFM width computation */ + TFMPREPARE(font->scale, z, alpha, beta); + + /* get the character metrics */ + for(n = info->loc; n <= info->hic; ch++, ptr++, n++) { + int a, b, c, d; + + ch->offset = ptr->present; + if(ch->offset == 0) + continue; + /* this is what we came here for */ + ch->tfmwidth = TFMSCALE(z, ptr->advance, alpha, beta); + /* scale all other TFM units (so they are in DVI units) */ + a = TFMSCALE(z, ptr->left, alpha, beta); + b = TFMSCALE(z, ptr->right, alpha, beta); + c = TFMSCALE(z, ptr->height, alpha, beta); + d = TFMSCALE(z, ptr->depth, alpha, beta); + + /* now convert to unscaled pixels */ + ch->width = XCONV(b - a); + ch->height = YCONV(c - d); + if(ch->height < 0) ch->height = -ch->height; + ch->x = XCONV(a); + ch->y = YCONV(c); + /* + * the offset is not used, but we might as well set it to + * something meaningful (and it MUST be non-zero) + */ + ch->flags = 0; + ch->code = n; + ch->glyph.data = NULL; + ch->grey.data = NULL; + ch->shrunk.data = NULL; + ch->loaded = loaded; + } + + return 0; +} + +/* + * We use this function as a last resort to find the character widths in a + * font The DVI rendering code can correctly skip over a glyph if it knows + * its TFM width, which is what we try to find here. + */ +static int tfm_load_font(DviParams *params, DviFont *font) +{ + TFMInfo *tfm; + int type; + + switch(font->search.info->kpse_type) { + case kpse_tfm_format: + type = DviFontTFM; + break; + case kpse_afm_format: + type = DviFontAFM; + break; + case kpse_ofm_format: + type = DviFontOFM; + break; + default: + return -1; + } + + /* we don't need this */ + if(font->in) { + fclose(font->in); + font->in = NULL; + } + tfm = get_font_metrics(font->fontname, type, font->filename); + if(tfm == NULL) + return -1; + + if(tfm->checksum && font->checksum && tfm->checksum != font->checksum) { + mdvi_warning(_("%s: Checksum mismatch (got %u, expected %u)\n"), + font->fontname, (unsigned)tfm->checksum, + (unsigned)font->checksum); + } + font->checksum = tfm->checksum; + font->design = tfm->design; + font->loc = 0; + font->hic = 0; + font->chars = NULL; + get_tfm_chars(params, font, tfm, 1); + + /* free everything */ + free_font_metrics(tfm); + + return 0; +} + +static int tfm_font_get_glyph(DviParams *params, DviFont *font, int code) +{ + DviFontChar *ch; + + ch = FONTCHAR(font, code); + if(!glyph_present(ch)) + return -1; + ch->glyph.x = ch->x; + ch->glyph.y = ch->y; + ch->glyph.w = ch->width; + ch->glyph.h = ch->height; + /* + * This has two purposes: (1) avoid unnecessary calls to this function, + * and (2) detect when the glyph data for a TFM font is actually used + * (we'll get a SEGV). Any occurrence of that is a bug. + */ + ch->glyph.data = MDVI_GLYPH_EMPTY; + + return 0; +} diff --git a/backend/dvi/mdvi-lib/tfmfile.c b/backend/dvi/mdvi-lib/tfmfile.c new file mode 100644 index 00000000..73ebf26a --- /dev/null +++ b/backend/dvi/mdvi-lib/tfmfile.c @@ -0,0 +1,747 @@ +/* tfmfile.c -- readers for TFM, AFM, OTFM-0 and OTFM-1 files */ +/* + * Copyright (C) 2000, Matias Atria + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#include <config.h> +#include <stdio.h> /* tex-file.h needs this */ +#include <stdlib.h> +#include <stdarg.h> +#include <string.h> +#include <sys/stat.h> +#include <unistd.h> + +#include "mdvi.h" +#include "private.h" + +#ifdef WITH_AFM_FILES +#undef TRUE +#undef FALSE +#include "afmparse.h" +#endif + +typedef struct tfmpool { + struct tfmpool *next; + struct tfmpool *prev; + char *short_name; + int links; + TFMInfo tfminfo; +} TFMPool; + +static ListHead tfmpool = {NULL, NULL, 0}; +static DviHashTable tfmhash; + +#define TFM_HASH_SIZE 31 + +#ifdef WORD_LITTLE_ENDIAN +static inline void swap_array(Uint32 *ptr, int n) +{ + Uint32 i; + + while(n-- > 0) { + i = *ptr; + *ptr++ = ((i & 0xff000000) >> 24) + | ((i & 0x00ff0000) >> 8) + | ((i & 0x0000ff00) << 8) + | ((i & 0x000000ff) << 24); + } +} +#endif + +#ifdef WITH_AFM_FILES + +static int __PROTO(ofm_load_file(const char *filename, TFMInfo *info)); + +/* reading of AFM files */ +/* macro to convert between AFM and TFM units */ +#define AFM2TFM(x) FROUND((double)(x) * 0x100000 / 1000) +int afm_load_file(const char *filename, TFMInfo *info) +{ + /* the information we want is: + * - tfmwidth + * - width and heights + * - character origins + */ + FontInfo *fi = NULL; + int status; + CharMetricInfo *cm; + FILE *in; + + in = fopen(filename, "rb"); + if(in == NULL) + return -1; + status = afm_parse_file(in, &fi, P_GM); + fclose(in); + + if(status != ok) { + mdvi_error(_("%s: Error reading AFM data\n"), filename); + return -1; + } + + /* aim high */ + info->chars = xnalloc(TFMChar, 256); + info->loc = 256; + info->hic = 0; + info->design = 0xa00000; /* fake -- 10pt */ + info->checksum = 0; /* no checksum */ + info->type = DviFontAFM; + mdvi_strncpy(info->coding, fi->gfi->encodingScheme, 63); + mdvi_strncpy(info->family, fi->gfi->familyName, 63); + + /* now get the data */ + for(cm = fi->cmi; cm < fi->cmi + fi->numOfChars; cm++) { + int code; + TFMChar *ch; + + code = cm->code; + if(code < 0 || code > 255) + continue; /* ignore it */ + ch = &info->chars[code]; + ch->present = 1; + if(code < info->loc) + info->loc = code; + if(code > info->hic) + info->hic = code; + ch->advance = AFM2TFM(cm->wx); + /* this is the `leftSideBearing' */ + ch->left = AFM2TFM(cm->charBBox.llx); + /* this is the height (ascent - descent) -- the sign is to follow + * TeX conventions, as opposed to Adobe's ones */ + ch->depth = -AFM2TFM(cm->charBBox.lly); + /* this is the width (rightSideBearing - leftSideBearing) */ + ch->right = AFM2TFM(cm->charBBox.urx); + /* this is the `ascent' */ + ch->height = AFM2TFM(cm->charBBox.ury); + } + + /* we don't need this anymore */ + afm_free_fontinfo(fi); + + /* optimize storage */ + if(info->loc > 0 || info->hic < 256) { + memmove(&info->chars[0], + &info->chars[info->loc], + (info->hic - info->loc + 1) * sizeof(TFMChar)); + info->chars = mdvi_realloc(info->chars, + (info->hic - info->loc + 1) * sizeof(TFMChar)); + } + + /* we're done */ + return 0; +} + +#endif /* WITH_AFM_FILES */ + +int tfm_load_file(const char *filename, TFMInfo *info) +{ + int lf, lh, bc, ec, nw, nh, nd, ne; + int i, n; + Uchar *tfm; + Uchar *ptr; + struct stat st; + int size; + FILE *in; + Int32 *cb; + Int32 *charinfo; + Int32 *widths; + Int32 *heights; + Int32 *depths; + Uint32 checksum; + + in = fopen(filename, "rb"); + if(in == NULL) + return -1; + tfm = NULL; + + DEBUG((DBG_FONTS, "(mt) reading TFM file `%s'\n", + filename)); + /* We read the entire TFM file into core */ + if(fstat(fileno(in), &st) < 0) + return -1; + if(st.st_size == 0) + goto bad_tfm; + + /* allocate a word-aligned buffer to hold the file */ + size = 4 * ROUND(st.st_size, 4); + if(size != st.st_size) + mdvi_warning(_("Warning: TFM file `%s' has suspicious size\n"), + filename); + tfm = (Uchar *)mdvi_malloc(size); + if(fread(tfm, st.st_size, 1, in) != 1) + goto error; + /* we don't need this anymore */ + fclose(in); + in = NULL; + + /* not a checksum, but serves a similar purpose */ + checksum = 0; + + ptr = tfm; + /* get the counters */ + lf = muget2(ptr); + lh = muget2(ptr); checksum += 6 + lh; + bc = muget2(ptr); + ec = muget2(ptr); checksum += ec - bc + 1; + nw = muget2(ptr); checksum += nw; + nh = muget2(ptr); checksum += nh; + nd = muget2(ptr); checksum += nd; + checksum += muget2(ptr); /* skip italics correction count */ + checksum += muget2(ptr); /* skip lig/kern table size */ + checksum += muget2(ptr); /* skip kern table size */ + ne = muget2(ptr); checksum += ne; + checksum += muget2(ptr); /* skip # of font parameters */ + + size = ec - bc + 1; + cb = (Int32 *)tfm; cb += 6 + lh; + charinfo = cb; cb += size; + widths = cb; cb += nw; + heights = cb; cb += nh; + depths = cb; + + if(widths[0] || heights[0] || depths[0] || + checksum != lf || bc - 1 > ec || ec > 255 || ne > 256) + goto bad_tfm; + + /* from this point on, no error checking is done */ + + /* now we're at the header */ + /* get the checksum */ + info->checksum = muget4(ptr); + /* get the design size */ + info->design = muget4(ptr); + /* get the coding scheme */ + if(lh > 2) { + /* get the coding scheme */ + i = n = msget1(ptr); + if(n < 0 || n > 39) { + mdvi_warning(_("%s: font coding scheme truncated to 40 bytes\n"), + filename); + n = 39; + } + memcpy(info->coding, ptr, n); + info->coding[n] = 0; + ptr += i; + } else + strcpy(info->coding, "FontSpecific"); + /* get the font family */ + if(lh > 12) { + n = msget1(ptr); + if(n > 0) { + i = Max(n, 63); + memcpy(info->family, ptr, i); + info->family[i] = 0; + } else + strcpy(info->family, "unspecified"); + ptr += n; + } + /* now we don't read from `ptr' anymore */ + + info->loc = bc; + info->hic = ec; + info->type = DviFontTFM; + + /* allocate characters */ + info->chars = xnalloc(TFMChar, size); + + +#ifdef WORD_LITTLE_ENDIAN + /* byte-swap the three arrays at once (they are consecutive in memory) */ + swap_array((Uint32 *)widths, nw + nh + nd); +#endif + + /* get the relevant data */ + ptr = (Uchar *)charinfo; + for(i = bc; i <= ec; ptr += 3, i++) { + int ndx; + + ndx = (int)*ptr; ptr++; + info->chars[i-bc].advance = widths[ndx]; + /* TFM files lack this information */ + info->chars[i-bc].left = 0; + info->chars[i-bc].right = widths[ndx]; + info->chars[i-bc].present = (ndx != 0); + if(ndx) { + ndx = ((*ptr >> 4) & 0xf); + info->chars[i-bc].height = heights[ndx]; + ndx = (*ptr & 0xf); + info->chars[i-bc].depth = depths[ndx]; + } + } + + /* free everything */ + mdvi_free(tfm); + + return 0; + +bad_tfm: + mdvi_error(_("%s: File corrupted, or not a TFM file\n"), filename); +error: + if(tfm) mdvi_free(tfm); + if(in) fclose(in); + return -1; +} + +static int ofm1_load_file(FILE *in, TFMInfo *info) +{ + int lf, lh, bc, ec, nw, nh, nd; + int nco, ncw, npc; + int i; + int n; + int size; + Int32 *tfm; + Int32 *widths; + Int32 *heights; + Int32 *depths; + TFMChar *tch; + TFMChar *end; + + lf = fuget4(in); + lh = fuget4(in); + bc = fuget4(in); + ec = fuget4(in); + nw = fuget4(in); + nh = fuget4(in); + nd = fuget4(in); + fuget4(in); /* italics */ + fuget4(in); /* lig-kern */ + fuget4(in); /* kern */ + fuget4(in); /* extensible recipe */ + fuget4(in); /* parameters */ + fuget4(in); /* direction */ + nco = fuget4(in); + ncw = fuget4(in); + npc = fuget4(in); + + /* get the checksum */ + info->checksum = fuget4(in); + /* the design size */ + info->design = fuget4(in); + /* get the coding scheme */ + if(lh > 2) { + /* get the coding scheme */ + i = n = fsget1(in); + if(n < 0 || n > 39) + n = 39; + fread(info->coding, 39, 1, in); + info->coding[n] = 0; + } else + strcpy(info->coding, "FontSpecific"); + /* get the font family */ + if(lh > 12) { + n = fsget1(in); + if(n > 0) { + i = Max(n, 63); + fread(info->family, i, 1, in); + info->family[i] = 0; + } else + strcpy(info->family, "unspecified"); + } + tfm = NULL; + + /* jump to the beginning of the char-info table */ + fseek(in, 4L*nco, SEEK_SET); + + size = ec - bc + 1; + info->loc = bc; + info->hic = ec; + info->chars = xnalloc(TFMChar, size); + end = info->chars + size; + + for(tch = info->chars, i = 0; i < ncw; i++) { + TFMChar ch; + int nr; + + /* in the characters we store the actual indices */ + ch.advance = fuget2(in); + ch.height = fuget1(in); + ch.depth = fuget1(in); + /* skip 2nd word */ + fuget4(in); + /* get # of repeats */ + nr = fuget2(in); + /* skip parameters */ + fseek(in, (long)npc * 2, SEEK_CUR); + /* if npc is odd, skip padding */ + if(npc & 1) fuget2(in); + + /* now repeat the character */ + while(nr-- >= 0 && tch < end) + memcpy(tch++, &ch, sizeof(TFMChar)); + if(tch == end) + goto bad_tfm; + } + + /* I wish we were done, but we aren't */ + + /* get the widths, heights and depths */ + size = nw + nh + nd; + tfm = xnalloc(Int32, size); + /* read them in one sweep */ + if(fread(tfm, 4, size, in) != size) { + mdvi_free(tfm); + goto bad_tfm; + } + + /* byte-swap things if necessary */ +#ifdef WORD_LITTLE_ENDIAN + swap_array((Uint32 *)tfm, size); +#endif + widths = tfm; + heights = widths + nw; + depths = heights + nh; + + if(widths[0] || heights[0] || depths[0]) + goto bad_tfm; + + /* now fix the characters */ + size = ec - bc + 1; + for(tch = info->chars; tch < end; tch++) { + tch->present = (tch->advance != 0); + tch->advance = widths[tch->advance]; + tch->height = heights[tch->height]; + tch->depth = depths[tch->depth]; + tch->left = 0; + tch->right = tch->advance; + } + + /* NOW we're done */ + mdvi_free(tfm); + return 0; + +bad_tfm: + if(tfm) mdvi_free(tfm); + return -1; +} + +/* we don't read OFM files into memory, because they can potentially be large */ +static int ofm_load_file(const char *filename, TFMInfo *info) +{ + int lf, lh, bc, ec, nw, nh, nd; + int i, n; + Int32 *tfm; + Uchar *ptr; + int size; + FILE *in; + Int32 *cb; + Int32 *charinfo; + Int32 *widths; + Int32 *heights; + Int32 *depths; + Uint32 checksum; + int olevel; + int nwords; + + in = fopen(filename, "rb"); + if(in == NULL) + return -1; + + /* not a checksum, but serves a similar purpose */ + checksum = 0; + + /* get the counters */ + /* get file level */ + olevel = fsget2(in); + if(olevel != 0) + goto bad_tfm; + olevel = fsget2(in); + if(olevel != 0) { + DEBUG((DBG_FONTS, "(mt) reading Level-1 OFM file `%s'\n", + filename)); + /* we handle level-1 files separately */ + if(ofm1_load_file(in, info) < 0) + goto bad_tfm; + return 0; + } + + DEBUG((DBG_FONTS, "(mt) reading Level-0 OFM file `%s'\n", filename)); + nwords = 14; + lf = fuget4(in); checksum = nwords; + lh = fuget4(in); checksum += lh; + bc = fuget4(in); + ec = fuget4(in); checksum += 2 * (ec - bc + 1); + nw = fuget4(in); checksum += nw; + nh = fuget4(in); checksum += nh; + nd = fuget4(in); checksum += nd; + checksum += fuget4(in); /* skip italics correction count */ + checksum += 2*fuget4(in); /* skip lig/kern table size */ + checksum += fuget4(in); /* skip kern table size */ + checksum += 2*fuget4(in); /* skip extensible recipe count */ + checksum += fuget4(in); /* skip # of font parameters */ + + /* I have found several .ofm files that seem to have the + * font-direction word missing, so we try to detect that here */ + if(checksum == lf + 1) { + DEBUG((DBG_FONTS, "(mt) font direction missing in `%s'\n", + filename)); + checksum--; + nwords--; + } else { + /* skip font direction */ + fuget4(in); + } + + if(checksum != lf || bc > ec + 1 || ec > 65535) + goto bad_tfm; + + /* now we're at the header */ + + /* get the checksum */ + info->checksum = fuget4(in); + /* get the design size */ + info->design = fuget4(in); + + /* get the coding scheme */ + if(lh > 2) { + /* get the coding scheme */ + i = n = fsget1(in); + if(n < 0 || n > 39) { + mdvi_warning(_("%s: font coding scheme truncated to 40 bytes\n"), + filename); + n = 39; + } + fread(info->coding, 39, 1, in); + info->coding[n] = 0; + } else + strcpy(info->coding, "FontSpecific"); + /* get the font family */ + if(lh > 12) { + n = fsget1(in); + if(n > 0) { + i = Max(n, 63); + fread(info->family, i, 1, in); + info->family[i] = 0; + } else + strcpy(info->family, "unspecified"); + } + + /* now skip anything else in the header */ + fseek(in, 4L*(nwords + lh), SEEK_SET); + /* and read everything at once */ + size = 2*(ec - bc + 1) + nw + nh + nd; + tfm = xnalloc(Int32, size * sizeof(Int32)); + if(fread(tfm, 4, size, in) != size) { + mdvi_free(tfm); + goto bad_tfm; + } + /* byte-swap all the tables at once */ +#ifdef WORD_LITTLE_ENDIAN + swap_array((Uint32 *)tfm, size); +#endif + cb = tfm; + charinfo = cb; cb += 2*(ec - bc + 1); + widths = cb; cb += nw; + heights = cb; cb += nh; + depths = cb; + + if(widths[0] || heights[0] || depths[0]) { + mdvi_free(tfm); + goto bad_tfm; + } + + /* from this point on, no error checking is done */ + + /* we don't need this anymore */ + fclose(in); + + /* now we don't read from `ptr' anymore */ + + info->loc = bc; + info->hic = ec; + info->type = DviFontTFM; + + /* allocate characters */ + info->chars = xnalloc(TFMChar, size); + + /* get the relevant data */ + ptr = (Uchar *)charinfo; + for(i = bc; i <= ec; ptr += 4, i++) { + int ndx; + + ndx = muget2(ptr); + info->chars[i-bc].advance = widths[ndx]; + /* TFM files lack this information */ + info->chars[i-bc].left = 0; + info->chars[i-bc].right = widths[ndx]; + info->chars[i-bc].present = (ndx != 0); + ndx = muget1(ptr); + info->chars[i-bc].height = heights[ndx]; + ndx = muget1(ptr); + info->chars[i-bc].depth = depths[ndx]; + } + + mdvi_free(tfm); + return 0; + +bad_tfm: + mdvi_error(_("%s: File corrupted, or not a TFM file\n"), filename); + fclose(in); + return -1; +} + +char *lookup_font_metrics(const char *name, int *type) +{ + char *file; + + switch(*type) { +#ifndef WITH_AFM_FILES + case DviFontAny: +#endif + case DviFontTFM: + file = kpse_find_tfm(name); + *type = DviFontTFM; + break; + case DviFontOFM: { + file = kpse_find_ofm(name); + /* we may have gotten a TFM back */ + if(file != NULL) { + const char *ext = file_extension(file); + if(ext && STREQ(ext, "tfm")) + *type = DviFontTFM; + } + break; + } +#ifdef WITH_AFM_FILES + case DviFontAFM: + file = kpse_find_file(name, kpse_afm_format, 0); + break; + case DviFontAny: + file = kpse_find_file(name, kpse_afm_format, 0); + *type = DviFontAFM; + if(file == NULL) { + file = kpse_find_tfm(name); + *type = DviFontTFM; + } + break; +#endif + default: + return NULL; + } + + return file; +} + +/* + * The next two functions are just wrappers for the font metric loaders, + * and use the pool of TFM data + */ + +/* this is how we interpret arguments: + * - if filename is NULL, we look for files of the given type, + * unless type is DviFontAny, in which case we try all the + * types we know of. + * - if filename is not NULL, we look at `type' to decide + * how to read the file. If type is DviFontAny, we just + * return an error. + */ +TFMInfo *get_font_metrics(const char *short_name, int type, const char *filename) +{ + TFMPool *tfm = NULL; + int status; + char *file; + + if(tfmpool.count) { + tfm = (TFMPool *)mdvi_hash_lookup(&tfmhash, + MDVI_KEY(short_name)); + if(tfm != NULL) { + DEBUG((DBG_FONTS, "(mt) reusing metric file `%s' (%d links)\n", + short_name, tfm->links)); + tfm->links++; + return &tfm->tfminfo; + } + } + + file = filename ? (char *)filename : lookup_font_metrics(short_name, &type); + if(file == NULL) + return NULL; + + tfm = xalloc(TFMPool); + DEBUG((DBG_FONTS, "(mt) loading font metric data from `%s'\n", file, file)); + switch(type) { + case DviFontTFM: + status = tfm_load_file(file, &tfm->tfminfo); + break; + case DviFontOFM: + status = ofm_load_file(file, &tfm->tfminfo); + break; +#ifdef WITH_AFM_FILES + case DviFontAFM: + status = afm_load_file(file, &tfm->tfminfo); + break; +#endif + default: + status = -1; + break; + } + if(file != filename) + mdvi_free(file); + if(status < 0) { + mdvi_free(tfm); + return NULL; + } + tfm->short_name = mdvi_strdup(short_name); + + /* add it to the pool */ + if(tfmpool.count == 0) + mdvi_hash_create(&tfmhash, TFM_HASH_SIZE); + mdvi_hash_add(&tfmhash, MDVI_KEY(tfm->short_name), + tfm, MDVI_HASH_UNCHECKED); + listh_prepend(&tfmpool, LIST(tfm)); + tfm->links = 1; + + return &tfm->tfminfo; +} + +void free_font_metrics(TFMInfo *info) +{ + TFMPool *tfm; + + if(tfmpool.count == 0) + return; + /* get the entry -- can't use the hash table for this, because + * we don't have the short name */ + for(tfm = (TFMPool *)tfmpool.head; tfm; tfm = tfm->next) + if(info == &tfm->tfminfo) + break; + if(tfm == NULL) + return; + if(--tfm->links > 0) { + DEBUG((DBG_FONTS, "(mt) %s not removed, still in use\n", + tfm->short_name)); + return; + } + mdvi_hash_remove_ptr(&tfmhash, MDVI_KEY(tfm->short_name)); + + DEBUG((DBG_FONTS, "(mt) removing unused TFM data for `%s'\n", tfm->short_name)); + listh_remove(&tfmpool, LIST(tfm)); + mdvi_free(tfm->short_name); + mdvi_free(tfm->tfminfo.chars); + mdvi_free(tfm); +} + +void flush_font_metrics(void) +{ + TFMPool *ptr; + + for(; (ptr = (TFMPool *)tfmpool.head); ) { + tfmpool.head = LIST(ptr->next); + + mdvi_free(ptr->short_name); + mdvi_free(ptr->tfminfo.chars); + mdvi_free(ptr); + } + mdvi_hash_reset(&tfmhash, 0); +} diff --git a/backend/dvi/mdvi-lib/tt.c b/backend/dvi/mdvi-lib/tt.c new file mode 100644 index 00000000..e85d8e70 --- /dev/null +++ b/backend/dvi/mdvi-lib/tt.c @@ -0,0 +1,495 @@ +/* + * Copyright (C) 2000, Matias Atria + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#include <config.h> +#include "mdvi.h" + +#ifdef WITH_TRUETYPE_FONTS + +#include <string.h> +#include <freetype.h> +#include <ftxpost.h> +#include <ftxerr18.h> + +#include "private.h" + +static TT_Engine tt_handle; +static int initialized = 0; + +typedef struct ftinfo { + struct ftinfo *next; + struct ftinfo *prev; + char *fontname; + char *fmfname; + TT_Face face; + TT_Instance instance; + TT_Glyph glyph; + int hasmetrics; + int loaded; + int fmftype; + TFMInfo *tfminfo; + DviFontMapInfo mapinfo; + DviEncoding *encoding; +} FTInfo; + +static int tt_load_font __PROTO((DviParams *, DviFont *)); +static int tt_font_get_glyph __PROTO((DviParams *, DviFont *, int)); +static void tt_free_data __PROTO((DviFont *)); +static void tt_reset_font __PROTO((DviFont *)); +static void tt_shrink_glyph + __PROTO((DviContext *, DviFont *, DviFontChar *, DviGlyph *)); +static void tt_font_remove __PROTO((FTInfo *)); + +DviFontInfo tt_font_info = { + "TT", + 0, + tt_load_font, + tt_font_get_glyph, + tt_shrink_glyph, + mdvi_shrink_glyph_grey, + tt_free_data, /* free */ + tt_reset_font, /* reset */ + NULL, /* lookup */ + kpse_truetype_format, + NULL +}; + +#define FT_HASH_SIZE 31 + +static ListHead ttfonts = {NULL, NULL, 0}; + +static int init_freetype(void) +{ + TT_Error code; + + ASSERT(initialized == 0); + code = TT_Init_FreeType(&tt_handle); + if(code) { + DEBUG((DBG_TT, "(tt) Init_Freetype: error %d\n", code)); + return -1; + } + code = TT_Init_Post_Extension(tt_handle); + if(code) { + TT_Done_FreeType(tt_handle); + return -1; + } + /* we're on */ + initialized = 1; + return 0; +} + +static void tt_encode_font(DviFont *font, FTInfo *info) +{ + TT_Face_Properties prop; + int i; + + if(TT_Get_Face_Properties(info->face, &prop)) + return; + + for(i = 0; i < prop.num_Glyphs; i++) { + char *string; + int ndx; + + if(TT_Get_PS_Name(info->face, i, &string)) + continue; + ndx = mdvi_encode_glyph(info->encoding, string); + if(ndx < font->loc || ndx > font->hic) + continue; + font->chars[ndx - font->loc].code = i; + } +} + +static int tt_really_load_font(DviParams *params, DviFont *font, FTInfo *info) +{ + DviFontChar *ch; + TFMChar *ptr; + Int32 z, alpha, beta; + int i; + FTInfo *old; + TT_Error status; + double point_size; + static int warned = 0; + TT_CharMap cmap; + TT_Face_Properties props; + int map_found; + + DEBUG((DBG_TT, "(tt) really_load_font(%s)\n", info->fontname)); + + /* get the point size */ + point_size = (double)font->scale / (params->tfm_conv * 0x100000); + point_size = 72.0 * point_size / 72.27; + if(info->loaded) { + /* just reset the size info */ + TT_Set_Instance_Resolutions(info->instance, + params->dpi, params->vdpi); + TT_Set_Instance_CharSize(info->instance, FROUND(point_size * 64)); + /* FIXME: should extend/slant again */ + info->hasmetrics = 1; + return 0; + } + + /* load the face */ + DEBUG((DBG_TT, "(tt) loading new face `%s'\n", + info->fontname)); + status = TT_Open_Face(tt_handle, font->filename, &info->face); + if(status) { + mdvi_warning(_("(tt) %s: could not load face: %s\n"), + info->fontname, TT_ErrToString18(status)); + return -1; + } + + /* create a new instance of this face */ + status = TT_New_Instance(info->face, &info->instance); + if(status) { + mdvi_warning(_("(tt) %s: could not create face: %s\n"), + info->fontname, TT_ErrToString18(status)); + TT_Close_Face(info->face); + return -1; + } + + /* create a glyph */ + status = TT_New_Glyph(info->face, &info->glyph); + if(status) { + mdvi_warning(_("(tt) %s: could not create glyph: %s\n"), + info->fontname, TT_ErrToString18(status)); + goto tt_error; + } + + /* + * We'll try to find a Unicode charmap. It's not that important that we + * actually find one, especially if the fontmap files are installed + * properly, but it's good to have some predefined behaviour + */ + TT_Get_Face_Properties(info->face, &props); + + map_found = -1; + for(i = 0; map_found < 0 && i < props.num_CharMaps; i++) { + TT_UShort pid, eid; + + TT_Get_CharMap_ID(info->face, i, &pid, &eid); + switch(pid) { + case TT_PLATFORM_APPLE_UNICODE: + map_found = i; + break; + case TT_PLATFORM_ISO: + if(eid == TT_ISO_ID_7BIT_ASCII || + eid == TT_ISO_ID_8859_1) + map_found = 1; + break; + case TT_PLATFORM_MICROSOFT: + if(eid == TT_MS_ID_UNICODE_CS) + map_found = 1; + break; + } + } + if(map_found < 0) { + mdvi_warning(_("(tt) %s: no acceptable map found, using #0\n"), + info->fontname); + map_found = 0; + } + DEBUG((DBG_TT, "(tt) %s: using charmap #%d\n", + info->fontname, map_found)); + TT_Get_CharMap(info->face, map_found, &cmap); + + DEBUG((DBG_TT, "(tt) %s: Set_Char_Size(%.2f, %d, %d)\n", + font->fontname, point_size, font->hdpi, font->vdpi)); + status = TT_Set_Instance_Resolutions(info->instance, + params->dpi, params->vdpi); + if(status) { + error(_("(tt) %s: could not set resolution: %s\n"), + info->fontname, TT_ErrToString18(status)); + goto tt_error; + } + status = TT_Set_Instance_CharSize(info->instance, + FROUND(point_size * 64)); + if(status) { + error(_("(tt) %s: could not set point size: %s\n"), + info->fontname, TT_ErrToString18(status)); + goto tt_error; + } + + /* after this point we don't fail */ + + /* get information from the fontmap */ + status = mdvi_query_fontmap(&info->mapinfo, info->fontname); + if(!status && info->mapinfo.encoding) + info->encoding = mdvi_request_encoding(info->mapinfo.encoding); + else + info->encoding = NULL; + + if(info->encoding != NULL) { + TT_Post post; + + status = TT_Load_PS_Names(info->face, &post); + if(status) { + mdvi_warning(_("(tt) %s: could not load PS name table\n"), + info->fontname); + mdvi_release_encoding(info->encoding, 0); + info->encoding = NULL; + } + } + + /* get the metrics. If this fails, it's not fatal, but certainly bad */ + info->tfminfo = get_font_metrics(info->fontname, + info->fmftype, info->fmfname); + + if(info->tfminfo == NULL) { + mdvi_warning("(tt) %s: no metrics data, font ignored\n", + info->fontname); + goto tt_error; + } + /* fix this */ + font->design = info->tfminfo->design; + + /* get the scaled character metrics */ + get_tfm_chars(params, font, info->tfminfo, 0); + + if(info->encoding) + tt_encode_font(font, info); + else { + mdvi_warning(_("%s: no encoding vector found, expect bad output\n"), + info->fontname); + /* this is better than nothing */ + for(i = font->loc; i <= font->hic; i++) + font->chars[i - font->loc].code = TT_Char_Index(cmap, i); + } + + info->loaded = 1; + info->hasmetrics = 1; + return 0; + +tt_error: + tt_font_remove(info); + mdvi_free(font->chars); + font->chars = NULL; + font->loc = font->hic = 0; + return -1; +} + +static int tt_load_font(DviParams *params, DviFont *font) +{ + int i; + FTInfo *info; + + if(!initialized && init_freetype() < 0) + return -1; + + if(font->in != NULL) { + fclose(font->in); + font->in = NULL; + } + + info = xalloc(FTInfo); + + memzero(info, sizeof(FTInfo)); + info->fmftype = DviFontAny; /* any metrics type will do */ + info->fmfname = lookup_font_metrics(font->fontname, &info->fmftype); + info->fontname = font->fontname; + info->hasmetrics = 0; + info->loaded = 0; + + /* these will be obtained from the fontmaps */ + info->mapinfo.psname = NULL; + info->mapinfo.encoding = NULL; + info->mapinfo.fontfile = NULL; + info->mapinfo.extend = 0; + info->mapinfo.slant = 0; + + /* initialize these */ + font->chars = xnalloc(DviFontChar, 256); + font->loc = 0; + font->hic = 255; + for(i = 0; i < 256; i++) { + font->chars[i].offset = 1; + font->chars[i].glyph.data = NULL; + font->chars[i].shrunk.data = NULL; + font->chars[i].grey.data = NULL; + } + + if(info->fmfname == NULL) + mdvi_warning(_("(tt) %s: no font metric data\n"), font->fontname); + + listh_append(&ttfonts, LIST(info)); + font->private = info; + + return 0; +} + +static int tt_get_bitmap(DviParams *params, DviFont *font, + int code, double xscale, double yscale, DviGlyph *glyph) +{ + TT_Outline outline; + TT_Raster_Map raster; + TT_BBox bbox; + TT_Glyph_Metrics metrics; + TT_Matrix mat; + FTInfo *info; + int error; + int have_outline = 0; + int w, h; + + info = (FTInfo *)font->private; + if(info == NULL) + return -1; + + error = TT_Load_Glyph(info->instance, info->glyph, + code, TTLOAD_DEFAULT); + if(error) goto tt_error; + error = TT_Get_Glyph_Outline(info->glyph, &outline); + if(error) goto tt_error; + have_outline = 1; + mat.xx = FROUND(xscale * 65536); + mat.yy = FROUND(yscale * 65536); + mat.yx = 0; + mat.xy = 0; + TT_Transform_Outline(&outline, &mat); + error = TT_Get_Outline_BBox(&outline, &bbox); + if(error) goto tt_error; + bbox.xMin &= -64; + bbox.yMin &= -64; + bbox.xMax = (bbox.xMax + 63) & -64; + bbox.yMax = (bbox.yMax + 63) & -64; + w = (bbox.xMax - bbox.xMin) / 64; + h = (bbox.yMax - bbox.yMin) / 64; + + glyph->w = w; + glyph->h = h; + glyph->x = -bbox.xMin / 64; + glyph->y = bbox.yMax / 64; + if(!w || !h) + goto tt_error; + raster.rows = h; + raster.width = w; + raster.cols = ROUND(w, 8); + raster.size = h * raster.cols; + raster.flow = TT_Flow_Down; + raster.bitmap = mdvi_calloc(h, raster.cols); + + TT_Translate_Outline(&outline, -bbox.xMin, -bbox.yMin); + TT_Get_Outline_Bitmap(tt_handle, &outline, &raster); + glyph->data = bitmap_convert_msb8(raster.bitmap, w, h, ROUND(w, 8)); + TT_Done_Outline(&outline); + mdvi_free(raster.bitmap); + + return 0; +tt_error: + if(have_outline) + TT_Done_Outline(&outline); + return -1; +} + +static int tt_font_get_glyph(DviParams *params, DviFont *font, int code) +{ + FTInfo *info = (FTInfo *)font->private; + DviFontChar *ch; + int error; + double xs, ys; + int dpi; + + ASSERT(info != NULL); + if(!info->hasmetrics && tt_really_load_font(params, font, info) < 0) + return -1; + ch = FONTCHAR(font, code); + if(!ch || !glyph_present(ch)) + return -1; + ch->loaded = 1; + if(!ch->width || !ch->height) + goto blank; + if(ch->code == 0) { + ch->glyph.data = NULL; + goto missing; + } + /* get the glyph */ + dpi = Max(font->hdpi, font->vdpi); + error = tt_get_bitmap(params, font, ch->code, + (double)font->hdpi / dpi, + (double)font->vdpi / dpi, + &ch->glyph); + if(error) + goto missing; + ch->x = ch->glyph.x; + ch->y = ch->glyph.y; + + return 0; + +missing: + ch->glyph.data = MDVI_GLYPH_EMPTY; + ch->missing = 1; +blank: + ch->glyph.w = ch->width; + ch->glyph.h = ch->height; + ch->glyph.x = ch->x; + ch->glyph.y = ch->y; + return 0; +} + +static void tt_shrink_glyph(DviContext *dvi, DviFont *font, DviFontChar *ch, DviGlyph *dest) +{ + tt_get_bitmap(&dvi->params, font, + ch->code, + (double)font->hdpi / (dvi->params.dpi * dvi->params.hshrink), + (double)font->vdpi / (dvi->params.vdpi * dvi->params.vshrink), + dest); + /* transform the glyph for the current orientation */ + font_transform_glyph(dvi->params.orientation, dest); +} + +static void tt_reset_font(DviFont *font) +{ + FTInfo *info = (FTInfo *)font->private; + + if(info == NULL) + return; + info->hasmetrics = 0; +} + +static void tt_font_remove(FTInfo *info) +{ + FTInfo *old; + + if(info->loaded) { + /* all fonts in the hash table have called TT_Open_Face */ + TT_Done_Instance(info->instance); + TT_Close_Face(info->face); + } + listh_remove(&ttfonts, LIST(info)); + /* release our encodings */ + if(info->encoding) + mdvi_release_encoding(info->encoding, 1); + /* and destroy the font */ + if(info->tfminfo) + free_font_metrics(info->tfminfo); + if(info->fmfname) + mdvi_free(info->fmfname); + mdvi_free(info); +} + +static void tt_free_data(DviFont *font) +{ + if(font->private == NULL) + return; + + tt_font_remove((FTInfo *)font->private); + if(initialized && ttfonts.count == 0) { + DEBUG((DBG_TT, "(tt) last font removed -- closing FreeType\n")); + TT_Done_FreeType(tt_handle); + initialized = 0; + } +} + +#endif /* WITH_TRUETYPE_FONTS */ diff --git a/backend/dvi/mdvi-lib/util.c b/backend/dvi/mdvi-lib/util.c new file mode 100644 index 00000000..349d273a --- /dev/null +++ b/backend/dvi/mdvi-lib/util.c @@ -0,0 +1,550 @@ +/* + * Copyright (C) 2000, Matias Atria + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#include <config.h> +#include <stdio.h> +#include <stdlib.h> +#include <stdarg.h> +#include <string.h> +#include <time.h> +#include <sys/stat.h> +#include <errno.h> +#include <unistd.h> + +#include "common.h" +#include "private.h" + +static char *const messages[] = { + _G("Ooops!"), + _G("Aieeeee!!"), + _G("Ouch!"), + _G("Houston, we have a problem"), + _G("3.. 2.. 1.. BOOM!"), + _G("I'm history"), + _G("I'm going down"), + _G("I smell a rat") +}; +#define NMSGS (sizeof(messages) / sizeof(char *)) + +static FILE *logfile = NULL; +static int _mdvi_log_level; + +int mdvi_set_logfile(const char *filename); +int mdvi_set_logstream(FILE *file); +int mdvi_set_loglevel(int level); + +static void vputlog(int level, const char *head, const char *format, va_list ap) +{ + if(logfile != NULL && _mdvi_log_level >= level) { + if(head != NULL) + fprintf(logfile, "%s: ", head); + vfprintf(logfile, format, ap); + } +} + +int mdvi_set_logfile(const char *filename) +{ + FILE *f = NULL; + + if(filename && (f = fopen(filename, "w")) == NULL) + return -1; + if(logfile != NULL && !isatty(fileno(logfile))) { + fclose(logfile); + logfile = NULL; + } + if(filename) + logfile = f; + return 0; +} + +int mdvi_set_logstream(FILE *file) +{ + if(logfile && !isatty(fileno(logfile))) { + fclose(logfile); + logfile = NULL; + } + logfile = file; + return 0; +} + +int mdvi_set_loglevel(int level) +{ + int old = _mdvi_log_level; + + _mdvi_log_level = level; + return old; +} + +#ifndef NODEBUG +Uint32 _mdvi_debug_mask = 0; + +void __debug(int mask, const char *format, ...) +{ + va_list ap; + + va_start(ap, format); + if(_mdvi_debug_mask & mask) { + if(!DEBUGGING(SILENT)) { + fprintf(stderr, "Debug: "); + vfprintf(stderr, format, ap); + fflush(stderr); + } +#ifndef __GNUC__ + /* let's be portable */ + va_end(ap); + va_start(ap, format); +#endif + vputlog(LOG_DEBUG, "Debug", format, ap); + } + va_end(ap); +} +#endif + +void mdvi_message(const char *format, ...) +{ + va_list ap; + + va_start(ap, format); + if(_mdvi_log_level >= LOG_INFO) { + fprintf(stderr, "%s: ", program_name); + vfprintf(stderr, format, ap); +#ifndef __GNUC__ + va_end(ap); + va_start(ap, format); +#endif + } + vputlog(LOG_INFO, NULL, format, ap); + va_end(ap); +} + +void mdvi_crash(const char *format, ...) +{ + va_list ap; + + va_start(ap, format); + fprintf(stderr, "%s: %s: ", + program_name, + gettext(messages[(int)time(NULL) % NMSGS])); + vfprintf(stderr, format, ap); +#ifndef __GNUC__ + /* let's be portable */ + va_end(ap); + va_start(ap, format); +#endif + vputlog(LOG_ERROR, _("Crashing"), format, ap); + va_end(ap); + abort(); +} + +void mdvi_error(const char *format, ...) +{ + va_list ap; + + va_start(ap, format); + fprintf(stderr, _("%s: Error: "), program_name); + vfprintf(stderr, format, ap); +#ifndef __GNUC__ + /* let's be portable */ + va_end(ap); + va_start(ap, format); +#endif + vputlog(LOG_ERROR, _("Error"), format, ap); + va_end(ap); +} + +void mdvi_warning(const char *format, ...) +{ + va_list ap; + + va_start(ap, format); + fprintf(stderr, _("%s: Warning: "), program_name); + vfprintf(stderr, format, ap); +#ifndef __GNUC__ + /* let's be portable */ + va_end(ap); + va_start(ap, format); +#endif + vputlog(LOG_WARN, _("Warning"), format, ap); + va_end(ap); +} + +void mdvi_fatal(const char *format, ...) +{ + va_list ap; + + va_start(ap, format); + fprintf(stderr, _("%s: Fatal: "), program_name); + vfprintf(stderr, format, ap); +#ifndef __GNUC__ + /* let's be portable */ + va_end(ap); + va_start(ap, format); +#endif + vputlog(LOG_ERROR, _("Fatal"), format, ap); + va_end(ap); +#ifndef NODEBUG + abort(); +#else + exit(EXIT_FAILURE); +#endif +} + +void *mdvi_malloc(size_t nelems) +{ + void *ptr = malloc(nelems); + + if(ptr == NULL) + mdvi_fatal(_("out of memory allocating %u bytes\n"), + (unsigned)nelems); + return ptr; +} + +void *mdvi_realloc(void *data, size_t newsize) +{ + void *ptr; + + if(newsize == 0) + mdvi_crash(_("attempted to reallocate with zero size\n")); + ptr = realloc(data, newsize); + if(ptr == NULL) + mdvi_fatal(_("failed to reallocate %u bytes\n"), (unsigned)newsize); + return ptr; +} + +void *mdvi_calloc(size_t nmemb, size_t size) +{ + void *ptr; + + if(nmemb == 0) + mdvi_crash(_("attempted to callocate 0 members\n")); + if(size == 0) + mdvi_crash(_("attempted to callocate %u members with size 0\n"), + (unsigned)nmemb); + ptr = calloc(nmemb, size); + if(ptr == 0) + mdvi_fatal(_("failed to allocate %ux%u bytes\n"), + (unsigned)nmemb, (unsigned)size); + return ptr; +} + +void mdvi_free(void *ptr) +{ + if(ptr == NULL) + mdvi_crash(_("attempted to free NULL pointer\n")); + free(ptr); +} + +char *mdvi_strdup(const char *string) +{ + int length; + char *ptr; + + length = strlen(string) + 1; + ptr = (char *)mdvi_malloc(length); + memcpy(ptr, string, length); + return ptr; +} + +/* `to' should have room for length+1 bytes */ +char *mdvi_strncpy(char *to, const char *from, size_t length) +{ + strncpy(to, from, length); + to[length] = '\0'; + return to; +} + +char *mdvi_strndup(const char *string, size_t length) +{ + int n; + char *ptr; + + n = strlen(string); + if(n > length) + n = length; + ptr = (char *)mdvi_malloc(n + 1); + memcpy(ptr, string, n); + return ptr; +} + +void *mdvi_memdup(const void *data, size_t length) +{ + void *ptr = mdvi_malloc(length); + + memcpy(ptr, data, length); + return ptr; +} + +char *mdvi_strrstr (const char *haystack, const char *needle) +{ + size_t i; + size_t needle_len; + size_t haystack_len; + const char *p; + + needle_len = strlen (needle); + haystack_len = strlen (haystack); + + if (needle_len == 0) + return NULL; + + if (haystack_len < needle_len) + return (char *)haystack; + + p = haystack + haystack_len - needle_len; + while (p >= haystack) { + for (i = 0; i < needle_len; i++) + if (p[i] != needle[i]) + goto next; + + return (char *)p; + + next: + p--; + } + + return NULL; +} + +char *mdvi_build_path_from_cwd (const char *path) +{ + char *ptr; + char *buf = NULL; + size_t buf_size = 512; + + while (1) { + buf = mdvi_realloc (buf, buf_size); + if ((ptr = getcwd (buf, buf_size)) == NULL && errno == ERANGE) { + buf_size *= 2; + } else { + buf = ptr; + break; + } + } + + buf = mdvi_realloc (buf, strlen (buf) + strlen (path) + 2); + strcat (buf, "/"); + strncat (buf, path, strlen (path)); + + return buf; +} + +double unit2pix_factor(const char *spec) +{ + double val; + double factor; + const char *p, *q; + static const char *units = "incmmmmtptpcddccspbpftydcs"; + + val = 0.0; + + for(p = spec; *p >= '0' && *p <= '9'; p++) + val = 10.0 * val + (double)(*p - '0'); + if(*p == '.') { + p++; + factor = 0.1; + while(*p && *p >= '0' && *p <= '9') { + val += (*p++ - '0') * factor; + factor = factor * 0.1; + } + } + factor = 1.0; + for(q = units; *q; q += 2) { + if(p[0] == q[0] && p[1] == q[1]) + break; + } + switch((int)(q - units)) { + /*in*/ case 0: factor = 1.0; break; + /*cm*/ case 2: factor = 1.0 / 2.54; break; + /*mm*/ case 4: factor = 1.0 / 25.4; break; + /*mt*/ case 6: factor = 1.0 / 0.0254; break; + /*pt*/ case 8: factor = 1.0 / 72.27; break; + /*pc*/ case 10: factor = 12.0 / 72.27; break; + /*dd*/ case 12: factor = (1238.0 / 1157.0) / 72.27; break; + /*cc*/ case 14: factor = 12 * (1238.0 / 1157.0) / 72.27; break; + /*sp*/ case 16: factor = 1.0 / (72.27 * 65536); break; + /*bp*/ case 18: factor = 1.0 / 72.0; break; + /*ft*/ case 20: factor = 12.0; break; + /*yd*/ case 22: factor = 36.0; break; + /*cs*/ case 24: factor = 1.0 / 72000.0; break; + default: factor = 1.0; + } + return factor * val; +} + +int unit2pix(int dpi, const char *spec) +{ + double factor = unit2pix_factor(spec); + + return (int)(factor * dpi + 0.5); +} + +Ulong get_mtime(int fd) +{ + struct stat st; + + if(fstat(fd, &st) == 0) + return (Ulong)st.st_mtime; + return 0; +} + +char *xstradd(char *dest, size_t *size, size_t n, const char *src, size_t m) +{ + if(m == 0) + m = strlen(src); + if(n + m >= *size) { + dest = mdvi_realloc(dest, n + m + 1); + *size = n + m + 1; + } + memcpy(dest + n, src, m); + dest[n + m] = 0; + return dest; +} + +char *getword(char *string, const char *delim, char **end) +{ + char *ptr; + char *word; + + /* skip leading delimiters */ + for(ptr = string; *ptr && strchr(delim, *ptr); ptr++); + + if(*ptr == 0) + return NULL; + word = ptr++; + /* skip non-delimiters */ + while(*ptr && !strchr(delim, *ptr)) + ptr++; + *end = (char *)ptr; + return word; +} + +char *getstring(char *string, const char *delim, char **end) +{ + char *ptr; + char *word; + int quoted = 0; + + /* skip leading delimiters */ + for(ptr = string; *ptr && strchr(delim, *ptr); ptr++); + + if(ptr == NULL) + return NULL; + quoted = (*ptr == '"'); + if(quoted) + for(word = ++ptr; *ptr && *ptr != '"'; ptr++); + else + for(word = ptr; *ptr && !strchr(delim, *ptr); ptr++); + *end = (char *)ptr; + return word; +} + +static long pow2(size_t n) +{ + long x = 8; /* don't bother allocating less than this */ + + while(x < n) + x <<= 1L; + return x; +} + +void dstring_init(Dstring *dstr) +{ + dstr->data = NULL; + dstr->size = 0; + dstr->length = 0; +} + +int dstring_append(Dstring *dstr, const char *string, int len) +{ + if(len < 0) + len = strlen(string); + if(len) { + if(dstr->length + len >= dstr->size) { + dstr->size = pow2(dstr->length + len + 1); + dstr->data = mdvi_realloc(dstr->data, dstr->size); + } + memcpy(dstr->data + dstr->length, string, len); + dstr->length += len; + dstr->data[dstr->length] = 0; + } else if(dstr->size == 0) { + ASSERT(dstr->data == NULL); + dstr->size = 8; + dstr->data = mdvi_malloc(8); + dstr->data[0] = 0; + } + + return dstr->length; +} + +int dstring_copy(Dstring *dstr, int pos, const char *string, int len) +{ + ASSERT(pos >= 0); + if(len < 0) + len = strlen(string); + if(len) { + if(pos + len >= dstr->length) { + dstr->length = pos; + return dstring_append(dstr, string, len); + } + memcpy(dstr->data + pos, string, len); + } + return dstr->length; +} + +int dstring_insert(Dstring *dstr, int pos, const char *string, int len) +{ + ASSERT(pos >= 0); + if(pos == dstr->length) + return dstring_append(dstr, string, len); + if(len < 0) + len = strlen(string); + if(len) { + if(dstr->length + len >= dstr->size) { + dstr->size = pow2(dstr->length + len + 1); + dstr->data = mdvi_realloc(dstr->data, dstr->size); + } + /* make room */ + memmove(dstr->data + pos, dstr->data + pos + len, len); + /* now copy */ + memcpy(dstr->data + pos, string, len); + dstr->length += len; + dstr->data[dstr->length] = 0; + } + return dstr->length; +} + +int dstring_new(Dstring *dstr, const char *string, int len) +{ + if(len < 0) + len = strlen(string); + if(len) { + dstr->size = pow2(len + 1); + dstr->data = mdvi_malloc(dstr->size * len); + memcpy(dstr->data, string, len); + } else + dstring_init(dstr); + return dstr->length; +} + +void dstring_reset(Dstring *dstr) +{ + if(dstr->data) + mdvi_free(dstr->data); + dstring_init(dstr); +} + diff --git a/backend/dvi/mdvi-lib/vf.c b/backend/dvi/mdvi-lib/vf.c new file mode 100644 index 00000000..fb498476 --- /dev/null +++ b/backend/dvi/mdvi-lib/vf.c @@ -0,0 +1,241 @@ +/* vf.c -- VF font support */ +/* + * Copyright (C) 2000, Matias Atria + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + */ + +#include <config.h> +#include <string.h> + +#include "mdvi.h" +#include "private.h" + +static int vf_load_font __PROTO((DviParams *, DviFont *)); +static void vf_free_macros __PROTO((DviFont *)); + +/* only symbol exported by this file */ +DviFontInfo vf_font_info = { + "VF", + 1, /* virtual fonts scale just fine */ + vf_load_font, + NULL, /* get_glyph */ + NULL, /* shrink0 */ + NULL, /* shrink1 */ + vf_free_macros, + NULL, /* reset */ + NULL, /* lookup */ + kpse_vf_format, + NULL +}; + +DviFontInfo ovf_font_info = { + "OVF", + 1, /* virtual fonts scale just fine */ + vf_load_font, + NULL, /* get_glyph */ + NULL, /* shrink0 */ + NULL, /* shrink1 */ + vf_free_macros, + NULL, /* reset */ + NULL, /* lookup */ + kpse_ovf_format, + NULL +}; + +static int vf_load_font(DviParams *params, DviFont *font) +{ + FILE *p; + Uchar *macros; + int msize; + int mlen; + Int32 checksum; + long alpha, beta, z; + int op; + int i; + int nchars; + int loc, hic; + DviFontRef *last; + + macros = NULL; + msize = mlen = 0; + p = font->in; + + if(fuget1(p) != 247 || fuget1(p) != 202) + goto badvf; + mlen = fuget1(p); + fseek(p, (long)mlen, SEEK_CUR); + checksum = fuget4(p); + if(checksum && font->checksum && checksum != font->checksum) { + mdvi_warning(_("%s: Checksum mismatch (expected %u, got %u)\n"), + font->fontname, font->checksum, checksum); + } else if(!font->checksum) + font->checksum = checksum; + font->design = fuget4(p); + + /* read all the fonts in the preamble */ + last = NULL; + + /* initialize alpha, beta and z for TFM width computation */ + TFMPREPARE(font->scale, z, alpha, beta); + + op = fuget1(p); + while(op >= DVI_FNT_DEF1 && op <= DVI_FNT_DEF4) { + DviFontRef *ref; + Int32 scale, design; + Uint32 checksum; + int id; + int n; + int hdpi; + int vdpi; + char *name; + + /* process fnt_def commands */ + + id = fugetn(p, op - DVI_FNT_DEF1 + 1); + checksum = fuget4(p); + scale = fuget4(p); + design = fuget4(p); + + /* scale this font according to our parent's scale */ + scale = TFMSCALE(scale, z, alpha, beta); + design = FROUND(params->tfm_conv * design); + + /* compute the resolution */ + hdpi = FROUND(params->mag * params->dpi * scale / design); + vdpi = FROUND(params->mag * params->vdpi * scale / design); + n = fuget1(p) + fuget1(p); + name = mdvi_malloc(n + 1); + fread(name, 1, n, p); + name[n] = 0; + DEBUG((DBG_FONTS, "(vf) %s: defined font `%s' at %.1fpt (%dx%d dpi)\n", + font->fontname, name, + (double)scale / (params->tfm_conv * 0x100000), hdpi, vdpi)); + + /* get the font */ + ref = font_reference(params, id, name, checksum, hdpi, vdpi, scale); + if(ref == NULL) { + mdvi_error(_("(vf) %s: could not load font `%s'\n"), + font->fontname, name); + goto error; + } + mdvi_free(name); + if(last == NULL) + font->subfonts = last = ref; + else + last->next = ref; + ref->next = NULL; + op = fuget1(p); + } + + if(op >= DVI_FNT_DEF1 && op <= DVI_FNT_DEF4) + goto error; + + /* This function correctly reads both .vf and .ovf files */ + + font->chars = xnalloc(DviFontChar, 256); + for(i = 0; i < 256; i++) + font->chars[i].offset = 0; + nchars = 256; + loc = -1; hic = -1; + /* now read the characters themselves */ + while(op <= 242) { + int pl; + Int32 cc; + Int32 tfm; + + if(op == 242) { + pl = fuget4(p); + cc = fuget4(p); + tfm = fuget4(p); + } else { + pl = op; + cc = fuget1(p); + tfm = fuget3(p); + } + if(loc < 0 || cc < loc) + loc = cc; + if(hic < 0 || cc > hic) + hic = cc; + if(cc >= nchars) { + font->chars = xresize(font->chars, + DviFontChar, cc + 16); + for(i = nchars; i < cc + 16; i++) + font->chars[i].offset = 0; + nchars = cc + 16; + } + if(font->chars[cc].offset) { + mdvi_error(_("(vf) %s: character %d redefined\n"), + font->fontname, cc); + goto error; + } + + DEBUG((DBG_GLYPHS, "(vf) %s: defined character %d (macro length %d)\n", + font->fontname, cc, pl)); + font->chars[cc].width = pl + 1; + font->chars[cc].code = cc; + font->chars[cc].tfmwidth = TFMSCALE(tfm, z, alpha, beta); + font->chars[cc].offset = mlen; + font->chars[cc].loaded = 1; + if(mlen + pl + 1 > msize) { + msize = mlen + pl + 256; + macros = xresize(macros, Uchar, msize); + } + if(pl && fread(macros + mlen, 1, pl, p) != pl) + break; + macros[mlen+pl] = DVI_EOP; + mlen += pl + 1; + op = fuget1(p); + } + if(op != 248) { + mdvi_error(_("(vf) %s: no postamble\n"), font->fontname); + goto error; + } + + /* make macro memory just big enough */ + if(msize > mlen) { + macros = xresize(macros, Uchar, mlen); + msize = mlen; + } + + DEBUG((DBG_FONTS|DBG_GLYPHS, + "(vf) %s: macros use %d bytes\n", font->fontname, msize)); + + if(loc > 0 || hic < nchars-1) { + memmove(font->chars, font->chars + loc, + (hic - loc + 1) * sizeof(DviFontChar)); + font->chars = xresize(font->chars, + DviFontChar, hic - loc + 1); + } + font->loc = loc; + font->hic = hic; + font->private = macros; + + return 0; + +badvf: + mdvi_error(_("%s: File corrupted, or not a VF file.\n"), font->fontname); +error: + if(font->chars) + mdvi_free(font->chars); + if(macros) + mdvi_free(macros); + return -1; +} + +static void vf_free_macros(DviFont *font) +{ + mdvi_free(font->private); +} |