2020-09-30 17:12:29 +02:00

749 lines
24 KiB
C++

/***************************************************************************
*
* File Name: newform4.cpp
*
* Copyright (C) 1993-1996 Hewlett-Packard Company.
* All rights reserved.
*
* 11311 Chinden Blvd.
* Boise, Idaho 83714
*
* This is a part of the HP JetAdmin Printer Utility
*
* This source code is only intended as a supplement for support and
* localization of HP JetAdmin by 3rd party Operating System vendors.
* Modification of source code cannot be made without the express written
* consent of Hewlett-Packard.
*
*
* Description:
*
* Author: Name
*
*
* Modification history:
*
* date initials change description
*
* mm-dd-yy MJB
*
*
*
*
*
*
***************************************************************************/
/* newform4.c - 7/21/93 by mdt - contains function buildNewFormat4 used
by cmapchange program. Also intended for use by new
version of payttlib. */
/* NOTE: the following #includes are for compilation by
Make_cmapchange. Other makefiles may want a different
set of #includes. */
#include <pch_c.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#include "types.hpp"
#include "strncase.hpp"
#include "ttf2tte.hpp"
#include "ttread.hpp"
#define NULL_CHAR 0xFFFF
typedef enum {DEC, HEX} numberFormat;
static void extract_lists_from_tt_f4_t (tt_f4_t *f4, ushort *numCodes,
ushort **codes, ushort **glyphList);
static void calcSegCountAndArraySize (ushort numCodes, ushort *codes,
ushort *glyphList, ushort *segCount,
ushort *sizeGlyphIdArray);
static void measureFormat4Segment(ushort numCodes, ushort *startIndex,
ushort *codes, ushort *glyphList, ushort *lengthSeg,
tt_boolean *consecutiveGlyphIDs);
static void getNewCodes (FILE *symfile, ushort *numCodes,
ushort **codes, ushort **matchingCodes);
static void parseSymfileHeader (FILE *symstream, ushort *firstCode,
ushort *lastCode, numberFormat *matchingCodeFormat);
static void parseSymfileMap (FILE *symfile, ushort firstCode, ushort lastCode,
numberFormat matchFormat, ushort *codes,
ushort *matchingCodes, ushort *count);
static void matchNewCodeToGlyph (ushort numNewCodes, ushort *matchingCodes,
ushort numOldCodes, ushort *oldCodes, ushort *glyphList);
static ushort get_glyphID_form4 (ushort seg, ushort current_code, tt_f4_t *f4);
static ushort findNearestExponent (ushort number);
/****************************************************************/
/*
dbm dbm dbm dbm
HACK HACK HACK HACK
FIX ME FIXME
Who will free the memory for tt_f4_t ?
*/
void buildNewFormat4 (FILE *symfile, tt_f4_t *oldF4, tt_f4_t *newF4)
{
ushort numNewCodes, *newCodes, *matchingCodes,
numOldCodes, *oldCodes, *glyphList;
if (AbortState == bTrue) return;
if ((symfile == 0) ||
(oldF4 == NULL) ||
(newF4 == NULL))
{
SetAbortState;
return;
}
extract_lists_from_tt_f4_t (oldF4, &numOldCodes, &oldCodes, &glyphList);
// read symfile and produce a list of new character codes and
// a list of matching codes in the original indexing system.
// symfile is assumed to be in .sym format. After /symbols =,
// each line will begin with a new code in decimal and a matching
// code either in decimal or hexadecimal
// dbm allocates memory that must be free'd
getNewCodes (symfile, &numNewCodes, &newCodes, &matchingCodes);
// for each new code, find its glyph by cross-referencing the
// matching code. matchNewCodeToGlyph will write glyph indices
// into the matchingCodes array
matchNewCodeToGlyph (numNewCodes, matchingCodes, numOldCodes,
oldCodes, glyphList);
delete [] oldCodes;
delete [] glyphList;
build_tt_f4_t_from_lists (newF4, numNewCodes, newCodes, matchingCodes);
delete [] newCodes;
delete [] matchingCodes;
} // buildNewFormat4
/*************************************************************************/
/*
This function allocates memory that
must be free'd by the caller.
*/
static void extract_lists_from_tt_f4_t (tt_f4_t *f4, ushort *numCodes,
ushort **codes, ushort **glyphList)
{
ushort seg, codeCount, charCode;
if (AbortState == bTrue) return;
if ((f4 == NULL) ||
(codes == NULL) ||
(glyphList == NULL))
{
SetAbortState;
return;
}
codeCount = 0;
for (seg = 0; seg < f4->segCount - 1; seg++)
codeCount += ((f4->endCount)[seg] - (f4->startCount)[seg] + 1);
*numCodes = codeCount;
*codes = new ushort[codeCount];
*glyphList = new ushort[codeCount];
if ((NULL == *codes) || (NULL == *glyphList))
{
// Unable to get space for lists of codes and glyphs.
SetAbortState;
return;
}
codeCount = 0;
for (seg = 0; seg < f4->segCount - 1; seg++)
{
for (charCode = (f4->startCount)[seg];
charCode <= (f4->endCount)[seg]; charCode++)
{
(*codes)[codeCount] = charCode;
(*glyphList)[codeCount] = get_glyphID_form4 (seg, charCode, f4);
codeCount ++;
}
}
} // extract_lists_from_tt_f4_t
/*******************************************************************/
static ushort get_glyphID_form4 (ushort seg, ushort current_code,
tt_f4_t *f4)
{
ushort bytes_advance, words_advance, index_from_RangeOffset;
short index;
if (AbortState == bTrue) return 0;
if (f4 == NULL)
{
SetAbortState;
return 0;
}
if ((f4->idRangeOffset)[seg] == 0)
{
return (current_code + (f4->idDelta)[seg]);
}
else
{
/* 12/2/91: my interpretation of "obscure indexing trick" described
on p. 231 of TrueType Font Files. */
bytes_advance = (f4->idRangeOffset)[seg];
words_advance = bytes_advance >> 1;
index_from_RangeOffset = words_advance + seg +
(current_code - (f4->startCount)[seg]);
index = (short)(index_from_RangeOffset - f4->segCount);
if (index < 0)
{
SetAbortState;
return (ushort)~0;
}
else if ((f4->glyphIdArray)[index] == 0)
{
return (0);
}
else
{
return ((f4->glyphIdArray)[index] +
(f4->idDelta)[seg]);
}
}
} // get_glyphID_form4
/*************************************************************/
/*
dbm dbm dbm dbm
HACK HACK HACK
FIX ME FIXME
*codes and *glyphList have already been allocated.
newF4->UshortArray gets allocated inside here.
We need to free them.
*/
void build_tt_f4_t_from_lists (tt_f4_t *newF4, ushort numCodes,
ushort *codes, ushort *glyphList)
{
ushort sizeGlyphIdArray, entrySelector, searchRange,
seg, index, gIdArrayIndex, nextIndex, nextCode,
lengthSeg; /* lengthSeg only needed as a parameter */
tt_boolean consecutiveGlyphIDs;
ushort idDeltaCt, explicitCt, fillerCt;
if (AbortState == bTrue) return;
if (newF4 == NULL)
{
SetAbortState;
return;
}
idDeltaCt = explicitCt = fillerCt = 0;
calcSegCountAndArraySize (numCodes, codes, glyphList,
&(newF4->segCount), &sizeGlyphIdArray);
newF4->length = sizeof(ushort) *
(8 + (4 * (newF4->segCount)) + sizeGlyphIdArray);
// dbm dbm dbm dbm
// HACK HACK HACK HACK
// FIX ME FIXME
// Who will free this memory?
newF4->AllocateUshortArray(newF4->length);
if (newF4->UshortArray == NULL)
{
// Unable to malloc space for new format 4 subtable
SetAbortState;
return;
}
/* build all the header info for the subtable and write into p */
(newF4->UshortArray)[0] = 4;
(newF4->UshortArray)[1] = newF4->length;
(newF4->UshortArray)[2] = 0;
(newF4->UshortArray)[3] = (newF4->segCount) << 1;
entrySelector = findNearestExponent (newF4->segCount);
searchRange = (ushort)(1 << (entrySelector + 1));
(newF4->UshortArray)[4] = searchRange;
(newF4->UshortArray)[5] = entrySelector;
(newF4->UshortArray)[6] = (newF4->UshortArray)[3] - searchRange; /* rangeShift */
(newF4->UshortArray)[7 + newF4->segCount] = 0; /* reservedPad */
/* set addresses of arrays to appropriate locations in p */
newF4->endCount = newF4->UshortArray + 7;
newF4->startCount = newF4->UshortArray + (8 + newF4->segCount);
newF4->idDelta = (short *)(newF4->UshortArray + (8 + (2 * newF4->segCount)));
newF4->idRangeOffset = newF4->UshortArray + (8 + (3 * newF4->segCount));
newF4->glyphIdArray = newF4->UshortArray + (8 + (4 * newF4->segCount));
/* build arrays */
index = nextIndex = gIdArrayIndex = 0;
for (seg = 0; seg < (newF4->segCount - 1); seg++)
{
(newF4->startCount)[seg] = codes[nextIndex];
measureFormat4Segment(numCodes, &nextIndex, codes, glyphList,
&lengthSeg, &consecutiveGlyphIDs);
/* upon return from measureFormat4Segment, nextIndex indicates the
element of codes corresponding to startCount[seg+1] */
(newF4->endCount)[seg] = codes[nextIndex - 1];
if (consecutiveGlyphIDs)
{
/* use idDelta array for glyph ID */
(newF4->idDelta)[seg] = glyphList[nextIndex - 1]
- codes[nextIndex - 1];
(newF4->idRangeOffset)[seg] = 0;
idDeltaCt += (newF4->endCount)[seg] - (newF4->startCount)[seg] + 1;
index = nextIndex;
}
else
{
/* use idRangeOffset */
(newF4->idDelta)[seg] = 0;
(newF4->idRangeOffset)[seg] = 2 *
((newF4->segCount) + gIdArrayIndex - seg);
explicitCt += nextIndex - index;
/* write glyphIdArray entries for character codes from
startCount[seg] to endCount[seg] */
/* index currently indicates element of codes array corresponding
to startCount[seg] */
while (index < nextIndex)
{
/* write glyphList[index] into glyphIdArray */
(newF4->glyphIdArray)[gIdArrayIndex++] = glyphList[index];
/* if we have not reached the character code for endCount[seg]
and if codes[index+1] - codes[index] >= 2, then we need to
insert filler entries */
nextCode = codes[index] + 1;
while ((index < nextIndex - 1) &&
(nextCode < codes[index + 1]))
{
(newF4->glyphIdArray)[gIdArrayIndex++] = 0;
nextCode ++;
fillerCt++;
}
index ++;
}
}
}
#ifdef RRM_DEBUG
_ftprintf(stderr,TEXT("size of glyphIdArray = %hu\n"), sizeGlyphIdArray);
_ftprintf(stderr,TEXT("final glyphIdArray index: %hu\n"), gIdArrayIndex);
_ftprintf(stderr,TEXT("codes accounted for by idDelta segs: %hu\n"), idDeltaCt);
_ftprintf(stderr,TEXT("in IdRangeOffset segs, symset explicit codes: %hu\n"), explicitCt);
_ftprintf(stderr,TEXT(" ... ... ...., filler codes: %hu\n"), fillerCt);
_ftprintf(stderr,TEXT("final index value: %hu\n"), index);
#endif
/* do the last segment, which contains the null character */
(newF4->startCount)[newF4->segCount - 1] = NULL_CHAR;
(newF4->endCount)[newF4->segCount - 1] = NULL_CHAR;
(newF4->idDelta)[newF4->segCount - 1] = 1;
(newF4->idRangeOffset)[newF4->segCount - 1] = 0;
} // build_tt_f4_t_from_lists
/**************************************************************/
static void calcSegCountAndArraySize (ushort numCodes, ushort *codes,
ushort *glyphList, ushort *segCount, ushort *sizeGlyphIdArray)
{
ushort index, lengthSeg;
tt_boolean consecutiveGlyphIDs;
*segCount = 0;
*sizeGlyphIdArray = index = 0;
while (index < numCodes)
{
measureFormat4Segment(numCodes, &index, codes, glyphList,
&lengthSeg, &consecutiveGlyphIDs);
if (! consecutiveGlyphIDs)
(*sizeGlyphIdArray) += lengthSeg;
(*segCount)++;
}
/* add final null segment */
(*segCount)++;
} // calcSegCountAndArraySize
/************************************************************/
static void measureFormat4Segment(ushort numCodes, ushort *startIndex,
ushort *codes, ushort *glyphList, ushort *lengthSeg,
tt_boolean *consecutiveGlyphIDs)
{
ushort index, currentStreakLength, fillerEntries;
tt_boolean newStreakDetected, longStreakSnapped;
currentStreakLength = 1;
*consecutiveGlyphIDs = bTrue;
newStreakDetected = longStreakSnapped = bFalse;
index = *startIndex;
fillerEntries = 0;
/* segment ends when (i) we reach the end of the charlist, or (ii)
consecutiveGlyphIDs has been set true and there is a break in
Unicode numbers, or (iii) consecutiveGlyphIDs has been set false
and there is a large break in Unicode numbers, or (iv) consecu-
tiveGlyphIDs has been set false but we have detected a streak of
5 or more consecutive char IDs, or (v) consecutiveGlyphIDs has
been set true and a streak of 5 or more consecutive char IDs has
been detected prior to a non-consecutive char ID */
while ((index + 1 < numCodes) &&
(! newStreakDetected) &&
(! longStreakSnapped) &&
((codes[index] + 1 == codes[index + 1]) ||
((! *consecutiveGlyphIDs) &&
(codes[index] + 4 >= codes[index + 1]))))
{
if (codes[index] + 1 < codes[index + 1])
{
/* false == *consecutiveGlyphIDs */
fillerEntries += (codes[index + 1] - codes[index] - 1);
index++;
currentStreakLength = 1;
}
else if (glyphList[index] + 1 == glyphList[index + 1])
{
index++;
currentStreakLength ++;
if ((currentStreakLength >= 5) && (! *consecutiveGlyphIDs))
{
newStreakDetected = bTrue;
/* exit loop with index referring to last element prior
to streak by decrementing index */
index -= currentStreakLength;
fillerEntries -= (codes[index + 1] - codes[index] - 1);
}
}
else if ((currentStreakLength >= 5) && (*consecutiveGlyphIDs))
{
/* since it is not TRUE that
glyphList[index] + 1 == glyphList[index + 1] */
longStreakSnapped = bTrue;
/* exit loop with index referring to last element in streak;
don't increment index */
}
else
{
/* build a segment that uses IdRangeOffset != 0 */
*consecutiveGlyphIDs = bFalse;
index++;
currentStreakLength = 1;
}
}
*lengthSeg = index - *startIndex + 1 + fillerEntries;
*startIndex = index + 1;
} // measureFormat4Segment
/*************************************************************/
static ushort findNearestExponent (ushort number)
{
ushort shiftCount;
shiftCount = 0;
while (number > 1)
{
number = number >> 1;
shiftCount ++;
}
return(shiftCount);
} // findNearestExponent
/***************************************************************/
/*
dbm dbm dbm dbm
HACK HACK HACK HACK
FIX ME FIXME
Allocates room for *codes and *matchingCodes
Who will free them?
*/
static void getNewCodes (FILE *symfile, ushort *numCodes,
ushort **codes, ushort **matchingCodes)
/* symfile is assumed to be in .sym format. After /symbols =,
each line will begin with a character code in decimal and a
matching code either in decimal or hexadecimal */
{
numberFormat matchingCodeFormat; /* decimal or hex? */
ushort firstCode, lastCode, count;
parseSymfileHeader (symfile, &firstCode, &lastCode, &matchingCodeFormat);
*codes = new ushort[(lastCode - firstCode + 1)];
*matchingCodes = new ushort[(lastCode - firstCode + 1)];
if ((*codes == NULL) || (*matchingCodes == NULL))
{
// _ftprintf(stderr,TEXT("Unable to malloc space for .sym file codes.\n"));
SetAbortState;
}
parseSymfileMap (symfile, firstCode, lastCode, matchingCodeFormat,
*codes, *matchingCodes, &count);
if (count < lastCode - firstCode + 1)
{
delete [] (*codes);
*codes = new ushort[count];
delete [] (*matchingCodes);
*matchingCodes = new ushort[count];
if ((*codes == NULL) || (*matchingCodes == NULL))
{
// _ftprintf(stderr,TEXT("Unable to realloc space for .sym file codes.\n"));
SetAbortState;
}
}
*numCodes = count;
} // getNewCodes
/**********************************************************************/
static void parseSymfileHeader (FILE *symstream, ushort *firstCode,
ushort *lastCode, numberFormat *matchingCodeFormat)
/* this is an adaptation of functions `symfile_header' and
`evaluate_index_string' from the December '91 version of
/users/fonts/outlprod/writesets/mk-sym.c */
{
tt_boolean header_finished, indexing_system_known, know_first, know_last;
short ch;
char string[80], index_string[30];
header_finished = indexing_system_known = know_first = know_last = bFalse;
while (header_finished == bFalse)
{
ch = fgetc(symstream);
if ((char)ch=='S')
{
/* expect "SYMBOL SET = ..." */
}
if ((char)ch=='/') /* Start of a header line */
{
hackScanf(symstream,"%[^=]",string);
if (strncmp(string,"first code",10) == 0)
{
hackScanf(symstream,"%*[ =]%hd", firstCode);
know_first = bTrue;
}
if (strncmp(string,"last code",9) == 0)
{
hackScanf(symstream,"%*[ =]%hd", lastCode);
know_last = bTrue;
}
if (strncmp(string,"index",5) == 0)
{
hackScanf(symstream,"%*[ =]%s",index_string);
if (0 == strncasecmp ("ACG", index_string, 3))
{
indexing_system_known = bTrue;
*matchingCodeFormat = DEC;
}
else if (0 == strncasecmp ("Unicode", index_string, 7))
{
indexing_system_known = bTrue;
*matchingCodeFormat = HEX;
}
else /* I don't understand the /index entry */
{
SetAbortState;
}
}
if (strncmp(string,"symbols",7) == 0) /* symbol list follows */
header_finished = bTrue;
}
while (fgetc(symstream) != '\n') ; /* clear rest of line */
}
if ((! know_first) || (! know_last))
{
// _ftprintf(stderr,TEXT("I need both a first and a last code in .sym header.\n"));
SetAbortState;
}
if (*firstCode > *lastCode)
{
// _ftprintf(stderr,TEXT("First code > last code in .sym header. Quitting\n"));
SetAbortState;
}
if (! indexing_system_known)
{
// _ftprintf(stderr,TEXT("Can't tell whether to look for decimal or hex char codes.\n"));
SetAbortState;
}
/* ready to read symbols map from symstream */
} // parseSymfileHeader
/**********************************************************************/
/*
this is an adaptation of function `read_symbol_map' from
12/91 version of /users/fonts/outlprod/writesets/mk-sym.c
*/
static void parseSymfileMap (FILE *symfile, ushort firstCode, ushort lastCode,
numberFormat matchFormat, ushort *codes,
ushort *matchingCodes, ushort *count)
{
char string[80], *token;
ushort newcode, matchCode;
/* ushort transCode; want CG => MSL ?? */
tt_boolean end_reached, have_pair;
int sscanf_return;
*count = 0;
end_reached = bFalse;
while ((!feof(symfile)) && (! end_reached))
{
have_pair = bFalse;
hackScanf(symfile,"%[^\n]", string);
token = strtok(string," ");
if (isdigit(*token))
{
newcode = atoi(token);
if ((newcode >= firstCode) && (newcode <= lastCode))
{
if (matchFormat == DEC)
{
token = strtok(NULL," ");
if (isdigit(*token))
{
matchCode = atoi(token);
/*
-- permitting CG => MSL translation will require more than just uncommenting
the next few lines.
transCode = retrieve_from_translation_index (ACG, matchCode);
if (transCode == NULL_CODE)
{
_ftprintf(stderr,TEXT("Warning - Unable to translate source "));
_ftprintf(stderr,("character code #%hu\n"), matchCode);
}
matchCode = transCode;
*/
have_pair = bTrue;
}
else /* 2nd token is not a digit, 1st was */
{
// _ftprintf(stderr,TEXT("Invalid line in symbol set map\n"));
SetAbortState;
}
}
else if (matchFormat == HEX)
{
/* expect a string of the form "0xF000", which would signify
character number 61440. First, strtok needs to jump beyond
the 0x. Then, the remaining string of digits needs to be
interpreted as a hexidecimal number. For the latter purpose,
sscanf is used here instead of atoi. 12/19/91 */
token = strtok(NULL,"x");
token = strtok(NULL," ");
sscanf_return = hackSScanf (token,"%hx", &matchCode);
if (sscanf_return == 1)
{
/* matchCode successfully read as hex number */
have_pair = bTrue;
}
else /* 2nd token is not a digit, 1st was */
{
// _ftprintf(stderr,TEXT("Invalid line in symbol set map\n"));
SetAbortState;
}
}
}
else /* newcode has an illegal value */
{
// _ftprintf(stderr,TEXT("%hu is an invalid code in symbol set file\n"), matchFormat);
SetAbortState;
}
}
else /* 1st token is not a digit */
{
if (strncmp(token,"/end",4) == 0)
end_reached = bTrue;
else
// _ftprintf(stderr,TEXT("Error in symbol set map where /end expected\n"));
SetAbortState;
}
if (have_pair)
{
codes[*count] = newcode;
matchingCodes[*count] = matchCode;
(*count)++;
}
while (fgetc(symfile) != '\n') ; /* clear to end of line */
} /* end of while !feof and !end_reached */
if (*count == 0)
{
// _ftprintf(stderr,TEXT("No character code pairs obtained from symfile.\n"));
SetAbortState;
}
} // parseSymfileMap
/***************************************************************/
static void matchNewCodeToGlyph (ushort numNewCodes, ushort *matchingCodes,
ushort numOldCodes,
ushort *oldCodes, ushort *glyphList)
{
ushort i, low, high, mid;
for (i = 0; i < numNewCodes; i++)
{
low = 0; high = numOldCodes - 1; mid = numOldCodes >> 1;
while (low + 1 < high)
{
if (oldCodes[mid] < matchingCodes[i])
low = mid + 1;
else
high = mid;
mid = (high + low) >> 1;
}
if (oldCodes[low] == matchingCodes[i])
matchingCodes[i] = glyphList[low];
else if (oldCodes[high] == matchingCodes[i])
matchingCodes[i] = glyphList[high];
else
matchingCodes[i] = 0; /* missing character glyph */
}
} // matchNewCodeToGlyph