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 }