You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

203 lines
6.1 KiB

/*
SPDX-FileCopyrightText: 2003-2009 Craig Drummond <craig@kde.org>
SPDX-License-Identifier: GPL-2.0-or-later
*/
#include "Utils.h"
#include "Fc.h"
#include "FontInst.h"
#include "Misc.h"
#include "WritingSystems.h"
#include <KShell>
#include <QByteArray>
#include <QFile>
#include <QTextStream>
#include <fontconfig/fontconfig.h>
namespace KFI
{
namespace Utils
{
bool isAAfm(const QString &fname)
{
if (Misc::checkExt(QFile::encodeName(fname), "afm")) // CPD? Is this a necessary check?
{
QFile file(fname);
if (file.open(QIODevice::ReadOnly)) {
QTextStream stream(&file);
QString line;
for (int lc = 0; lc < 30 && !stream.atEnd(); ++lc) {
line = stream.readLine();
if (line.contains("StartFontMetrics")) {
file.close();
return true;
}
}
file.close();
}
}
return false;
}
bool isAPfm(const QString &fname)
{
bool ok = false;
// I know extension checking is bad, but Ghostscript's pf2afm requires the pfm file to
// have the .pfm extension...
QByteArray name(QFile::encodeName(fname));
if (Misc::checkExt(name, "pfm")) {
//
// OK, the extension matches, so perform a little contents checking...
FILE *f = fopen(name.constData(), "r");
if (f) {
static const unsigned long constCopyrightLen = 60;
static const unsigned long constTypeToExt = 49;
static const unsigned long constExtToFname = 20;
static const unsigned long constExtLen = 30;
static const unsigned long constFontnameMin = 75;
static const unsigned long constFontnameMax = 512;
unsigned short version = 0, type = 0, extlen = 0;
unsigned long length = 0, fontname = 0, fLength = 0;
fseek(f, 0, SEEK_END);
fLength = ftell(f);
fseek(f, 0, SEEK_SET);
if (2 == fread(&version, 1, 2, f) && // Read version
4 == fread(&length, 1, 4, f) && // length...
length == fLength && 0 == fseek(f, constCopyrightLen, SEEK_CUR) && // Skip copyright notice...
2 == fread(&type, 1, 2, f) && 0 == fseek(f, constTypeToExt, SEEK_CUR) && 2 == fread(&extlen, 1, 2, f) && extlen == constExtLen
&& 0 == fseek(f, constExtToFname, SEEK_CUR) && 4 == fread(&fontname, 1, 4, f) && fontname > constFontnameMin && fontname < constFontnameMax) {
ok = true;
}
fclose(f);
}
}
return ok;
}
// This function is *only* used for the generation of AFMs from PFMs.
bool isAType1(const QString &fname)
{
static const char constStr[] = "%!PS-AdobeFont-";
static const unsigned int constStrLen = 15;
static const unsigned int constPfbOffset = 6;
static const unsigned int constPfbLen = constStrLen + constPfbOffset;
QByteArray name(QFile::encodeName(fname));
char buffer[constPfbLen];
bool match = false;
if (Misc::checkExt(name, "pfa")) {
FILE *f = fopen(name.constData(), "r");
if (f) {
if (constStrLen == fread(buffer, 1, constStrLen, f)) {
match = 0 == memcmp(buffer, constStr, constStrLen);
}
fclose(f);
}
} else if (Misc::checkExt(name, "pfb")) {
static const char constPfbMarker = static_cast<char>(0x80);
FILE *f = fopen(name.constData(), "r");
if (f) {
if (constPfbLen == fread(buffer, 1, constPfbLen, f)) {
match = buffer[0] == constPfbMarker && 0 == memcmp(&buffer[constPfbOffset], constStr, constStrLen);
}
fclose(f);
}
}
return match;
}
static QString getMatch(const QString &file, const char *extension)
{
QString f(Misc::changeExt(file, extension));
return Misc::fExists(f) ? f : QString();
}
void createAfm(const QString &file, EFileType type)
{
bool pfm = FILE_PFM == type, type1 = FILE_SCALABLE == type && isAType1(file);
if (type1 || pfm) {
// pf2afm wants files with lowercase extension, so just check for lowercase!
// -- when a font is installed, the extension is converted to lowercase anyway...
QString afm = getMatch(file, "afm");
if (afm.isEmpty()) // No point creating if AFM already exists!
{
QString pfm, t1;
if (type1) // Its a Type1, so look for existing PFM
{
pfm = getMatch(file, "pfm");
t1 = file;
} else // Its a PFM, so look for existing Type1
{
t1 = getMatch(file, "pfa");
if (t1.isEmpty()) {
t1 = getMatch(file, "pfb");
}
pfm = file;
}
if (!t1.isEmpty() && !pfm.isEmpty()) // Do we have both Type1 and PFM?
{
QString rootName(t1.left(t1.length() - 4));
Misc::doCmd("pf2afm", KShell::quoteArg(rootName)); // pf2afm wants name without extension...
Misc::setFilePerms(QFile::encodeName(rootName + ".afm"));
}
}
}
}
EFileType check(const QString &file, Family &fam)
{
if (isAAfm(file)) {
return FILE_AFM;
} else if (isAPfm(file)) {
return FILE_PFM;
} else {
// Check that file is a font via FreeType...
int count = 0;
FcPattern *pat = FcFreeTypeQuery((const FcChar8 *)(QFile::encodeName(file).constData()), 0, nullptr, &count);
if (pat) {
FcBool scalable;
QString family, foundry;
quint32 style;
int index;
qulonglong ws;
EFileType type = (FcResultMatch != FcPatternGetBool(pat, FC_SCALABLE, 0, &scalable) || !scalable) ? FILE_BITMAP : FILE_SCALABLE;
FC::getDetails(pat, family, style, index, foundry);
ws = WritingSystems::instance()->get(pat);
FcPatternDestroy(pat);
Style st(style, scalable, ws);
st.add(File(file, foundry, index));
fam = Family(family);
fam.add(st);
return type;
}
}
return FILE_INVALID;
}
}
}