summaryrefslogtreecommitdiff
path: root/vcl/unx/generic
diff options
context:
space:
mode:
Diffstat (limited to 'vcl/unx/generic')
-rw-r--r--vcl/unx/generic/fontmanager/adobe_encoding_table.hxx1078
-rw-r--r--vcl/unx/generic/fontmanager/afm_keyword_list62
-rw-r--r--vcl/unx/generic/fontmanager/fontcache.cxx740
-rw-r--r--vcl/unx/generic/fontmanager/fontconfig.cxx1293
-rw-r--r--vcl/unx/generic/fontmanager/fontmanager.cxx2222
-rw-r--r--vcl/unx/generic/fontmanager/fontsubst.cxx252
-rw-r--r--vcl/unx/generic/fontmanager/helper.cxx390
-rw-r--r--vcl/unx/generic/fontmanager/parseAFM.cxx1466
-rw-r--r--vcl/unx/generic/fontmanager/parseAFM.hxx323
9 files changed, 7826 insertions, 0 deletions
diff --git a/vcl/unx/generic/fontmanager/adobe_encoding_table.hxx b/vcl/unx/generic/fontmanager/adobe_encoding_table.hxx
new file mode 100644
index 000000000000..f2a7451f9c1a
--- /dev/null
+++ b/vcl/unx/generic/fontmanager/adobe_encoding_table.hxx
@@ -0,0 +1,1078 @@
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+struct AdobeEncEntry {
+ sal_Unicode aUnicode;
+ sal_uInt8 aAdobeStandardCode;
+ const char* const pAdobename;
+};
+
+static const AdobeEncEntry aAdobeCodes[]=
+{
+ { 0x0041, 0101, "A" },
+ { 0x00C6, 0341, "AE" },
+ { 0x01FC, 0, "AEacute" },
+ { 0xF7E6, 0, "AEsmall" },
+ { 0x00C1, 0, "Aacute" },
+ { 0xF7E1, 0, "Aacutesmall" },
+ { 0x0102, 0, "Abreve" },
+ { 0x00C2, 0, "Acircumflex" },
+ { 0xF7E2, 0, "Acircumflexsmall" },
+ { 0xF6C9, 0, "Acute" },
+ { 0xF7B4, 0, "Acutesmall" },
+ { 0x00C4, 0, "Adieresis" },
+ { 0xF7E4, 0, "Adieresissmall" },
+ { 0x00C0, 0, "Agrave" },
+ { 0xF7E0, 0, "Agravesmall" },
+ { 0x0391, 0, "Alpha" },
+ { 0x0386, 0, "Alphatonos" },
+ { 0x0100, 0, "Amacron" },
+ { 0x0104, 0, "Aogonek" },
+ { 0x00C5, 0, "Aring" },
+ { 0x01FA, 0, "Aringacute" },
+ { 0xF7E5, 0, "Aringsmall" },
+ { 0xF761, 0, "Asmall" },
+ { 0x00C3, 0, "Atilde" },
+ { 0xF7E3, 0, "Atildesmall" },
+ { 0x0042, 0102, "B" },
+ { 0x0392, 0, "Beta" },
+ { 0xF6F4, 0, "Brevesmall" },
+ { 0xF762, 0, "Bsmall" },
+ { 0x0043, 0103, "C" },
+ { 0x0106, 0, "Cacute" },
+ { 0xF6CA, 0, "Caron" },
+ { 0xF6F5, 0, "Caronsmall" },
+ { 0x010C, 0, "Ccaron" },
+ { 0x00C7, 0, "Ccedilla" },
+ { 0xF7E7, 0, "Ccedillasmall" },
+ { 0x0108, 0, "Ccircumflex" },
+ { 0x010A, 0, "Cdotaccent" },
+ { 0xF7B8, 0, "Cedillasmall" },
+ { 0x03A7, 0, "Chi" },
+ { 0xF6F6, 0, "Circumflexsmall" },
+ { 0xF763, 0, "Csmall" },
+ { 0x0044, 0104, "D" },
+ { 0x010E, 0, "Dcaron" },
+ { 0x0110, 0, "Dcroat" },
+ { 0x2206, 0, "Delta" },
+ { 0x0394, 0, "Delta" },
+ { 0xF6CB, 0, "Dieresis" },
+ { 0xF6CC, 0, "DieresisAcute" },
+ { 0xF6CD, 0, "DieresisGrave" },
+ { 0xF7A8, 0, "Dieresissmall" },
+ { 0xF6F7, 0, "Dotaccentsmall" },
+ { 0xF764, 0, "Dsmall" },
+ { 0x0045, 0105, "E" },
+ { 0x00C9, 0, "Eacute" },
+ { 0xF7E9, 0, "Eacutesmall" },
+ { 0x0114, 0, "Ebreve" },
+ { 0x011A, 0, "Ecaron" },
+ { 0x00CA, 0, "Ecircumflex" },
+ { 0xF7EA, 0, "Ecircumflexsmall" },
+ { 0x00CB, 0, "Edieresis" },
+ { 0xF7EB, 0, "Edieresissmall" },
+ { 0x0116, 0, "Edotaccent" },
+ { 0x00C8, 0, "Egrave" },
+ { 0xF7E8, 0, "Egravesmall" },
+ { 0x0112, 0, "Emacron" },
+ { 0x014A, 0, "Eng" },
+ { 0x0118, 0, "Eogonek" },
+ { 0x0395, 0, "Epsilon" },
+ { 0x0388, 0, "Epsilontonos" },
+ { 0xF765, 0, "Esmall" },
+ { 0x0397, 0, "Eta" },
+ { 0x0389, 0, "Etatonos" },
+ { 0x00D0, 0, "Eth" },
+ { 0xF7F0, 0, "Ethsmall" },
+ { 0x20AC, 0, "Euro" },
+ { 0x0046, 0106, "F" },
+ { 0xF766, 0, "Fsmall" },
+ { 0x0047, 0107, "G" },
+ { 0x0393, 0, "Gamma" },
+ { 0x011E, 0, "Gbreve" },
+ { 0x01E6, 0, "Gcaron" },
+ { 0x011C, 0, "Gcircumflex" },
+ { 0x0122, 0, "Gcommaaccent" },
+ { 0x0120, 0, "Gdotaccent" },
+ { 0xF6CE, 0, "Grave" },
+ { 0xF760, 0, "Gravesmall" },
+ { 0xF767, 0, "Gsmall" },
+ { 0x0048, 0110, "H" },
+ { 0x25CF, 0, "H18533" },
+ { 0x25AA, 0, "H18543" },
+ { 0x25AB, 0, "H18551" },
+ { 0x25A1, 0, "H22073" },
+ { 0x0126, 0, "Hbar" },
+ { 0x0124, 0, "Hcircumflex" },
+ { 0xF768, 0, "Hsmall" },
+ { 0xF6CF, 0, "Hungarumlaut" },
+ { 0xF6F8, 0, "Hungarumlautsmall" },
+ { 0x0049, 0111, "I" },
+ { 0x0132, 0, "IJ" },
+ { 0x00CD, 0, "Iacute" },
+ { 0xF7ED, 0, "Iacutesmall" },
+ { 0x012C, 0, "Ibreve" },
+ { 0x00CE, 0, "Icircumflex" },
+ { 0xF7EE, 0, "Icircumflexsmall" },
+ { 0x00CF, 0, "Idieresis" },
+ { 0xF7EF, 0, "Idieresissmall" },
+ { 0x0130, 0, "Idotaccent" },
+ { 0x2111, 0, "Ifraktur" },
+ { 0x00CC, 0, "Igrave" },
+ { 0xF7EC, 0, "Igravesmall" },
+ { 0x012A, 0, "Imacron" },
+ { 0x012E, 0, "Iogonek" },
+ { 0x0399, 0, "Iota" },
+ { 0x03AA, 0, "Iotadieresis" },
+ { 0x038A, 0, "Iotatonos" },
+ { 0xF769, 0, "Ismall" },
+ { 0x0128, 0, "Itilde" },
+ { 0x004A, 0112, "J" },
+ { 0x0134, 0, "Jcircumflex" },
+ { 0xF76A, 0, "Jsmall" },
+ { 0x004B, 0113, "K" },
+ { 0x039A, 0, "Kappa" },
+ { 0x0136, 0, "Kcommaaccent" },
+ { 0xF76B, 0, "Ksmall" },
+ { 0x004C, 0114, "L" },
+ { 0xF6BF, 0, "LL" },
+ { 0x0139, 0, "Lacute" },
+ { 0x039B, 0, "Lambda" },
+ { 0x013D, 0, "Lcaron" },
+ { 0x013B, 0, "Lcommaaccent" },
+ { 0x013F, 0, "Ldot" },
+ { 0x0141, 0350, "Lslash" },
+ { 0xF6F9, 0, "Lslashsmall" },
+ { 0xF76C, 0, "Lsmall" },
+ { 0x004D, 0115, "M" },
+ { 0xF6D0, 0, "Macron" },
+ { 0xF7AF, 0, "Macronsmall" },
+ { 0xF76D, 0, "Msmall" },
+ { 0x039C, 0, "Mu" },
+ { 0x004E, 0116, "N" },
+ { 0x0143, 0, "Nacute" },
+ { 0x0147, 0, "Ncaron" },
+ { 0x0145, 0, "Ncommaaccent" },
+ { 0xF76E, 0, "Nsmall" },
+ { 0x00D1, 0, "Ntilde" },
+ { 0xF7F1, 0, "Ntildesmall" },
+ { 0x039D, 0, "Nu" },
+ { 0x004F, 0117, "O" },
+ { 0x0152, 0, "OE" },
+ { 0xF6FA, 0, "OEsmall" },
+ { 0x00D3, 0, "Oacute" },
+ { 0xF7F3, 0, "Oacutesmall" },
+ { 0x014E, 0, "Obreve" },
+ { 0x00D4, 0, "Ocircumflex" },
+ { 0xF7F4, 0, "Ocircumflexsmall" },
+ { 0x00D6, 0, "Odieresis" },
+ { 0xF7F6, 0, "Odieresissmall" },
+ { 0xF6FB, 0, "Ogoneksmall" },
+ { 0x00D2, 0, "Ograve" },
+ { 0xF7F2, 0, "Ogravesmall" },
+ { 0x01A0, 0, "Ohorn" },
+ { 0x0150, 0, "Ohungarumlaut" },
+ { 0x014C, 0, "Omacron" },
+ { 0x2126, 0, "Omega" },
+ { 0x03A9, 0, "Omega" },
+ { 0x038F, 0, "Omegatonos" },
+ { 0x039F, 0, "Omicron" },
+ { 0x038C, 0, "Omicrontonos" },
+ { 0x00D8, 0351, "Oslash" },
+ { 0x01FE, 0, "Oslashacute" },
+ { 0xF7F8, 0, "Oslashsmall" },
+ { 0xF76F, 0, "Osmall" },
+ { 0x00D5, 0, "Otilde" },
+ { 0xF7F5, 0, "Otildesmall" },
+ { 0x0050, 0120, "P" },
+ { 0x03A6, 0, "Phi" },
+ { 0x03A0, 0, "Pi" },
+ { 0x03A8, 0, "Psi" },
+ { 0xF770, 0, "Psmall" },
+ { 0x0051, 0121, "Q" },
+ { 0xF771, 0, "Qsmall" },
+ { 0x0052, 0122, "R" },
+ { 0x0154, 0, "Racute" },
+ { 0x0158, 0, "Rcaron" },
+ { 0x0156, 0, "Rcommaaccent" },
+ { 0x211C, 0, "Rfraktur" },
+ { 0x03A1, 0, "Rho" },
+ { 0xF6FC, 0, "Ringsmall" },
+ { 0xF772, 0, "Rsmall" },
+ { 0x0053, 0123, "S" },
+ { 0x250C, 0, "SF010000" },
+ { 0x2514, 0, "SF020000" },
+ { 0x2510, 0, "SF030000" },
+ { 0x2518, 0, "SF040000" },
+ { 0x253C, 0, "SF050000" },
+ { 0x252C, 0, "SF060000" },
+ { 0x2534, 0, "SF070000" },
+ { 0x251C, 0, "SF080000" },
+ { 0x2524, 0, "SF090000" },
+ { 0x2500, 0, "SF100000" },
+ { 0x2502, 0, "SF110000" },
+ { 0x2561, 0, "SF190000" },
+ { 0x2562, 0, "SF200000" },
+ { 0x2556, 0, "SF210000" },
+ { 0x2555, 0, "SF220000" },
+ { 0x2563, 0, "SF230000" },
+ { 0x2551, 0, "SF240000" },
+ { 0x2557, 0, "SF250000" },
+ { 0x255D, 0, "SF260000" },
+ { 0x255C, 0, "SF270000" },
+ { 0x255B, 0, "SF280000" },
+ { 0x255E, 0, "SF360000" },
+ { 0x255F, 0, "SF370000" },
+ { 0x255A, 0, "SF380000" },
+ { 0x2554, 0, "SF390000" },
+ { 0x2569, 0, "SF400000" },
+ { 0x2566, 0, "SF410000" },
+ { 0x2560, 0, "SF420000" },
+ { 0x2550, 0, "SF430000" },
+ { 0x256C, 0, "SF440000" },
+ { 0x2567, 0, "SF450000" },
+ { 0x2568, 0, "SF460000" },
+ { 0x2564, 0, "SF470000" },
+ { 0x2565, 0, "SF480000" },
+ { 0x2559, 0, "SF490000" },
+ { 0x2558, 0, "SF500000" },
+ { 0x2552, 0, "SF510000" },
+ { 0x2553, 0, "SF520000" },
+ { 0x256B, 0, "SF530000" },
+ { 0x256A, 0, "SF540000" },
+ { 0x015A, 0, "Sacute" },
+ { 0x0160, 0, "Scaron" },
+ { 0xF6FD, 0, "Scaronsmall" },
+ { 0x015E, 0, "Scedilla" },
+ { 0xF6C1, 0, "Scedilla" },
+ { 0x015C, 0, "Scircumflex" },
+ { 0x0218, 0, "Scommaaccent" },
+ { 0x03A3, 0, "Sigma" },
+ { 0xF773, 0, "Ssmall" },
+ { 0x0054, 0124, "T" },
+ { 0x03A4, 0, "Tau" },
+ { 0x0166, 0, "Tbar" },
+ { 0x0164, 0, "Tcaron" },
+ { 0x0162, 0, "Tcommaaccent" },
+ { 0x021A, 0, "Tcommaaccent" },
+ { 0x0398, 0, "Theta" },
+ { 0x00DE, 0, "Thorn" },
+ { 0xF7FE, 0, "Thornsmall" },
+ { 0xF6FE, 0, "Tildesmall" },
+ { 0xF774, 0, "Tsmall" },
+ { 0x0055, 0125, "U" },
+ { 0x00DA, 0, "Uacute" },
+ { 0xF7FA, 0, "Uacutesmall" },
+ { 0x016C, 0, "Ubreve" },
+ { 0x00DB, 0, "Ucircumflex" },
+ { 0xF7FB, 0, "Ucircumflexsmall" },
+ { 0x00DC, 0, "Udieresis" },
+ { 0xF7FC, 0, "Udieresissmall" },
+ { 0x00D9, 0, "Ugrave" },
+ { 0xF7F9, 0, "Ugravesmall" },
+ { 0x01AF, 0, "Uhorn" },
+ { 0x0170, 0, "Uhungarumlaut" },
+ { 0x016A, 0, "Umacron" },
+ { 0x0172, 0, "Uogonek" },
+ { 0x03A5, 0, "Upsilon" },
+ { 0x03D2, 0, "Upsilon1" },
+ { 0x03AB, 0, "Upsilondieresis" },
+ { 0x038E, 0, "Upsilontonos" },
+ { 0x016E, 0, "Uring" },
+ { 0xF775, 0, "Usmall" },
+ { 0x0168, 0, "Utilde" },
+ { 0x0056, 0126, "V" },
+ { 0xF776, 0, "Vsmall" },
+ { 0x0057, 0127, "W" },
+ { 0x1E82, 0, "Wacute" },
+ { 0x0174, 0, "Wcircumflex" },
+ { 0x1E84, 0, "Wdieresis" },
+ { 0x1E80, 0, "Wgrave" },
+ { 0xF777, 0, "Wsmall" },
+ { 0x0058, 0130, "X" },
+ { 0x039E, 0, "Xi" },
+ { 0xF778, 0, "Xsmall" },
+ { 0x0059, 0131, "Y" },
+ { 0x00DD, 0, "Yacute" },
+ { 0xF7FD, 0, "Yacutesmall" },
+ { 0x0176, 0, "Ycircumflex" },
+ { 0x0178, 0, "Ydieresis" },
+ { 0xF7FF, 0, "Ydieresissmall" },
+ { 0x1EF2, 0, "Ygrave" },
+ { 0xF779, 0, "Ysmall" },
+ { 0x005A, 0132, "Z" },
+ { 0x0179, 0, "Zacute" },
+ { 0x017D, 0, "Zcaron" },
+ { 0xF6FF, 0, "Zcaronsmall" },
+ { 0x017B, 0, "Zdotaccent" },
+ { 0x0396, 0, "Zeta" },
+ { 0xF77A, 0, "Zsmall" },
+ { 0x0061, 0141, "a" },
+ { 0x00E1, 0, "aacute" },
+ { 0x0103, 0, "abreve" },
+ { 0x00E2, 0, "acircumflex" },
+ { 0x00B4, 0302, "acute" },
+ { 0x0301, 0, "acutecomb" },
+ { 0x00E4, 0, "adieresis" },
+ { 0x00E6, 0361, "ae" },
+ { 0x01FD, 0, "aeacute" },
+ { 0x2015, 0, "afii00208" },
+ { 0x0410, 0, "afii10017" },
+ { 0x0411, 0, "afii10018" },
+ { 0x0412, 0, "afii10019" },
+ { 0x0413, 0, "afii10020" },
+ { 0x0414, 0, "afii10021" },
+ { 0x0415, 0, "afii10022" },
+ { 0x0401, 0, "afii10023" },
+ { 0x0416, 0, "afii10024" },
+ { 0x0417, 0, "afii10025" },
+ { 0x0418, 0, "afii10026" },
+ { 0x0419, 0, "afii10027" },
+ { 0x041A, 0, "afii10028" },
+ { 0x041B, 0, "afii10029" },
+ { 0x041C, 0, "afii10030" },
+ { 0x041D, 0, "afii10031" },
+ { 0x041E, 0, "afii10032" },
+ { 0x041F, 0, "afii10033" },
+ { 0x0420, 0, "afii10034" },
+ { 0x0421, 0, "afii10035" },
+ { 0x0422, 0, "afii10036" },
+ { 0x0423, 0, "afii10037" },
+ { 0x0424, 0, "afii10038" },
+ { 0x0425, 0, "afii10039" },
+ { 0x0426, 0, "afii10040" },
+ { 0x0427, 0, "afii10041" },
+ { 0x0428, 0, "afii10042" },
+ { 0x0429, 0, "afii10043" },
+ { 0x042A, 0, "afii10044" },
+ { 0x042B, 0, "afii10045" },
+ { 0x042C, 0, "afii10046" },
+ { 0x042D, 0, "afii10047" },
+ { 0x042E, 0, "afii10048" },
+ { 0x042F, 0, "afii10049" },
+ { 0x0490, 0, "afii10050" },
+ { 0x0402, 0, "afii10051" },
+ { 0x0403, 0, "afii10052" },
+ { 0x0404, 0, "afii10053" },
+ { 0x0405, 0, "afii10054" },
+ { 0x0406, 0, "afii10055" },
+ { 0x0407, 0, "afii10056" },
+ { 0x0408, 0, "afii10057" },
+ { 0x0409, 0, "afii10058" },
+ { 0x040A, 0, "afii10059" },
+ { 0x040B, 0, "afii10060" },
+ { 0x040C, 0, "afii10061" },
+ { 0x040E, 0, "afii10062" },
+ { 0xF6C4, 0, "afii10063" },
+ { 0xF6C5, 0, "afii10064" },
+ { 0x0430, 0, "afii10065" },
+ { 0x0431, 0, "afii10066" },
+ { 0x0432, 0, "afii10067" },
+ { 0x0433, 0, "afii10068" },
+ { 0x0434, 0, "afii10069" },
+ { 0x0435, 0, "afii10070" },
+ { 0x0451, 0, "afii10071" },
+ { 0x0436, 0, "afii10072" },
+ { 0x0437, 0, "afii10073" },
+ { 0x0438, 0, "afii10074" },
+ { 0x0439, 0, "afii10075" },
+ { 0x043A, 0, "afii10076" },
+ { 0x043B, 0, "afii10077" },
+ { 0x043C, 0, "afii10078" },
+ { 0x043D, 0, "afii10079" },
+ { 0x043E, 0, "afii10080" },
+ { 0x043F, 0, "afii10081" },
+ { 0x0440, 0, "afii10082" },
+ { 0x0441, 0, "afii10083" },
+ { 0x0442, 0, "afii10084" },
+ { 0x0443, 0, "afii10085" },
+ { 0x0444, 0, "afii10086" },
+ { 0x0445, 0, "afii10087" },
+ { 0x0446, 0, "afii10088" },
+ { 0x0447, 0, "afii10089" },
+ { 0x0448, 0, "afii10090" },
+ { 0x0449, 0, "afii10091" },
+ { 0x044A, 0, "afii10092" },
+ { 0x044B, 0, "afii10093" },
+ { 0x044C, 0, "afii10094" },
+ { 0x044D, 0, "afii10095" },
+ { 0x044E, 0, "afii10096" },
+ { 0x044F, 0, "afii10097" },
+ { 0x0491, 0, "afii10098" },
+ { 0x0452, 0, "afii10099" },
+ { 0x0453, 0, "afii10100" },
+ { 0x0454, 0, "afii10101" },
+ { 0x0455, 0, "afii10102" },
+ { 0x0456, 0, "afii10103" },
+ { 0x0457, 0, "afii10104" },
+ { 0x0458, 0, "afii10105" },
+ { 0x0459, 0, "afii10106" },
+ { 0x045A, 0, "afii10107" },
+ { 0x045B, 0, "afii10108" },
+ { 0x045C, 0, "afii10109" },
+ { 0x045E, 0, "afii10110" },
+ { 0x040F, 0, "afii10145" },
+ { 0x0462, 0, "afii10146" },
+ { 0x0472, 0, "afii10147" },
+ { 0x0474, 0, "afii10148" },
+ { 0xF6C6, 0, "afii10192" },
+ { 0x045F, 0, "afii10193" },
+ { 0x0463, 0, "afii10194" },
+ { 0x0473, 0, "afii10195" },
+ { 0x0475, 0, "afii10196" },
+ { 0xF6C7, 0, "afii10831" },
+ { 0xF6C8, 0, "afii10832" },
+ { 0x04D9, 0, "afii10846" },
+ { 0x200E, 0, "afii299" },
+ { 0x200F, 0, "afii300" },
+ { 0x200D, 0, "afii301" },
+ { 0x066A, 0, "afii57381" },
+ { 0x060C, 0, "afii57388" },
+ { 0x0660, 0, "afii57392" },
+ { 0x0661, 0, "afii57393" },
+ { 0x0662, 0, "afii57394" },
+ { 0x0663, 0, "afii57395" },
+ { 0x0664, 0, "afii57396" },
+ { 0x0665, 0, "afii57397" },
+ { 0x0666, 0, "afii57398" },
+ { 0x0667, 0, "afii57399" },
+ { 0x0668, 0, "afii57400" },
+ { 0x0669, 0, "afii57401" },
+ { 0x061B, 0, "afii57403" },
+ { 0x061F, 0, "afii57407" },
+ { 0x0621, 0, "afii57409" },
+ { 0x0622, 0, "afii57410" },
+ { 0x0623, 0, "afii57411" },
+ { 0x0624, 0, "afii57412" },
+ { 0x0625, 0, "afii57413" },
+ { 0x0626, 0, "afii57414" },
+ { 0x0627, 0, "afii57415" },
+ { 0x0628, 0, "afii57416" },
+ { 0x0629, 0, "afii57417" },
+ { 0x062A, 0, "afii57418" },
+ { 0x062B, 0, "afii57419" },
+ { 0x062C, 0, "afii57420" },
+ { 0x062D, 0, "afii57421" },
+ { 0x062E, 0, "afii57422" },
+ { 0x062F, 0, "afii57423" },
+ { 0x0630, 0, "afii57424" },
+ { 0x0631, 0, "afii57425" },
+ { 0x0632, 0, "afii57426" },
+ { 0x0633, 0, "afii57427" },
+ { 0x0634, 0, "afii57428" },
+ { 0x0635, 0, "afii57429" },
+ { 0x0636, 0, "afii57430" },
+ { 0x0637, 0, "afii57431" },
+ { 0x0638, 0, "afii57432" },
+ { 0x0639, 0, "afii57433" },
+ { 0x063A, 0, "afii57434" },
+ { 0x0640, 0, "afii57440" },
+ { 0x0641, 0, "afii57441" },
+ { 0x0642, 0, "afii57442" },
+ { 0x0643, 0, "afii57443" },
+ { 0x0644, 0, "afii57444" },
+ { 0x0645, 0, "afii57445" },
+ { 0x0646, 0, "afii57446" },
+ { 0x0648, 0, "afii57448" },
+ { 0x0649, 0, "afii57449" },
+ { 0x064A, 0, "afii57450" },
+ { 0x064B, 0, "afii57451" },
+ { 0x064C, 0, "afii57452" },
+ { 0x064D, 0, "afii57453" },
+ { 0x064E, 0, "afii57454" },
+ { 0x064F, 0, "afii57455" },
+ { 0x0650, 0, "afii57456" },
+ { 0x0651, 0, "afii57457" },
+ { 0x0652, 0, "afii57458" },
+ { 0x0647, 0, "afii57470" },
+ { 0x06A4, 0, "afii57505" },
+ { 0x067E, 0, "afii57506" },
+ { 0x0686, 0, "afii57507" },
+ { 0x0698, 0, "afii57508" },
+ { 0x06AF, 0, "afii57509" },
+ { 0x0679, 0, "afii57511" },
+ { 0x0688, 0, "afii57512" },
+ { 0x0691, 0, "afii57513" },
+ { 0x06BA, 0, "afii57514" },
+ { 0x06D2, 0, "afii57519" },
+ { 0x06D5, 0, "afii57534" },
+ { 0x20AA, 0, "afii57636" },
+ { 0x05BE, 0, "afii57645" },
+ { 0x05C3, 0, "afii57658" },
+ { 0x05D0, 0, "afii57664" },
+ { 0x05D1, 0, "afii57665" },
+ { 0x05D2, 0, "afii57666" },
+ { 0x05D3, 0, "afii57667" },
+ { 0x05D4, 0, "afii57668" },
+ { 0x05D5, 0, "afii57669" },
+ { 0x05D6, 0, "afii57670" },
+ { 0x05D7, 0, "afii57671" },
+ { 0x05D8, 0, "afii57672" },
+ { 0x05D9, 0, "afii57673" },
+ { 0x05DA, 0, "afii57674" },
+ { 0x05DB, 0, "afii57675" },
+ { 0x05DC, 0, "afii57676" },
+ { 0x05DD, 0, "afii57677" },
+ { 0x05DE, 0, "afii57678" },
+ { 0x05DF, 0, "afii57679" },
+ { 0x05E0, 0, "afii57680" },
+ { 0x05E1, 0, "afii57681" },
+ { 0x05E2, 0, "afii57682" },
+ { 0x05E3, 0, "afii57683" },
+ { 0x05E4, 0, "afii57684" },
+ { 0x05E5, 0, "afii57685" },
+ { 0x05E6, 0, "afii57686" },
+ { 0x05E7, 0, "afii57687" },
+ { 0x05E8, 0, "afii57688" },
+ { 0x05E9, 0, "afii57689" },
+ { 0x05EA, 0, "afii57690" },
+ { 0xFB2A, 0, "afii57694" },
+ { 0xFB2B, 0, "afii57695" },
+ { 0xFB4B, 0, "afii57700" },
+ { 0xFB1F, 0, "afii57705" },
+ { 0x05F0, 0, "afii57716" },
+ { 0x05F1, 0, "afii57717" },
+ { 0x05F2, 0, "afii57718" },
+ { 0xFB35, 0, "afii57723" },
+ { 0x05B4, 0, "afii57793" },
+ { 0x05B5, 0, "afii57794" },
+ { 0x05B6, 0, "afii57795" },
+ { 0x05BB, 0, "afii57796" },
+ { 0x05B8, 0, "afii57797" },
+ { 0x05B7, 0, "afii57798" },
+ { 0x05B0, 0, "afii57799" },
+ { 0x05B2, 0, "afii57800" },
+ { 0x05B1, 0, "afii57801" },
+ { 0x05B3, 0, "afii57802" },
+ { 0x05C2, 0, "afii57803" },
+ { 0x05C1, 0, "afii57804" },
+ { 0x05B9, 0, "afii57806" },
+ { 0x05BC, 0, "afii57807" },
+ { 0x05BD, 0, "afii57839" },
+ { 0x05BF, 0, "afii57841" },
+ { 0x05C0, 0, "afii57842" },
+ { 0x02BC, 0, "afii57929" },
+ { 0x2105, 0, "afii61248" },
+ { 0x2113, 0, "afii61289" },
+ { 0x2116, 0, "afii61352" },
+ { 0x202C, 0, "afii61573" },
+ { 0x202D, 0, "afii61574" },
+ { 0x202E, 0, "afii61575" },
+ { 0x200C, 0, "afii61664" },
+ { 0x066D, 0, "afii63167" },
+ { 0x02BD, 0, "afii64937" },
+ { 0x00E0, 0, "agrave" },
+ { 0x2135, 0, "aleph" },
+ { 0x03B1, 0, "alpha" },
+ { 0x03AC, 0, "alphatonos" },
+ { 0x0101, 0, "amacron" },
+ { 0x0026, 046, "ampersand" },
+ { 0xF726, 0, "ampersandsmall" },
+ { 0x2220, 0, "angle" },
+ { 0x2329, 0, "angleleft" },
+ { 0x232A, 0, "angleright" },
+ { 0x0387, 0, "anoteleia" },
+ { 0x0105, 0, "aogonek" },
+ { 0x2248, 0, "approxequal" },
+ { 0x00E5, 0, "aring" },
+ { 0x01FB, 0, "aringacute" },
+ { 0x2194, 0, "arrowboth" },
+ { 0x21D4, 0, "arrowdblboth" },
+ { 0x21D3, 0, "arrowdbldown" },
+ { 0x21D0, 0, "arrowdblleft" },
+ { 0x21D2, 0, "arrowdblright" },
+ { 0x21D1, 0, "arrowdblup" },
+ { 0x2193, 0, "arrowdown" },
+ { 0xF8E7, 0, "arrowhorizex" },
+ { 0x2190, 0, "arrowleft" },
+ { 0x2192, 0, "arrowright" },
+ { 0x2191, 0, "arrowup" },
+ { 0x2195, 0, "arrowupdn" },
+ { 0x21A8, 0, "arrowupdnbse" },
+ { 0xF8E6, 0, "arrowvertex" },
+ { 0x005E, 0136, "asciicircum" },
+ { 0x007E, 0176, "asciitilde" },
+ { 0x002A, 052, "asterisk" },
+ { 0x2217, 0, "asteriskmath" },
+ { 0xF6E9, 0, "asuperior" },
+ { 0x0040, 0100, "at" },
+ { 0x00E3, 0, "atilde" },
+ { 0x0062, 0142, "b" },
+ { 0x005C, 0134, "backslash" },
+ { 0x007C, 0174, "bar" },
+ { 0x03B2, 0, "beta" },
+ { 0x2588, 0, "block" },
+ { 0xF8F4, 0, "braceex" },
+ { 0x007B, 0173, "braceleft" },
+ { 0xF8F3, 0, "braceleftbt" },
+ { 0xF8F2, 0, "braceleftmid" },
+ { 0xF8F1, 0, "bracelefttp" },
+ { 0x007D, 0175, "braceright" },
+ { 0xF8FE, 0, "bracerightbt" },
+ { 0xF8FD, 0, "bracerightmid" },
+ { 0xF8FC, 0, "bracerighttp" },
+ { 0x005B, 0133, "bracketleft" },
+ { 0xF8F0, 0, "bracketleftbt" },
+ { 0xF8EF, 0, "bracketleftex" },
+ { 0xF8EE, 0, "bracketlefttp" },
+ { 0x005D, 0135, "bracketright" },
+ { 0xF8FB, 0, "bracketrightbt" },
+ { 0xF8FA, 0, "bracketrightex" },
+ { 0xF8F9, 0, "bracketrighttp" },
+ { 0x02D8, 0306, "breve" },
+ { 0x00A6, 0, "brokenbar" },
+ { 0xF6EA, 0, "bsuperior" },
+ { 0x2022, 0267, "bullet" },
+ { 0x0063, 0143, "c" },
+ { 0x0107, 0, "cacute" },
+ { 0x02C7, 0317, "caron" },
+ { 0x21B5, 0, "carriagereturn" },
+ { 0x010D, 0, "ccaron" },
+ { 0x00E7, 0, "ccedilla" },
+ { 0x0109, 0, "ccircumflex" },
+ { 0x010B, 0, "cdotaccent" },
+ { 0x00B8, 0313, "cedilla" },
+ { 0x00A2, 0242, "cent" },
+ { 0xF6DF, 0, "centinferior" },
+ { 0xF7A2, 0, "centoldstyle" },
+ { 0xF6E0, 0, "centsuperior" },
+ { 0x03C7, 0, "chi" },
+ { 0x25CB, 0, "circle" },
+ { 0x2297, 0, "circlemultiply" },
+ { 0x2295, 0, "circleplus" },
+ { 0x02C6, 0303, "circumflex" },
+ { 0x2663, 0, "club" },
+ { 0x003A, 072, "colon" },
+ { 0x20A1, 0, "colonmonetary" },
+ { 0x002C, 054, "comma" },
+ { 0xF6C3, 0, "commaaccent" },
+ { 0xF6E1, 0, "commainferior" },
+ { 0xF6E2, 0, "commasuperior" },
+ { 0x2245, 0, "congruent" },
+ { 0x00A9, 0, "copyright" },
+ { 0xF8E9, 0, "copyrightsans" },
+ { 0xF6D9, 0, "copyrightserif" },
+ { 0x00A4, 0250, "currency" },
+ { 0xF6D1, 0, "cyrBreve" },
+ { 0xF6D2, 0, "cyrFlex" },
+ { 0xF6D4, 0, "cyrbreve" },
+ { 0xF6D5, 0, "cyrflex" },
+ { 0x0064, 0144, "d" },
+ { 0x2020, 0262, "dagger" },
+ { 0x2021, 0263, "daggerdbl" },
+ { 0xF6D3, 0, "dblGrave" },
+ { 0xF6D6, 0, "dblgrave" },
+ { 0x010F, 0, "dcaron" },
+ { 0x0111, 0, "dcroat" },
+ { 0x00B0, 0, "degree" },
+ { 0x03B4, 0, "delta" },
+ { 0x2666, 0, "diamond" },
+ { 0x00A8, 0310, "dieresis" },
+ { 0xF6D7, 0, "dieresisacute" },
+ { 0xF6D8, 0, "dieresisgrave" },
+ { 0x0385, 0, "dieresistonos" },
+ { 0x00F7, 0, "divide" },
+ { 0x2593, 0, "dkshade" },
+ { 0x2584, 0, "dnblock" },
+ { 0x0024, 044, "dollar" },
+ { 0xF6E3, 0, "dollarinferior" },
+ { 0xF724, 0, "dollaroldstyle" },
+ { 0xF6E4, 0, "dollarsuperior" },
+ { 0x20AB, 0, "dong" },
+ { 0x02D9, 0307, "dotaccent" },
+ { 0x0323, 0, "dotbelowcomb" },
+ { 0x0131, 0365, "dotlessi" },
+ { 0xF6BE, 0, "dotlessj" },
+ { 0x22C5, 0, "dotmath" },
+ { 0xF6EB, 0, "dsuperior" },
+ { 0x0065, 0145, "e" },
+ { 0x00E9, 0, "eacute" },
+ { 0x0115, 0, "ebreve" },
+ { 0x011B, 0, "ecaron" },
+ { 0x00EA, 0, "ecircumflex" },
+ { 0x00EB, 0, "edieresis" },
+ { 0x0117, 0, "edotaccent" },
+ { 0x00E8, 0, "egrave" },
+ { 0x0038, 070, "eight" },
+ { 0x2088, 0, "eightinferior" },
+ { 0xF738, 0, "eightoldstyle" },
+ { 0x2078, 0, "eightsuperior" },
+ { 0x2208, 0, "element" },
+ { 0x2026, 0274, "ellipsis" },
+ { 0x0113, 0, "emacron" },
+ { 0x2014, 0320, "emdash" },
+ { 0x2205, 0, "emptyset" },
+ { 0x2013, 0261, "endash" },
+ { 0x014B, 0, "eng" },
+ { 0x0119, 0, "eogonek" },
+ { 0x03B5, 0, "epsilon" },
+ { 0x03AD, 0, "epsilontonos" },
+ { 0x003D, 075, "equal" },
+ { 0x2261, 0, "equivalence" },
+ { 0x212E, 0, "estimated" },
+ { 0xF6EC, 0, "esuperior" },
+ { 0x03B7, 0, "eta" },
+ { 0x03AE, 0, "etatonos" },
+ { 0x00F0, 0, "eth" },
+ { 0x0021, 041, "exclam" },
+ { 0x203C, 0, "exclamdbl" },
+ { 0x00A1, 0241, "exclamdown" },
+ { 0xF7A1, 0, "exclamdownsmall" },
+ { 0xF721, 0, "exclamsmall" },
+ { 0x2203, 0, "existential" },
+ { 0x0066, 0146, "f" },
+ { 0x2640, 0, "female" },
+ { 0xFB00, 0, "ff" },
+ { 0xFB03, 0, "ffi" },
+ { 0xFB04, 0, "ffl" },
+ { 0xFB01, 0256, "fi" },
+ { 0x2012, 0, "figuredash" },
+ { 0x25A0, 0, "filledbox" },
+ { 0x25AC, 0, "filledrect" },
+ { 0x0035, 065, "five" },
+ { 0x215D, 0, "fiveeighths" },
+ { 0x2085, 0, "fiveinferior" },
+ { 0xF735, 0, "fiveoldstyle" },
+ { 0x2075, 0, "fivesuperior" },
+ { 0xFB02, 0257, "fl" },
+ { 0x0192, 0246, "florin" },
+ { 0x0034, 064, "four" },
+ { 0x2084, 0, "fourinferior" },
+ { 0xF734, 0, "fouroldstyle" },
+ { 0x2074, 0, "foursuperior" },
+ { 0x2044, 0244, "fraction" },
+ { 0x2215, 0244, "fraction" },
+ { 0x20A3, 0, "franc" },
+ { 0x0067, 0147, "g" },
+ { 0x03B3, 0, "gamma" },
+ { 0x011F, 0, "gbreve" },
+ { 0x01E7, 0, "gcaron" },
+ { 0x011D, 0, "gcircumflex" },
+ { 0x0123, 0, "gcommaaccent" },
+ { 0x0121, 0, "gdotaccent" },
+ { 0x00DF, 0373, "germandbls" },
+ { 0x2207, 0, "gradient" },
+ { 0x0060, 0301, "grave" },
+ { 0x0300, 0, "gravecomb" },
+ { 0x003E, 076, "greater" },
+ { 0x2265, 0, "greaterequal" },
+ { 0x00AB, 0253, "guillemotleft" },
+ { 0x00BB, 0273, "guillemotright" },
+ { 0x2039, 0254, "guilsinglleft" },
+ { 0x203A, 0255, "guilsinglright" },
+ { 0x0068, 0150, "h" },
+ { 0x0127, 0, "hbar" },
+ { 0x0125, 0, "hcircumflex" },
+ { 0x2665, 0, "heart" },
+ { 0x0309, 0, "hookabovecomb" },
+ { 0x2302, 0, "house" },
+ { 0x02DD, 0315, "hungarumlaut" },
+ { 0x002D, 055, "hyphen" },
+ { 0x00AD, 0, "hyphen" },
+ { 0xF6E5, 0, "hypheninferior" },
+ { 0xF6E6, 0, "hyphensuperior" },
+ { 0x0069, 0151, "i" },
+ { 0x00ED, 0, "iacute" },
+ { 0x012D, 0, "ibreve" },
+ { 0x00EE, 0, "icircumflex" },
+ { 0x00EF, 0, "idieresis" },
+ { 0x00EC, 0, "igrave" },
+ { 0x0133, 0, "ij" },
+ { 0x012B, 0, "imacron" },
+ { 0x221E, 0, "infinity" },
+ { 0x222B, 0, "integral" },
+ { 0x2321, 0, "integralbt" },
+ { 0xF8F5, 0, "integralex" },
+ { 0x2320, 0, "integraltp" },
+ { 0x2229, 0, "intersection" },
+ { 0x25D8, 0, "invbullet" },
+ { 0x25D9, 0, "invcircle" },
+ { 0x263B, 0, "invsmileface" },
+ { 0x012F, 0, "iogonek" },
+ { 0x03B9, 0, "iota" },
+ { 0x03CA, 0, "iotadieresis" },
+ { 0x0390, 0, "iotadieresistonos" },
+ { 0x03AF, 0, "iotatonos" },
+ { 0xF6ED, 0, "isuperior" },
+ { 0x0129, 0, "itilde" },
+ { 0x006A, 0152, "j" },
+ { 0x0135, 0, "jcircumflex" },
+ { 0x006B, 0153, "k" },
+ { 0x03BA, 0, "kappa" },
+ { 0x0137, 0, "kcommaaccent" },
+ { 0x0138, 0, "kgreenlandic" },
+ { 0x006C, 0154, "l" },
+ { 0x013A, 0, "lacute" },
+ { 0x03BB, 0, "lambda" },
+ { 0x013E, 0, "lcaron" },
+ { 0x013C, 0, "lcommaaccent" },
+ { 0x0140, 0, "ldot" },
+ { 0x003C, 074, "less" },
+ { 0x2264, 0, "lessequal" },
+ { 0x258C, 0, "lfblock" },
+ { 0x20A4, 0, "lira" },
+ { 0xF6C0, 0, "ll" },
+ { 0x2227, 0, "logicaland" },
+ { 0x00AC, 0, "logicalnot" },
+ { 0x2228, 0, "logicalor" },
+ { 0x017F, 0, "longs" },
+ { 0x25CA, 0, "lozenge" },
+ { 0x0142, 0370, "lslash" },
+ { 0xF6EE, 0, "lsuperior" },
+ { 0x2591, 0, "ltshade" },
+ { 0x006D, 0155, "m" },
+ { 0x00AF, 0305, "macron" },
+ { 0x02C9, 0305, "macron" },
+ { 0x2642, 0, "male" },
+ { 0x2212, 0, "minus" },
+ { 0x2032, 0, "minute" },
+ { 0xF6EF, 0, "msuperior" },
+ { 0x00B5, 0, "mu" },
+ { 0x03BC, 0, "mu" },
+ { 0x00D7, 0, "multiply" },
+ { 0x266A, 0, "musicalnote" },
+ { 0x266B, 0, "musicalnotedbl" },
+ { 0x006E, 0156, "n" },
+ { 0x0144, 0, "nacute" },
+ { 0x0149, 0, "napostrophe" },
+ { 0x0148, 0, "ncaron" },
+ { 0x0146, 0, "ncommaaccent" },
+ { 0x0039, 071, "nine" },
+ { 0x2089, 0, "nineinferior" },
+ { 0xF739, 0, "nineoldstyle" },
+ { 0x2079, 0, "ninesuperior" },
+ { 0x2209, 0, "notelement" },
+ { 0x2260, 0, "notequal" },
+ { 0x2284, 0, "notsubset" },
+ { 0x207F, 0, "nsuperior" },
+ { 0x00F1, 0, "ntilde" },
+ { 0x03BD, 0, "nu" },
+ { 0x0023, 043, "numbersign" },
+ { 0x006F, 0157, "o" },
+ { 0x00F3, 0, "oacute" },
+ { 0x014F, 0, "obreve" },
+ { 0x00F4, 0, "ocircumflex" },
+ { 0x00F6, 0, "odieresis" },
+ { 0x0153, 0372, "oe" },
+ { 0x02DB, 0316, "ogonek" },
+ { 0x00F2, 0, "ograve" },
+ { 0x01A1, 0, "ohorn" },
+ { 0x0151, 0, "ohungarumlaut" },
+ { 0x014D, 0, "omacron" },
+ { 0x03C9, 0, "omega" },
+ { 0x03D6, 0, "omega1" },
+ { 0x03CE, 0, "omegatonos" },
+ { 0x03BF, 0, "omicron" },
+ { 0x03CC, 0, "omicrontonos" },
+ { 0x0031, 061, "one" },
+ { 0x2024, 0, "onedotenleader" },
+ { 0x215B, 0, "oneeighth" },
+ { 0xF6DC, 0, "onefitted" },
+ { 0x00BD, 0, "onehalf" },
+ { 0x2081, 0, "oneinferior" },
+ { 0xF731, 0, "oneoldstyle" },
+ { 0x00BC, 0, "onequarter" },
+ { 0x00B9, 0, "onesuperior" },
+ { 0x2153, 0, "onethird" },
+ { 0x25E6, 0, "openbullet" },
+ { 0x00AA, 0343, "ordfeminine" },
+ { 0x00BA, 0353, "ordmasculine" },
+ { 0x221F, 0, "orthogonal" },
+ { 0x00F8, 0371, "oslash" },
+ { 0x01FF, 0, "oslashacute" },
+ { 0xF6F0, 0, "osuperior" },
+ { 0x00F5, 0, "otilde" },
+ { 0x0070, 0160, "p" },
+ { 0x00B6, 0266, "paragraph" },
+ { 0x0028, 050, "parenleft" },
+ { 0xF8ED, 0, "parenleftbt" },
+ { 0xF8EC, 0, "parenleftex" },
+ { 0x208D, 0, "parenleftinferior" },
+ { 0x207D, 0, "parenleftsuperior" },
+ { 0xF8EB, 0, "parenlefttp" },
+ { 0x0029, 051, "parenright" },
+ { 0xF8F8, 0, "parenrightbt" },
+ { 0xF8F7, 0, "parenrightex" },
+ { 0x208E, 0, "parenrightinferior" },
+ { 0x207E, 0, "parenrightsuperior" },
+ { 0xF8F6, 0, "parenrighttp" },
+ { 0x2202, 0, "partialdiff" },
+ { 0x0025, 045, "percent" },
+ { 0x002E, 056, "period" },
+ { 0x00B7, 0264, "periodcentered" },
+ { 0x2219, 0, "periodcentered" },
+ { 0xF6E7, 0, "periodinferior" },
+ { 0xF6E8, 0, "periodsuperior" },
+ { 0x22A5, 0, "perpendicular" },
+ { 0x2030, 0275, "perthousand" },
+ { 0x20A7, 0, "peseta" },
+ { 0x03C6, 0, "phi" },
+ { 0x03D5, 0, "phi1" },
+ { 0x03C0, 0, "pi" },
+ { 0x002B, 053, "plus" },
+ { 0x00B1, 0, "plusminus" },
+ { 0x211E, 0, "prescription" },
+ { 0x220F, 0, "product" },
+ { 0x2282, 0, "propersubset" },
+ { 0x2283, 0, "propersuperset" },
+ { 0x221D, 0, "proportional" },
+ { 0x03C8, 0, "psi" },
+ { 0x0071, 0161, "q" },
+ { 0x003F, 077, "question" },
+ { 0x00BF, 0277, "questiondown" },
+ { 0xF7BF, 0, "questiondownsmall" },
+ { 0xF73F, 0, "questionsmall" },
+ { 0x0022, 042, "quotedbl" },
+ { 0x201E, 0271, "quotedblbase" },
+ { 0x201C, 0252, "quotedblleft" },
+ { 0x201D, 0272, "quotedblright" },
+ { 0x2018, 0140, "quoteleft" },
+ { 0x201B, 0, "quotereversed" },
+ { 0x2019, 047, "quoteright" },
+ { 0x201A, 0270, "quotesinglbase" },
+ { 0x0027, 0251, "quotesingle" },
+ { 0x0072, 0162, "r" },
+ { 0x0155, 0, "racute" },
+ { 0x221A, 0, "radical" },
+ { 0xF8E5, 0, "radicalex" },
+ { 0x0159, 0, "rcaron" },
+ { 0x0157, 0, "rcommaaccent" },
+ { 0x2286, 0, "reflexsubset" },
+ { 0x2287, 0, "reflexsuperset" },
+ { 0x00AE, 0, "registered" },
+ { 0xF8E8, 0, "registersans" },
+ { 0xF6DA, 0, "registerserif" },
+ { 0x2310, 0, "revlogicalnot" },
+ { 0x03C1, 0, "rho" },
+ { 0x02DA, 0312, "ring" },
+ { 0xF6F1, 0, "rsuperior" },
+ { 0x2590, 0, "rtblock" },
+ { 0xF6DD, 0, "rupiah" },
+ { 0x0073, 0163, "s" },
+ { 0x015B, 0, "sacute" },
+ { 0x0161, 0, "scaron" },
+ { 0x015F, 0, "scedilla" },
+ { 0xF6C2, 0, "scedilla" },
+ { 0x015D, 0, "scircumflex" },
+ { 0x0219, 0, "scommaaccent" },
+ { 0x2033, 0, "second" },
+ { 0x00A7, 0247, "section" },
+ { 0x003B, 073, "semicolon" },
+ { 0x0037, 067, "seven" },
+ { 0x215E, 0, "seveneighths" },
+ { 0x2087, 0, "seveninferior" },
+ { 0xF737, 0, "sevenoldstyle" },
+ { 0x2077, 0, "sevensuperior" },
+ { 0x2592, 0, "shade" },
+ { 0x03C3, 0, "sigma" },
+ { 0x03C2, 0, "sigma1" },
+ { 0x223C, 0, "similar" },
+ { 0x0036, 066, "six" },
+ { 0x2086, 0, "sixinferior" },
+ { 0xF736, 0, "sixoldstyle" },
+ { 0x2076, 0, "sixsuperior" },
+ { 0x002F, 057, "slash" },
+ { 0x263A, 0, "smileface" },
+ { 0x0020, 040, "space" },
+ { 0x00A0, 040, "space" },
+ { 0x2660, 0, "spade" },
+ { 0xF6F2, 0, "ssuperior" },
+ { 0x00A3, 0243, "sterling" },
+ { 0x220B, 0, "suchthat" },
+ { 0x2211, 0, "summation" },
+ { 0x263C, 0, "sun" },
+ { 0x0074, 0164, "t" },
+ { 0x03C4, 0, "tau" },
+ { 0x0167, 0, "tbar" },
+ { 0x0165, 0, "tcaron" },
+ { 0x0163, 0, "tcommaaccent" },
+ { 0x021B, 0, "tcommaaccent" },
+ { 0x2234, 0, "therefore" },
+ { 0x03B8, 0, "theta" },
+ { 0x03D1, 0, "theta1" },
+ { 0x00FE, 0, "thorn" },
+ { 0x0033, 063, "three" },
+ { 0x215C, 0, "threeeighths" },
+ { 0x2083, 0, "threeinferior" },
+ { 0xF733, 0, "threeoldstyle" },
+ { 0x00BE, 0, "threequarters" },
+ { 0xF6DE, 0, "threequartersemdash" },
+ { 0x00B3, 0, "threesuperior" },
+ { 0x02DC, 0304, "tilde" },
+ { 0x0303, 0, "tildecomb" },
+ { 0x0384, 0, "tonos" },
+ { 0x2122, 0, "trademark" },
+ { 0xF8EA, 0, "trademarksans" },
+ { 0xF6DB, 0, "trademarkserif" },
+ { 0x25BC, 0, "triagdn" },
+ { 0x25C4, 0, "triaglf" },
+ { 0x25BA, 0, "triagrt" },
+ { 0x25B2, 0, "triagup" },
+ { 0xF6F3, 0, "tsuperior" },
+ { 0x0032, 062, "two" },
+ { 0x2025, 0, "twodotenleader" },
+ { 0x2082, 0, "twoinferior" },
+ { 0xF732, 0, "twooldstyle" },
+ { 0x00B2, 0, "twosuperior" },
+ { 0x2154, 0, "twothirds" },
+ { 0x0075, 0165, "u" },
+ { 0x00FA, 0, "uacute" },
+ { 0x016D, 0, "ubreve" },
+ { 0x00FB, 0, "ucircumflex" },
+ { 0x00FC, 0, "udieresis" },
+ { 0x00F9, 0, "ugrave" },
+ { 0x01B0, 0, "uhorn" },
+ { 0x0171, 0, "uhungarumlaut" },
+ { 0x016B, 0, "umacron" },
+ { 0x005F, 0137, "underscore" },
+ { 0x2017, 0, "underscoredbl" },
+ { 0x222A, 0, "union" },
+ { 0x2200, 0, "universal" },
+ { 0x0173, 0, "uogonek" },
+ { 0x2580, 0, "upblock" },
+ { 0x03C5, 0, "upsilon" },
+ { 0x03CB, 0, "upsilondieresis" },
+ { 0x03B0, 0, "upsilondieresistonos" },
+ { 0x03CD, 0, "upsilontonos" },
+ { 0x016F, 0, "uring" },
+ { 0x0169, 0, "utilde" },
+ { 0x0076, 0166, "v" },
+ { 0x0077, 0167, "w" },
+ { 0x1E83, 0, "wacute" },
+ { 0x0175, 0, "wcircumflex" },
+ { 0x1E85, 0, "wdieresis" },
+ { 0x2118, 0, "weierstrass" },
+ { 0x1E81, 0, "wgrave" },
+ { 0x0078, 0170, "x" },
+ { 0x03BE, 0, "xi" },
+ { 0x0079, 0171, "y" },
+ { 0x00FD, 0, "yacute" },
+ { 0x0177, 0, "ycircumflex" },
+ { 0x00FF, 0, "ydieresis" },
+ { 0x00A5, 0245, "yen" },
+ { 0x1EF3, 0, "ygrave" },
+ { 0x007A, 0172, "z" },
+ { 0x017A, 0, "zacute" },
+ { 0x017E, 0, "zcaron" },
+ { 0x017C, 0, "zdotaccent" },
+ { 0x0030, 060, "zero" },
+ { 0x2080, 0, "zeroinferior" },
+ { 0xF730, 0, "zerooldstyle" },
+ { 0x2070, 0, "zerosuperior" },
+ { 0x03B6, 0, "zeta" }
+};
diff --git a/vcl/unx/generic/fontmanager/afm_keyword_list b/vcl/unx/generic/fontmanager/afm_keyword_list
new file mode 100644
index 000000000000..c9bb13467e3e
--- /dev/null
+++ b/vcl/unx/generic/fontmanager/afm_keyword_list
@@ -0,0 +1,62 @@
+%language=C++
+%global-table
+%null-strings
+%struct-type
+struct hash_entry { const char* name; enum parseKey eKey; };
+%%
+Ascender,ASCENDER
+Ascent,ASCENT
+B,CHARBBOX
+C,CODE
+CC,COMPCHAR
+CH,CODEHEX
+CapHeight,CAPHEIGHT
+CharWidth,CHARWIDTH
+CharacterSet,CHARACTERSET
+Characters,CHARACTERS
+Comment,COMMENT
+Descender,DESCENDER
+Descent,DESCENT
+Em,EM
+EncodingScheme,ENCODINGSCHEME
+EndCharMetrics,ENDCHARMETRICS
+EndComposites,ENDCOMPOSITES
+EndDirection,ENDDIRECTION
+EndFontMetrics,ENDFONTMETRICS
+EndKernData,ENDKERNDATA
+EndKernPairs,ENDKERNPAIRS
+EndTrackKern,ENDTRACKKERN
+FamilyName,FAMILYNAME
+FontBBox,FONTBBOX
+FontName,FONTNAME
+FullName,FULLNAME
+IsBaseFont,ISBASEFONT
+IsFixedPitch,ISFIXEDPITCH
+ItalicAngle,ITALICANGLE
+KP,KERNPAIR
+KPX,KERNPAIRXAMT
+L,LIGATURE
+MappingScheme,MAPPINGSCHEME
+MetricsSets,METRICSSETS
+N,CHARNAME
+Notice,NOTICE
+PCC,COMPCHARPIECE
+StartCharMetrics,STARTCHARMETRICS
+StartComposites,STARTCOMPOSITES
+StartDirection,STARTDIRECTION
+StartFontMetrics,STARTFONTMETRICS
+StartKernData,STARTKERNDATA
+StartKernPairs,STARTKERNPAIRS
+StartTrackKern,STARTTRACKKERN
+StdHW,STDHW
+StdVW,STDVW
+TrackKern,TRACKKERN
+UnderlinePosition,UNDERLINEPOSITION
+UnderlineThickness,UNDERLINETHICKNESS
+V,VVECTOR
+Version,VERSION
+W,XYWIDTH
+W0X,X0WIDTH
+WX,XWIDTH
+Weight,WEIGHT
+XHeight,XHEIGHT
diff --git a/vcl/unx/generic/fontmanager/fontcache.cxx b/vcl/unx/generic/fontmanager/fontcache.cxx
new file mode 100644
index 000000000000..84f48f27d1cf
--- /dev/null
+++ b/vcl/unx/generic/fontmanager/fontcache.cxx
@@ -0,0 +1,740 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <cstdlib>
+#include <cstring>
+
+#include "fontcache.hxx"
+
+#include "osl/thread.h"
+
+#include "unotools/atom.hxx"
+
+#include "tools/stream.hxx"
+
+#include <rtl/strbuf.hxx>
+
+#include <unistd.h>
+#include <sys/stat.h>
+
+#if OSL_DEBUG_LEVEL >1
+#include <cstdio>
+#endif
+
+#define CACHE_MAGIC "LibreOffice PspFontCacheFile format 6"
+
+using namespace std;
+using namespace psp;
+using namespace utl;
+
+/*
+ * FontCache constructor
+ */
+
+FontCache::FontCache()
+{
+ m_bDoFlush = false;
+ m_aCacheFile = getOfficePath( UserPath );
+ if( !m_aCacheFile.isEmpty() )
+ {
+ m_aCacheFile += "/user/psprint/pspfontcache";
+ read();
+ }
+}
+
+/*
+ * FontCache destructor
+ */
+
+FontCache::~FontCache()
+{
+ clearCache();
+}
+
+/*
+ * FontCache::clearCache
+ */
+void FontCache::clearCache()
+{
+ for( FontCacheData::iterator dir_it = m_aCache.begin(); dir_it != m_aCache.end(); ++dir_it )
+ {
+ for( FontDirMap::iterator entry_it = dir_it->second.m_aEntries.begin(); entry_it != dir_it->second.m_aEntries.end(); ++entry_it )
+ {
+ for( FontCacheEntry::iterator font_it = entry_it->second.m_aEntry.begin(); font_it != entry_it->second.m_aEntry.end(); ++font_it )
+ delete *font_it;
+ }
+ }
+ m_aCache.clear();
+}
+
+/*
+ * FontCache::Commit
+ */
+
+void FontCache::flush()
+{
+ if( ! m_bDoFlush || m_aCacheFile.isEmpty() )
+ return;
+
+ SvFileStream aStream;
+ aStream.Open( m_aCacheFile, StreamMode::WRITE | StreamMode::TRUNC );
+ if( ! (aStream.IsOpen() && aStream.IsWritable()) )
+ {
+#if OSL_DEBUG_LEVEL > 1
+ fprintf( stderr, "FontCache::flush: opening cache file %s failed\n", OUStringToOString(m_aCacheFile, osl_getThreadTextEncoding()).getStr() );
+#endif
+ return;
+ }
+
+ aStream.SetLineDelimiter( LINEEND_LF );
+ aStream.WriteLine( CACHE_MAGIC );
+
+ PrintFontManager& rManager( PrintFontManager::get() );
+ MultiAtomProvider* pAtoms = rManager.m_pAtoms;
+
+ for( FontCacheData::const_iterator dir_it = m_aCache.begin(); dir_it != m_aCache.end(); ++ dir_it )
+ {
+ const FontDirMap& rDir( dir_it->second.m_aEntries );
+
+ OString aDirectory(rManager.getDirectory(dir_it->first));
+ OStringBuffer aLine("FontCacheDirectory:");
+ aLine.append(dir_it->second.m_nTimestamp);
+ aLine.append(':');
+ aLine.append(aDirectory);
+ if( rDir.empty() && dir_it->second.m_bNoFiles )
+ aLine.insert(0, "Empty");
+ aStream.WriteLine(aLine.makeStringAndClear());
+
+ for( FontDirMap::const_iterator entry_it = rDir.begin(); entry_it != rDir.end(); ++entry_it )
+ {
+ // insert cache entries
+ const FontCacheEntry& rEntry( entry_it->second.m_aEntry );
+ if( rEntry.empty() )
+ continue;
+
+ aLine.append("File:");
+ aLine.append(entry_it->first);
+ aStream.WriteLine(aLine.makeStringAndClear());
+
+ int nEntrySize = entry_it->second.m_aEntry.size();
+ // write: type;nfonts
+ aLine.append(static_cast<sal_Int32>(rEntry.front()->m_eType));
+ aLine.append(';');
+ aLine.append(static_cast<sal_Int32>(nEntrySize));
+ aStream.WriteLine(aLine.makeStringAndClear());
+
+ sal_Int32 nSubEntry = 0;
+ for( FontCacheEntry::const_iterator it = rEntry.begin(); it != rEntry.end(); ++it, nSubEntry++ )
+ {
+ /*
+ * for each font entry write:
+ * name[;name[;name]]
+ * fontnr;PSName;italic;weight;width;pitch;encoding;ascend;descend;leading;vsubst;gxw;gxh;gyw;gyh;useroverride;embed;antialias[;{metricfile,typeflags}][;stylename]
+ */
+ if( nEntrySize > 1 )
+ nSubEntry = static_cast<const PrintFontManager::TrueTypeFontFile*>(*it)->m_nCollectionEntry;
+ else
+ nSubEntry = 0;
+
+ aLine.append(OUStringToOString(pAtoms->getString( ATOM_FAMILYNAME, (*it)->m_nFamilyName), RTL_TEXTENCODING_UTF8));
+ for( ::std::list< int >::const_iterator name_it = (*it)->m_aAliases.begin(); name_it != (*it)->m_aAliases.end(); ++name_it )
+ {
+ const OUString& rAdd( pAtoms->getString( ATOM_FAMILYNAME, *name_it ) );
+ if( !rAdd.isEmpty() )
+ {
+ aLine.append(';');
+ aLine.append(OUStringToOString(rAdd, RTL_TEXTENCODING_UTF8));
+ }
+ }
+ aStream.WriteLine(aLine.makeStringAndClear());
+
+ const OUString& rPSName( pAtoms->getString( ATOM_PSNAME, (*it)->m_nPSName ) );
+ aLine.append(nSubEntry);
+ aLine.append(';');
+ aLine.append(OUStringToOString(rPSName, RTL_TEXTENCODING_UTF8));
+ aLine.append(';');
+ aLine.append(static_cast<sal_Int32>((*it)->m_eItalic));
+ aLine.append(';');
+ aLine.append(static_cast<sal_Int32>((*it)->m_eWeight));
+ aLine.append(';');
+ aLine.append(static_cast<sal_Int32>((*it)->m_eWidth));
+ aLine.append(';');
+ aLine.append(static_cast<sal_Int32>((*it)->m_ePitch));
+ aLine.append(';');
+ aLine.append(static_cast<sal_Int32>((*it)->m_aEncoding));
+ aLine.append(';');
+ aLine.append(static_cast<sal_Int32>((*it)->m_nAscend));
+ aLine.append(';');
+ aLine.append(static_cast<sal_Int32>((*it)->m_nDescend));
+ aLine.append(';');
+ aLine.append(static_cast<sal_Int32>((*it)->m_nLeading));
+ aLine.append(';');
+ aLine.append((*it)->m_bHaveVerticalSubstitutedGlyphs ? '1' : '0');
+ aLine.append(';');
+ aLine.append(static_cast<sal_Int32>((*it)->m_aGlobalMetricX.width ));
+ aLine.append(';');
+ aLine.append(static_cast<sal_Int32>((*it)->m_aGlobalMetricX.height));
+ aLine.append(';');
+ aLine.append(static_cast<sal_Int32>((*it)->m_aGlobalMetricY.width ));
+ aLine.append(';');
+ aLine.append(static_cast<sal_Int32>((*it)->m_aGlobalMetricY.height));
+ aLine.append(';');
+ aLine.append((*it)->m_bUserOverride ? '1' : '0');
+ aLine.append(';');
+ aLine.append(static_cast<sal_Int32>(0));
+ aLine.append(';');
+ aLine.append(static_cast<sal_Int32>(0));
+
+ switch( (*it)->m_eType )
+ {
+ case fonttype::Type1:
+ aLine.append(';');
+ aLine.append(static_cast<const PrintFontManager::Type1FontFile*>(*it)->m_aMetricFile);
+ break;
+ case fonttype::TrueType:
+ aLine.append(';');
+ aLine.append(static_cast<sal_Int32>(static_cast<const PrintFontManager::TrueTypeFontFile*>(*it)->m_nTypeFlags));
+ break;
+ default: break;
+ }
+ if( !(*it)->m_aStyleName.isEmpty() )
+ {
+ aLine.append(';');
+ aLine.append(OUStringToOString((*it)->m_aStyleName, RTL_TEXTENCODING_UTF8));
+ }
+ aStream.WriteLine(aLine.makeStringAndClear());
+ }
+ aStream.WriteLine(OString());
+ }
+ }
+ m_bDoFlush = false;
+}
+
+/*
+ * FontCache::read
+ */
+
+void FontCache::read()
+{
+ PrintFontManager& rManager( PrintFontManager::get() );
+ MultiAtomProvider* pAtoms = rManager.m_pAtoms;
+
+ SvFileStream aStream( m_aCacheFile, StreamMode::READ );
+ if( ! aStream.IsOpen() )
+ {
+#if OSL_DEBUG_LEVEL > 1
+ fprintf( stderr, "FontCache::read: opening cache file %s failed\n", OUStringToOString(m_aCacheFile, osl_getThreadTextEncoding()).getStr() );
+#endif
+ return;
+ }
+
+ OString aLine;
+ aStream.ReadLine( aLine );
+ if ( !(aLine == CACHE_MAGIC) )
+ {
+ #if OSL_DEBUG_LEVEL >1
+ fprintf( stderr, "FontCache::read: cache file %s fails magic test\n", OUStringToOString(m_aCacheFile, osl_getThreadTextEncoding()).getStr() );
+ #endif
+ return;
+ }
+
+ int nDir = 0;
+ FontDirMap* pDir = nullptr;
+ bool bKeepOnlyUserOverridden = false;
+ do
+ {
+ aStream.ReadLine( aLine );
+ if( aLine.startsWith("FontCacheDirectory:") ||
+ aLine.startsWith("EmptyFontCacheDirectory:") )
+ {
+ bool bEmpty = aLine.startsWith("Empty");
+ sal_Int32 nSearchIndex = bEmpty ? 24 : 19;
+
+ OString aDir;
+ sal_Int64 nTimestamp = 0;
+ sal_Int32 nTEnd = aLine.indexOf( ':', nSearchIndex );
+ if( nTEnd != -1 )
+ {
+ OString aTimeStamp = aLine.copy( nSearchIndex, nTEnd - nSearchIndex );
+ nTimestamp = aTimeStamp.toInt64();
+ aDir = aLine.copy( nTEnd+1 );
+ }
+ else
+ {
+ // invalid format, remove
+ pDir = nullptr;
+ nDir = 0;
+ m_bDoFlush = true;
+ continue;
+ }
+
+ // is the directory modified ?
+ struct stat aStat;
+ if( stat( aDir.getStr(), &aStat ) ||
+ ! S_ISDIR(aStat.st_mode) )
+ {
+ // remove outdated cache data
+ pDir = nullptr;
+ nDir = 0;
+ m_bDoFlush = true;
+ continue;
+ }
+ else
+ {
+ nDir = rManager.getDirectoryAtom( aDir, true );
+ m_aCache[ nDir ].m_nTimestamp = (sal_Int64)aStat.st_mtime;
+ m_aCache[ nDir ].m_bNoFiles = bEmpty;
+ pDir = bEmpty ? nullptr : &m_aCache[ nDir ].m_aEntries;
+ bKeepOnlyUserOverridden = ((sal_Int64)aStat.st_mtime != nTimestamp);
+ m_aCache[ nDir ].m_bUserOverrideOnly = bKeepOnlyUserOverridden;
+ }
+ }
+ else if( pDir && aLine.startsWith("File:") )
+ {
+ OString aFile( aLine.copy( 5 ) );
+ aStream.ReadLine( aLine );
+
+ const char* pLine = aLine.getStr();
+
+ fonttype::type eType = (fonttype::type)atoi( pLine );
+ if( eType != fonttype::TrueType &&
+ eType != fonttype::Type1 )
+ continue;
+ while( *pLine && *pLine != ';' )
+ pLine++;
+ if( *pLine != ';' )
+ continue;
+
+ pLine++;
+ sal_Int32 nFonts = atoi( pLine );
+ for( int n = 0; n < nFonts; n++ )
+ {
+ aStream.ReadLine( aLine );
+ pLine = aLine.getStr();
+ int nLen = aLine.getLength();
+
+ PrintFontManager::PrintFont* pFont = nullptr;
+ switch( eType )
+ {
+ case fonttype::TrueType:
+ pFont = new PrintFontManager::TrueTypeFontFile();
+ break;
+ case fonttype::Type1:
+ pFont = new PrintFontManager::Type1FontFile();
+ break;
+ default: break;
+ }
+
+ sal_Int32 nIndex;
+
+ for( nIndex = 0; nIndex < nLen && pLine[nIndex] != ';'; nIndex++ )
+ ;
+
+ pFont->m_nFamilyName = pAtoms->getAtom( ATOM_FAMILYNAME,
+ OUString( pLine, nIndex, RTL_TEXTENCODING_UTF8 ),
+ true );
+ while( nIndex < nLen )
+ {
+ sal_Int32 nLastIndex = nIndex+1;
+ for( nIndex = nLastIndex ; nIndex < nLen && pLine[nIndex] != ';'; nIndex++ )
+ ;
+ if( nIndex - nLastIndex )
+ {
+ OUString aAlias( pLine+nLastIndex, nIndex-nLastIndex, RTL_TEXTENCODING_UTF8 );
+ pFont->m_aAliases.push_back( pAtoms->getAtom( ATOM_FAMILYNAME, aAlias, true ) );
+ }
+ }
+ aStream.ReadLine( aLine );
+ pLine = aLine.getStr();
+ nLen = aLine.getLength();
+
+ // get up to 20 token positions
+ const int nMaxTokens = 20;
+ int nTokenPos[nMaxTokens];
+ nTokenPos[0] = 0;
+ int nTokens = 1;
+ for( int i = 0; i < nLen; i++ )
+ {
+ if( pLine[i] == ';' )
+ {
+ nTokenPos[nTokens++] = i+1;
+ if( nTokens == nMaxTokens )
+ break;
+ }
+ }
+ if( nTokens < 18 )
+ {
+ delete pFont;
+ continue;
+ }
+ int nCollEntry = atoi( pLine );
+ pFont->m_nPSName = pAtoms->getAtom( ATOM_PSNAME, OUString( pLine + nTokenPos[1], nTokenPos[2]-nTokenPos[1]-1, RTL_TEXTENCODING_UTF8 ), true );
+ pFont->m_eItalic = (FontItalic)atoi( pLine+nTokenPos[2] );
+ pFont->m_eWeight = (FontWeight)atoi( pLine+nTokenPos[3] );
+ pFont->m_eWidth = (FontWidth)atoi( pLine+nTokenPos[4] );
+ pFont->m_ePitch = (FontPitch)atoi( pLine+nTokenPos[5] );
+ pFont->m_aEncoding = (rtl_TextEncoding)atoi( pLine+nTokenPos[6] );
+ pFont->m_nAscend = atoi( pLine + nTokenPos[7] );
+ pFont->m_nDescend = atoi( pLine + nTokenPos[8] );
+ pFont->m_nLeading = atoi( pLine + nTokenPos[9] );
+ pFont->m_bHaveVerticalSubstitutedGlyphs
+ = (atoi( pLine + nTokenPos[10] ) != 0);
+ pFont->m_aGlobalMetricX.width
+ = atoi( pLine + nTokenPos[11] );
+ pFont->m_aGlobalMetricX.height
+ = atoi( pLine + nTokenPos[12] );
+ pFont->m_aGlobalMetricY.width
+ = atoi( pLine + nTokenPos[13] );
+ pFont->m_aGlobalMetricY.height
+ = atoi( pLine + nTokenPos[14] );
+ pFont->m_bUserOverride
+ = (atoi( pLine + nTokenPos[15] ) != 0);
+ int nStyleTokenNr = 18;
+ switch( eType )
+ {
+ case fonttype::TrueType:
+ static_cast<PrintFontManager::TrueTypeFontFile*>(pFont)->m_nTypeFlags = atoi( pLine + nTokenPos[18] );
+ static_cast<PrintFontManager::TrueTypeFontFile*>(pFont)->m_nCollectionEntry = nCollEntry;
+ static_cast<PrintFontManager::TrueTypeFontFile*>(pFont)->m_nDirectory = nDir;
+ static_cast<PrintFontManager::TrueTypeFontFile*>(pFont)->m_aFontFile = aFile;
+ nStyleTokenNr++;
+ break;
+ case fonttype::Type1:
+ {
+ int nTokLen = (nTokens > 19 ) ? nTokenPos[19]-nTokenPos[18]-1 : nLen - nTokenPos[18];
+ static_cast<PrintFontManager::Type1FontFile*>(pFont)->m_aMetricFile = OString( pLine + nTokenPos[18], nTokLen );
+ static_cast<PrintFontManager::Type1FontFile*>(pFont)->m_nDirectory = nDir;
+ static_cast<PrintFontManager::Type1FontFile*>(pFont)->m_aFontFile = aFile;
+ nStyleTokenNr++;
+ }
+ break;
+ default: break;
+ }
+ if( nTokens > nStyleTokenNr )
+ pFont->m_aStyleName = OUString::intern( pLine + nTokenPos[nStyleTokenNr],
+ nLen - nTokenPos[nStyleTokenNr],
+ RTL_TEXTENCODING_UTF8 );
+
+ bool bObsolete = false;
+ if( bKeepOnlyUserOverridden )
+ {
+ if( pFont->m_bUserOverride )
+ {
+ OStringBuffer aFilePath(rManager.getDirectory(nDir));
+ aFilePath.append('/').append(aFile);
+ struct stat aStat;
+ if( stat( aFilePath.getStr(), &aStat ) ||
+ ! S_ISREG( aStat.st_mode ) ||
+ aStat.st_size < 16 )
+ {
+ bObsolete = true;
+ }
+ #if OSL_DEBUG_LEVEL > 2
+ else
+ fprintf( stderr, "keeping file %s in outdated cache entry due to user override\n",
+ aFilePath.getStr() );
+ #endif
+ }
+ else
+ bObsolete = true;
+ }
+ if( bObsolete )
+ {
+ m_bDoFlush = true;
+#if OSL_DEBUG_LEVEL > 2
+ fprintf( stderr, "removing obsolete font %s\n", aFile.getStr() );
+#endif
+ delete pFont;
+ continue;
+ }
+
+ FontCacheEntry& rEntry = (*pDir)[aFile].m_aEntry;
+ rEntry.push_back( pFont );
+ }
+ }
+ } while( ! aStream.IsEof() );
+}
+
+/*
+ * FontCache::copyPrintFont
+ */
+void FontCache::copyPrintFont( const PrintFontManager::PrintFont* pFrom, PrintFontManager::PrintFont* pTo )
+{
+ if( pFrom->m_eType != pTo->m_eType )
+ return;
+ switch( pFrom->m_eType )
+ {
+ case fonttype::TrueType:
+ static_cast<PrintFontManager::TrueTypeFontFile*>(pTo)->m_nDirectory = static_cast<const PrintFontManager::TrueTypeFontFile*>(pFrom)->m_nDirectory;
+ static_cast<PrintFontManager::TrueTypeFontFile*>(pTo)->m_aFontFile = static_cast<const PrintFontManager::TrueTypeFontFile*>(pFrom)->m_aFontFile;
+ static_cast<PrintFontManager::TrueTypeFontFile*>(pTo)->m_nCollectionEntry = static_cast<const PrintFontManager::TrueTypeFontFile*>(pFrom)->m_nCollectionEntry;
+ static_cast<PrintFontManager::TrueTypeFontFile*>(pTo)->m_nTypeFlags = static_cast<const PrintFontManager::TrueTypeFontFile*>(pFrom)->m_nTypeFlags;
+ break;
+ case fonttype::Type1:
+ static_cast<PrintFontManager::Type1FontFile*>(pTo)->m_nDirectory = static_cast<const PrintFontManager::Type1FontFile*>(pFrom)->m_nDirectory;
+ static_cast<PrintFontManager::Type1FontFile*>(pTo)->m_aFontFile = static_cast<const PrintFontManager::Type1FontFile*>(pFrom)->m_aFontFile;
+ static_cast<PrintFontManager::Type1FontFile*>(pTo)->m_aMetricFile = static_cast<const PrintFontManager::Type1FontFile*>(pFrom)->m_aMetricFile;
+ break;
+ default: break;
+ }
+ pTo->m_nFamilyName = pFrom->m_nFamilyName;
+ pTo->m_aStyleName = pFrom->m_aStyleName;
+ pTo->m_aAliases = pFrom->m_aAliases;
+ pTo->m_nPSName = pFrom->m_nPSName;
+ pTo->m_eItalic = pFrom->m_eItalic;
+ pTo->m_eWeight = pFrom->m_eWeight;
+ pTo->m_eWidth = pFrom->m_eWidth;
+ pTo->m_ePitch = pFrom->m_ePitch;
+ pTo->m_aEncoding = pFrom->m_aEncoding;
+ pTo->m_aGlobalMetricX = pFrom->m_aGlobalMetricX;
+ pTo->m_aGlobalMetricY = pFrom->m_aGlobalMetricY;
+ pTo->m_nAscend = pFrom->m_nAscend;
+ pTo->m_nDescend = pFrom->m_nDescend;
+ pTo->m_nLeading = pFrom->m_nLeading;
+ pTo->m_nXMin = pFrom->m_nXMin;
+ pTo->m_nYMin = pFrom->m_nYMin;
+ pTo->m_nXMax = pFrom->m_nXMax;
+ pTo->m_nYMax = pFrom->m_nYMax;
+ pTo->m_bHaveVerticalSubstitutedGlyphs = pFrom->m_bHaveVerticalSubstitutedGlyphs;
+ pTo->m_bUserOverride = pFrom->m_bUserOverride;
+}
+
+/*
+ * FontCache::equalsPrintFont
+ */
+bool FontCache::equalsPrintFont( const PrintFontManager::PrintFont* pLeft, PrintFontManager::PrintFont* pRight )
+{
+ if( pLeft->m_eType != pRight->m_eType )
+ return false;
+ switch( pLeft->m_eType )
+ {
+ case fonttype::TrueType:
+ {
+ const PrintFontManager::TrueTypeFontFile* pLT = static_cast<const PrintFontManager::TrueTypeFontFile*>(pLeft);
+ const PrintFontManager::TrueTypeFontFile* pRT = static_cast<const PrintFontManager::TrueTypeFontFile*>(pRight);
+ if( pRT->m_nDirectory != pLT->m_nDirectory ||
+ pRT->m_aFontFile != pLT->m_aFontFile ||
+ pRT->m_nCollectionEntry != pLT->m_nCollectionEntry ||
+ pRT->m_nTypeFlags != pLT->m_nTypeFlags )
+ return false;
+ }
+ break;
+ case fonttype::Type1:
+ {
+ const PrintFontManager::Type1FontFile* pLT = static_cast<const PrintFontManager::Type1FontFile*>(pLeft);
+ const PrintFontManager::Type1FontFile* pRT = static_cast<const PrintFontManager::Type1FontFile*>(pRight);
+ if( pRT->m_nDirectory != pLT->m_nDirectory ||
+ pRT->m_aFontFile != pLT->m_aFontFile ||
+ pRT->m_aMetricFile != pLT->m_aMetricFile )
+ return false;
+ }
+ break;
+ default: break;
+ }
+ if( pRight->m_nFamilyName != pLeft->m_nFamilyName ||
+ pRight->m_aStyleName != pLeft->m_aStyleName ||
+ pRight->m_nPSName != pLeft->m_nPSName ||
+ pRight->m_eItalic != pLeft->m_eItalic ||
+ pRight->m_eWeight != pLeft->m_eWeight ||
+ pRight->m_eWidth != pLeft->m_eWidth ||
+ pRight->m_ePitch != pLeft->m_ePitch ||
+ pRight->m_aEncoding != pLeft->m_aEncoding ||
+ pRight->m_aGlobalMetricX != pLeft->m_aGlobalMetricX ||
+ pRight->m_aGlobalMetricY != pLeft->m_aGlobalMetricY ||
+ pRight->m_nAscend != pLeft->m_nAscend ||
+ pRight->m_nDescend != pLeft->m_nDescend ||
+ pRight->m_nLeading != pLeft->m_nLeading ||
+ pRight->m_nXMin != pLeft->m_nXMin ||
+ pRight->m_nYMin != pLeft->m_nYMin ||
+ pRight->m_nXMax != pLeft->m_nXMax ||
+ pRight->m_nYMax != pLeft->m_nYMax ||
+ pRight->m_bHaveVerticalSubstitutedGlyphs != pLeft->m_bHaveVerticalSubstitutedGlyphs ||
+ pRight->m_bUserOverride != pLeft->m_bUserOverride
+ )
+ return false;
+ std::list< int >::const_iterator lit, rit;
+ for( lit = pLeft->m_aAliases.begin(), rit = pRight->m_aAliases.begin();
+ lit != pLeft->m_aAliases.end() && rit != pRight->m_aAliases.end() && (*lit) == (*rit);
+ ++lit, ++rit )
+ ;
+ return lit == pLeft->m_aAliases.end() && rit == pRight->m_aAliases.end();
+}
+
+/*
+ * FontCache::clonePrintFont
+ */
+PrintFontManager::PrintFont* FontCache::clonePrintFont( const PrintFontManager::PrintFont* pOldFont )
+{
+ PrintFontManager::PrintFont* pFont = nullptr;
+ switch( pOldFont->m_eType )
+ {
+ case fonttype::TrueType:
+ pFont = new PrintFontManager::TrueTypeFontFile();
+ break;
+ case fonttype::Type1:
+ pFont = new PrintFontManager::Type1FontFile();
+ break;
+ default: break;
+ }
+ if( pFont )
+ {
+ copyPrintFont( pOldFont, pFont );
+ }
+ return pFont;
+ }
+
+/*
+ * FontCache::getFontCacheFile
+ */
+bool FontCache::getFontCacheFile( int nDirID, const OString& rFile, list< PrintFontManager::PrintFont* >& rNewFonts ) const
+{
+ bool bSuccess = false;
+
+ FontCacheData::const_iterator dir = m_aCache.find( nDirID );
+ if( dir != m_aCache.end() )
+ {
+ FontDirMap::const_iterator entry = dir->second.m_aEntries.find( rFile );
+ if( entry != dir->second.m_aEntries.end() )
+ {
+ for( FontCacheEntry::const_iterator font = entry->second.m_aEntry.begin(); font != entry->second.m_aEntry.end(); ++font )
+ {
+ bSuccess = true;
+ PrintFontManager::PrintFont* pFont = clonePrintFont( *font );
+ rNewFonts.push_back( pFont );
+ }
+ }
+ }
+ return bSuccess;
+}
+
+/*
+ * FontCache::updateFontCacheEntry
+ */
+void FontCache::updateFontCacheEntry( const PrintFontManager::PrintFont* pFont, bool bFlush )
+{
+ OString aFile;
+ int nDirID = 0;
+ switch( pFont->m_eType )
+ {
+ case fonttype::TrueType:
+ nDirID = static_cast<const PrintFontManager::TrueTypeFontFile*>(pFont)->m_nDirectory;
+ aFile = static_cast<const PrintFontManager::TrueTypeFontFile*>(pFont)->m_aFontFile;
+ break;
+ case fonttype::Type1:
+ nDirID = static_cast<const PrintFontManager::Type1FontFile*>(pFont)->m_nDirectory;
+ aFile = static_cast<const PrintFontManager::Type1FontFile*>(pFont)->m_aFontFile;
+ break;
+ default:
+ return;
+ }
+ FontCacheData::const_iterator dir = m_aCache.find( nDirID );
+ FontDirMap::const_iterator entry;
+ FontCacheEntry::const_iterator font;
+ PrintFontManager::PrintFont* pCacheFont = nullptr;
+
+ if( dir != m_aCache.end() )
+ {
+ entry = dir->second.m_aEntries.find( aFile );
+ if( entry != dir->second.m_aEntries.end() )
+ {
+ for( font = entry->second.m_aEntry.begin(); font != entry->second.m_aEntry.end(); ++font )
+ {
+ if( (*font)->m_eType == pFont->m_eType &&
+ ( (*font)->m_eType != fonttype::TrueType ||
+ static_cast<const PrintFontManager::TrueTypeFontFile*>(*font)->m_nCollectionEntry == static_cast<const PrintFontManager::TrueTypeFontFile*>(pFont)->m_nCollectionEntry
+ ) )
+ break;
+ }
+ if( font != entry->second.m_aEntry.end() )
+ pCacheFont = *font;
+ }
+ }
+ else
+ createCacheDir( nDirID );
+
+ if( pCacheFont )
+ {
+ if( ! equalsPrintFont( pFont, pCacheFont ) )
+ {
+ copyPrintFont( pFont, pCacheFont );
+ m_bDoFlush = true;
+ }
+ }
+ else
+ {
+ pCacheFont = clonePrintFont( pFont );
+ m_aCache[nDirID].m_aEntries[aFile].m_aEntry.push_back( pCacheFont );
+ m_bDoFlush = true;
+ }
+ if( bFlush )
+ flush();
+}
+
+/*
+ * FontCache::listDirectory
+ */
+bool FontCache::listDirectory( const OString& rDir, std::list< PrintFontManager::PrintFont* >& rNewFonts ) const
+{
+ PrintFontManager& rManager( PrintFontManager::get() );
+ int nDirID = rManager.getDirectoryAtom( rDir );
+
+ FontCacheData::const_iterator dir = m_aCache.find( nDirID );
+ bool bFound = (dir != m_aCache.end());
+
+ if( bFound && !dir->second.m_bNoFiles )
+ {
+ for( FontDirMap::const_iterator file = dir->second.m_aEntries.begin(); file != dir->second.m_aEntries.end(); ++file )
+ {
+ for( FontCacheEntry::const_iterator font = file->second.m_aEntry.begin(); font != file->second.m_aEntry.end(); ++font )
+ {
+ PrintFontManager::PrintFont* pFont = clonePrintFont( *font );
+ rNewFonts.push_back( pFont );
+ }
+ }
+ }
+ return bFound;
+}
+
+/*
+ * FontCache::listDirectory
+ */
+bool FontCache::scanAdditionalFiles( const OString& rDir )
+{
+ PrintFontManager& rManager( PrintFontManager::get() );
+ int nDirID = rManager.getDirectoryAtom( rDir );
+ FontCacheData::const_iterator dir = m_aCache.find( nDirID );
+ bool bFound = (dir != m_aCache.end());
+
+ return (bFound && dir->second.m_bUserOverrideOnly);
+}
+
+/*
+ * FontCache::createCacheDir
+ */
+void FontCache::createCacheDir( int nDirID )
+{
+ PrintFontManager& rManager( PrintFontManager::get() );
+
+ const OString& rDir = rManager.getDirectory( nDirID );
+ struct stat aStat;
+ if( ! stat( rDir.getStr(), &aStat ) )
+ m_aCache[nDirID].m_nTimestamp = (sal_Int64)aStat.st_mtime;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/vcl/unx/generic/fontmanager/fontconfig.cxx b/vcl/unx/generic/fontmanager/fontconfig.cxx
new file mode 100644
index 000000000000..531e62243f78
--- /dev/null
+++ b/vcl/unx/generic/fontmanager/fontconfig.cxx
@@ -0,0 +1,1293 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include "fontcache.hxx"
+#include "impfont.hxx"
+#include "fontmanager.hxx"
+#include <vcl/svapp.hxx>
+#include <vcl/sysdata.hxx>
+#include <vcl/vclenum.hxx>
+#include <vcl/wrkwin.hxx>
+#include "fontinstance.hxx"
+#include <i18nlangtag/languagetag.hxx>
+#include <i18nutil/unicode.hxx>
+#include <rtl/strbuf.hxx>
+#include <unicode/uchar.h>
+#include <unicode/uscript.h>
+
+using namespace psp;
+
+#include <fontconfig/fontconfig.h>
+#include <ft2build.h>
+#include <fontconfig/fcfreetype.h>
+
+#if defined(ENABLE_DBUS) && defined(ENABLE_PACKAGEKIT)
+#include <dbus/dbus-glib.h>
+#endif
+
+#include <cstdio>
+#include <cstdarg>
+
+#include <unotools/atom.hxx>
+#include <unotools/configmgr.hxx>
+
+#include "osl/module.h"
+#include "osl/thread.h"
+#include "osl/process.h"
+
+#include "rtl/ustrbuf.hxx"
+
+#include <utility>
+#include <algorithm>
+
+using namespace osl;
+
+namespace
+{
+ typedef std::pair<FcChar8*, FcChar8*> lang_and_element;
+}
+
+class FontCfgWrapper
+{
+ FcFontSet* m_pOutlineSet;
+
+ void addFontSet( FcSetName );
+
+ FontCfgWrapper();
+ ~FontCfgWrapper();
+
+public:
+ static FontCfgWrapper& get();
+ static void release();
+
+ FcFontSet* getFontSet();
+
+ void clear();
+
+public:
+ FcResult LocalizedElementFromPattern(FcPattern* pPattern, FcChar8 **family,
+ const char *elementtype, const char *elementlangtype);
+//to-do, make private and add some cleaner accessor methods
+ std::unordered_map< OString, OString, OStringHash > m_aFontNameToLocalized;
+ std::unordered_map< OString, OString, OStringHash > m_aLocalizedToCanonical;
+private:
+ void cacheLocalizedFontNames(const FcChar8 *origfontname, const FcChar8 *bestfontname, const std::vector< lang_and_element > &lang_and_elements);
+
+ LanguageTag* m_pLanguageTag;
+};
+
+FontCfgWrapper::FontCfgWrapper()
+ :
+ m_pOutlineSet( nullptr ),
+ m_pLanguageTag( nullptr )
+{
+ FcInit();
+}
+
+void FontCfgWrapper::addFontSet( FcSetName eSetName )
+{
+ /*
+ add only acceptable outlined fonts to our config,
+ for future fontconfig use
+ */
+ FcFontSet* pOrig = FcConfigGetFonts( FcConfigGetCurrent(), eSetName );
+ if( !pOrig )
+ return;
+
+ // filter the font sets to remove obsolete faces
+ for( int i = 0; i < pOrig->nfont; ++i )
+ {
+ FcPattern* pPattern = pOrig->fonts[i];
+ // #i115131# ignore non-outline fonts
+ FcBool bOutline = FcFalse;
+ FcResult eOutRes = FcPatternGetBool( pPattern, FC_OUTLINE, 0, &bOutline );
+ if( (eOutRes != FcResultMatch) || (bOutline == FcFalse) )
+ continue;
+ FcPatternReference( pPattern );
+ FcFontSetAdd( m_pOutlineSet, pPattern );
+ }
+
+ // TODO?: FcFontSetDestroy( pOrig );
+}
+
+namespace
+{
+ int compareFontNames(const FcPattern *a, const FcPattern *b)
+ {
+ FcChar8 *pNameA=nullptr, *pNameB=nullptr;
+
+ bool bHaveA = FcPatternGetString(a, FC_FAMILY, 0, &pNameA) == FcResultMatch;
+ bool bHaveB = FcPatternGetString(b, FC_FAMILY, 0, &pNameB) == FcResultMatch;
+
+ if (bHaveA && bHaveB)
+ return strcmp(reinterpret_cast<const char*>(pNameA), reinterpret_cast<const char*>(pNameB));
+
+ return int(bHaveA) - int(bHaveB);
+ }
+
+ //Sort fonts so that fonts with the same family name are side-by-side, with
+ //those with higher version numbers first
+ class SortFont : public ::std::binary_function< const FcPattern*, const FcPattern*, bool >
+ {
+ public:
+ bool operator()(const FcPattern *a, const FcPattern *b)
+ {
+ int comp = compareFontNames(a, b);
+ if (comp != 0)
+ return comp < 0;
+
+ int nVersionA=0, nVersionB=0;
+
+ bool bHaveA = FcPatternGetInteger(a, FC_FONTVERSION, 0, &nVersionA) == FcResultMatch;
+ bool bHaveB = FcPatternGetInteger(b, FC_FONTVERSION, 0, &nVersionB) == FcResultMatch;
+
+ if (bHaveA && bHaveB)
+ return nVersionA > nVersionB;
+
+ return bHaveA > bHaveB;
+ }
+ };
+
+ //See fdo#30729 for where an old opensymbol installed system-wide can
+ //clobber the new opensymbol installed locally
+
+ //See if this font is a duplicate with equal attributes which has already been
+ //inserted, or if it an older version of an inserted fonts. Depends on FcFontSet
+ //on being sorted with SortFont
+ bool isPreviouslyDuplicateOrObsoleted(FcFontSet *pFSet, int i)
+ {
+ const FcPattern *a = pFSet->fonts[i];
+
+ FcPattern* pTestPatternA = FcPatternDuplicate(a);
+ FcPatternDel(pTestPatternA, FC_FILE);
+ FcPatternDel(pTestPatternA, FC_CHARSET);
+ FcPatternDel(pTestPatternA, FC_CAPABILITY);
+ FcPatternDel(pTestPatternA, FC_FONTVERSION);
+ FcPatternDel(pTestPatternA, FC_LANG);
+
+ bool bIsDup(false);
+
+ // fdo#66715: loop for case of several font files for same font
+ for (int j = i - 1; 0 <= j && !bIsDup; --j)
+ {
+ const FcPattern *b = pFSet->fonts[j];
+
+ if (compareFontNames(a, b) != 0)
+ break;
+
+ FcPattern* pTestPatternB = FcPatternDuplicate(b);
+ FcPatternDel(pTestPatternB, FC_FILE);
+ FcPatternDel(pTestPatternB, FC_CHARSET);
+ FcPatternDel(pTestPatternB, FC_CAPABILITY);
+ FcPatternDel(pTestPatternB, FC_FONTVERSION);
+ FcPatternDel(pTestPatternB, FC_LANG);
+
+ bIsDup = FcPatternEqual(pTestPatternA, pTestPatternB);
+
+ FcPatternDestroy(pTestPatternB);
+ }
+
+ FcPatternDestroy(pTestPatternA);
+
+ return bIsDup;
+ }
+}
+
+FcFontSet* FontCfgWrapper::getFontSet()
+{
+ if( !m_pOutlineSet )
+ {
+ m_pOutlineSet = FcFontSetCreate();
+ addFontSet( FcSetSystem );
+ if( FcGetVersion() > 20400 ) // #i85462# prevent crashes
+ addFontSet( FcSetApplication );
+
+ ::std::sort(m_pOutlineSet->fonts,m_pOutlineSet->fonts+m_pOutlineSet->nfont,SortFont());
+ }
+
+ return m_pOutlineSet;
+}
+
+FontCfgWrapper::~FontCfgWrapper()
+{
+ clear();
+ //To-Do: get gtk vclplug smoketest to pass
+ //FcFini();
+}
+
+static FontCfgWrapper* pOneInstance = nullptr;
+
+FontCfgWrapper& FontCfgWrapper::get()
+{
+ if( ! pOneInstance )
+ pOneInstance = new FontCfgWrapper();
+ return *pOneInstance;
+}
+
+void FontCfgWrapper::release()
+{
+ if( pOneInstance )
+ {
+ delete pOneInstance;
+ pOneInstance = nullptr;
+ }
+}
+
+namespace
+{
+ static FcChar8* bestname(const std::vector<lang_and_element> &elements, const LanguageTag & rLangTag);
+
+ FcChar8* bestname(const std::vector<lang_and_element> &elements, const LanguageTag & rLangTag)
+ {
+ FcChar8* candidate = elements.begin()->second;
+ /* FIXME-BCP47: once fontconfig supports language tags this
+ * language-territory stuff needs to be changed! */
+ SAL_INFO_IF( !rLangTag.isIsoLocale(), "i18n", "localizedsorter::bestname - not an ISO locale");
+ OString sLangMatch(OUStringToOString(rLangTag.getLanguage().toAsciiLowerCase(), RTL_TEXTENCODING_UTF8));
+ OString sFullMatch = sLangMatch;
+ sFullMatch += OString('-');
+ sFullMatch += OUStringToOString(rLangTag.getCountry().toAsciiLowerCase(), RTL_TEXTENCODING_UTF8);
+
+ std::vector<lang_and_element>::const_iterator aEnd = elements.end();
+ bool alreadyclosematch = false;
+ bool found_fallback_englishname = false;
+ for( std::vector<lang_and_element>::const_iterator aIter = elements.begin(); aIter != aEnd; ++aIter )
+ {
+ const char *pLang = reinterpret_cast<const char*>(aIter->first);
+ if( rtl_str_compare( pLang, sFullMatch.getStr() ) == 0)
+ {
+ // both language and country match
+ candidate = aIter->second;
+ break;
+ }
+ else if( alreadyclosematch )
+ {
+ // current candidate matches lang of lang-TERRITORY
+ // override candidate only if there is a full match
+ continue;
+ }
+ else if( rtl_str_compare( pLang, sLangMatch.getStr()) == 0)
+ {
+ // just the language matches
+ candidate = aIter->second;
+ alreadyclosematch = true;
+ }
+ else if( found_fallback_englishname )
+ {
+ // already found an english fallback, don't override candidate
+ // unless there is a better language match
+ continue;
+ }
+ else if( rtl_str_compare( pLang, "en") == 0)
+ {
+ // select a fallback candidate of the first english element
+ // name
+ candidate = aIter->second;
+ found_fallback_englishname = true;
+ }
+ }
+ return candidate;
+ }
+}
+
+//Set up maps to quickly map between a fonts best UI name and all the rest of its names, and vice versa
+void FontCfgWrapper::cacheLocalizedFontNames(const FcChar8 *origfontname, const FcChar8 *bestfontname,
+ const std::vector< lang_and_element > &lang_and_elements)
+{
+ std::vector<lang_and_element>::const_iterator aEnd = lang_and_elements.end();
+ for (std::vector<lang_and_element>::const_iterator aIter = lang_and_elements.begin(); aIter != aEnd; ++aIter)
+ {
+ const char *candidate = reinterpret_cast<const char*>(aIter->second);
+ if (rtl_str_compare(candidate, reinterpret_cast<const char*>(bestfontname)) != 0)
+ m_aFontNameToLocalized[OString(candidate)] = OString(reinterpret_cast<const char*>(bestfontname));
+ }
+ if (rtl_str_compare(reinterpret_cast<const char*>(origfontname), reinterpret_cast<const char*>(bestfontname)) != 0)
+ m_aLocalizedToCanonical[OString(reinterpret_cast<const char*>(bestfontname))] = OString(reinterpret_cast<const char*>(origfontname));
+}
+
+FcResult FontCfgWrapper::LocalizedElementFromPattern(FcPattern* pPattern, FcChar8 **element,
+ const char *elementtype, const char *elementlangtype)
+{ /* e. g.: ^ FC_FAMILY ^ FC_FAMILYLANG */
+ FcChar8 *origelement;
+ FcResult eElementRes = FcPatternGetString( pPattern, elementtype, 0, &origelement );
+ *element = origelement;
+
+ if( eElementRes == FcResultMatch)
+ {
+ FcChar8* elementlang = nullptr;
+ if (FcPatternGetString( pPattern, elementlangtype, 0, &elementlang ) == FcResultMatch)
+ {
+ std::vector< lang_and_element > lang_and_elements;
+ lang_and_elements.push_back(lang_and_element(elementlang, *element));
+ int k = 1;
+ while (true)
+ {
+ if (FcPatternGetString( pPattern, elementlangtype, k, &elementlang ) != FcResultMatch)
+ break;
+ if (FcPatternGetString( pPattern, elementtype, k, element ) != FcResultMatch)
+ break;
+ lang_and_elements.push_back(lang_and_element(elementlang, *element));
+ ++k;
+ }
+
+ //possible to-do, sort by UILocale instead of process locale
+ if (!m_pLanguageTag)
+ {
+ rtl_Locale* pLoc = nullptr;
+ osl_getProcessLocale(&pLoc);
+ m_pLanguageTag = new LanguageTag(*pLoc);
+ }
+ *element = bestname(lang_and_elements, *m_pLanguageTag);
+
+ //if this element is a fontname, map the other names to this best-name
+ if (rtl_str_compare(elementtype, FC_FAMILY) == 0)
+ cacheLocalizedFontNames(origelement, *element, lang_and_elements);
+ }
+ }
+
+ return eElementRes;
+}
+
+void FontCfgWrapper::clear()
+{
+ m_aFontNameToLocalized.clear();
+ m_aLocalizedToCanonical.clear();
+ if( m_pOutlineSet )
+ {
+ FcFontSetDestroy( m_pOutlineSet );
+ m_pOutlineSet = nullptr;
+ }
+ delete m_pLanguageTag;
+ m_pLanguageTag = nullptr;
+}
+
+/*
+ * PrintFontManager::initFontconfig
+ */
+void PrintFontManager::initFontconfig()
+{
+ FontCfgWrapper& rWrapper = FontCfgWrapper::get();
+ rWrapper.clear();
+}
+
+namespace
+{
+ FontWeight convertWeight(int weight)
+ {
+ // set weight
+ if( weight <= FC_WEIGHT_THIN )
+ return WEIGHT_THIN;
+ else if( weight <= FC_WEIGHT_ULTRALIGHT )
+ return WEIGHT_ULTRALIGHT;
+ else if( weight <= FC_WEIGHT_LIGHT )
+ return WEIGHT_LIGHT;
+ else if( weight <= FC_WEIGHT_BOOK )
+ return WEIGHT_SEMILIGHT;
+ else if( weight <= FC_WEIGHT_NORMAL )
+ return WEIGHT_NORMAL;
+ else if( weight <= FC_WEIGHT_MEDIUM )
+ return WEIGHT_MEDIUM;
+ else if( weight <= FC_WEIGHT_SEMIBOLD )
+ return WEIGHT_SEMIBOLD;
+ else if( weight <= FC_WEIGHT_BOLD )
+ return WEIGHT_BOLD;
+ else if( weight <= FC_WEIGHT_ULTRABOLD )
+ return WEIGHT_ULTRABOLD;
+ return WEIGHT_BLACK;
+ }
+
+ FontItalic convertSlant(int slant)
+ {
+ // set italic
+ if( slant == FC_SLANT_ITALIC )
+ return ITALIC_NORMAL;
+ else if( slant == FC_SLANT_OBLIQUE )
+ return ITALIC_OBLIQUE;
+ return ITALIC_NONE;
+ }
+
+ FontPitch convertSpacing(int spacing)
+ {
+ // set pitch
+ if( spacing == FC_MONO || spacing == FC_CHARCELL )
+ return PITCH_FIXED;
+ return PITCH_VARIABLE;
+ }
+
+ // translation: fontconfig enum -> vcl enum
+ FontWidth convertWidth(int width)
+ {
+ if (width == FC_WIDTH_ULTRACONDENSED)
+ return WIDTH_ULTRA_CONDENSED;
+ else if (width == FC_WIDTH_EXTRACONDENSED)
+ return WIDTH_EXTRA_CONDENSED;
+ else if (width == FC_WIDTH_CONDENSED)
+ return WIDTH_CONDENSED;
+ else if (width == FC_WIDTH_SEMICONDENSED)
+ return WIDTH_SEMI_CONDENSED;
+ else if (width == FC_WIDTH_SEMIEXPANDED)
+ return WIDTH_SEMI_EXPANDED;
+ else if (width == FC_WIDTH_EXPANDED)
+ return WIDTH_EXPANDED;
+ else if (width == FC_WIDTH_EXTRAEXPANDED)
+ return WIDTH_EXTRA_EXPANDED;
+ else if (width == FC_WIDTH_ULTRAEXPANDED)
+ return WIDTH_ULTRA_EXPANDED;
+ return WIDTH_NORMAL;
+ }
+}
+
+//FontConfig doesn't come with a way to remove an element from a FontSet as far
+//as I can see
+static void lcl_FcFontSetRemove(FcFontSet* pFSet, int i)
+{
+ FcPatternDestroy(pFSet->fonts[i]);
+
+ int nTail = pFSet->nfont - (i + 1);
+ --pFSet->nfont;
+ if (!nTail)
+ return;
+ memmove(pFSet->fonts + i, pFSet->fonts + i + 1, nTail*sizeof(FcPattern*));
+}
+
+void PrintFontManager::countFontconfigFonts( std::unordered_map<OString, int, OStringHash>& o_rVisitedPaths )
+{
+#if OSL_DEBUG_LEVEL > 1
+ int nFonts = 0;
+#endif
+ FontCfgWrapper& rWrapper = FontCfgWrapper::get();
+
+ FcFontSet* pFSet = rWrapper.getFontSet();
+ const bool bMinimalFontset = utl::ConfigManager::IsAvoidConfig();
+ if( pFSet )
+ {
+#if OSL_DEBUG_LEVEL > 1
+ fprintf( stderr, "found %d entries in fontconfig fontset\n", pFSet->nfont );
+#endif
+ for( int i = 0; i < pFSet->nfont; i++ )
+ {
+ FcChar8* file = nullptr;
+ FcChar8* family = nullptr;
+ FcChar8* style = nullptr;
+ FcChar8* format = nullptr;
+ int slant = 0;
+ int weight = 0;
+ int width = 0;
+ int spacing = 0;
+ int nCollectionEntry = -1;
+ FcBool outline = false;
+
+ FcResult eFileRes = FcPatternGetString(pFSet->fonts[i], FC_FILE, 0, &file);
+ FcResult eFamilyRes = rWrapper.LocalizedElementFromPattern( pFSet->fonts[i], &family, FC_FAMILY, FC_FAMILYLANG );
+ if (bMinimalFontset && strncmp(reinterpret_cast<char*>(family), "Liberation", strlen("Liberation")))
+ continue;
+ FcResult eStyleRes = rWrapper.LocalizedElementFromPattern( pFSet->fonts[i], &style, FC_STYLE, FC_STYLELANG );
+ FcResult eSlantRes = FcPatternGetInteger(pFSet->fonts[i], FC_SLANT, 0, &slant);
+ FcResult eWeightRes = FcPatternGetInteger(pFSet->fonts[i], FC_WEIGHT, 0, &weight);
+ FcResult eWidthRes = FcPatternGetInteger(pFSet->fonts[i], FC_WIDTH, 0, &width);
+ FcResult eSpacRes = FcPatternGetInteger(pFSet->fonts[i], FC_SPACING, 0, &spacing);
+ FcResult eOutRes = FcPatternGetBool(pFSet->fonts[i], FC_OUTLINE, 0, &outline);
+ FcResult eIndexRes = FcPatternGetInteger(pFSet->fonts[i], FC_INDEX, 0, &nCollectionEntry);
+ FcResult eFormatRes = FcPatternGetString(pFSet->fonts[i], FC_FONTFORMAT, 0, &format);
+
+ if( eFileRes != FcResultMatch || eFamilyRes != FcResultMatch || eOutRes != FcResultMatch )
+ continue;
+
+#if (OSL_DEBUG_LEVEL > 2)
+ fprintf( stderr, "found font \"%s\" in file %s\n"
+ " weight = %d, slant = %d, style = \"%s\"\n"
+ " width = %d, spacing = %d, outline = %d, format %s\n"
+ , family, file
+ , eWeightRes == FcResultMatch ? weight : -1
+ , eSpacRes == FcResultMatch ? slant : -1
+ , eStyleRes == FcResultMatch ? (const char*) style : "<nil>"
+ , eWeightRes == FcResultMatch ? width : -1
+ , eSpacRes == FcResultMatch ? spacing : -1
+ , eOutRes == FcResultMatch ? outline : -1
+ , eFormatRes == FcResultMatch ? (const char*)format : "<unknown>"
+ );
+#endif
+
+// OSL_ASSERT(eOutRes != FcResultMatch || outline);
+
+ // only outline fonts are usable to psprint anyway
+ if( eOutRes == FcResultMatch && ! outline )
+ continue;
+
+ if (isPreviouslyDuplicateOrObsoleted(pFSet, i))
+ {
+#if OSL_DEBUG_LEVEL > 2
+ fprintf(stderr, "Ditching %s as duplicate/obsolete\n", file);
+#endif
+ continue;
+ }
+
+ // see if this font is already cached
+ // update attributes
+ std::list< PrintFont* > aFonts;
+ OString aDir, aBase, aOrgPath( reinterpret_cast<char*>(file) );
+ splitPath( aOrgPath, aDir, aBase );
+
+ o_rVisitedPaths[aDir] = 1;
+
+ int nDirID = getDirectoryAtom( aDir, true );
+ if( ! m_pFontCache->getFontCacheFile( nDirID, aBase, aFonts ) )
+ {
+#if OSL_DEBUG_LEVEL > 2
+ fprintf( stderr, "file %s not cached\n", aBase.getStr() );
+#endif
+ // not known, analyze font file to get attributes
+ // not described by fontconfig (e.g. alias names, PSName)
+ if (eFormatRes != FcResultMatch)
+ format = nullptr;
+ analyzeFontFile( nDirID, aBase, aFonts, reinterpret_cast<char*>(format) );
+#if OSL_DEBUG_LEVEL > 1
+ if( aFonts.empty() )
+ fprintf( stderr, "Warning: file \"%s\" is unusable to psprint\n", aOrgPath.getStr() );
+#endif
+ }
+ if( aFonts.empty() )
+ {
+ //remove font, reuse index
+ //we want to remove unusable fonts here, in case there is a usable font
+ //which duplicates the properties of the unusable one
+
+ //not removing the unusable font will risk the usable font being rejected
+ //as a duplicate by isPreviouslyDuplicateOrObsoleted
+ lcl_FcFontSetRemove(pFSet, i--);
+ continue;
+ }
+
+ int nFamilyName = m_pAtoms->getAtom( ATOM_FAMILYNAME, OStringToOUString( OString( reinterpret_cast<char*>(family) ), RTL_TEXTENCODING_UTF8 ), true );
+ PrintFont* pUpdate = aFonts.front();
+ std::list<PrintFont*>::const_iterator second_font = aFonts.begin();
+ ++second_font;
+ if( second_font != aFonts.end() ) // more than one font
+ {
+ // a collection entry, get the correct index
+ if( eIndexRes == FcResultMatch && nCollectionEntry != -1 )
+ {
+ for( std::list< PrintFont* >::iterator it = aFonts.begin(); it != aFonts.end(); ++it )
+ {
+ if( (*it)->m_eType == fonttype::TrueType &&
+ static_cast<TrueTypeFontFile*>(*it)->m_nCollectionEntry == nCollectionEntry )
+ {
+ pUpdate = *it;
+ break;
+ }
+ }
+ // update collection entry
+ // additional entries will be created in the cache
+ // if this is a new index (that is if the loop above
+ // ran to the end of the list)
+ if( pUpdate->m_eType == fonttype::TrueType ) // sanity check, this should always be the case here
+ static_cast<TrueTypeFontFile*>(pUpdate)->m_nCollectionEntry = nCollectionEntry;
+ }
+ else
+ {
+#if OSL_DEBUG_LEVEL > 1
+ fprintf( stderr, "multiple fonts for file, but no index in fontconfig pattern ! (index res = %d collection entry = %d\nfile will not be used\n", eIndexRes, nCollectionEntry );
+#endif
+ // we have found more than one font in this file
+ // but fontconfig will not tell us which index is meant
+ // -> something is in disorder, do not use this font
+ pUpdate = nullptr;
+ }
+ }
+
+ if( pUpdate )
+ {
+ // set family name
+ if( pUpdate->m_nFamilyName != nFamilyName )
+ {
+ }
+ if( eWeightRes == FcResultMatch )
+ pUpdate->m_eWeight = convertWeight(weight);
+ if( eWidthRes == FcResultMatch )
+ pUpdate->m_eWidth = convertWidth(width);
+ if( eSpacRes == FcResultMatch )
+ pUpdate->m_ePitch = convertSpacing(spacing);
+ if( eSlantRes == FcResultMatch )
+ pUpdate->m_eItalic = convertSlant(slant);
+ if( eStyleRes == FcResultMatch )
+ {
+ pUpdate->m_aStyleName = OStringToOUString( OString( reinterpret_cast<char*>(style) ), RTL_TEXTENCODING_UTF8 );
+ }
+
+ // update font cache
+ m_pFontCache->updateFontCacheEntry( pUpdate, false );
+ // sort into known fonts
+ fontID aFont = m_nNextFontID++;
+ m_aFonts[ aFont ] = pUpdate;
+ m_aFontFileToFontID[ aBase ].insert( aFont );
+#if OSL_DEBUG_LEVEL > 1
+ nFonts++;
+#endif
+#if OSL_DEBUG_LEVEL > 2
+ fprintf( stderr, "inserted font %s as fontID %d\n", family, aFont );
+#endif
+ }
+ // clean up the fonts we did not put into the list
+ for( std::list< PrintFont* >::iterator it = aFonts.begin(); it != aFonts.end(); ++it )
+ {
+ if( *it != pUpdate )
+ {
+ m_pFontCache->updateFontCacheEntry( *it, false ); // prepare a cache entry for a collection item
+ delete *it;
+ }
+ }
+ }
+ }
+
+ // how does one get rid of the config ?
+#if OSL_DEBUG_LEVEL > 1
+ fprintf( stderr, "inserted %d fonts from fontconfig\n", nFonts );
+#endif
+}
+
+void PrintFontManager::deinitFontconfig()
+{
+ FontCfgWrapper::release();
+}
+
+bool PrintFontManager::addFontconfigDir( const OString& rDirName )
+{
+ // workaround for a stability problems in older FC versions
+ // when handling application specific fonts
+ const int nVersion = FcGetVersion();
+ if( nVersion <= 20400 )
+ return false;
+ const char* pDirName = rDirName.getStr();
+ bool bDirOk = (FcConfigAppFontAddDir(FcConfigGetCurrent(), reinterpret_cast<FcChar8 const *>(pDirName) ) == FcTrue);
+
+#if OSL_DEBUG_LEVEL > 1
+ fprintf( stderr, "FcConfigAppFontAddDir( \"%s\") => %d\n", pDirName, bDirOk );
+#endif
+
+ if( !bDirOk )
+ return false;
+
+ // load dir-specific fc-config file too if available
+ const OString aConfFileName = rDirName + "/fc_local.conf";
+ FILE* pCfgFile = fopen( aConfFileName.getStr(), "rb" );
+ if( pCfgFile )
+ {
+ fclose( pCfgFile);
+ bool bCfgOk = FcConfigParseAndLoad(FcConfigGetCurrent(),
+ reinterpret_cast<FcChar8 const *>(aConfFileName.getStr()), FcTrue);
+ if( !bCfgOk )
+ fprintf( stderr, "FcConfigParseAndLoad( \"%s\") => %d\n", aConfFileName.getStr(), bCfgOk );
+ }
+
+ return true;
+}
+
+static void addtopattern(FcPattern *pPattern,
+ FontItalic eItalic, FontWeight eWeight, FontWidth eWidth, FontPitch ePitch)
+{
+ if( eItalic != ITALIC_DONTKNOW )
+ {
+ int nSlant = FC_SLANT_ROMAN;
+ switch( eItalic )
+ {
+ case ITALIC_NORMAL:
+ nSlant = FC_SLANT_ITALIC;
+ break;
+ case ITALIC_OBLIQUE:
+ nSlant = FC_SLANT_OBLIQUE;
+ break;
+ default:
+ break;
+ }
+ FcPatternAddInteger(pPattern, FC_SLANT, nSlant);
+ }
+ if( eWeight != WEIGHT_DONTKNOW )
+ {
+ int nWeight = FC_WEIGHT_NORMAL;
+ switch( eWeight )
+ {
+ case WEIGHT_THIN: nWeight = FC_WEIGHT_THIN;break;
+ case WEIGHT_ULTRALIGHT: nWeight = FC_WEIGHT_ULTRALIGHT;break;
+ case WEIGHT_LIGHT: nWeight = FC_WEIGHT_LIGHT;break;
+ case WEIGHT_SEMILIGHT: nWeight = FC_WEIGHT_BOOK;break;
+ case WEIGHT_NORMAL: nWeight = FC_WEIGHT_NORMAL;break;
+ case WEIGHT_MEDIUM: nWeight = FC_WEIGHT_MEDIUM;break;
+ case WEIGHT_SEMIBOLD: nWeight = FC_WEIGHT_SEMIBOLD;break;
+ case WEIGHT_BOLD: nWeight = FC_WEIGHT_BOLD;break;
+ case WEIGHT_ULTRABOLD: nWeight = FC_WEIGHT_ULTRABOLD;break;
+ case WEIGHT_BLACK: nWeight = FC_WEIGHT_BLACK;break;
+ default:
+ break;
+ }
+ FcPatternAddInteger(pPattern, FC_WEIGHT, nWeight);
+ }
+ if( eWidth != WIDTH_DONTKNOW )
+ {
+ int nWidth = FC_WIDTH_NORMAL;
+ switch( eWidth )
+ {
+ case WIDTH_ULTRA_CONDENSED: nWidth = FC_WIDTH_ULTRACONDENSED;break;
+ case WIDTH_EXTRA_CONDENSED: nWidth = FC_WIDTH_EXTRACONDENSED;break;
+ case WIDTH_CONDENSED: nWidth = FC_WIDTH_CONDENSED;break;
+ case WIDTH_SEMI_CONDENSED: nWidth = FC_WIDTH_SEMICONDENSED;break;
+ case WIDTH_NORMAL: nWidth = FC_WIDTH_NORMAL;break;
+ case WIDTH_SEMI_EXPANDED: nWidth = FC_WIDTH_SEMIEXPANDED;break;
+ case WIDTH_EXPANDED: nWidth = FC_WIDTH_EXPANDED;break;
+ case WIDTH_EXTRA_EXPANDED: nWidth = FC_WIDTH_EXTRAEXPANDED;break;
+ case WIDTH_ULTRA_EXPANDED: nWidth = FC_WIDTH_ULTRAEXPANDED;break;
+ default:
+ break;
+ }
+ FcPatternAddInteger(pPattern, FC_WIDTH, nWidth);
+ }
+ if( ePitch != PITCH_DONTKNOW )
+ {
+ int nSpacing = FC_PROPORTIONAL;
+ switch( ePitch )
+ {
+ case PITCH_FIXED: nSpacing = FC_MONO;break;
+ case PITCH_VARIABLE: nSpacing = FC_PROPORTIONAL;break;
+ default:
+ break;
+ }
+ FcPatternAddInteger(pPattern, FC_SPACING, nSpacing);
+ if (nSpacing == FC_MONO)
+ FcPatternAddString(pPattern, FC_FAMILY, reinterpret_cast<FcChar8 const *>("monospace"));
+ }
+}
+
+namespace
+{
+ //Someday fontconfig will hopefully use bcp47, see fdo#19869
+ //In the meantime try something that will fit to workaround fdo#35118
+ OString mapToFontConfigLangTag(const LanguageTag &rLangTag)
+ {
+#if defined(FC_VERSION) && (FC_VERSION >= 20492)
+ std::shared_ptr<FcStrSet> xLangSet(FcGetLangs(), FcStrSetDestroy);
+ OString sLangAttrib;
+
+ sLangAttrib = OUStringToOString(rLangTag.getBcp47(), RTL_TEXTENCODING_UTF8).toAsciiLowerCase();
+ if (FcStrSetMember(xLangSet.get(), reinterpret_cast<const FcChar8*>(sLangAttrib.getStr())))
+ {
+ return sLangAttrib;
+ }
+
+ sLangAttrib = OUStringToOString(rLangTag.getLanguageAndScript(), RTL_TEXTENCODING_UTF8).toAsciiLowerCase();
+ if (FcStrSetMember(xLangSet.get(), reinterpret_cast<const FcChar8*>(sLangAttrib.getStr())))
+ {
+ return sLangAttrib;
+ }
+
+ OString sLang = OUStringToOString(rLangTag.getLanguage(), RTL_TEXTENCODING_UTF8).toAsciiLowerCase();
+ OString sRegion = OUStringToOString(rLangTag.getCountry(), RTL_TEXTENCODING_UTF8).toAsciiLowerCase();
+
+ if (!sRegion.isEmpty())
+ {
+ sLangAttrib = sLang + OString('-') + sRegion;
+ if (FcStrSetMember(xLangSet.get(), reinterpret_cast<const FcChar8*>(sLangAttrib.getStr())))
+ {
+ return sLangAttrib;
+ }
+ }
+
+ if (FcStrSetMember(xLangSet.get(), reinterpret_cast<const FcChar8*>(sLang.getStr())))
+ {
+ return sLang;
+ }
+
+ return OString();
+#else
+ OString sLangAttrib = OUStringToOString(rLangTag.getLanguageAndScript(), RTL_TEXTENCODING_UTF8).toAsciiLowerCase();
+ if (sLangAttrib.equalsIgnoreAsciiCase("pa-in"))
+ sLangAttrib = "pa";
+ return sLangAttrib;
+#endif
+ }
+
+ //returns true if the given code-point couldn't possibly be in rLangTag.
+ bool isImpossibleCodePointForLang(const LanguageTag &rLangTag, sal_uInt32 currentChar)
+ {
+ //a non-default script is set, lets believe it
+ if (rLangTag.hasScript())
+ return false;
+
+ int32_t script = u_getIntPropertyValue(currentChar, UCHAR_SCRIPT);
+ UScriptCode eScript = static_cast<UScriptCode>(script);
+ bool bIsImpossible = false;
+ OUString sLang = rLangTag.getLanguage();
+ switch (eScript)
+ {
+ //http://en.wiktionary.org/wiki/Category:Oriya_script_languages
+ case USCRIPT_ORIYA:
+ bIsImpossible =
+ sLang != "or" &&
+ sLang != "kxv";
+ break;
+ //http://en.wiktionary.org/wiki/Category:Telugu_script_languages
+ case USCRIPT_TELUGU:
+ bIsImpossible =
+ sLang != "te" &&
+ sLang != "gon" &&
+ sLang != "kfc";
+ break;
+ //http://en.wiktionary.org/wiki/Category:Bengali_script_languages
+ case USCRIPT_BENGALI:
+ bIsImpossible =
+ sLang != "bn" &&
+ sLang != "as" &&
+ sLang != "bpy" &&
+ sLang != "ctg" &&
+ sLang != "sa";
+ break;
+ default:
+ break;
+ }
+ SAL_WARN_IF(bIsImpossible, "vcl", "In glyph fallback throwing away the language property of "
+ << sLang << " because the detected script for '0x"
+ << OUString::number(currentChar, 16)
+ << "' is " << uscript_getName(eScript)
+ << " and that language doesn't make sense. Autodetecting instead.");
+ return bIsImpossible;
+ }
+
+ LanguageTag getExemplarLangTagForCodePoint(sal_uInt32 currentChar)
+ {
+ int32_t script = u_getIntPropertyValue(currentChar, UCHAR_SCRIPT);
+ UScriptCode eScript = static_cast<UScriptCode>(script);
+ OStringBuffer aBuf(unicode::getExemplarLanguageForUScriptCode(eScript));
+ const char* pScriptCode = uscript_getShortName(eScript);
+ if (pScriptCode)
+ aBuf.append('-').append(pScriptCode);
+ return LanguageTag(OStringToOUString(aBuf.makeStringAndClear(), RTL_TEXTENCODING_UTF8));
+ }
+
+#if defined(ENABLE_DBUS) && defined(ENABLE_PACKAGEKIT)
+ guint get_xid_for_dbus()
+ {
+ const vcl::Window *pTopWindow = Application::IsHeadlessModeEnabled() ? nullptr : Application::GetActiveTopWindow();
+ const SystemEnvData* pEnvData = pTopWindow ? pTopWindow->GetSystemData() : nullptr;
+ return pEnvData ? pEnvData->aWindow : 0;
+ }
+#endif
+}
+
+#if defined(ENABLE_DBUS) && defined(ENABLE_PACKAGEKIT)
+IMPL_LINK_NOARG_TYPED(PrintFontManager, autoInstallFontLangSupport, Timer *, void)
+{
+ guint xid = get_xid_for_dbus();
+
+ if (!xid)
+ return;
+
+ GError *error = nullptr;
+ /* get the DBUS session connection */
+ DBusGConnection *session_connection = dbus_g_bus_get(DBUS_BUS_SESSION, &error);
+ if (error != nullptr)
+ {
+ g_debug ("DBUS cannot connect : %s", error->message);
+ g_error_free (error);
+ return;
+ }
+
+ /* get the proxy with gnome-session-manager */
+ DBusGProxy *proxy = dbus_g_proxy_new_for_name(session_connection,
+ "org.freedesktop.PackageKit",
+ "/org/freedesktop/PackageKit",
+ "org.freedesktop.PackageKit.Modify");
+ if (proxy == nullptr)
+ {
+ g_debug("Could not get DBUS proxy: org.freedesktop.PackageKit");
+ return;
+ }
+
+ gchar **fonts = static_cast<gchar**>(g_malloc((m_aCurrentRequests.size() + 1) * sizeof(gchar*)));
+ gchar **font = fonts;
+ for (std::vector<OString>::const_iterator aI = m_aCurrentRequests.begin(); aI != m_aCurrentRequests.end(); ++aI)
+ *font++ = const_cast<gchar*>(aI->getStr());
+ *font = nullptr;
+ gboolean res = dbus_g_proxy_call(proxy, "InstallFontconfigResources", &error,
+ G_TYPE_UINT, xid, /* xid */
+ G_TYPE_STRV, fonts, /* data */
+ G_TYPE_STRING, "hide-finished", /* interaction */
+ G_TYPE_INVALID,
+ G_TYPE_INVALID);
+ /* check the return value */
+ if (!res)
+ g_debug("InstallFontconfigResources method failed");
+
+ /* check the error value */
+ if (error != nullptr)
+ {
+ g_debug("InstallFontconfigResources problem : %s", error->message);
+ g_error_free(error);
+ }
+
+ g_free(fonts);
+ g_object_unref(G_OBJECT (proxy));
+ m_aCurrentRequests.clear();
+}
+#endif
+
+bool PrintFontManager::Substitute( FontSelectPattern &rPattern, OUString& rMissingCodes )
+{
+ bool bRet = false;
+
+ FontCfgWrapper& rWrapper = FontCfgWrapper::get();
+
+ // build pattern argument for fontconfig query
+ FcPattern* pPattern = FcPatternCreate();
+
+ // Prefer scalable fonts
+ FcPatternAddBool(pPattern, FC_SCALABLE, FcTrue);
+
+ const OString aTargetName = OUStringToOString( rPattern.maTargetName, RTL_TEXTENCODING_UTF8 );
+ const FcChar8* pTargetNameUtf8 = reinterpret_cast<FcChar8 const *>(aTargetName.getStr());
+ FcPatternAddString(pPattern, FC_FAMILY, pTargetNameUtf8);
+
+ LanguageTag aLangTag(rPattern.meLanguage);
+ OString aLangAttrib = mapToFontConfigLangTag(aLangTag);
+
+ // Add required Unicode characters, if any
+ if ( !rMissingCodes.isEmpty() )
+ {
+ FcCharSet *unicodes = FcCharSetCreate();
+ for( sal_Int32 nStrIndex = 0; nStrIndex < rMissingCodes.getLength(); )
+ {
+ // also handle unicode surrogates
+ const sal_uInt32 nCode = rMissingCodes.iterateCodePoints( &nStrIndex );
+ FcCharSetAddChar( unicodes, nCode );
+ //if the codepoint is impossible for this lang tag, then clear it
+ //and autodetect something useful
+ if (!aLangAttrib.isEmpty() && isImpossibleCodePointForLang(aLangTag, nCode))
+ aLangAttrib.clear();
+ //#i105784#/rhbz#527719 improve selection of fallback font
+ if (aLangAttrib.isEmpty())
+ {
+ aLangTag = getExemplarLangTagForCodePoint(nCode);
+ aLangAttrib = mapToFontConfigLangTag(aLangTag);
+ }
+ }
+ FcPatternAddCharSet(pPattern, FC_CHARSET, unicodes);
+ FcCharSetDestroy(unicodes);
+ }
+
+ if (!aLangAttrib.isEmpty())
+ FcPatternAddString(pPattern, FC_LANG, reinterpret_cast<FcChar8 const *>(aLangAttrib.getStr()));
+
+ addtopattern(pPattern, rPattern.GetSlantType(), rPattern.GetWeight(),
+ rPattern.GetWidthType(), rPattern.GetPitch());
+
+ // query fontconfig for a substitute
+ FcConfigSubstitute(FcConfigGetCurrent(), pPattern, FcMatchPattern);
+ FcDefaultSubstitute(pPattern);
+
+ // process the result of the fontconfig query
+ FcResult eResult = FcResultNoMatch;
+ FcFontSet* pFontSet = rWrapper.getFontSet();
+ FcPattern* pResult = FcFontSetMatch(FcConfigGetCurrent(), &pFontSet, 1, pPattern, &eResult);
+ FcPatternDestroy( pPattern );
+
+ FcFontSet* pSet = nullptr;
+ if( pResult )
+ {
+ pSet = FcFontSetCreate();
+ // info: destroying the pSet destroys pResult implicitly
+ // since pResult was "added" to pSet
+ FcFontSetAdd( pSet, pResult );
+ }
+
+ if( pSet )
+ {
+ if( pSet->nfont > 0 )
+ {
+ //extract the closest match
+ FcChar8* file = nullptr;
+ FcResult eFileRes = FcPatternGetString(pSet->fonts[0], FC_FILE, 0, &file);
+ int nCollectionEntry = 0;
+ FcResult eIndexRes = FcPatternGetInteger(pSet->fonts[0], FC_INDEX, 0, &nCollectionEntry);
+ if (eIndexRes != FcResultMatch)
+ nCollectionEntry = 0;
+ if( eFileRes == FcResultMatch )
+ {
+ OString aDir, aBase, aOrgPath( reinterpret_cast<char*>(file) );
+ splitPath( aOrgPath, aDir, aBase );
+ int nDirID = getDirectoryAtom( aDir, true );
+ fontID aFont = findFontFileID( nDirID, aBase, nCollectionEntry );
+ if( aFont > 0 )
+ {
+ FastPrintFontInfo aInfo;
+ bRet = getFontFastInfo( aFont, aInfo );
+ rPattern.maSearchName = aInfo.m_aFamilyName;
+ }
+ }
+
+ SAL_WARN_IF(!bRet, "vcl", "no FC_FILE found, falling back to name search");
+
+ if (!bRet)
+ {
+ FcChar8* family = nullptr;
+ FcResult eFamilyRes = FcPatternGetString( pSet->fonts[0], FC_FAMILY, 0, &family );
+
+ // get the family name
+ if( eFamilyRes == FcResultMatch )
+ {
+ OString sFamily(reinterpret_cast<char*>(family));
+ std::unordered_map< OString, OString, OStringHash >::const_iterator aI =
+ rWrapper.m_aFontNameToLocalized.find(sFamily);
+ if (aI != rWrapper.m_aFontNameToLocalized.end())
+ sFamily = aI->second;
+ rPattern.maSearchName = OStringToOUString( sFamily, RTL_TEXTENCODING_UTF8 );
+ bRet = true;
+ }
+ }
+
+ if (bRet)
+ {
+ int val = 0;
+ if (FcResultMatch == FcPatternGetInteger(pSet->fonts[0], FC_WEIGHT, 0, &val))
+ rPattern.SetWeight( convertWeight(val) );
+ if (FcResultMatch == FcPatternGetInteger(pSet->fonts[0], FC_SLANT, 0, &val))
+ rPattern.SetItalic( convertSlant(val) );
+ if (FcResultMatch == FcPatternGetInteger(pSet->fonts[0], FC_SPACING, 0, &val))
+ rPattern.SetPitch ( convertSpacing(val) );
+ if (FcResultMatch == FcPatternGetInteger(pSet->fonts[0], FC_WIDTH, 0, &val))
+ rPattern.SetWidthType ( convertWidth(val) );
+ FcBool bEmbolden;
+ if (FcResultMatch == FcPatternGetBool(pSet->fonts[0], FC_EMBOLDEN, 0, &bEmbolden))
+ rPattern.mbEmbolden = bEmbolden;
+ FcMatrix *pMatrix = nullptr;
+ if (FcResultMatch == FcPatternGetMatrix(pSet->fonts[0], FC_MATRIX, 0, &pMatrix))
+ {
+ rPattern.maItalicMatrix.xx = pMatrix->xx;
+ rPattern.maItalicMatrix.xy = pMatrix->xy;
+ rPattern.maItalicMatrix.yx = pMatrix->yx;
+ rPattern.maItalicMatrix.yy = pMatrix->yy;
+ }
+ }
+
+ // update rMissingCodes by removing resolved unicodes
+ if( !rMissingCodes.isEmpty() )
+ {
+ std::unique_ptr<sal_uInt32[]> const pRemainingCodes(new sal_uInt32[rMissingCodes.getLength()]);
+ int nRemainingLen = 0;
+ FcCharSet* unicodes;
+ if (!FcPatternGetCharSet(pSet->fonts[0], FC_CHARSET, 0, &unicodes))
+ {
+ for( sal_Int32 nStrIndex = 0; nStrIndex < rMissingCodes.getLength(); )
+ {
+ // also handle unicode surrogates
+ const sal_uInt32 nCode = rMissingCodes.iterateCodePoints( &nStrIndex );
+ if (FcCharSetHasChar(unicodes, nCode) != FcTrue)
+ pRemainingCodes[ nRemainingLen++ ] = nCode;
+ }
+ }
+ OUString sStillMissing(pRemainingCodes.get(), nRemainingLen);
+#if defined(ENABLE_DBUS) && defined(ENABLE_PACKAGEKIT)
+ if (get_xid_for_dbus())
+ {
+ if (sStillMissing == rMissingCodes) //replaced nothing
+ {
+ //It'd be better if we could ask packagekit using the
+ //missing codepoints or some such rather than using
+ //"language" as a proxy to how fontconfig considers
+ //scripts to default to a given language.
+ for (sal_Int32 i = 0; i < nRemainingLen; ++i)
+ {
+ LanguageTag aOurTag = getExemplarLangTagForCodePoint(pRemainingCodes[i]);
+ OString sTag = OUStringToOString(aOurTag.getBcp47(), RTL_TEXTENCODING_UTF8);
+ if (m_aPreviousLangSupportRequests.find(sTag) != m_aPreviousLangSupportRequests.end())
+ continue;
+ m_aPreviousLangSupportRequests.insert(sTag);
+ sTag = mapToFontConfigLangTag(aOurTag);
+ if (!sTag.isEmpty() && m_aPreviousLangSupportRequests.find(sTag) == m_aPreviousLangSupportRequests.end())
+ {
+ OString sReq = OString(":lang=") + sTag;
+ m_aCurrentRequests.push_back(sReq);
+ m_aPreviousLangSupportRequests.insert(sTag);
+ }
+ }
+ }
+ if (!m_aCurrentRequests.empty())
+ {
+ m_aFontInstallerTimer.Stop();
+ m_aFontInstallerTimer.Start();
+ }
+ }
+#endif
+ rMissingCodes = sStillMissing;
+ }
+ }
+
+ FcFontSetDestroy( pSet );
+ }
+
+ return bRet;
+}
+
+FontConfigFontOptions::~FontConfigFontOptions()
+{
+ FcPatternDestroy(mpPattern);
+}
+
+ void *FontConfigFontOptions::GetPattern(void * face, bool bEmbolden) const
+ {
+ FcValue value;
+ value.type = FcTypeFTFace;
+ value.u.f = face;
+ FcPatternDel(mpPattern, FC_FT_FACE);
+ FcPatternAdd (mpPattern, FC_FT_FACE, value, FcTrue);
+ FcPatternDel(mpPattern, FC_EMBOLDEN);
+ FcPatternAddBool(mpPattern, FC_EMBOLDEN, bEmbolden ? FcTrue : FcFalse);
+#if 0
+ FcPatternDel(mpPattern, FC_VERTICAL_LAYOUT);
+ FcPatternAddBool(mpPattern, FC_VERTICAL_LAYOUT, bVerticalLayout ? FcTrue : FcFalse);
+#endif
+ return mpPattern;
+ }
+
+FontConfigFontOptions* PrintFontManager::getFontOptions(
+ const FastPrintFontInfo& rInfo, int nSize, void (*subcallback)(void*))
+{
+ FontCfgWrapper& rWrapper = FontCfgWrapper::get();
+
+ FontConfigFontOptions* pOptions = nullptr;
+ FcConfig* pConfig = FcConfigGetCurrent();
+ FcPattern* pPattern = FcPatternCreate();
+
+ OString sFamily = OUStringToOString( rInfo.m_aFamilyName, RTL_TEXTENCODING_UTF8 );
+
+ std::unordered_map< OString, OString, OStringHash >::const_iterator aI = rWrapper.m_aLocalizedToCanonical.find(sFamily);
+ if (aI != rWrapper.m_aLocalizedToCanonical.end())
+ sFamily = aI->second;
+ if( !sFamily.isEmpty() )
+ FcPatternAddString(pPattern, FC_FAMILY, reinterpret_cast<FcChar8 const *>(sFamily.getStr()));
+
+ addtopattern(pPattern, rInfo.m_eItalic, rInfo.m_eWeight, rInfo.m_eWidth, rInfo.m_ePitch);
+ FcPatternAddDouble(pPattern, FC_PIXEL_SIZE, nSize);
+
+ FcBool embitmap = true, antialias = true, autohint = true, hinting = true;
+ int hintstyle = FC_HINT_FULL;
+
+ FcConfigSubstitute(pConfig, pPattern, FcMatchPattern);
+ if (subcallback)
+ subcallback(pPattern);
+ FcDefaultSubstitute(pPattern);
+
+ FcResult eResult = FcResultNoMatch;
+ FcFontSet* pFontSet = rWrapper.getFontSet();
+ FcPattern* pResult = FcFontSetMatch( pConfig, &pFontSet, 1, pPattern, &eResult );
+ if( pResult )
+ {
+ FcResult eEmbeddedBitmap = FcPatternGetBool(pResult,
+ FC_EMBEDDED_BITMAP, 0, &embitmap);
+ FcResult eAntialias = FcPatternGetBool(pResult,
+ FC_ANTIALIAS, 0, &antialias);
+ FcResult eAutoHint = FcPatternGetBool(pResult,
+ FC_AUTOHINT, 0, &autohint);
+ FcResult eHinting = FcPatternGetBool(pResult,
+ FC_HINTING, 0, &hinting);
+ (void) FcPatternGetInteger(pResult,
+ FC_HINT_STYLE, 0, &hintstyle);
+
+ pOptions = new FontConfigFontOptions(pResult);
+
+ if( eEmbeddedBitmap == FcResultMatch )
+ pOptions->meEmbeddedBitmap = embitmap ? EMBEDDEDBITMAP_TRUE : EMBEDDEDBITMAP_FALSE;
+ if( eAntialias == FcResultMatch )
+ pOptions->meAntiAlias = antialias ? ANTIALIAS_TRUE : ANTIALIAS_FALSE;
+ if( eAutoHint == FcResultMatch )
+ pOptions->meAutoHint = autohint ? AUTOHINT_TRUE : AUTOHINT_FALSE;
+ if( eHinting == FcResultMatch )
+ pOptions->meHinting = hinting ? HINTING_TRUE : HINTING_FALSE;
+ switch (hintstyle)
+ {
+ case FC_HINT_NONE: pOptions->meHintStyle = HINT_NONE; break;
+ case FC_HINT_SLIGHT: pOptions->meHintStyle = HINT_SLIGHT; break;
+ case FC_HINT_MEDIUM: pOptions->meHintStyle = HINT_MEDIUM; break;
+ default: // fall through
+ case FC_HINT_FULL: pOptions->meHintStyle = HINT_FULL; break;
+ }
+ }
+
+ // cleanup
+ FcPatternDestroy( pPattern );
+
+ return pOptions;
+}
+
+bool PrintFontManager::matchFont( FastPrintFontInfo& rInfo, const css::lang::Locale& rLocale )
+{
+ FontCfgWrapper& rWrapper = FontCfgWrapper::get();
+
+ FcConfig* pConfig = FcConfigGetCurrent();
+ FcPattern* pPattern = FcPatternCreate();
+
+ // populate pattern with font characteristics
+ const LanguageTag aLangTag(rLocale);
+ const OString aLangAttrib = mapToFontConfigLangTag(aLangTag);
+ if (!aLangAttrib.isEmpty())
+ FcPatternAddString(pPattern, FC_LANG, reinterpret_cast<FcChar8 const *>(aLangAttrib.getStr()));
+
+ OString aFamily = OUStringToOString( rInfo.m_aFamilyName, RTL_TEXTENCODING_UTF8 );
+ if( !aFamily.isEmpty() )
+ FcPatternAddString(pPattern, FC_FAMILY, reinterpret_cast<FcChar8 const *>(aFamily.getStr()));
+
+ addtopattern(pPattern, rInfo.m_eItalic, rInfo.m_eWeight, rInfo.m_eWidth, rInfo.m_ePitch);
+
+ FcConfigSubstitute(pConfig, pPattern, FcMatchPattern);
+ FcDefaultSubstitute(pPattern);
+ FcResult eResult = FcResultNoMatch;
+ FcFontSet *pFontSet = rWrapper.getFontSet();
+ FcPattern* pResult = FcFontSetMatch(pConfig, &pFontSet, 1, pPattern, &eResult);
+ bool bSuccess = false;
+ if( pResult )
+ {
+ FcFontSet* pSet = FcFontSetCreate();
+ FcFontSetAdd( pSet, pResult );
+ if( pSet->nfont > 0 )
+ {
+ //extract the closest match
+ FcChar8* file = nullptr;
+ FcResult eFileRes = FcPatternGetString(pSet->fonts[0], FC_FILE, 0, &file);
+ int nCollectionEntry = 0;
+ FcResult eIndexRes = FcPatternGetInteger(pSet->fonts[0], FC_INDEX, 0, &nCollectionEntry);
+ if (eIndexRes != FcResultMatch)
+ nCollectionEntry = 0;
+ if( eFileRes == FcResultMatch )
+ {
+ OString aDir, aBase, aOrgPath( reinterpret_cast<char*>(file) );
+ splitPath( aOrgPath, aDir, aBase );
+ int nDirID = getDirectoryAtom( aDir, true );
+ fontID aFont = findFontFileID( nDirID, aBase, nCollectionEntry );
+ if( aFont > 0 )
+ bSuccess = getFontFastInfo( aFont, rInfo );
+ }
+ }
+ // info: destroying the pSet destroys pResult implicitly
+ // since pResult was "added" to pSet
+ FcFontSetDestroy( pSet );
+ }
+
+ // cleanup
+ FcPatternDestroy( pPattern );
+
+ return bSuccess;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/vcl/unx/generic/fontmanager/fontmanager.cxx b/vcl/unx/generic/fontmanager/fontmanager.cxx
new file mode 100644
index 000000000000..6145f54c163e
--- /dev/null
+++ b/vcl/unx/generic/fontmanager/fontmanager.cxx
@@ -0,0 +1,2222 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <unistd.h>
+#include <sys/stat.h>
+#include <dirent.h>
+#include <stdlib.h>
+#include <osl/thread.h>
+
+#include "unotools/atom.hxx"
+
+#include "fontcache.hxx"
+#include "fontsubset.hxx"
+#include "impfont.hxx"
+#include "svdata.hxx"
+#include "generic/geninst.h"
+#include "fontmanager.hxx"
+#include "vcl/strhelper.hxx"
+#include "vcl/ppdparser.hxx"
+#include <vcl/embeddedfontshelper.hxx>
+#include <vcl/fontcharmap.hxx>
+
+#include "tools/urlobj.hxx"
+#include "tools/stream.hxx"
+#include "tools/debug.hxx"
+
+#include "osl/file.hxx"
+#include "osl/process.h"
+
+#include "rtl/tencinfo.h"
+#include "rtl/ustrbuf.hxx"
+#include "rtl/strbuf.hxx"
+
+#include <sal/macros.h>
+
+#include "i18nlangtag/applelangid.hxx"
+#include "i18nlangtag/mslangid.hxx"
+
+#include "parseAFM.hxx"
+#include "sft.hxx"
+
+#if OSL_DEBUG_LEVEL > 1
+#include <sys/times.h>
+#include <stdio.h>
+#endif
+
+#include <algorithm>
+#include <set>
+#include <unordered_set>
+
+#include "adobe_encoding_table.hxx"
+
+#ifdef CALLGRIND_COMPILE
+#include <valgrind/callgrind.h>
+#endif
+
+#include <comphelper/processfactory.hxx>
+#include <comphelper/string.hxx>
+#include "com/sun/star/beans/XMaterialHolder.hpp"
+#include "com/sun/star/beans/NamedValue.hpp"
+
+using namespace vcl;
+using namespace utl;
+using namespace psp;
+using namespace osl;
+using namespace com::sun::star::uno;
+using namespace com::sun::star::beans;
+using namespace com::sun::star::lang;
+
+/*
+ * static helpers
+ */
+
+inline sal_uInt16 getUInt16BE( const sal_uInt8*& pBuffer )
+{
+ sal_uInt16 nRet = (sal_uInt16)pBuffer[1] |
+ (((sal_uInt16)pBuffer[0]) << 8);
+ pBuffer+=2;
+ return nRet;
+}
+
+static FontWeight parseWeight( const OString& rWeight )
+{
+ FontWeight eWeight = WEIGHT_DONTKNOW;
+ if (rWeight.indexOf("bold") != -1)
+ {
+ if (rWeight.indexOf("emi") != -1) // semi, demi
+ eWeight = WEIGHT_SEMIBOLD;
+ else if (rWeight.indexOf("ultra") != -1)
+ eWeight = WEIGHT_ULTRABOLD;
+ else
+ eWeight = WEIGHT_BOLD;
+ }
+ else if (rWeight.indexOf("heavy") != -1)
+ eWeight = WEIGHT_BOLD;
+ else if (rWeight.indexOf("light") != -1)
+ {
+ if (rWeight.indexOf("emi") != -1) // semi, demi
+ eWeight = WEIGHT_SEMILIGHT;
+ else if (rWeight.indexOf("ultra") != -1)
+ eWeight = WEIGHT_ULTRALIGHT;
+ else
+ eWeight = WEIGHT_LIGHT;
+ }
+ else if (rWeight.indexOf("black") != -1)
+ eWeight = WEIGHT_BLACK;
+ else if (rWeight == "demi")
+ eWeight = WEIGHT_SEMIBOLD;
+ else if ((rWeight == "book") ||
+ (rWeight == "semicondensed"))
+ eWeight = WEIGHT_LIGHT;
+ else if ((rWeight == "medium") || (rWeight == "roman"))
+ eWeight = WEIGHT_MEDIUM;
+ else
+ eWeight = WEIGHT_NORMAL;
+ return eWeight;
+}
+
+/*
+ * PrintFont implementations
+ */
+PrintFontManager::PrintFont::PrintFont( fonttype::type eType ) :
+ m_eType( eType ),
+ m_nFamilyName( 0 ),
+ m_nPSName( 0 ),
+ m_eItalic( ITALIC_DONTKNOW ),
+ m_eWidth( WIDTH_DONTKNOW ),
+ m_eWeight( WEIGHT_DONTKNOW ),
+ m_ePitch( PITCH_DONTKNOW ),
+ m_aEncoding( RTL_TEXTENCODING_DONTKNOW ),
+ m_bFontEncodingOnly( false ),
+ m_pMetrics( nullptr ),
+ m_nAscend( 0 ),
+ m_nDescend( 0 ),
+ m_nLeading( 0 ),
+ m_nXMin( 0 ),
+ m_nYMin( 0 ),
+ m_nXMax( 0 ),
+ m_nYMax( 0 ),
+ m_bHaveVerticalSubstitutedGlyphs( false ),
+ m_bUserOverride( false )
+{
+}
+
+PrintFontManager::PrintFont::~PrintFont()
+{
+ delete m_pMetrics;
+}
+
+PrintFontManager::Type1FontFile::~Type1FontFile()
+{
+}
+
+PrintFontManager::TrueTypeFontFile::TrueTypeFontFile()
+: PrintFont( fonttype::TrueType )
+, m_nDirectory( 0 )
+, m_nCollectionEntry( 0 )
+, m_nTypeFlags( TYPEFLAG_INVALID )
+{}
+
+PrintFontManager::TrueTypeFontFile::~TrueTypeFontFile()
+{
+}
+
+bool PrintFontManager::Type1FontFile::queryMetricPage( int /*nPage*/, MultiAtomProvider* pProvider )
+{
+ return readAfmMetrics( pProvider, false, false );
+}
+
+bool PrintFontManager::TrueTypeFontFile::queryMetricPage( int nPage, MultiAtomProvider* /*pProvider*/ )
+{
+ bool bSuccess = false;
+
+ OString aFile( PrintFontManager::get().getFontFile( this ) );
+
+ TrueTypeFont* pTTFont = nullptr;
+
+ if( OpenTTFontFile( aFile.getStr(), m_nCollectionEntry, &pTTFont ) == SF_OK )
+ {
+ if( ! m_pMetrics )
+ {
+ m_pMetrics = new PrintFontMetrics;
+ memset (m_pMetrics->m_aPages, 0, sizeof(m_pMetrics->m_aPages));
+ }
+ m_pMetrics->m_aPages[ nPage/8 ] |= (1 << ( nPage & 7 ));
+ int i;
+ sal_uInt16 table[256], table_vert[256];
+
+ for( i = 0; i < 256; i++ )
+ table[ i ] = 256*nPage + i;
+
+ int nCharacters = nPage < 255 ? 256 : 254;
+ MapString( pTTFont, table, nCharacters, nullptr, false );
+ TTSimpleGlyphMetrics* pMetrics = GetTTSimpleCharMetrics( pTTFont, nPage*256, nCharacters, false );
+ if( pMetrics )
+ {
+ for( i = 0; i < nCharacters; i++ )
+ {
+ if( table[i] )
+ {
+ CharacterMetric& rChar = m_pMetrics->m_aMetrics[ nPage*256 + i ];
+ rChar.width = pMetrics[ i ].adv;
+ rChar.height = m_aGlobalMetricX.height;
+ }
+ }
+
+ free( pMetrics );
+ }
+
+ for( i = 0; i < 256; i++ )
+ table_vert[ i ] = 256*nPage + i;
+ MapString( pTTFont, table_vert, nCharacters, nullptr, true );
+ pMetrics = GetTTSimpleCharMetrics( pTTFont, nPage*256, nCharacters, true );
+ if( pMetrics )
+ {
+ for( i = 0; i < nCharacters; i++ )
+ {
+ if( table_vert[i] )
+ {
+ CharacterMetric& rChar = m_pMetrics->m_aMetrics[ nPage*256 + i + ( 1 << 16 ) ];
+ rChar.width = m_aGlobalMetricY.width;
+ rChar.height = pMetrics[ i ].adv;
+ if( table_vert[i] != table[i] )
+ m_pMetrics->m_bVerticalSubstitutions[ nPage*256 + i ] = true;
+ }
+ }
+ free( pMetrics );
+ }
+ CloseTTFont( pTTFont );
+ bSuccess = true;
+ }
+ return bSuccess;
+}
+
+/* #i73387# There seem to be fonts with a rather unwell chosen family name
+* consider e.g. "Helvetica Narrow" which defines its family as "Helvetica"
+* It can really only be distinguished by its PSName and FullName. Both of
+* which are not user presentable in OOo. So replace it by something sensible.
+*
+* If other fonts feature this behaviour, insert them to the map.
+*/
+static bool familyNameOverride( const OUString& i_rPSname, OUString& o_rFamilyName )
+{
+ static std::unordered_map< OUString, OUString, OUStringHash > aPSNameToFamily( 16 );
+ if( aPSNameToFamily.empty() ) // initialization
+ {
+ aPSNameToFamily[ "Helvetica-Narrow" ] = "Helvetica Narrow";
+ aPSNameToFamily[ "Helvetica-Narrow-Bold" ] = "Helvetica Narrow";
+ aPSNameToFamily[ "Helvetica-Narrow-BoldOblique" ] = "Helvetica Narrow";
+ aPSNameToFamily[ "Helvetica-Narrow-Oblique" ] = "Helvetica Narrow";
+ }
+ std::unordered_map<OUString,OUString,OUStringHash>::const_iterator it =
+ aPSNameToFamily.find( i_rPSname );
+ bool bReplaced = (it != aPSNameToFamily.end() );
+ if( bReplaced )
+ o_rFamilyName = it->second;
+ return bReplaced;
+};
+
+bool PrintFontManager::PrintFont::readAfmMetrics( MultiAtomProvider* pProvider, bool bFillEncodingvector, bool bOnlyGlobalAttributes )
+{
+ PrintFontManager& rManager( PrintFontManager::get() );
+ const OString& rFileName = rManager.getAfmFile( this );
+
+ FontInfo* pInfo = nullptr;
+ parseFile( rFileName.getStr(), &pInfo, P_ALL );
+ if( ! pInfo || ! pInfo->numOfChars )
+ {
+ if( pInfo )
+ freeFontInfo( pInfo );
+ return false;
+ }
+
+ m_aEncodingVector.clear();
+ m_aEncodingVectorPriority.clear();
+ // fill in global info
+
+ // PSName
+ OUString aPSName( OStringToOUString( pInfo->gfi->fontName, RTL_TEXTENCODING_ISO_8859_1 ) );
+ m_nPSName = pProvider->getAtom( ATOM_PSNAME, aPSName, true );
+
+ // family name (if not already set)
+ OUString aFamily;
+ if( ! m_nFamilyName )
+ {
+ aFamily = OStringToOUString( pInfo->gfi->familyName, RTL_TEXTENCODING_ISO_8859_1 );
+ if( aFamily.isEmpty() )
+ {
+ aFamily = OStringToOUString( pInfo->gfi->fontName, RTL_TEXTENCODING_ISO_8859_1 );
+ sal_Int32 nIndex = 0;
+ aFamily = aFamily.getToken( 0, '-', nIndex );
+ }
+ familyNameOverride( aPSName, aFamily );
+ m_nFamilyName = pProvider->getAtom( ATOM_FAMILYNAME, aFamily, true );
+ }
+ else
+ aFamily = pProvider->getString( ATOM_FAMILYNAME, m_nFamilyName );
+
+ // style name: if fullname begins with family name
+ // interpret the rest of fullname as style
+ if( m_aStyleName.isEmpty() && pInfo->gfi->fullName && *pInfo->gfi->fullName )
+ {
+ OUString aFullName( OStringToOUString( pInfo->gfi->fullName, RTL_TEXTENCODING_ISO_8859_1 ) );
+ if( aFullName.startsWith( aFamily ) )
+ m_aStyleName = WhitespaceToSpace( aFullName.copy( aFamily.getLength() ) );
+ }
+
+ // italic
+ if( pInfo->gfi->italicAngle > 0 )
+ m_eItalic = ITALIC_OBLIQUE;
+ else if( pInfo->gfi->italicAngle < 0 )
+ m_eItalic = ITALIC_NORMAL;
+ else
+ m_eItalic = ITALIC_NONE;
+
+ // weight
+ OString aWeight( pInfo->gfi->weight );
+ m_eWeight = parseWeight( aWeight.toAsciiLowerCase() );
+
+ // pitch
+ m_ePitch = pInfo->gfi->isFixedPitch ? PITCH_FIXED : PITCH_VARIABLE;
+
+ // encoding - only set if unknown
+ int nAdobeEncoding = 0;
+ if( pInfo->gfi->encodingScheme )
+ {
+ if( !strcmp( pInfo->gfi->encodingScheme, "AdobeStandardEncoding" ) )
+ nAdobeEncoding = 1;
+ else if( !strcmp( pInfo->gfi->encodingScheme, "ISO10646-1" ) )
+ {
+ nAdobeEncoding = 1;
+ m_aEncoding = RTL_TEXTENCODING_UNICODE;
+ }
+ else if( !strcmp( pInfo->gfi->encodingScheme, "Symbol") )
+ nAdobeEncoding = 2;
+ else if( !strcmp( pInfo->gfi->encodingScheme, "FontSpecific") )
+ nAdobeEncoding = 3;
+
+ if( m_aEncoding == RTL_TEXTENCODING_DONTKNOW )
+ m_aEncoding = nAdobeEncoding == 1 ?
+ RTL_TEXTENCODING_ADOBE_STANDARD : RTL_TEXTENCODING_SYMBOL;
+ }
+ else if( m_aEncoding == RTL_TEXTENCODING_DONTKNOW )
+ m_aEncoding = RTL_TEXTENCODING_ADOBE_STANDARD;
+
+ // try to parse the font name and decide whether it might be a
+ // japanese font. Who invented this PITA ?
+ OUString aPSNameLastToken( aPSName.copy( aPSName.lastIndexOf( '-' )+1 ) );
+ if( aPSNameLastToken == "H" ||
+ aPSNameLastToken == "V" )
+ {
+ static const char* pEncs[] =
+ {
+ "EUC",
+ "RKSJ",
+ "SJ"
+ };
+ static const rtl_TextEncoding aEncs[] =
+ {
+ RTL_TEXTENCODING_EUC_JP,
+ RTL_TEXTENCODING_SHIFT_JIS,
+ RTL_TEXTENCODING_JIS_X_0208
+ };
+
+ for( unsigned int enc = 0; enc < SAL_N_ELEMENTS( aEncs ) && m_aEncoding == RTL_TEXTENCODING_DONTKNOW; enc++ )
+ {
+ sal_Int32 nIndex = 0, nOffset = 1;
+ do
+ {
+ OUString aToken( aPSName.getToken( nOffset, '-', nIndex ) );
+ if( nIndex == -1 )
+ break;
+ nOffset = 0;
+ if( aToken.equalsAscii( pEncs[enc] ) )
+ {
+ m_aEncoding = aEncs[ enc ];
+ m_bFontEncodingOnly = true;
+ }
+ } while( nIndex != -1 );
+ }
+
+ // default is jis
+ if( m_aEncoding == RTL_TEXTENCODING_DONTKNOW )
+ m_aEncoding = RTL_TEXTENCODING_JIS_X_0208;
+#if OSL_DEBUG_LEVEL > 1
+ fprintf( stderr, "Encoding %d for %s\n", m_aEncoding, pInfo->gfi->fontName );
+#endif
+ }
+
+ // #i37313# check if Fontspecific is not rather some character encoding
+ if( nAdobeEncoding == 3 && m_aEncoding == RTL_TEXTENCODING_SYMBOL )
+ {
+ bool bYFound = false;
+ bool bQFound = false;
+ CharMetricInfo* pChar = pInfo->cmi;
+ for( int j = 0; j < pInfo->numOfChars && ! (bYFound && bQFound); j++ )
+ {
+ if( pChar[j].name )
+ {
+ if( pChar[j].name[0] == 'Y' && pChar[j].name[1] == 0 )
+ bYFound = true;
+ else if( pChar[j].name[0] == 'Q' && pChar[j].name[1] == 0 )
+ bQFound = true;
+ }
+ }
+ if( bQFound && bYFound )
+ {
+ #if OSL_DEBUG_LEVEL > 1
+ fprintf( stderr, "setting FontSpecific font %s (file %s) to unicode\n",
+ pInfo->gfi->fontName,
+ rFileName.getStr()
+ );
+ #endif
+ nAdobeEncoding = 4;
+ m_aEncoding = RTL_TEXTENCODING_UNICODE;
+ bFillEncodingvector = false; // will be filled anyway, don't do the work twice
+ }
+ }
+
+ // ascend
+ m_nAscend = pInfo->gfi->fontBBox.ury;
+
+ // descend
+ // descends have opposite sign of our definition
+ m_nDescend = -pInfo->gfi->fontBBox.lly;
+
+ // fallback to ascender, descender
+ // interesting: the BBox seems to describe Ascender and Descender better
+ // as we understand it
+ if( m_nAscend == 0 )
+ m_nAscend = pInfo->gfi->ascender;
+ if( m_nDescend == 0)
+ m_nDescend = -pInfo->gfi->descender;
+
+ m_nLeading = m_nAscend + m_nDescend - 1000;
+
+ delete m_pMetrics;
+ m_pMetrics = new PrintFontMetrics;
+ // mark all pages as queried (or clear if only global font info queried)
+ memset( m_pMetrics->m_aPages, bOnlyGlobalAttributes ? 0 : 0xff, sizeof( m_pMetrics->m_aPages ) );
+
+ m_aGlobalMetricX.width = m_aGlobalMetricY.width =
+ pInfo->gfi->charwidth ? pInfo->gfi->charwidth : pInfo->gfi->fontBBox.urx;
+ m_aGlobalMetricX.height = m_aGlobalMetricY.height =
+ pInfo->gfi->capHeight ? pInfo->gfi->capHeight : pInfo->gfi->fontBBox.ury;
+
+ m_nXMin = pInfo->gfi->fontBBox.llx;
+ m_nYMin = pInfo->gfi->fontBBox.lly;
+ m_nXMax = pInfo->gfi->fontBBox.urx;
+ m_nYMax = pInfo->gfi->fontBBox.ury;
+
+ if( bFillEncodingvector || !bOnlyGlobalAttributes )
+ {
+ // fill in character metrics
+
+ // first transform the character codes to unicode
+ // note: this only works with single byte encodings
+ std::unique_ptr<sal_Unicode[]> const pUnicodes(new sal_Unicode[pInfo->numOfChars]);
+ CharMetricInfo* pChar = pInfo->cmi;
+ int i;
+
+ for( i = 0; i < pInfo->numOfChars; i++, pChar++ )
+ {
+ if( nAdobeEncoding == 4 )
+ {
+ if( pChar->name )
+ {
+ pUnicodes[i] = 0;
+ std::list< sal_Unicode > aCodes = rManager.getUnicodeFromAdobeName( pChar->name );
+ for( std::list< sal_Unicode >::const_iterator it = aCodes.begin(); it != aCodes.end(); ++it )
+ {
+ if( *it != 0 )
+ {
+ m_aEncodingVector[ *it ] = pChar->code;
+ if( pChar->code == -1 )
+ m_aNonEncoded[ *it ] = pChar->name;
+ if( ! pUnicodes[i] ) // map the first
+ pUnicodes[i] = *it;
+ }
+ }
+ }
+ }
+ else if( pChar->code != -1 )
+ {
+ if( nAdobeEncoding == 3 && m_aEncoding == RTL_TEXTENCODING_SYMBOL )
+ {
+ pUnicodes[i] = pChar->code + 0xf000;
+ if( bFillEncodingvector )
+ {
+ m_aEncodingVector[ pUnicodes[i] ] = pChar->code;
+ m_aEncodingVectorPriority.insert(pUnicodes[i]);
+ }
+ continue;
+ }
+
+ if( m_aEncoding == RTL_TEXTENCODING_UNICODE )
+ {
+ pUnicodes[i] = (sal_Unicode)pChar->code;
+ continue;
+ }
+
+ OStringBuffer aTranslate;
+ if( pChar->code & 0xff000000 )
+ aTranslate.append((char)(pChar->code >> 24));
+ if( pChar->code & 0xffff0000 )
+ aTranslate.append((char)((pChar->code & 0x00ff0000) >> 16));
+ if( pChar->code & 0xffffff00 )
+ aTranslate.append((char)((pChar->code & 0x0000ff00) >> 8 ));
+ aTranslate.append((char)(pChar->code & 0xff));
+ OUString aUni(OStringToOUString(aTranslate.makeStringAndClear(), m_aEncoding));
+ pUnicodes[i] = aUni.toChar();
+ }
+ else
+ pUnicodes[i] = 0;
+ }
+
+ // now fill in the character metrics
+ // parseAFM.cxx effectively only supports direction 0 (horizontal)
+ pChar = pInfo->cmi;
+ CharacterMetric aMetric;
+ for( i = 0; i < pInfo->numOfChars; i++, pChar++ )
+ {
+ if( pChar->code == -1 && ! pChar->name )
+ continue;
+
+ if( bFillEncodingvector && pChar->name )
+ {
+ std::list< sal_Unicode > aCodes = rManager.getUnicodeFromAdobeName( pChar->name );
+ for( std::list< sal_Unicode >::const_iterator it = aCodes.begin(); it != aCodes.end(); ++it )
+ {
+ if( *it != 0 )
+ {
+ m_aEncodingVector[ *it ] = pChar->code;
+ if( pChar->code == -1 )
+ m_aNonEncoded[ *it ] = pChar->name;
+ }
+ }
+ }
+
+ aMetric.width = pChar->wx ? pChar->wx : pChar->charBBox.urx;
+ aMetric.height = pChar->wy ? pChar->wy : pChar->charBBox.ury - pChar->charBBox.lly;
+ if( aMetric.width == 0 && aMetric.height == 0 )
+ // guess something for e.g. space
+ aMetric.width = m_aGlobalMetricX.width/4;
+
+ if( ( nAdobeEncoding == 0 ) ||
+ ( ( nAdobeEncoding == 3 ) && ( m_aEncoding != RTL_TEXTENCODING_SYMBOL ) ) )
+ {
+ if( pChar->code != -1 )
+ {
+ m_pMetrics->m_aMetrics[ pUnicodes[i] ] = aMetric;
+ if( bFillEncodingvector )
+ {
+ m_aEncodingVector[ pUnicodes[i] ] = pChar->code;
+ m_aEncodingVectorPriority.insert(pUnicodes[i]);
+ }
+ }
+ else if( pChar->name )
+ {
+ std::list< sal_Unicode > aCodes = rManager.getUnicodeFromAdobeName( pChar->name );
+ for( std::list< sal_Unicode >::const_iterator it = aCodes.begin(); it != aCodes.end(); ++it )
+ {
+ if( *it != 0 )
+ m_pMetrics->m_aMetrics[ *it ] = aMetric;
+ }
+ }
+ }
+ else if( nAdobeEncoding == 1 || nAdobeEncoding == 2 || nAdobeEncoding == 4)
+ {
+ if( pChar->name )
+ {
+ std::list< sal_Unicode > aCodes = rManager.getUnicodeFromAdobeName( pChar->name );
+ for( std::list< sal_Unicode >::const_iterator it = aCodes.begin(); it != aCodes.end(); ++it )
+ {
+ if( *it != 0 )
+ m_pMetrics->m_aMetrics[ *it ] = aMetric;
+ }
+ }
+ else if( pChar->code != -1 )
+ {
+ ::std::pair< std::unordered_multimap< sal_uInt8, sal_Unicode >::const_iterator,
+ std::unordered_multimap< sal_uInt8, sal_Unicode >::const_iterator >
+ aCodes = rManager.getUnicodeFromAdobeCode( pChar->code );
+ bool bFirst = true;
+ while( aCodes.first != aCodes.second )
+ {
+ if( (*aCodes.first).second != 0 )
+ {
+ m_pMetrics->m_aMetrics[ (*aCodes.first).second ] = aMetric;
+ if( bFillEncodingvector )
+ {
+ m_aEncodingVector[ (*aCodes.first).second ] = pChar->code;
+ if (bFirst) // arbitrarily prefer the first one
+ {
+ m_aEncodingVectorPriority.insert((*aCodes.first).second);
+ bFirst = false;
+ }
+ }
+ }
+ ++aCodes.first;
+ }
+ }
+ }
+ else if( nAdobeEncoding == 3 )
+ {
+ if( pChar->code != -1 )
+ {
+ sal_Unicode code = 0xf000 + pChar->code;
+ m_pMetrics->m_aMetrics[ code ] = aMetric;
+ // maybe should try to find the name in the convtabs ?
+ if( bFillEncodingvector )
+ {
+ m_aEncodingVector[ code ] = pChar->code;
+ m_aEncodingVectorPriority.insert(code);
+ }
+ }
+ }
+ }
+ }
+
+ freeFontInfo( pInfo );
+ return true;
+}
+
+/*
+ * one instance only
+ */
+PrintFontManager& PrintFontManager::get()
+{
+ static PrintFontManager* pManager = nullptr;
+ if( ! pManager )
+ {
+ static PrintFontManager theManager;
+ pManager = &theManager;
+ pManager->initialize();
+ }
+ return *pManager;
+}
+
+/*
+ * the PrintFontManager
+ */
+
+PrintFontManager::PrintFontManager()
+ : m_nNextFontID( 1 )
+ , m_pAtoms( new MultiAtomProvider() )
+ , m_nNextDirAtom( 1 )
+ , m_pFontCache( nullptr )
+{
+ for( unsigned int i = 0; i < SAL_N_ELEMENTS( aAdobeCodes ); i++ )
+ {
+ m_aUnicodeToAdobename.insert( std::unordered_multimap< sal_Unicode, OString >::value_type( aAdobeCodes[i].aUnicode, aAdobeCodes[i].pAdobename ) );
+ m_aAdobenameToUnicode.insert( std::unordered_multimap< OString, sal_Unicode, OStringHash >::value_type( aAdobeCodes[i].pAdobename, aAdobeCodes[i].aUnicode ) );
+ if( aAdobeCodes[i].aAdobeStandardCode )
+ {
+ m_aUnicodeToAdobecode.insert( std::unordered_multimap< sal_Unicode, sal_uInt8 >::value_type( aAdobeCodes[i].aUnicode, aAdobeCodes[i].aAdobeStandardCode ) );
+ m_aAdobecodeToUnicode.insert( std::unordered_multimap< sal_uInt8, sal_Unicode >::value_type( aAdobeCodes[i].aAdobeStandardCode, aAdobeCodes[i].aUnicode ) );
+ }
+ }
+
+#if defined(ENABLE_DBUS) && defined(ENABLE_PACKAGEKIT)
+ m_aFontInstallerTimer.SetTimeoutHdl(LINK(this, PrintFontManager, autoInstallFontLangSupport));
+ m_aFontInstallerTimer.SetTimeout(5000);
+#endif
+}
+
+PrintFontManager::~PrintFontManager()
+{
+ m_aFontInstallerTimer.Stop();
+ deinitFontconfig();
+ for( std::unordered_map< fontID, PrintFont* >::const_iterator it = m_aFonts.begin(); it != m_aFonts.end(); ++it )
+ delete (*it).second;
+ delete m_pAtoms;
+ delete m_pFontCache;
+}
+
+OString PrintFontManager::getDirectory( int nAtom ) const
+{
+ std::unordered_map< int, OString >::const_iterator it( m_aAtomToDir.find( nAtom ) );
+ return it != m_aAtomToDir.end() ? it->second : OString();
+}
+
+int PrintFontManager::getDirectoryAtom( const OString& rDirectory, bool bCreate )
+{
+ int nAtom = 0;
+ std::unordered_map< OString, int, OStringHash >::const_iterator it
+ ( m_aDirToAtom.find( rDirectory ) );
+ if( it != m_aDirToAtom.end() )
+ nAtom = it->second;
+ else if( bCreate )
+ {
+ nAtom = m_nNextDirAtom++;
+ m_aDirToAtom[ rDirectory ] = nAtom;
+ m_aAtomToDir[ nAtom ] = rDirectory;
+ }
+ return nAtom;
+}
+
+std::vector<fontID> PrintFontManager::addFontFile( const OString& rFileName )
+{
+ rtl_TextEncoding aEncoding = osl_getThreadTextEncoding();
+ INetURLObject aPath( OStringToOUString( rFileName, aEncoding ), INetURLObject::FSYS_DETECT );
+ OString aName( OUStringToOString( aPath.GetName( INetURLObject::DECODE_WITH_CHARSET, aEncoding ), aEncoding ) );
+ OString aDir( OUStringToOString(
+ INetURLObject::decode( aPath.GetPath(), INetURLObject::DECODE_WITH_CHARSET, aEncoding ), aEncoding ) );
+
+ int nDirID = getDirectoryAtom( aDir, true );
+ std::vector<fontID> aFontIds = findFontFileIDs( nDirID, aName );
+ if( aFontIds.empty() )
+ {
+ ::std::list< PrintFont* > aNewFonts;
+ if( analyzeFontFile( nDirID, aName, aNewFonts ) )
+ {
+ for( ::std::list< PrintFont* >::iterator it = aNewFonts.begin();
+ it != aNewFonts.end(); ++it )
+ {
+ fontID nFontId = m_nNextFontID++;
+ m_aFonts[nFontId] = *it;
+ m_aFontFileToFontID[ aName ].insert( nFontId );
+ m_pFontCache->updateFontCacheEntry( *it, true );
+ aFontIds.push_back(nFontId);
+ }
+ }
+ }
+ return aFontIds;
+}
+
+enum fontFormat
+{
+ UNKNOWN, TRUETYPE, CFF, TYPE1
+};
+
+bool PrintFontManager::analyzeFontFile( int nDirID, const OString& rFontFile, ::std::list< PrintFontManager::PrintFont* >& rNewFonts, const char *pFormat ) const
+{
+ rNewFonts.clear();
+
+ OString aDir( getDirectory( nDirID ) );
+
+ OString aFullPath( aDir );
+ aFullPath += "/";
+ aFullPath += rFontFile;
+
+ // #i1872# reject unreadable files
+ if( access( aFullPath.getStr(), R_OK ) )
+ return false;
+
+ fontFormat eFormat = UNKNOWN;
+ if (pFormat)
+ {
+ if (!strcmp(pFormat, "TrueType"))
+ eFormat = TRUETYPE;
+ else if (!strcmp(pFormat, "CFF"))
+ eFormat = CFF;
+ else if (!strcmp(pFormat, "Type 1"))
+ eFormat = TYPE1;
+ }
+ if (eFormat == UNKNOWN)
+ {
+ OString aExt( rFontFile.copy( rFontFile.lastIndexOf( '.' )+1 ) );
+ if( aExt.equalsIgnoreAsciiCase("pfb") || aExt.equalsIgnoreAsciiCase("pfa") )
+ eFormat = TYPE1;
+ else if( aExt.equalsIgnoreAsciiCase("ttf")
+ || aExt.equalsIgnoreAsciiCase("ttc")
+ || aExt.equalsIgnoreAsciiCase("tte") ) // #i33947# for Gaiji support
+ eFormat = TRUETYPE;
+ else if( aExt.equalsIgnoreAsciiCase("otf") ) // check for TTF- and PS-OpenType too
+ eFormat = CFF;
+ }
+
+ if (eFormat == TYPE1)
+ {
+ // check for corresponding afm metric
+ // first look for an adjacent file
+ static const char* pSuffix[] = { ".afm", ".AFM" };
+
+ for( unsigned int i = 0; i < SAL_N_ELEMENTS(pSuffix); i++ )
+ {
+ OString aName = OStringBuffer(
+ rFontFile.copy(0, rFontFile.getLength() - 4)).
+ append(pSuffix[i]).makeStringAndClear();
+
+ OStringBuffer aFilePath(aDir);
+ aFilePath.append('/').append(aName);
+
+ OString aAfmFile;
+ if( access( aFilePath.makeStringAndClear().getStr(), R_OK ) )
+ {
+ // try in subdirectory afm instead
+ aFilePath.append(aDir).append("/afm/").append(aName);
+
+ if (!access(aFilePath.getStr(), R_OK))
+ aAfmFile = OString("afm/") + aName;
+ }
+ else
+ aAfmFile = aName;
+
+ if( !aAfmFile.isEmpty() )
+ {
+ Type1FontFile* pFont = new Type1FontFile();
+ pFont->m_nDirectory = nDirID;
+
+ pFont->m_aFontFile = rFontFile;
+ pFont->m_aMetricFile = aAfmFile;
+
+ if( ! pFont->readAfmMetrics( m_pAtoms, false, true ) )
+ {
+ delete pFont;
+ pFont = nullptr;
+ }
+ if( pFont )
+ rNewFonts.push_back( pFont );
+ break;
+ }
+ }
+ }
+ else if (eFormat == TRUETYPE || eFormat == CFF)
+ {
+ // get number of ttc entries
+ int nLength = CountTTCFonts( aFullPath.getStr() );
+ if (nLength > 0)
+ {
+#if OSL_DEBUG_LEVEL > 1
+ fprintf( stderr, "ttc: %s contains %d fonts\n", aFullPath.getStr(), nLength );
+#endif
+
+ sal_uInt64 fileSize = 0;
+
+ OUString aURL;
+ if (osl::File::getFileURLFromSystemPath(OStringToOUString(aFullPath, osl_getThreadTextEncoding()),
+ aURL) == osl::File::E_None)
+ {
+ osl::File aFile(aURL);
+ if (aFile.open(osl_File_OpenFlag_Read | osl_File_OpenFlag_NoLock) == osl::File::E_None)
+ {
+ osl::DirectoryItem aItem;
+ osl::DirectoryItem::get( aURL, aItem );
+ osl::FileStatus aFileStatus( osl_FileStatus_Mask_FileSize );
+ aItem.getFileStatus( aFileStatus );
+ fileSize = aFileStatus.getFileSize();
+ }
+ }
+
+ //Feel free to calc the exact max possible number of fonts a file
+ //could contain given its physical size. But this will clamp it to
+ //a sane starting point
+ //http://processingjs.nihongoresources.com/the_smallest_font/
+ //https://github.com/grzegorzrolek/null-ttf
+ const int nMaxFontsPossible = fileSize / 528;
+ if (nLength > nMaxFontsPossible)
+ nLength = nMaxFontsPossible;
+
+ for( int i = 0; i < nLength; i++ )
+ {
+ TrueTypeFontFile* pFont = new TrueTypeFontFile();
+ pFont->m_nDirectory = nDirID;
+ pFont->m_aFontFile = rFontFile;
+ pFont->m_nCollectionEntry = i;
+ if( ! analyzeTrueTypeFile( pFont ) )
+ {
+ delete pFont;
+ pFont = nullptr;
+ }
+ else
+ rNewFonts.push_back( pFont );
+ }
+ }
+ else
+ {
+ TrueTypeFontFile* pFont = new TrueTypeFontFile();
+ pFont->m_nDirectory = nDirID;
+ pFont->m_aFontFile = rFontFile;
+ pFont->m_nCollectionEntry = 0;
+
+ // need to read the font anyway to get aliases inside the font file
+ if( ! analyzeTrueTypeFile( pFont ) )
+ {
+ delete pFont;
+ pFont = nullptr;
+ }
+ else
+ rNewFonts.push_back( pFont );
+ }
+ }
+ return ! rNewFonts.empty();
+}
+
+fontID PrintFontManager::findFontFileID( int nDirID, const OString& rFontFile, int nFaceIndex ) const
+{
+ fontID nID = 0;
+
+ std::unordered_map< OString, ::std::set< fontID >, OStringHash >::const_iterator set_it = m_aFontFileToFontID.find( rFontFile );
+ if( set_it == m_aFontFileToFontID.end() )
+ return nID;
+
+ for( ::std::set< fontID >::const_iterator font_it = set_it->second.begin(); font_it != set_it->second.end() && ! nID; ++font_it )
+ {
+ std::unordered_map< fontID, PrintFont* >::const_iterator it = m_aFonts.find( *font_it );
+ if( it == m_aFonts.end() )
+ continue;
+ switch( it->second->m_eType )
+ {
+ case fonttype::Type1:
+ {
+ Type1FontFile* const pFont = static_cast< Type1FontFile* const >((*it).second);
+ if( pFont->m_nDirectory == nDirID &&
+ pFont->m_aFontFile == rFontFile )
+ nID = it->first;
+ }
+ break;
+ case fonttype::TrueType:
+ {
+ TrueTypeFontFile* const pFont = static_cast< TrueTypeFontFile* const >((*it).second);
+ if( pFont->m_nDirectory == nDirID &&
+ pFont->m_aFontFile == rFontFile && pFont->m_nCollectionEntry == nFaceIndex )
+ nID = it->first;
+ }
+ break;
+ default:
+ break;
+ }
+ }
+
+ return nID;
+}
+
+std::vector<fontID> PrintFontManager::findFontFileIDs( int nDirID, const OString& rFontFile ) const
+{
+ std::vector<fontID> aIds;
+
+ std::unordered_map< OString, ::std::set< fontID >, OStringHash >::const_iterator set_it = m_aFontFileToFontID.find( rFontFile );
+ if( set_it == m_aFontFileToFontID.end() )
+ return aIds;
+
+ for( ::std::set< fontID >::const_iterator font_it = set_it->second.begin(); font_it != set_it->second.end(); ++font_it )
+ {
+ std::unordered_map< fontID, PrintFont* >::const_iterator it = m_aFonts.find( *font_it );
+ if( it == m_aFonts.end() )
+ continue;
+ switch( it->second->m_eType )
+ {
+ case fonttype::Type1:
+ {
+ Type1FontFile* const pFont = static_cast< Type1FontFile* const >((*it).second);
+ if( pFont->m_nDirectory == nDirID &&
+ pFont->m_aFontFile == rFontFile )
+ aIds.push_back(it->first);
+ }
+ break;
+ case fonttype::TrueType:
+ {
+ TrueTypeFontFile* const pFont = static_cast< TrueTypeFontFile* const >((*it).second);
+ if( pFont->m_nDirectory == nDirID &&
+ pFont->m_aFontFile == rFontFile )
+ aIds.push_back(it->first);
+ }
+ break;
+ default:
+ break;
+ }
+ }
+
+ return aIds;
+}
+
+OUString PrintFontManager::convertTrueTypeName( void* pRecord )
+{
+ NameRecord* pNameRecord = static_cast<NameRecord*>(pRecord);
+ OUString aValue;
+ if(
+ ( pNameRecord->platformID == 3 && ( pNameRecord->encodingID == 0 || pNameRecord->encodingID == 1 ) ) // MS, Unicode
+ ||
+ ( pNameRecord->platformID == 0 ) // Apple, Unicode
+ )
+ {
+ OUStringBuffer aName( pNameRecord->slen/2 );
+ const sal_uInt8* pNameBuffer = pNameRecord->sptr;
+ for(int n = 0; n < pNameRecord->slen/2; n++ )
+ aName.append( (sal_Unicode)getUInt16BE( pNameBuffer ) );
+ aValue = aName.makeStringAndClear();
+ }
+ else if( pNameRecord->platformID == 3 )
+ {
+ if( pNameRecord->encodingID >= 2 && pNameRecord->encodingID <= 6 )
+ {
+ /*
+ * and now for a special kind of madness:
+ * some fonts encode their byte value string as BE uint16
+ * (leading to stray zero bytes in the string)
+ * while others code two bytes as a uint16 and swap to BE
+ */
+ OStringBuffer aName;
+ const sal_uInt8* pNameBuffer = pNameRecord->sptr;
+ for(int n = 0; n < pNameRecord->slen/2; n++ )
+ {
+ sal_Unicode aCode = (sal_Unicode)getUInt16BE( pNameBuffer );
+ sal_Char aChar = aCode >> 8;
+ if( aChar )
+ aName.append( aChar );
+ aChar = aCode & 0x00ff;
+ if( aChar )
+ aName.append( aChar );
+ }
+ switch( pNameRecord->encodingID )
+ {
+ case 2:
+ aValue = OStringToOUString( aName.makeStringAndClear(), RTL_TEXTENCODING_MS_932 );
+ break;
+ case 3:
+ aValue = OStringToOUString( aName.makeStringAndClear(), RTL_TEXTENCODING_MS_936 );
+ break;
+ case 4:
+ aValue = OStringToOUString( aName.makeStringAndClear(), RTL_TEXTENCODING_MS_950 );
+ break;
+ case 5:
+ aValue = OStringToOUString( aName.makeStringAndClear(), RTL_TEXTENCODING_MS_949 );
+ break;
+ case 6:
+ aValue = OStringToOUString( aName.makeStringAndClear(), RTL_TEXTENCODING_MS_1361 );
+ break;
+ }
+ }
+ }
+ else if( pNameRecord->platformID == 1 )
+ {
+ OString aName(reinterpret_cast<char*>(pNameRecord->sptr), pNameRecord->slen);
+ rtl_TextEncoding eEncoding = RTL_TEXTENCODING_DONTKNOW;
+ switch (pNameRecord->encodingID)
+ {
+ case 0:
+ eEncoding = RTL_TEXTENCODING_APPLE_ROMAN;
+ break;
+ case 1:
+ eEncoding = RTL_TEXTENCODING_APPLE_JAPANESE;
+ break;
+ case 2:
+ eEncoding = RTL_TEXTENCODING_APPLE_CHINTRAD;
+ break;
+ case 3:
+ eEncoding = RTL_TEXTENCODING_APPLE_KOREAN;
+ break;
+ case 4:
+ eEncoding = RTL_TEXTENCODING_APPLE_ARABIC;
+ break;
+ case 5:
+ eEncoding = RTL_TEXTENCODING_APPLE_HEBREW;
+ break;
+ case 6:
+ eEncoding = RTL_TEXTENCODING_APPLE_GREEK;
+ break;
+ case 7:
+ eEncoding = RTL_TEXTENCODING_APPLE_CYRILLIC;
+ break;
+ case 9:
+ eEncoding = RTL_TEXTENCODING_APPLE_DEVANAGARI;
+ break;
+ case 10:
+ eEncoding = RTL_TEXTENCODING_APPLE_GURMUKHI;
+ break;
+ case 11:
+ eEncoding = RTL_TEXTENCODING_APPLE_GUJARATI;
+ break;
+ case 21:
+ eEncoding = RTL_TEXTENCODING_APPLE_THAI;
+ break;
+ case 25:
+ eEncoding = RTL_TEXTENCODING_APPLE_CHINSIMP;
+ break;
+ case 29:
+ eEncoding = RTL_TEXTENCODING_APPLE_CENTEURO;
+ break;
+ case 32: //Uninterpreted
+ eEncoding = RTL_TEXTENCODING_UTF8;
+ break;
+ default:
+ SAL_WARN("vcl", "Unimplemented mac encoding " << pNameRecord->encodingID << " to unicode conversion");
+ break;
+ }
+ if (eEncoding != RTL_TEXTENCODING_DONTKNOW)
+ aValue = OStringToOUString(aName, eEncoding);
+ }
+
+ return aValue;
+}
+
+//fdo#33349.There exists an archaic Berling Antiqua font which has a "Times New
+//Roman" name field in it. We don't want the "Times New Roman" name to take
+//precedence in this case. We take Berling Antiqua as a higher priority name,
+//and erase the "Times New Roman" name
+namespace
+{
+ bool isBadTNR(const OUString &rName, ::std::set< OUString >& rSet)
+ {
+ bool bRet = false;
+ if ( rName == "Berling Antiqua" )
+ {
+ ::std::set< OUString >::iterator aEnd = rSet.end();
+ ::std::set< OUString >::iterator aI = rSet.find("Times New Roman");
+ if (aI != aEnd)
+ {
+ bRet = true;
+ rSet.erase(aI);
+ }
+ }
+ return bRet;
+ }
+}
+
+void PrintFontManager::analyzeTrueTypeFamilyName( void* pTTFont, ::std::list< OUString >& rNames )
+{
+ OUString aFamily;
+
+ rNames.clear();
+ ::std::set< OUString > aSet;
+
+ NameRecord* pNameRecords = nullptr;
+ int nNameRecords = GetTTNameRecords( static_cast<TrueTypeFont*>(pTTFont), &pNameRecords );
+ if( nNameRecords && pNameRecords )
+ {
+ LanguageTag aSystem("");
+ LanguageType eLang = aSystem.getLanguageType();
+ int nLastMatch = -1;
+ for( int i = 0; i < nNameRecords; i++ )
+ {
+ if( pNameRecords[i].nameID != 1 || pNameRecords[i].sptr == nullptr )
+ continue;
+ int nMatch = -1;
+ if( pNameRecords[i].platformID == 0 ) // Unicode
+ nMatch = 4000;
+ else if( pNameRecords[i].platformID == 3 )
+ {
+ // this bases on the LanguageType actually being a Win LCID
+ if (pNameRecords[i].languageID == eLang)
+ nMatch = 8000;
+ else if( pNameRecords[i].languageID == LANGUAGE_ENGLISH_US )
+ nMatch = 2000;
+ else if( pNameRecords[i].languageID == LANGUAGE_ENGLISH ||
+ pNameRecords[i].languageID == LANGUAGE_ENGLISH_UK )
+ nMatch = 1500;
+ else
+ nMatch = 1000;
+ }
+ else if (pNameRecords[i].platformID == 1)
+ {
+ AppleLanguageId aAppleId = static_cast<AppleLanguageId>(pNameRecords[i].languageID);
+ LanguageTag aApple(makeLanguageTagFromAppleLanguageId(aAppleId));
+ if (aApple == aSystem)
+ nMatch = 8000;
+ else if (aAppleId == AppleLanguageId::ENGLISH)
+ nMatch = 2000;
+ else
+ nMatch = 1000;
+ }
+ OUString aName = convertTrueTypeName( pNameRecords + i );
+ aSet.insert( aName );
+ if (aName.isEmpty())
+ continue;
+ if( nMatch > nLastMatch || isBadTNR(aName, aSet) )
+ {
+ nLastMatch = nMatch;
+ aFamily = aName;
+ }
+ }
+ }
+ DisposeNameRecords( pNameRecords, nNameRecords );
+ if( !aFamily.isEmpty() )
+ {
+ rNames.push_front( aFamily );
+ for( ::std::set< OUString >::const_iterator it = aSet.begin(); it != aSet.end(); ++it )
+ if( *it != aFamily )
+ rNames.push_back( *it );
+ }
+ return;
+}
+
+bool PrintFontManager::analyzeTrueTypeFile( PrintFont* pFont ) const
+{
+ bool bSuccess = false;
+ rtl_TextEncoding aEncoding = osl_getThreadTextEncoding();
+ OString aFile = getFontFile( pFont );
+ TrueTypeFont* pTTFont = nullptr;
+
+ TrueTypeFontFile* pTTFontFile = static_cast< TrueTypeFontFile* >(pFont);
+ if( OpenTTFontFile( aFile.getStr(), pTTFontFile->m_nCollectionEntry, &pTTFont ) == SF_OK )
+ {
+ TTGlobalFontInfo aInfo;
+ GetTTGlobalFontInfo( pTTFont, & aInfo );
+
+ ::std::list< OUString > aNames;
+ analyzeTrueTypeFamilyName( pTTFont, aNames );
+
+ // set family name from XLFD if possible
+ if( ! pFont->m_nFamilyName )
+ {
+ if( !aNames.empty() )
+ {
+ pFont->m_nFamilyName = m_pAtoms->getAtom( ATOM_FAMILYNAME, aNames.front(), true );
+ aNames.pop_front();
+ }
+ else
+ {
+ sal_Int32 dotIndex;
+
+ // poor font does not have a family name
+ // name it to file name minus the extension
+ dotIndex = pTTFontFile->m_aFontFile.lastIndexOf( '.' );
+ if ( dotIndex == -1 )
+ dotIndex = pTTFontFile->m_aFontFile.getLength();
+
+ pFont->m_nFamilyName = m_pAtoms->getAtom( ATOM_FAMILYNAME, OStringToOUString( pTTFontFile->m_aFontFile.copy( 0, dotIndex ), aEncoding ), true );
+ }
+ }
+ for( ::std::list< OUString >::iterator it = aNames.begin(); it != aNames.end(); ++it )
+ {
+ if( !it->isEmpty() )
+ {
+ int nAlias = m_pAtoms->getAtom( ATOM_FAMILYNAME, *it, true );
+ if( nAlias != pFont->m_nFamilyName )
+ {
+ std::list< int >::const_iterator al_it;
+ for( al_it = pFont->m_aAliases.begin(); al_it != pFont->m_aAliases.end() && *al_it != nAlias; ++al_it )
+ ;
+ if( al_it == pFont->m_aAliases.end() )
+ pFont->m_aAliases.push_back( nAlias );
+ }
+ }
+ }
+
+ if( aInfo.usubfamily )
+ pFont->m_aStyleName = OUString( aInfo.usubfamily );
+
+ SAL_WARN_IF( !aInfo.psname, "vcl", "No PostScript name in font:" << aFile.getStr() );
+
+ OUString sPSName = aInfo.psname ?
+ OUString(aInfo.psname, rtl_str_getLength(aInfo.psname), aEncoding) :
+ m_pAtoms->getString(ATOM_FAMILYNAME, pFont->m_nFamilyName); // poor font does not have a postscript name
+
+ pFont->m_nPSName = m_pAtoms->getAtom( ATOM_PSNAME, sPSName, true );
+
+ switch( aInfo.weight )
+ {
+ case FW_THIN: pFont->m_eWeight = WEIGHT_THIN; break;
+ case FW_EXTRALIGHT: pFont->m_eWeight = WEIGHT_ULTRALIGHT; break;
+ case FW_LIGHT: pFont->m_eWeight = WEIGHT_LIGHT; break;
+ case FW_MEDIUM: pFont->m_eWeight = WEIGHT_MEDIUM; break;
+ case FW_SEMIBOLD: pFont->m_eWeight = WEIGHT_SEMIBOLD; break;
+ case FW_BOLD: pFont->m_eWeight = WEIGHT_BOLD; break;
+ case FW_EXTRABOLD: pFont->m_eWeight = WEIGHT_ULTRABOLD; break;
+ case FW_BLACK: pFont->m_eWeight = WEIGHT_BLACK; break;
+
+ case FW_NORMAL:
+ default: pFont->m_eWeight = WEIGHT_NORMAL; break;
+ }
+
+ switch( aInfo.width )
+ {
+ case FWIDTH_ULTRA_CONDENSED: pFont->m_eWidth = WIDTH_ULTRA_CONDENSED; break;
+ case FWIDTH_EXTRA_CONDENSED: pFont->m_eWidth = WIDTH_EXTRA_CONDENSED; break;
+ case FWIDTH_CONDENSED: pFont->m_eWidth = WIDTH_CONDENSED; break;
+ case FWIDTH_SEMI_CONDENSED: pFont->m_eWidth = WIDTH_SEMI_CONDENSED; break;
+ case FWIDTH_SEMI_EXPANDED: pFont->m_eWidth = WIDTH_SEMI_EXPANDED; break;
+ case FWIDTH_EXPANDED: pFont->m_eWidth = WIDTH_EXPANDED; break;
+ case FWIDTH_EXTRA_EXPANDED: pFont->m_eWidth = WIDTH_EXTRA_EXPANDED; break;
+ case FWIDTH_ULTRA_EXPANDED: pFont->m_eWidth = WIDTH_ULTRA_EXPANDED; break;
+
+ case FWIDTH_NORMAL:
+ default: pFont->m_eWidth = WIDTH_NORMAL; break;
+ }
+
+ pFont->m_ePitch = aInfo.pitch ? PITCH_FIXED : PITCH_VARIABLE;
+ pFont->m_eItalic = aInfo.italicAngle == 0 ? ITALIC_NONE : ( aInfo.italicAngle < 0 ? ITALIC_NORMAL : ITALIC_OBLIQUE );
+ // #104264# there are fonts that set italic angle 0 although they are
+ // italic; use macstyle bit here
+ if( aInfo.italicAngle == 0 && (aInfo.macStyle & 2) )
+ pFont->m_eItalic = ITALIC_NORMAL;
+
+ pFont->m_aEncoding = aInfo.symbolEncoded ? RTL_TEXTENCODING_SYMBOL : RTL_TEXTENCODING_UCS2;
+
+ pFont->m_aGlobalMetricY.width = pFont->m_aGlobalMetricX.width = aInfo.xMax - aInfo.xMin;
+ pFont->m_aGlobalMetricY.height = pFont->m_aGlobalMetricX.height = aInfo.yMax - aInfo.yMin;
+
+ if( aInfo.winAscent && aInfo.winDescent )
+ {
+ pFont->m_nAscend = aInfo.winAscent;
+ pFont->m_nDescend = aInfo.winDescent;
+ pFont->m_nLeading = pFont->m_nAscend + pFont->m_nDescend - 1000;
+ }
+ else if( aInfo.typoAscender && aInfo.typoDescender )
+ {
+ pFont->m_nLeading = aInfo.typoLineGap;
+ pFont->m_nAscend = aInfo.typoAscender;
+ pFont->m_nDescend = -aInfo.typoDescender;
+ }
+ else
+ {
+ pFont->m_nLeading = aInfo.linegap;
+ pFont->m_nAscend = aInfo.ascender;
+ pFont->m_nDescend = -aInfo.descender;
+ }
+
+ // last try: font bounding box
+ if( pFont->m_nAscend == 0 )
+ pFont->m_nAscend = aInfo.yMax;
+ if( pFont->m_nDescend == 0 )
+ pFont->m_nDescend = -aInfo.yMin;
+ if( pFont->m_nLeading == 0 )
+ pFont->m_nLeading = 15 * (pFont->m_nAscend+pFont->m_nDescend) / 100;
+
+ if( pFont->m_nAscend )
+ pFont->m_aGlobalMetricX.height = pFont->m_aGlobalMetricY.height = pFont->m_nAscend + pFont->m_nDescend;
+
+ // get bounding box
+ pFont->m_nXMin = aInfo.xMin;
+ pFont->m_nYMin = aInfo.yMin;
+ pFont->m_nXMax = aInfo.xMax;
+ pFont->m_nYMax = aInfo.yMax;
+
+ // get type flags
+ pTTFontFile->m_nTypeFlags = (unsigned int)aInfo.typeFlags;
+
+ // get vertical substitutions flag
+ pFont->m_bHaveVerticalSubstitutedGlyphs = DoesVerticalSubstitution( pTTFont, 1 );
+
+ CloseTTFont( pTTFont );
+ bSuccess = true;
+ }
+ else
+ SAL_WARN("vcl", "Could not OpenTTFont \"" << aFile.getStr() << "\"\n");
+
+ return bSuccess;
+}
+
+static bool AreFCSubstitutionsEnabled()
+{
+ return (SalGenericInstance::FetchFontSubstitutionFlags() & 3) == 0;
+}
+
+void PrintFontManager::initialize()
+{
+ #ifdef CALLGRIND_COMPILE
+ CALLGRIND_TOGGLE_COLLECT();
+ CALLGRIND_ZERO_STATS();
+ #endif
+
+ if( ! m_pFontCache )
+ {
+#if OSL_DEBUG_LEVEL > 1
+ fprintf( stderr, "creating font cache ... " );
+ clock_t aStart;
+ struct tms tms;
+ aStart = times( &tms );
+#endif
+ m_pFontCache = new FontCache();
+#if OSL_DEBUG_LEVEL > 1
+ clock_t aStop = times( &tms );
+ fprintf( stderr, "done in %lf s\n", (double)(aStop - aStart)/(double)sysconf( _SC_CLK_TCK ) );
+#endif
+ }
+
+ // initialize can be called more than once, e.g.
+ // gtk-fontconfig-timestamp changes to reflect new font installed and
+ // PrintFontManager::initialize called again
+ {
+ for( std::unordered_map< fontID, PrintFont* >::const_iterator it = m_aFonts.begin(); it != m_aFonts.end(); ++it )
+ delete (*it).second;
+ m_nNextFontID = 1;
+ m_aFonts.clear();
+ m_aFontDirectories.clear();
+ m_aPrivateFontDirectories.clear();
+ }
+
+#if OSL_DEBUG_LEVEL > 1
+ clock_t aStart;
+ clock_t aStep1;
+ clock_t aStep2;
+
+ struct tms tms;
+
+ aStart = times( &tms );
+#endif
+
+ // first try fontconfig
+ initFontconfig();
+
+ // part one - look for downloadable fonts
+ rtl_TextEncoding aEncoding = osl_getThreadTextEncoding();
+ const OUString &rSalPrivatePath = psp::getFontPath();
+
+ // search for the fonts in SAL_PRIVATE_FONTPATH first; those are
+ // the fonts installed with the office
+ if( !rSalPrivatePath.isEmpty() )
+ {
+ OString aPath = OUStringToOString( rSalPrivatePath, aEncoding );
+ const bool bAreFCSubstitutionsEnabled = AreFCSubstitutionsEnabled();
+ sal_Int32 nIndex = 0;
+ do
+ {
+ OString aToken = aPath.getToken( 0, ';', nIndex );
+ normPath( aToken );
+ if ( aToken.isEmpty() )
+ {
+ continue;
+ }
+ // if registering an app-specific fontdir with fontconfig fails
+ // and fontconfig-based substitutions are enabled
+ // then trying to use these app-specific fonts doesn't make sense
+ if( !addFontconfigDir( aToken ) )
+ if( bAreFCSubstitutionsEnabled )
+ continue;
+ m_aFontDirectories.push_back( aToken );
+ m_aPrivateFontDirectories.push_back( getDirectoryAtom( aToken, true ) );
+ } while( nIndex >= 0 );
+ }
+
+ // protect against duplicate paths
+ std::unordered_map< OString, int, OStringHash > visited_dirs;
+
+ // Don't search directories that fontconfig already did
+ countFontconfigFonts( visited_dirs );
+
+ // search for font files in each path
+ std::list< OString >::iterator dir_it;
+ for( dir_it = m_aFontDirectories.begin(); dir_it != m_aFontDirectories.end(); ++dir_it )
+ {
+ OString aPath( *dir_it );
+ // see if we were here already
+ if( visited_dirs.find( aPath ) != visited_dirs.end() )
+ continue;
+ visited_dirs[ aPath ] = 1;
+
+ // there may be ":unscaled" directories (see XFree86)
+ // it should be safe to ignore them since they should not
+ // contain any of our recognizeable fonts
+
+ // ask the font cache whether it handles this directory
+ std::list< PrintFont* > aCacheFonts;
+ if( m_pFontCache->listDirectory( aPath, aCacheFonts ) )
+ {
+#if OSL_DEBUG_LEVEL > 1
+ fprintf( stderr, "adding cache directory: %s\n", aPath.getStr() );
+#endif
+ for( ::std::list< PrintFont* >::iterator it = aCacheFonts.begin(); it != aCacheFonts.end(); ++it )
+ {
+ fontID aFont = m_nNextFontID++;
+ m_aFonts[ aFont ] = *it;
+ if( (*it)->m_eType == fonttype::Type1 )
+ m_aFontFileToFontID[ static_cast<Type1FontFile*>(*it)->m_aFontFile ].insert( aFont );
+ else if( (*it)->m_eType == fonttype::TrueType )
+ m_aFontFileToFontID[ static_cast<TrueTypeFontFile*>(*it)->m_aFontFile ].insert( aFont );
+#if OSL_DEBUG_LEVEL > 1
+ else
+ fprintf(stderr, "Un-cached type '%d'\n", (*it)->m_eType);
+#if OSL_DEBUG_LEVEL > 2
+ fprintf( stderr, "adding cached font %d: %s\n", aFont, getFontFileSysPath( aFont ).getStr() );
+#endif
+#endif
+ }
+ if( ! m_pFontCache->scanAdditionalFiles( aPath ) )
+ continue;
+ }
+
+ }
+
+#if OSL_DEBUG_LEVEL > 1
+ aStep1 = times( &tms );
+#endif
+
+ // part three - fill in family styles
+ std::unordered_map< fontID, PrintFont* >::iterator font_it;
+ for (font_it = m_aFonts.begin(); font_it != m_aFonts.end(); ++font_it)
+ {
+ std::unordered_map< int, FontFamily >::const_iterator it =
+ m_aFamilyTypes.find( font_it->second->m_nFamilyName );
+ if (it != m_aFamilyTypes.end())
+ continue;
+ const OUString& rFamily =
+ m_pAtoms->getString( ATOM_FAMILYNAME, font_it->second->m_nFamilyName);
+ FontFamily eType = matchFamilyName( rFamily );
+ m_aFamilyTypes[ font_it->second->m_nFamilyName ] = eType;
+ }
+
+#if OSL_DEBUG_LEVEL > 1
+ aStep2 = times( &tms );
+ fprintf( stderr, "PrintFontManager::initialize: collected %" SAL_PRI_SIZET "u fonts\n", m_aFonts.size() );
+ double fTick = (double)sysconf( _SC_CLK_TCK );
+ fprintf( stderr, "Step 1 took %lf seconds\n", (double)(aStep1 - aStart)/fTick );
+ fprintf( stderr, "Step 2 took %lf seconds\n", (double)(aStep2 - aStep1)/fTick );
+#endif
+
+ m_pFontCache->flush();
+
+ #ifdef CALLGRIND_COMPILE
+ CALLGRIND_DUMP_STATS();
+ CALLGRIND_TOGGLE_COLLECT();
+ #endif
+}
+
+void PrintFontManager::getFontList( ::std::list< fontID >& rFontIDs )
+{
+ rFontIDs.clear();
+ std::unordered_map< fontID, PrintFont* >::const_iterator it;
+
+ for( it = m_aFonts.begin(); it != m_aFonts.end(); ++it )
+ rFontIDs.push_back( it->first );
+}
+
+void PrintFontManager::fillPrintFontInfo( PrintFont* pFont, FastPrintFontInfo& rInfo ) const
+{
+ std::unordered_map< int, FontFamily >::const_iterator style_it =
+ m_aFamilyTypes.find( pFont->m_nFamilyName );
+ rInfo.m_eType = pFont->m_eType;
+ rInfo.m_aFamilyName = m_pAtoms->getString( ATOM_FAMILYNAME, pFont->m_nFamilyName );
+ rInfo.m_aStyleName = pFont->m_aStyleName;
+ rInfo.m_eFamilyStyle = style_it != m_aFamilyTypes.end() ? style_it->second : FAMILY_DONTKNOW;
+ rInfo.m_eItalic = pFont->m_eItalic;
+ rInfo.m_eWidth = pFont->m_eWidth;
+ rInfo.m_eWeight = pFont->m_eWeight;
+ rInfo.m_ePitch = pFont->m_ePitch;
+ rInfo.m_aEncoding = pFont->m_aEncoding;
+
+ rInfo.m_bEmbeddable = (pFont->m_eType == fonttype::Type1);
+ rInfo.m_bSubsettable = (pFont->m_eType == fonttype::TrueType); // TODO: rename to SfntType
+
+ rInfo.m_aAliases.clear();
+ for( ::std::list< int >::iterator it = pFont->m_aAliases.begin(); it != pFont->m_aAliases.end(); ++it )
+ rInfo.m_aAliases.push_back( m_pAtoms->getString( ATOM_FAMILYNAME, *it ) );
+}
+
+void PrintFontManager::fillPrintFontInfo( PrintFont* pFont, PrintFontInfo& rInfo ) const
+{
+ if( ( pFont->m_nAscend == 0 && pFont->m_nDescend == 0 ) ||
+ ! pFont->m_pMetrics || pFont->m_pMetrics->isEmpty()
+ )
+ {
+ // might be a truetype font not analyzed or type1 without metrics read
+ if( pFont->m_eType == fonttype::Type1 )
+ pFont->readAfmMetrics( m_pAtoms, false, false );
+ else if( pFont->m_eType == fonttype::TrueType )
+ analyzeTrueTypeFile( pFont );
+ }
+
+ fillPrintFontInfo( pFont, static_cast< FastPrintFontInfo& >( rInfo ) );
+
+ rInfo.m_nAscend = pFont->m_nAscend;
+ rInfo.m_nDescend = pFont->m_nDescend;
+ rInfo.m_nLeading = pFont->m_nLeading;
+ rInfo.m_nWidth = pFont->m_aGlobalMetricX.width < pFont->m_aGlobalMetricY.width ? pFont->m_aGlobalMetricY.width : pFont->m_aGlobalMetricX.width;
+}
+
+bool PrintFontManager::getFontInfo( fontID nFontID, PrintFontInfo& rInfo ) const
+{
+ PrintFont* pFont = getFont( nFontID );
+ if( pFont )
+ {
+ rInfo.m_nID = nFontID;
+ fillPrintFontInfo( pFont, rInfo );
+ }
+ return pFont != nullptr;
+}
+
+bool PrintFontManager::getFontFastInfo( fontID nFontID, FastPrintFontInfo& rInfo ) const
+{
+ PrintFont* pFont = getFont( nFontID );
+ if( pFont )
+ {
+ rInfo.m_nID = nFontID;
+ fillPrintFontInfo( pFont, rInfo );
+ }
+ return pFont != nullptr;
+}
+
+bool PrintFontManager::getFontBoundingBox( fontID nFontID, int& xMin, int& yMin, int& xMax, int& yMax )
+{
+ bool bSuccess = false;
+ PrintFont* pFont = getFont( nFontID );
+ if( pFont )
+ {
+ if( pFont->m_nXMin == 0 && pFont->m_nYMin == 0 && pFont->m_nXMax == 0 && pFont->m_nYMax == 0 )
+ {
+ // might be a truetype font not analyzed or type1 without metrics read
+ if( pFont->m_eType == fonttype::Type1 )
+ pFont->readAfmMetrics( m_pAtoms, false, true );
+ else if( pFont->m_eType == fonttype::TrueType )
+ analyzeTrueTypeFile( pFont );
+ }
+ bSuccess = true;
+ xMin = pFont->m_nXMin;
+ yMin = pFont->m_nYMin;
+ xMax = pFont->m_nXMax;
+ yMax = pFont->m_nYMax;
+ }
+ return bSuccess;
+}
+
+int PrintFontManager::getFontFaceNumber( fontID nFontID ) const
+{
+ int nRet = 0;
+ PrintFont* pFont = getFont( nFontID );
+ if( pFont && pFont->m_eType == fonttype::TrueType )
+ nRet = static_cast< TrueTypeFontFile* >(pFont)->m_nCollectionEntry;
+ if (nRet < 0)
+ nRet = 0;
+ return nRet;
+}
+
+FontFamily PrintFontManager::matchFamilyName( const OUString& rFamily )
+{
+ typedef struct {
+ const char* mpName;
+ sal_uInt16 mnLength;
+ FontFamily meType;
+ } family_t;
+
+#define InitializeClass( p, a ) p, sizeof(p) - 1, a
+ const family_t pFamilyMatch[] = {
+ { InitializeClass( "arial", FAMILY_SWISS ) },
+ { InitializeClass( "arioso", FAMILY_SCRIPT ) },
+ { InitializeClass( "avant garde", FAMILY_SWISS ) },
+ { InitializeClass( "avantgarde", FAMILY_SWISS ) },
+ { InitializeClass( "bembo", FAMILY_ROMAN ) },
+ { InitializeClass( "bookman", FAMILY_ROMAN ) },
+ { InitializeClass( "conga", FAMILY_ROMAN ) },
+ { InitializeClass( "courier", FAMILY_MODERN ) },
+ { InitializeClass( "curl", FAMILY_SCRIPT ) },
+ { InitializeClass( "fixed", FAMILY_MODERN ) },
+ { InitializeClass( "gill", FAMILY_SWISS ) },
+ { InitializeClass( "helmet", FAMILY_MODERN ) },
+ { InitializeClass( "helvetica", FAMILY_SWISS ) },
+ { InitializeClass( "international", FAMILY_MODERN ) },
+ { InitializeClass( "lucida", FAMILY_SWISS ) },
+ { InitializeClass( "new century schoolbook", FAMILY_ROMAN ) },
+ { InitializeClass( "palatino", FAMILY_ROMAN ) },
+ { InitializeClass( "roman", FAMILY_ROMAN ) },
+ { InitializeClass( "sans serif", FAMILY_SWISS ) },
+ { InitializeClass( "sansserif", FAMILY_SWISS ) },
+ { InitializeClass( "serf", FAMILY_ROMAN ) },
+ { InitializeClass( "serif", FAMILY_ROMAN ) },
+ { InitializeClass( "times", FAMILY_ROMAN ) },
+ { InitializeClass( "utopia", FAMILY_ROMAN ) },
+ { InitializeClass( "zapf chancery", FAMILY_SCRIPT ) },
+ { InitializeClass( "zapfchancery", FAMILY_SCRIPT ) }
+ };
+
+ OString aFamily = OUStringToOString( rFamily, RTL_TEXTENCODING_ASCII_US );
+ sal_uInt32 nLower = 0;
+ sal_uInt32 nUpper = SAL_N_ELEMENTS(pFamilyMatch);
+
+ while( nLower < nUpper )
+ {
+ sal_uInt32 nCurrent = (nLower + nUpper) / 2;
+ const family_t* pHaystack = pFamilyMatch + nCurrent;
+ sal_Int32 nComparison =
+ rtl_str_compareIgnoreAsciiCase_WithLength
+ (
+ aFamily.getStr(), aFamily.getLength(),
+ pHaystack->mpName, pHaystack->mnLength
+ );
+
+ if( nComparison < 0 )
+ nUpper = nCurrent;
+ else
+ if( nComparison > 0 )
+ nLower = nCurrent + 1;
+ else
+ return pHaystack->meType;
+ }
+
+ return FAMILY_DONTKNOW;
+}
+
+OString PrintFontManager::getAfmFile( PrintFont* pFont ) const
+{
+ OString aMetricPath;
+ if( pFont )
+ {
+ switch( pFont->m_eType )
+ {
+ case fonttype::Type1:
+ {
+ Type1FontFile* pPSFont = static_cast< Type1FontFile* >(pFont);
+ aMetricPath = getDirectory( pPSFont->m_nDirectory );
+ aMetricPath += "/";
+ aMetricPath += pPSFont->m_aMetricFile;
+ }
+ break;
+ default: break;
+ }
+ }
+ return aMetricPath;
+}
+
+OString PrintFontManager::getFontFile( PrintFont* pFont ) const
+{
+ OString aPath;
+
+ if( pFont && pFont->m_eType == fonttype::Type1 )
+ {
+ Type1FontFile* pPSFont = static_cast< Type1FontFile* >(pFont);
+ std::unordered_map< int, OString >::const_iterator it = m_aAtomToDir.find( pPSFont->m_nDirectory );
+ aPath = it->second;
+ aPath += "/";
+ aPath += pPSFont->m_aFontFile;
+ }
+ else if( pFont && pFont->m_eType == fonttype::TrueType )
+ {
+ TrueTypeFontFile* pTTFont = static_cast< TrueTypeFontFile* >(pFont);
+ std::unordered_map< int, OString >::const_iterator it = m_aAtomToDir.find( pTTFont->m_nDirectory );
+ aPath = it->second;
+ aPath += "/";
+ aPath += pTTFont->m_aFontFile;
+ }
+ return aPath;
+}
+
+const OUString& PrintFontManager::getPSName( fontID nFontID ) const
+{
+ PrintFont* pFont = getFont( nFontID );
+ if( pFont && pFont->m_nPSName == 0 )
+ {
+ if( pFont->m_eType == fonttype::TrueType )
+ analyzeTrueTypeFile( pFont );
+ }
+
+ return m_pAtoms->getString( ATOM_PSNAME, pFont ? pFont->m_nPSName : INVALID_ATOM );
+}
+
+int PrintFontManager::getFontAscend( fontID nFontID ) const
+{
+ PrintFont* pFont = getFont( nFontID );
+ if (pFont && pFont->m_nAscend == 0 && pFont->m_nDescend == 0)
+ {
+ // might be a truetype font not yet analyzed
+ if( pFont->m_eType == fonttype::TrueType )
+ analyzeTrueTypeFile( pFont );
+ else if( pFont->m_eType == fonttype::Type1 )
+ pFont->readAfmMetrics( m_pAtoms, false, true );
+ }
+ return pFont ? pFont->m_nAscend : 0;
+}
+
+int PrintFontManager::getFontDescend( fontID nFontID ) const
+{
+ PrintFont* pFont = getFont( nFontID );
+ if (pFont && pFont->m_nAscend == 0 && pFont->m_nDescend == 0)
+ {
+ // might be a truetype font not yet analyzed
+ if( pFont->m_eType == fonttype::TrueType )
+ analyzeTrueTypeFile( pFont );
+ else if( pFont->m_eType == fonttype::Type1 )
+ pFont->readAfmMetrics( m_pAtoms, false, true );
+ }
+ return pFont ? pFont->m_nDescend : 0;
+}
+
+void PrintFontManager::hasVerticalSubstitutions( fontID nFontID,
+ const sal_Unicode* pCharacters, int nCharacters, bool* pHasSubst ) const
+{
+ PrintFont* pFont = getFont( nFontID );
+ if (pFont && pFont->m_nAscend == 0 && pFont->m_nDescend == 0)
+ {
+ // might be a truetype font not yet analyzed
+ if( pFont->m_eType == fonttype::TrueType )
+ analyzeTrueTypeFile( pFont );
+ }
+
+ if (!pFont || !pFont->m_bHaveVerticalSubstitutedGlyphs)
+ memset( pHasSubst, 0, sizeof(bool)*nCharacters );
+ else
+ {
+ for( int i = 0; i < nCharacters; i++ )
+ {
+ sal_Unicode code = pCharacters[i];
+ if( ! pFont->m_pMetrics ||
+ ! ( pFont->m_pMetrics->m_aPages[ code >> 11 ] & ( 1 << ( ( code >> 8 ) & 7 ) ) ) )
+ pFont->queryMetricPage( code >> 8, m_pAtoms );
+ std::unordered_map< sal_Unicode, bool >::const_iterator it = pFont->m_pMetrics->m_bVerticalSubstitutions.find( code );
+ pHasSubst[i] = it != pFont->m_pMetrics->m_bVerticalSubstitutions.end();
+ }
+ }
+}
+
+bool PrintFontManager::isFontDownloadingAllowedForPrinting( fontID nFont ) const
+{
+ static const char* pEnable = getenv( "PSPRINT_ENABLE_TTF_COPYRIGHTAWARENESS" );
+ bool bRet = true;
+
+ if( pEnable && *pEnable )
+ {
+ PrintFont* pFont = getFont( nFont );
+ if( pFont && pFont->m_eType == fonttype::TrueType )
+ {
+ TrueTypeFontFile* pTTFontFile = static_cast<TrueTypeFontFile*>(pFont);
+ if( pTTFontFile->m_nTypeFlags & TYPEFLAG_INVALID )
+ {
+ TrueTypeFont* pTTFont = nullptr;
+ OString aFile = getFontFile( pFont );
+ if( OpenTTFontFile( aFile.getStr(), pTTFontFile->m_nCollectionEntry, &pTTFont ) == SF_OK )
+ {
+ // get type flags
+ TTGlobalFontInfo aInfo;
+ GetTTGlobalFontInfo( pTTFont, & aInfo );
+ pTTFontFile->m_nTypeFlags = (unsigned int)aInfo.typeFlags;
+ CloseTTFont( pTTFont );
+ }
+ }
+
+ unsigned int nCopyrightFlags = pTTFontFile->m_nTypeFlags & TYPEFLAG_COPYRIGHT_MASK;
+
+ // http://www.microsoft.com/typography/tt/ttf_spec/ttch02.doc
+ // Font embedding is allowed if not restricted completely (only bit 1 set).
+ // Preview&Print (bit 2), Editable (bit 3) or Installable (==0) fonts are ok.
+ bRet = ( nCopyrightFlags & 0x02 ) != 0x02;
+ }
+ }
+ return bRet;
+}
+
+bool PrintFontManager::getMetrics( fontID nFontID, const sal_Unicode* pString, int nLen, CharacterMetric* pArray, bool bVertical ) const
+{
+ PrintFont* pFont = getFont( nFontID );
+ if( ! pFont )
+ return false;
+
+ if( ( pFont->m_nAscend == 0 && pFont->m_nDescend == 0 )
+ || ! pFont->m_pMetrics || pFont->m_pMetrics->isEmpty()
+ )
+ {
+ // might be a font not yet analyzed
+ if( pFont->m_eType == fonttype::Type1 )
+ pFont->readAfmMetrics( m_pAtoms, false, false );
+ else if( pFont->m_eType == fonttype::TrueType )
+ analyzeTrueTypeFile( pFont );
+ }
+
+ for( int i = 0; i < nLen; i++ )
+ {
+ if( ! pFont->m_pMetrics ||
+ ! ( pFont->m_pMetrics->m_aPages[ pString[i] >> 11 ] & ( 1 << ( ( pString[i] >> 8 ) & 7 ) ) ) )
+ pFont->queryMetricPage( pString[i] >> 8, m_pAtoms );
+ pArray[i].width = pArray[i].height = -1;
+ if( pFont->m_pMetrics )
+ {
+ int effectiveCode = pString[i];
+ effectiveCode |= bVertical ? 1 << 16 : 0;
+ std::unordered_map< int, CharacterMetric >::const_iterator it =
+ pFont->m_pMetrics->m_aMetrics.find( effectiveCode );
+ // if no vertical metrics are available assume rotated horizontal metrics
+ if( bVertical && (it == pFont->m_pMetrics->m_aMetrics.end()) )
+ it = pFont->m_pMetrics->m_aMetrics.find( pString[i] );
+ // the character metrics are in it->second
+ if( it != pFont->m_pMetrics->m_aMetrics.end() )
+ pArray[ i ] = it->second;
+ }
+ }
+
+ return true;
+}
+
+bool PrintFontManager::getMetrics( fontID nFontID, sal_Unicode minCharacter, sal_Unicode maxCharacter, CharacterMetric* pArray, bool bVertical ) const
+{
+ OSL_PRECOND(minCharacter <= maxCharacter, "invalid char. range");
+ if (minCharacter > maxCharacter)
+ return false;
+
+ PrintFont* pFont = getFont( nFontID );
+ if( ! pFont )
+ return false;
+
+ if( ( pFont->m_nAscend == 0 && pFont->m_nDescend == 0 )
+ || ! pFont->m_pMetrics || pFont->m_pMetrics->isEmpty()
+ )
+ {
+ // might be a font not yet analyzed
+ if( pFont->m_eType == fonttype::Type1 )
+ pFont->readAfmMetrics( m_pAtoms, false, false );
+ else if( pFont->m_eType == fonttype::TrueType )
+ analyzeTrueTypeFile( pFont );
+ }
+
+ sal_Unicode code = minCharacter;
+ do
+ {
+ if( ! pFont->m_pMetrics ||
+ ! ( pFont->m_pMetrics->m_aPages[ code >> 11 ] & ( 1 << ( ( code >> 8 ) & 7 ) ) ) )
+ pFont->queryMetricPage( code >> 8, m_pAtoms );
+ pArray[ code - minCharacter ].width = -1;
+ pArray[ code - minCharacter ].height = -1;
+ if( pFont->m_pMetrics )
+ {
+ int effectiveCode = code;
+ effectiveCode |= bVertical ? 1 << 16 : 0;
+ std::unordered_map< int, CharacterMetric >::const_iterator it =
+ pFont->m_pMetrics->m_aMetrics.find( effectiveCode );
+ // if no vertical metrics are available assume rotated horizontal metrics
+ if( bVertical && (it == pFont->m_pMetrics->m_aMetrics.end()) )
+ it = pFont->m_pMetrics->m_aMetrics.find( code );
+ // the character metrics are in it->second
+ if( it != pFont->m_pMetrics->m_aMetrics.end() )
+ pArray[ code - minCharacter ] = it->second;
+ }
+ } while( code++ != maxCharacter );
+
+ return true;
+}
+
+// TODO: move most of this stuff into the central font-subsetting code
+bool PrintFontManager::createFontSubset(
+ FontSubsetInfo& rInfo,
+ fontID nFont,
+ const OUString& rOutFile,
+ const sal_GlyphId* pGlyphIds,
+ const sal_uInt8* pNewEncoding,
+ sal_Int32* pWidths,
+ int nGlyphs,
+ bool bVertical
+ )
+{
+ PrintFont* pFont = getFont( nFont );
+ if( !pFont )
+ return false;
+
+ switch( pFont->m_eType )
+ {
+ case psp::fonttype::TrueType: rInfo.m_nFontType = FontSubsetInfo::SFNT_TTF; break;
+ case psp::fonttype::Type1: rInfo.m_nFontType = FontSubsetInfo::ANY_TYPE1; break;
+ default:
+ return false;
+ }
+ // TODO: remove when Type1 subsetting gets implemented
+ if( pFont->m_eType != fonttype::TrueType )
+ return false;
+
+ // reshuffle array of requested glyphs to make sure glyph0==notdef
+ sal_uInt8 pEnc[256];
+ sal_uInt16 pGID[256];
+ sal_uInt8 pOldIndex[256];
+ memset( pEnc, 0, sizeof( pEnc ) );
+ memset( pGID, 0, sizeof( pGID ) );
+ memset( pOldIndex, 0, sizeof( pOldIndex ) );
+ if( nGlyphs > 256 )
+ return false;
+ int nChar = 1;
+ for( int i = 0; i < nGlyphs; i++ )
+ {
+ if( pNewEncoding[i] == 0 )
+ {
+ pOldIndex[ 0 ] = i;
+ }
+ else
+ {
+ DBG_ASSERT( !(pGlyphIds[i] & 0x007f0000), "overlong glyph id" );
+ DBG_ASSERT( (int)pNewEncoding[i] < nGlyphs, "encoding wrong" );
+ DBG_ASSERT( pEnc[pNewEncoding[i]] == 0 && pGID[pNewEncoding[i]] == 0, "duplicate encoded glyph" );
+ pEnc[ pNewEncoding[i] ] = pNewEncoding[i];
+ pGID[ pNewEncoding[i] ] = (sal_uInt16)pGlyphIds[ i ];
+ pOldIndex[ pNewEncoding[i] ] = i;
+ nChar++;
+ }
+ }
+ nGlyphs = nChar; // either input value or increased by one
+
+ // prepare system name for read access for subset source file
+ // TODO: since this file is usually already mmapped there is no need to open it again
+ const OString aFromFile = getFontFile( pFont );
+
+ TrueTypeFont* pTTFont = nullptr; // TODO: rename to SfntFont
+ TrueTypeFontFile* pTTFontFile = static_cast< TrueTypeFontFile* >(pFont);
+ if( OpenTTFontFile( aFromFile.getStr(), pTTFontFile->m_nCollectionEntry, &pTTFont ) != SF_OK )
+ return false;
+
+ // prepare system name for write access for subset file target
+ OUString aSysPath;
+ if( osl_File_E_None != osl_getSystemPathFromFileURL( rOutFile.pData, &aSysPath.pData ) )
+ return false;
+ const rtl_TextEncoding aEncoding = osl_getThreadTextEncoding();
+ const OString aToFile( OUStringToOString( aSysPath, aEncoding ) );
+
+ // do CFF subsetting if possible
+ int nCffLength = 0;
+ const sal_uInt8* pCffBytes = nullptr;
+ if( GetSfntTable( pTTFont, O_CFF, &pCffBytes, &nCffLength ) )
+ {
+ rInfo.LoadFont( FontSubsetInfo::CFF_FONT, pCffBytes, nCffLength );
+#if 1 // TODO: remove 16bit->long conversion when related methods handle non-16bit glyphids
+ sal_GlyphId aRequestedGlyphIds[256];
+ for( int i = 0; i < nGlyphs; ++i )
+ aRequestedGlyphIds[i] = pGID[i];
+#endif
+ // create subset file at requested path
+ FILE* pOutFile = fopen( aToFile.getStr(), "wb" );
+ if (!pOutFile)
+ {
+ CloseTTFont( pTTFont );
+ return false;
+ }
+ // create font subset
+ const char* pGlyphSetName = nullptr; // TODO: better name?
+ const bool bOK = rInfo.CreateFontSubset(
+ FontSubsetInfo::TYPE1_PFB,
+ pOutFile, pGlyphSetName,
+ aRequestedGlyphIds, pEnc, nGlyphs, pWidths );
+ fclose( pOutFile );
+ // cleanup before early return
+ CloseTTFont( pTTFont );
+ return bOK;
+ }
+
+ // do TTF->Type42 or Type3 subsetting
+ // fill in font info
+ psp::PrintFontInfo aFontInfo;
+ if( ! getFontInfo( nFont, aFontInfo ) )
+ return false;
+
+ rInfo.m_nAscent = aFontInfo.m_nAscend;
+ rInfo.m_nDescent = aFontInfo.m_nDescend;
+ rInfo.m_aPSName = getPSName( nFont );
+
+ int xMin, yMin, xMax, yMax;
+ getFontBoundingBox( nFont, xMin, yMin, xMax, yMax );
+ rInfo.m_aFontBBox = Rectangle( Point( xMin, yMin ), Size( xMax-xMin, yMax-yMin ) );
+ rInfo.m_nCapHeight = yMax; // Well ...
+
+ // fill in glyph advance widths
+ TTSimpleGlyphMetrics* pMetrics = GetTTSimpleGlyphMetrics( pTTFont,
+ pGID,
+ nGlyphs,
+ bVertical );
+ if( pMetrics )
+ {
+ for( int i = 0; i < nGlyphs; i++ )
+ pWidths[pOldIndex[i]] = pMetrics[i].adv;
+ free( pMetrics );
+ }
+ else
+ {
+ CloseTTFont( pTTFont );
+ return false;
+ }
+
+ bool bSuccess = ( SF_OK == CreateTTFromTTGlyphs( pTTFont,
+ aToFile.getStr(),
+ pGID,
+ pEnc,
+ nGlyphs,
+ 0,
+ nullptr,
+ 0 ) );
+ CloseTTFont( pTTFont );
+
+ return bSuccess;
+}
+
+void PrintFontManager::getGlyphWidths( fontID nFont,
+ bool bVertical,
+ std::vector< sal_Int32 >& rWidths,
+ std::map< sal_Unicode, sal_uInt32 >& rUnicodeEnc )
+{
+ PrintFont* pFont = getFont( nFont );
+ if( !pFont ||
+ (pFont->m_eType != fonttype::TrueType && pFont->m_eType != fonttype::Type1) )
+ return;
+ if( pFont->m_eType == fonttype::TrueType )
+ {
+ TrueTypeFont* pTTFont = nullptr;
+ TrueTypeFontFile* pTTFontFile = static_cast< TrueTypeFontFile* >(pFont);
+ OString aFromFile = getFontFile( pFont );
+ if( OpenTTFontFile( aFromFile.getStr(), pTTFontFile->m_nCollectionEntry, &pTTFont ) != SF_OK )
+ return;
+ int nGlyphs = GetTTGlyphCount( pTTFont );
+ if( nGlyphs > 0 )
+ {
+ rWidths.resize(nGlyphs);
+ std::vector<sal_uInt16> aGlyphIds(nGlyphs);
+ for( int i = 0; i < nGlyphs; i++ )
+ aGlyphIds[i] = sal_uInt16(i);
+ TTSimpleGlyphMetrics* pMetrics = GetTTSimpleGlyphMetrics( pTTFont,
+ &aGlyphIds[0],
+ nGlyphs,
+ bVertical );
+ if( pMetrics )
+ {
+ for( int i = 0; i< nGlyphs; i++ )
+ rWidths[i] = pMetrics[i].adv;
+ free( pMetrics );
+ rUnicodeEnc.clear();
+ }
+
+ // fill the unicode map
+ // TODO: isn't this map already available elsewhere in the fontmanager?
+ const sal_uInt8* pCmapData = nullptr;
+ int nCmapSize = 0;
+ if( GetSfntTable( pTTFont, O_cmap, &pCmapData, &nCmapSize ) )
+ {
+ CmapResult aCmapResult;
+ if( ParseCMAP( pCmapData, nCmapSize, aCmapResult ) )
+ {
+ FontCharMapPtr xFontCharMap( new FontCharMap(aCmapResult) );
+ for( sal_uInt32 cOld = 0;;)
+ {
+ // get next unicode covered by font
+ const sal_uInt32 c = xFontCharMap->GetNextChar( cOld );
+ if( c == cOld )
+ break;
+ cOld = c;
+#if 1 // TODO: remove when sal_Unicode covers all of unicode
+ if( c > (sal_Unicode)~0 )
+ break;
+#endif
+ // get the matching glyph index
+ const sal_GlyphId aGlyphId = xFontCharMap->GetGlyphIndex( c );
+ // update the requested map
+ rUnicodeEnc[ (sal_Unicode)c ] = aGlyphId;
+ }
+
+ xFontCharMap = nullptr;
+ }
+ }
+ }
+ CloseTTFont( pTTFont );
+ }
+ else if( pFont->m_eType == fonttype::Type1 )
+ {
+ if( pFont->m_aEncodingVector.empty() )
+ pFont->readAfmMetrics( m_pAtoms, true, true );
+ if( pFont->m_pMetrics )
+ {
+ rUnicodeEnc.clear();
+ rWidths.clear();
+ rWidths.reserve( pFont->m_pMetrics->m_aMetrics.size() );
+ for( std::unordered_map< int, CharacterMetric >::const_iterator it =
+ pFont->m_pMetrics->m_aMetrics.begin();
+ it != pFont->m_pMetrics->m_aMetrics.end(); ++it )
+ {
+ if( (it->first & 0x00010000) == 0 || bVertical )
+ {
+ rUnicodeEnc[ sal_Unicode(it->first & 0x0000ffff) ] = sal_uInt32(rWidths.size());
+ rWidths.push_back( it->second.width );
+ }
+ }
+ }
+ }
+}
+
+const std::map< sal_Unicode, sal_Int32 >* PrintFontManager::getEncodingMap( fontID nFont, const std::map< sal_Unicode, OString >** pNonEncoded, std::set<sal_Unicode> const** ppPriority ) const
+{
+ PrintFont* pFont = getFont( nFont );
+ if( !pFont || pFont->m_eType != fonttype::Type1 )
+ return nullptr;
+
+ if( pFont->m_aEncodingVector.empty() )
+ pFont->readAfmMetrics( m_pAtoms, true, true );
+
+ if( pNonEncoded )
+ *pNonEncoded = !pFont->m_aNonEncoded.empty() ? &pFont->m_aNonEncoded : nullptr;
+
+ if (ppPriority)
+ {
+ *ppPriority = &pFont->m_aEncodingVectorPriority;
+ }
+
+ return !pFont->m_aEncodingVector.empty() ? &pFont->m_aEncodingVector : nullptr;
+}
+
+std::list< OString > PrintFontManager::getAdobeNameFromUnicode( sal_Unicode aChar ) const
+{
+ std::pair< std::unordered_multimap< sal_Unicode, OString >::const_iterator,
+ std::unordered_multimap< sal_Unicode, OString >::const_iterator > range
+ = m_aUnicodeToAdobename.equal_range( aChar );
+
+ std::list< OString > aRet;
+ for( ; range.first != range.second; ++range.first )
+ aRet.push_back( range.first->second );
+
+ if( aRet.empty() && aChar != 0 )
+ {
+ sal_Char aBuf[8];
+ sal_Int32 nChars = snprintf(
+ aBuf, sizeof(aBuf), "uni%.4hX", sal_uInt16(aChar));
+ aRet.push_back( OString( aBuf, nChars ) );
+ }
+
+ return aRet;
+}
+
+std::list< sal_Unicode > PrintFontManager::getUnicodeFromAdobeName( const OString& rName ) const
+{
+ std::pair< std::unordered_multimap< OString, sal_Unicode, OStringHash >::const_iterator,
+ std::unordered_multimap< OString, sal_Unicode, OStringHash >::const_iterator > range
+ = m_aAdobenameToUnicode.equal_range( rName );
+
+ std::list< sal_Unicode > aRet;
+ for( ; range.first != range.second; ++range.first )
+ aRet.push_back( range.first->second );
+
+ if( aRet.empty() )
+ {
+ if( rName.getLength() == 7 && rName.startsWith( "uni" ) )
+ {
+ sal_Unicode aCode = (sal_Unicode)rName.copy( 3 ).toUInt32( 16 );
+ aRet.push_back( aCode );
+ }
+ }
+
+ return aRet;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/vcl/unx/generic/fontmanager/fontsubst.cxx b/vcl/unx/generic/fontmanager/fontsubst.cxx
new file mode 100644
index 000000000000..f88944504764
--- /dev/null
+++ b/vcl/unx/generic/fontmanager/fontsubst.cxx
@@ -0,0 +1,252 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include "generic/geninst.h"
+#include "generic/genpspgraphics.h"
+#include "unx/glyphcache.hxx"
+
+#include "vcl/sysdata.hxx"
+#include "fontinstance.hxx"
+
+#include "generic/printergfx.hxx"
+#include "salbmp.hxx"
+#include "impfont.hxx"
+#include "outdev.h"
+#include "PhysicalFontCollection.hxx"
+#include "fontsubset.hxx"
+#include "salprn.hxx"
+
+#include <unotools/fontdefs.hxx>
+#include <list>
+
+// platform specific font substitution hooks
+
+class FcPreMatchSubstitution
+: public ImplPreMatchFontSubstitution
+{
+public:
+ bool FindFontSubstitute( FontSelectPattern& ) const override;
+ typedef ::std::pair<FontSelectPattern, FontSelectPattern> value_type;
+private:
+ typedef ::std::list<value_type> CachedFontMapType;
+ mutable CachedFontMapType maCachedFontMap;
+};
+
+class FcGlyphFallbackSubstitution
+: public ImplGlyphFallbackFontSubstitution
+{
+ // TODO: add a cache
+public:
+ bool FindFontSubstitute( FontSelectPattern&, OUString& rMissingCodes ) const override;
+};
+
+int SalGenericInstance::FetchFontSubstitutionFlags()
+{
+ // init font substitution defaults
+ int nDisableBits = 0;
+#ifdef SOLARIS
+ nDisableBits = 1; // disable "font fallback" here on default
+#endif
+ // apply the environment variable if any
+ const char* pEnvStr = ::getenv( "SAL_DISABLE_FC_SUBST" );
+ if( pEnvStr )
+ {
+ if( (*pEnvStr >= '0') && (*pEnvStr <= '9') )
+ nDisableBits = (*pEnvStr - '0');
+ else
+ nDisableBits = ~0U; // no specific bits set: disable all
+ }
+ return nDisableBits;
+}
+
+void SalGenericInstance::RegisterFontSubstitutors( PhysicalFontCollection* pFontCollection )
+{
+ // init font substitution defaults
+ int nDisableBits = 0;
+#ifdef SOLARIS
+ nDisableBits = 1; // disable "font fallback" here on default
+#endif
+ // apply the environment variable if any
+ const char* pEnvStr = ::getenv( "SAL_DISABLE_FC_SUBST" );
+ if( pEnvStr )
+ {
+ if( (*pEnvStr >= '0') && (*pEnvStr <= '9') )
+ nDisableBits = (*pEnvStr - '0');
+ else
+ nDisableBits = ~0U; // no specific bits set: disable all
+ }
+
+ // register font fallback substitutions (unless disabled by bit0)
+ if( (nDisableBits & 1) == 0 )
+ {
+ static FcPreMatchSubstitution aSubstPreMatch;
+ pFontCollection->SetPreMatchHook( &aSubstPreMatch );
+ }
+
+ // register glyph fallback substitutions (unless disabled by bit1)
+ if( (nDisableBits & 2) == 0 )
+ {
+ static FcGlyphFallbackSubstitution aSubstFallback;
+ pFontCollection->SetFallbackHook( &aSubstFallback );
+ }
+}
+
+static FontSelectPattern GetFcSubstitute(const FontSelectPattern &rFontSelData, OUString& rMissingCodes )
+{
+ FontSelectPattern aSubstituted(rFontSelData);
+ psp::PrintFontManager& rMgr = psp::PrintFontManager::get();
+ rMgr.Substitute(aSubstituted, rMissingCodes);
+ return aSubstituted;
+}
+
+namespace
+{
+ bool uselessmatch(const FontSelectPattern &rOrig, const FontSelectPattern &rNew)
+ {
+ return
+ (
+ rOrig.maTargetName == rNew.maSearchName &&
+ rOrig.GetWeight() == rNew.GetWeight() &&
+ rOrig.GetSlantType() == rNew.GetSlantType() &&
+ rOrig.GetPitch() == rNew.GetPitch() &&
+ rOrig.GetWidthType() == rNew.GetWidthType()
+ );
+ }
+
+ class equal
+ {
+ private:
+ const FontSelectPattern& mrAttributes;
+ public:
+ explicit equal(const FontSelectPattern& rAttributes)
+ : mrAttributes(rAttributes)
+ {
+ }
+ bool operator()(const FcPreMatchSubstitution::value_type& rOther) const
+ { return rOther.first == mrAttributes; }
+ };
+}
+
+bool FcPreMatchSubstitution::FindFontSubstitute( FontSelectPattern &rFontSelData ) const
+{
+ // We don't actually want to talk to Fontconfig at all for symbol fonts
+ if( rFontSelData.IsSymbolFont() )
+ return false;
+ // StarSymbol is a unicode font, but it still deserves the symbol flag
+ if ( IsStarSymbol(rFontSelData.maSearchName) )
+ return false;
+
+ //see fdo#41556 and fdo#47636
+ //fontconfig can return e.g. an italic font for a non-italic input and/or
+ //different fonts depending on fontsize, bold, etc settings so don't cache
+ //just on the name, cache map all the input and all the output not just map
+ //from original selection to output fontname
+ FontSelectPattern& rPatternAttributes = rFontSelData;
+ CachedFontMapType &rCachedFontMap = const_cast<CachedFontMapType &>(maCachedFontMap);
+ CachedFontMapType::iterator itr = std::find_if(rCachedFontMap.begin(), rCachedFontMap.end(), equal(rPatternAttributes));
+ if (itr != rCachedFontMap.end())
+ {
+ // Cached substitution
+ rFontSelData.copyAttributes(itr->second);
+ if (itr != rCachedFontMap.begin())
+ {
+ // MRU, move it to the front
+ rCachedFontMap.splice(rCachedFontMap.begin(), rCachedFontMap, itr);
+ }
+ return true;
+ }
+
+ OUString aDummy;
+ const FontSelectPattern aOut = GetFcSubstitute( rFontSelData, aDummy );
+
+ if( aOut.maSearchName.isEmpty() )
+ return false;
+
+ const bool bHaveSubstitute = !uselessmatch( rFontSelData, aOut );
+
+#ifdef DEBUG
+ const OString aOrigName(OUStringToOString(rFontSelData.maTargetName,
+ RTL_TEXTENCODING_UTF8));
+ const OString aSubstName(OUStringToOString(aOut.maSearchName,
+ RTL_TEXTENCODING_UTF8));
+ printf( "FcPreMatchSubstitution \"%s\" bipw=%d%d%d%d -> ",
+ aOrigName.getStr(), rFontSelData.GetWeight(), rFontSelData.GetSlantType(),
+ rFontSelData.GetPitch(), rFontSelData.GetWidthType() );
+ if( !bHaveSubstitute )
+ printf( "no substitute available\n" );
+ else
+ printf( "\"%s\" bipw=%d%d%d%d\n", aSubstName.getStr(),
+ aOut.GetWeight(), aOut.GetSlantType(), aOut.GetPitch(), aOut.GetWidthType() );
+#endif
+
+ if( bHaveSubstitute )
+ {
+ rCachedFontMap.push_front(value_type(rFontSelData, aOut));
+ //fairly arbitrary limit in this case, but I recall measuring max 8
+ //fonts as the typical max amount of fonts in medium sized documents
+ if (rCachedFontMap.size() > 8)
+ rCachedFontMap.pop_back();
+ rFontSelData = aOut;
+ }
+
+ return bHaveSubstitute;
+}
+
+bool FcGlyphFallbackSubstitution::FindFontSubstitute( FontSelectPattern& rFontSelData,
+ OUString& rMissingCodes ) const
+{
+ // We don't actually want to talk to Fontconfig at all for symbol fonts
+ if( rFontSelData.IsSymbolFont() )
+ return false;
+ // StarSymbol is a unicode font, but it still deserves the symbol flag
+ if ( IsStarSymbol(rFontSelData.maSearchName) )
+ return false;
+
+ const FontSelectPattern aOut = GetFcSubstitute( rFontSelData, rMissingCodes );
+ // TODO: cache the unicode + srcfont specific result
+ // FC doing it would be preferable because it knows the invariables
+ // e.g. FC knows the FC rule that all Arial gets replaced by LiberationSans
+ // whereas we would have to check for every size or attribute
+ if( aOut.maSearchName.isEmpty() )
+ return false;
+
+ const bool bHaveSubstitute = !uselessmatch( rFontSelData, aOut );
+
+#ifdef DEBUG
+ const OString aOrigName(OUStringToOString(rFontSelData.maTargetName,
+ RTL_TEXTENCODING_UTF8));
+ const OString aSubstName(OUStringToOString(aOut.maSearchName,
+ RTL_TEXTENCODING_UTF8));
+ printf( "FcGFSubstitution \"%s\" bipw=%d%d%d%d ->",
+ aOrigName.getStr(), rFontSelData.GetWeight(), rFontSelData.GetSlantType(),
+ rFontSelData.GetPitch(), rFontSelData.GetWidthType() );
+ if( !bHaveSubstitute )
+ printf( "no substitute available\n" );
+ else
+ printf( "\"%s\" bipw=%d%d%d%d\n", aSubstName.getStr(),
+ aOut.GetWeight(), aOut.GetSlantType(), aOut.GetPitch(), aOut.GetWidthType() );
+#endif
+
+ if( bHaveSubstitute )
+ rFontSelData = aOut;
+
+ return bHaveSubstitute;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/vcl/unx/generic/fontmanager/helper.cxx b/vcl/unx/generic/fontmanager/helper.cxx
new file mode 100644
index 000000000000..af8ae0aedeaa
--- /dev/null
+++ b/vcl/unx/generic/fontmanager/helper.cxx
@@ -0,0 +1,390 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This file is part of the LibreOffice project.
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * This file incorporates work covered by the following license notice:
+ *
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed
+ * with this work for additional information regarding copyright
+ * ownership. The ASF licenses this file to you under the Apache
+ * License, Version 2.0 (the "License"); you may not use this file
+ * except in compliance with the License. You may obtain a copy of
+ * the License at http://www.apache.org/licenses/LICENSE-2.0 .
+ */
+
+#include <config_folders.h>
+
+#include <cstring>
+#include <sys/stat.h>
+#include <unistd.h>
+#include <limits.h>
+#include <osl/file.hxx>
+#include <osl/process.h>
+#include <osl/thread.h>
+#include <rtl/bootstrap.hxx>
+#include <rtl/ustring.hxx>
+#include <tools/urlobj.hxx>
+#include "vcl/helper.hxx"
+#include "vcl/ppdparser.hxx"
+#include <memory>
+
+using ::rtl::Bootstrap;
+
+namespace psp {
+
+OUString getOfficePath( enum whichOfficePath ePath )
+{
+ static OUString aInstallationRootPath;
+ static OUString aUserPath;
+ static OUString aConfigPath;
+ static OUString aEmpty;
+ static bool bOnce = false;
+
+ if( ! bOnce )
+ {
+ bOnce = true;
+ OUString aIni;
+ Bootstrap::get( "BRAND_BASE_DIR", aInstallationRootPath );
+ aIni = aInstallationRootPath + "/" LIBO_ETC_FOLDER "/" SAL_CONFIGFILE( "bootstrap" );
+ Bootstrap aBootstrap( aIni );
+ aBootstrap.getFrom( "CustomDataUrl", aConfigPath );
+ aBootstrap.getFrom( "UserInstallation", aUserPath );
+ OUString aUPath = aUserPath;
+
+ if( aConfigPath.startsWith( "file://" ) )
+ {
+ OUString aSysPath;
+ if( osl_getSystemPathFromFileURL( aConfigPath.pData, &aSysPath.pData ) == osl_File_E_None )
+ aConfigPath = aSysPath;
+ }
+ if( aInstallationRootPath.startsWith( "file://" ) )
+ {
+ OUString aSysPath;
+ if( osl_getSystemPathFromFileURL( aInstallationRootPath.pData, &aSysPath.pData ) == osl_File_E_None )
+ aInstallationRootPath = aSysPath;
+ }
+ if( aUserPath.startsWith( "file://" ) )
+ {
+ OUString aSysPath;
+ if( osl_getSystemPathFromFileURL( aUserPath.pData, &aSysPath.pData ) == osl_File_E_None )
+ aUserPath = aSysPath;
+ }
+ // ensure user path exists
+ aUPath += "/user/psprint";
+ #if OSL_DEBUG_LEVEL > 1
+ oslFileError eErr =
+ #endif
+ osl_createDirectoryPath( aUPath.pData, nullptr, nullptr );
+ #if OSL_DEBUG_LEVEL > 1
+ fprintf( stderr, "try to create \"%s\" = %d\n", OUStringToOString( aUPath, RTL_TEXTENCODING_UTF8 ).getStr(), eErr );
+ #endif
+ }
+
+ switch( ePath )
+ {
+ case ConfigPath: return aConfigPath;
+ case InstallationRootPath: return aInstallationRootPath;
+ case UserPath: return aUserPath;
+ }
+ return aEmpty;
+}
+
+static OString getEnvironmentPath( const char* pKey )
+{
+ OString aPath;
+
+ const char* pValue = getenv( pKey );
+ if( pValue && *pValue )
+ {
+ aPath = OString( pValue );
+ }
+ return aPath;
+}
+
+} // namespace psp
+
+void psp::getPrinterPathList( std::list< OUString >& rPathList, const char* pSubDir )
+{
+ rPathList.clear();
+ rtl_TextEncoding aEncoding = osl_getThreadTextEncoding();
+
+ OUStringBuffer aPathBuffer( 256 );
+
+ // append net path
+ aPathBuffer.append( getOfficePath( psp::InstallationRootPath ) );
+ if( !aPathBuffer.isEmpty() )
+ {
+ aPathBuffer.append( "/" LIBO_SHARE_FOLDER "/psprint" );
+ if( pSubDir )
+ {
+ aPathBuffer.append( '/' );
+ aPathBuffer.appendAscii( pSubDir );
+ }
+ rPathList.push_back( aPathBuffer.makeStringAndClear() );
+ }
+ // append user path
+ aPathBuffer.append( getOfficePath( psp::UserPath ) );
+ if( !aPathBuffer.isEmpty() )
+ {
+ aPathBuffer.append( "/user/psprint" );
+ if( pSubDir )
+ {
+ aPathBuffer.append( '/' );
+ aPathBuffer.appendAscii( pSubDir );
+ }
+ rPathList.push_back( aPathBuffer.makeStringAndClear() );
+ }
+
+ OString aPath( getEnvironmentPath("SAL_PSPRINT") );
+ sal_Int32 nIndex = 0;
+ do
+ {
+ OString aDir( aPath.getToken( 0, ':', nIndex ) );
+ if( aDir.isEmpty() )
+ continue;
+
+ if( pSubDir )
+ {
+ aDir += "/";
+ aDir += pSubDir;
+ }
+ struct stat aStat;
+ if( stat( aDir.getStr(), &aStat ) || ! S_ISDIR( aStat.st_mode ) )
+ continue;
+
+ rPathList.push_back( OStringToOUString( aDir, aEncoding ) );
+ } while( nIndex != -1 );
+
+ #ifdef SYSTEM_PPD_DIR
+ if( pSubDir && rtl_str_compare( pSubDir, PRINTER_PPDDIR ) == 0 )
+ {
+ rPathList.push_back( OStringToOUString( OString( SYSTEM_PPD_DIR ), RTL_TEXTENCODING_UTF8 ) );
+ }
+ #endif
+
+ if( rPathList.empty() )
+ {
+ // last resort: next to program file (mainly for setup)
+ OUString aExe;
+ if( osl_getExecutableFile( &aExe.pData ) == osl_Process_E_None )
+ {
+ INetURLObject aDir( aExe );
+ aDir.removeSegment();
+ aExe = aDir.GetMainURL( INetURLObject::NO_DECODE );
+ OUString aSysPath;
+ if( osl_getSystemPathFromFileURL( aExe.pData, &aSysPath.pData ) == osl_File_E_None )
+ {
+ rPathList.push_back( aSysPath );
+ }
+ }
+ }
+}
+
+OUString psp::getFontPath()
+{
+ static OUString aPath;
+
+ if (aPath.isEmpty())
+ {
+ OUStringBuffer aPathBuffer( 512 );
+
+ OUString aConfigPath( getOfficePath( psp::ConfigPath ) );
+ OUString aInstallationRootPath( getOfficePath( psp::InstallationRootPath ) );
+ OUString aUserPath( getOfficePath( psp::UserPath ) );
+ if( !aConfigPath.isEmpty() )
+ {
+ // #i53530# Path from CustomDataUrl will completely
+ // replace net and user paths if the path exists
+ aPathBuffer.append(aConfigPath);
+ aPathBuffer.append("/" LIBO_SHARE_FOLDER "/fonts");
+ // check existence of config path
+ struct stat aStat;
+ if( 0 != stat( OUStringToOString( aPathBuffer.makeStringAndClear(), osl_getThreadTextEncoding() ).getStr(), &aStat )
+ || ! S_ISDIR( aStat.st_mode ) )
+ aConfigPath.clear();
+ else
+ {
+ aPathBuffer.append(aConfigPath);
+ aPathBuffer.append("/" LIBO_SHARE_FOLDER "/fonts");
+ }
+ }
+ if( aConfigPath.isEmpty() )
+ {
+ if( !aInstallationRootPath.isEmpty() )
+ {
+ aPathBuffer.append( aInstallationRootPath );
+ aPathBuffer.append( "/" LIBO_SHARE_FOLDER "/fonts/truetype;");
+ aPathBuffer.append( aInstallationRootPath );
+ aPathBuffer.append( "/" LIBO_SHARE_FOLDER "/fonts/type1;" );
+ }
+ if( !aUserPath.isEmpty() )
+ {
+ aPathBuffer.append( aUserPath );
+ aPathBuffer.append( "/user/fonts" );
+ }
+ }
+
+ aPath = aPathBuffer.makeStringAndClear();
+#if OSL_DEBUG_LEVEL > 1
+ fprintf( stderr, "initializing font path to \"%s\"\n", OUStringToOString( aPath, RTL_TEXTENCODING_ISO_8859_1 ).getStr() );
+#endif
+ }
+ return aPath;
+}
+
+bool psp::convertPfbToPfa( ::osl::File& rInFile, ::osl::File& rOutFile )
+{
+ static const unsigned char hexDigits[] =
+ {
+ '0', '1', '2', '3', '4', '5', '6', '7',
+ '8', '9', 'A', 'B', 'C', 'D', 'E', 'F'
+ };
+
+ bool bSuccess = true;
+ bool bEof = false;
+ unsigned char buffer[256];
+ sal_uInt64 nSize(0);
+ rInFile.getSize(nSize);
+
+ while( bSuccess && ! bEof )
+ {
+ sal_uInt64 nRead;
+ // read leading bytes
+ bEof = ((0 != rInFile.read( buffer, 6, nRead)) || (nRead != 6));
+ if( bEof )
+ break;
+ unsigned int nType = buffer[ 1 ];
+ unsigned int nBytesToRead = buffer[2] | buffer[3] << 8 | buffer[4] << 16 | buffer[5] << 24;
+ if( buffer[0] != 0x80 ) // test for pfb magic number
+ {
+ // this might be a pfa font already
+ if( ! rInFile.read( buffer+6, 9, nRead ) && nRead == 9 &&
+ ( ! std::strncmp( reinterpret_cast<char*>(buffer), "%!FontType1-", 12 ) ||
+ ! std::strncmp( reinterpret_cast<char*>(buffer), "%!PS-AdobeFont-", 15 ) ) )
+ {
+ sal_uInt64 nWrite = 0;
+ if( rOutFile.write( buffer, 15, nWrite ) || nWrite != 15 )
+ bSuccess = false;
+ while( bSuccess &&
+ ! rInFile.read( buffer, sizeof( buffer ), nRead ) &&
+ nRead != 0 )
+ {
+ if( rOutFile.write( buffer, nRead, nWrite ) ||
+ nWrite != nRead )
+ bSuccess = false;
+ }
+ bEof = true;
+ }
+ else
+ bSuccess = false;
+ }
+ else if( nType == 1 || nType == 2 )
+ {
+ sal_uInt64 nOrgPos(0);
+ rInFile.getPos(nOrgPos);
+ nBytesToRead = std::min<sal_uInt64>(nBytesToRead, nSize - nOrgPos);
+
+ std::unique_ptr<unsigned char[]> pBuffer(new unsigned char[nBytesToRead+1]);
+ pBuffer[nBytesToRead] = 0;
+
+ if( ! rInFile.read( pBuffer.get(), nBytesToRead, nRead ) && nRead == nBytesToRead )
+ {
+ if( nType == 1 )
+ {
+ // ascii data, convert dos lineends( \r\n ) and
+ // m_ac lineends( \r ) to \n
+ std::unique_ptr<unsigned char[]> pWriteBuffer(new unsigned char[ nBytesToRead ]);
+ unsigned int nBytesToWrite = 0;
+ for( unsigned int i = 0; i < nBytesToRead; i++ )
+ {
+ if( pBuffer[i] != '\r' )
+ pWriteBuffer[ nBytesToWrite++ ] = pBuffer[i];
+ else if( pBuffer[ i+1 ] == '\n' )
+ {
+ i++;
+ pWriteBuffer[ nBytesToWrite++ ] = '\n';
+ }
+ else
+ pWriteBuffer[ nBytesToWrite++ ] = '\n';
+ }
+ if( rOutFile.write( pWriteBuffer.get(), nBytesToWrite, nRead ) || nRead != nBytesToWrite )
+ bSuccess = false;
+ }
+ else
+ {
+ // binary data
+ unsigned int nBuffer = 0;
+ for( unsigned int i = 0; i < nBytesToRead && bSuccess; i++ )
+ {
+ buffer[ nBuffer++ ] = hexDigits[ pBuffer[ i ] >> 4 ];
+ buffer[ nBuffer++ ] = hexDigits[ pBuffer[ i ] & 15 ];
+ if( nBuffer >= 80 )
+ {
+ buffer[ nBuffer++ ] = '\n';
+ if( rOutFile.write( buffer, nBuffer, nRead ) || nRead != nBuffer )
+ bSuccess = false;
+ nBuffer = 0;
+ }
+ }
+ if( nBuffer > 0 && bSuccess )
+ {
+ buffer[ nBuffer++ ] = '\n';
+ if( rOutFile.write( buffer, nBuffer, nRead ) || nRead != nBuffer )
+ bSuccess = false;
+ }
+ }
+ }
+ else
+ bSuccess = false;
+ }
+ else if( nType == 3 )
+ bEof = true;
+ else
+ bSuccess = false;
+ }
+
+ return bSuccess;
+}
+
+void psp::normPath( OString& rPath )
+{
+ char buf[PATH_MAX];
+
+ // double slashes and slash at end are probably
+ // removed by realpath anyway, but since this runs
+ // on many different platforms let's play it safe
+ OString aPath = rPath.replaceAll("//", "/");
+
+ if( aPath.endsWith("/") )
+ aPath = aPath.copy(0, aPath.getLength()-1);
+
+ if( ( aPath.indexOf("./") != -1 ||
+ aPath.indexOf( '~' ) != -1 )
+ && realpath( aPath.getStr(), buf ) )
+ {
+ rPath = buf;
+ }
+ else
+ {
+ rPath = aPath;
+ }
+}
+
+void psp::splitPath( OString& rPath, OString& rDir, OString& rBase )
+{
+ normPath( rPath );
+ sal_Int32 nIndex = rPath.lastIndexOf( '/' );
+ if( nIndex > 0 )
+ rDir = rPath.copy( 0, nIndex );
+ else if( nIndex == 0 ) // root dir
+ rDir = rPath.copy( 0, 1 );
+ if( rPath.getLength() > nIndex+1 )
+ rBase = rPath.copy( nIndex+1 );
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/vcl/unx/generic/fontmanager/parseAFM.cxx b/vcl/unx/generic/fontmanager/parseAFM.cxx
new file mode 100644
index 000000000000..3db271bab3ba
--- /dev/null
+++ b/vcl/unx/generic/fontmanager/parseAFM.cxx
@@ -0,0 +1,1466 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * (C) 1988, 1989, 1990 by Adobe Systems Incorporated. All rights reserved.
+ *
+ * This file may be freely copied and redistributed as long as:
+ * 1) This entire notice continues to be included in the file,
+ * 2) If the file has been modified in any way, a notice of such
+ * modification is conspicuously indicated.
+ *
+ * PostScript, Display PostScript, and Adobe are registered trademarks of
+ * Adobe Systems Incorporated.
+ *
+ * ************************************************************************
+ * THE INFORMATION BELOW IS FURNISHED AS IS, IS SUBJECT TO CHANGE WITHOUT
+ * NOTICE, AND SHOULD NOT BE CONSTRUED AS A COMMITMENT BY ADOBE SYSTEMS
+ * INCORPORATED. ADOBE SYSTEMS INCORPORATED ASSUMES NO RESPONSIBILITY OR
+ * LIABILITY FOR ANY ERRORS OR INACCURACIES, MAKES NO WARRANTY OF ANY
+ * KIND (EXPRESS, IMPLIED OR STATUTORY) WITH RESPECT TO THIS INFORMATION,
+ * AND EXPRESSLY DISCLAIMS ANY AND ALL WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR PARTICULAR PURPOSES AND NONINFRINGEMENT OF THIRD PARTY RIGHTS.
+ * ************************************************************************
+ */
+
+/*
+ * Changes made for OpenOffice.org
+ *
+ * 10/24/2000 pl - changed code to compile with c++-compilers
+ * - added namespace to avoid symbol clashes
+ * - replaced BOOL by bool
+ * - added function to free space allocated by parseFile
+ * 10/26/2000 pl - added additional keys
+ * - added ability to parse slightly broken files
+ * - added charwidth member to GlobalFontInfo
+ * 04/26/2001 pl - added OpenOffice header
+ * 10/19/2005 pl - performance increase:
+ * - fread file in one pass
+ * - replace file io by buffer access
+ * 10/20/2005 pl - performance increase:
+ * - use one table lookup in token() routine
+ * instead of many conditions
+ * - return token length in token() routine
+ * - use hash lookup instead of binary search
+ * in recognize() routine
+ */
+
+/* parseAFM.c
+ *
+ * This file is used in conjunction with the parseAFM.h header file.
+ * This file contains several procedures that are used to parse AFM
+ * files. It is intended to work with an application program that needs
+ * font metric information. The program can be used as is by making a
+ * procedure call to "parseFile" (passing in the expected parameters)
+ * and having it fill in a data structure with the data from the
+ * AFM file, or an application developer may wish to customize this
+ * code.
+ *
+ * There is also a file, parseAFMclient.c, that is a sample application
+ * showing how to call the "parseFile" procedure and how to use the data
+ * after "parseFile" has returned.
+ *
+ * Please read the comments in parseAFM.h and parseAFMclient.c.
+ *
+ * History:
+ * original: DSM Thu Oct 20 17:39:59 PDT 1988
+ * modified: DSM Mon Jul 3 14:17:50 PDT 1989
+ * - added 'storageProblem' return code
+ * - fixed bug of not allocating extra byte for string duplication
+ * - fixed typos
+ * modified: DSM Tue Apr 3 11:18:34 PDT 1990
+ * - added free(ident) at end of parseFile routine
+ * modified: DSM Tue Jun 19 10:16:29 PDT 1990
+ * - changed (width == 250) to (width = 250) in initializeArray
+ */
+
+#include <string.h>
+#include <stdlib.h>
+#include <sys/stat.h>
+
+#include "parseAFM.hxx"
+#include "vcl/strhelper.hxx"
+
+#include "rtl/alloc.h"
+
+#define lineterm EOL /* line terminating character */
+#define normalEOF 1 /* return code from parsing routines used only */
+/* in this module */
+#define False "false" /* used in string comparison to check the value of */
+/* boolean keys (e.g. IsFixedPitch) */
+
+#define MATCH(A,B) (strncmp((A),(B), MAX_NAME) == 0)
+
+namespace psp {
+
+class FileInputStream
+{
+ char* m_pMemory;
+ unsigned int m_nPos;
+ unsigned int m_nLen;
+ public:
+ explicit FileInputStream( const char* pFilename );
+ ~FileInputStream();
+
+ int getChar() { return (m_nPos < m_nLen) ? int(m_pMemory[m_nPos++]) : -1; }
+ void ungetChar()
+ {
+ if( m_nPos > 0 )
+ m_nPos--;
+ }
+};
+
+FileInputStream::FileInputStream(const char* pFilename)
+ : m_pMemory(nullptr)
+ , m_nPos(0)
+ , m_nLen(0)
+{
+ FILE* fp = fopen( pFilename, "r" );
+ if( fp )
+ {
+ struct stat aStat;
+ if (!fstat(fileno(fp), &aStat) && S_ISREG(aStat.st_mode) && aStat.st_size > 0)
+ {
+ m_pMemory = static_cast<char*>(rtl_allocateMemory( aStat.st_size ));
+ m_nLen = (unsigned int)fread( m_pMemory, 1, aStat.st_size, fp );
+ }
+ fclose( fp );
+ }
+}
+
+FileInputStream::~FileInputStream()
+{
+ rtl_freeMemory( m_pMemory );
+}
+
+/*************************** GLOBALS ***********************/
+/* "shorts" for fast case statement
+ * The values of each of these enumerated items correspond to an entry in the
+ * table of strings defined below. Therefore, if you add a new string as
+ * new keyword into the keyStrings table, you must also add a corresponding
+ * parseKey AND it MUST be in the same position!
+ *
+ * IMPORTANT: since the sorting algorithm is a binary search, the strings of
+ * keywords must be placed in lexicographical order, below. [Therefore, the
+ * enumerated items are not necessarily in lexicographical order, depending
+ * on the name chosen. BUT, they must be placed in the same position as the
+ * corresponding key string.] The NOPE shall remain in the last position,
+ * since it does not correspond to any key string, and it is used in the
+ * "recognize" procedure to calculate how many possible keys there are.
+ */
+
+// some metrics have Ascent, Descent instead Ascender, Descender or Em
+// which is not allowed per afm spcification, but let us handle
+// this gently
+enum parseKey {
+ ASCENDER, ASCENT, CHARBBOX, CODE, COMPCHAR, CODEHEX, CAPHEIGHT, CHARWIDTH, CHARACTERSET, CHARACTERS, COMMENT,
+ DESCENDER, DESCENT, EM, ENCODINGSCHEME, ENDCHARMETRICS, ENDCOMPOSITES, ENDDIRECTION,
+ ENDFONTMETRICS, ENDKERNDATA, ENDKERNPAIRS, ENDTRACKKERN,
+ FAMILYNAME, FONTBBOX, FONTNAME, FULLNAME, ISBASEFONT, ISFIXEDPITCH,
+ ITALICANGLE, KERNPAIR, KERNPAIRXAMT, LIGATURE, MAPPINGSCHEME, METRICSSETS, CHARNAME,
+ NOTICE, COMPCHARPIECE, STARTCHARMETRICS, STARTCOMPOSITES, STARTDIRECTION,
+ STARTFONTMETRICS, STARTKERNDATA, STARTKERNPAIRS,
+ STARTTRACKKERN, STDHW, STDVW, TRACKKERN, UNDERLINEPOSITION,
+ UNDERLINETHICKNESS, VVECTOR, VERSION, XYWIDTH, X0WIDTH, XWIDTH, WEIGHT, XHEIGHT,
+ NOPE
+};
+
+/*************************** PARSING ROUTINES **************/
+
+/*************************** token *************************/
+
+/* A "AFM file Conventions" tokenizer. That means that it will
+ * return the next token delimited by white space. See also
+ * the `linetoken' routine, which does a similar thing but
+ * reads all tokens until the next end-of-line.
+ */
+
+// token white space is ' ', '\n', '\r', ',', '\t', ';'
+static const bool is_white_Array[ 256 ] =
+{ false, false, false, false, false, false, false, false, // 0-7
+ false, true, true, false, false, true, false, false, // 8-15
+ false, false, false, false, false, false, false, false, // 16-23
+ false, false, false, false, false, false, false, false, // 24-31
+ true, false, false, false, false, false, false, false, // 32-39
+ false, false, false, false, true, false, false, false, // 40-47
+ false, false, false, false, false, false, false, false, // 48-55
+ false, false, false, true, false, false, false, false, // 56-63
+
+ false, false, false, false, false, false, false, false, // 64 -
+ false, false, false, false, false, false, false, false,
+ false, false, false, false, false, false, false, false,
+ false, false, false, false, false, false, false, false,
+ false, false, false, false, false, false, false, false,
+ false, false, false, false, false, false, false, false,
+ false, false, false, false, false, false, false, false,
+ false, false, false, false, false, false, false, false, // 127
+
+ false, false, false, false, false, false, false, false, // 128 -
+ false, false, false, false, false, false, false, false,
+ false, false, false, false, false, false, false, false,
+ false, false, false, false, false, false, false, false,
+ false, false, false, false, false, false, false, false,
+ false, false, false, false, false, false, false, false,
+ false, false, false, false, false, false, false, false,
+ false, false, false, false, false, false, false, false, // 191
+
+ false, false, false, false, false, false, false, false, // 192 -
+ false, false, false, false, false, false, false, false,
+ false, false, false, false, false, false, false, false,
+ false, false, false, false, false, false, false, false,
+ false, false, false, false, false, false, false, false,
+ false, false, false, false, false, false, false, false,
+ false, false, false, false, false, false, false, false,
+ false, false, false, false, false, false, false, false, // 255
+};
+// token delimiters are ' ', '\n', '\r', '\t', ':', ';'
+static const bool is_delimiter_Array[ 256 ] =
+{ false, false, false, false, false, false, false, false, // 0-7
+ false, true, true, false, false, true, false, false, // 8-15
+ false, false, false, false, false, false, false, false, // 16-23
+ false, false, false, false, false, false, false, false, // 24-31
+ true, false, false, false, false, false, false, false, // 32-39
+ false, false, false, false, false, false, false, false, // 40-47
+ false, false, false, false, false, false, false, false, // 48-55
+ false, false, true, true, false, false, false, false, // 56-63
+
+ false, false, false, false, false, false, false, false, // 64 -
+ false, false, false, false, false, false, false, false,
+ false, false, false, false, false, false, false, false,
+ false, false, false, false, false, false, false, false,
+ false, false, false, false, false, false, false, false,
+ false, false, false, false, false, false, false, false,
+ false, false, false, false, false, false, false, false,
+ false, false, false, false, false, false, false, false, // 127
+
+ false, false, false, false, false, false, false, false, // 128 -
+ false, false, false, false, false, false, false, false,
+ false, false, false, false, false, false, false, false,
+ false, false, false, false, false, false, false, false,
+ false, false, false, false, false, false, false, false,
+ false, false, false, false, false, false, false, false,
+ false, false, false, false, false, false, false, false,
+ false, false, false, false, false, false, false, false, // 191
+
+ false, false, false, false, false, false, false, false, // 192 -
+ false, false, false, false, false, false, false, false,
+ false, false, false, false, false, false, false, false,
+ false, false, false, false, false, false, false, false,
+ false, false, false, false, false, false, false, false,
+ false, false, false, false, false, false, false, false,
+ false, false, false, false, false, false, false, false,
+ false, false, false, false, false, false, false, false, // 255
+};
+static char *token( FileInputStream* stream, int& rLen )
+{
+ static char ident[MAX_NAME]; /* storage buffer for keywords */
+
+ int ch, idx;
+
+ /* skip over white space */
+ // relies on EOF = -1
+ while( is_white_Array[ (ch = stream->getChar()) & 255 ] )
+ ;
+
+ idx = 0;
+ while( ch != -1 && ! is_delimiter_Array[ ch & 255 ] && idx < MAX_NAME-1 )
+ {
+ ident[idx++] = ch;
+ ch = stream->getChar();
+ }
+
+ if (ch == -1 && idx < 1) return (nullptr);
+ if (idx >= 1 && ch != ':' && ch != -1) stream->ungetChar();
+ if (idx < 1 ) ident[idx++] = ch; /* single-character token */
+ ident[idx] = 0;
+ rLen = idx;
+
+ return ident; /* returns pointer to the token */
+
+} /* token */
+
+/*************************** linetoken *************************/
+
+/* "linetoken" will get read all tokens until the EOL character from
+ * the given stream. This is used to get any arguments that can be
+ * more than one word (like Comment lines and FullName).
+ */
+
+static char *linetoken( FileInputStream* stream )
+{
+ static char ident[MAX_NAME]; /* storage buffer for keywords */
+ int ch, idx;
+
+ while ((ch = stream->getChar()) == ' ' || ch == '\t' ) ;
+
+ idx = 0;
+ while (ch != -1 && ch != lineterm && ch != '\r' && idx < MAX_NAME-1 )
+ {
+ ident[idx++] = ch;
+ ch = stream->getChar();
+ } /* while */
+
+ stream->ungetChar();
+ ident[idx] = 0;
+
+ return ident; /* returns pointer to the token */
+
+} /* linetoken */
+
+/*************************** recognize *************************/
+
+/* This function tries to match a string to a known list of
+ * valid AFM entries (check the keyStrings array above).
+ * "ident" contains everything from white space through the
+ * next space, tab, or ":" character.
+ *
+ * The algorithm is a standard Knuth binary search.
+ */
+#if defined __clang__
+#if __has_warning("-Wdeprecated-register")
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Wdeprecated-register"
+#endif
+#endif
+#include "afm_hash.hpp"
+#if defined __clang__
+#if __has_warning("-Wdeprecated-register")
+#pragma GCC diagnostic pop
+#endif
+#endif
+
+static inline enum parseKey recognize( char* ident, int len)
+{
+ const hash_entry* pEntry = AfmKeywordHash::in_word_set( ident, len );
+ return pEntry ? pEntry->eKey : NOPE;
+
+} /* recognize */
+
+/************************* parseGlobals *****************************/
+
+/* This function is called by "parseFile". It will parse the AFM file
+ * up to the "StartCharMetrics" keyword, which essentially marks the
+ * end of the Global Font Information and the beginning of the character
+ * metrics information.
+ *
+ * If the caller of "parseFile" specified that it wanted the Global
+ * Font Information (as defined by the "AFM file Specification"
+ * document), then that information will be stored in the returned
+ * data structure.
+ *
+ * Any Global Font Information entries that are not found in a
+ * given file, will have the usual default initialization value
+ * for its type (i.e. entries of type int will be 0, etc).
+ *
+ * This function returns an error code specifying whether there was
+ * a premature EOF or a parsing error. This return value is used by
+ * parseFile to determine if there is more file to parse.
+ */
+
+static int parseGlobals( FileInputStream* fp, GlobalFontInfo* gfi )
+{
+ bool cont = true, save = (gfi != nullptr);
+ int error = ok;
+ int direction = -1;
+ int tokenlen;
+
+ while (cont)
+ {
+ char *keyword = token(fp, tokenlen);
+
+ if (keyword == nullptr)
+ /* Have reached an early and unexpected EOF. */
+ /* Set flag and stop parsing */
+ {
+ error = earlyEOF;
+ break; /* get out of loop */
+ }
+ if (!save)
+ /* get tokens until the end of the Global Font info section */
+ /* without saving any of the data */
+ switch (recognize(keyword, tokenlen))
+ {
+ case STARTCHARMETRICS:
+ cont = false;
+ break;
+ case ENDFONTMETRICS:
+ cont = false;
+ error = normalEOF;
+ break;
+ default:
+ break;
+ } /* switch */
+ else
+ /* otherwise parse entire global font info section, */
+ /* saving the data */
+ switch(recognize(keyword, tokenlen))
+ {
+ case STARTFONTMETRICS:
+ if ((keyword = token(fp,tokenlen)) != nullptr)
+ gfi->afmVersion = strdup( keyword );
+ break;
+ case COMMENT:
+ linetoken(fp);
+ break;
+ case FONTNAME:
+ if ((keyword = token(fp,tokenlen)) != nullptr)
+ gfi->fontName = strdup( keyword );
+ break;
+ case ENCODINGSCHEME:
+ if ((keyword = token(fp,tokenlen)) != nullptr)
+ gfi->encodingScheme = strdup( keyword );
+ break;
+ case FULLNAME:
+ if ((keyword = linetoken(fp)) != nullptr)
+ gfi->fullName = strdup( keyword );
+ break;
+ case FAMILYNAME:
+ if ((keyword = linetoken(fp)) != nullptr)
+ gfi->familyName = strdup( keyword );
+ break;
+ case WEIGHT:
+ if ((keyword = token(fp,tokenlen)) != nullptr)
+ gfi->weight = strdup( keyword );
+ break;
+ case ITALICANGLE:
+ if ((keyword = token(fp,tokenlen)) != nullptr)
+ gfi->italicAngle = StringToDouble( keyword );
+ break;
+ case ISFIXEDPITCH:
+ if ((keyword = token(fp,tokenlen)) != nullptr)
+ {
+ if (MATCH(keyword, False))
+ gfi->isFixedPitch = false;
+ else
+ gfi->isFixedPitch = true;
+ }
+ break;
+ case UNDERLINEPOSITION:
+ if ((keyword = token(fp,tokenlen)) != nullptr)
+ gfi->underlinePosition = atoi(keyword);
+ break;
+ case UNDERLINETHICKNESS:
+ if ((keyword = token(fp,tokenlen)) != nullptr)
+ gfi->underlineThickness = atoi(keyword);
+ break;
+ case VERSION:
+ if ((keyword = token(fp,tokenlen)) != nullptr)
+ gfi->version = strdup( keyword );
+ break;
+ case NOTICE:
+ if ((keyword = linetoken(fp)) != nullptr)
+ gfi->notice = strdup( keyword );
+ break;
+ case FONTBBOX:
+ if ((keyword = token(fp,tokenlen)) != nullptr)
+ gfi->fontBBox.llx = atoi(keyword);
+ if ((keyword = token(fp,tokenlen)) != nullptr)
+ gfi->fontBBox.lly = atoi(keyword);
+ if ((keyword = token(fp,tokenlen)) != nullptr)
+ gfi->fontBBox.urx = atoi(keyword);
+ if ((keyword = token(fp,tokenlen)) != nullptr)
+ gfi->fontBBox.ury = atoi(keyword);
+ break;
+ case CAPHEIGHT:
+ if ((keyword = token(fp,tokenlen)) != nullptr)
+ gfi->capHeight = atoi(keyword);
+ break;
+ case XHEIGHT:
+ if ((keyword = token(fp,tokenlen)) != nullptr)
+ gfi->xHeight = atoi(keyword);
+ break;
+ case DESCENT:
+ if ((keyword = token(fp,tokenlen)) != nullptr)
+ gfi->descender = -atoi(keyword);
+ break;
+ case DESCENDER:
+ if ((keyword = token(fp,tokenlen)) != nullptr)
+ gfi->descender = atoi(keyword);
+ break;
+ case ASCENT:
+ case ASCENDER:
+ if ((keyword = token(fp,tokenlen)) != nullptr)
+ gfi->ascender = atoi(keyword);
+ break;
+ case STARTCHARMETRICS:
+ cont = false;
+ break;
+ case ENDFONTMETRICS:
+ cont = false;
+ error = normalEOF;
+ break;
+ case EM:
+ // skip one token
+ token(fp,tokenlen);
+ break;
+ case STARTDIRECTION:
+ if ((keyword = token(fp,tokenlen)) != nullptr)
+ direction = atoi(keyword);
+ break; /* ignore this for now */
+ case ENDDIRECTION:
+ break; /* ignore this for now */
+ case MAPPINGSCHEME:
+ token(fp,tokenlen);
+ break; /* ignore this for now */
+ case CHARACTERS:
+ token(fp,tokenlen);
+ break; /* ignore this for now */
+ case ISBASEFONT:
+ token(fp,tokenlen);
+ break; /* ignore this for now */
+ case CHARACTERSET:
+ token(fp,tokenlen); //ignore
+ break;
+ case STDHW:
+ token(fp,tokenlen); //ignore
+ break;
+ case STDVW:
+ token(fp,tokenlen); //ignore
+ break;
+ case CHARWIDTH:
+ if ((keyword = token(fp,tokenlen)) != nullptr)
+ {
+ if (direction == 0)
+ gfi->charwidth = atoi(keyword);
+ }
+ token(fp,tokenlen);
+ /* ignore y-width for now */
+ break;
+ case METRICSSETS:
+ token(fp,tokenlen); /*eat token*/
+ break; /* ignore this for now */
+ case NOPE:
+ default:
+ error = parseError;
+ break;
+ } /* switch */
+ } /* while */
+
+ return error;
+
+} /* parseGlobals */
+
+/************************* parseCharWidths **************************/
+
+/* This function is called by "parseFile". It will parse the AFM file
+ * up to the "EndCharMetrics" keyword. It will save the character
+ * width info (as opposed to all of the character metric information)
+ * if requested by the caller of parseFile. Otherwise, it will just
+ * parse through the section without saving any information.
+ *
+ * If data is to be saved, parseCharWidths is passed in a pointer
+ * to an array of widths that has already been initialized by the
+ * standard value for unmapped character codes. This function parses
+ * the Character Metrics section only storing the width information
+ * for the encoded characters into the array using the character code
+ * as the index into that array.
+ *
+ * This function returns an error code specifying whether there was
+ * a premature EOF or a parsing error. This return value is used by
+ * parseFile to determine if there is more file to parse.
+ */
+
+static int parseCharWidths( FileInputStream* fp, int* cwi)
+{
+ bool cont = true, save = (cwi != nullptr);
+ int pos = 0, error = ok, tokenlen;
+
+ while (cont)
+ {
+ char *keyword = token(fp,tokenlen);
+ /* Have reached an early and unexpected EOF. */
+ /* Set flag and stop parsing */
+ if (keyword == nullptr)
+ {
+ error = earlyEOF;
+ break; /* get out of loop */
+ }
+ if (!save)
+ /* get tokens until the end of the Char Metrics section without */
+ /* saving any of the data*/
+ switch (recognize(keyword,tokenlen))
+ {
+ case ENDCHARMETRICS:
+ cont = false;
+ break;
+ case ENDFONTMETRICS:
+ cont = false;
+ error = normalEOF;
+ break;
+ default:
+ break;
+ } /* switch */
+ else
+ /* otherwise parse entire char metrics section, saving */
+ /* only the char x-width info */
+ switch(recognize(keyword,tokenlen))
+ {
+ case COMMENT:
+ linetoken(fp); /*eat token*/
+ break;
+ case CODE:
+ if ((keyword = token(fp,tokenlen)) != nullptr)
+ pos = atoi(keyword);
+ break;
+ case XYWIDTH:
+ /* PROBLEM: Should be no Y-WIDTH when doing "quick & dirty" */
+ token(fp,tokenlen); token(fp,tokenlen); /* eat values */
+ error = parseError;
+ break;
+ case CODEHEX:
+ if ((keyword = token(fp,tokenlen)) != nullptr)
+ sscanf(keyword, "<%x>", &pos);
+ break;
+ case X0WIDTH:
+ (void) token(fp,tokenlen);
+ break;
+ case XWIDTH:
+ if ((keyword = token(fp,tokenlen)) != nullptr)
+ if (pos >= 0) /* ignore unmapped chars */
+ cwi[pos] = atoi(keyword);
+ break;
+ case ENDCHARMETRICS:
+ cont = false;
+ break;
+ case ENDFONTMETRICS:
+ cont = false;
+ error = normalEOF;
+ break;
+ case CHARNAME: /* eat values (so doesn't cause parseError) */
+ token(fp,tokenlen);
+ break;
+ case CHARBBOX:
+ token(fp,tokenlen); token(fp,tokenlen);
+ token(fp,tokenlen); token(fp,tokenlen);
+ break;
+ case LIGATURE:
+ token(fp,tokenlen); token(fp,tokenlen);
+ break;
+ case VVECTOR:
+ token(fp,tokenlen); /*eat token*/
+ token(fp,tokenlen); /*eat token*/
+ break;
+ case NOPE:
+ default:
+ error = parseError;
+ break;
+ } /* switch */
+ } /* while */
+
+ return error;
+
+} /* parseCharWidths */
+
+/*
+ * number of char metrics is almost always inaccurate, so be gentle and try to
+ * adapt our internal storage by adjusting the allocated list
+ */
+
+static int
+reallocFontMetrics( void **pp_fontmetrics, int *p_oldcount, int n_newcount, unsigned int n_size )
+{
+ char *p_tmpmetrics = nullptr;
+
+ if ((pp_fontmetrics == nullptr) || (*pp_fontmetrics == nullptr))
+ return storageProblem;
+
+ if (*p_oldcount == n_newcount)
+ return ok;
+
+ p_tmpmetrics = static_cast<char*>(realloc(*pp_fontmetrics, n_newcount * n_size));
+ if (p_tmpmetrics == nullptr)
+ return storageProblem;
+
+ if ( n_newcount > *p_oldcount )
+ {
+ char *p_inimetrics = p_tmpmetrics + n_size * *p_oldcount;
+ int n_inimetrics = n_size * (n_newcount - *p_oldcount);
+ memset( p_inimetrics, 0, n_inimetrics );
+ }
+
+ *pp_fontmetrics = p_tmpmetrics;
+ *p_oldcount = n_newcount;
+
+ return ok;
+}
+
+static unsigned int
+enlargeCount( unsigned int n_oldcount )
+{
+ unsigned int n_newcount = n_oldcount + n_oldcount / 5;
+ if (n_oldcount == n_newcount )
+ n_newcount = n_oldcount + 5;
+
+ return n_newcount;
+}
+
+/************************* parseCharMetrics ************************/
+
+/* This function is called by parseFile if the caller of parseFile
+ * requested that all character metric information be saved
+ * (as opposed to only the character width information).
+ *
+ * parseCharMetrics is passed in a pointer to an array of records
+ * to hold information on a per character basis. This function
+ * parses the Character Metrics section storing all character
+ * metric information for the ALL characters (mapped and unmapped)
+ * into the array.
+ *
+ * This function returns an error code specifying whether there was
+ * a premature EOF or a parsing error. This return value is used by
+ * parseFile to determine if there is more file to parse.
+ */
+
+static int parseCharMetrics( FileInputStream* fp, FontInfo* fi)
+{
+ bool cont = true, firstTime = true;
+ int error = ok, count = 0, tokenlen;
+ CharMetricInfo *temp = fi->cmi;
+
+ while (cont)
+ {
+ char *keyword = token(fp,tokenlen);
+ if (keyword == nullptr)
+ {
+ error = earlyEOF;
+ break; /* get out of loop */
+ }
+ switch(recognize(keyword,tokenlen))
+ {
+ case COMMENT:
+ linetoken(fp); /*eat token*/
+ break;
+ case CODE:
+ if (!(count < fi->numOfChars))
+ {
+ reallocFontMetrics( reinterpret_cast<void**>(&fi->cmi),
+ &(fi->numOfChars), enlargeCount(fi->numOfChars),
+ sizeof(CharMetricInfo) );
+ temp = &(fi->cmi[ count - 1 ]);
+ }
+ if (count < fi->numOfChars)
+ {
+ if (firstTime) firstTime = false;
+ else temp++;
+ if ((keyword = token(fp,tokenlen)) != nullptr)
+ temp->code = atoi(keyword);
+ if (fi->gfi && fi->gfi->charwidth)
+ temp->wx = fi->gfi->charwidth;
+ count++;
+ }
+ else
+ {
+ error = parseError;
+ cont = false;
+ }
+ break;
+ case CODEHEX:
+ if (!(count < fi->numOfChars ))
+ {
+ reallocFontMetrics( reinterpret_cast<void**>(&fi->cmi),
+ &(fi->numOfChars), enlargeCount(fi->numOfChars),
+ sizeof(CharMetricInfo) );
+ temp = &(fi->cmi[ count - 1 ]);
+ }
+ if (count < fi->numOfChars) {
+ if (firstTime)
+ firstTime = false;
+ else
+ temp++;
+ if ((keyword = token(fp,tokenlen)) != nullptr)
+ sscanf(keyword,"<%x>", &temp->code);
+ if (fi->gfi && fi->gfi->charwidth)
+ temp->wx = fi->gfi->charwidth;
+ count++;
+ }
+ else {
+ error = parseError;
+ cont = false;
+ }
+ break;
+ case XYWIDTH:
+ if ((keyword = token(fp,tokenlen)) != nullptr)
+ temp->wx = atoi(keyword);
+ if ((keyword = token(fp,tokenlen)) != nullptr)
+ temp->wy = atoi(keyword);
+ break;
+ case X0WIDTH:
+ if ((keyword = token(fp,tokenlen)) != nullptr)
+ temp->wx = atoi(keyword);
+ break;
+ case XWIDTH:
+ if ((keyword = token(fp,tokenlen)) != nullptr)
+ temp->wx = atoi(keyword);
+ break;
+ case CHARNAME:
+ if ((keyword = token(fp,tokenlen)) != nullptr)
+ temp->name = strdup(keyword);
+ break;
+ case CHARBBOX:
+ if ((keyword = token(fp,tokenlen)) != nullptr)
+ temp->charBBox.llx = atoi(keyword);
+ if ((keyword = token(fp,tokenlen)) != nullptr)
+ temp->charBBox.lly = atoi(keyword);
+ if ((keyword = token(fp,tokenlen)) != nullptr)
+ temp->charBBox.urx = atoi(keyword);
+ if ((keyword = token(fp,tokenlen)) != nullptr)
+ temp->charBBox.ury = atoi(keyword);
+ break;
+ case LIGATURE: {
+ Ligature **tail = &(temp->ligs);
+ Ligature *node = *tail;
+
+ if (*tail != nullptr)
+ {
+ while (node->next != nullptr)
+ node = node->next;
+ tail = &(node->next);
+ }
+
+ *tail = static_cast<Ligature *>(calloc(1, sizeof(Ligature)));
+ if ((keyword = token(fp,tokenlen)) != nullptr)
+ (*tail)->succ = strdup(keyword);
+ if ((keyword = token(fp,tokenlen)) != nullptr)
+ (*tail)->lig = strdup(keyword);
+ break; }
+ case ENDCHARMETRICS:
+ cont = false;
+ break;
+ case ENDFONTMETRICS:
+ cont = false;
+ error = normalEOF;
+ break;
+ case VVECTOR:
+ token(fp,tokenlen); /*eat token*/
+ token(fp,tokenlen); /*eat token*/
+ break;
+ case NOPE:
+ default:
+ error = parseError;
+ break;
+ } /* switch */
+ } /* while */
+
+ if ((error == ok) && (count != fi->numOfChars))
+ error = reallocFontMetrics( reinterpret_cast<void**>(&fi->cmi), &(fi->numOfChars),
+ count, sizeof(CharMetricInfo) );
+
+ if ((error == ok) && (count != fi->numOfChars))
+ error = parseError;
+
+ return error;
+
+} /* parseCharMetrics */
+
+/************************* parseTrackKernData ***********************/
+
+/* This function is called by "parseFile". It will parse the AFM file
+ * up to the "EndTrackKern" or "EndKernData" keywords. It will save the
+ * track kerning data if requested by the caller of parseFile.
+ *
+ * parseTrackKernData is passed in a pointer to the FontInfo record.
+ * If data is to be saved, the FontInfo record will already contain
+ * a valid pointer to storage for the track kerning data.
+ *
+ * This function returns an error code specifying whether there was
+ * a premature EOF or a parsing error. This return value is used by
+ * parseFile to determine if there is more file to parse.
+ */
+
+static int parseTrackKernData( FileInputStream* fp, FontInfo* fi)
+{
+ bool cont = true, save = (fi->tkd != nullptr);
+ int pos = 0, error = ok, tcount = 0, tokenlen;
+
+ while (cont)
+ {
+ char *keyword = token(fp,tokenlen);
+
+ if (keyword == nullptr)
+ {
+ error = earlyEOF;
+ break; /* get out of loop */
+ }
+ if (!save)
+ /* get tokens until the end of the Track Kerning Data */
+ /* section without saving any of the data */
+ switch(recognize(keyword,tokenlen))
+ {
+ case ENDTRACKKERN:
+ case ENDKERNDATA:
+ cont = false;
+ break;
+ case ENDFONTMETRICS:
+ cont = false;
+ error = normalEOF;
+ break;
+ default:
+ break;
+ } /* switch */
+ else
+ /* otherwise parse entire Track Kerning Data section, */
+ /* saving the data */
+ switch(recognize(keyword,tokenlen))
+ {
+ case COMMENT:
+ linetoken(fp); /*eat token*/
+ break;
+ case TRACKKERN:
+ if (!(tcount < fi->numOfTracks))
+ {
+ reallocFontMetrics( reinterpret_cast<void**>(&fi->tkd), &(fi->numOfTracks),
+ enlargeCount(fi->numOfTracks), sizeof(TrackKernData) );
+ }
+
+ if (tcount < fi->numOfTracks)
+ {
+ if ((keyword = token(fp,tokenlen)) != nullptr)
+ fi->tkd[pos].degree = atoi(keyword);
+ if ((keyword = token(fp,tokenlen)) != nullptr)
+ fi->tkd[pos].minPtSize = StringToDouble(keyword);
+ if ((keyword = token(fp,tokenlen)) != nullptr)
+ fi->tkd[pos].minKernAmt = StringToDouble(keyword);
+ if ((keyword = token(fp,tokenlen)) != nullptr)
+ fi->tkd[pos].maxPtSize = StringToDouble(keyword);
+ if ((keyword = token(fp,tokenlen)) != nullptr)
+ fi->tkd[pos++].maxKernAmt = StringToDouble(keyword);
+ tcount++;
+ }
+ else
+ {
+ error = parseError;
+ cont = false;
+ }
+ break;
+ case ENDTRACKKERN:
+ case ENDKERNDATA:
+ cont = false;
+ break;
+ case ENDFONTMETRICS:
+ cont = false;
+ error = normalEOF;
+ break;
+ case NOPE:
+ default:
+ error = parseError;
+ break;
+ } /* switch */
+ } /* while */
+
+ if (error == ok && tcount != fi->numOfTracks)
+ error = reallocFontMetrics( reinterpret_cast<void**>(&fi->tkd), &(fi->numOfTracks),
+ tcount, sizeof(TrackKernData) );
+
+ if (error == ok && tcount != fi->numOfTracks)
+ error = parseError;
+
+ return error;
+
+} /* parseTrackKernData */
+
+/************************* parsePairKernData ************************/
+
+/* This function is called by "parseFile". It will parse the AFM file
+ * up to the "EndKernPairs" or "EndKernData" keywords. It will save
+ * the pair kerning data if requested by the caller of parseFile.
+ *
+ * parsePairKernData is passed in a pointer to the FontInfo record.
+ * If data is to be saved, the FontInfo record will already contain
+ * a valid pointer to storage for the pair kerning data.
+ *
+ * This function returns an error code specifying whether there was
+ * a premature EOF or a parsing error. This return value is used by
+ * parseFile to determine if there is more file to parse.
+ */
+
+static int parsePairKernData( FileInputStream* fp, FontInfo* fi)
+{
+ bool cont = true, save = (fi->pkd != nullptr);
+ int pos = 0, error = ok, pcount = 0, tokenlen;
+
+ while (cont)
+ {
+ char *keyword = token(fp,tokenlen);
+
+ if (keyword == nullptr)
+ {
+ error = earlyEOF;
+ break; /* get out of loop */
+ }
+ if (!save)
+ /* get tokens until the end of the Pair Kerning Data */
+ /* section without saving any of the data */
+ switch(recognize(keyword,tokenlen))
+ {
+ case ENDKERNPAIRS:
+ case ENDKERNDATA:
+ cont = false;
+ break;
+ case ENDFONTMETRICS:
+ cont = false;
+ error = normalEOF;
+ break;
+ default:
+ break;
+ } /* switch */
+ else
+ /* otherwise parse entire Pair Kerning Data section, */
+ /* saving the data */
+ switch(recognize(keyword,tokenlen))
+ {
+ case COMMENT:
+ linetoken(fp); /*eat token*/
+ break;
+ case KERNPAIR:
+ if (!(pcount < fi->numOfPairs))
+ {
+ reallocFontMetrics( reinterpret_cast<void**>(&fi->pkd), &(fi->numOfPairs),
+ enlargeCount(fi->numOfPairs), sizeof(PairKernData) );
+ }
+ if (pcount < fi->numOfPairs)
+ {
+ if ((keyword = token(fp,tokenlen)) != nullptr)
+ fi->pkd[pos].name1 = strdup( keyword );
+ if ((keyword = token(fp,tokenlen)) != nullptr)
+ fi->pkd[pos].name2 = strdup( keyword );
+ if ((keyword = token(fp,tokenlen)) != nullptr)
+ fi->pkd[pos].xamt = atoi(keyword);
+ if ((keyword = token(fp,tokenlen)) != nullptr)
+ fi->pkd[pos++].yamt = atoi(keyword);
+ pcount++;
+ }
+ else
+ {
+ error = parseError;
+ cont = false;
+ }
+ break;
+ case KERNPAIRXAMT:
+ if (!(pcount < fi->numOfPairs))
+ {
+ reallocFontMetrics( reinterpret_cast<void**>(&fi->pkd), &(fi->numOfPairs),
+ enlargeCount(fi->numOfPairs), sizeof(PairKernData) );
+ }
+ if (pcount < fi->numOfPairs)
+ {
+ if ((keyword = token(fp,tokenlen)) != nullptr)
+ fi->pkd[pos].name1 = strdup( keyword );
+ if ((keyword = token(fp,tokenlen)) != nullptr)
+ fi->pkd[pos].name2 = strdup( keyword );
+ if ((keyword = token(fp,tokenlen)) != nullptr)
+ fi->pkd[pos++].xamt = atoi(keyword);
+ pcount++;
+ }
+ else
+ {
+ error = parseError;
+ cont = false;
+ }
+ break;
+ case ENDKERNPAIRS:
+ case ENDKERNDATA:
+ cont = false;
+ break;
+ case ENDFONTMETRICS:
+ cont = false;
+ error = normalEOF;
+ break;
+ case NOPE:
+ default:
+ error = parseError;
+ break;
+ } /* switch */
+ } /* while */
+
+ if ((error == ok) && (pcount != fi->numOfPairs))
+ error = reallocFontMetrics( reinterpret_cast<void**>(&fi->pkd), &(fi->numOfPairs),
+ pcount, sizeof(PairKernData) );
+
+ if (error == ok && pcount != fi->numOfPairs)
+ error = parseError;
+
+ return error;
+
+} /* parsePairKernData */
+
+/************************* parseCompCharData **************************/
+
+/* This function is called by "parseFile". It will parse the AFM file
+ * up to the "EndComposites" keyword. It will save the composite
+ * character data if requested by the caller of parseFile.
+ *
+ * parseCompCharData is passed in a pointer to the FontInfo record, and
+ * a boolean representing if the data should be saved.
+ *
+ * This function will create the appropriate amount of storage for
+ * the composite character data and store a pointer to the storage
+ * in the FontInfo record.
+ *
+ * This function returns an error code specifying whether there was
+ * a premature EOF or a parsing error. This return value is used by
+ * parseFile to determine if there is more file to parse.
+ */
+
+static int parseCompCharData( FileInputStream* fp, FontInfo* fi)
+{
+ bool cont = true, firstTime = true, save = (fi->ccd != nullptr);
+ int pos = 0, j = 0, error = ok, ccount = 0, pcount = 0, tokenlen;
+
+ while (cont)
+ {
+ char *keyword = token(fp,tokenlen);
+ if (keyword == nullptr)
+ /* Have reached an early and unexpected EOF. */
+ /* Set flag and stop parsing */
+ {
+ error = earlyEOF;
+ break; /* get out of loop */
+ }
+ if (ccount > fi->numOfComps)
+ {
+ reallocFontMetrics( reinterpret_cast<void**>(&fi->ccd), &(fi->numOfComps),
+ enlargeCount(fi->numOfComps), sizeof(CompCharData) );
+ }
+ if (ccount > fi->numOfComps)
+ {
+ error = parseError;
+ break; /* get out of loop */
+ }
+ if (!save)
+ /* get tokens until the end of the Composite Character info */
+ /* section without saving any of the data */
+ switch(recognize(keyword,tokenlen))
+ {
+ case ENDCOMPOSITES:
+ cont = false;
+ break;
+ case ENDFONTMETRICS:
+ cont = false;
+ error = normalEOF;
+ break;
+ case COMMENT:
+ case COMPCHAR:
+ linetoken(fp);
+ break;
+ default:
+ break;
+ } /* switch */
+ else
+ /* otherwise parse entire Composite Character info section, */
+ /* saving the data */
+ switch(recognize(keyword,tokenlen))
+ {
+ case COMMENT:
+ linetoken(fp); /*eat token*/
+ break;
+ case COMPCHAR:
+ if (!(ccount < fi->numOfComps))
+ {
+ reallocFontMetrics( reinterpret_cast<void**>(&fi->ccd), &(fi->numOfComps),
+ enlargeCount(fi->numOfComps), sizeof(CompCharData) );
+ }
+ if (ccount < fi->numOfComps)
+ {
+ keyword = token(fp,tokenlen);
+ if (pcount != fi->ccd[pos].numOfPieces)
+ error = parseError;
+ pcount = 0;
+ if (firstTime) firstTime = false;
+ else pos++;
+ fi->ccd[pos].ccName = strdup( keyword );
+ if ((keyword = token(fp,tokenlen)) != nullptr)
+ fi->ccd[pos].numOfPieces = atoi(keyword);
+ fi->ccd[pos].pieces = static_cast<Pcc *>(
+ calloc(fi->ccd[pos].numOfPieces, sizeof(Pcc)));
+ j = 0;
+ ccount++;
+ }
+ else
+ {
+ error = parseError;
+ cont = false;
+ }
+ break;
+ case COMPCHARPIECE:
+ if (pcount < fi->ccd[pos].numOfPieces)
+ {
+ if ((keyword = token(fp,tokenlen)) != nullptr)
+ fi->ccd[pos].pieces[j].pccName = strdup( keyword );
+ if ((keyword = token(fp,tokenlen)) != nullptr)
+ fi->ccd[pos].pieces[j].deltax = atoi(keyword);
+ if ((keyword = token(fp,tokenlen)) != nullptr)
+ fi->ccd[pos].pieces[j++].deltay = atoi(keyword);
+ pcount++;
+ }
+ else
+ error = parseError;
+ break;
+ case ENDCOMPOSITES:
+ cont = false;
+ break;
+ case ENDFONTMETRICS:
+ cont = false;
+ error = normalEOF;
+ break;
+ case NOPE:
+ default:
+ error = parseError;
+ break;
+ } /* switch */
+ } /* while */
+
+ if (error == ok && ccount != fi->numOfComps)
+ reallocFontMetrics( reinterpret_cast<void**>(&fi->ccd), &(fi->numOfComps),
+ ccount, sizeof(CompCharData) );
+
+ if (error == ok && ccount != fi->numOfComps)
+ error = parseError;
+
+ return error;
+
+} /* parseCompCharData */
+
+/*************************** 'PUBLIC' FUNCTION ********************/
+
+/*************************** parseFile *****************************/
+
+/* parseFile is the only 'public' procedure available. It is called
+ * from an application wishing to get information from an AFM file.
+ * The caller of this function is responsible for locating and opening
+ * an AFM file and handling all errors associated with that task.
+ *
+ * parseFile expects 3 parameters: a filename pointer, a pointer
+ * to a (FontInfo *) variable (for which storage will be allocated and
+ * the data requested filled in), and a mask specifying which
+ * data from the AFM file should be saved in the FontInfo structure.
+ *
+ * The file will be parsed and the requested data will be stored in
+ * a record of type FontInfo (refer to ParseAFM.h).
+ *
+ * parseFile returns an error code as defined in parseAFM.h.
+ *
+ * The position of the read/write pointer associated with the file
+ * pointer upon return of this function is undefined.
+ */
+
+int parseFile( const char* pFilename, FontInfo** fi, FLAGS flags)
+{
+ FileInputStream aFile( pFilename );
+
+ int code = ok; /* return code from each of the parsing routines */
+ int error = ok; /* used as the return code from this function */
+ int tokenlen;
+
+ char *keyword; /* used to store a token */
+
+ (*fi) = static_cast<FontInfo *>(calloc(1, sizeof(FontInfo)));
+ if ((*fi) == nullptr) { error = storageProblem; return error; }
+
+ if (flags & P_G)
+ {
+ (*fi)->gfi = static_cast<GlobalFontInfo *>(calloc(1, sizeof(GlobalFontInfo)));
+ if ((*fi)->gfi == nullptr) { error = storageProblem; return error; }
+ }
+
+ /* The AFM file begins with Global Font Information. This section */
+ /* will be parsed whether or not information should be saved. */
+ code = parseGlobals(&aFile, (*fi)->gfi);
+
+ if (code < 0) error = code;
+
+ /* The Global Font Information is followed by the Character Metrics */
+ /* section. Which procedure is used to parse this section depends on */
+ /* how much information should be saved. If all of the metrics info */
+ /* is wanted, parseCharMetrics is called. If only the character widths */
+ /* is wanted, parseCharWidths is called. parseCharWidths will also */
+ /* be called in the case that no character data is to be saved, just */
+ /* to parse through the section. */
+
+ if ((code != normalEOF) && (code != earlyEOF))
+ {
+ if ((keyword = token(&aFile,tokenlen)) != nullptr)
+ (*fi)->numOfChars = atoi(keyword);
+ if (flags & (P_M ^ P_W))
+ {
+ (*fi)->cmi = static_cast<CharMetricInfo *>(
+ calloc((*fi)->numOfChars, sizeof(CharMetricInfo)));
+ if ((*fi)->cmi == nullptr) { error = storageProblem; return error; }
+ code = parseCharMetrics(&aFile, *fi);
+ }
+ else
+ {
+ if (flags & P_W)
+ {
+ (*fi)->cwi = static_cast<int *>(calloc(256, sizeof(int)));
+ if ((*fi)->cwi == nullptr)
+ {
+ error = storageProblem;
+ return error;
+ }
+ }
+ /* parse section regardless */
+ code = parseCharWidths(&aFile, (*fi)->cwi);
+ } /* else */
+ } /* if */
+
+ if ((error != earlyEOF) && (code < 0)) error = code;
+
+ /* The remaining sections of the AFM are optional. This code will */
+ /* look at the next keyword in the file to determine what section */
+ /* is next, and then allocate the appropriate amount of storage */
+ /* for the data (if the data is to be saved) and call the */
+ /* appropriate parsing routine to parse the section. */
+
+ while ((code != normalEOF) && (code != earlyEOF))
+ {
+ keyword = token(&aFile,tokenlen);
+ if (keyword == nullptr)
+ /* Have reached an early and unexpected EOF. */
+ /* Set flag and stop parsing */
+ {
+ code = earlyEOF;
+ break; /* get out of loop */
+ }
+ switch(recognize(keyword,tokenlen))
+ {
+ case STARTKERNDATA:
+ break;
+ case ENDKERNDATA:
+ break;
+ case STARTTRACKKERN:
+ keyword = token(&aFile,tokenlen);
+ if ((flags & P_T) && keyword)
+ {
+ (*fi)->numOfTracks = atoi(keyword);
+ (*fi)->tkd = static_cast<TrackKernData *>(
+ calloc((*fi)->numOfTracks, sizeof(TrackKernData)));
+ if ((*fi)->tkd == nullptr)
+ {
+ error = storageProblem;
+ return error;
+ }
+ } /* if */
+ code = parseTrackKernData(&aFile, *fi);
+ break;
+ case STARTKERNPAIRS:
+ keyword = token(&aFile,tokenlen);
+ if ((flags & P_P) && keyword)
+ {
+ (*fi)->numOfPairs = atoi(keyword);
+ (*fi)->pkd = static_cast<PairKernData *>(
+ calloc((*fi)->numOfPairs, sizeof(PairKernData)));
+ if ((*fi)->pkd == nullptr)
+ {
+ error = storageProblem;
+ return error;
+ }
+ } /* if */
+ code = parsePairKernData(&aFile, *fi);
+ break;
+ case STARTCOMPOSITES:
+ keyword = token(&aFile,tokenlen);
+ if ((flags & P_C) && keyword)
+ {
+ (*fi)->numOfComps = atoi(keyword);
+ (*fi)->ccd = static_cast<CompCharData *>(
+ calloc((*fi)->numOfComps, sizeof(CompCharData)));
+ if ((*fi)->ccd == nullptr)
+ {
+ error = storageProblem;
+ return error;
+ }
+ } /* if */
+ code = parseCompCharData(&aFile, *fi);
+ break;
+ case ENDFONTMETRICS:
+ code = normalEOF;
+ break;
+ case COMMENT:
+ linetoken(&aFile);
+ break;
+ case NOPE:
+ default:
+ code = parseError;
+ break;
+ } /* switch */
+
+ if ((error != earlyEOF) && (code < 0)) error = code;
+
+ } /* while */
+
+ if ((error != earlyEOF) && (code < 0)) error = code;
+
+ return error;
+
+} /* parseFile */
+
+void
+freeFontInfo (FontInfo *fi)
+{
+ int i;
+
+ if (fi->gfi)
+ {
+ free (fi->gfi->afmVersion);
+ free (fi->gfi->fontName);
+ free (fi->gfi->fullName);
+ free (fi->gfi->familyName);
+ free (fi->gfi->weight);
+ free (fi->gfi->version);
+ free (fi->gfi->notice);
+ free (fi->gfi->encodingScheme);
+ free (fi->gfi);
+ }
+
+ free (fi->cwi);
+
+ if (fi->cmi)
+ {
+ for (i = 0; i < fi->numOfChars; i++)
+ {
+ Ligature *ligs;
+ free (fi->cmi[i].name);
+ ligs = fi->cmi[i].ligs;
+ while (ligs)
+ {
+ Ligature *tmp;
+ tmp = ligs;
+ ligs = ligs->next;
+ free (tmp->succ);
+ free (tmp->lig);
+ free (tmp);
+ }
+ }
+ free (fi->cmi);
+ }
+
+ free (fi->tkd);
+
+ if (fi->pkd)
+ {
+ for ( i = 0; i < fi->numOfPairs; i++)
+ {
+ free (fi->pkd[i].name1);
+ free (fi->pkd[i].name2);
+ }
+ free (fi->pkd);
+ }
+
+ if (fi->ccd)
+ {
+ for (i = 0; i < fi->numOfComps; i++)
+ {
+ free (fi->ccd[i].ccName);
+ int j;
+ for (j = 0; j < fi->ccd[i].numOfPieces; j++)
+ free (fi->ccd[i].pieces[j].pccName);
+
+ free (fi->ccd[i].pieces);
+ }
+ free (fi->ccd);
+ }
+
+ free (fi);
+}
+
+} // namspace
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/vcl/unx/generic/fontmanager/parseAFM.hxx b/vcl/unx/generic/fontmanager/parseAFM.hxx
new file mode 100644
index 000000000000..a31217b9a14f
--- /dev/null
+++ b/vcl/unx/generic/fontmanager/parseAFM.hxx
@@ -0,0 +1,323 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * (C) 1988, 1989 by Adobe Systems Incorporated. All rights reserved.
+ *
+ * This file may be freely copied and redistributed as long as:
+ * 1) This entire notice continues to be included in the file,
+ * 2) If the file has been modified in any way, a notice of such
+ * modification is conspicuously indicated.
+ *
+ * PostScript, Display PostScript, and Adobe are registered trademarks of
+ * Adobe Systems Incorporated.
+ *
+ * ************************************************************************
+ * THE INFORMATION BELOW IS FURNISHED AS IS, IS SUBJECT TO CHANGE WITHOUT
+ * NOTICE, AND SHOULD NOT BE CONSTRUED AS A COMMITMENT BY ADOBE SYSTEMS
+ * INCORPORATED. ADOBE SYSTEMS INCORPORATED ASSUMES NO RESPONSIBILITY OR
+ * LIABILITY FOR ANY ERRORS OR INACCURACIES, MAKES NO WARRANTY OF ANY
+ * KIND (EXPRESS, IMPLIED OR STATUTORY) WITH RESPECT TO THIS INFORMATION,
+ * AND EXPRESSLY DISCLAIMS ANY AND ALL WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR PARTICULAR PURPOSES AND NONINFRINGEMENT OF THIRD PARTY RIGHTS.
+ * ************************************************************************
+ */
+
+/*
+ * Changes made for OpenOffice.org
+ *
+ * 10/24/2000 pl - changed code to compile with c++-compilers
+ * - added namespace to avoid symbol clashes
+ * - replaced BOOL by bool
+ * - added function to free space allocated by parseFile
+ * 10/26/2000 pl - added additional keys
+ * - added ability to parse slightly broken files
+ * - added charwidth member to GlobalFontInfo
+ * 04/26/2001 pl - added OpenOffice header
+ * 10/19/2005 pl - changed parseFile to accept a file name instead of a stream
+ */
+
+/* ParseAFM.h
+ *
+ * This header file is used in conjunction with the parseAFM.c file.
+ * Together these files provide the functionality to parse Adobe Font
+ * Metrics files and store the information in predefined data structures.
+ * It is intended to work with an application program that needs font metric
+ * information. The program can be used as is by making a procedure call to
+ * parse an AFM file and have the data stored, or an application developer
+ * may wish to customize the code.
+ *
+ * This header file defines the data structures used as well as the key
+ * strings that are currently recognized by this version of the AFM parser.
+ * This program is based on the document "Adobe Font Metrics Files,
+ * Specification Version 2.0".
+ *
+ * AFM files are separated into distinct sections of different data. Because
+ * of this, the parseAFM program can parse a specified file to only save
+ * certain sections of information based on the application's needs. A record
+ * containing the requested information will be returned to the application.
+ *
+ * AFM files are divided into five sections of data:
+ * 1) The Global Font Information
+ * 2) The Character Metrics Information
+ * 3) The Track Kerning Data
+ * 4) The Pair-Wise Kerning Data
+ * 5) The Composite Character Data
+ *
+ * Basically, the application can request any of these sections independent
+ * of what other sections are requested. In addition, in recognizing that
+ * many applications will want ONLY the x-width of characters and not all
+ * of the other character metrics information, there is a way to receive
+ * only the width information so as not to pay the storage cost for the
+ * unwanted data. An application should never request both the
+ * "quick and dirty" char metrics (widths only) and the Character Metrics
+ * Information since the Character Metrics Information will contain all
+ * of the character widths as well.
+ *
+ * There is a procedure in parseAFM.c, called parseFile, that can be
+ * called from any application wishing to get information from the AFM File.
+ * This procedure expects 3 parameters: a valid file descriptor, a pointer
+ * to a (FontInfo *) variable (for which space will be allocated and then
+ * will be filled in with the data requested), and a mask specifying
+ * which data from the AFM File should be saved in the FontInfo structure.
+ *
+ * The flags that can be used to set the appropriate mask are defined below.
+ * In addition, several commonly used masks have already been defined.
+ *
+ * History:
+ * original: DSM Thu Oct 20 17:39:59 PDT 1988
+ * modified: DSM Mon Jul 3 14:17:50 PDT 1989
+ * - added 'storageProblem' return code
+ * - fixed typos
+ */
+
+#ifndef INCLUDED_VCL_GENERIC_FONTMANAGER_PARSEAFM_HXX
+#define INCLUDED_VCL_GENERIC_FONTMANAGER_PARSEAFM_HXX
+
+
+namespace psp {
+
+/* your basic constants */
+#define EOL '\n' /* end-of-line indicator */
+#define MAX_NAME 4096 /* max length for identifiers */
+#define FLAGS int
+
+/* Flags that can be AND'ed together to specify exactly what
+ * information from the AFM file should be saved.
+ */
+#define P_G 0x01 /* 0000 0001 */ /* Global Font Info */
+#define P_W 0x02 /* 0000 0010 */ /* Character Widths ONLY */
+#define P_M 0x06 /* 0000 0110 */ /* All Char Metric Info */
+#define P_P 0x08 /* 0000 1000 */ /* Pair Kerning Info */
+#define P_T 0x10 /* 0001 0000 */ /* Track Kerning Info */
+#define P_C 0x20 /* 0010 0000 */ /* Composite Char Info */
+
+/* Commonly used flags
+ */
+#define P_GW (P_G | P_W)
+#define P_GM (P_G | P_M)
+#define P_GMP (P_G | P_M | P_P)
+#define P_GMK (P_G | P_M | P_P | P_T)
+#define P_ALL (P_G | P_M | P_P | P_T | P_C)
+
+/* Possible return codes from the parseFile procedure.
+ *
+ * ok means there were no problems parsing the file.
+ *
+ * parseError means that there was some kind of parsing error, but the
+ * parser went on. This could include problems like the count for any given
+ * section does not add up to how many entries there actually were, or
+ * there was a key that was not recognized. The return record may contain
+ * valid data or it may not.
+ *
+ * earlyEOF means that an End of File was encountered before expected. This
+ * may mean that the AFM file had been truncated, or improperly formed.
+ *
+ * storageProblem means that there were problems allocating storage for
+ * the data structures that would have contained the AFM data.
+ */
+
+enum afmError { ok = 0, parseError = -1, earlyEOF = -2, storageProblem = -3 };
+
+/************************* TYPES *********************************/
+/* Below are all of the data structure definitions. These structures
+ * try to map as closely as possible to grouping and naming of data
+ * in the AFM Files.
+ */
+
+/* Bounding box definition. Used for the Font BBox as well as the
+ * Character BBox.
+ */
+typedef struct
+{
+ int llx; /* lower left x-position */
+ int lly; /* lower left y-position */
+ int urx; /* upper right x-position */
+ int ury; /* upper right y-position */
+} BBox;
+
+/* Global Font information.
+ * The key that each field is associated with is in comments. For an
+ * explanation about each key and its value please refer to the AFM
+ * documentation (full title & version given above).
+ */
+typedef struct
+{
+ char *afmVersion; /* key: StartFontMetrics */
+ char *fontName; /* key: FontName */
+ char *fullName; /* key: FullName */
+ char *familyName; /* key: FamilyName */
+ char *weight; /* key: Weight */
+ float italicAngle; /* key: ItalicAngle */
+ bool isFixedPitch; /* key: IsFixedPitch */
+ BBox fontBBox; /* key: FontBBox */
+ int underlinePosition; /* key: UnderlinePosition */
+ int underlineThickness; /* key: UnderlineThickness */
+ char *version; /* key: Version */
+ char *notice; /* key: Notice */
+ char *encodingScheme; /* key: EncodingScheme */
+ int capHeight; /* key: CapHeight */
+ int xHeight; /* key: XHeight */
+ int ascender; /* key: Ascender */
+ int descender; /* key: Descender */
+ int charwidth; /* key: CharWidth */
+} GlobalFontInfo;
+
+/* Ligature definition is a linked list since any character can have
+ * any number of ligatures.
+ */
+typedef struct _t_ligature
+{
+ char *succ, *lig;
+ struct _t_ligature *next;
+} Ligature;
+
+/* Character Metric Information. This structure is used only if ALL
+ * character metric information is requested. If only the character
+ * widths is requested, then only an array of the character x-widths
+ * is returned.
+ *
+ * The key that each field is associated with is in comments. For an
+ * explanation about each key and its value please refer to the
+ * Character Metrics section of the AFM documentation (full title
+ * & version given above).
+ */
+typedef struct
+{
+ int code, /* key: C */
+ wx, /* key: WX */
+ wy; /* together wx and wy are associated with key: W */
+ char *name; /* key: N */
+ BBox charBBox; /* key: B */
+ Ligature *ligs; /* key: L (linked list; not a fixed number of Ls */
+} CharMetricInfo;
+
+/* Track kerning data structure.
+ * The fields of this record are the five values associated with every
+ * TrackKern entry.
+ *
+ * For an explanation about each value please refer to the
+ * Track Kerning section of the AFM documentation (full title
+ * & version given above).
+ */
+typedef struct
+{
+ int degree;
+ float minPtSize,
+ minKernAmt,
+ maxPtSize,
+ maxKernAmt;
+} TrackKernData;
+
+/* Pair Kerning data structure.
+ * The fields of this record are the four values associated with every
+ * KP entry. For KPX entries, the yamt will be zero.
+ *
+ * For an explanation about each value please refer to the
+ * Pair Kerning section of the AFM documentation (full title
+ * & version given above).
+ */
+typedef struct
+{
+ char *name1;
+ char *name2;
+ int xamt,
+ yamt;
+} PairKernData;
+
+/* PCC is a piece of a composite character. This is a sub structure of a
+ * compCharData described below.
+ * These fields will be filled in with the values from the key PCC.
+ *
+ * For an explanation about each key and its value please refer to the
+ * Composite Character section of the AFM documentation (full title
+ * & version given above).
+ */
+typedef struct
+{
+ char *pccName;
+ int deltax,
+ deltay;
+} Pcc;
+
+/* Composite Character Information data structure.
+ * The fields ccName and numOfPieces are filled with the values associated
+ * with the key CC. The field pieces points to an array (size = numOfPieces)
+ * of information about each of the parts of the composite character. That
+ * array is filled in with the values from the key PCC.
+ *
+ * For an explanation about each key and its value please refer to the
+ * Composite Character section of the AFM documentation (full title
+ * & version given above).
+ */
+typedef struct
+{
+ char *ccName;
+ int numOfPieces;
+ Pcc *pieces;
+} CompCharData;
+
+/* FontInfo
+ * Record type containing pointers to all of the other data
+ * structures containing information about a font.
+ * A record of this type is filled with data by the
+ * parseFile function.
+ */
+typedef struct
+{
+ GlobalFontInfo *gfi; /* ptr to a GlobalFontInfo record */
+ int *cwi; /* ptr to 256 element array of just char widths */
+ int numOfChars; /* number of entries in char metrics array */
+ CharMetricInfo *cmi; /* ptr to char metrics array */
+ int numOfTracks; /* number to entries in track kerning array */
+ TrackKernData *tkd; /* ptr to track kerning array */
+ int numOfPairs; /* number to entries in pair kerning array */
+ PairKernData *pkd; /* ptr to pair kerning array */
+ int numOfComps; /* number to entries in comp char array */
+ CompCharData *ccd; /* ptr to comp char array */
+} FontInfo;
+
+/************************* PROCEDURES ****************************/
+
+/* Call this procedure to do the grunt work of parsing an AFM file.
+ *
+ * "fp" should be a valid file pointer to an AFM file.
+ *
+ * "fi" is a pointer to a pointer to a FontInfo record structure
+ * (defined above). Storage for the FontInfo structure will be
+ * allocated in parseFile and the structure will be filled in
+ * with the requested data from the AFM File.
+ *
+ * "flags" is a mask with bits set representing what data should
+ * be saved. Defined above are valid flags that can be used to set
+ * the mask, as well as a few commonly used masks.
+ *
+ * The possible return codes from parseFile are defined above.
+ */
+
+int parseFile( const char* pFilename, FontInfo **fi, FLAGS flags );
+void freeFontInfo(FontInfo *fi);
+
+} // namespace
+
+#endif // INCLUDED_VCL_GENERIC_FONTMANAGER_PARSEAFM_HXX
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */