File indexing completed on 2025-01-26 05:08:07

0001 /*
0002     SPDX-FileCopyrightText: 2003-2009 Craig Drummond <craig@kde.org>
0003     SPDX-License-Identifier: GPL-2.0-or-later
0004 */
0005 
0006 #include "Utils.h"
0007 #include "Fc.h"
0008 #include "FontInst.h"
0009 #include "Misc.h"
0010 #include "WritingSystems.h"
0011 #include <KShell>
0012 #include <QByteArray>
0013 #include <QFile>
0014 #include <QTextStream>
0015 #include <fontconfig/fontconfig.h>
0016 
0017 namespace KFI
0018 {
0019 namespace Utils
0020 {
0021 bool isAAfm(const QString &fname)
0022 {
0023     if (Misc::checkExt(QFile::encodeName(fname), "afm")) // CPD? Is this a necessary check?
0024     {
0025         QFile file(fname);
0026 
0027         if (file.open(QIODevice::ReadOnly)) {
0028             QTextStream stream(&file);
0029             QString line;
0030 
0031             for (int lc = 0; lc < 30 && !stream.atEnd(); ++lc) {
0032                 line = stream.readLine();
0033 
0034                 if (line.contains("StartFontMetrics")) {
0035                     file.close();
0036                     return true;
0037                 }
0038             }
0039 
0040             file.close();
0041         }
0042     }
0043 
0044     return false;
0045 }
0046 
0047 bool isAPfm(const QString &fname)
0048 {
0049     bool ok = false;
0050 
0051     // I know extension checking is bad, but Ghostscript's pf2afm requires the pfm file to
0052     // have the .pfm extension...
0053     QByteArray name(QFile::encodeName(fname));
0054 
0055     if (Misc::checkExt(name, "pfm")) {
0056         //
0057         // OK, the extension matches, so perform a little contents checking...
0058         FILE *f = fopen(name.constData(), "r");
0059 
0060         if (f) {
0061             static const unsigned long constCopyrightLen = 60;
0062             static const unsigned long constTypeToExt = 49;
0063             static const unsigned long constExtToFname = 20;
0064             static const unsigned long constExtLen = 30;
0065             static const unsigned long constFontnameMin = 75;
0066             static const unsigned long constFontnameMax = 512;
0067 
0068             unsigned short version = 0, type = 0, extlen = 0;
0069             unsigned long length = 0, fontname = 0, fLength = 0;
0070 
0071             fseek(f, 0, SEEK_END);
0072             fLength = ftell(f);
0073             fseek(f, 0, SEEK_SET);
0074 
0075             if (2 == fread(&version, 1, 2, f) && // Read version
0076                 4 == fread(&length, 1, 4, f) && // length...
0077                 length == fLength && 0 == fseek(f, constCopyrightLen, SEEK_CUR) && // Skip copyright notice...
0078                 2 == fread(&type, 1, 2, f) && 0 == fseek(f, constTypeToExt, SEEK_CUR) && 2 == fread(&extlen, 1, 2, f) && extlen == constExtLen
0079                 && 0 == fseek(f, constExtToFname, SEEK_CUR) && 4 == fread(&fontname, 1, 4, f) && fontname > constFontnameMin && fontname < constFontnameMax) {
0080                 ok = true;
0081             }
0082             fclose(f);
0083         }
0084     }
0085 
0086     return ok;
0087 }
0088 
0089 // This function is *only* used for the generation of AFMs from PFMs.
0090 bool isAType1(const QString &fname)
0091 {
0092     static const char constStr[] = "%!PS-AdobeFont-";
0093     static const unsigned int constStrLen = 15;
0094     static const unsigned int constPfbOffset = 6;
0095     static const unsigned int constPfbLen = constStrLen + constPfbOffset;
0096 
0097     QByteArray name(QFile::encodeName(fname));
0098     char buffer[constPfbLen];
0099     bool match = false;
0100 
0101     if (Misc::checkExt(name, "pfa")) {
0102         FILE *f = fopen(name.constData(), "r");
0103 
0104         if (f) {
0105             if (constStrLen == fread(buffer, 1, constStrLen, f)) {
0106                 match = 0 == memcmp(buffer, constStr, constStrLen);
0107             }
0108             fclose(f);
0109         }
0110     } else if (Misc::checkExt(name, "pfb")) {
0111         static const char constPfbMarker = static_cast<char>(0x80);
0112 
0113         FILE *f = fopen(name.constData(), "r");
0114 
0115         if (f) {
0116             if (constPfbLen == fread(buffer, 1, constPfbLen, f)) {
0117                 match = buffer[0] == constPfbMarker && 0 == memcmp(&buffer[constPfbOffset], constStr, constStrLen);
0118             }
0119             fclose(f);
0120         }
0121     }
0122 
0123     return match;
0124 }
0125 
0126 static QString getMatch(const QString &file, const char *extension)
0127 {
0128     QString f(Misc::changeExt(file, extension));
0129 
0130     return Misc::fExists(f) ? f : QString();
0131 }
0132 
0133 void createAfm(const QString &file, EFileType type)
0134 {
0135     bool pfm = FILE_PFM == type, type1 = FILE_SCALABLE == type && isAType1(file);
0136 
0137     if (type1 || pfm) {
0138         // pf2afm wants files with lowercase extension, so just check for lowercase!
0139         // -- when a font is installed, the extension is converted to lowercase anyway...
0140         QString afm = getMatch(file, "afm");
0141 
0142         if (afm.isEmpty()) // No point creating if AFM already exists!
0143         {
0144             QString pfm, t1;
0145 
0146             if (type1) // Its a Type1, so look for existing PFM
0147             {
0148                 pfm = getMatch(file, "pfm");
0149                 t1 = file;
0150             } else // Its a PFM, so look for existing Type1
0151             {
0152                 t1 = getMatch(file, "pfa");
0153                 if (t1.isEmpty()) {
0154                     t1 = getMatch(file, "pfb");
0155                 }
0156                 pfm = file;
0157             }
0158 
0159             if (!t1.isEmpty() && !pfm.isEmpty()) // Do we have both Type1 and PFM?
0160             {
0161                 QString rootName(t1.left(t1.length() - 4));
0162                 Misc::doCmd("pf2afm", KShell::quoteArg(rootName)); // pf2afm wants name without extension...
0163                 Misc::setFilePerms(QFile::encodeName(rootName + ".afm"));
0164             }
0165         }
0166     }
0167 }
0168 
0169 EFileType check(const QString &file, Family &fam)
0170 {
0171     if (isAAfm(file)) {
0172         return FILE_AFM;
0173     } else if (isAPfm(file)) {
0174         return FILE_PFM;
0175     } else {
0176         // Check that file is a font via FreeType...
0177         int count = 0;
0178         FcPattern *pat = FcFreeTypeQuery((const FcChar8 *)(QFile::encodeName(file).constData()), 0, nullptr, &count);
0179 
0180         if (pat) {
0181             FcBool scalable;
0182             QString family, foundry;
0183             quint32 style;
0184             int index;
0185             qulonglong ws;
0186             EFileType type = (FcResultMatch != FcPatternGetBool(pat, FC_SCALABLE, 0, &scalable) || !scalable) ? FILE_BITMAP : FILE_SCALABLE;
0187 
0188             FC::getDetails(pat, family, style, index, foundry);
0189             ws = WritingSystems::instance()->get(pat);
0190             FcPatternDestroy(pat);
0191             Style st(style, scalable, ws);
0192             st.add(File(file, foundry, index));
0193             fam = Family(family);
0194             fam.add(st);
0195             return type;
0196         }
0197     }
0198     return FILE_INVALID;
0199 }
0200 
0201 }
0202 
0203 }