blob: f9ac55bd0fa303d643573ce103a1496097038010 [file] [log] [blame]
//========================================================================
//
// FoFiType1.cc
//
// Copyright 1999-2003 Glyph & Cog, LLC
//
//========================================================================
//========================================================================
//
// Modified under the Poppler project - http://poppler.freedesktop.org
//
// All changes made under the Poppler project to this file are licensed
// under GPL version 2 or later
//
// Copyright (C) 2005, 2008, 2010, 2018 Albert Astals Cid <aacid@kde.org>
// Copyright (C) 2005 Kristian Høgsberg <krh@redhat.com>
// Copyright (C) 2010 Jakub Wilk <jwilk@jwilk.net>
// Copyright (C) 2014 Carlos Garcia Campos <carlosgc@gnome.org>
// Copyright (C) 2017 Adrian Johnson <ajohnson@redneon.com>
// Copyright (C) 2017 Jean Ghali <jghali@libertysurf.fr>
//
// To see a description of the changes please see the Changelog file that
// came with your tarball or type make ChangeLog if you are building from git
//
//========================================================================
#include <config.h>
#include <stdlib.h>
#include <string.h>
#include <limits.h>
#include "goo/glibc.h"
#include "goo/gmem.h"
#include "goo/GooLikely.h"
#include "FoFiEncodings.h"
#include "FoFiType1.h"
#include "poppler/Error.h"
//------------------------------------------------------------------------
// FoFiType1
//------------------------------------------------------------------------
FoFiType1 *FoFiType1::make(const char *fileA, int lenA) {
return new FoFiType1(fileA, lenA, false);
}
FoFiType1 *FoFiType1::load(const char *fileName) {
char *fileA;
int lenA;
if (!(fileA = FoFiBase::readFile(fileName, &lenA))) {
return nullptr;
}
return new FoFiType1(fileA, lenA, true);
}
FoFiType1::FoFiType1(const char *fileA, int lenA, bool freeFileDataA):
FoFiBase(fileA, lenA, freeFileDataA)
{
name = nullptr;
encoding = nullptr;
fontMatrix[0] = 0.001;
fontMatrix[1] = 0;
fontMatrix[2] = 0;
fontMatrix[3] = 0.001;
fontMatrix[4] = 0;
fontMatrix[5] = 0;
parsed = false;
undoPFB();
}
FoFiType1::~FoFiType1() {
int i;
if (name) {
gfree(name);
}
if (encoding && encoding != fofiType1StandardEncoding) {
for (i = 0; i < 256; ++i) {
gfree(encoding[i]);
}
gfree(encoding);
}
}
const char *FoFiType1::getName() {
if (!parsed) {
parse();
}
return name;
}
char **FoFiType1::getEncoding() {
if (!parsed) {
parse();
}
return encoding;
}
void FoFiType1::getFontMatrix(double *mat) {
int i;
if (!parsed) {
parse();
}
for (i = 0; i < 6; ++i) {
mat[i] = fontMatrix[i];
}
}
void FoFiType1::writeEncoded(const char **newEncoding,
FoFiOutputFunc outputFunc, void *outputStream) const {
char buf[512];
char *line, *line2, *p;
int i;
// copy everything up to the encoding
for (line = (char *)file;
line && strncmp(line, "/Encoding", 9);
line = getNextLine(line)) ;
if (!line) {
// no encoding - just copy the whole font file
(*outputFunc)(outputStream, (char *)file, len);
return;
}
(*outputFunc)(outputStream, (char *)file, line - (char *)file);
// write the new encoding
(*outputFunc)(outputStream, "/Encoding 256 array\n", 20);
(*outputFunc)(outputStream,
"0 1 255 {1 index exch /.notdef put} for\n", 40);
for (i = 0; i < 256; ++i) {
if (newEncoding[i]) {
sprintf(buf, "dup %d /%s put\n", i, newEncoding[i]);
(*outputFunc)(outputStream, buf, strlen(buf));
}
}
(*outputFunc)(outputStream, "readonly def\n", 13);
// find the end of the encoding data
//~ this ought to parse PostScript tokens
if (!strncmp(line, "/Encoding StandardEncoding def", 30)) {
line = getNextLine(line);
} else {
// skip "/Encoding" + one whitespace char,
// then look for 'def' preceded by PostScript whitespace
p = line + 10;
line = nullptr;
for (; p < (char *)file + len; ++p) {
if ((*p == ' ' || *p == '\t' || *p == '\x0a' ||
*p == '\x0d' || *p == '\x0c' || *p == '\0') &&
p + 4 <= (char *)file + len &&
!strncmp(p + 1, "def", 3)) {
line = p + 4;
break;
}
}
}
// some fonts have two /Encoding entries in their dictionary, so we
// check for a second one here
if (line) {
for (line2 = line, i = 0;
i < 20 && line2 && strncmp(line2, "/Encoding", 9);
line2 = getNextLine(line2), ++i) ;
if (i < 20 && line2) {
(*outputFunc)(outputStream, line, line2 - line);
if (!strncmp(line2, "/Encoding StandardEncoding def", 30)) {
line = getNextLine(line2);
} else {
// skip "/Encoding" + one whitespace char,
// then look for 'def' preceded by PostScript whitespace
p = line2 + 10;
line = nullptr;
for (; p < (char *)file + len; ++p) {
if ((*p == ' ' || *p == '\t' || *p == '\x0a' ||
*p == '\x0d' || *p == '\x0c' || *p == '\0') &&
p + 4 <= (char *)file + len &&
!strncmp(p + 1, "def", 3)) {
line = p + 4;
break;
}
}
}
}
// copy everything after the encoding
if (line) {
(*outputFunc)(outputStream, line, ((char *)file + len) - line);
}
}
}
char *FoFiType1::getNextLine(char *line) const {
while (line < (char *)file + len && *line != '\x0a' && *line != '\x0d') {
++line;
}
if (line < (char *)file + len && *line == '\x0d') {
++line;
}
if (line < (char *)file + len && *line == '\x0a') {
++line;
}
if (line >= (char *)file + len) {
return nullptr;
}
return line;
}
void FoFiType1::parse() {
char *line, *line1, *firstLine, *p, *p2;
char buf[256];
char c;
int n, code, base, i, j;
char *tokptr;
bool gotMatrix, continueLine;
gotMatrix = false;
for (i = 1, line = (char *)file;
i <= 100 && line && (!name || !encoding);
++i) {
// get font name
if (!name &&
(line + 9 <= (char*)file + len) &&
!strncmp(line, "/FontName", 9)) {
const auto availableFile = (char*)file + len - line;
const int lineLen = availableFile < 255 ? availableFile : 255;
strncpy(buf, line, lineLen);
buf[lineLen] = '\0';
if ((p = strchr(buf+9, '/')) &&
(p = strtok_r(p+1, " \t\n\r", &tokptr))) {
name = copyString(p);
}
line = getNextLine(line);
// get encoding
} else if (!encoding &&
(line + 30 <= (char*)file + len) &&
!strncmp(line, "/Encoding StandardEncoding def", 30)) {
encoding = (char **)fofiType1StandardEncoding;
} else if (!encoding &&
(line + 19 <= (char*)file + len) &&
!strncmp(line, "/Encoding 256 array", 19)) {
encoding = (char **)gmallocn(256, sizeof(char *));
for (j = 0; j < 256; ++j) {
encoding[j] = nullptr;
}
continueLine = false;
for (j = 0, line = getNextLine(line);
j < 300 && line && (line1 = getNextLine(line));
++j, line = line1) {
if ((n = (int)(line1 - line)) > 255) {
error(errSyntaxWarning, -1, "FoFiType1::parse a line has more than 255 characters, we don't support this");
n = 255;
}
if (continueLine) {
continueLine = false;
if ((line1 - firstLine) + 1 > (int)sizeof(buf))
break;
p = firstLine;
p2 = buf;
while (p < line1) {
if (*p == '\n' || *p == '\r') {
*p2++ = ' ';
p++;
} else {
*p2++ = *p++;
}
}
*p2 = '\0';
} else {
firstLine = line;
strncpy(buf, line, n);
buf[n] = '\0';
}
for (p = buf; *p == ' ' || *p == '\t'; ++p) ;
if (!strncmp(p, "dup", 3)) {
while (1) {
p += 3;
for (; *p == ' ' || *p == '\t'; ++p) ;
code = 0;
if (*p == '8' && p[1] == '#') {
base = 8;
p += 2;
} else if (*p >= '0' && *p <= '9') {
base = 10;
} else if (*p == '\n' || *p == '\r') {
continueLine = true;
break;
} else {
break;
}
for (; *p >= '0' && *p < '0' + base && code < INT_MAX / (base + (*p - '0')); ++p) {
code = code * base + (*p - '0');
}
for (; *p == ' ' || *p == '\t'; ++p) ;
if (*p == '\n' || *p == '\r') {
continueLine = true;
break;
} else if (*p != '/') {
break;
}
++p;
for (p2 = p; *p2 && *p2 != ' ' && *p2 != '\t'; ++p2) ;
if (code >= 0 && code < 256) {
c = *p2;
*p2 = '\0';
gfree(encoding[code]);
encoding[code] = copyString(p);
*p2 = c;
}
for (p = p2; *p == ' ' || *p == '\t'; ++p) ;
if (*p == '\n' || *p == '\r') {
continueLine = true;
break;
}
if (strncmp(p, "put", 3)) {
break;
}
for (p += 3; *p == ' ' || *p == '\t'; ++p) ;
if (strncmp(p, "dup", 3)) {
break;
}
}
} else {
if (strtok_r(buf, " \t", &tokptr) &&
(p = strtok_r(nullptr, " \t\n\r", &tokptr)) && !strcmp(p, "def")) {
break;
}
}
}
//~ check for getinterval/putinterval junk
} else if (!gotMatrix &&
(line + 11 <= (char*)file + len) &&
!strncmp(line, "/FontMatrix", 11)) {
const auto availableFile = (char*)file + len - (line + 11);
const int bufLen = availableFile < 255 ? availableFile : 255;
strncpy(buf, line + 11, bufLen);
buf[bufLen] = '\0';
if ((p = strchr(buf, '['))) {
++p;
if ((p2 = strchr(p, ']'))) {
*p2 = '\0';
for (j = 0; j < 6; ++j) {
if ((p = strtok(j == 0 ? p : nullptr, " \t\n\r"))) {
fontMatrix[j] = atof(p);
} else {
break;
}
}
}
}
gotMatrix = true;
} else {
line = getNextLine(line);
}
}
parsed = true;
}
// Undo the PFB encoding, i.e., remove the PFB headers.
void FoFiType1::undoPFB() {
bool ok;
unsigned char *file2;
int pos1, pos2, type;
unsigned int segLen;
ok = true;
if (getU8(0, &ok) != 0x80 || !ok) {
return;
}
file2 = (unsigned char *)gmalloc(len);
pos1 = pos2 = 0;
while (getU8(pos1, &ok) == 0x80 && ok) {
type = getU8(pos1 + 1, &ok);
if (type < 1 || type > 2 || !ok) {
break;
}
segLen = getU32LE(pos1 + 2, &ok);
pos1 += 6;
if (!ok || !checkRegion(pos1, segLen)) {
break;
}
memcpy(file2 + pos2, file + pos1, segLen);
pos1 += segLen;
pos2 += segLen;
}
if (freeFileData) {
gfree((char*)file);
}
file = file2;
freeFileData = true;
len = pos2;
}