summaryrefslogtreecommitdiff
path: root/vcl/generic
diff options
context:
space:
mode:
authorMichael Meeks <michael.meeks@suse.com>2011-09-29 12:11:28 +0100
committerMichael Meeks <michael.meeks@suse.com>2011-10-25 13:41:47 +0100
commit08fb22932015cc0d57fa1dbe422e4109cf8be071 (patch)
tree34f3d8a6e15eb83000137a3ef63607b461fd5e28 /vcl/generic
parenta77fff188b96169f74224ab118c75f4c9f14fa36 (diff)
generic: re-structure generic code to increase re-use between backends
move more chunks of unx/generic into generic/ and into libvcl itself. This allows the headless backend to remove it's X linkage finally.
Diffstat (limited to 'vcl/generic')
-rw-r--r--vcl/generic/fontmanager/Makefile32
-rw-r--r--vcl/generic/fontmanager/adobeenc.tab1087
-rwxr-xr-xvcl/generic/fontmanager/afm_keyword_list62
-rw-r--r--vcl/generic/fontmanager/fontcache.cxx814
-rw-r--r--vcl/generic/fontmanager/fontconfig.cxx1028
-rw-r--r--vcl/generic/fontmanager/fontmanager.cxx4089
-rw-r--r--vcl/generic/fontmanager/helper.cxx404
-rw-r--r--vcl/generic/fontmanager/parseAFM.cxx1492
-rw-r--r--vcl/generic/fontmanager/parseAFM.hxx337
-rw-r--r--vcl/generic/glyphs/gcach_ftyp.cxx2649
-rw-r--r--vcl/generic/glyphs/gcach_ftyp.hxx201
-rw-r--r--vcl/generic/glyphs/gcach_layout.cxx669
-rw-r--r--vcl/generic/glyphs/gcach_rbmp.cxx277
-rw-r--r--vcl/generic/glyphs/glyphcache.cxx503
-rw-r--r--vcl/generic/glyphs/graphite_serverfont.cxx154
-rw-r--r--vcl/generic/print/bitmap_gfx.cxx735
-rw-r--r--vcl/generic/print/common_gfx.cxx1287
-rw-r--r--vcl/generic/print/fontsubst.cxx227
-rw-r--r--vcl/generic/print/genprnpsp.cxx1418
-rw-r--r--vcl/generic/print/glyphset.cxx949
-rw-r--r--vcl/generic/print/glyphset.hxx137
-rw-r--r--vcl/generic/print/printerjob.cxx1196
-rw-r--r--vcl/generic/print/psheader.ps368
-rw-r--r--vcl/generic/print/pspgraphics.cxx1413
-rw-r--r--vcl/generic/print/psputil.cxx271
-rw-r--r--vcl/generic/print/psputil.hxx80
-rw-r--r--vcl/generic/print/text_gfx.cxx869
27 files changed, 22748 insertions, 0 deletions
diff --git a/vcl/generic/fontmanager/Makefile b/vcl/generic/fontmanager/Makefile
new file mode 100644
index 000000000000..7fb7b0325aea
--- /dev/null
+++ b/vcl/generic/fontmanager/Makefile
@@ -0,0 +1,32 @@
+# Version: MPL 1.1 / GPLv3+ / LGPLv3+
+#
+# The contents of this file are subject to the Mozilla Public License Version
+# 1.1 (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.mozilla.org/MPL/
+#
+# Software distributed under the License is distributed on an "AS IS" basis,
+# WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+# for the specific language governing rights and limitations under the
+# License.
+#
+# The Initial Developer of the Original Code is
+# Bjoern Michaelsen <bjoern.michaelsen@canonical.com> (Canonical Ltd.)
+# Portions created by the Initial Developer are Copyright (C) 2011 the
+# Initial Developer. All Rights Reserved.
+#
+# Contributor(s): Jan Holesovsky <kendy@suse.cz>
+#
+# Alternatively, the contents of this file may be used under the terms of
+# either the GNU General Public License Version 3 or later (the "GPLv3+"), or
+# the GNU Lesser General Public License Version 3 or later (the "LGPLv3+"),
+# in which case the provisions of the GPLv3+ or the LGPLv3+ are applicable
+# instead of those above.
+
+all : afm_hash.hpp
+
+afm_hash.hpp : $(realpath $(dir $(firstword $(MAKEFILE_LIST))))/afm_keyword_list
+ $(GPERF) -C -t -l -L C++ -m 20 -Z AfmKeywordHash -k '1,4,6,$$' $< | sed -e "s/(char\*)0/(char\*)0, NOPE/g" | grep -v "^#line" > $@
+
+.PHONY : all
+# vim: set noet sw=4:
diff --git a/vcl/generic/fontmanager/adobeenc.tab b/vcl/generic/fontmanager/adobeenc.tab
new file mode 100644
index 000000000000..492e92f3fcf2
--- /dev/null
+++ b/vcl/generic/fontmanager/adobeenc.tab
@@ -0,0 +1,1087 @@
+/*************************************************************************
+ *
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * Copyright 2000, 2010 Oracle and/or its affiliates.
+ *
+ * OpenOffice.org - a multi-platform office productivity suite
+ *
+ * This file is part of OpenOffice.org.
+ *
+ * OpenOffice.org is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License version 3
+ * only, as published by the Free Software Foundation.
+ *
+ * OpenOffice.org is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License version 3 for more details
+ * (a copy is included in the LICENSE file that accompanied this code).
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * version 3 along with OpenOffice.org. If not, see
+ * <http://www.openoffice.org/license.html>
+ * for a copy of the LGPLv3 License.
+ *
+ ************************************************************************/
+
+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/generic/fontmanager/afm_keyword_list b/vcl/generic/fontmanager/afm_keyword_list
new file mode 100755
index 000000000000..c9bb13467e3e
--- /dev/null
+++ b/vcl/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/generic/fontmanager/fontcache.cxx b/vcl/generic/fontmanager/fontcache.cxx
new file mode 100644
index 000000000000..78a16260d179
--- /dev/null
+++ b/vcl/generic/fontmanager/fontcache.cxx
@@ -0,0 +1,814 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*************************************************************************
+ *
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * Copyright 2000, 2010 Oracle and/or its affiliates.
+ *
+ * OpenOffice.org - a multi-platform office productivity suite
+ *
+ * This file is part of OpenOffice.org.
+ *
+ * OpenOffice.org is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License version 3
+ * only, as published by the Free Software Foundation.
+ *
+ * OpenOffice.org is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License version 3 for more details
+ * (a copy is included in the LICENSE file that accompanied this code).
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * version 3 along with OpenOffice.org. If not, see
+ * <http://www.openoffice.org/license.html>
+ * for a copy of the LGPLv3 License.
+ *
+ ************************************************************************/
+
+// MARKER(update_precomp.py): autogen include statement, do not remove
+#include "precompiled_vcl.hxx"
+
+#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 FONTCACHEFILE "/user/psprint/pspfontcache"
+#define CACHE_MAGIC "PspFontCacheFile format 4"
+
+using namespace std;
+using namespace psp;
+using namespace utl;
+
+using ::rtl::OUString;
+using ::rtl::OString;
+using ::rtl::OUStringToOString;
+
+/*
+ * static helpers
+ */
+
+/*
+ * FontCache constructor
+ */
+
+FontCache::FontCache()
+{
+ m_bDoFlush = false;
+ m_aCacheFile = getOfficePath( UserPath );
+ if( m_aCacheFile.Len() )
+ {
+ m_aCacheFile.AppendAscii( FONTCACHEFILE );
+ 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.Len() )
+ return;
+
+ SvFileStream aStream;
+ aStream.Open( m_aCacheFile, STREAM_WRITE | STREAM_TRUNC );
+ if( ! (aStream.IsOpen() && aStream.IsWritable()) )
+ {
+#if OSL_DEBUG_LEVEL > 1
+ fprintf( stderr, "FontCache::flush: opening cache file %s failed\n", ByteString( m_aCacheFile, osl_getThreadTextEncoding() ).GetBuffer() );
+#endif
+ return;
+ }
+
+ aStream.SetLineDelimiter( LINEEND_LF );
+ aStream.WriteLine( ByteString( 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 );
+
+ ByteString aDirectory( rManager.getDirectory( dir_it->first ) );
+ rtl::OStringBuffer aLine(
+ RTL_CONSTASCII_STRINGPARAM("FontCacheDirectory:"));
+ aLine.append(dir_it->second.m_nTimestamp);
+ aLine.append(':');
+ aLine.append(aDirectory);
+ if( rDir.empty() && dir_it->second.m_bNoFiles )
+ aLine.insert(0, RTL_CONSTASCII_STRINGPARAM("Empty"));
+ aStream.WriteLine(ByteString(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.begin() == rEntry.end() )
+ continue;
+
+ aLine.append(RTL_CONSTASCII_STRINGPARAM("File:"));
+ aLine.append(entry_it->first);
+ aStream.WriteLine(ByteString(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(ByteString(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;useroverrride;embed;antialias[;{metricfile,typeflags}][;stylename]
+ */
+ if( nEntrySize > 1 )
+ nSubEntry = static_cast<const PrintFontManager::TrueTypeFontFile*>(*it)->m_nCollectionEntry;
+ else
+ nSubEntry = -1;
+
+ 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.getLength() )
+ {
+ aLine.append(';');
+ aLine.append(OUStringToOString(rAdd, RTL_TEXTENCODING_UTF8));
+ }
+ }
+ aStream.WriteLine(ByteString(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.getLength() )
+ {
+ aLine.append(';');
+ aLine.append(OUStringToOString((*it)->m_aStyleName, RTL_TEXTENCODING_UTF8));
+ }
+ aStream.WriteLine(ByteString(aLine.makeStringAndClear()));
+ }
+ aStream.WriteLine( ByteString() );
+ }
+ }
+ m_bDoFlush = false;
+}
+
+/*
+ * FontCache::read
+ */
+
+void FontCache::read()
+{
+ PrintFontManager& rManager( PrintFontManager::get() );
+ MultiAtomProvider* pAtoms = rManager.m_pAtoms;
+
+ SvFileStream aStream( m_aCacheFile, STREAM_READ );
+ if( ! aStream.IsOpen() )
+ {
+#if OSL_DEBUG_LEVEL > 1
+ fprintf( stderr, "FontCache::read: opening cache file %s failed\n", ByteString( m_aCacheFile, osl_getThreadTextEncoding() ).GetBuffer() );
+#endif
+ return;
+ }
+
+
+ ByteString aLine;
+ aStream.ReadLine( aLine );
+ if( !aLine.Equals( CACHE_MAGIC ) )
+ {
+ #if OSL_DEBUG_LEVEL >1
+ fprintf( stderr, "FontCache::read: cache file %s fails magic test\n", ByteString( m_aCacheFile, osl_getThreadTextEncoding() ).GetBuffer() );
+ #endif
+ return;
+ }
+
+ int nDir = 0;
+ FontDirMap* pDir = NULL;
+ xub_StrLen nIndex;
+ bool bKeepOnlyUserOverridden = false;
+ do
+ {
+ aStream.ReadLine( aLine );
+ if( aLine.CompareTo( "FontCacheDirectory:", 19 ) == COMPARE_EQUAL ||
+ aLine.CompareTo( "EmptyFontCacheDirectory:", 24 ) == COMPARE_EQUAL )
+ {
+ bool bEmpty = (aLine.CompareTo( "Empty", 5 ) == COMPARE_EQUAL);
+ xub_StrLen nSearchIndex = bEmpty ? 24 : 19;
+
+ OString aDir;
+ sal_Int64 nTimestamp = 0;
+ xub_StrLen nTEnd = aLine.Search( ':', nSearchIndex );
+ if( nTEnd != STRING_NOTFOUND )
+ {
+ rtl::OString aTimeStamp = aLine.Copy( nSearchIndex, nTEnd - nSearchIndex );
+ nTimestamp = aTimeStamp.toInt64();
+ aDir = aLine.Copy( nTEnd+1 );
+ }
+ else
+ {
+ // invalid format, remove
+ pDir = NULL;
+ 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 = NULL;
+ 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 ? NULL : &m_aCache[ nDir ].m_aEntries;
+ bKeepOnlyUserOverridden = ((sal_Int64)aStat.st_mtime != nTimestamp);
+ m_aCache[ nDir ].m_bUserOverrideOnly = bKeepOnlyUserOverridden;
+ }
+ }
+ else if( pDir && aLine.CompareTo( "File:", 5 ) == COMPARE_EQUAL )
+ {
+ OString aFile( aLine.Copy( 5 ) );
+ aStream.ReadLine( aLine );
+
+ const char* pLine = aLine.GetBuffer();
+
+ fonttype::type eType = (fonttype::type)atoi( pLine );
+ if( eType != fonttype::TrueType &&
+ eType != fonttype::Type1 &&
+ eType != fonttype::Builtin
+ )
+ 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.GetBuffer();
+ int nLen = aLine.Len();
+
+ PrintFontManager::PrintFont* pFont = NULL;
+ switch( eType )
+ {
+ case fonttype::TrueType:
+ pFont = new PrintFontManager::TrueTypeFontFile();
+ break;
+ case fonttype::Type1:
+ pFont = new PrintFontManager::Type1FontFile();
+ break;
+ case fonttype::Builtin:
+ pFont = new PrintFontManager::BuiltinFont();
+ break;
+ default: break;
+ }
+
+ for( nIndex = 0; nIndex < nLen && pLine[nIndex] != ';'; nIndex++ )
+ ;
+
+ pFont->m_nFamilyName = pAtoms->getAtom( ATOM_FAMILYNAME,
+ OUString( pLine, nIndex, RTL_TEXTENCODING_UTF8 ),
+ sal_True );
+ while( nIndex < nLen )
+ {
+ xub_StrLen 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, sal_True ) );
+ }
+ }
+ aStream.ReadLine( aLine );
+ pLine = aLine.GetBuffer();
+ nLen = aLine.Len();
+
+ // 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 ), sal_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;
+ case fonttype::Builtin:
+ static_cast<PrintFontManager::BuiltinFont*>(pFont)->m_nDirectory = nDir;
+ static_cast<PrintFontManager::BuiltinFont*>(pFont)->m_aMetricFile = aFile;
+ 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 )
+ {
+ rtl::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::updateDirTimestamp
+ */
+void FontCache::updateDirTimestamp( 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;
+}
+
+
+/*
+ * FontCache::copyPrintFont
+ */
+void FontCache::copyPrintFont( const PrintFontManager::PrintFont* pFrom, PrintFontManager::PrintFont* pTo ) const
+{
+ 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;
+ case fonttype::Builtin:
+ static_cast<PrintFontManager::BuiltinFont*>(pTo)->m_nDirectory = static_cast<const PrintFontManager::BuiltinFont*>(pFrom)->m_nDirectory;
+ static_cast<PrintFontManager::BuiltinFont*>(pTo)->m_aMetricFile = static_cast<const PrintFontManager::BuiltinFont*>(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 ) const
+{
+ 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;
+ case fonttype::Builtin:
+ {
+ const PrintFontManager::BuiltinFont* pLT = static_cast<const PrintFontManager::BuiltinFont*>(pLeft);
+ const PrintFontManager::BuiltinFont* pRT = static_cast<const PrintFontManager::BuiltinFont*>(pRight);
+ if( pRT->m_nDirectory != pLT->m_nDirectory ||
+ 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 ) const
+{
+ PrintFontManager::PrintFont* pFont = NULL;
+ switch( pOldFont->m_eType )
+ {
+ case fonttype::TrueType:
+ pFont = new PrintFontManager::TrueTypeFontFile();
+ break;
+ case fonttype::Type1:
+ pFont = new PrintFontManager::Type1FontFile();
+ break;
+ case fonttype::Builtin:
+ pFont = new PrintFontManager::BuiltinFont();
+ 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;
+ case fonttype::Builtin:
+ nDirID = static_cast<const PrintFontManager::BuiltinFont*>(pFont)->m_nDirectory;
+ aFile = static_cast<const PrintFontManager::BuiltinFont*>(pFont)->m_aMetricFile;
+ break;
+ default:
+ return;
+ }
+ FontCacheData::const_iterator dir = m_aCache.find( nDirID );
+ FontDirMap::const_iterator entry;
+ FontCacheEntry::const_iterator font;
+ PrintFontManager::PrintFont* pCacheFont = NULL;
+
+ 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;
+}
+
+/*
+ * FontCache::markEmptyDir
+ */
+void FontCache::markEmptyDir( int nDirID, bool bNoFiles )
+{
+ createCacheDir( nDirID );
+ m_aCache[nDirID].m_bNoFiles = bNoFiles;
+ m_bDoFlush = true;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/vcl/generic/fontmanager/fontconfig.cxx b/vcl/generic/fontmanager/fontconfig.cxx
new file mode 100644
index 000000000000..be91349eadc4
--- /dev/null
+++ b/vcl/generic/fontmanager/fontconfig.cxx
@@ -0,0 +1,1028 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*************************************************************************
+ *
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * Copyright 2000, 2010 Oracle and/or its affiliates.
+ *
+ * OpenOffice.org - a multi-platform office productivity suite
+ *
+ * This file is part of OpenOffice.org.
+ *
+ * OpenOffice.org is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License version 3
+ * only, as published by the Free Software Foundation.
+ *
+ * OpenOffice.org is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License version 3 for more details
+ * (a copy is included in the LICENSE file that accompanied this code).
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * version 3 along with OpenOffice.org. If not, see
+ * <http://www.openoffice.org/license.html>
+ * for a copy of the LGPLv3 License.
+ *
+ ************************************************************************/
+
+// MARKER(update_precomp.py): autogen include statement, do not remove
+#include "precompiled_vcl.hxx"
+
+#include "fontcache.hxx"
+#include "impfont.hxx"
+#include "vcl/fontmanager.hxx"
+#include "vcl/vclenum.hxx"
+
+using namespace psp;
+
+#include <fontconfig/fontconfig.h>
+#include <ft2build.h>
+#include <fontconfig/fcfreetype.h>
+// allow compile on baseline (currently with fontconfig 2.2.0)
+#ifndef FC_WEIGHT_BOOK // TODO: remove when baseline moves to fc>=2.2.1
+ #define FC_WEIGHT_BOOK 75
+#endif
+#ifndef FC_EMBEDDED_BITMAP // TODO: remove when baseline moves to fc>=2.3.92
+ #define FC_EMBEDDED_BITMAP "embeddedbitmap"
+#endif
+#ifndef FC_FAMILYLANG // TODO: remove when baseline moves to fc>=2.2.97
+ #define FC_FAMILYLANG "familylang"
+#endif
+#ifndef FC_CAPABILITY // TODO: remove when baseline moves to fc>=2.2.97
+ #define FC_CAPABILITY "capability"
+#endif
+#ifndef FC_STYLELANG // TODO: remove when baseline moves to fc>=2.2.97
+ #define FC_STYLELANG "stylelang"
+#endif
+#ifndef FC_HINT_STYLE // TODO: remove when baseline moves to fc>=2.2.91
+ #define FC_HINT_STYLE "hintstyle"
+ #define FC_HINT_NONE 0
+ #define FC_HINT_SLIGHT 1
+ #define FC_HINT_MEDIUM 2
+ #define FC_HINT_FULL 3
+#endif
+#ifndef FC_FT_FACE
+ #define FC_FT_FACE "ftface"
+#endif
+#ifndef FC_EMBOLDEN
+ #define FC_EMBOLDEN "embolden"
+#endif
+#ifndef FC_FONTFORMAT
+ #define FC_FONTFORMAT "fontformat"
+#endif
+
+#include <cstdio>
+#include <cstdarg>
+
+#include "unotools/atom.hxx"
+
+#include "osl/module.h"
+#include "osl/thread.h"
+#include "osl/process.h"
+
+#include "rtl/ustrbuf.hxx"
+#include "rtl/locale.hxx"
+
+#include "sal/alloca.h"
+
+#include <utility>
+#include <algorithm>
+
+using namespace osl;
+using ::rtl::OUString;
+using ::rtl::OUStringBuffer;
+using ::rtl::OString;
+
+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();
+
+public:
+ FcResult LocalizedElementFromPattern(FcPattern* pPattern, FcChar8 **family,
+ const char *elementtype, const char *elementlangtype);
+//to-do, make private and add some cleanish accessor methods
+ boost::unordered_map< rtl::OString, rtl::OString, rtl::OStringHash > m_aFontNameToLocalized;
+ boost::unordered_map< rtl::OString, rtl::OString, rtl::OStringHash > m_aLocalizedToCanonical;
+private:
+ void cacheLocalizedFontNames(FcChar8 *origfontname, FcChar8 *bestfontname, const std::vector< lang_and_element > &lang_and_elements);
+};
+
+FontCfgWrapper::FontCfgWrapper()
+ : m_pOutlineSet( NULL )
+{
+ 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=NULL, *pNameB=NULL;
+
+ bool bHaveA = FcPatternGetString(a, FC_FAMILY, 0, &pNameA) == FcResultMatch;
+ bool bHaveB = FcPatternGetString(b, FC_FAMILY, 0, &pNameB) == FcResultMatch;
+
+ if (bHaveA && bHaveB)
+ return strcmp((const char*)pNameA, (const char*)pNameB);
+
+ return bHaveA - 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 - bHaveA;
+ }
+ };
+
+ //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)
+ {
+ if (i == 0)
+ return false;
+
+ const FcPattern *a = pFSet->fonts[i];
+ const FcPattern *b = pFSet->fonts[i-1];
+
+ if (compareFontNames(a, b) != 0)
+ return false;
+
+ FcPattern* pTestPatternA = FcPatternDuplicate(a);
+ FcPatternDel(pTestPatternA, FC_FILE);
+ FcPatternDel(pTestPatternA, FC_CHARSET);
+ FcPatternDel(pTestPatternA, FC_CAPABILITY);
+ FcPatternDel(pTestPatternA, FC_FONTVERSION);
+
+ FcPattern* pTestPatternB = FcPatternDuplicate(b);
+ FcPatternDel(pTestPatternB, FC_FILE);
+ FcPatternDel(pTestPatternB, FC_CHARSET);
+ FcPatternDel(pTestPatternB, FC_CAPABILITY);
+ FcPatternDel(pTestPatternB, FC_FONTVERSION);
+
+ bool 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()
+{
+ if( m_pOutlineSet )
+ FcFontSetDestroy( m_pOutlineSet );
+ //To-Do: get gtk vclplug smoketest to pass
+ //FcFini();
+}
+
+static FontCfgWrapper* pOneInstance = NULL;
+
+FontCfgWrapper& FontCfgWrapper::get()
+{
+ if( ! pOneInstance )
+ pOneInstance = new FontCfgWrapper();
+ return *pOneInstance;
+}
+
+void FontCfgWrapper::release()
+{
+ if( pOneInstance )
+ {
+ delete pOneInstance;
+ pOneInstance = NULL;
+ }
+}
+
+namespace
+{
+ class localizedsorter
+ {
+ rtl::OLocale maLoc;
+ public:
+ localizedsorter(rtl_Locale* pLoc) : maLoc(pLoc) {}
+ FcChar8* bestname(const std::vector<lang_and_element> &elements);
+ };
+
+ FcChar8* localizedsorter::bestname(const std::vector<lang_and_element> &elements)
+ {
+ FcChar8* candidate = elements.begin()->second;
+ rtl::OString sLangMatch(rtl::OUStringToOString(maLoc.getLanguage().toAsciiLowerCase(), RTL_TEXTENCODING_UTF8));
+ rtl::OString sFullMatch = sLangMatch;
+ sFullMatch += OString('-');
+ sFullMatch += rtl::OUStringToOString(maLoc.getCountry().toAsciiLowerCase(), RTL_TEXTENCODING_UTF8);
+
+ std::vector<lang_and_element>::const_iterator aEnd = elements.end();
+ bool alreadyclosematch = false;
+ for( std::vector<lang_and_element>::const_iterator aIter = elements.begin(); aIter != aEnd; ++aIter )
+ {
+ const char *pLang = (const char*)aIter->first;
+ if( rtl_str_compare( pLang, sFullMatch.getStr() ) == 0)
+ {
+ // both language and country match
+ candidate = aIter->second;
+ break;
+ }
+ else if( alreadyclosematch )
+ {
+ // override candidate only if there is a perfect match
+ continue;
+ }
+ else if( rtl_str_compare( pLang, sLangMatch.getStr()) == 0)
+ {
+ // just the language matches
+ candidate = aIter->second;
+ alreadyclosematch = true;
+ }
+ else if( rtl_str_compare( pLang, "en") == 0)
+ {
+ // fallback to the english element name
+ candidate = aIter->second;
+ }
+ }
+ 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(FcChar8 *origfontname, 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 = (const char*)(aIter->second);
+ if (rtl_str_compare(candidate, (const char*)bestfontname) != 0)
+ m_aFontNameToLocalized[OString(candidate)] = OString((const char*)bestfontname);
+ }
+ if (rtl_str_compare((const char*)origfontname, (const char*)bestfontname) != 0)
+ m_aLocalizedToCanonical[OString((const char*)bestfontname)] = OString((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 = NULL;
+ 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 (1)
+ {
+ 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
+ rtl_Locale* pLoc;
+ osl_getProcessLocale(&pLoc);
+ localizedsorter aSorter(pLoc);
+ *element = aSorter.bestname(lang_and_elements);
+
+ //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;
+}
+
+/*
+ * PrintFontManager::initFontconfig
+ */
+bool PrintFontManager::initFontconfig()
+{
+ FontCfgWrapper::get();
+ return true;
+}
+
+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;
+ }
+}
+
+int PrintFontManager::countFontconfigFonts( boost::unordered_map<rtl::OString, int, rtl::OStringHash>& o_rVisitedPaths )
+{
+ int nFonts = 0;
+
+ FontCfgWrapper& rWrapper = FontCfgWrapper::get();
+
+ FcFontSet* pFSet = rWrapper.getFontSet();
+ 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 = NULL;
+ FcChar8* family = NULL;
+ FcChar8* style = NULL;
+ FcChar8* format = NULL;
+ int slant = 0;
+ int weight = 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 );
+ 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 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"
+ " spacing = %d, outline = %d, format %s\n"
+ , family, file
+ , eWeightRes == FcResultMatch ? weight : -1
+ , eSpacRes == FcResultMatch ? slant : -1
+ , eStyleRes == FcResultMatch ? (const char*) style : "<nil>"
+ , 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( (sal_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)
+ std::list< OString > aDummy;
+ if (eFormatRes != FcResultMatch)
+ format = NULL;
+ analyzeFontFile( nDirID, aBase, aDummy, aFonts, (const 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() )
+ {
+ // TODO: remove fonts unusable to psprint from fontset
+ continue;
+ }
+
+ int nFamilyName = m_pAtoms->getAtom( ATOM_FAMILYNAME, OStringToOUString( OString( (sal_Char*)family ), RTL_TEXTENCODING_UTF8 ), sal_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 = NULL;
+ }
+ }
+
+ if( pUpdate )
+ {
+ // set family name
+ if( pUpdate->m_nFamilyName != nFamilyName )
+ {
+ }
+ if( eWeightRes == FcResultMatch )
+ pUpdate->m_eWeight = convertWeight(weight);
+ if( eSpacRes == FcResultMatch )
+ pUpdate->m_ePitch = convertSpacing(spacing);
+ if( eSlantRes == FcResultMatch )
+ pUpdate->m_eItalic = convertSlant(slant);
+ if( eStyleRes == FcResultMatch )
+ {
+ pUpdate->m_aStyleName = OStringToOUString( OString( (sal_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 );
+ nFonts++;
+#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
+ return nFonts;
+}
+
+void PrintFontManager::deinitFontconfig()
+{
+ FontCfgWrapper::release();
+}
+
+bool PrintFontManager::addFontconfigDir( const rtl::OString& rDirName )
+{
+ // workaround for a stability problems in older FC versions
+ // when handling application specifc fonts
+ const int nVersion = FcGetVersion();
+ if( nVersion <= 20400 )
+ return false;
+ const char* pDirName = (const char*)rDirName.getStr();
+ bool bDirOk = (FcConfigAppFontAddDir(FcConfigGetCurrent(), (FcChar8*)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 rtl::OString aConfFileName = rDirName + "/fc_local.conf";
+ FILE* pCfgFile = fopen( aConfFileName.getStr(), "rb" );
+ if( pCfgFile )
+ {
+ fclose( pCfgFile);
+ bool bCfgOk = FcConfigParseAndLoad(FcConfigGetCurrent(),
+ (FcChar8*)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_ULTRACONDENSED;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, (FcChar8*)"monospace");
+ }
+}
+
+rtl::OUString PrintFontManager::Substitute(const rtl::OUString& rFontName,
+ rtl::OUString& rMissingCodes, const rtl::OString &rLangAttrib,
+ FontItalic &rItalic, FontWeight &rWeight,
+ FontWidth &rWidth, FontPitch &rPitch) const
+{
+ rtl::OUString aName;
+ FontCfgWrapper& rWrapper = FontCfgWrapper::get();
+
+ // build pattern argument for fontconfig query
+ FcPattern* pPattern = FcPatternCreate();
+
+ // Prefer scalable fonts
+ FcPatternAddBool(pPattern, FC_SCALABLE, FcTrue);
+
+ const rtl::OString aTargetName = rtl::OUStringToOString( rFontName, RTL_TEXTENCODING_UTF8 );
+ const FcChar8* pTargetNameUtf8 = (FcChar8*)aTargetName.getStr();
+ FcPatternAddString(pPattern, FC_FAMILY, pTargetNameUtf8);
+
+ if( rLangAttrib.getLength() )
+ {
+ const FcChar8* pLangAttribUtf8;
+ if (rLangAttrib.equalsIgnoreAsciiCase(OString(RTL_CONSTASCII_STRINGPARAM("pa-in"))))
+ pLangAttribUtf8 = (FcChar8*)"pa";
+ else
+ pLangAttribUtf8 = (FcChar8*)rLangAttrib.getStr();
+ FcPatternAddString(pPattern, FC_LANG, pLangAttribUtf8);
+ }
+
+ // Add required Unicode characters, if any
+ if ( rMissingCodes.getLength() )
+ {
+ 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 );
+ }
+ FcPatternAddCharSet(pPattern, FC_CHARSET, unicodes);
+ FcCharSetDestroy(unicodes);
+ }
+
+ addtopattern(pPattern, rItalic, rWeight, rWidth, rPitch);
+
+ // 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 = NULL;
+ 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* family = NULL;
+ FcResult eFileRes = FcPatternGetString( pSet->fonts[0], FC_FAMILY, 0, &family );
+
+ // get the family name
+ if( eFileRes == FcResultMatch )
+ {
+ OString sFamily((sal_Char*)family);
+ boost::unordered_map< rtl::OString, rtl::OString, rtl::OStringHash >::const_iterator aI = rWrapper.m_aFontNameToLocalized.find(sFamily);
+ if (aI != rWrapper.m_aFontNameToLocalized.end())
+ sFamily = aI->second;
+ aName = rtl::OStringToOUString( sFamily, RTL_TEXTENCODING_UTF8 );
+
+
+ int val = 0;
+ if (FcResultMatch == FcPatternGetInteger(pSet->fonts[0], FC_WEIGHT, 0, &val))
+ rWeight = convertWeight(val);
+ if (FcResultMatch == FcPatternGetInteger(pSet->fonts[0], FC_SLANT, 0, &val))
+ rItalic = convertSlant(val);
+ if (FcResultMatch == FcPatternGetInteger(pSet->fonts[0], FC_SPACING, 0, &val))
+ rPitch = convertSpacing(val);
+ if (FcResultMatch == FcPatternGetInteger(pSet->fonts[0], FC_WIDTH, 0, &val))
+ rWidth = convertWidth(val);
+ }
+
+ // update rMissingCodes by removing resolved unicodes
+ if( rMissingCodes.getLength() > 0 )
+ {
+ sal_uInt32* pRemainingCodes = (sal_uInt32*)alloca( rMissingCodes.getLength() * sizeof(sal_uInt32) );
+ 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;
+ }
+ }
+ rMissingCodes = OUString( pRemainingCodes, nRemainingLen );
+ }
+ }
+
+ FcFontSetDestroy( pSet );
+ }
+
+ return aName;
+}
+
+class FontConfigFontOptions : public ImplFontOptions
+{
+public:
+ FontConfigFontOptions() : mpPattern(0) {}
+ ~FontConfigFontOptions()
+ {
+ FcPatternDestroy(mpPattern);
+ }
+ virtual void *GetPattern(void * face, bool bEmbolden, bool /*bVerticalLayout*/) 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;
+ }
+ FcPattern* mpPattern;
+};
+
+ImplFontOptions* PrintFontManager::getFontOptions(
+ const FastPrintFontInfo& rInfo, int nSize, void (*subcallback)(void*)) const
+{
+ FontCfgWrapper& rWrapper = FontCfgWrapper::get();
+
+ FontConfigFontOptions* pOptions = NULL;
+ FcConfig* pConfig = FcConfigGetCurrent();
+ FcPattern* pPattern = FcPatternCreate();
+
+ OString sFamily = OUStringToOString( rInfo.m_aFamilyName, RTL_TEXTENCODING_UTF8 );
+
+ boost::unordered_map< rtl::OString, rtl::OString, rtl::OStringHash >::const_iterator aI = rWrapper.m_aLocalizedToCanonical.find(sFamily);
+ if (aI != rWrapper.m_aLocalizedToCanonical.end())
+ sFamily = aI->second;
+ if( sFamily.getLength() )
+ FcPatternAddString(pPattern, FC_FAMILY, (FcChar8*)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);
+ /*FcResult eHintStyle =*/ FcPatternGetInteger(pResult,
+ FC_HINT_STYLE, 0, &hintstyle);
+
+ pOptions = new FontConfigFontOptions;
+
+ pOptions->mpPattern = 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 com::sun::star::lang::Locale& rLocale )
+{
+ FontCfgWrapper& rWrapper = FontCfgWrapper::get();
+
+ FcConfig* pConfig = FcConfigGetCurrent();
+ FcPattern* pPattern = FcPatternCreate();
+
+ OString aLangAttrib;
+ // populate pattern with font characteristics
+ if( rLocale.Language.getLength() )
+ {
+ OUStringBuffer aLang(6);
+ aLang.append( rLocale.Language );
+ if( rLocale.Country.getLength() )
+ {
+ aLang.append( sal_Unicode('-') );
+ aLang.append( rLocale.Country );
+ }
+ aLangAttrib = OUStringToOString( aLang.makeStringAndClear(), RTL_TEXTENCODING_UTF8 );
+ }
+ if( aLangAttrib.getLength() )
+ FcPatternAddString(pPattern, FC_LANG, (FcChar8*)aLangAttrib.getStr());
+
+ OString aFamily = OUStringToOString( rInfo.m_aFamilyName, RTL_TEXTENCODING_UTF8 );
+ if( aFamily.getLength() )
+ FcPatternAddString(pPattern, FC_FAMILY, (FcChar8*)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 = NULL;
+ FcResult eFileRes = FcPatternGetString(pSet->fonts[0], FC_FILE, 0, &file);
+ if( eFileRes == FcResultMatch )
+ {
+ OString aDir, aBase, aOrgPath( (sal_Char*)file );
+ splitPath( aOrgPath, aDir, aBase );
+ int nDirID = getDirectoryAtom( aDir, true );
+ fontID aFont = findFontFileID( nDirID, aBase );
+ 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/generic/fontmanager/fontmanager.cxx b/vcl/generic/fontmanager/fontmanager.cxx
new file mode 100644
index 000000000000..80cddeec3317
--- /dev/null
+++ b/vcl/generic/fontmanager/fontmanager.cxx
@@ -0,0 +1,4089 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*************************************************************************
+ *
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * Copyright 2000, 2010 Oracle and/or its affiliates.
+ *
+ * OpenOffice.org - a multi-platform office productivity suite
+ *
+ * This file is part of OpenOffice.org.
+ *
+ * OpenOffice.org is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License version 3
+ * only, as published by the Free Software Foundation.
+ *
+ * OpenOffice.org is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License version 3 for more details
+ * (a copy is included in the LICENSE file that accompanied this code).
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * version 3 along with OpenOffice.org. If not, see
+ * <http://www.openoffice.org/license.html>
+ * for a copy of the LGPLv3 License.
+ *
+ ************************************************************************/
+
+// MARKER(update_precomp.py): autogen include statement, do not remove
+#include "precompiled_vcl.hxx"
+
+#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 "salinst.hxx"
+#include "vcl/fontmanager.hxx"
+#include "vcl/strhelper.hxx"
+#include "vcl/ppdparser.hxx"
+
+#include "tools/urlobj.hxx"
+#include "tools/stream.hxx"
+#include "tools/debug.hxx"
+#include "tools/config.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 "i18npool/mslangid.hxx"
+
+
+#include "parseAFM.hxx"
+#include "sft.hxx"
+
+#if OSL_DEBUG_LEVEL > 1
+#include <sys/times.h>
+#include <stdio.h>
+#endif
+
+#include "sal/alloca.h"
+
+#include <set>
+#include <boost/unordered_set.hpp>
+#include <algorithm>
+
+#include "adobeenc.tab" // get encoding table for AFM metrics
+
+#ifdef CALLGRIND_COMPILE
+#include <valgrind/callgrind.h>
+#endif
+
+#include "comphelper/processfactory.hxx"
+#include "com/sun/star/beans/XMaterialHolder.hpp"
+#include "com/sun/star/beans/NamedValue.hpp"
+
+#define PRINTER_METRICDIR "fontmetric"
+
+namespace {
+
+namespace css = com::sun::star;
+
+}
+
+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;
+
+using ::rtl::OUString;
+using ::rtl::OString;
+using ::rtl::OStringHash;
+using ::rtl::OStringBuffer;
+using ::rtl::OUStringBuffer;
+using ::rtl::OUStringHash;
+using ::rtl::OStringToOUString;
+using ::rtl::OUStringToOString;
+
+/*
+ * 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;
+}
+
+inline sal_uInt32 getUInt32BE( const sal_uInt8*& pBuffer )
+{
+ sal_uInt32 nRet = (((sal_uInt32)pBuffer[0]) << 24) |
+ (((sal_uInt32)pBuffer[1]) << 16) |
+ (((sal_uInt32)pBuffer[2]) << 8) |
+ (((sal_uInt32)pBuffer[3]) );
+ pBuffer += 4;
+ return nRet;
+}
+
+static FontItalic parseItalic( const ByteString& rItalic )
+{
+ FontItalic eItalic = ITALIC_DONTKNOW;
+ if( rItalic.EqualsIgnoreCaseAscii( "i" ) )
+ eItalic = ITALIC_NORMAL;
+ else if( rItalic.EqualsIgnoreCaseAscii( "o" ) )
+ eItalic = ITALIC_OBLIQUE;
+ else
+ eItalic = ITALIC_NONE;
+ return eItalic;
+}
+
+// -------------------------------------------------------------------------
+
+static FontWeight parseWeight( const ByteString& rWeight )
+{
+ FontWeight eWeight = WEIGHT_DONTKNOW;
+ if( rWeight.Search( "bold" ) != STRING_NOTFOUND )
+ {
+ if( rWeight.Search( "emi" ) != STRING_NOTFOUND ) // semi, demi
+ eWeight = WEIGHT_SEMIBOLD;
+ else if( rWeight.Search( "ultra" ) != STRING_NOTFOUND )
+ eWeight = WEIGHT_ULTRABOLD;
+ else
+ eWeight = WEIGHT_BOLD;
+ }
+ else if( rWeight.Search( "heavy" ) != STRING_NOTFOUND )
+ eWeight = WEIGHT_BOLD;
+ else if( rWeight.Search( "light" ) != STRING_NOTFOUND )
+ {
+ if( rWeight.Search( "emi" ) != STRING_NOTFOUND ) // semi, demi
+ eWeight = WEIGHT_SEMILIGHT;
+ else if( rWeight.Search( "ultra" ) != STRING_NOTFOUND )
+ eWeight = WEIGHT_ULTRALIGHT;
+ else
+ eWeight = WEIGHT_LIGHT;
+ }
+ else if( rWeight.Search( "black" ) != STRING_NOTFOUND )
+ eWeight = WEIGHT_BLACK;
+ else if( rWeight.Equals( "demi" ) )
+ eWeight = WEIGHT_SEMIBOLD;
+ else if( rWeight.Equals( "book" ) ||
+ rWeight.Equals( "semicondensed" ) )
+ eWeight = WEIGHT_LIGHT;
+ else if( rWeight.Equals( "medium" ) || rWeight.Equals( "roman" ) )
+ eWeight = WEIGHT_MEDIUM;
+ else
+ eWeight = WEIGHT_NORMAL;
+ return eWeight;
+}
+
+// -------------------------------------------------------------------------
+
+static FontWidth parseWidth( const ByteString& rWidth )
+{
+ FontWidth eWidth = WIDTH_DONTKNOW;
+ if( rWidth.Equals( "bold" ) ||
+ rWidth.Equals( "semiexpanded" ) )
+ eWidth = WIDTH_SEMI_EXPANDED;
+ else if( rWidth.Equals( "condensed" ) ||
+ rWidth.Equals( "narrow" ) )
+ eWidth = WIDTH_CONDENSED;
+ else if( rWidth.Equals( "double wide" ) ||
+ rWidth.Equals( "extraexpanded" ) ||
+ rWidth.Equals( "ultraexpanded" ) )
+ eWidth = WIDTH_ULTRA_EXPANDED;
+ else if( rWidth.Equals( "expanded" ) ||
+ rWidth.Equals( "wide" ) )
+ eWidth = WIDTH_EXPANDED;
+ else if( rWidth.Equals( "extracondensed" ) )
+ eWidth = WIDTH_EXTRA_CONDENSED;
+ else if( rWidth.Equals( "semicondensed" ) )
+ eWidth = WIDTH_SEMI_CONDENSED;
+ else if( rWidth.Equals( "ultracondensed" ) )
+ eWidth = WIDTH_ULTRA_CONDENSED;
+ else
+ eWidth = WIDTH_NORMAL;
+
+ return eWidth;
+}
+
+// -------------------------------------------------------------------------
+bool PrintFontManager::XLFDEntry::operator<(const PrintFontManager::XLFDEntry& rRight) const
+{
+ sal_Int32 nCmp = 0;
+ if( (nMask & MaskFamily) && (rRight.nMask & MaskFamily) )
+ {
+ nCmp = rtl_str_compareIgnoreAsciiCase_WithLength( aFamily.pData->buffer,
+ aFamily.pData->length,
+ rRight.aFamily.pData->buffer,
+ rRight.aFamily.pData->length );
+ if( nCmp != 0 )
+ return nCmp < 0;
+ }
+
+ if( (nMask & MaskFoundry) && (rRight.nMask & MaskFoundry) )
+ {
+ nCmp = rtl_str_compareIgnoreAsciiCase_WithLength( aFoundry.pData->buffer,
+ aFoundry.pData->length,
+ rRight.aFoundry.pData->buffer,
+ rRight.aFoundry.pData->length );
+ if( nCmp != 0 )
+ return nCmp < 0;
+ }
+
+ if( (nMask & MaskItalic) && (rRight.nMask & MaskItalic) )
+ {
+ if( eItalic != rRight.eItalic )
+ return (int)eItalic < (int)rRight.eItalic;
+ }
+
+ if( (nMask & MaskWeight) && (rRight.nMask & MaskWeight) )
+ {
+ if( eWeight != rRight.eWeight )
+ return (int)eWeight < (int)rRight.eWeight;
+ }
+
+ if( (nMask & MaskWidth) && (rRight.nMask & MaskWidth) )
+ {
+ if( eWidth != rRight.eWidth )
+ return (int)eWidth < (int)rRight.eWidth;
+ }
+
+ if( (nMask & MaskPitch) && (rRight.nMask & MaskPitch) )
+ {
+ if( ePitch != rRight.ePitch )
+ return (int)ePitch < (int)rRight.ePitch;
+ }
+
+ if( (nMask & MaskAddStyle) && (rRight.nMask & MaskAddStyle) )
+ {
+ nCmp = rtl_str_compareIgnoreAsciiCase_WithLength( aAddStyle.pData->buffer,
+ aAddStyle.pData->length,
+ rRight.aAddStyle.pData->buffer,
+ rRight.aAddStyle.pData->length );
+ if( nCmp != 0 )
+ return nCmp < 0;
+ }
+
+ if( (nMask & MaskEncoding) && (rRight.nMask & MaskEncoding) )
+ {
+ if( aEncoding != rRight.aEncoding )
+ return aEncoding < rRight.aEncoding;
+ }
+
+ return false;
+}
+
+bool PrintFontManager::XLFDEntry::operator==(const PrintFontManager::XLFDEntry& rRight) const
+{
+ sal_Int32 nCmp = 0;
+ if( (nMask & MaskFamily) && (rRight.nMask & MaskFamily) )
+ {
+ nCmp = rtl_str_compareIgnoreAsciiCase_WithLength( aFamily.pData->buffer,
+ aFamily.pData->length,
+ rRight.aFamily.pData->buffer,
+ rRight.aFamily.pData->length );
+ if( nCmp != 0 )
+ return false;
+ }
+
+ if( (nMask & MaskFoundry) && (rRight.nMask & MaskFoundry) )
+ {
+ nCmp = rtl_str_compareIgnoreAsciiCase_WithLength( aFoundry.pData->buffer,
+ aFoundry.pData->length,
+ rRight.aFoundry.pData->buffer,
+ rRight.aFoundry.pData->length );
+ if( nCmp != 0 )
+ return false;
+ }
+
+ if( (nMask & MaskItalic) && (rRight.nMask & MaskItalic) )
+ {
+ if( eItalic != rRight.eItalic )
+ return false;
+ }
+
+ if( (nMask & MaskWeight) && (rRight.nMask & MaskWeight) )
+ {
+ if( eWeight != rRight.eWeight )
+ return false;
+ }
+
+ if( (nMask & MaskWidth) && (rRight.nMask & MaskWidth) )
+ {
+ if( eWidth != rRight.eWidth )
+ return false;
+ }
+
+ if( (nMask & MaskPitch) && (rRight.nMask & MaskPitch) )
+ {
+ if( ePitch != rRight.ePitch )
+ return false;
+ }
+
+ if( (nMask & MaskAddStyle) && (rRight.nMask & MaskAddStyle) )
+ {
+ nCmp = rtl_str_compareIgnoreAsciiCase_WithLength( aAddStyle.pData->buffer,
+ aAddStyle.pData->length,
+ rRight.aAddStyle.pData->buffer,
+ rRight.aAddStyle.pData->length );
+ if( nCmp != 0 )
+ return false;
+ }
+
+ if( (nMask & MaskEncoding) && (rRight.nMask & MaskEncoding) )
+ {
+ if( aEncoding != rRight.aEncoding )
+ return false;
+ }
+
+ return true;
+}
+
+/*
+ * 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( NULL ),
+ 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()
+{
+ if( m_pMetrics )
+ delete m_pMetrics;
+}
+
+// -------------------------------------------------------------------------
+
+PrintFontManager::Type1FontFile::~Type1FontFile()
+{
+}
+
+// -------------------------------------------------------------------------
+
+PrintFontManager::TrueTypeFontFile::TrueTypeFontFile()
+: PrintFont( fonttype::TrueType )
+, m_nDirectory( 0 )
+, m_nCollectionEntry(-1)
+, m_nTypeFlags( TYPEFLAG_INVALID )
+{}
+
+// -------------------------------------------------------------------------
+
+PrintFontManager::TrueTypeFontFile::~TrueTypeFontFile()
+{
+}
+
+// -------------------------------------------------------------------------
+
+PrintFontManager::BuiltinFont::~BuiltinFont()
+{
+}
+
+// -------------------------------------------------------------------------
+
+bool PrintFontManager::Type1FontFile::queryMetricPage( int /*nPage*/, MultiAtomProvider* pProvider )
+{
+ return readAfmMetrics( PrintFontManager::get().getAfmFile( this ), pProvider, false, false );
+}
+
+// -------------------------------------------------------------------------
+
+bool PrintFontManager::BuiltinFont::queryMetricPage( int /*nPage*/, MultiAtomProvider* pProvider )
+{
+ return readAfmMetrics( PrintFontManager::get().getAfmFile( this ), pProvider, false, false );
+}
+
+// -------------------------------------------------------------------------
+
+bool PrintFontManager::TrueTypeFontFile::queryMetricPage( int nPage, MultiAtomProvider* pProvider )
+{
+ bool bSuccess = false;
+
+ ByteString aFile( PrintFontManager::get().getFontFile( this ) );
+
+ TrueTypeFont* pTTFont = NULL;
+
+ if( OpenTTFontFile( aFile.GetBuffer(), m_nCollectionEntry < 0 ? 0 : 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, NULL, 0 );
+ TTSimpleGlyphMetrics* pMetrics = GetTTSimpleCharMetrics( pTTFont, nPage*256, nCharacters, 0 );
+ 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, NULL, 1 );
+ pMetrics = GetTTSimpleCharMetrics( pTTFont, nPage*256, nCharacters, 1 );
+ 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 ] = 1;
+ }
+ }
+ free( pMetrics );
+ }
+
+ if( ! m_pMetrics->m_bKernPairsQueried )
+ {
+ m_pMetrics->m_bKernPairsQueried = true;
+ // this is really a hack
+ // in future MapString/KernGlyphs should be used
+ // but vcl is not in a state where that could be used
+ // so currently we get kernpairs by accessing the raw data
+ struct _TrueTypeFont* pImplTTFont = (struct _TrueTypeFont*)pTTFont;
+
+ //-----------------------------------------------------------------
+ // Kerning: KT_MICROSOFT
+ //-----------------------------------------------------------------
+ if( pImplTTFont->nkern && pImplTTFont->kerntype == KT_MICROSOFT )
+ {
+ // create a glyph -> character mapping
+ ::boost::unordered_map< sal_uInt16, sal_Unicode > aGlyphMap;
+ ::boost::unordered_map< sal_uInt16, sal_Unicode >::iterator left, right;
+ for( i = 21; i < 0xfffd; i++ )
+ {
+ sal_uInt16 nGlyph = MapChar( pTTFont, (sal_Unicode)i, 0 ); // kerning for horz only
+ if( nGlyph != 0 )
+ aGlyphMap[ nGlyph ] = (sal_Unicode)i;
+ }
+
+
+ KernPair aPair;
+ for( i = 0; i < (int)pImplTTFont->nkern; i++ )
+ {
+ const sal_uInt8* pTable = pImplTTFont->kerntables[i];
+
+ /*sal_uInt16 nVersion =*/ getUInt16BE( pTable );
+ /*sal_uInt16 nLength =*/ getUInt16BE( pTable );
+ sal_uInt16 nCoverage = getUInt16BE( pTable );
+
+ aPair.kern_x = 0;
+ aPair.kern_y = 0;
+ switch( nCoverage >> 8 )
+ {
+ case 0:
+ {
+ sal_uInt16 nPairs = getUInt16BE( pTable );
+ pTable += 6;
+ for( int n = 0; n < nPairs; n++ )
+ {
+ sal_uInt16 nLeftGlyph = getUInt16BE( pTable );
+ sal_uInt16 nRightGlyph = getUInt16BE( pTable );
+ sal_Int16 nKern = (sal_Int16)getUInt16BE( pTable );
+
+ left = aGlyphMap.find( nLeftGlyph );
+ right = aGlyphMap.find( nRightGlyph );
+ if( left != aGlyphMap.end() && right != aGlyphMap.end() )
+ {
+ aPair.first = left->second;
+ aPair.second = right->second;
+ switch( nCoverage & 1 )
+ {
+ case 1:
+ aPair.kern_x = (int)nKern * 1000 / pImplTTFont->unitsPerEm;
+ m_pMetrics->m_aXKernPairs.push_back( aPair );
+ break;
+ case 0:
+ aPair.kern_y = (int)nKern * 1000 / pImplTTFont->unitsPerEm;
+ m_pMetrics->m_aYKernPairs.push_back( aPair );
+ break;
+ }
+ }
+ }
+ }
+ break;
+
+ case 2:
+ {
+ const sal_uInt8* pSubTable = pTable;
+ /*sal_uInt16 nRowWidth =*/ getUInt16BE( pTable );
+ sal_uInt16 nOfLeft = getUInt16BE( pTable );
+ sal_uInt16 nOfRight = getUInt16BE( pTable );
+ /*sal_uInt16 nOfArray =*/ getUInt16BE( pTable );
+ const sal_uInt8* pTmp = pSubTable + nOfLeft;
+ sal_uInt16 nFirstLeft = getUInt16BE( pTmp );
+ sal_uInt16 nLastLeft = getUInt16BE( pTmp ) + nFirstLeft - 1;
+ pTmp = pSubTable + nOfRight;
+ sal_uInt16 nFirstRight = getUInt16BE( pTmp );
+ sal_uInt16 nLastRight = getUInt16BE( pTmp ) + nFirstRight -1;
+
+ // int nPairs = (int)(nLastLeft-nFirstLeft+1)*(int)(nLastRight-nFirstRight+1);
+ for( aPair.first = nFirstLeft; aPair.first < nLastLeft; aPair.first++ )
+ {
+ for( aPair.second = 0; aPair.second < nLastRight; aPair.second++ )
+ {
+ sal_Int16 nKern = (sal_Int16)getUInt16BE( pTmp );
+ switch( nCoverage & 1 )
+ {
+ case 1:
+ aPair.kern_x = (int)nKern * 1000 / pImplTTFont->unitsPerEm;
+ m_pMetrics->m_aXKernPairs.push_back( aPair );
+ break;
+ case 0:
+ aPair.kern_y = (int)nKern * 1000 / pImplTTFont->unitsPerEm;
+ m_pMetrics->m_aYKernPairs.push_back( aPair );
+ break;
+ }
+ }
+ }
+ }
+ break;
+ }
+ }
+ }
+
+ //-----------------------------------------------------------------
+ // Kerning: KT_APPLE_NEW
+ //-----------------------------------------------------------------
+ if( pImplTTFont->nkern && pImplTTFont->kerntype == KT_APPLE_NEW )
+ {
+ // create a glyph -> character mapping
+ ::boost::unordered_map< sal_uInt16, sal_Unicode > aGlyphMap;
+ ::boost::unordered_map< sal_uInt16, sal_Unicode >::iterator left, right;
+ for( i = 21; i < 0xfffd; i++ )
+ {
+ sal_uInt16 nGlyph = MapChar( pTTFont, (sal_Unicode)i, 0 ); // kerning for horz only
+ if( nGlyph != 0 )
+ aGlyphMap[ nGlyph ] = (sal_Unicode)i;
+ }
+
+ // Loop through each of the 'kern' subtables
+ KernPair aPair;
+ for( i = 0; (unsigned int)i < pImplTTFont->nkern; i++ )
+ {
+ const sal_uInt8* pTable = pImplTTFont->kerntables[i];
+
+ /*sal_uInt32 nLength =*/ getUInt32BE( pTable );
+ sal_uInt16 nCoverage = getUInt16BE( pTable );
+ /*sal_uInt16 nTupleIndex =*/ getUInt16BE( pTable );
+
+ // Get kerning type
+ // sal_Bool bKernVertical = nCoverage & 0x8000;
+ // sal_Bool bKernCrossStream = nCoverage & 0x4000;
+ // sal_Bool bKernVariation = nCoverage & 0x2000;
+
+ // Kerning sub-table format, 0 through 3
+ sal_uInt8 nSubTableFormat = nCoverage & 0x00FF;
+
+ aPair.kern_x = 0;
+ aPair.kern_y = 0;
+ switch( nSubTableFormat )
+ {
+ case 0:
+ {
+ // Grab the # of kern pairs but skip over the:
+ // searchRange
+ // entrySelector
+ // rangeShift
+ sal_uInt16 nPairs = getUInt16BE( pTable );
+ pTable += 6;
+
+ for( int n = 0; n < nPairs; n++ )
+ {
+ sal_uInt16 nLeftGlyph = getUInt16BE( pTable );
+ sal_uInt16 nRightGlyph = getUInt16BE( pTable );
+ sal_Int16 nKern = (sal_Int16)getUInt16BE( pTable );
+
+ left = aGlyphMap.find( nLeftGlyph );
+ right = aGlyphMap.find( nRightGlyph );
+ if( left != aGlyphMap.end() && right != aGlyphMap.end() )
+ {
+ aPair.first = left->second;
+ aPair.second = right->second;
+
+ // Only support horizontal kerning for now
+ aPair.kern_x = (int)nKern * 1000 / pImplTTFont->unitsPerEm;
+ aPair.kern_y = 0;
+ m_pMetrics->m_aXKernPairs.push_back( aPair );
+
+/* switch( nCoverage & 1 )
+ {
+ case 1:
+ aPair.kern_x = (int)nKern * 1000 / pImplTTFont->unitsPerEm;
+ m_pMetrics->m_aXKernPairs.push_back( aPair );
+ break;
+ case 0:
+ aPair.kern_y = (int)nKern * 1000 / pImplTTFont->unitsPerEm;
+ m_pMetrics->m_aYKernPairs.push_back( aPair );
+ break;
+ }
+*/
+ }
+ }
+ }
+ break;
+
+ case 2:
+ {
+ const sal_uInt8* pSubTable = pTable;
+ /*sal_uInt16 nRowWidth =*/ getUInt16BE( pTable );
+ sal_uInt16 nOfLeft = getUInt16BE( pTable );
+ sal_uInt16 nOfRight = getUInt16BE( pTable );
+ /*sal_uInt16 nOfArray =*/ getUInt16BE( pTable );
+ const sal_uInt8* pTmp = pSubTable + nOfLeft;
+ sal_uInt16 nFirstLeft = getUInt16BE( pTmp );
+ sal_uInt16 nLastLeft = getUInt16BE( pTmp ) + nFirstLeft - 1;
+ pTmp = pSubTable + nOfRight;
+ sal_uInt16 nFirstRight = getUInt16BE( pTmp );
+ sal_uInt16 nLastRight = getUInt16BE( pTmp ) + nFirstRight -1;
+
+ for( aPair.first = nFirstLeft; aPair.first < nLastLeft; aPair.first++ )
+ {
+ for( aPair.second = 0; aPair.second < nLastRight; aPair.second++ )
+ {
+ sal_Int16 nKern = (sal_Int16)getUInt16BE( pTmp );
+ switch( nCoverage & 1 )
+ {
+ case 1:
+ aPair.kern_x = (int)nKern * 1000 / pImplTTFont->unitsPerEm;
+ m_pMetrics->m_aXKernPairs.push_back( aPair );
+ break;
+ case 0:
+ aPair.kern_y = (int)nKern * 1000 / pImplTTFont->unitsPerEm;
+ m_pMetrics->m_aYKernPairs.push_back( aPair );
+ break;
+ }
+ }
+ }
+ }
+ break;
+
+ default:
+ fprintf( stderr, "Found unsupported Apple-style kern subtable type %d.\n", nSubTableFormat );
+ break;
+ }
+ }
+ }
+
+#if OSL_DEBUG_LEVEL > 1
+ fprintf( stderr, "found %" SAL_PRI_SIZET "u/%" SAL_PRI_SIZET "u kern pairs for %s\n",
+ m_pMetrics->m_aXKernPairs.size(),
+ m_pMetrics->m_aYKernPairs.size(),
+ OUStringToOString( pProvider->getString( ATOM_FAMILYNAME, m_nFamilyName ), RTL_TEXTENCODING_MS_1252 ).getStr() );
+#else
+ (void) pProvider; /* avoid warnings */
+#endif
+ }
+
+ 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 boost::unordered_map< OUString, OUString, OUStringHash > aPSNameToFamily( 16 );
+ if( aPSNameToFamily.empty() ) // initialization
+ {
+ aPSNameToFamily[ OUString( RTL_CONSTASCII_USTRINGPARAM( "Helvetica-Narrow" ) ) ] =
+ OUString( RTL_CONSTASCII_USTRINGPARAM( "Helvetica Narrow" ) );
+ aPSNameToFamily[ OUString( RTL_CONSTASCII_USTRINGPARAM( "Helvetica-Narrow-Bold" ) ) ] =
+ OUString( RTL_CONSTASCII_USTRINGPARAM( "Helvetica Narrow" ) );
+ aPSNameToFamily[ OUString( RTL_CONSTASCII_USTRINGPARAM( "Helvetica-Narrow-BoldOblique" ) ) ] =
+ OUString( RTL_CONSTASCII_USTRINGPARAM( "Helvetica Narrow" ) );
+ aPSNameToFamily[ OUString( RTL_CONSTASCII_USTRINGPARAM( "Helvetica-Narrow-Oblique" ) ) ] =
+ OUString( RTL_CONSTASCII_USTRINGPARAM( "Helvetica Narrow" ) );
+ }
+ boost::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( const OString& rFileName, MultiAtomProvider* pProvider, bool bFillEncodingvector, bool bOnlyGlobalAttributes )
+{
+ PrintFontManager& rManager( PrintFontManager::get() );
+
+ FontInfo* pInfo = NULL;
+ parseFile( rFileName.getStr(), &pInfo, P_ALL );
+ if( ! pInfo || ! pInfo->numOfChars )
+ {
+ if( pInfo )
+ freeFontInfo( pInfo );
+ return false;
+ }
+
+ m_aEncodingVector.clear();
+ // fill in global info
+
+ // PSName
+ OUString aPSName( OStringToOUString( pInfo->gfi->fontName, RTL_TEXTENCODING_ISO_8859_1 ) );
+ m_nPSName = pProvider->getAtom( ATOM_PSNAME, aPSName, sal_True );
+
+ // family name (if not already set)
+ OUString aFamily;
+ if( ! m_nFamilyName )
+ {
+ aFamily = OStringToOUString( pInfo->gfi->familyName, RTL_TEXTENCODING_ISO_8859_1 );
+ if( ! aFamily.getLength() )
+ {
+ 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, sal_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.getLength() && pInfo->gfi->fullName && *pInfo->gfi->fullName )
+ {
+ OUString aFullName( OStringToOUString( pInfo->gfi->fullName, RTL_TEXTENCODING_ISO_8859_1 ) );
+ if( aFullName.indexOf( aFamily ) == 0 )
+ 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
+ ByteString aLowerWeight( pInfo->gfi->weight );
+ aLowerWeight.ToLowerAscii();
+ m_eWeight = parseWeight( aLowerWeight );
+
+ // 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 wether it might be a
+ // japanese font. Who invented this PITA ?
+ OUString aPSNameLastToken( aPSName.copy( aPSName.lastIndexOf( '-' )+1 ) );
+ if( ! aPSNameLastToken.compareToAscii( "H" ) ||
+ ! aPSNameLastToken.compareToAscii( "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.compareToAscii( 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
+ }
+
+ // hack for GB encoded builtin fonts posing as FontSpecific
+ if( m_eType == fonttype::Builtin && ( nAdobeEncoding == 3 || nAdobeEncoding == 0 ) )
+ {
+ int nLen = aFamily.getLength();
+ if( nLen > 2 &&
+ aFamily.getStr()[ nLen-2 ] == 'G' &&
+ aFamily.getStr()[ nLen-1 ] == 'B' &&
+ pInfo->numOfChars > 255 )
+ {
+ m_aEncoding = RTL_TEXTENCODING_GBK;
+ m_bFontEncodingOnly = true;
+#if OSL_DEBUG_LEVEL > 1
+ fprintf( stderr, "found builtin font %s with GBK encoding\n", 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;
+
+ if( m_pMetrics )
+ delete m_pMetrics;
+ m_pMetrics = new PrintFontMetrics;
+ // mark all pages as queried (or clear if only global font info queiried)
+ 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
+ sal_Unicode* pUnicodes = (sal_Unicode*)alloca( pInfo->numOfChars * sizeof(sal_Unicode));
+ 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;
+ continue;
+ }
+
+ if( m_aEncoding == RTL_TEXTENCODING_UNICODE )
+ {
+ pUnicodes[i] = (sal_Unicode)pChar->code;
+ continue;
+ }
+
+ rtl::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));
+ rtl::OUString aUni(rtl::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;
+ }
+ 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< ::boost::unordered_multimap< sal_uInt8, sal_Unicode >::const_iterator,
+ ::boost::unordered_multimap< sal_uInt8, sal_Unicode >::const_iterator >
+ aCodes = rManager.getUnicodeFromAdobeCode( pChar->code );
+ 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;
+ }
+ ++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_pMetrics->m_aXKernPairs.clear();
+ m_pMetrics->m_aYKernPairs.clear();
+
+ // now fill in the kern pairs
+ // parseAFM.cxx effectively only supports direction 0 (horizontal)
+ PairKernData* pKern = pInfo->pkd;
+ KernPair aPair;
+ for( i = 0; i < pInfo->numOfPairs; i++, pKern++ )
+ {
+ // #i37703# broken kern table
+ if( ! pKern->name1 || ! pKern->name2 )
+ continue;
+
+ aPair.first = 0;
+ aPair.second = 0;
+ // currently we have to find the adobe character names
+ // in the already parsed character metrics to find
+ // the corresponding UCS2 code which is a bit dangerous
+ // since the character names are not required
+ // in the metric descriptions
+ pChar = pInfo->cmi;
+ for( int j = 0;
+ j < pInfo->numOfChars && ( aPair.first == 0 || aPair.second == 0 );
+ j++, pChar++ )
+ {
+ if( pChar->code != -1 )
+ {
+ if( ! strcmp( pKern->name1, pChar->name ? pChar->name : "" ) )
+ aPair.first = pUnicodes[ j ];
+ if( ! strcmp( pKern->name2, pChar->name ? pChar->name : "" ) )
+ aPair.second = pUnicodes[ j ];
+ }
+ }
+ if( aPair.first && aPair.second )
+ {
+ aPair.kern_x = pKern->xamt;
+ aPair.kern_y = pKern->yamt;
+ m_pMetrics->m_aXKernPairs.push_back( aPair );
+ }
+ }
+ m_pMetrics->m_bKernPairsQueried = true;
+ }
+
+ freeFontInfo( pInfo );
+ return true;
+}
+
+// -------------------------------------------------------------------------
+
+OString PrintFontManager::s_aEmptyOString;
+
+/*
+ * one instance only
+ */
+PrintFontManager& PrintFontManager::get()
+{
+ static PrintFontManager* pManager = NULL;
+ 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( NULL ),
+ m_bFontconfigSuccess( false )
+{
+ for( unsigned int i = 0; i < SAL_N_ELEMENTS( aAdobeCodes ); i++ )
+ {
+ m_aUnicodeToAdobename.insert( ::boost::unordered_multimap< sal_Unicode, ::rtl::OString >::value_type( aAdobeCodes[i].aUnicode, aAdobeCodes[i].pAdobename ) );
+ m_aAdobenameToUnicode.insert( ::boost::unordered_multimap< ::rtl::OString, sal_Unicode, ::rtl::OStringHash >::value_type( aAdobeCodes[i].pAdobename, aAdobeCodes[i].aUnicode ) );
+ if( aAdobeCodes[i].aAdobeStandardCode )
+ {
+ m_aUnicodeToAdobecode.insert( ::boost::unordered_multimap< sal_Unicode, sal_uInt8 >::value_type( aAdobeCodes[i].aUnicode, aAdobeCodes[i].aAdobeStandardCode ) );
+ m_aAdobecodeToUnicode.insert( ::boost::unordered_multimap< sal_uInt8, sal_Unicode >::value_type( aAdobeCodes[i].aAdobeStandardCode, aAdobeCodes[i].aUnicode ) );
+ }
+ }
+}
+
+// -------------------------------------------------------------------------
+
+PrintFontManager::~PrintFontManager()
+{
+ deinitFontconfig();
+ for( ::boost::unordered_map< fontID, PrintFont* >::const_iterator it = m_aFonts.begin(); it != m_aFonts.end(); ++it )
+ delete (*it).second;
+ delete m_pAtoms;
+ if( m_pFontCache )
+ delete m_pFontCache;
+}
+
+// -------------------------------------------------------------------------
+
+const OString& PrintFontManager::getDirectory( int nAtom ) const
+{
+ ::boost::unordered_map< int, OString >::const_iterator it( m_aAtomToDir.find( nAtom ) );
+ return it != m_aAtomToDir.end() ? it->second : s_aEmptyOString;
+}
+
+// -------------------------------------------------------------------------
+
+int PrintFontManager::getDirectoryAtom( const OString& rDirectory, bool bCreate )
+{
+ int nAtom = 0;
+ ::boost::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;
+}
+
+// -------------------------------------------------------------------------
+
+int PrintFontManager::addFontFile( const ::rtl::OString& rFileName, int /*nFaceNum*/ )
+{
+ rtl_TextEncoding aEncoding = osl_getThreadTextEncoding();
+ INetURLObject aPath( OStringToOUString( rFileName, aEncoding ), INET_PROT_FILE, INetURLObject::ENCODE_ALL );
+ OString aName( OUStringToOString( aPath.GetName(), aEncoding ) );
+ OString aDir( OUStringToOString( aPath.GetPath(), aEncoding ) );
+
+ int nDirID = getDirectoryAtom( aDir, true );
+ fontID nFontId = findFontFileID( nDirID, aName );
+ if( !nFontId )
+ {
+ ::std::list< PrintFont* > aNewFonts;
+ if( analyzeFontFile( nDirID, aName, ::std::list<OString>(), aNewFonts ) )
+ {
+ for( ::std::list< PrintFont* >::iterator it = aNewFonts.begin();
+ it != aNewFonts.end(); ++it )
+ {
+ m_aFonts[ nFontId = m_nNextFontID++ ] = *it;
+ m_aFontFileToFontID[ aName ].insert( nFontId );
+ m_pFontCache->updateFontCacheEntry( *it, true );
+ }
+ }
+ }
+ return nFontId;
+}
+
+enum fontFormat
+{
+ UNKNOWN, TRUETYPE, CFF, TYPE1, AFM
+};
+
+bool PrintFontManager::analyzeFontFile( int nDirID, const OString& rFontFile, const ::std::list<OString>& rXLFDs, ::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)
+ {
+ ByteString aExt( rFontFile.copy( rFontFile.lastIndexOf( '.' )+1 ) );
+ if( aExt.EqualsIgnoreCaseAscii( "pfb" ) || aExt.EqualsIgnoreCaseAscii( "pfa" ) )
+ eFormat = TYPE1;
+ else if( aExt.EqualsIgnoreCaseAscii( "afm" ) )
+ eFormat = AFM;
+ else if( aExt.EqualsIgnoreCaseAscii( "ttf" )
+ || aExt.EqualsIgnoreCaseAscii( "ttc" )
+ || aExt.EqualsIgnoreCaseAscii( "tte" ) ) // #i33947# for Gaiji support
+ eFormat = TRUETYPE;
+ else if( aExt.EqualsIgnoreCaseAscii( "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++ )
+ {
+ ByteString aName( rFontFile );
+ aName.Erase( aName.Len()-4 );
+ aName.Append( pSuffix[i] );
+
+ rtl::OStringBuffer aFilePath(aDir);
+ aFilePath.append('/').append(aName);
+
+ ByteString 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 = "afm/";
+ aAfmFile += aName;
+ }
+ }
+ else
+ aAfmFile = aName;
+
+ if( aAfmFile.Len() )
+ {
+ Type1FontFile* pFont = new Type1FontFile();
+ pFont->m_nDirectory = nDirID;
+
+ pFont->m_aFontFile = rFontFile;
+ pFont->m_aMetricFile = aAfmFile;
+
+ if( ! pFont->readAfmMetrics( getAfmFile( pFont ), m_pAtoms, false, true ) )
+ {
+ delete pFont;
+ pFont = NULL;
+ }
+ if( pFont && rXLFDs.size() )
+ getFontAttributesFromXLFD( pFont, rXLFDs );
+ if( pFont )
+ rNewFonts.push_back( pFont );
+ break;
+ }
+ }
+ }
+ else if (eFormat == AFM)
+ {
+ rtl::OStringBuffer aFilePath(aDir);
+ aFilePath.append('/').append(rFontFile);
+ BuiltinFont* pFont = new BuiltinFont();
+ pFont->m_nDirectory = nDirID;
+ pFont->m_aMetricFile = rFontFile;
+ if( pFont->readAfmMetrics( aFilePath.makeStringAndClear(), m_pAtoms,
+ false, true ) )
+ {
+ rNewFonts.push_back( pFont );
+ }
+ else
+ delete pFont;
+ }
+ else if (eFormat == TRUETYPE || eFormat == CFF)
+ {
+ // get number of ttc entries
+ int nLength = CountTTCFonts( aFullPath.getStr() );
+ if( nLength )
+ {
+#if OSL_DEBUG_LEVEL > 1
+ fprintf( stderr, "ttc: %s contains %d fonts\n", aFullPath.getStr(), nLength );
+#endif
+ for( int i = 0; i < nLength; i++ )
+ {
+ TrueTypeFontFile* pFont = new TrueTypeFontFile();
+ pFont->m_nDirectory = nDirID;
+ pFont->m_aFontFile = rFontFile;
+ pFont->m_nCollectionEntry = i;
+ if( nLength == 1 )
+ getFontAttributesFromXLFD( pFont, rXLFDs );
+ if( ! analyzeTrueTypeFile( pFont ) )
+ {
+ delete pFont;
+ pFont = NULL;
+ }
+ else
+ rNewFonts.push_back( pFont );
+ }
+ }
+ else
+ {
+ TrueTypeFontFile* pFont = new TrueTypeFontFile();
+ pFont->m_nDirectory = nDirID;
+ pFont->m_aFontFile = rFontFile;
+ pFont->m_nCollectionEntry = -1;
+
+ if( rXLFDs.size() )
+ getFontAttributesFromXLFD( pFont, rXLFDs );
+ // need to read the font anyway to get aliases inside the font file
+ if( ! analyzeTrueTypeFile( pFont ) )
+ {
+ delete pFont;
+ pFont = NULL;
+ }
+ else
+ rNewFonts.push_back( pFont );
+ }
+ }
+ return ! rNewFonts.empty();
+}
+
+// -------------------------------------------------------------------------
+
+fontID PrintFontManager::findFontBuiltinID( int nPSNameAtom ) const
+{
+ fontID nID = 0;
+ ::boost::unordered_map< fontID, PrintFont* >::const_iterator it;
+ for( it = m_aFonts.begin(); nID == 0 && it != m_aFonts.end(); ++it )
+ {
+ if( it->second->m_eType == fonttype::Builtin &&
+ it->second->m_nPSName == nPSNameAtom )
+ nID = it->first;
+ }
+ return nID;
+}
+
+// -------------------------------------------------------------------------
+
+fontID PrintFontManager::findFontFileID( int nDirID, const OString& rFontFile ) const
+{
+ fontID nID = 0;
+
+ ::boost::unordered_map< OString, ::std::set< fontID >, OStringHash >::const_iterator set_it = m_aFontFileToFontID.find( rFontFile );
+ if( set_it != m_aFontFileToFontID.end() )
+ {
+ for( ::std::set< fontID >::const_iterator font_it = set_it->second.begin(); font_it != set_it->second.end() && ! nID; ++font_it )
+ {
+ ::boost::unordered_map< fontID, PrintFont* >::const_iterator it = m_aFonts.find( *font_it );
+ if( it != m_aFonts.end() )
+ {
+ 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 )
+ nID = it->first;
+ }
+ break;
+ case fonttype::Builtin:
+ if( static_cast<const BuiltinFont*>((*it).second)->m_nDirectory == nDirID &&
+ static_cast<const BuiltinFont*>((*it).second)->m_aMetricFile == rFontFile )
+ nID = it->first;
+ break;
+ default:
+ break;
+ }
+ }
+ }
+ }
+ return nID;
+}
+
+// -------------------------------------------------------------------------
+
+bool PrintFontManager::parseXLFD( const OString& rXLFD, XLFDEntry& rEntry )
+{
+ sal_Int32 nIndex = 0;
+ OString aFoundry = WhitespaceToSpace( rXLFD.getToken( 1, '-', nIndex ) );
+ if( nIndex < 0 ) return false;
+ OString aFamilyXLFD = WhitespaceToSpace( rXLFD.getToken( 0, '-', nIndex ) );
+ if( nIndex < 0 ) return false;
+ OString aWeight = rXLFD.getToken( 0, '-', nIndex ).toAsciiLowerCase();
+ if( nIndex < 0 ) return false;
+ OString aSlant = rXLFD.getToken( 0, '-', nIndex ).toAsciiLowerCase();
+ if( nIndex < 0 ) return false;
+ OString aWidth = rXLFD.getToken( 0, '-', nIndex ).toAsciiLowerCase();
+ if( nIndex < 0 ) return false;
+ OString aAddStyle = rXLFD.getToken( 0, '-', nIndex ).toAsciiLowerCase();
+ if( nIndex < 0 ) return false;
+ OString aPitch = rXLFD.getToken( 4, '-', nIndex ).toAsciiLowerCase();
+ if( nIndex < 0 ) return false;
+ OString aRegEnc = WhitespaceToSpace( rXLFD.getToken( 1, '-', nIndex ).toAsciiLowerCase() );
+ if( nIndex < 0 ) return false;
+ OString aEnc = WhitespaceToSpace( rXLFD.getToken( 0, '-', nIndex ).toAsciiLowerCase() );
+
+ // capitalize words
+ sal_Int32 nFamIndex = 0;
+ OStringBuffer aFamilyName;
+ while( nFamIndex >= 0 )
+ {
+ OString aToken = aFamilyXLFD.getToken( 0, ' ', nFamIndex );
+ sal_Char aFirst = aToken.toChar();
+ if( aFirst >= 'a' && aFirst <= 'z' )
+ aFirst = aFirst - 'a' + 'A';
+ OStringBuffer aNewToken( aToken.getLength() );
+ aNewToken.append( aToken );
+ aNewToken.setCharAt( 0, aFirst );
+ if( aFamilyName.getLength() > 0 )
+ aFamilyName.append( ' ' );
+ aFamilyName.append( aNewToken.makeStringAndClear() );
+ }
+
+ rEntry.aFoundry = aFoundry;
+ rEntry.aFamily = aFamilyName.makeStringAndClear();
+ rEntry.aAddStyle = aAddStyle;
+ // evaluate weight
+ rEntry.eWeight = parseWeight( aWeight );
+ // evaluate slant
+ rEntry.eItalic = parseItalic( aSlant );
+ // evaluate width
+ rEntry.eWidth = parseWidth( aWidth );
+
+ // evaluate pitch
+ if( aPitch.toChar() == 'c' || aPitch.toChar() == 'm' )
+ rEntry.ePitch = PITCH_FIXED;
+ else
+ rEntry.ePitch = PITCH_VARIABLE;
+
+ OString aToken = aEnc.toAsciiLowerCase();
+ // get encoding
+ if( aAddStyle.indexOf( "symbol" ) != -1 )
+ rEntry.aEncoding = RTL_TEXTENCODING_SYMBOL;
+ else
+ {
+ if( aToken.equals( "symbol" ) )
+ rEntry.aEncoding = RTL_TEXTENCODING_SYMBOL;
+ else
+ {
+ OStringBuffer aCharset( aRegEnc.getLength() + aEnc.getLength() + 1 );
+ aCharset.append( aRegEnc );
+ aCharset.append( '-' );
+ aCharset.append( aEnc );
+ rEntry.aEncoding = rtl_getTextEncodingFromUnixCharset( aCharset.getStr() );
+ }
+ }
+
+ // set correct mask flags
+ rEntry.nMask = 0;
+ if( rEntry.aFoundry != "*" ) rEntry.nMask |= XLFDEntry::MaskFoundry;
+ if( rEntry.aFamily != "*" ) rEntry.nMask |= XLFDEntry::MaskFamily;
+ if( rEntry.aAddStyle != "*" ) rEntry.nMask |= XLFDEntry::MaskAddStyle;
+ if( aWeight != "*" ) rEntry.nMask |= XLFDEntry::MaskWeight;
+ if( aSlant != "*" ) rEntry.nMask |= XLFDEntry::MaskItalic;
+ if( aWidth != "*" ) rEntry.nMask |= XLFDEntry::MaskWidth;
+ if( aPitch != "*" ) rEntry.nMask |= XLFDEntry::MaskPitch;
+ if( aRegEnc != "*" && aEnc != "*" ) rEntry.nMask |= XLFDEntry::MaskEncoding;
+
+ return true;
+}
+
+// -------------------------------------------------------------------------
+
+void PrintFontManager::parseXLFD_appendAliases( const std::list< OString >& rXLFDs, std::list< XLFDEntry >& rEntries ) const
+{
+ for( std::list< OString >::const_iterator it = rXLFDs.begin(); it != rXLFDs.end(); ++it )
+ {
+ XLFDEntry aEntry;
+ if( ! parseXLFD(*it, aEntry) )
+ continue;
+ rEntries.push_back( aEntry );
+ std::map< XLFDEntry, std::list< XLFDEntry > >::const_iterator alias_it =
+ m_aXLFD_Aliases.find( aEntry );
+ if( alias_it != m_aXLFD_Aliases.end() )
+ {
+ rEntries.insert( rEntries.end(), alias_it->second.begin(), alias_it->second.end() );
+ }
+ }
+}
+
+// -------------------------------------------------------------------------
+
+void PrintFontManager::getFontAttributesFromXLFD( PrintFont* pFont, const std::list< OString >& rXLFDs ) const
+{
+ bool bFamilyName = false;
+
+ std::list< XLFDEntry > aXLFDs;
+
+ parseXLFD_appendAliases( rXLFDs, aXLFDs );
+
+ for( std::list< XLFDEntry >::const_iterator it = aXLFDs.begin();
+ it != aXLFDs.end(); ++it )
+ {
+ // set family name or alias
+ int nFam =
+ m_pAtoms->getAtom( ATOM_FAMILYNAME,
+ OStringToOUString( it->aFamily, it->aAddStyle.indexOf( "utf8" ) != -1 ? RTL_TEXTENCODING_UTF8 : RTL_TEXTENCODING_ISO_8859_1 ),
+ sal_True );
+ if( ! bFamilyName )
+ {
+ bFamilyName = true;
+ pFont->m_nFamilyName = nFam;
+ switch( pFont->m_eType )
+ {
+ case fonttype::Type1:
+ static_cast<Type1FontFile*>(pFont)->m_aXLFD = rXLFDs.front();
+ break;
+ case fonttype::TrueType:
+ static_cast<TrueTypeFontFile*>(pFont)->m_aXLFD = rXLFDs.front();
+ break;
+ default:
+ break;
+ }
+ }
+ else
+ {
+ // make sure that aliases are unique
+ if( nFam != 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 != nFam; ++al_it )
+ ;
+ if( al_it == pFont->m_aAliases.end() )
+ pFont->m_aAliases.push_back( nFam );
+
+ }
+ // for the rest of the attributes there can only be one value;
+ // we'll trust the first one
+ continue;
+ }
+
+ // fill in weight
+ pFont->m_eWeight = it->eWeight;
+ // fill in slant
+ pFont->m_eItalic = it->eItalic;
+ // fill in width
+ pFont->m_eWidth = it->eWidth;
+ // fill in pitch
+ pFont->m_ePitch = it->ePitch;
+ // fill in encoding
+ pFont->m_aEncoding = it->aEncoding;
+ }
+
+ // handle iso8859-1 as ms1252 to fill the "gap" starting at 0x80
+ if( pFont->m_aEncoding == RTL_TEXTENCODING_ISO_8859_1 )
+ pFont->m_aEncoding = RTL_TEXTENCODING_MS_1252;
+ if( rXLFDs.begin() != rXLFDs.end() )
+ {
+ switch( pFont->m_eType )
+ {
+ case fonttype::Type1:
+ static_cast<Type1FontFile*>(pFont)->m_aXLFD = rXLFDs.front();
+ break;
+ case fonttype::TrueType:
+ static_cast<TrueTypeFontFile*>(pFont)->m_aXLFD = rXLFDs.front();
+ break;
+ default: break;
+ }
+ }
+}
+
+// -------------------------------------------------------------------------
+
+OString PrintFontManager::getXLFD( PrintFont* pFont ) const
+{
+ if( pFont->m_eType == fonttype::Type1 )
+ {
+ if( static_cast<Type1FontFile*>(pFont)->m_aXLFD.getLength() )
+ return static_cast<Type1FontFile*>(pFont)->m_aXLFD;
+ }
+ if( pFont->m_eType == fonttype::TrueType )
+ {
+ if( static_cast<TrueTypeFontFile*>(pFont)->m_aXLFD.getLength() )
+ return static_cast<TrueTypeFontFile*>(pFont)->m_aXLFD;
+ }
+
+ OStringBuffer aXLFD( 128 );
+
+ aXLFD.append( "-misc-" );
+ ByteString aFamily( String( m_pAtoms->getString( ATOM_FAMILYNAME, pFont->m_nFamilyName ) ), RTL_TEXTENCODING_UTF8 );
+ aFamily.SearchAndReplaceAll( '-',' ' );
+ aFamily.SearchAndReplaceAll( '?',' ' );
+ aFamily.SearchAndReplaceAll( '*',' ' );
+ aXLFD.append( OString( aFamily ) );
+ aXLFD.append( '-' );
+ switch( pFont->m_eWeight )
+ {
+ case WEIGHT_THIN: aXLFD.append("thin");break;
+ case WEIGHT_ULTRALIGHT: aXLFD.append("ultralight");break;
+ case WEIGHT_LIGHT: aXLFD.append("light");break;
+ case WEIGHT_SEMILIGHT: aXLFD.append("semilight");break;
+ case WEIGHT_NORMAL: aXLFD.append("normal");break;
+ case WEIGHT_MEDIUM: aXLFD.append("medium");break;
+ case WEIGHT_SEMIBOLD: aXLFD.append("semibold");break;
+ case WEIGHT_BOLD: aXLFD.append("bold");break;
+ case WEIGHT_ULTRABOLD: aXLFD.append("ultrabold");break;
+ case WEIGHT_BLACK: aXLFD.append("black");break;
+ default: break;
+ }
+ aXLFD.append('-');
+ switch( pFont->m_eItalic )
+ {
+ case ITALIC_NONE: aXLFD.append('r');break;
+ case ITALIC_OBLIQUE: aXLFD.append('o');break;
+ case ITALIC_NORMAL: aXLFD.append('i');break;
+ default: break;
+ }
+ aXLFD.append('-');
+ switch( pFont->m_eWidth )
+ {
+ case WIDTH_ULTRA_CONDENSED: aXLFD.append("ultracondensed");break;
+ case WIDTH_EXTRA_CONDENSED: aXLFD.append("extracondensed");break;
+ case WIDTH_CONDENSED: aXLFD.append("condensed");break;
+ case WIDTH_SEMI_CONDENSED: aXLFD.append("semicondensed");break;
+ case WIDTH_NORMAL: aXLFD.append("normal");break;
+ case WIDTH_SEMI_EXPANDED: aXLFD.append("semiexpanded");break;
+ case WIDTH_EXPANDED: aXLFD.append("expanded");break;
+ case WIDTH_EXTRA_EXPANDED: aXLFD.append("extraexpanded");break;
+ case WIDTH_ULTRA_EXPANDED: aXLFD.append("ultraexpanded");break;
+ default: break;
+ }
+ aXLFD.append("-utf8-0-0-0-0-");
+ aXLFD.append( pFont->m_ePitch == PITCH_FIXED ? "m" : "p" );
+ aXLFD.append("-0-");
+ const char* pEnc = rtl_getBestUnixCharsetFromTextEncoding( pFont->m_aEncoding );
+ if( ! pEnc )
+ {
+ if( pFont->m_aEncoding == RTL_TEXTENCODING_ADOBE_STANDARD )
+ pEnc = "adobe-standard";
+ else
+ pEnc = "iso8859-1";
+ }
+ aXLFD .append( pEnc );
+
+ return aXLFD.makeStringAndClear();
+}
+
+// -------------------------------------------------------------------------
+
+OUString PrintFontManager::convertTrueTypeName( void* pRecord ) const
+{
+ NameRecord* pNameRecord = (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;
+ }
+ }
+ }
+ 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.equalsAsciiL(RTL_CONSTASCII_STRINGPARAM("Berling Antiqua")))
+ {
+ ::std::set< OUString >::iterator aEnd = rSet.end();
+ ::std::set< OUString >::iterator aI = rSet.find(OUString(RTL_CONSTASCII_USTRINGPARAM("Times New Roman")));
+ if (aI != aEnd)
+ {
+ bRet = true;
+ rSet.erase(aI);
+ }
+ }
+ return bRet;
+ }
+}
+
+// -------------------------------------------------------------------------
+
+void PrintFontManager::analyzeTrueTypeFamilyName( void* pTTFont, ::std::list< OUString >& rNames ) const
+{
+ OUString aFamily;
+
+ rNames.clear();
+ ::std::set< OUString > aSet;
+
+ NameRecord* pNameRecords = NULL;
+ int nNameRecords = GetTTNameRecords( (TrueTypeFont*)pTTFont, &pNameRecords );
+ if( nNameRecords && pNameRecords )
+ {
+ LanguageType aLang = MsLangId::getSystemLanguage();
+ int nLastMatch = -1;
+ for( int i = 0; i < nNameRecords; i++ )
+ {
+ if( pNameRecords[i].nameID != 1 || pNameRecords[i].sptr == NULL )
+ 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 == aLang )
+ 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;
+ }
+ OUString aName = convertTrueTypeName( pNameRecords + i );
+ aSet.insert( aName );
+ if( nMatch > nLastMatch || isBadTNR(aName, aSet) )
+ {
+ nLastMatch = nMatch;
+ aFamily = aName;
+ }
+ }
+ DisposeNameRecords( pNameRecords, nNameRecords );
+ }
+ if( aFamily.getLength() )
+ {
+ 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();
+ ByteString aFile = getFontFile( pFont );
+ TrueTypeFont* pTTFont = NULL;
+
+ TrueTypeFontFile* pTTFontFile = static_cast< TrueTypeFontFile* >(pFont);
+ if( OpenTTFontFile( aFile.GetBuffer(), pTTFontFile->m_nCollectionEntry < 0 ? 0 : 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.begin() != aNames.end() )
+ {
+ pFont->m_nFamilyName = m_pAtoms->getAtom( ATOM_FAMILYNAME, aNames.front(), sal_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 ), sal_True );
+ }
+ }
+ for( ::std::list< OUString >::iterator it = aNames.begin(); it != aNames.end(); ++it )
+ {
+ if( it->getLength() )
+ {
+ int nAlias = m_pAtoms->getAtom( ATOM_FAMILYNAME, *it, sal_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 );
+
+ pFont->m_nPSName = m_pAtoms->getAtom( ATOM_PSNAME, String( ByteString( aInfo.psname ), aEncoding ), sal_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;
+ }
+#if OSL_DEBUG_LEVEL > 1
+ else
+ fprintf( stderr, "could not OpenTTFont \"%s\"\n", aFile.GetBuffer() );
+#endif
+
+ return bSuccess;
+}
+
+// -------------------------------------------------------------------------
+
+void PrintFontManager::initFontsAlias()
+{
+ m_aXLFD_Aliases.clear();
+ rtl_TextEncoding aEnc = osl_getThreadTextEncoding();
+ for( std::list< OString >::const_iterator dir_it = m_aFontDirectories.begin();
+ dir_it != m_aFontDirectories.end(); ++dir_it )
+ {
+ OStringBuffer aDirName(512);
+ aDirName.append( *dir_it );
+ aDirName.append( "/fonts.alias" );
+ SvFileStream aStream( OStringToOUString( aDirName.makeStringAndClear(), aEnc ), STREAM_READ );
+ if( ! aStream.IsOpen() )
+ continue;
+
+ do
+ {
+ ByteString aLine;
+ aStream.ReadLine( aLine );
+
+ // get the alias and the pattern it gets translated to
+ ByteString aAlias = GetCommandLineToken( 0, aLine );
+ ByteString aMap = GetCommandLineToken( 1, aLine );
+
+ // remove eventual quotes
+ aAlias.EraseLeadingChars( '"' );
+ aAlias.EraseTrailingChars( '"' );
+ aMap.EraseLeadingChars( '"' );
+ aMap.EraseTrailingChars( '"' );
+
+ XLFDEntry aAliasEntry, aMapEntry;
+ parseXLFD( aAlias, aAliasEntry );
+ parseXLFD( aMap, aMapEntry );
+
+ if( aAliasEntry.nMask && aMapEntry.nMask )
+ m_aXLFD_Aliases[ aMapEntry ].push_back( aAliasEntry );
+ } while( ! aStream.IsEof() );
+ }
+}
+
+// code stolen from vcl's RegisterFontSubstitutors()
+// TODO: use that method once psprint gets merged into vcl
+static bool AreFCSubstitutionsEnabled()
+{
+ // init font substitution defaults
+ int nDisableBits = 0;
+#ifdef SOLARIS
+ // TODO: check the OS version and fc-data maintenance level
+ 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 & 3) == 0);
+}
+
+void PrintFontManager::initialize()
+{
+ #ifdef CALLGRIND_COMPILE
+ CALLGRIND_TOGGLE_COLLECT();
+ CALLGRIND_ZERO_STATS();
+ #endif
+
+ long aDirEntBuffer[ (sizeof(struct dirent)+_PC_NAME_MAX)+1 ];
+
+ 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 may be called twice in the future
+ {
+ for( ::boost::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();
+ m_aOverrideFonts.clear();
+ }
+
+#if OSL_DEBUG_LEVEL > 1
+ clock_t aStart;
+ clock_t aStep1;
+ clock_t aStep2;
+ clock_t aStep3;
+ int nBuiltinFonts = 0;
+ int nCached = 0;
+
+ struct tms tms;
+
+ aStart = times( &tms );
+#endif
+
+ // first try fontconfig
+ m_bFontconfigSuccess = initFontconfig();
+
+ // part one - look for downloadable fonts
+ rtl_TextEncoding aEncoding = osl_getThreadTextEncoding();
+ const ::rtl::OUString &rSalPrivatePath = psp::getFontPath();
+
+ // search for the fonts in SAL_PRIVATE_FONTPATH first; those are
+ // the fonts installed with the office
+ if( rSalPrivatePath.getLength() )
+ {
+ OString aPath = rtl::OUStringToOString( rSalPrivatePath, aEncoding );
+ const bool bAreFCSubstitutionsEnabled = AreFCSubstitutionsEnabled();
+ sal_Int32 nIndex = 0;
+ do
+ {
+ OString aToken = aPath.getToken( 0, ';', nIndex );
+ normPath( aToken );
+ if (!aToken.getLength())
+ {
+ 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( m_bFontconfigSuccess && !addFontconfigDir( aToken ) )
+ if( bAreFCSubstitutionsEnabled )
+ continue;
+ m_aFontDirectories.push_back( aToken );
+ m_aPrivateFontDirectories.push_back( getDirectoryAtom( aToken, true ) );
+ } while( nIndex >= 0 );
+ }
+
+ // protect against duplicate paths
+ boost::unordered_map< OString, int, OStringHash > visited_dirs;
+
+ // now that all global and local font dirs are known to fontconfig
+ // check that there are fonts actually managed by fontconfig
+ // also don't search directories that fontconfig already did
+ if( m_bFontconfigSuccess )
+ m_bFontconfigSuccess = (countFontconfigFonts( visited_dirs ) > 0);
+
+ // don't search through many directories fontconfig already told us about
+ if( ! m_bFontconfigSuccess )
+ ImplGetSVData()->mpDefInst->FillFontPathList( m_aFontDirectories );
+
+ // fill XLFD aliases from fonts.alias files
+ initFontsAlias();
+
+ // 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 );
+ else if( (*it)->m_eType == fonttype::Builtin )
+ m_aFontFileToFontID[ static_cast<BuiltinFont*>(*it)->m_aMetricFile ].insert( aFont );
+#if OSL_DEBUG_LEVEL > 1
+ if( (*it)->m_eType == fonttype::Builtin )
+ nBuiltinFonts++;
+ nCached++;
+#if OSL_DEBUG_LEVEL > 2
+ fprintf( stderr, "adding cached font %d: \"%s\" from %s\n", aFont,
+ OUStringToOString( getFontFamily( aFont ), RTL_TEXTENCODING_MS_1252 ).getStr(),
+ getFontFileSysPath( aFont ).getStr() );
+#endif
+#endif
+ }
+ if( ! m_pFontCache->scanAdditionalFiles( aPath ) )
+ continue;
+ }
+
+ DIR* pDIR = opendir( aPath.getStr() );
+ struct dirent* pEntry = (struct dirent*)aDirEntBuffer;
+ if( pDIR )
+ {
+ // read fonts.dir if possible
+ ::boost::unordered_map< OString, ::std::list<OString>, OStringHash > aFontsDir;
+ int nDirID = getDirectoryAtom( aPath, true );
+ // #i38367# no fonts.dir in our own directories anymore
+ std::list< int >::const_iterator priv_dir;
+ for( priv_dir = m_aPrivateFontDirectories.begin();
+ priv_dir != m_aPrivateFontDirectories.end() && *priv_dir != nDirID;
+ ++priv_dir )
+ ;
+
+ if( priv_dir == m_aPrivateFontDirectories.end() )
+ {
+ ByteString aGccDummy( aPath );
+ String aFontsDirPath( aGccDummy, aEncoding );
+ aFontsDirPath.AppendAscii( "/fonts.dir" );
+ SvFileStream aStream( aFontsDirPath, STREAM_READ );
+ if( aStream.IsOpen() )
+ {
+ ByteString aLine;
+ while( ! aStream.IsEof() )
+ {
+ aStream.ReadLine( aLine );
+ ByteString aFileName( GetCommandLineToken( 0, aLine ) );
+ ByteString aXLFD( aLine.Copy( aFileName.Len() ) );
+ if( aFileName.Len() && aXLFD.Len() )
+ aFontsDir[ aFileName ].push_back(aXLFD);
+ }
+ }
+ }
+
+ int nDirFonts = 0;
+ while( ! readdir_r( pDIR, (struct dirent*)aDirEntBuffer, &pEntry ) && pEntry )
+ {
+ OString aFileName( pEntry->d_name );
+ // ignore .afm files here
+ if( aFileName.getLength() > 3 &&
+ aFileName.lastIndexOf( ".afm" ) == aFileName.getLength()-4 )
+ continue;
+
+ struct stat aStat;
+ rtl::OStringBuffer aFilePath(aPath);
+ aFilePath.append('/').append(aFileName);
+ if( ! stat( aFilePath.getStr(), &aStat ) &&
+ S_ISREG( aStat.st_mode ) )
+ {
+ if( findFontFileID( nDirID, aFileName ) == 0 )
+ {
+ ::std::list<OString> aXLFDs;
+ ::boost::unordered_map< OString, ::std::list<OString>, OStringHash >::const_iterator it =
+ aFontsDir.find( aFileName );
+ if( it != aFontsDir.end() )
+ aXLFDs = (*it).second;
+
+ // fill in font attributes from XLFD rather
+ // than reading every file
+ ::std::list< PrintFont* > aNewFonts;
+ if( analyzeFontFile( nDirID, aFileName, aXLFDs, aNewFonts ) )
+ {
+ for( ::std::list< PrintFont* >::iterator font_it = aNewFonts.begin(); font_it != aNewFonts.end(); ++font_it )
+ {
+ fontID aFont = m_nNextFontID++;
+ m_aFonts[ aFont ] = *font_it;
+ m_aFontFileToFontID[ aFileName ].insert( aFont );
+ m_pFontCache->updateFontCacheEntry( *font_it, false );
+ nDirFonts++;
+#if OSL_DEBUG_LEVEL > 2
+ fprintf( stderr, "adding font %d: \"%s\" from %s\n", aFont,
+ OUStringToOString( getFontFamily( aFont ), RTL_TEXTENCODING_MS_1252 ).getStr(),
+ getFontFileSysPath( aFont ).getStr() );
+#endif
+ }
+ }
+ }
+ }
+ }
+ closedir( pDIR );
+ m_pFontCache->updateDirTimestamp( nDirID );
+ if( ! nDirFonts )
+ m_pFontCache->markEmptyDir( nDirID );
+ }
+ }
+
+#if OSL_DEBUG_LEVEL > 1
+ aStep1 = times( &tms );
+#endif
+
+ // part two - look for metrics for builtin printer fonts
+ std::list< OUString > aMetricDirs;
+ psp::getPrinterPathList( aMetricDirs, PRINTER_METRICDIR );
+
+ std::list< OString > aEmptyFontsDir;
+ for( std::list< OUString >::const_iterator met_dir_it = aMetricDirs.begin(); met_dir_it != aMetricDirs.end(); ++met_dir_it )
+ {
+ OString aDir = OUStringToOString( *met_dir_it, aEncoding );
+
+ // ask the font cache whether it handles this directory
+ std::list< PrintFont* > aCacheFonts;
+
+ if( m_pFontCache->listDirectory( aDir, aCacheFonts ) )
+ {
+#if OSL_DEBUG_LEVEL > 1
+ fprintf( stderr, "adding cache directory: %s\n", aDir.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 );
+ else if( (*it)->m_eType == fonttype::Builtin )
+ m_aFontFileToFontID[ static_cast<BuiltinFont*>(*it)->m_aMetricFile ].insert( aFont );
+#if OSL_DEBUG_LEVEL > 1
+ if( (*it)->m_eType == fonttype::Builtin )
+ nBuiltinFonts++;
+ nCached++;
+#if OSL_DEBUG_LEVEL > 2
+ fprintf( stderr, "adding cached font %d: \"%s\" from %s\n", aFont,
+ OUStringToOString( getFontFamily( aFont ), RTL_TEXTENCODING_MS_1252 ).getStr(),
+ getFontFileSysPath( aFont ).getStr() );
+#endif
+#endif
+ }
+ continue;
+ }
+
+ DIR* pDIR = opendir( aDir.getStr() );
+ if( pDIR )
+ {
+ struct dirent* pDirEntry = (struct dirent*)aDirEntBuffer;
+ int nDirID = getDirectoryAtom( aDir, true );
+ int nDirFonts = 0;
+
+ while( ! readdir_r( pDIR, (struct dirent*)aDirEntBuffer, &pDirEntry ) && pDirEntry )
+ {
+ rtl::OStringBuffer aFile(aDir);
+ aFile.append('/').append(pDirEntry->d_name);
+ struct stat aStat;
+ if( ! stat( aFile.getStr(), &aStat )
+ && S_ISREG( aStat.st_mode )
+ )
+ {
+ OString aFileName( pDirEntry->d_name, strlen( pDirEntry->d_name ) );
+ OString aExt( aFileName.copy( aFileName.lastIndexOf( '.' )+1 ) );
+ if( aExt.equalsIgnoreAsciiCase( "afm" ) )
+ {
+ ::std::list< PrintFont* > aNewFonts;
+
+ analyzeFontFile( nDirID, aFileName, aEmptyFontsDir, aNewFonts );
+ for( ::std::list< PrintFont* >::iterator it = aNewFonts.begin(); it != aNewFonts.end(); ++it )
+ {
+ if( findFontBuiltinID( (*it)->m_nPSName ) == 0 )
+ {
+ m_aFontFileToFontID[ aFileName ].insert( m_nNextFontID );
+ m_aFonts[ m_nNextFontID++ ] = *it;
+ m_pFontCache->updateFontCacheEntry( *it, false );
+#if OSL_DEBUG_LEVEL > 2
+ nBuiltinFonts++;
+#endif
+ }
+ else
+ delete *it;
+ }
+ }
+ }
+ }
+ closedir( pDIR );
+ if( ! nDirFonts )
+ m_pFontCache->markEmptyDir( nDirID );
+ }
+ }
+
+#if OSL_DEBUG_LEVEL > 1
+ aStep2 = times( &tms );
+#endif
+
+ // part three - fill in family styles
+ ::boost::unordered_map< fontID, PrintFont* >::iterator font_it;
+ for (font_it = m_aFonts.begin(); font_it != m_aFonts.end(); ++font_it)
+ {
+ ::boost::unordered_map< int, FontFamily >::const_iterator it =
+ m_aFamilyTypes.find( font_it->second->m_nFamilyName );
+ if (it != m_aFamilyTypes.end())
+ continue;
+ const ::rtl::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
+ aStep3 = times( &tms );
+ fprintf( stderr, "PrintFontManager::initialize: collected %" SAL_PRI_SIZET "u fonts (%d builtin, %d cached)\n", m_aFonts.size(), nBuiltinFonts, nCached );
+ 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 );
+ fprintf( stderr, "Step 3 took %lf seconds\n", (double)(aStep3 - aStep2)/fTick );
+#endif
+
+ m_pFontCache->flush();
+
+ #ifdef CALLGRIND_COMPILE
+ CALLGRIND_DUMP_STATS();
+ CALLGRIND_TOGGLE_COLLECT();
+ #endif
+}
+
+// -------------------------------------------------------------------------
+inline bool
+equalPitch (FontPitch from, FontPitch to)
+{
+ return from == to;
+}
+
+inline bool
+equalWeight (FontWeight from, FontWeight to)
+{
+ return from > to ? (from - to) <= 3 : (to - from) <= 3;
+}
+
+inline bool
+equalItalic (FontItalic from, FontItalic to)
+{
+ if ( (from == ITALIC_NORMAL) || (from == ITALIC_OBLIQUE) )
+ return (to == ITALIC_NORMAL) || (to == ITALIC_OBLIQUE);
+ return to == from;
+}
+inline bool
+equalEncoding (rtl_TextEncoding from, rtl_TextEncoding to)
+{
+ if ((from == RTL_TEXTENCODING_ISO_8859_1) || (from == RTL_TEXTENCODING_MS_1252))
+ return (to == RTL_TEXTENCODING_ISO_8859_1) || (to == RTL_TEXTENCODING_MS_1252);
+ return from == to;
+}
+
+namespace {
+ struct BuiltinFontIdentifier
+ {
+ OUString aFamily;
+ FontItalic eItalic;
+ FontWeight eWeight;
+ FontPitch ePitch;
+ rtl_TextEncoding aEncoding;
+
+ BuiltinFontIdentifier( const OUString& rFam,
+ FontItalic eIt,
+ FontWeight eWg,
+ FontPitch ePt,
+ rtl_TextEncoding enc ) :
+ aFamily( rFam ),
+ eItalic( eIt ),
+ eWeight( eWg ),
+ ePitch( ePt ),
+ aEncoding( enc )
+ {}
+
+ bool operator==( const BuiltinFontIdentifier& rRight ) const
+ {
+ return equalItalic( eItalic, rRight.eItalic ) &&
+ equalWeight( eWeight, rRight.eWeight ) &&
+ equalPitch( ePitch, rRight.ePitch ) &&
+ equalEncoding( aEncoding, rRight.aEncoding ) &&
+ aFamily.equalsIgnoreAsciiCase( rRight.aFamily );
+ }
+ };
+
+ struct BuiltinFontIdentifierHash
+ {
+ size_t operator()( const BuiltinFontIdentifier& rFont ) const
+ {
+ return rFont.aFamily.hashCode() ^ rFont.eItalic ^ rFont.eWeight ^ rFont.ePitch ^ rFont.aEncoding;
+ }
+ };
+}
+
+void PrintFontManager::getFontList( ::std::list< fontID >& rFontIDs, const PPDParser* pParser, bool bUseOverrideMetrics )
+{
+ rFontIDs.clear();
+ boost::unordered_map< fontID, PrintFont* >::const_iterator it;
+
+ /*
+ * Note: there are two easy steps making this faster:
+ * first: insert the printer builtins first, then the not builtins,
+ * if they do not match.
+ * drawback: this would change the sequence of fonts; this could have
+ * subtle, unknown consequences in vcl font matching
+ * second: instead of comparing attributes to see whether a softfont
+ * is duplicate to a builtin one could simply compare the PSName (which is
+ * supposed to be unique), which at this point is just an int.
+ * drawback: this could change which fonts are listed; especially TrueType
+ * fonts often have a rather dubious PSName, so this could change the
+ * font list not so subtle.
+ * Until getFontList for a printer becomes a performance issue (which is
+ * currently not the case), best stay with the current algorithm.
+ */
+
+ // fill sets of printer supported fonts
+ if( pParser )
+ {
+ std::set<int> aBuiltinPSNames;
+ boost::unordered_set< BuiltinFontIdentifier,
+ BuiltinFontIdentifierHash
+ > aBuiltinFonts;
+
+ std::map<int, fontID > aOverridePSNames;
+ if( bUseOverrideMetrics )
+ {
+ readOverrideMetrics();
+ for( std::vector<fontID>::const_iterator over = m_aOverrideFonts.begin();
+ over != m_aOverrideFonts.end(); ++over )
+ {
+ boost::unordered_map<fontID,PrintFont*>::const_iterator font_it = m_aFonts.find( *over );
+ DBG_ASSERT( font_it != m_aFonts.end(), "override to nonexistant font" );
+ if( font_it != m_aFonts.end() )
+ aOverridePSNames[ font_it->second->m_nPSName ] = *over;
+ }
+ }
+
+ int nFonts = pParser->getFonts();
+ for( int i = 0; i < nFonts; i++ )
+ aBuiltinPSNames.insert( m_pAtoms->getAtom( ATOM_PSNAME, pParser->getFont( i ) ) );
+ for( it = m_aFonts.begin(); it != m_aFonts.end(); ++it )
+ {
+ PrintFont* pFont = it->second;
+ if( it->second->m_eType == fonttype::Builtin &&
+ aBuiltinPSNames.find( pFont->m_nPSName ) != aBuiltinPSNames.end() )
+ {
+ bool bInsert = true;
+ if( bUseOverrideMetrics )
+ {
+ // in override case only use the override fonts, not their counterparts
+ std::map<int,fontID>::const_iterator over = aOverridePSNames.find( pFont->m_nPSName );
+ if( over != aOverridePSNames.end() && over->second != it->first )
+ bInsert = false;
+ }
+ else
+ {
+ // do not insert override fonts in non override case
+ if( std::find( m_aOverrideFonts.begin(), m_aOverrideFonts.end(), it->first ) != m_aOverrideFonts.end() )
+ bInsert = false;
+ }
+ if( bInsert )
+ {
+ aBuiltinFonts.insert( BuiltinFontIdentifier(
+ m_pAtoms->getString( ATOM_FAMILYNAME, pFont->m_nFamilyName ),
+ pFont->m_eItalic,
+ pFont->m_eWeight,
+ pFont->m_ePitch,
+ pFont->m_aEncoding
+ ) );
+ }
+ }
+ }
+ for( it = m_aFonts.begin(); it != m_aFonts.end(); ++it )
+ {
+ PrintFont* pFont = it->second;
+ if( it->second->m_eType == fonttype::Builtin )
+ {
+ if( aBuiltinPSNames.find( pFont->m_nPSName ) != aBuiltinPSNames.end() )
+ {
+ bool bInsert = true;
+ if( bUseOverrideMetrics )
+ {
+ // in override case only use the override fonts, not their counterparts
+ std::map<int,fontID>::const_iterator over = aOverridePSNames.find( pFont->m_nPSName );
+ if( over != aOverridePSNames.end() && over->second != it->first )
+ bInsert = false;
+ }
+ else
+ {
+ // do not insert override fonts in non override case
+ if( std::find( m_aOverrideFonts.begin(), m_aOverrideFonts.end(), it->first ) != m_aOverrideFonts.end() )
+ bInsert = false;
+ }
+ if( bInsert )
+ rFontIDs.push_back( it->first );
+ }
+ }
+ else if( aBuiltinFonts.find( BuiltinFontIdentifier(
+ m_pAtoms->getString( ATOM_FAMILYNAME, pFont->m_nFamilyName ),
+ pFont->m_eItalic,
+ pFont->m_eWeight,
+ pFont->m_ePitch,
+ pFont->m_aEncoding
+ ) ) == aBuiltinFonts.end() )
+ {
+ rFontIDs.push_back( it->first );
+ }
+ }
+ }
+ else // no specific printer
+ {
+ for( it = m_aFonts.begin(); it != m_aFonts.end(); ++it )
+ rFontIDs.push_back( it->first );
+ }
+}
+
+// -------------------------------------------------------------------------
+
+void PrintFontManager::fillPrintFontInfo( PrintFont* pFont, FastPrintFontInfo& rInfo ) const
+{
+ ::boost::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( getAfmFile( pFont ), 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;
+}
+
+// -------------------------------------------------------------------------
+
+void PrintFontManager::getFontListWithFastInfo( ::std::list< FastPrintFontInfo >& rFonts, const PPDParser* pParser, bool bUseOverrideMetrics )
+{
+ rFonts.clear();
+ ::std::list< fontID > aFontList;
+ getFontList( aFontList, pParser, bUseOverrideMetrics );
+
+ ::std::list< fontID >::iterator it;
+ for( it = aFontList.begin(); it != aFontList.end(); ++it )
+ {
+ FastPrintFontInfo aInfo;
+ aInfo.m_nID = *it;
+ fillPrintFontInfo( getFont( *it ), aInfo );
+ rFonts.push_back( aInfo );
+ }
+}
+
+// -------------------------------------------------------------------------
+
+bool PrintFontManager::getFontInfo( fontID nFontID, PrintFontInfo& rInfo ) const
+{
+ PrintFont* pFont = getFont( nFontID );
+ if( pFont )
+ {
+ rInfo.m_nID = nFontID;
+ fillPrintFontInfo( pFont, rInfo );
+ }
+ return pFont ? true : false;
+}
+
+// -------------------------------------------------------------------------
+
+bool PrintFontManager::getFontFastInfo( fontID nFontID, FastPrintFontInfo& rInfo ) const
+{
+ PrintFont* pFont = getFont( nFontID );
+ if( pFont )
+ {
+ rInfo.m_nID = nFontID;
+ fillPrintFontInfo( pFont, rInfo );
+ }
+ return pFont ? true : false;
+}
+
+// -------------------------------------------------------------------------
+
+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->m_eType == fonttype::Builtin )
+ pFont->readAfmMetrics( getAfmFile( pFont ), 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 = -1;
+ PrintFont* pFont = getFont( nFontID );
+ if( pFont && pFont->m_eType == fonttype::TrueType )
+ nRet = static_cast< TrueTypeFontFile* >(pFont)->m_nCollectionEntry;
+ return nRet;
+}
+
+// -------------------------------------------------------------------------
+
+
+FontFamily PrintFontManager::matchFamilyName( const ::rtl::OUString& rFamily ) const
+{
+ 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 ) }
+ };
+
+ rtl::OString aFamily = rtl::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;
+}
+
+// -------------------------------------------------------------------------
+
+const ::rtl::OUString& PrintFontManager::getFontFamily( fontID nFontID ) const
+{
+ PrintFont* pFont = getFont( nFontID );
+ return m_pAtoms->getString( ATOM_FAMILYNAME, pFont ? pFont->m_nFamilyName : INVALID_ATOM );
+}
+
+// -------------------------------------------------------------------------
+
+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;
+ case fonttype::Builtin:
+ {
+ BuiltinFont* pBuiltinFont = static_cast< BuiltinFont* >(pFont);
+ aMetricPath = getDirectory( pBuiltinFont->m_nDirectory );
+ aMetricPath += "/";
+ aMetricPath += pBuiltinFont->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);
+ ::boost::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);
+ ::boost::unordered_map< int, OString >::const_iterator it = m_aAtomToDir.find( pTTFont->m_nDirectory );
+ aPath = it->second;
+ aPath += "/";
+ aPath += pTTFont->m_aFontFile;
+ }
+ return aPath;
+}
+
+// -------------------------------------------------------------------------
+
+const ::rtl::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->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->m_eType == fonttype::Builtin )
+ pFont->readAfmMetrics( getAfmFile( pFont ), m_pAtoms, false, true );
+ }
+ return pFont->m_nAscend;
+}
+
+// -------------------------------------------------------------------------
+
+int PrintFontManager::getFontDescend( fontID nFontID ) const
+{
+ PrintFont* pFont = getFont( nFontID );
+ if( 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->m_eType == fonttype::Builtin )
+ pFont->readAfmMetrics( getAfmFile( pFont ), m_pAtoms, false, true );
+ }
+ return pFont->m_nDescend;
+}
+
+// -------------------------------------------------------------------------
+
+void PrintFontManager::hasVerticalSubstitutions( fontID nFontID,
+ const sal_Unicode* pCharacters, int nCharacters, bool* pHasSubst ) const
+{
+ PrintFont* pFont = getFont( nFontID );
+ if( 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->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 );
+ ::boost::unordered_map< sal_Unicode, bool >::const_iterator it = pFont->m_pMetrics->m_bVerticalSubstitutions.find( code );
+ pHasSubst[i] = it != pFont->m_pMetrics->m_bVerticalSubstitutions.end();
+ }
+ }
+}
+
+// -------------------------------------------------------------------------
+
+OUString PrintFontManager::getFontXLFD( fontID nFontID ) const
+{
+ PrintFont* pFont = getFont( nFontID );
+ OUString aRet;
+ if( pFont )
+ {
+ ByteString aXLFD( getXLFD( pFont ) );
+ rtl_TextEncoding aEncoding = aXLFD.GetToken( 6, '-' ).Search( "utf8" ) != STRING_NOTFOUND ? RTL_TEXTENCODING_UTF8 : RTL_TEXTENCODING_ISO_8859_1;
+ aRet = OStringToOUString( aXLFD, aEncoding );
+ }
+ return aRet;
+}
+
+// -------------------------------------------------------------------------
+
+const ::std::list< KernPair >& PrintFontManager::getKernPairs( fontID nFontID, bool bVertical ) const
+{
+ static ::std::list< KernPair > aEmpty;
+
+ PrintFont* pFont = getFont( nFontID );
+ if( ! pFont )
+ return aEmpty;
+
+ if( ! pFont->m_pMetrics || ! pFont->m_pMetrics->m_bKernPairsQueried )
+ pFont->queryMetricPage( 0, m_pAtoms );
+ if( ! pFont->m_pMetrics || ! pFont->m_pMetrics->m_bKernPairsQueried )
+ return aEmpty;
+ return bVertical ? pFont->m_pMetrics->m_aYKernPairs : pFont->m_pMetrics->m_aXKernPairs;
+}
+
+// -------------------------------------------------------------------------
+
+bool PrintFontManager::isFontDownloadingAllowed( 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 = NULL;
+ ByteString aFile = getFontFile( pFont );
+ if( OpenTTFontFile( aFile.GetBuffer(), pTTFontFile->m_nCollectionEntry < 0 ? 0 : 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;
+
+ // font embedding is allowed if either
+ // no restriction at all (bit 1 clear)
+ // printing allowed (bit 1 set, bit 2 set )
+ bRet = ! ( nCopyrightFlags & 0x02 ) || ( nCopyrightFlags & 0x04 );
+ }
+ }
+ 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->m_eType == fonttype::Builtin )
+ pFont->readAfmMetrics( getAfmFile( pFont ), 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;
+ ::boost::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->m_eType == fonttype::Builtin )
+ pFont->readAfmMetrics( getAfmFile( pFont ), 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;
+ ::boost::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;
+}
+
+// -------------------------------------------------------------------------
+
+static bool createWriteablePath( const ByteString& rPath )
+{
+ bool bSuccess = false;
+
+ if( access( rPath.GetBuffer(), W_OK ) )
+ {
+ int nPos = rPath.SearchBackward( '/' );
+ if( nPos != STRING_NOTFOUND )
+ while( nPos > 0 && rPath.GetChar( nPos ) == '/' )
+ nPos--;
+
+ if( nPos != STRING_NOTFOUND && nPos != 0 && createWriteablePath( rPath.Copy( 0, nPos+1 ) ) )
+ {
+ bSuccess = mkdir( rPath.GetBuffer(), 0777 ) ? false : true;
+ }
+ }
+ else
+ bSuccess = true;
+
+ return bSuccess;
+}
+
+
+// -------------------------------------------------------------------------
+
+int PrintFontManager::importFonts( const ::std::list< OString >& rFiles, bool bLinkOnly, ImportFontCallback* pCallback )
+{
+ int nSuccess = 0;
+
+ // find a directory with write access
+ rtl_TextEncoding aEncoding = osl_getThreadTextEncoding();
+ bool bCanWrite = false;
+ int nDirID = 0;
+ INetURLObject aDir;
+ for( ::std::list< int >::const_iterator dir_it = m_aPrivateFontDirectories.begin();
+ ! bCanWrite && dir_it != m_aPrivateFontDirectories.end(); ++dir_it )
+ {
+ // check if we can create files in that directory
+ ByteString aDirPath = getDirectory( *dir_it );
+ if( createWriteablePath( aDirPath ) )
+ {
+ aDir = INetURLObject( OStringToOUString( aDirPath, aEncoding ), INET_PROT_FILE, INetURLObject::ENCODE_ALL );
+ nDirID = *dir_it;
+ bCanWrite = true;
+ }
+ }
+ if( bCanWrite )
+ {
+ for( ::std::list< OString >::const_iterator font_it = rFiles.begin();
+ font_it != rFiles.end(); ++font_it )
+ {
+ INetURLObject aFrom( OStringToOUString( *font_it, aEncoding ), INET_PROT_FILE, INetURLObject::ENCODE_ALL );
+ INetURLObject aTo( aDir );
+ aTo.Append( aFrom.GetName() );
+
+ if( pCallback )
+ pCallback->progress( aTo.PathToFileName() );
+
+ if( pCallback && pCallback->isCanceled() )
+ break;
+
+ if( ! access( ByteString( String(aTo.PathToFileName()), aEncoding ).GetBuffer(), F_OK ) )
+ {
+ if( ! ( pCallback ? pCallback->queryOverwriteFile( aTo.PathToFileName() ) : false ) )
+ continue;
+ }
+ // look for afm if necessary
+ OUString aAfmCopied;
+ FileBase::RC nError;
+ if( aFrom.getExtension().equalsIgnoreAsciiCaseAscii( "pfa" ) ||
+ aFrom.getExtension().equalsIgnoreAsciiCaseAscii( "pfb" ) )
+ {
+ INetURLObject aFromAfm( aFrom );
+ aFromAfm.setExtension( rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "afm" ) ) );
+ if( access( ByteString( String(aFromAfm.PathToFileName()), aEncoding ).GetBuffer(), F_OK ) )
+ {
+ aFromAfm.setExtension( rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "AFM" ) ) );
+ if( access( ByteString( String(aFromAfm.PathToFileName()), aEncoding ).GetBuffer(), F_OK ) )
+ {
+ aFromAfm.removeSegment();
+ aFromAfm.Append( String( RTL_CONSTASCII_USTRINGPARAM( "afm" ) ) );
+ aFromAfm.Append( aTo.GetName() );
+ aFromAfm.setExtension( rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "afm" ) ) );
+ if( access( ByteString( String(aFromAfm.PathToFileName()), aEncoding ).GetBuffer(), F_OK ) )
+ {
+ aFromAfm.setExtension( rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "AFM" ) ) );
+ if( access( ByteString( String(aFromAfm.PathToFileName()), aEncoding ).GetBuffer(), F_OK ) )
+ {
+ // give up
+ if( pCallback )
+ pCallback->importFontFailed( aTo.PathToFileName(), ImportFontCallback::NoAfmMetric );
+ continue;
+ }
+ }
+ }
+ }
+ INetURLObject aToAfm( aTo );
+ aToAfm.setExtension( rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "afm" ) ) );
+ OUString aFromPath, aToPath;
+ if( bLinkOnly )
+ {
+ ByteString aLinkFromPath( String(aFromAfm.PathToFileName()),
+ aEncoding );
+ ByteString aLinkToPath( String(aToAfm.PathToFileName()),
+ aEncoding );
+ nError = (FileBase::RC)symlink( aLinkFromPath.GetBuffer(), aLinkToPath.GetBuffer() );
+ }
+ else
+ nError = File::copy( aFromAfm.GetMainURL(INetURLObject::DECODE_TO_IURI), aToAfm.GetMainURL(INetURLObject::DECODE_TO_IURI) );
+ if( nError )
+ {
+ if( pCallback )
+ pCallback->importFontFailed( aTo.PathToFileName(), ImportFontCallback::AfmCopyFailed );
+ continue;
+ }
+ aAfmCopied = aToPath;
+ }
+ if( bLinkOnly )
+ {
+ ByteString aFromPath( String(aFrom.PathToFileName()),
+ aEncoding );
+ ByteString aToPath( String(aTo.PathToFileName()), aEncoding );
+ nError = (FileBase::RC)symlink( aFromPath.GetBuffer(),
+ aToPath.GetBuffer() );
+ }
+ else
+ nError = File::copy( aFrom.GetMainURL(INetURLObject::DECODE_TO_IURI), aTo.GetMainURL(INetURLObject::DECODE_TO_IURI) );
+ // copy font file
+ if( nError )
+ {
+ if( aAfmCopied.getLength() )
+ File::remove( aAfmCopied );
+ if( pCallback )
+ pCallback->importFontFailed( aTo.PathToFileName(), ImportFontCallback::FontCopyFailed );
+ continue;
+ }
+
+ ::std::list< PrintFont* > aNewFonts;
+ ::std::list< PrintFont* >::iterator it;
+ if( analyzeFontFile( nDirID, OUStringToOString( aTo.GetName(), aEncoding ), ::std::list<OString>(), aNewFonts ) )
+ {
+ // remove all fonts for the same file
+ // discarding their font ids
+ ::boost::unordered_map< fontID, PrintFont* >::iterator current, next;
+ current = m_aFonts.begin();
+ OString aFileName( OUStringToOString( aTo.GetName(), aEncoding ) );
+ while( current != m_aFonts.end() )
+ {
+ bool bRemove = false;
+ switch( current->second->m_eType )
+ {
+ case fonttype::Type1:
+ if( static_cast<Type1FontFile*>(current->second)->m_aFontFile == aFileName )
+ bRemove = true;
+ break;
+ case fonttype::TrueType:
+ if( static_cast<TrueTypeFontFile*>(current->second)->m_aFontFile == aFileName )
+ bRemove = true;
+ break;
+ default: break;
+ }
+ if( bRemove )
+ {
+ next = current;
+ ++next;
+ m_aFontFileToFontID[ aFileName ].erase( current->first );
+ delete current->second;
+ m_aFonts.erase( current );
+ current = next;
+ }
+ else
+ ++current;
+ }
+
+ DBG_ASSERT( !findFontFileID( nDirID, aFileName ), "not all fonts removed for file" );
+
+ nSuccess++;
+ for( it = aNewFonts.begin(); it != aNewFonts.end(); ++it )
+ {
+ m_aFontFileToFontID[ aFileName ].insert( m_nNextFontID );
+ m_aFonts[ m_nNextFontID++ ] = *it;
+ m_pFontCache->updateFontCacheEntry( *it, false );
+ }
+ }
+ }
+
+ m_pFontCache->updateDirTimestamp( nDirID );
+ m_pFontCache->flush();
+ }
+ else if( pCallback )
+ pCallback->importFontsFailed( ImportFontCallback::NoWritableDirectory );
+
+ return nSuccess;
+}
+
+// -------------------------------------------------------------------------
+
+bool PrintFontManager::checkImportPossible() const
+{
+ bool bSuccess = false;
+
+ // find a directory with write access
+ ByteString aDir;
+ for( std::list< int >::const_iterator dir_it = m_aPrivateFontDirectories.begin();
+ dir_it != m_aPrivateFontDirectories.end(); ++dir_it )
+ {
+ aDir = getDirectory( *dir_it );
+ if( createWriteablePath( aDir ) )
+ {
+ bSuccess = true;
+ break;
+ }
+ }
+
+#if OSL_DEBUG_LEVEL > 1
+ if( bSuccess )
+ fprintf( stderr, "found writable %s\n", aDir.GetBuffer() );
+#endif
+
+ return bSuccess;
+}
+
+// -------------------------------------------------------------------------
+
+bool PrintFontManager::checkChangeFontPropertiesPossible( fontID /*nFontID*/ ) const
+{
+ // since font properties are changed in the font cache file only nowadays
+ // they can always be changed
+ return true;
+}
+
+// -------------------------------------------------------------------------
+
+bool PrintFontManager::changeFontProperties( fontID nFontID, const ::rtl::OUString& rXLFD )
+{
+ ByteString aXLFD( OUStringToOString( rXLFD, RTL_TEXTENCODING_UTF8 ) );
+ ByteString aAddStyle = aXLFD.GetToken( '-', 6 );
+ if( aAddStyle.Search( "utf8" ) == STRING_NOTFOUND )
+ {
+ aAddStyle.Append( aAddStyle.Len() ? ";utf8" : "utf8" );
+ aXLFD.SetToken( 6, ';', aAddStyle );
+ }
+ PrintFont* pFont = getFont( nFontID );
+ std::list< OString > aDummyList;
+ aDummyList.push_back( aXLFD );
+ getFontAttributesFromXLFD( pFont, aDummyList );
+ pFont->m_bUserOverride = true;
+ m_pFontCache->updateFontCacheEntry( pFont, true );
+
+ return true;
+}
+
+// -------------------------------------------------------------------------
+
+bool PrintFontManager::
+getImportableFontProperties(
+ const OString& rFile,
+ ::std::list< FastPrintFontInfo >& rFontProps
+ )
+{
+ rFontProps.clear();
+ int nIndex = rFile.lastIndexOf( '/' );
+ OString aDir, aFile( rFile.copy( nIndex+1 ) );
+ if( nIndex != -1 )
+ aDir = rFile.copy( 0, nIndex );
+ int nDirID = getDirectoryAtom( aDir, true );
+ ::std::list< PrintFont* > aFonts;
+ bool bRet = analyzeFontFile( nDirID, aFile, ::std::list<OString>(), aFonts );
+ while( aFonts.begin() != aFonts.end() )
+ {
+ PrintFont* pFont = aFonts.front();
+ aFonts.pop_front();
+ FastPrintFontInfo aInfo;
+ fillPrintFontInfo( pFont, aInfo );
+ rFontProps.push_back( aInfo );
+ delete pFont;
+ }
+ return bRet;
+}
+
+// -------------------------------------------------------------------------
+
+bool PrintFontManager::getFileDuplicates( fontID nFont, ::std::list< fontID >& rFonts ) const
+{
+ bool bRet = false;
+
+ rFonts.clear();
+
+ PrintFont* pSearchFont = getFont( nFont );
+ if( ! pSearchFont ||
+ pSearchFont->m_eType != fonttype::TrueType ||
+ static_cast<TrueTypeFontFile*>(pSearchFont)->m_nCollectionEntry == -1
+ )
+ return false;
+
+ OString aFile( getFontFileSysPath( nFont ) );
+ if( ! aFile.getLength() )
+ return false;
+
+ for( ::boost::unordered_map< fontID, PrintFont* >::const_iterator it = m_aFonts.begin(); it != m_aFonts.end(); ++it )
+ {
+ if( nFont != it->first )
+ {
+ OString aCompFile( getFontFile( it->second ) );
+ if( aCompFile == aFile )
+ {
+ rFonts.push_back( it->first );
+ bRet = true;
+ }
+ }
+ }
+ return bRet;
+}
+
+// -------------------------------------------------------------------------
+
+bool PrintFontManager::removeFonts( const ::std::list< fontID >& rFonts )
+{
+ bool bRet = true;
+ ::std::list< fontID > aDuplicates;
+ for( ::std::list< fontID >::const_iterator it = rFonts.begin(); it != rFonts.end(); ++it )
+ {
+ ::boost::unordered_map< fontID, PrintFont* >::const_iterator haveFont = m_aFonts.find( *it );
+ if( haveFont == m_aFonts.end() )
+ continue;
+
+ PrintFont* pFont = haveFont->second;
+ bool bRemoveDuplicates = getFileDuplicates( *it, aDuplicates );
+ ByteString aFile( getFontFile( pFont ) );
+ if( aFile.Len() )
+ {
+#if OSL_DEBUG_LEVEL > 1
+ fprintf( stderr, "try unlink( \"%s\" ) ... ", aFile.GetBuffer() );
+#endif
+ if( unlink( aFile.GetBuffer() ) )
+ {
+ bRet = false;
+#if OSL_DEBUG_LEVEL > 1
+ fprintf( stderr, "failed\n" );
+#endif
+ continue;
+ }
+#if OSL_DEBUG_LEVEL > 1
+ fprintf( stderr, "succeeded\n" );
+#endif
+ OString aAfm( getAfmFile( pFont ) );
+ if( aAfm.getLength() )
+ {
+#if OSL_DEBUG_LEVEL > 1
+ fprintf( stderr, "unlink( \"%s\" )\n", aAfm.getStr() );
+#endif
+ unlink( aAfm.getStr() );
+ }
+ m_aFonts.erase( *it );
+ delete pFont;
+ if( bRemoveDuplicates )
+ {
+ for( ::std::list< fontID >::iterator dup = aDuplicates.begin(); dup != aDuplicates.end(); ++dup )
+ {
+ m_aFontFileToFontID[ aFile ].erase( *dup );
+ PrintFont* pDup = m_aFonts[ *dup ];
+ m_aFonts.erase( *dup );
+ delete pDup;
+ }
+ }
+ }
+ }
+ return bRet;
+}
+
+// -------------------------------------------------------------------------
+
+bool PrintFontManager::isPrivateFontFile( fontID nFont ) const
+{
+ bool bRet = false;
+ int nDirID = -1;
+ PrintFont* pFont = getFont( nFont );
+ if( pFont )
+ {
+ switch( pFont->m_eType )
+ {
+ case fonttype::Type1: nDirID = static_cast< Type1FontFile* >(pFont)->m_nDirectory;break;
+ case fonttype::TrueType: nDirID = static_cast< TrueTypeFontFile* >(pFont)->m_nDirectory;break;
+ default: break;
+ }
+ }
+ if( nDirID != -1 )
+ {
+ for( ::std::list< int >::const_iterator it = m_aPrivateFontDirectories.begin(); it != m_aPrivateFontDirectories.end(); ++it )
+ {
+ if( nDirID == *it )
+ {
+ bRet = true;
+ break;
+ }
+ }
+ }
+ return bRet;
+}
+
+// -------------------------------------------------------------------------
+
+bool PrintFontManager::getAlternativeFamilyNames( fontID nFont, ::std::list< OUString >& rNames ) const
+{
+ rNames.clear();
+
+ PrintFont* pFont = getFont( nFont );
+ if( pFont && pFont->m_eType == fonttype::TrueType )
+ {
+ TrueTypeFontFile* pTTFontFile = static_cast< TrueTypeFontFile* >(pFont);
+ ByteString aFile( getFontFile( pFont ) );
+ TrueTypeFont* pTTFont;
+ if( OpenTTFontFile( aFile.GetBuffer(), pTTFontFile->m_nCollectionEntry < 0 ? 0 : pTTFontFile->m_nCollectionEntry, &pTTFont ) == SF_OK )
+ {
+ NameRecord* pNameRecords = NULL;
+ int nNameRecords = GetTTNameRecords( pTTFont, &pNameRecords );
+ for( int i = 0; i < nNameRecords; i++ )
+ {
+ if( pNameRecords[i].nameID != 1 ) // family name
+ continue;
+
+ OUString aFamily( convertTrueTypeName( pNameRecords+i ) );
+ if( aFamily.getLength()
+ &&
+ m_pAtoms->getAtom( ATOM_FAMILYNAME, aFamily, sal_True ) != pFont->m_nFamilyName
+ )
+ {
+ rNames.push_back( aFamily );
+ }
+ }
+
+ if( nNameRecords )
+ DisposeNameRecords( pNameRecords, nNameRecords );
+ CloseTTFont( pTTFont );
+ }
+ }
+ return rNames.begin() != rNames.end();
+}
+
+// -------------------------------------------------------------------------
+
+// TODO: move most of this stuff into the central font-subsetting code
+bool PrintFontManager::createFontSubset(
+ FontSubsetInfo& rInfo,
+ fontID nFont,
+ const OUString& rOutFile,
+ sal_Int32* pGlyphIDs,
+ 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 ByteString aFromFile = getFontFile( pFont );
+
+ TrueTypeFont* pTTFont = NULL; // TODO: rename to SfntFont
+ TrueTypeFontFile* pTTFontFile = static_cast< TrueTypeFontFile* >(pFont);
+ if( OpenTTFontFile( aFromFile.GetBuffer(), pTTFontFile->m_nCollectionEntry < 0 ? 0 : 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 ByteString aToFile( OUStringToOString( aSysPath, aEncoding ) );
+
+ // do CFF subsetting if possible
+ int nCffLength = 0;
+ const sal_uInt8* pCffBytes = NULL;
+ 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
+ long aRequestedGlyphs[256];
+ for( int i = 0; i < nGlyphs; ++i )
+ aRequestedGlyphs[i] = pGID[i];
+#endif
+ // create subset file at requested path
+ FILE* pOutFile = fopen( aToFile.GetBuffer(), "wb" );
+ // create font subset
+ const char* pGlyphSetName = NULL; // TODO: better name?
+ const bool bOK = rInfo.CreateFontSubset(
+ FontSubsetInfo::TYPE1_PFB,
+ pOutFile, pGlyphSetName,
+ aRequestedGlyphs, 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 ? 1 : 0 );
+ 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.GetBuffer(),
+ pGID,
+ pEnc,
+ nGlyphs,
+ 0,
+ NULL,
+ 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 = NULL;
+ TrueTypeFontFile* pTTFontFile = static_cast< TrueTypeFontFile* >(pFont);
+ ByteString aFromFile = getFontFile( pFont );
+ if( OpenTTFontFile( aFromFile.GetBuffer(), pTTFontFile->m_nCollectionEntry < 0 ? 0 : 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 ? 1 : 0 );
+ 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 = NULL;
+ int nCmapSize = 0;
+ if( GetSfntTable( pTTFont, O_cmap, &pCmapData, &nCmapSize ) )
+ {
+ CmapResult aCmapResult;
+ if( ParseCMAP( pCmapData, nCmapSize, aCmapResult ) )
+ {
+ const ImplFontCharMap aCharMap( aCmapResult );
+ for( sal_uInt32 cOld = 0;;)
+ {
+ // get next unicode covered by font
+ const sal_uInt32 c = aCharMap.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_uInt32 nGlyphId = aCharMap.GetGlyphIndex( c );
+ // update the requested map
+ rUnicodeEnc[ (sal_Unicode)c ] = nGlyphId;
+ }
+ }
+ }
+ }
+ CloseTTFont( pTTFont );
+ }
+ else if( pFont->m_eType == fonttype::Type1 )
+ {
+ if( ! pFont->m_aEncodingVector.size() )
+ pFont->readAfmMetrics( getAfmFile( pFont ), m_pAtoms, true, true );
+ if( pFont->m_pMetrics )
+ {
+ rUnicodeEnc.clear();
+ rWidths.clear();
+ rWidths.reserve( pFont->m_pMetrics->m_aMetrics.size() );
+ for( boost::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, rtl::OString >** pNonEncoded ) const
+{
+ PrintFont* pFont = getFont( nFont );
+ if( !pFont ||
+ (pFont->m_eType != fonttype::Type1 && pFont->m_eType != fonttype::Builtin)
+ )
+ return NULL;
+
+ if( ! pFont->m_aEncodingVector.size() )
+ pFont->readAfmMetrics( getAfmFile( pFont ), m_pAtoms, true, true );
+
+ if( pNonEncoded )
+ *pNonEncoded = pFont->m_aNonEncoded.size() ? &pFont->m_aNonEncoded : NULL;
+
+ return pFont->m_aEncodingVector.size() ? &pFont->m_aEncodingVector : NULL;
+}
+
+// -------------------------------------------------------------------------
+
+std::list< OString > PrintFontManager::getAdobeNameFromUnicode( sal_Unicode aChar ) const
+{
+ std::pair< boost::unordered_multimap< sal_Unicode, rtl::OString >::const_iterator,
+ boost::unordered_multimap< sal_Unicode, rtl::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.begin() == aRet.end() && aChar != 0 )
+ {
+ sal_Char aBuf[8];
+ sal_Int32 nChars = snprintf( (char*)aBuf, sizeof( aBuf ), "uni%.4hX", aChar );
+ aRet.push_back( OString( aBuf, nChars ) );
+ }
+
+ return aRet;
+}
+
+// -------------------------------------------------------------------------
+std::list< sal_Unicode > PrintFontManager::getUnicodeFromAdobeName( const rtl::OString& rName ) const
+{
+ std::pair< boost::unordered_multimap< rtl::OString, sal_Unicode, rtl::OStringHash >::const_iterator,
+ boost::unordered_multimap< rtl::OString, sal_Unicode, rtl::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.begin() == aRet.end() )
+ {
+ if( rName.getLength() == 7 && rName.indexOf( "uni" ) == 0 )
+ {
+ sal_Unicode aCode = (sal_Unicode)rName.copy( 3 ).toInt32( 16 );
+ aRet.push_back( aCode );
+ }
+ }
+
+ return aRet;
+}
+
+// -------------------------------------------------------------------------
+namespace
+{
+ OUString getString( const Any& rAny )
+ {
+ OUString aStr;
+ rAny >>= aStr;
+ return aStr;
+ }
+ bool getBool( const Any& rAny )
+ {
+ sal_Bool bBool = sal_False;
+ rAny >>= bBool;
+ return static_cast<bool>(bBool);
+ }
+ sal_Int32 getInt( const Any& rAny )
+ {
+ sal_Int32 n = 0;
+ rAny >>= n;
+ return n;
+ }
+}
+bool PrintFontManager::readOverrideMetrics()
+{
+ if( ! m_aOverrideFonts.empty() )
+ return false;
+
+ css::uno::Reference< XMultiServiceFactory > xFact( comphelper::getProcessServiceFactory() );
+ if( !xFact.is() )
+ return false;
+ css::uno::Reference< XMaterialHolder > xMat(
+ xFact->createInstance( OUString( RTL_CONSTASCII_USTRINGPARAM( "com.sun.star.psprint.CompatMetricOverride" ) ) ),
+ UNO_QUERY );
+ if( !xMat.is() )
+ return false;
+
+ Any aAny( xMat->getMaterial() );
+ Sequence< Any > aOverrideFonts;
+ if( ! (aAny >>= aOverrideFonts ) )
+ return false;
+ sal_Int32 nFonts = aOverrideFonts.getLength();
+ for( sal_Int32 i = 0; i < nFonts; i++ )
+ {
+ Sequence< NamedValue > aMetrics;
+ if( ! (aOverrideFonts.getConstArray()[i] >>= aMetrics) )
+ continue;
+ BuiltinFont* pFont = new BuiltinFont();
+ pFont->m_nDirectory = 0;
+ pFont->m_bUserOverride = false;
+ pFont->m_pMetrics = new PrintFontMetrics;
+ memset( pFont->m_pMetrics->m_aPages, 0xff, sizeof( pFont->m_pMetrics->m_aPages ) );
+ pFont->m_pMetrics->m_bKernPairsQueried = true;
+ sal_Int32 nProps = aMetrics.getLength();
+ const NamedValue* pProps = aMetrics.getConstArray();
+ for( sal_Int32 n = 0; n < nProps; n++ )
+ {
+ if( pProps[n].Name.equalsAsciiL( RTL_CONSTASCII_STRINGPARAM( "FamilyName" ) ) )
+ pFont->m_nFamilyName = m_pAtoms->getAtom( ATOM_FAMILYNAME,
+ getString(pProps[n].Value),
+ sal_True );
+ else if( pProps[n].Name.equalsAsciiL( RTL_CONSTASCII_STRINGPARAM( "PSName" ) ) )
+ pFont->m_nPSName = m_pAtoms->getAtom( ATOM_PSNAME,
+ getString(pProps[n].Value),
+ sal_True );
+ else if( pProps[n].Name.equalsAsciiL( RTL_CONSTASCII_STRINGPARAM( "StyleName" ) ) )
+ pFont->m_aStyleName = getString(pProps[n].Value);
+ else if( pProps[n].Name.equalsAsciiL( RTL_CONSTASCII_STRINGPARAM( "Italic" ) ) )
+ pFont->m_eItalic = static_cast<FontItalic>(getInt(pProps[n].Value));
+ else if( pProps[n].Name.equalsAsciiL( RTL_CONSTASCII_STRINGPARAM( "Width" ) ) )
+ pFont->m_eWidth = static_cast<FontWidth>(getInt(pProps[n].Value));
+ else if( pProps[n].Name.equalsAsciiL( RTL_CONSTASCII_STRINGPARAM( "Weight" ) ) )
+ pFont->m_eWeight = static_cast<FontWeight>(getInt(pProps[n].Value));
+ else if( pProps[n].Name.equalsAsciiL( RTL_CONSTASCII_STRINGPARAM( "Pitch" ) ) )
+ pFont->m_ePitch = static_cast<FontPitch>(getInt(pProps[n].Value));
+ else if( pProps[n].Name.equalsAsciiL( RTL_CONSTASCII_STRINGPARAM( "Encoding" ) ) )
+ pFont->m_aEncoding = static_cast<rtl_TextEncoding>(getInt(pProps[n].Value));
+ else if( pProps[n].Name.equalsAsciiL( RTL_CONSTASCII_STRINGPARAM( "FontEncodingOnly" ) ) )
+ pFont->m_bFontEncodingOnly = getBool(pProps[n].Value);
+ else if( pProps[n].Name.equalsAsciiL( RTL_CONSTASCII_STRINGPARAM( "GlobalMetricXWidth" ) ) )
+ pFont->m_aGlobalMetricX.width = getInt(pProps[n].Value);
+ else if( pProps[n].Name.equalsAsciiL( RTL_CONSTASCII_STRINGPARAM( "GlobalMetricXHeight" ) ) )
+ pFont->m_aGlobalMetricX.height = getInt(pProps[n].Value);
+ else if( pProps[n].Name.equalsAsciiL( RTL_CONSTASCII_STRINGPARAM( "GlobalMetricYWidth" ) ) )
+ pFont->m_aGlobalMetricY.width = getInt(pProps[n].Value);
+ else if( pProps[n].Name.equalsAsciiL( RTL_CONSTASCII_STRINGPARAM( "GlobalMetricYHeight" ) ) )
+ pFont->m_aGlobalMetricY.height = getInt(pProps[n].Value);
+ else if( pProps[n].Name.equalsAsciiL( RTL_CONSTASCII_STRINGPARAM( "Ascend" ) ) )
+ pFont->m_nAscend = getInt(pProps[n].Value);
+ else if( pProps[n].Name.equalsAsciiL( RTL_CONSTASCII_STRINGPARAM( "Descend" ) ) )
+ pFont->m_nDescend = getInt(pProps[n].Value);
+ else if( pProps[n].Name.equalsAsciiL( RTL_CONSTASCII_STRINGPARAM( "Leading" ) ) )
+ pFont->m_nLeading = getInt(pProps[n].Value);
+ else if( pProps[n].Name.equalsAsciiL( RTL_CONSTASCII_STRINGPARAM( "XMin" ) ) )
+ pFont->m_nXMin = getInt(pProps[n].Value);
+ else if( pProps[n].Name.equalsAsciiL( RTL_CONSTASCII_STRINGPARAM( "YMin" ) ) )
+ pFont->m_nYMin = getInt(pProps[n].Value);
+ else if( pProps[n].Name.equalsAsciiL( RTL_CONSTASCII_STRINGPARAM( "XMax" ) ) )
+ pFont->m_nXMax = getInt(pProps[n].Value);
+ else if( pProps[n].Name.equalsAsciiL( RTL_CONSTASCII_STRINGPARAM( "YMax" ) ) )
+ pFont->m_nYMax = getInt(pProps[n].Value);
+ else if( pProps[n].Name.equalsAsciiL( RTL_CONSTASCII_STRINGPARAM( "VerticalSubstitutes" ) ) )
+ pFont->m_bHaveVerticalSubstitutedGlyphs = getBool(pProps[n].Value);
+ else if( pProps[n].Name.equalsAsciiL( RTL_CONSTASCII_STRINGPARAM( "EncodingVector" ) ) )
+ {
+ Sequence< NamedValue > aEncoding;
+ pProps[n].Value >>= aEncoding;
+ sal_Int32 nEnc = aEncoding.getLength();
+ const NamedValue* pEnc = aEncoding.getConstArray();
+ for( sal_Int32 m = 0; m < nEnc; m++ )
+ {
+ sal_Unicode cCode = *pEnc[m].Name.getStr();
+ sal_Int32 nGlyph = getInt(pEnc[m].Value);
+ pFont->m_aEncodingVector[ cCode ] = nGlyph;
+ }
+ }
+ else if( pProps[n].Name.equalsAsciiL( RTL_CONSTASCII_STRINGPARAM( "NonEncoded" ) ) )
+ {
+ Sequence< NamedValue > aEncoding;
+ pProps[n].Value >>= aEncoding;
+ sal_Int32 nEnc = aEncoding.getLength();
+ const NamedValue* pEnc = aEncoding.getConstArray();
+ for( sal_Int32 m = 0; m < nEnc; m++ )
+ {
+ sal_Unicode cCode = *pEnc[m].Name.getStr();
+ OUString aGlyphName( getString(pEnc[m].Value) );
+ pFont->m_aNonEncoded[ cCode ] = OUStringToOString(aGlyphName,RTL_TEXTENCODING_ASCII_US);
+ }
+ }
+ else if( pProps[n].Name.equalsAsciiL( RTL_CONSTASCII_STRINGPARAM( "CharacterMetrics" ) ) )
+ {
+ // fill pFont->m_pMetrics->m_aMetrics
+ // expect triples of int: int -> CharacterMetric.{ width, height }
+ Sequence< sal_Int32 > aSeq;
+ pProps[n].Value >>= aSeq;
+ sal_Int32 nInts = aSeq.getLength();
+ const sal_Int32* pInts = aSeq.getConstArray();
+ for( sal_Int32 m = 0; m < nInts; m+=3 )
+ {
+ pFont->m_pMetrics->m_aMetrics[ pInts[m] ].width = static_cast<short int>(pInts[m+1]);
+ pFont->m_pMetrics->m_aMetrics[ pInts[m] ].height = static_cast<short int>(pInts[m+2]);
+ }
+ }
+ else if( pProps[n].Name.equalsAsciiL( RTL_CONSTASCII_STRINGPARAM( "XKernPairs" ) ) )
+ {
+ // fill pFont->m_pMetrics->m_aXKernPairs
+ // expection name: <unicode1><unicode2> value: ((height << 16)| width)
+ Sequence< NamedValue > aKern;
+ pProps[n].Value >>= aKern;
+ KernPair aPair;
+ const NamedValue* pVals = aKern.getConstArray();
+ int nPairs = aKern.getLength();
+ for( int m = 0; m < nPairs; m++ )
+ {
+ if( pVals[m].Name.getLength() == 2 )
+ {
+ aPair.first = pVals[m].Name.getStr()[0];
+ aPair.second = pVals[m].Name.getStr()[1];
+ sal_Int32 nKern = getInt( pVals[m].Value );
+ aPair.kern_x = static_cast<short int>(nKern & 0xffff);
+ aPair.kern_y = static_cast<short int>((sal_uInt32(nKern) >> 16) & 0xffff);
+ pFont->m_pMetrics->m_aXKernPairs.push_back( aPair );
+ }
+ }
+ }
+ }
+ // sanity check
+ if( pFont->m_nPSName &&
+ pFont->m_nFamilyName &&
+ ! pFont->m_pMetrics->m_aMetrics.empty() )
+ {
+ m_aOverrideFonts.push_back( m_nNextFontID );
+ m_aFonts[ m_nNextFontID++ ] = pFont;
+ }
+ else
+ {
+ DBG_ASSERT( 0, "override font failed" );
+ delete pFont;
+ }
+ }
+
+ return true;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/vcl/generic/fontmanager/helper.cxx b/vcl/generic/fontmanager/helper.cxx
new file mode 100644
index 000000000000..50203276fedd
--- /dev/null
+++ b/vcl/generic/fontmanager/helper.cxx
@@ -0,0 +1,404 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*************************************************************************
+ *
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * Copyright 2000, 2010 Oracle and/or its affiliates.
+ *
+ * OpenOffice.org - a multi-platform office productivity suite
+ *
+ * This file is part of OpenOffice.org.
+ *
+ * OpenOffice.org is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License version 3
+ * only, as published by the Free Software Foundation.
+ *
+ * OpenOffice.org is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License version 3 for more details
+ * (a copy is included in the LICENSE file that accompanied this code).
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * version 3 along with OpenOffice.org. If not, see
+ * <http://www.openoffice.org/license.html>
+ * for a copy of the LGPLv3 License.
+ *
+ ************************************************************************/
+
+// MARKER(update_precomp.py): autogen include statement, do not remove
+#include "precompiled_vcl.hxx"
+
+#include <cstring>
+#include <sys/stat.h>
+#include <unistd.h>
+#include <limits.h>
+
+#include "vcl/helper.hxx"
+#include "vcl/ppdparser.hxx"
+#include "tools/string.hxx"
+#include "tools/urlobj.hxx"
+#include "osl/file.hxx"
+#include "osl/process.h"
+#include "rtl/bootstrap.hxx"
+
+using ::rtl::Bootstrap;
+using ::rtl::OUString;
+using ::rtl::OUStringBuffer;
+using ::rtl::OString;
+using ::rtl::OStringToOUString;
+using ::rtl::OUStringToOString;
+
+namespace psp {
+
+OUString getOfficePath( enum whichOfficePath ePath )
+{
+ static OUString aNetPath;
+ static OUString aUserPath;
+ static OUString aConfigPath;
+ static OUString aEmpty;
+ static bool bOnce = false;
+
+ if( ! bOnce )
+ {
+ bOnce = true;
+ OUString aIni;
+ Bootstrap::get( OUString( RTL_CONSTASCII_USTRINGPARAM( "BRAND_BASE_DIR" ) ), aNetPath );
+ aIni = aNetPath + OUString( RTL_CONSTASCII_USTRINGPARAM( "/program/" SAL_CONFIGFILE( "bootstrap" ) ) );
+ Bootstrap aBootstrap( aIni );
+ aBootstrap.getFrom( OUString( RTL_CONSTASCII_USTRINGPARAM( "CustomDataUrl" ) ), aConfigPath );
+ aBootstrap.getFrom( OUString( RTL_CONSTASCII_USTRINGPARAM( "UserInstallation" ) ), aUserPath );
+ OUString aUPath = aUserPath;
+
+ if( ! aConfigPath.compareToAscii( "file://", 7 ) )
+ {
+ OUString aSysPath;
+ if( osl_getSystemPathFromFileURL( aConfigPath.pData, &aSysPath.pData ) == osl_File_E_None )
+ aConfigPath = aSysPath;
+ }
+ if( ! aNetPath.compareToAscii( "file://", 7 ) )
+ {
+ OUString aSysPath;
+ if( osl_getSystemPathFromFileURL( aNetPath.pData, &aSysPath.pData ) == osl_File_E_None )
+ aNetPath = aSysPath;
+ }
+ if( ! aUserPath.compareToAscii( "file://", 7 ) )
+ {
+ OUString aSysPath;
+ if( osl_getSystemPathFromFileURL( aUserPath.pData, &aSysPath.pData ) == osl_File_E_None )
+ aUserPath = aSysPath;
+ }
+ // ensure user path exists
+ aUPath += OUString( RTL_CONSTASCII_USTRINGPARAM( "/user/psprint" ) );
+ #if OSL_DEBUG_LEVEL > 1
+ oslFileError eErr =
+ #endif
+ osl_createDirectoryPath( aUPath.pData, NULL, NULL );
+ #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 NetPath: return aNetPath;
+ 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::NetPath ) );
+ if( aPathBuffer.getLength() )
+ {
+ aPathBuffer.appendAscii( "/share/psprint" );
+ if( pSubDir )
+ {
+ aPathBuffer.append( sal_Unicode('/') );
+ aPathBuffer.appendAscii( pSubDir );
+ }
+ rPathList.push_back( aPathBuffer.makeStringAndClear() );
+ }
+ // append user path
+ aPathBuffer.append( getOfficePath( psp::UserPath ) );
+ if( aPathBuffer.getLength() )
+ {
+ aPathBuffer.appendAscii( "/user/psprint" );
+ if( pSubDir )
+ {
+ aPathBuffer.append( sal_Unicode('/') );
+ 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.getLength() )
+ 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( rtl::OStringToOUString( rtl::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.getLength() )
+ {
+ OUStringBuffer aPathBuffer( 512 );
+
+ OUString aConfigPath( getOfficePath( psp::ConfigPath ) );
+ OUString aNetPath( getOfficePath( psp::NetPath ) );
+ OUString aUserPath( getOfficePath( psp::UserPath ) );
+ if( aConfigPath.getLength() )
+ {
+ // #i53530# Path from CustomDataUrl will completely
+ // replace net and user paths if the path exists
+ aPathBuffer.append(aConfigPath);
+ aPathBuffer.appendAscii("/share/fonts");
+ // check existance of config path
+ struct stat aStat;
+ if( 0 != stat( OUStringToOString( aPathBuffer.makeStringAndClear(), osl_getThreadTextEncoding() ).getStr(), &aStat )
+ || ! S_ISDIR( aStat.st_mode ) )
+ aConfigPath = OUString();
+ else
+ {
+ aPathBuffer.append(aConfigPath);
+ aPathBuffer.appendAscii("/share/fonts");
+ }
+ }
+ if( aConfigPath.getLength() == 0 )
+ {
+ if( aNetPath.getLength() )
+ {
+ aPathBuffer.append( aNetPath );
+ aPathBuffer.appendAscii( "/share/fonts/truetype;");
+ aPathBuffer.append( aNetPath );
+ aPathBuffer.appendAscii( "/share/fonts/type1;" );
+ }
+ if( aUserPath.getLength() )
+ {
+ aPathBuffer.append( aUserPath );
+ aPathBuffer.appendAscii( "/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 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 nRead;
+ sal_uInt64 nOrgPos = 0;
+ rInFile.getPos( nOrgPos );
+
+ while( bSuccess && ! bEof )
+ {
+ // read leading bytes
+ bEof = ! rInFile.read( buffer, 6, nRead ) && nRead == 6 ? false : true;
+ 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 m_agic number
+ {
+ // this migt be a pfa font already
+ if( ! rInFile.read( buffer+6, 9, nRead ) && nRead == 9 &&
+ ( ! std::strncmp( (char*)buffer, "%!FontType1-", 12 ) ||
+ ! std::strncmp( (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 )
+ {
+ unsigned char* pBuffer = new unsigned char[ nBytesToRead+1 ];
+
+ if( ! rInFile.read( pBuffer, nBytesToRead, nRead ) && nRead == nBytesToRead )
+ {
+ if( nType == 1 )
+ {
+ // ascii data, convert dos lineends( \r\n ) and
+ // m_ac lineends( \r ) to \n
+ 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, nBytesToWrite, nRead ) || nRead != nBytesToWrite )
+ bSuccess = false;
+
+ delete [] pWriteBuffer;
+ }
+ 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;
+
+ delete [] pBuffer;
+ }
+ else if( nType == 3 )
+ bEof = true;
+ else
+ bSuccess = false;
+ }
+
+ return bSuccess;
+}
+
+void psp::normPath( OString& rPath )
+{
+ char buf[PATH_MAX];
+
+ ByteString aPath( rPath );
+
+ // 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
+ while( aPath.SearchAndReplace( "//", "/" ) != STRING_NOTFOUND )
+ ;
+ if( aPath.Len() > 0 && aPath.GetChar( aPath.Len()-1 ) == '/' )
+ aPath.Erase( aPath.Len()-1 );
+
+ if( ( aPath.Search( "./" ) != STRING_NOTFOUND ||
+ aPath.Search( "~" ) != STRING_NOTFOUND )
+ && realpath( aPath.GetBuffer(), 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/generic/fontmanager/parseAFM.cxx b/vcl/generic/fontmanager/parseAFM.cxx
new file mode 100644
index 000000000000..82d94f2518e0
--- /dev/null
+++ b/vcl/generic/fontmanager/parseAFM.cxx
@@ -0,0 +1,1492 @@
+/* -*- 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 toke() routine
+ * - use hash lookup instead of binary search
+ * in recognize() routine
+ */
+
+// MARKER(update_precomp.py): autogen include statement, do not remove
+#include "precompiled_vcl.hxx"
+
+/* parseAFM.c
+ *
+ * This file is used in conjuction 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 <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <errno.h>
+#include <sys/file.h>
+#include <sys/stat.h>
+#include <math.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 Space "space" /* used in string comparison to look for the width */
+/* of the space character to init the widths array */
+#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:
+ 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--;
+ }
+ unsigned int tell() const { return m_nPos; }
+ void seek( unsigned int nPos )
+ // NOTE: do not check input data since only results of tell()
+ // get seek()ed in this file
+ { m_nPos = nPos; }
+};
+
+FileInputStream::FileInputStream( const char* pFilename ) :
+ m_pMemory( NULL ),
+ m_nPos( 0 ),
+ m_nLen( 0 )
+{
+ struct stat aStat;
+ if( ! stat( pFilename, &aStat ) &&
+ S_ISREG( aStat.st_mode ) &&
+ aStat.st_size > 0
+ )
+ {
+ FILE* fp = fopen( pFilename, "r" );
+ if( fp )
+ {
+ m_pMemory = (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 ((char *)NULL);
+ 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.
+ */
+#include "afm_hash.hpp"
+
+static inline enum parseKey recognize( register 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, register GlobalFontInfo* gfi )
+{
+ bool cont = true, save = (gfi != NULL);
+ int error = ok;
+ register char *keyword;
+ int direction = -1;
+ int tokenlen;
+
+ while (cont)
+ {
+ keyword = token(fp, tokenlen);
+
+ if (keyword == NULL)
+ /* 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)) != NULL)
+ gfi->afmVersion = strdup( keyword );
+ break;
+ case COMMENT:
+ keyword = linetoken(fp);
+ break;
+ case FONTNAME:
+ if ((keyword = token(fp,tokenlen)) != NULL)
+ gfi->fontName = strdup( keyword );
+ break;
+ case ENCODINGSCHEME:
+ if ((keyword = token(fp,tokenlen)) != NULL)
+ gfi->encodingScheme = strdup( keyword );
+ break;
+ case FULLNAME:
+ if ((keyword = linetoken(fp)) != NULL)
+ gfi->fullName = strdup( keyword );
+ break;
+ case FAMILYNAME:
+ if ((keyword = linetoken(fp)) != NULL)
+ gfi->familyName = strdup( keyword );
+ break;
+ case WEIGHT:
+ if ((keyword = token(fp,tokenlen)) != NULL)
+ gfi->weight = strdup( keyword );
+ break;
+ case ITALICANGLE:
+ if ((keyword = token(fp,tokenlen)) != NULL)
+ gfi->italicAngle = StringToDouble( keyword );
+ break;
+ case ISFIXEDPITCH:
+ if ((keyword = token(fp,tokenlen)) != NULL)
+ {
+ if (MATCH(keyword, False))
+ gfi->isFixedPitch = 0;
+ else
+ gfi->isFixedPitch = 1;
+ }
+ break;
+ case UNDERLINEPOSITION:
+ if ((keyword = token(fp,tokenlen)) != NULL)
+ gfi->underlinePosition = atoi(keyword);
+ break;
+ case UNDERLINETHICKNESS:
+ if ((keyword = token(fp,tokenlen)) != NULL)
+ gfi->underlineThickness = atoi(keyword);
+ break;
+ case VERSION:
+ if ((keyword = token(fp,tokenlen)) != NULL)
+ gfi->version = strdup( keyword );
+ break;
+ case NOTICE:
+ if ((keyword = linetoken(fp)) != NULL)
+ gfi->notice = strdup( keyword );
+ break;
+ case FONTBBOX:
+ if ((keyword = token(fp,tokenlen)) != NULL)
+ gfi->fontBBox.llx = atoi(keyword);
+ if ((keyword = token(fp,tokenlen)) != NULL)
+ gfi->fontBBox.lly = atoi(keyword);
+ if ((keyword = token(fp,tokenlen)) != NULL)
+ gfi->fontBBox.urx = atoi(keyword);
+ if ((keyword = token(fp,tokenlen)) != NULL)
+ gfi->fontBBox.ury = atoi(keyword);
+ break;
+ case CAPHEIGHT:
+ if ((keyword = token(fp,tokenlen)) != NULL)
+ gfi->capHeight = atoi(keyword);
+ break;
+ case XHEIGHT:
+ if ((keyword = token(fp,tokenlen)) != NULL)
+ gfi->xHeight = atoi(keyword);
+ break;
+ case DESCENT:
+ if ((keyword = token(fp,tokenlen)) != NULL)
+ gfi->descender = -atoi(keyword);
+ break;
+ case DESCENDER:
+ if ((keyword = token(fp,tokenlen)) != NULL)
+ gfi->descender = atoi(keyword);
+ break;
+ case ASCENT:
+ case ASCENDER:
+ if ((keyword = token(fp,tokenlen)) != NULL)
+ gfi->ascender = atoi(keyword);
+ break;
+ case STARTCHARMETRICS:
+ cont = false;
+ break;
+ case ENDFONTMETRICS:
+ cont = false;
+ error = normalEOF;
+ break;
+ case EM:
+ // skip one token
+ keyword = token(fp,tokenlen);
+ break;
+ case STARTDIRECTION:
+ if ((keyword = token(fp,tokenlen)) != NULL)
+ direction = atoi(keyword);
+ break; /* ignore this for now */
+ case ENDDIRECTION:
+ break; /* ignore this for now */
+ case MAPPINGSCHEME:
+ keyword = token(fp,tokenlen);
+ break; /* ignore this for now */
+ case CHARACTERS:
+ keyword = token(fp,tokenlen);
+ break; /* ignore this for now */
+ case ISBASEFONT:
+ keyword = token(fp,tokenlen);
+ break; /* ignore this for now */
+ case CHARACTERSET:
+ keyword=token(fp,tokenlen); //ignore
+ break;
+ case STDHW:
+ keyword=token(fp,tokenlen); //ignore
+ break;
+ case STDVW:
+ keyword=token(fp,tokenlen); //ignore
+ break;
+ case CHARWIDTH:
+ if ((keyword = token(fp,tokenlen)) != NULL)
+ {
+ if (direction == 0)
+ gfi->charwidth = atoi(keyword);
+ }
+ keyword = token(fp,tokenlen);
+ /* ignore y-width for now */
+ break;
+ case METRICSSETS:
+ keyword = token(fp,tokenlen);
+ 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, register int* cwi)
+{
+ bool cont = true, save = (cwi != NULL);
+ int pos = 0, error = ok, tokenlen;
+ register char *keyword;
+
+ while (cont)
+ {
+ keyword = token(fp,tokenlen);
+ /* Have reached an early and unexpected EOF. */
+ /* Set flag and stop parsing */
+ if (keyword == NULL)
+ {
+ 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:
+ keyword = linetoken(fp);
+ break;
+ case CODE:
+ if ((keyword = token(fp,tokenlen)) != NULL)
+ pos = atoi(keyword);
+ break;
+ case XYWIDTH:
+ /* PROBLEM: Should be no Y-WIDTH when doing "quick & dirty" */
+ keyword = token(fp,tokenlen); keyword = token(fp,tokenlen); /* eat values */
+ error = parseError;
+ break;
+ case CODEHEX:
+ if ((keyword = token(fp,tokenlen)) != NULL)
+ sscanf(keyword, "<%x>", &pos);
+ break;
+ case X0WIDTH:
+ (void) token(fp,tokenlen);
+ break;
+ case XWIDTH:
+ if ((keyword = token(fp,tokenlen)) != NULL)
+ 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) */
+ keyword = token(fp,tokenlen);
+ break;
+ case CHARBBOX:
+ keyword = token(fp,tokenlen); keyword = token(fp,tokenlen);
+ keyword = token(fp,tokenlen); keyword = token(fp,tokenlen);
+ break;
+ case LIGATURE:
+ keyword = token(fp,tokenlen); keyword = token(fp,tokenlen);
+ break;
+ case VVECTOR:
+ keyword = token(fp,tokenlen);
+ keyword = token(fp,tokenlen);
+ break;
+ case NOPE:
+ default:
+ error = parseError;
+ break;
+ } /* switch */
+ } /* while */
+
+ return(error);
+
+} /* parseCharWidths */
+
+
+/*
+ * number of char metrics is almost allways 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 = NULL;
+
+ if ((pp_fontmetrics == NULL) || (*pp_fontmetrics == NULL))
+ return storageProblem;
+
+ if (*p_oldcount == n_newcount)
+ return ok;
+
+ p_tmpmetrics = (char*)realloc(*pp_fontmetrics, n_newcount * n_size);
+ if (p_tmpmetrics == NULL)
+ 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, register FontInfo* fi)
+{
+ bool cont = true, firstTime = true;
+ int error = ok, count = 0, tokenlen;
+ register CharMetricInfo *temp = fi->cmi;
+ register char *keyword;
+
+ while (cont)
+ {
+ keyword = token(fp,tokenlen);
+ if (keyword == NULL)
+ {
+ error = earlyEOF;
+ break; /* get out of loop */
+ }
+ switch(recognize(keyword,tokenlen))
+ {
+ case COMMENT:
+ keyword = linetoken(fp);
+ break;
+ case CODE:
+ if (!(count < fi->numOfChars))
+ {
+ reallocFontMetrics( (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)) != NULL)
+ 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( (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)) != NULL)
+ 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)) != NULL)
+ temp->wx = atoi(keyword);
+ if ((keyword = token(fp,tokenlen)) != NULL)
+ temp->wy = atoi(keyword);
+ break;
+ case X0WIDTH:
+ if ((keyword = token(fp,tokenlen)) != NULL)
+ temp->wx = atoi(keyword);
+ break;
+ case XWIDTH:
+ if ((keyword = token(fp,tokenlen)) != NULL)
+ temp->wx = atoi(keyword);
+ break;
+ case CHARNAME:
+ if ((keyword = token(fp,tokenlen)) != NULL)
+ temp->name = (char *)strdup(keyword);
+ break;
+ case CHARBBOX:
+ if ((keyword = token(fp,tokenlen)) != NULL)
+ temp->charBBox.llx = atoi(keyword);
+ if ((keyword = token(fp,tokenlen)) != NULL)
+ temp->charBBox.lly = atoi(keyword);
+ if ((keyword = token(fp,tokenlen)) != NULL)
+ temp->charBBox.urx = atoi(keyword);
+ if ((keyword = token(fp,tokenlen)) != NULL)
+ temp->charBBox.ury = atoi(keyword);
+ break;
+ case LIGATURE: {
+ Ligature **tail = &(temp->ligs);
+ Ligature *node = *tail;
+
+ if (*tail != NULL)
+ {
+ while (node->next != NULL)
+ node = node->next;
+ tail = &(node->next);
+ }
+
+ *tail = (Ligature *) calloc(1, sizeof(Ligature));
+ if ((keyword = token(fp,tokenlen)) != NULL)
+ (*tail)->succ = (char *)strdup(keyword);
+ if ((keyword = token(fp,tokenlen)) != NULL)
+ (*tail)->lig = (char *)strdup(keyword);
+ break; }
+ case ENDCHARMETRICS:
+ cont = false;;
+ break;
+ case ENDFONTMETRICS:
+ cont = false;
+ error = normalEOF;
+ break;
+ case VVECTOR:
+ keyword = token(fp,tokenlen);
+ keyword = token(fp,tokenlen);
+ break;
+ case NOPE:
+ default:
+ error = parseError;
+ break;
+ } /* switch */
+ } /* while */
+
+ if ((error == ok) && (count != fi->numOfChars))
+ error = reallocFontMetrics( (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, register FontInfo* fi)
+{
+ bool cont = true, save = (fi->tkd != NULL);
+ int pos = 0, error = ok, tcount = 0, tokenlen;
+ register char *keyword;
+
+ while (cont)
+ {
+ keyword = token(fp,tokenlen);
+
+ if (keyword == NULL)
+ {
+ 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:
+ keyword = linetoken(fp);
+ break;
+ case TRACKKERN:
+ if (!(tcount < fi->numOfTracks))
+ {
+ reallocFontMetrics( (void**)&(fi->tkd), &(fi->numOfTracks),
+ enlargeCount(fi->numOfTracks), sizeof(TrackKernData) );
+ }
+
+ if (tcount < fi->numOfTracks)
+ {
+ if ((keyword = token(fp,tokenlen)) != NULL)
+ fi->tkd[pos].degree = atoi(keyword);
+ if ((keyword = token(fp,tokenlen)) != NULL)
+ fi->tkd[pos].minPtSize = StringToDouble(keyword);
+ if ((keyword = token(fp,tokenlen)) != NULL)
+ fi->tkd[pos].minKernAmt = StringToDouble(keyword);
+ if ((keyword = token(fp,tokenlen)) != NULL)
+ fi->tkd[pos].maxPtSize = StringToDouble(keyword);
+ if ((keyword = token(fp,tokenlen)) != NULL)
+ 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( (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, register FontInfo* fi)
+{
+ bool cont = true, save = (fi->pkd != NULL);
+ int pos = 0, error = ok, pcount = 0, tokenlen;
+ register char *keyword;
+
+ while (cont)
+ {
+ keyword = token(fp,tokenlen);
+
+ if (keyword == NULL)
+ {
+ 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:
+ keyword = linetoken(fp);
+ break;
+ case KERNPAIR:
+ if (!(pcount < fi->numOfPairs))
+ {
+ reallocFontMetrics( (void**)&(fi->pkd), &(fi->numOfPairs),
+ enlargeCount(fi->numOfPairs), sizeof(PairKernData) );
+ }
+ if (pcount < fi->numOfPairs)
+ {
+ if ((keyword = token(fp,tokenlen)) != NULL)
+ fi->pkd[pos].name1 = strdup( keyword );
+ if ((keyword = token(fp,tokenlen)) != NULL)
+ fi->pkd[pos].name2 = strdup( keyword );
+ if ((keyword = token(fp,tokenlen)) != NULL)
+ fi->pkd[pos].xamt = atoi(keyword);
+ if ((keyword = token(fp,tokenlen)) != NULL)
+ fi->pkd[pos++].yamt = atoi(keyword);
+ pcount++;
+ }
+ else
+ {
+ error = parseError;
+ cont = false;
+ }
+ break;
+ case KERNPAIRXAMT:
+ if (!(pcount < fi->numOfPairs))
+ {
+ reallocFontMetrics( (void**)&(fi->pkd), &(fi->numOfPairs),
+ enlargeCount(fi->numOfPairs), sizeof(PairKernData) );
+ }
+ if (pcount < fi->numOfPairs)
+ {
+ if ((keyword = token(fp,tokenlen)) != NULL)
+ fi->pkd[pos].name1 = strdup( keyword );
+ if ((keyword = token(fp,tokenlen)) != NULL)
+ fi->pkd[pos].name2 = strdup( keyword );
+ if ((keyword = token(fp,tokenlen)) != NULL)
+ 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( (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, register FontInfo* fi)
+{
+ bool cont = true, firstTime = true, save = (fi->ccd != NULL);
+ int pos = 0, j = 0, error = ok, ccount = 0, pcount = 0, tokenlen;
+ register char *keyword;
+
+ while (cont)
+ {
+ keyword = token(fp,tokenlen);
+ if (keyword == NULL)
+ /* Have reached an early and unexpected EOF. */
+ /* Set flag and stop parsing */
+ {
+ error = earlyEOF;
+ break; /* get out of loop */
+ }
+ if (ccount > fi->numOfComps)
+ {
+ reallocFontMetrics( (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:
+ keyword = linetoken(fp);
+ break;
+ default:
+ break;
+ } /* switch */
+ else
+ /* otherwise parse entire Composite Character info section, */
+ /* saving the data */
+ switch(recognize(keyword,tokenlen))
+ {
+ case COMMENT:
+ keyword = linetoken(fp);
+ break;
+ case COMPCHAR:
+ if (!(ccount < fi->numOfComps))
+ {
+ reallocFontMetrics( (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)) != NULL)
+ fi->ccd[pos].numOfPieces = atoi(keyword);
+ fi->ccd[pos].pieces = (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)) != NULL)
+ fi->ccd[pos].pieces[j].pccName = strdup( keyword );
+ if ((keyword = token(fp,tokenlen)) != NULL)
+ fi->ccd[pos].pieces[j].deltax = atoi(keyword);
+ if ((keyword = token(fp,tokenlen)) != NULL)
+ 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( (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;
+
+ register char *keyword; /* used to store a token */
+
+
+ (*fi) = (FontInfo *) calloc(1, sizeof(FontInfo));
+ if ((*fi) == NULL) {error = storageProblem; return(error);}
+
+ if (flags & P_G)
+ {
+ (*fi)->gfi = (GlobalFontInfo *) calloc(1, sizeof(GlobalFontInfo));
+ if ((*fi)->gfi == NULL) {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)) != NULL)
+ (*fi)->numOfChars = atoi(keyword);
+ if (flags & (P_M ^ P_W))
+ {
+ (*fi)->cmi = (CharMetricInfo *)
+ calloc((*fi)->numOfChars, sizeof(CharMetricInfo));
+ if ((*fi)->cmi == NULL) {error = storageProblem; return(error);}
+ code = parseCharMetrics(&aFile, *fi);
+ }
+ else
+ {
+ if (flags & P_W)
+ {
+ (*fi)->cwi = (int *) calloc(256, sizeof(int));
+ if ((*fi)->cwi == NULL)
+ {
+ 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 == NULL)
+ /* 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 = (TrackKernData *)
+ calloc((*fi)->numOfTracks, sizeof(TrackKernData));
+ if ((*fi)->tkd == NULL)
+ {
+ 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 = (PairKernData *)
+ calloc((*fi)->numOfPairs, sizeof(PairKernData));
+ if ((*fi)->pkd == NULL)
+ {
+ 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 = (CompCharData *)
+ calloc((*fi)->numOfComps, sizeof(CompCharData));
+ if ((*fi)->ccd == NULL)
+ {
+ 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/generic/fontmanager/parseAFM.hxx b/vcl/generic/fontmanager/parseAFM.hxx
new file mode 100644
index 000000000000..0c390bd8cf8d
--- /dev/null
+++ b/vcl/generic/fontmanager/parseAFM.hxx
@@ -0,0 +1,337 @@
+/* -*- 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 conjuction 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 vaild 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
+ */
+
+#include <stdio.h>
+
+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
+ * vaild 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 */
+ w0x, /* key: W0X */
+ 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 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 sturcture
+ * (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
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/vcl/generic/glyphs/gcach_ftyp.cxx b/vcl/generic/glyphs/gcach_ftyp.cxx
new file mode 100644
index 000000000000..ffa4aa33b642
--- /dev/null
+++ b/vcl/generic/glyphs/gcach_ftyp.cxx
@@ -0,0 +1,2649 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*************************************************************************
+ *
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * Copyright 2000, 2010 Oracle and/or its affiliates.
+ *
+ * OpenOffice.org - a multi-platform office productivity suite
+ *
+ * This file is part of OpenOffice.org.
+ *
+ * OpenOffice.org is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License version 3
+ * only, as published by the Free Software Foundation.
+ *
+ * OpenOffice.org is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License version 3 for more details
+ * (a copy is included in the LICENSE file that accompanied this code).
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * version 3 along with OpenOffice.org. If not, see
+ * <http://www.openoffice.org/license.html>
+ * for a copy of the LGPLv3 License.
+ *
+ ************************************************************************/
+
+// MARKER(update_precomp.py): autogen include statement, do not remove
+#include "precompiled_vcl.hxx"
+
+#ifdef WNT
+#include <svsys.h>
+#undef CreateFont
+#endif
+
+#include "gcach_ftyp.hxx"
+
+#include "vcl/svapp.hxx"
+#include <outfont.hxx>
+#include <impfont.hxx>
+#ifdef ENABLE_GRAPHITE
+#include <graphite2/Font.h>
+#include <graphite_layout.hxx>
+#endif
+
+#include "tools/poly.hxx"
+#include "basegfx/matrix/b2dhommatrix.hxx"
+#include <basegfx/matrix/b2dhommatrixtools.hxx>
+#include "basegfx/polygon/b2dpolypolygon.hxx"
+
+#include "osl/file.hxx"
+#include "osl/thread.hxx"
+
+#include "sft.hxx"
+
+#include <ft2build.h>
+#include FT_FREETYPE_H
+#include FT_GLYPH_H
+#include FT_OUTLINE_H
+#include FT_TRUETYPE_TABLES_H
+#include FT_TRUETYPE_TAGS_H
+#include FT_TRUETYPE_IDS_H
+
+#ifndef FT_RENDER_MODE_MONO // happens in the MACOSX build
+ #define FT_RENDER_MODE_MONO ft_render_mode_mono
+#endif
+#include "rtl/instance.hxx"
+
+#ifndef FREETYPE_PATCH
+ // VERSION_MINOR in freetype.h is too coarse
+ // if patch-level is not available we need to fine-tune the version ourselves
+ #define FTVERSION 2005
+#else
+ #define FTVERSION (1000*FREETYPE_MAJOR + 100*FREETYPE_MINOR + FREETYPE_PATCH)
+#endif
+#if FTVERSION >= 2200
+typedef const FT_Vector* FT_Vector_CPtr;
+#else // FTVERSION < 2200
+typedef FT_Vector* FT_Vector_CPtr;
+#endif
+
+#include <vector>
+
+// TODO: move file mapping stuff to OSL
+#if defined(UNX)
+ // PORTERS: dlfcn is used for getting symbols from FT versions newer than baseline
+ #include <dlfcn.h>
+ #include <unistd.h>
+ #include <fcntl.h>
+ #include <sys/stat.h>
+ #include <sys/mman.h>
+ #include "vcl/fontmanager.hxx"
+#elif defined(WNT)
+ #include <io.h>
+ #define strncasecmp strnicmp
+#endif
+
+typedef const unsigned char* CPU8;
+inline sal_uInt16 NEXT_U16( CPU8& p ) { p+=2; return (p[-2]<<8)|p[-1]; }
+inline sal_Int16 NEXT_S16( CPU8& p ) { return (sal_Int16)NEXT_U16(p); }
+inline sal_uInt32 NEXT_U32( CPU8& p ) { p+=4; return (p[-4]<<24)|(p[-3]<<16)|(p[-2]<<8)|p[-1]; }
+//inline sal_Int32 NEXT_S32( U8*& p ) { return (sal_Int32)NEXT_U32(p); }
+
+// -----------------------------------------------------------------------
+
+// the gamma table makes artificial bold look better for CJK glyphs
+static unsigned char aGammaTable[257];
+
+static void InitGammaTable()
+{
+ static const int M_MAX = 255;
+ static const int M_X = 128;
+ static const int M_Y = 208;
+
+ int x, a;
+ for( x = 0; x < 256; x++)
+ {
+ if ( x <= M_X )
+ a = ( x * M_Y + M_X / 2) / M_X;
+ else
+ a = M_Y + ( ( x - M_X ) * ( M_MAX - M_Y ) +
+ ( M_MAX - M_X ) / 2 ) / ( M_MAX - M_X );
+
+ aGammaTable[x] = (unsigned char)a;
+ }
+}
+
+// -----------------------------------------------------------------------
+
+static FT_Library aLibFT = 0;
+
+// #110607# enable linking with old FT versions
+static int nFTVERSION = 0;
+static FT_Error (*pFTNewSize)(FT_Face,FT_Size*);
+static FT_Error (*pFTActivateSize)(FT_Size);
+static FT_Error (*pFTDoneSize)(FT_Size);
+FT_Error (*pFTEmbolden)(FT_GlyphSlot);
+FT_Error (*pFTOblique)(FT_GlyphSlot);
+static bool bEnableSizeFT = false;
+
+struct EqStr{ bool operator()(const char* a, const char* b) const { return !strcmp(a,b); } };
+struct HashStr { size_t operator()( const char* s ) const { return rtl_str_hashCode(s); } };
+typedef ::boost::unordered_map<const char*,boost::shared_ptr<FtFontFile>,HashStr, EqStr> FontFileList;
+namespace { struct vclFontFileList : public rtl::Static< FontFileList, vclFontFileList > {}; }
+
+// -----------------------------------------------------------------------
+
+// TODO: remove when the priorities are selected by UI
+// if (AH==0) => disable autohinting
+// if (AA==0) => disable antialiasing
+// if (EB==0) => disable embedded bitmaps
+// if (AA prio <= AH prio) => antialias + autohint
+// if (AH<AA) => do not autohint when antialiasing
+// if (EB<AH) => do not autohint for monochrome
+static int nDefaultPrioEmbedded = 2;
+static int nDefaultPrioAutoHint = 1;
+static int nDefaultPrioAntiAlias = 1;
+
+// =======================================================================
+// FreetypeManager
+// =======================================================================
+
+FtFontFile::FtFontFile( const ::rtl::OString& rNativeFileName )
+: maNativeFileName( rNativeFileName ),
+ mpFileMap( NULL ),
+ mnFileSize( 0 ),
+ mnRefCount( 0 ),
+ mnLangBoost( 0 )
+{
+ // boost font preference if UI language is mentioned in filename
+ int nPos = maNativeFileName.lastIndexOf( '_' );
+ if( nPos == -1 || maNativeFileName[nPos+1] == '.' )
+ mnLangBoost += 0x1000; // no langinfo => good
+ else
+ {
+ static const char* pLangBoost = NULL;
+ static bool bOnce = true;
+ if( bOnce )
+ {
+ bOnce = false;
+ LanguageType aLang = Application::GetSettings().GetUILanguage();
+ switch( aLang )
+ {
+ case LANGUAGE_JAPANESE:
+ pLangBoost = "jan";
+ break;
+ case LANGUAGE_CHINESE:
+ case LANGUAGE_CHINESE_SIMPLIFIED:
+ case LANGUAGE_CHINESE_SINGAPORE:
+ pLangBoost = "zhs";
+ break;
+ case LANGUAGE_CHINESE_TRADITIONAL:
+ case LANGUAGE_CHINESE_HONGKONG:
+ case LANGUAGE_CHINESE_MACAU:
+ pLangBoost = "zht";
+ break;
+ case LANGUAGE_KOREAN:
+ case LANGUAGE_KOREAN_JOHAB:
+ pLangBoost = "kor";
+ break;
+ }
+ }
+
+ if( pLangBoost && !strncasecmp( pLangBoost, &maNativeFileName.getStr()[nPos+1], 3 ) )
+ mnLangBoost += 0x2000; // matching langinfo => better
+ }
+}
+
+// -----------------------------------------------------------------------
+
+FtFontFile* FtFontFile::FindFontFile( const ::rtl::OString& rNativeFileName )
+{
+ // font file already known? (e.g. for ttc, synthetic, aliased fonts)
+ const char* pFileName = rNativeFileName.getStr();
+ FontFileList &rFontFileList = vclFontFileList::get();
+ FontFileList::const_iterator it = rFontFileList.find( pFileName );
+ if( it != rFontFileList.end() )
+ return it->second.get();
+
+ // no => create new one
+ FtFontFile* pFontFile = new FtFontFile( rNativeFileName );
+ pFileName = pFontFile->maNativeFileName.getStr();
+ rFontFileList[pFileName].reset(pFontFile);
+ return pFontFile;
+}
+
+// -----------------------------------------------------------------------
+
+bool FtFontFile::Map()
+{
+ if( mnRefCount++ <= 0 )
+ {
+ const char* pFileName = maNativeFileName.getStr();
+#if defined(UNX)
+ int nFile = open( pFileName, O_RDONLY );
+ if( nFile < 0 )
+ return false;
+
+ struct stat aStat;
+ fstat( nFile, &aStat );
+ mnFileSize = aStat.st_size;
+ mpFileMap = (const unsigned char*)
+ mmap( NULL, mnFileSize, PROT_READ, MAP_SHARED, nFile, 0 );
+ if( mpFileMap == MAP_FAILED )
+ mpFileMap = NULL;
+ close( nFile );
+#elif defined(WNT)
+ void* pFileDesc = ::CreateFile( pFileName, GENERIC_READ, FILE_SHARE_READ,
+ NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0 );
+ if( pFileDesc == INVALID_HANDLE_VALUE)
+ return false;
+
+ mnFileSize = ::GetFileSize( pFileDesc, NULL );
+ HANDLE aHandle = ::CreateFileMapping( pFileDesc, NULL, PAGE_READONLY, 0, mnFileSize, "TTF" );
+ mpFileMap = (const unsigned char*)::MapViewOfFile( aHandle, FILE_MAP_READ, 0, 0, mnFileSize );
+ ::CloseHandle( pFileDesc );
+#else
+ FILE* pFile = fopen( pFileName, "rb" );
+ if( !pFile )
+ return false;
+
+ struct stat aStat;
+ stat( pFileName, &aStat );
+ mnFileSize = aStat.st_size;
+ mpFileMap = new unsigned char[ mnFileSize ];
+ if( mnFileSize != fread( mpFileMap, 1, mnFileSize, pFile ) )
+ {
+ delete[] mpFileMap;
+ mpFileMap = NULL;
+ }
+ fclose( pFile );
+#endif
+ }
+
+ return (mpFileMap != NULL);
+}
+
+// -----------------------------------------------------------------------
+
+void FtFontFile::Unmap()
+{
+ if( (--mnRefCount > 0) || (mpFileMap == NULL) )
+ return;
+
+#if defined(UNX)
+ munmap( (char*)mpFileMap, mnFileSize );
+#elif defined(WNT)
+ UnmapViewOfFile( (LPCVOID)mpFileMap );
+#else
+ delete[] mpFileMap;
+#endif
+
+ mpFileMap = NULL;
+}
+
+#ifdef ENABLE_GRAPHITE
+// wrap FtFontInfo's table function
+const void * graphiteFontTable(const void* appFaceHandle, unsigned int name, size_t *len)
+{
+ const FtFontInfo * pFontInfo = reinterpret_cast<const FtFontInfo*>(appFaceHandle);
+ typedef union {
+ char m_c[5];
+ unsigned int m_id;
+ } TableId;
+ TableId tableId;
+ tableId.m_id = name;
+#ifndef WORDS_BIGENDIAN
+ TableId swapped;
+ swapped.m_c[3] = tableId.m_c[0];
+ swapped.m_c[2] = tableId.m_c[1];
+ swapped.m_c[1] = tableId.m_c[2];
+ swapped.m_c[0] = tableId.m_c[3];
+ tableId.m_id = swapped.m_id;
+#endif
+ tableId.m_c[4] = '\0';
+ sal_uLong nLength = 0;
+ const void * pTable = static_cast<const void*>(pFontInfo->GetTable(tableId.m_c, &nLength));
+ if (len) *len = static_cast<size_t>(nLength);
+ return pTable;
+}
+#endif
+
+// =======================================================================
+
+FtFontInfo::FtFontInfo( const ImplDevFontAttributes& rDevFontAttributes,
+ const ::rtl::OString& rNativeFileName, int nFaceNum, sal_IntPtr nFontId, int nSynthetic,
+ const ExtraKernInfo* pExtraKernInfo )
+:
+ maFaceFT( NULL ),
+ mpFontFile( FtFontFile::FindFontFile( rNativeFileName ) ),
+ mnFaceNum( nFaceNum ),
+ mnRefCount( 0 ),
+ mnSynthetic( nSynthetic ),
+#ifdef ENABLE_GRAPHITE
+ mbCheckedGraphite(false),
+ mpGraphiteFace(NULL),
+#endif
+ mnFontId( nFontId ),
+ maDevFontAttributes( rDevFontAttributes ),
+ mpFontCharMap( NULL ),
+ mpChar2Glyph( NULL ),
+ mpGlyph2Char( NULL ),
+ mpExtraKernInfo( pExtraKernInfo )
+{
+ // prefer font with low ID
+ maDevFontAttributes.mnQuality += 10000 - nFontId;
+ // prefer font with matching file names
+ maDevFontAttributes.mnQuality += mpFontFile->GetLangBoost();
+ // prefer font with more external info
+ if( pExtraKernInfo )
+ maDevFontAttributes.mnQuality += 100;
+}
+
+// -----------------------------------------------------------------------
+
+FtFontInfo::~FtFontInfo()
+{
+ if( mpFontCharMap )
+ mpFontCharMap->DeReference();
+ delete mpExtraKernInfo;
+ delete mpChar2Glyph;
+ delete mpGlyph2Char;
+#ifdef ENABLE_GRAPHITE
+ if (mpGraphiteFace)
+ delete mpGraphiteFace;
+#endif
+}
+
+void FtFontInfo::InitHashes() const
+{
+ // TODO: avoid pointers when empty stl::hash_* objects become cheap
+ mpChar2Glyph = new Int2IntMap();
+ mpGlyph2Char = new Int2IntMap();
+}
+
+// -----------------------------------------------------------------------
+
+FT_FaceRec_* FtFontInfo::GetFaceFT()
+{
+ // get faceFT once/multiple depending on availability of SizeFT APIs
+ if( (mnRefCount++ <= 0) || !bEnableSizeFT )
+ {
+ if( !mpFontFile->Map() )
+ return NULL;
+ FT_Error rc = FT_New_Memory_Face( aLibFT,
+ (FT_Byte*)mpFontFile->GetBuffer(),
+ mpFontFile->GetFileSize(), mnFaceNum, &maFaceFT );
+ if( (rc != FT_Err_Ok) || (maFaceFT->num_glyphs <= 0) )
+ maFaceFT = NULL;
+ }
+
+ return maFaceFT;
+}
+
+#ifdef ENABLE_GRAPHITE
+GraphiteFaceWrapper * FtFontInfo::GetGraphiteFace()
+{
+ if (mbCheckedGraphite)
+ return mpGraphiteFace;
+ // test for graphite here so that it is cached most efficiently
+ if (GetTable("Silf", 0))
+ {
+ int graphiteSegCacheSize = 10000;
+ static const char* pGraphiteCacheStr = getenv( "SAL_GRAPHITE_CACHE_SIZE" );
+ graphiteSegCacheSize = pGraphiteCacheStr ? (atoi(pGraphiteCacheStr)) : 0;
+ gr_face * pGraphiteFace;
+ if (graphiteSegCacheSize > 500)
+ pGraphiteFace = gr_make_face_with_seg_cache(this, graphiteFontTable, graphiteSegCacheSize, gr_face_cacheCmap);
+ else
+ pGraphiteFace = gr_make_face(this, graphiteFontTable, gr_face_cacheCmap);
+ if (pGraphiteFace)
+ mpGraphiteFace = new GraphiteFaceWrapper(pGraphiteFace);
+ }
+ mbCheckedGraphite = true;
+ return mpGraphiteFace;
+}
+#endif
+
+// -----------------------------------------------------------------------
+
+void FtFontInfo::ReleaseFaceFT( FT_FaceRec_* pFaceFT )
+{
+ // release last/each depending on SizeFT availability
+ if( (--mnRefCount <= 0) || !bEnableSizeFT )
+ {
+ FT_Done_Face( pFaceFT );
+ maFaceFT = NULL;
+ mpFontFile->Unmap();
+ }
+}
+
+// -----------------------------------------------------------------------
+
+bool FtFontInfo::HasExtraKerning() const
+{
+ if( !mpExtraKernInfo )
+ return false;
+ // TODO: how to enable the line below without getting #i29881# back?
+ // on the other hand being to optimistic doesn't cause problems
+ // return mpExtraKernInfo->HasKernPairs();
+ return true;
+}
+
+// -----------------------------------------------------------------------
+
+int FtFontInfo::GetExtraKernPairs( ImplKernPairData** ppKernPairs ) const
+{
+ if( !mpExtraKernInfo )
+ return 0;
+ return mpExtraKernInfo->GetUnscaledKernPairs( ppKernPairs );
+}
+
+// -----------------------------------------------------------------------
+
+int FtFontInfo::GetExtraGlyphKernValue( int nLeftGlyph, int nRightGlyph ) const
+{
+ if( !mpExtraKernInfo )
+ return 0;
+ if( !mpGlyph2Char )
+ return 0;
+ sal_Unicode cLeftChar = (*mpGlyph2Char)[ nLeftGlyph ];
+ sal_Unicode cRightChar = (*mpGlyph2Char)[ nRightGlyph ];
+ return mpExtraKernInfo->GetUnscaledKernValue( cLeftChar, cRightChar );
+}
+
+// -----------------------------------------------------------------------
+
+static unsigned GetUInt( const unsigned char* p ) { return((p[0]<<24)+(p[1]<<16)+(p[2]<<8)+p[3]);}
+static unsigned GetUShort( const unsigned char* p ){ return((p[0]<<8)+p[1]);}
+//static signed GetSShort( const unsigned char* p ){ return((short)((p[0]<<8)+p[1]));}
+
+// -----------------------------------------------------------------------
+
+const unsigned char* FtFontInfo::GetTable( const char* pTag, sal_uLong* pLength ) const
+{
+ const unsigned char* pBuffer = mpFontFile->GetBuffer();
+ int nFileSize = mpFontFile->GetFileSize();
+ if( !pBuffer || nFileSize<1024 )
+ return NULL;
+
+ // we currently only handle TTF and TTC headers
+ unsigned nFormat = GetUInt( pBuffer );
+ const unsigned char* p = pBuffer + 12;
+ if( nFormat == 0x74746366 ) // TTC_MAGIC
+ p += GetUInt( p + 4 * mnFaceNum );
+ else if( (nFormat!=0x00010000) && (nFormat!=0x74727565) ) // TTF_MAGIC and Apple TTF Magic
+ return NULL;
+
+ // walk table directory until match
+ int nTables = GetUShort( p - 8 );
+ if( nTables >= 64 ) // something fishy?
+ return NULL;
+ for( int i = 0; i < nTables; ++i, p+=16 )
+ {
+ if( p[0]==pTag[0] && p[1]==pTag[1] && p[2]==pTag[2] && p[3]==pTag[3] )
+ {
+ sal_uLong nLength = GetUInt( p + 12 );
+ if( pLength != NULL )
+ *pLength = nLength;
+ const unsigned char* pTable = pBuffer + GetUInt( p + 8 );
+ if( (pTable + nLength) <= (mpFontFile->GetBuffer() + nFileSize) )
+ return pTable;
+ }
+ }
+
+ return NULL;
+}
+
+// -----------------------------------------------------------------------
+
+void FtFontInfo::AnnounceFont( ImplDevFontList* pFontList )
+{
+ ImplFTSFontData* pFD = new ImplFTSFontData( this, maDevFontAttributes );
+ pFontList->Add( pFD );
+}
+
+// =======================================================================
+
+FreetypeManager::FreetypeManager()
+: mnMaxFontId( 0 ), mnNextFontId( 0x1000 )
+{
+ /*FT_Error rcFT =*/ FT_Init_FreeType( &aLibFT );
+
+#ifdef RTLD_DEFAULT // true if a good dlfcn.h header was included
+ // Get version of freetype library to enable workarounds.
+ // Freetype <= 2.0.9 does not have FT_Library_Version().
+ // Using dl_sym() instead of osl_getSymbol() because latter
+ // isn't designed to work with oslModule=NULL
+ void (*pFTLibraryVersion)(FT_Library library,
+ FT_Int *amajor, FT_Int *aminor, FT_Int *apatch);
+ pFTLibraryVersion = (void (*)(FT_Library library,
+ FT_Int *amajor, FT_Int *aminor, FT_Int *apatch))(sal_IntPtr)dlsym( RTLD_DEFAULT, "FT_Library_Version" );
+
+ pFTNewSize = (FT_Error(*)(FT_Face,FT_Size*))(sal_IntPtr)dlsym( RTLD_DEFAULT, "FT_New_Size" );
+ pFTActivateSize = (FT_Error(*)(FT_Size))(sal_IntPtr)dlsym( RTLD_DEFAULT, "FT_Activate_Size" );
+ pFTDoneSize = (FT_Error(*)(FT_Size))(sal_IntPtr)dlsym( RTLD_DEFAULT, "FT_Done_Size" );
+ pFTEmbolden = (FT_Error(*)(FT_GlyphSlot))(sal_IntPtr)dlsym( RTLD_DEFAULT, "FT_GlyphSlot_Embolden" );
+ pFTOblique = (FT_Error(*)(FT_GlyphSlot))(sal_IntPtr)dlsym( RTLD_DEFAULT, "FT_GlyphSlot_Oblique" );
+
+ bEnableSizeFT = (pFTNewSize!=NULL) && (pFTActivateSize!=NULL) && (pFTDoneSize!=NULL);
+
+ FT_Int nMajor = 0, nMinor = 0, nPatch = 0;
+ if( pFTLibraryVersion )
+ pFTLibraryVersion( aLibFT, &nMajor, &nMinor, &nPatch );
+ nFTVERSION = nMajor * 1000 + nMinor * 100 + nPatch;
+
+ // disable embedded bitmaps for Freetype-2.1.3 unless explicitly
+ // requested by env var below because it crashes StarOffice on RH9
+ // reason: double free in freetype's embedded bitmap handling
+ if( nFTVERSION == 2103 )
+ nDefaultPrioEmbedded = 0;
+ // disable artificial emboldening with the Freetype API for older versions
+ if( nFTVERSION < 2110 )
+ pFTEmbolden = NULL;
+
+#else // RTLD_DEFAULT
+ // assume systems where dlsym is not possible use supplied library
+ nFTVERSION = FTVERSION;
+#endif
+
+ // TODO: remove when the priorities are selected by UI
+ char* pEnv;
+ pEnv = ::getenv( "SAL_EMBEDDED_BITMAP_PRIORITY" );
+ if( pEnv )
+ nDefaultPrioEmbedded = pEnv[0] - '0';
+ pEnv = ::getenv( "SAL_ANTIALIASED_TEXT_PRIORITY" );
+ if( pEnv )
+ nDefaultPrioAntiAlias = pEnv[0] - '0';
+ pEnv = ::getenv( "SAL_AUTOHINTING_PRIORITY" );
+ if( pEnv )
+ nDefaultPrioAutoHint = pEnv[0] - '0';
+
+ InitGammaTable();
+ vclFontFileList::get();
+}
+
+// -----------------------------------------------------------------------
+
+FT_Face ServerFont::GetFtFace() const
+{
+ if( maSizeFT )
+ pFTActivateSize( maSizeFT );
+
+ return maFaceFT;
+}
+
+// -----------------------------------------------------------------------
+
+FreetypeManager::~FreetypeManager()
+{
+ ClearFontList();
+// This crashes on Solaris 10
+// TODO: check which versions have this problem
+//
+// FT_Error rcFT = FT_Done_FreeType( aLibFT );
+}
+
+// -----------------------------------------------------------------------
+
+void FreetypeManager::AddFontFile( const rtl::OString& rNormalizedName,
+ int nFaceNum, sal_IntPtr nFontId, const ImplDevFontAttributes& rDevFontAttr,
+ const ExtraKernInfo* pExtraKernInfo )
+{
+ if( !rNormalizedName.getLength() )
+ return;
+
+ if( maFontList.find( nFontId ) != maFontList.end() )
+ return;
+
+ FtFontInfo* pFontInfo = new FtFontInfo( rDevFontAttr,
+ rNormalizedName, nFaceNum, nFontId, 0, pExtraKernInfo );
+ maFontList[ nFontId ] = pFontInfo;
+ if( mnMaxFontId < nFontId )
+ mnMaxFontId = nFontId;
+}
+
+// -----------------------------------------------------------------------
+
+void FreetypeManager::AnnounceFonts( ImplDevFontList* pToAdd ) const
+{
+ for( FontList::const_iterator it = maFontList.begin(); it != maFontList.end(); ++it )
+ {
+ FtFontInfo* pFtFontInfo = it->second;
+ pFtFontInfo->AnnounceFont( pToAdd );
+ }
+}
+
+// -----------------------------------------------------------------------
+
+void FreetypeManager::ClearFontList( )
+{
+ for( FontList::iterator it = maFontList.begin(); it != maFontList.end(); ++it )
+ {
+ FtFontInfo* pFtFontInfo = it->second;
+ delete pFtFontInfo;
+ }
+ maFontList.clear();
+}
+
+// -----------------------------------------------------------------------
+
+ServerFont* FreetypeManager::CreateFont( const ImplFontSelectData& rFSD )
+{
+ FtFontInfo* pFontInfo = NULL;
+
+ // find a FontInfo matching to the font id
+ sal_IntPtr nFontId = reinterpret_cast<sal_IntPtr>( rFSD.mpFontData );
+ FontList::iterator it = maFontList.find( nFontId );
+ if( it != maFontList.end() )
+ pFontInfo = it->second;
+
+ if( !pFontInfo )
+ return NULL;
+
+ ServerFont* pNew = new ServerFont( rFSD, pFontInfo );
+
+ return pNew;
+}
+
+// =======================================================================
+
+ImplFTSFontData::ImplFTSFontData( FtFontInfo* pFI, const ImplDevFontAttributes& rDFA )
+: ImplFontData( rDFA, IFTSFONT_MAGIC ),
+ mpFtFontInfo( pFI )
+{
+ mbDevice = false;
+ mbOrientation = true;
+}
+
+// -----------------------------------------------------------------------
+
+ImplFontEntry* ImplFTSFontData::CreateFontInstance( ImplFontSelectData& rFSD ) const
+{
+ ImplServerFontEntry* pEntry = new ImplServerFontEntry( rFSD );
+ return pEntry;
+}
+
+// =======================================================================
+// ServerFont
+// =======================================================================
+
+ServerFont::ServerFont( const ImplFontSelectData& rFSD, FtFontInfo* pFI )
+: maGlyphList( 0),
+ maFontSelData(rFSD),
+ mnExtInfo(0),
+ mnRefCount(1),
+ mnBytesUsed( sizeof(ServerFont) ),
+ mpPrevGCFont( NULL ),
+ mpNextGCFont( NULL ),
+ mnCos( 0x10000),
+ mnSin( 0 ),
+ mnZWJ( 0 ),
+ mnZWNJ( 0 ),
+ mbCollectedZW( false ),
+ mnPrioEmbedded(nDefaultPrioEmbedded),
+ mnPrioAntiAlias(nDefaultPrioAntiAlias),
+ mnPrioAutoHint(nDefaultPrioAutoHint),
+ mpFontInfo( pFI ),
+ maFaceFT( NULL ),
+ maSizeFT( NULL ),
+ mbFaceOk( false ),
+ maRecodeConverter( NULL ),
+ mpLayoutEngine( NULL )
+{
+ // TODO: move update of mpFontEntry into FontEntry class when
+ // it becomes reponsible for the ServerFont instantiation
+ ((ImplServerFontEntry*)rFSD.mpFontEntry)->SetServerFont( this );
+
+ if( rFSD.mnOrientation != 0 )
+ {
+ const double dRad = rFSD.mnOrientation * ( F_2PI / 3600.0 );
+ mnCos = static_cast<long>( 0x10000 * cos( dRad ) + 0.5 );
+ mnSin = static_cast<long>( 0x10000 * sin( dRad ) + 0.5 );
+ }
+
+ maFaceFT = pFI->GetFaceFT();
+
+ if( !maFaceFT )
+ return;
+
+ // set the pixel size of the font instance
+ mnWidth = rFSD.mnWidth;
+ if( !mnWidth )
+ mnWidth = rFSD.mnHeight;
+ mfStretch = (double)mnWidth / rFSD.mnHeight;
+ // sanity check (e.g. #i66394#, #i66244#, #66537#)
+ if( (mnWidth < 0) || (mfStretch > +64.0) || (mfStretch < -64.0) )
+ return;
+
+ // perf: use maSizeFT if available
+ if( bEnableSizeFT )
+ {
+ pFTNewSize( maFaceFT, &maSizeFT );
+ pFTActivateSize( maSizeFT );
+ }
+ FT_Error rc = FT_Set_Pixel_Sizes( maFaceFT, mnWidth, rFSD.mnHeight );
+ if( rc != FT_Err_Ok )
+ return;
+
+ // prepare for font encodings other than unicode or symbol
+ FT_Encoding eEncoding = FT_ENCODING_UNICODE;
+ if( mpFontInfo->IsSymbolFont() )
+ {
+#if (FTVERSION < 2000)
+ eEncoding = FT_ENCODING_NONE;
+#else
+ if( FT_IS_SFNT( maFaceFT ) )
+ eEncoding = ft_encoding_symbol;
+ else
+ eEncoding = FT_ENCODING_ADOBE_CUSTOM; // freetype wants this for PS symbol fonts
+#endif
+ }
+ rc = FT_Select_Charmap( maFaceFT, eEncoding );
+ // no standard encoding applies => we need an encoding converter
+ if( rc != FT_Err_Ok )
+ {
+ rtl_TextEncoding eRecodeFrom = RTL_TEXTENCODING_UNICODE;
+ for( int i = maFaceFT->num_charmaps; --i >= 0; )
+ {
+ const FT_CharMap aCM = maFaceFT->charmaps[i];
+ if( aCM->platform_id == TT_PLATFORM_MICROSOFT )
+ {
+ switch( aCM->encoding_id )
+ {
+ case TT_MS_ID_SJIS:
+ eEncoding = FT_ENCODING_SJIS;
+ eRecodeFrom = RTL_TEXTENCODING_SHIFT_JIS;
+ break;
+ case TT_MS_ID_GB2312:
+ eEncoding = FT_ENCODING_GB2312;
+ eRecodeFrom = RTL_TEXTENCODING_GB_2312;
+ break;
+ case TT_MS_ID_BIG_5:
+ eEncoding = FT_ENCODING_BIG5;
+ eRecodeFrom = RTL_TEXTENCODING_BIG5;
+ break;
+ case TT_MS_ID_WANSUNG:
+ eEncoding = FT_ENCODING_WANSUNG;
+ eRecodeFrom = RTL_TEXTENCODING_MS_949;
+ break;
+ case TT_MS_ID_JOHAB:
+ eEncoding = FT_ENCODING_JOHAB;
+ eRecodeFrom = RTL_TEXTENCODING_MS_1361;
+ break;
+ }
+ }
+ else if( aCM->platform_id == TT_PLATFORM_MACINTOSH )
+ {
+ switch( aCM->encoding_id )
+ {
+ case TT_MAC_ID_ROMAN:
+ eEncoding = FT_ENCODING_APPLE_ROMAN;
+ eRecodeFrom = RTL_TEXTENCODING_UNICODE; // TODO: use better match
+ break;
+ // TODO: add other encodings when Mac-only
+ // non-unicode fonts show up
+ }
+ }
+ else if( aCM->platform_id == TT_PLATFORM_ADOBE )
+ {
+ switch( aCM->encoding_id )
+ {
+#ifdef TT_ADOBE_ID_LATIN1
+ case TT_ADOBE_ID_LATIN1: // better unicode than nothing
+ eEncoding = FT_ENCODING_ADOBE_LATIN_1;
+ eRecodeFrom = RTL_TEXTENCODING_ISO_8859_1;
+ break;
+#endif // TT_ADOBE_ID_LATIN1
+ case TT_ADOBE_ID_STANDARD: // better unicode than nothing
+ eEncoding = FT_ENCODING_ADOBE_STANDARD;
+ eRecodeFrom = RTL_TEXTENCODING_UNICODE; // TODO: use better match
+ break;
+ }
+ }
+ }
+
+ if( FT_Err_Ok != FT_Select_Charmap( maFaceFT, eEncoding ) )
+ return;
+
+ if( eRecodeFrom != RTL_TEXTENCODING_UNICODE )
+ maRecodeConverter = rtl_createUnicodeToTextConverter( eRecodeFrom );
+ }
+
+ mbFaceOk = true;
+
+ ApplyGSUB( rFSD );
+
+ // TODO: query GASP table for load flags
+ mnLoadFlags = FT_LOAD_DEFAULT;
+#if 1 // #i97326# cairo sometimes uses FT_Set_Transform() on our FT_FACE
+ // we are not using FT_Set_Transform() yet, so just ignore it for now
+ mnLoadFlags |= FT_LOAD_IGNORE_TRANSFORM;
+#endif
+
+ mbArtItalic = (rFSD.meItalic != ITALIC_NONE && pFI->GetFontAttributes().GetSlant() == ITALIC_NONE);
+ mbArtBold = (rFSD.meWeight > WEIGHT_MEDIUM && pFI->GetFontAttributes().GetWeight() <= WEIGHT_MEDIUM);
+ mbUseGamma = false;
+ if( mbArtBold )
+ {
+ //static const int TT_CODEPAGE_RANGE_874 = (1L << 16); // Thai
+ //static const int TT_CODEPAGE_RANGE_932 = (1L << 17); // JIS/Japan
+ //static const int TT_CODEPAGE_RANGE_936 = (1L << 18); // Chinese: Simplified
+ //static const int TT_CODEPAGE_RANGE_949 = (1L << 19); // Korean Wansung
+ //static const int TT_CODEPAGE_RANGE_950 = (1L << 20); // Chinese: Traditional
+ //static const int TT_CODEPAGE_RANGE_1361 = (1L << 21); // Korean Johab
+ static const int TT_CODEPAGE_RANGES1_CJKT = 0x3F0000; // all of the above
+ const TT_OS2* pOs2 = (const TT_OS2*)FT_Get_Sfnt_Table( maFaceFT, ft_sfnt_os2 );
+ if ((pOs2) && (pOs2->ulCodePageRange1 & TT_CODEPAGE_RANGES1_CJKT )
+ && rFSD.mnHeight < 20)
+ mbUseGamma = true;
+ }
+
+ if( ((mnCos != 0) && (mnSin != 0)) || (mnPrioEmbedded <= 0) )
+ mnLoadFlags |= FT_LOAD_NO_BITMAP;
+}
+
+void ServerFont::SetFontOptions( boost::shared_ptr<ImplFontOptions> pFontOptions)
+{
+ mpFontOptions = pFontOptions;
+
+ if (!mpFontOptions)
+ return;
+
+ FontAutoHint eHint = mpFontOptions->GetUseAutoHint();
+ if( eHint == AUTOHINT_DONTKNOW )
+ eHint = mbUseGamma ? AUTOHINT_TRUE : AUTOHINT_FALSE;
+
+ if( eHint == AUTOHINT_TRUE )
+ mnLoadFlags |= FT_LOAD_FORCE_AUTOHINT;
+
+ if( (mnSin != 0) && (mnCos != 0) ) // hinting for 0/90/180/270 degrees only
+ mnLoadFlags |= FT_LOAD_NO_HINTING;
+ mnLoadFlags |= FT_LOAD_IGNORE_GLOBAL_ADVANCE_WIDTH; //#88334#
+
+ if( mpFontOptions->DontUseAntiAlias() )
+ mnPrioAntiAlias = 0;
+ if( mpFontOptions->DontUseEmbeddedBitmaps() )
+ mnPrioEmbedded = 0;
+ if( mpFontOptions->DontUseHinting() )
+ mnPrioAutoHint = 0;
+
+#if (FTVERSION >= 2005) || defined(TT_CONFIG_OPTION_BYTECODE_INTERPRETER)
+ if( mnPrioAutoHint <= 0 )
+#endif
+ mnLoadFlags |= FT_LOAD_NO_HINTING;
+
+#if defined(FT_LOAD_TARGET_LIGHT) && defined(FT_LOAD_TARGET_NORMAL)
+ if( !(mnLoadFlags & FT_LOAD_NO_HINTING) && (nFTVERSION >= 2103))
+ {
+ mnLoadFlags |= FT_LOAD_TARGET_NORMAL;
+ switch( mpFontOptions->GetHintStyle() )
+ {
+ case HINT_NONE:
+ mnLoadFlags |= FT_LOAD_NO_HINTING;
+ break;
+ case HINT_SLIGHT:
+ mnLoadFlags |= FT_LOAD_TARGET_LIGHT;
+ break;
+ case HINT_MEDIUM:
+ break;
+ case HINT_FULL:
+ default:
+ break;
+ }
+ }
+#endif
+
+ if( mnPrioEmbedded <= 0 )
+ mnLoadFlags |= FT_LOAD_NO_BITMAP;
+}
+
+boost::shared_ptr<ImplFontOptions> ServerFont::GetFontOptions() const
+{
+ return mpFontOptions;
+}
+
+// -----------------------------------------------------------------------
+
+bool ServerFont::TestFont() const
+{
+ return mbFaceOk;
+}
+
+// -----------------------------------------------------------------------
+
+ServerFont::~ServerFont()
+{
+ if( mpLayoutEngine )
+ delete mpLayoutEngine;
+
+ if( maRecodeConverter )
+ rtl_destroyUnicodeToTextConverter( maRecodeConverter );
+
+ if( maSizeFT )
+ pFTDoneSize( maSizeFT );
+
+ mpFontInfo->ReleaseFaceFT( maFaceFT );
+
+ ReleaseFromGarbageCollect();
+}
+
+ // -----------------------------------------------------------------------
+
+int ServerFont::GetEmUnits() const
+{
+ return maFaceFT->units_per_EM;
+}
+
+// -----------------------------------------------------------------------
+
+void ServerFont::FetchFontMetric( ImplFontMetricData& rTo, long& rFactor ) const
+{
+ const int UNDETERMINED = 0xFEED;
+ static int nUseNewLineHeight = UNDETERMINED;
+ if (nUseNewLineHeight == UNDETERMINED)
+ {
+ osl::MutexGuard aGuard( osl::Mutex::getGlobalMutex());
+ if (nUseNewLineHeight == UNDETERMINED)
+ {
+ const char* pEnv = getenv( "SAL_USE_NEW_LINEHEIGHT");
+ nUseNewLineHeight = (pEnv ? atoi(pEnv) : 0);
+ }
+ }
+
+ static_cast<ImplFontAttributes&>(rTo) = mpFontInfo->GetFontAttributes();
+
+ rTo.mbScalableFont = true;
+ rTo.mbDevice = true;
+ rTo.mbKernableFont = (FT_HAS_KERNING( maFaceFT ) != 0) || mpFontInfo->HasExtraKerning();
+ rTo.mnOrientation = GetFontSelData().mnOrientation;
+
+ //Always consider [star]symbol as symbol fonts
+ if (
+ (rTo.GetFamilyName().EqualsAscii("OpenSymbol")) ||
+ (rTo.GetFamilyName().EqualsAscii("StarSymbol"))
+ )
+ {
+ rTo.mbSymbolFlag = true;
+ }
+
+ if( maSizeFT )
+ pFTActivateSize( maSizeFT );
+
+ rFactor = 0x100;
+
+ rTo.mnWidth = mnWidth;
+
+ const FT_Size_Metrics& rMetrics = maFaceFT->size->metrics;
+ rTo.mnAscent = (+rMetrics.ascender + 32) >> 6;
+ rTo.mnDescent = (-rMetrics.descender + 32) >> 6;
+ if (nUseNewLineHeight)
+ {
+ rTo.mnExtLeading = ((rMetrics.height + 32) >> 6) - (rTo.mnAscent + rTo.mnDescent);
+ rTo.mnIntLeading = (rTo.mnAscent + rTo.mnDescent) - ((maFaceFT->units_per_EM + 32) >> 6);
+ }
+ else
+ {
+ rTo.mnIntLeading = ((rMetrics.height + 32) >> 6) - (rTo.mnAscent + rTo.mnDescent);
+ }
+ rTo.mnSlant = 0;
+
+ const TT_OS2* pOS2 = (const TT_OS2*)FT_Get_Sfnt_Table( maFaceFT, ft_sfnt_os2 );
+ if( pOS2 && (pOS2->version != 0xFFFF) )
+ {
+ // map the panose info from the OS2 table to their VCL counterparts
+ switch( pOS2->panose[0] )
+ {
+ case 1: rTo.meFamily = FAMILY_ROMAN; break;
+ case 2: rTo.meFamily = FAMILY_SWISS; break;
+ case 3: rTo.meFamily = FAMILY_MODERN; break;
+ case 4: rTo.meFamily = FAMILY_SCRIPT; break;
+ case 5: rTo.meFamily = FAMILY_DECORATIVE; break;
+ // TODO: is it reasonable to override the attribute with DONTKNOW?
+ case 0: // fall through
+ default: rTo.meFamilyType = FAMILY_DONTKNOW; break;
+ }
+
+ switch( pOS2->panose[3] )
+ {
+ case 2: // fall through
+ case 3: // fall through
+ case 4: // fall through
+ case 5: // fall through
+ case 6: // fall through
+ case 7: // fall through
+ case 8: rTo.mePitch = PITCH_VARIABLE; break;
+ case 9: rTo.mePitch = PITCH_FIXED; break;
+ // TODO: is it reasonable to override the attribute with DONTKNOW?
+ case 0: // fall through
+ case 1: // fall through
+ default: rTo.mePitch = PITCH_DONTKNOW; break;
+ }
+
+ const double fScale = (double)GetFontSelData().mnHeight / maFaceFT->units_per_EM;
+ if (nUseNewLineHeight)
+ {
+ if( pOS2->sTypoAscender || pOS2->sTypoDescender )
+ {
+ rTo.mnAscent = (long)( pOS2->sTypoAscender * fScale + 0.5 );
+ rTo.mnDescent = (long)( -pOS2->sTypoDescender * fScale + 0.5 );
+ rTo.mnExtLeading = (long)( pOS2->sTypoLineGap * fScale + 0.5 );
+ rTo.mnIntLeading = (long)( (pOS2->sTypoAscender - pOS2->sTypoDescender - maFaceFT->units_per_EM) * fScale + 0.5 );
+ }
+ }
+ else
+ {
+ // #108862# sanity check, some fonts treat descent as signed !!!
+ int nDescent = pOS2->usWinDescent;
+ if( nDescent > 5*maFaceFT->units_per_EM )
+ nDescent = (short)pOS2->usWinDescent; // interpret it as signed!
+
+ if( pOS2->usWinAscent || pOS2->usWinDescent ) // #i30551#
+ {
+ rTo.mnAscent = (long)( +pOS2->usWinAscent * fScale + 0.5 );
+ rTo.mnDescent = (long)( +nDescent * fScale + 0.5 );
+ rTo.mnIntLeading = (long)( (+pOS2->usWinAscent + pOS2->usWinDescent - maFaceFT->units_per_EM) * fScale + 0.5 );
+ }
+ rTo.mnExtLeading = 0;
+ const TT_HoriHeader* pHHEA = (const TT_HoriHeader*)FT_Get_Sfnt_Table( maFaceFT, ft_sfnt_hhea );
+ if( (pHHEA != NULL) && (pOS2->usWinAscent || pOS2->usWinDescent) )
+ {
+ int nExtLeading = pHHEA->Line_Gap;
+ nExtLeading -= (pOS2->usWinAscent + pOS2->usWinDescent);
+ nExtLeading += (pHHEA->Ascender - pHHEA->Descender);
+ if( nExtLeading > 0 )
+ rTo.mnExtLeading = (long)(nExtLeading * fScale + 0.5);
+ }
+
+ // Check for CJK capabilities of the current font
+ // #107888# workaround for Asian...
+ // TODO: remove when ExtLeading fully implemented
+ sal_Bool bCJKCapable = ((pOS2->ulUnicodeRange2 & 0x2DF00000) != 0);
+
+ if ( bCJKCapable && (pOS2->usWinAscent || pOS2->usWinDescent) )
+ {
+ rTo.mnIntLeading += rTo.mnExtLeading;
+
+ // #109280# The line height for Asian fonts is too small.
+ // Therefore we add half of the external leading to the
+ // ascent, the other half is added to the descent.
+ const long nHalfTmpExtLeading = rTo.mnExtLeading / 2;
+ const long nOtherHalfTmpExtLeading = rTo.mnExtLeading - nHalfTmpExtLeading;
+
+ // #110641# external leading for Asian fonts.
+ // The factor 0.3 has been verified during experiments.
+ const long nCJKExtLeading = (long)(0.30 * (rTo.mnAscent + rTo.mnDescent));
+
+ if ( nCJKExtLeading > rTo.mnExtLeading )
+ rTo.mnExtLeading = nCJKExtLeading - rTo.mnExtLeading;
+ else
+ rTo.mnExtLeading = 0;
+
+ rTo.mnAscent += nHalfTmpExtLeading;
+ rTo.mnDescent += nOtherHalfTmpExtLeading;
+ }
+ }
+ }
+
+ // initialize kashida width
+ // TODO: what if there are different versions of this glyph available
+ rTo.mnMinKashida = rTo.mnAscent / 4; // a reasonable default
+ const int nKashidaGlyphId = GetRawGlyphIndex( 0x0640 );
+ if( nKashidaGlyphId )
+ {
+ GlyphData aGlyphData;
+ InitGlyphData( nKashidaGlyphId, aGlyphData );
+ rTo.mnMinKashida = aGlyphData.GetMetric().GetCharWidth();
+ }
+}
+
+// -----------------------------------------------------------------------
+
+static inline void SplitGlyphFlags( const ServerFont& rFont, int& nGlyphIndex, int& nGlyphFlags )
+{
+ nGlyphFlags = nGlyphIndex & GF_FLAGMASK;
+ nGlyphIndex &= GF_IDXMASK;
+
+ if( nGlyphIndex & GF_ISCHAR )
+ nGlyphIndex = rFont.GetRawGlyphIndex( nGlyphIndex );
+}
+
+// -----------------------------------------------------------------------
+
+int ServerFont::ApplyGlyphTransform( int nGlyphFlags,
+ FT_Glyph pGlyphFT, bool bForBitmapProcessing ) const
+{
+ int nAngle = GetFontSelData().mnOrientation;
+ // shortcut most common case
+ if( !nAngle && !nGlyphFlags )
+ return nAngle;
+
+ const FT_Size_Metrics& rMetrics = maFaceFT->size->metrics;
+ FT_Vector aVector;
+ FT_Matrix aMatrix;
+
+ bool bStretched = false;
+
+ switch( nGlyphFlags & GF_ROTMASK )
+ {
+ default: // straight
+ aVector.x = 0;
+ aVector.y = 0;
+ aMatrix.xx = +mnCos;
+ aMatrix.yy = +mnCos;
+ aMatrix.xy = -mnSin;
+ aMatrix.yx = +mnSin;
+ break;
+ case GF_ROTL: // left
+ nAngle += 900;
+ bStretched = (mfStretch != 1.0);
+ aVector.x = (FT_Pos)(+rMetrics.descender * mfStretch);
+ aVector.y = -rMetrics.ascender;
+ aMatrix.xx = (FT_Pos)(-mnSin / mfStretch);
+ aMatrix.yy = (FT_Pos)(-mnSin * mfStretch);
+ aMatrix.xy = (FT_Pos)(-mnCos * mfStretch);
+ aMatrix.yx = (FT_Pos)(+mnCos / mfStretch);
+ break;
+ case GF_ROTR: // right
+ nAngle -= 900;
+ bStretched = (mfStretch != 1.0);
+ aVector.x = -maFaceFT->glyph->metrics.horiAdvance;
+ aVector.x += (FT_Pos)(rMetrics.descender * mnSin/65536.0);
+ aVector.y = (FT_Pos)(-rMetrics.descender * mfStretch * mnCos/65536.0);
+ aMatrix.xx = (FT_Pos)(+mnSin / mfStretch);
+ aMatrix.yy = (FT_Pos)(+mnSin * mfStretch);
+ aMatrix.xy = (FT_Pos)(+mnCos * mfStretch);
+ aMatrix.yx = (FT_Pos)(-mnCos / mfStretch);
+ break;
+ }
+
+ while( nAngle < 0 )
+ nAngle += 3600;
+
+ if( pGlyphFT->format != FT_GLYPH_FORMAT_BITMAP )
+ {
+ FT_Glyph_Transform( pGlyphFT, NULL, &aVector );
+
+ // orthogonal transforms are better handled by bitmap operations
+ if( bStretched || (bForBitmapProcessing && (nAngle % 900) != 0) )
+ {
+ // workaround for compatibility with older FT versions
+ if( nFTVERSION < 2102 )
+ {
+ FT_Fixed t = aMatrix.xy;
+ aMatrix.xy = aMatrix.yx;
+ aMatrix.yx = t;
+ }
+
+ // apply non-orthogonal or stretch transformations
+ FT_Glyph_Transform( pGlyphFT, &aMatrix, NULL );
+ nAngle = 0;
+ }
+ }
+ else
+ {
+ // FT<=2005 ignores transforms for bitmaps, so do it manually
+ FT_BitmapGlyph pBmpGlyphFT = reinterpret_cast<FT_BitmapGlyph>(pGlyphFT);
+ pBmpGlyphFT->left += (aVector.x + 32) >> 6;
+ pBmpGlyphFT->top += (aVector.y + 32) >> 6;
+ }
+
+ return nAngle;
+}
+
+// -----------------------------------------------------------------------
+
+int ServerFont::GetRawGlyphIndex( sal_UCS4 aChar ) const
+{
+ if( mpFontInfo->IsSymbolFont() )
+ {
+ if( !FT_IS_SFNT( maFaceFT ) )
+ {
+ if( (aChar & 0xFF00) == 0xF000 )
+ aChar &= 0xFF; // PS font symbol mapping
+ else if( aChar > 0xFF )
+ return 0;
+ }
+ }
+
+ // if needed recode from unicode to font encoding
+ if( maRecodeConverter )
+ {
+ sal_Char aTempArray[8];
+ sal_Size nTempSize;
+ sal_uInt32 nCvtInfo;
+
+ // assume that modern UCS4 fonts have unicode CMAPs
+ // => no encoding remapping to unicode is needed
+ if( aChar > 0xFFFF )
+ return 0;
+
+ sal_Unicode aUCS2Char = static_cast<sal_Unicode>(aChar);
+ rtl_UnicodeToTextContext aContext = rtl_createUnicodeToTextContext( maRecodeConverter );
+ int nChars = rtl_convertUnicodeToText( maRecodeConverter, aContext,
+ &aUCS2Char, 1, aTempArray, sizeof(aTempArray),
+ RTL_UNICODETOTEXT_FLAGS_UNDEFINED_QUESTIONMARK
+ | RTL_UNICODETOTEXT_FLAGS_INVALID_QUESTIONMARK,
+ &nCvtInfo, &nTempSize );
+ rtl_destroyUnicodeToTextContext( maRecodeConverter, aContext );
+
+ aChar = 0;
+ for( int i = 0; i < nChars; ++i )
+ aChar = aChar*256 + (aTempArray[i] & 0xFF);
+ }
+
+ // cache glyph indexes in font info to share between different sizes
+ int nGlyphIndex = mpFontInfo->GetGlyphIndex( aChar );
+ if( nGlyphIndex < 0 )
+ {
+ nGlyphIndex = FT_Get_Char_Index( maFaceFT, aChar );
+ if( !nGlyphIndex)
+ {
+ // check if symbol aliasing helps
+ if( (aChar <= 0x00FF) && mpFontInfo->IsSymbolFont() )
+ nGlyphIndex = FT_Get_Char_Index( maFaceFT, aChar | 0xF000 );
+ }
+ mpFontInfo->CacheGlyphIndex( aChar, nGlyphIndex );
+ }
+
+ return nGlyphIndex;
+}
+
+// -----------------------------------------------------------------------
+
+int ServerFont::FixupGlyphIndex( int nGlyphIndex, sal_UCS4 aChar ) const
+{
+ int nGlyphFlags = GF_NONE;
+
+ // do glyph substitution if necessary
+ // CJK vertical writing needs special treatment
+ if( GetFontSelData().mbVertical )
+ {
+ // TODO: rethink when GSUB is used for non-vertical case
+ GlyphSubstitution::const_iterator it = maGlyphSubstitution.find( nGlyphIndex );
+ if( it == maGlyphSubstitution.end() )
+ {
+ int nTemp = GetVerticalChar( aChar );
+ if( nTemp ) // is substitution possible
+ nTemp = GetRawGlyphIndex( nTemp );
+ if( nTemp ) // substitute manually if sensible
+ nGlyphIndex = nTemp | (GF_GSUB | GF_ROTL);
+ else
+ nGlyphFlags |= GetVerticalFlags( aChar );
+ }
+ else
+ {
+ // for vertical GSUB also compensate for nOrientation=2700
+ nGlyphIndex = (*it).second;
+ nGlyphFlags |= GF_GSUB | GF_ROTL;
+ }
+ }
+
+ if( nGlyphIndex != 0 )
+ nGlyphIndex |= nGlyphFlags;
+
+ return nGlyphIndex;
+}
+
+
+// -----------------------------------------------------------------------
+
+int ServerFont::GetGlyphIndex( sal_UCS4 aChar ) const
+{
+ int nGlyphIndex = GetRawGlyphIndex( aChar );
+ nGlyphIndex = FixupGlyphIndex( nGlyphIndex, aChar );
+ return nGlyphIndex;
+}
+
+// -----------------------------------------------------------------------
+
+static int lcl_GetCharWidth( FT_FaceRec_* pFaceFT, double fStretch, int nGlyphFlags )
+{
+ int nCharWidth = pFaceFT->glyph->metrics.horiAdvance;
+
+ if( nGlyphFlags & GF_ROTMASK ) // for bVertical rotated glyphs
+ {
+ const FT_Size_Metrics& rMetrics = pFaceFT->size->metrics;
+#if (FTVERSION < 2000)
+ nCharWidth = (int)((rMetrics.height - rMetrics.descender) * fStretch);
+#else
+ nCharWidth = (int)((rMetrics.height + rMetrics.descender) * fStretch);
+#endif
+ }
+
+ return (nCharWidth + 32) >> 6;
+}
+
+// -----------------------------------------------------------------------
+
+void ServerFont::InitGlyphData( int nGlyphIndex, GlyphData& rGD ) const
+{
+ if( maSizeFT )
+ pFTActivateSize( maSizeFT );
+
+ int nGlyphFlags;
+ SplitGlyphFlags( *this, nGlyphIndex, nGlyphFlags );
+
+ int nLoadFlags = mnLoadFlags;
+
+// if( mbArtItalic )
+// nLoadFlags |= FT_LOAD_NO_BITMAP;
+
+ FT_Error rc = -1;
+#if (FTVERSION <= 2008)
+ // #88364# freetype<=2005 prefers autohinting to embedded bitmaps
+ // => first we have to try without hinting
+ if( (nLoadFlags & (FT_LOAD_NO_HINTING | FT_LOAD_NO_BITMAP)) == 0 )
+ {
+ rc = FT_Load_Glyph( maFaceFT, nGlyphIndex, nLoadFlags|FT_LOAD_NO_HINTING );
+ if( (rc==FT_Err_Ok) && (maFaceFT->glyph->format!=FT_GLYPH_FORMAT_BITMAP) )
+ rc = -1; // mark as "loading embedded bitmap" was unsuccessful
+ nLoadFlags |= FT_LOAD_NO_BITMAP;
+ }
+
+ if( rc != FT_Err_Ok )
+#endif
+ rc = FT_Load_Glyph( maFaceFT, nGlyphIndex, nLoadFlags );
+
+ if( rc != FT_Err_Ok )
+ {
+ // we get here e.g. when a PS font lacks the default glyph
+ rGD.SetCharWidth( 0 );
+ rGD.SetDelta( 0, 0 );
+ rGD.SetOffset( 0, 0 );
+ rGD.SetSize( Size( 0, 0 ) );
+ return;
+ }
+
+ const bool bOriginallyZeroWidth = (maFaceFT->glyph->metrics.horiAdvance == 0);
+ if( mbArtBold && pFTEmbolden )
+ (*pFTEmbolden)( maFaceFT->glyph );
+
+ const int nCharWidth = bOriginallyZeroWidth ? 0 : lcl_GetCharWidth( maFaceFT, mfStretch, nGlyphFlags );
+ rGD.SetCharWidth( nCharWidth );
+
+ FT_Glyph pGlyphFT;
+ rc = FT_Get_Glyph( maFaceFT->glyph, &pGlyphFT );
+
+ ApplyGlyphTransform( nGlyphFlags, pGlyphFT, false );
+ if( mbArtBold && pFTEmbolden && (nFTVERSION < 2200) ) // #i71094# workaround staircase bug
+ pGlyphFT->advance.y = 0;
+ rGD.SetDelta( (pGlyphFT->advance.x + 0x8000) >> 16, -((pGlyphFT->advance.y + 0x8000) >> 16) );
+
+ FT_BBox aBbox;
+ FT_Glyph_Get_CBox( pGlyphFT, FT_GLYPH_BBOX_PIXELS, &aBbox );
+ if( aBbox.yMin > aBbox.yMax ) // circumvent freetype bug
+ {
+ int t=aBbox.yMin; aBbox.yMin=aBbox.yMax, aBbox.yMax=t;
+ }
+
+ rGD.SetOffset( aBbox.xMin, -aBbox.yMax );
+ rGD.SetSize( Size( (aBbox.xMax-aBbox.xMin+1), (aBbox.yMax-aBbox.yMin) ) );
+
+ FT_Done_Glyph( pGlyphFT );
+}
+
+// -----------------------------------------------------------------------
+
+bool ServerFont::GetAntialiasAdvice( void ) const
+{
+ if( GetFontSelData().mbNonAntialiased || (mnPrioAntiAlias<=0) )
+ return false;
+ bool bAdviseAA = true;
+ // TODO: also use GASP info
+ return bAdviseAA;
+}
+
+// -----------------------------------------------------------------------
+
+bool ServerFont::GetGlyphBitmap1( int nGlyphIndex, RawBitmap& rRawBitmap ) const
+{
+ if( maSizeFT )
+ pFTActivateSize( maSizeFT );
+
+ int nGlyphFlags;
+ SplitGlyphFlags( *this, nGlyphIndex, nGlyphFlags );
+
+ FT_Int nLoadFlags = mnLoadFlags;
+ // #i70930# force mono-hinting for monochrome text
+ if( nFTVERSION >= 2110 ) //#i71947# unless it looks worse
+ {
+ nLoadFlags &= ~0xF0000;
+ nLoadFlags |= FT_LOAD_TARGET_MONO;
+ }
+
+ if( mbArtItalic )
+ nLoadFlags |= FT_LOAD_NO_BITMAP;
+
+#if (FTVERSION >= 2002)
+ // for 0/90/180/270 degree fonts enable hinting even if not advisable
+ // non-hinted and non-antialiased bitmaps just look too ugly
+ if( (mnCos==0 || mnSin==0) && (mnPrioAutoHint > 0) )
+ nLoadFlags &= ~FT_LOAD_NO_HINTING;
+#endif
+
+ if( mnPrioEmbedded <= mnPrioAutoHint )
+ nLoadFlags |= FT_LOAD_NO_BITMAP;
+
+ FT_Error rc = -1;
+#if (FTVERSION <= 2008)
+ // #88364# freetype<=2005 prefers autohinting to embedded bitmaps
+ // => first we have to try without hinting
+ if( (nLoadFlags & (FT_LOAD_NO_HINTING | FT_LOAD_NO_BITMAP)) == 0 )
+ {
+ rc = FT_Load_Glyph( maFaceFT, nGlyphIndex, nLoadFlags|FT_LOAD_NO_HINTING );
+ if( (rc==FT_Err_Ok) && (maFaceFT->glyph->format != FT_GLYPH_FORMAT_BITMAP) )
+ rc = -1; // mark as "loading embedded bitmap" was unsuccessful
+ nLoadFlags |= FT_LOAD_NO_BITMAP;
+ }
+
+ if( rc != FT_Err_Ok )
+#endif
+ rc = FT_Load_Glyph( maFaceFT, nGlyphIndex, nLoadFlags );
+ if( rc != FT_Err_Ok )
+ return false;
+
+ if( mbArtBold && pFTEmbolden )
+ (*pFTEmbolden)( maFaceFT->glyph );
+
+ FT_Glyph pGlyphFT;
+ rc = FT_Get_Glyph( maFaceFT->glyph, &pGlyphFT );
+ if( rc != FT_Err_Ok )
+ return false;
+
+ int nAngle = ApplyGlyphTransform( nGlyphFlags, pGlyphFT, true );
+
+ if( mbArtItalic )
+ {
+ FT_Matrix aMatrix;
+ aMatrix.xx = aMatrix.yy = 0x10000L;
+ if( nFTVERSION >= 2102 ) // Freetype 2.1.2 API swapped xy with yx
+ aMatrix.xy = 0x6000L, aMatrix.yx = 0;
+ else
+ aMatrix.yx = 0x6000L, aMatrix.xy = 0;
+ FT_Glyph_Transform( pGlyphFT, &aMatrix, NULL );
+ }
+
+ // Check for zero area bounding boxes as this crashes some versions of FT.
+ // This also provides a handy short cut as much of the code following
+ // becomes an expensive nop when a glyph covers no pixels.
+ FT_BBox cbox;
+ FT_Glyph_Get_CBox(pGlyphFT, ft_glyph_bbox_unscaled, &cbox);
+
+ if( (cbox.xMax - cbox.xMin) == 0 || (cbox.yMax - cbox.yMin == 0) )
+ {
+ nAngle = 0;
+ memset(&rRawBitmap, 0, sizeof rRawBitmap);
+ FT_Done_Glyph( pGlyphFT );
+ return true;
+ }
+
+ if( pGlyphFT->format != FT_GLYPH_FORMAT_BITMAP )
+ {
+ if( pGlyphFT->format == FT_GLYPH_FORMAT_OUTLINE )
+ ((FT_OutlineGlyphRec*)pGlyphFT)->outline.flags |= FT_OUTLINE_HIGH_PRECISION;
+ // #i15743# freetype API 2.1.3 changed the FT_RENDER_MODE_MONO constant
+ FT_Render_Mode nRenderMode = (FT_Render_Mode)((nFTVERSION<2103) ? 1 : FT_RENDER_MODE_MONO);
+
+ rc = FT_Glyph_To_Bitmap( &pGlyphFT, nRenderMode, NULL, sal_True );
+ if( rc != FT_Err_Ok )
+ {
+ FT_Done_Glyph( pGlyphFT );
+ return false;
+ }
+ }
+
+ const FT_BitmapGlyph pBmpGlyphFT = reinterpret_cast<const FT_BitmapGlyph>(pGlyphFT);
+ // NOTE: autohinting in FT<=2.0.2 miscalculates the offsets below by +-1
+ rRawBitmap.mnXOffset = +pBmpGlyphFT->left;
+ rRawBitmap.mnYOffset = -pBmpGlyphFT->top;
+
+ const FT_Bitmap& rBitmapFT = pBmpGlyphFT->bitmap;
+ rRawBitmap.mnHeight = rBitmapFT.rows;
+ rRawBitmap.mnBitCount = 1;
+ if( mbArtBold && !pFTEmbolden )
+ {
+ rRawBitmap.mnWidth = rBitmapFT.width + 1;
+ int nLineBytes = (rRawBitmap.mnWidth + 7) >> 3;
+ rRawBitmap.mnScanlineSize = (nLineBytes > rBitmapFT.pitch) ? nLineBytes : rBitmapFT.pitch;
+ }
+ else
+ {
+ rRawBitmap.mnWidth = rBitmapFT.width;
+ rRawBitmap.mnScanlineSize = rBitmapFT.pitch;
+ }
+
+ const sal_uLong nNeededSize = rRawBitmap.mnScanlineSize * rRawBitmap.mnHeight;
+
+ if( rRawBitmap.mnAllocated < nNeededSize )
+ {
+ delete[] rRawBitmap.mpBits;
+ rRawBitmap.mnAllocated = 2*nNeededSize;
+ rRawBitmap.mpBits = new unsigned char[ rRawBitmap.mnAllocated ];
+ }
+
+ if( !mbArtBold || pFTEmbolden )
+ {
+ memcpy( rRawBitmap.mpBits, rBitmapFT.buffer, nNeededSize );
+ }
+ else
+ {
+ memset( rRawBitmap.mpBits, 0, nNeededSize );
+ const unsigned char* pSrcLine = rBitmapFT.buffer;
+ unsigned char* pDstLine = rRawBitmap.mpBits;
+ for( int h = rRawBitmap.mnHeight; --h >= 0; )
+ {
+ memcpy( pDstLine, pSrcLine, rBitmapFT.pitch );
+ pDstLine += rRawBitmap.mnScanlineSize;
+ pSrcLine += rBitmapFT.pitch;
+ }
+
+ unsigned char* p = rRawBitmap.mpBits;
+ for( sal_uLong y=0; y < rRawBitmap.mnHeight; y++ )
+ {
+ unsigned char nLastByte = 0;
+ for( sal_uLong x=0; x < rRawBitmap.mnScanlineSize; x++ )
+ {
+ unsigned char nTmp = p[x] << 7;
+ p[x] |= (p[x] >> 1) | nLastByte;
+ nLastByte = nTmp;
+ }
+ p += rRawBitmap.mnScanlineSize;
+ }
+ }
+
+ FT_Done_Glyph( pGlyphFT );
+
+ // special case for 0/90/180/270 degree orientation
+ switch( nAngle )
+ {
+ case -900:
+ case +900:
+ case +1800:
+ case +2700:
+ rRawBitmap.Rotate( nAngle );
+ break;
+ }
+
+ return true;
+}
+
+// -----------------------------------------------------------------------
+
+bool ServerFont::GetGlyphBitmap8( int nGlyphIndex, RawBitmap& rRawBitmap ) const
+{
+ if( maSizeFT )
+ pFTActivateSize( maSizeFT );
+
+ int nGlyphFlags;
+ SplitGlyphFlags( *this, nGlyphIndex, nGlyphFlags );
+
+ FT_Int nLoadFlags = mnLoadFlags;
+
+ if( mbArtItalic )
+ nLoadFlags |= FT_LOAD_NO_BITMAP;
+
+#if (FTVERSION <= 2004) && !defined(TT_CONFIG_OPTION_BYTECODE_INTERPRETER)
+ // autohinting in FT<=2.0.4 makes antialiased glyphs look worse
+ nLoadFlags |= FT_LOAD_NO_HINTING;
+#else
+ if( (nGlyphFlags & GF_UNHINTED) || (mnPrioAutoHint < mnPrioAntiAlias) )
+ nLoadFlags |= FT_LOAD_NO_HINTING;
+#endif
+
+ if( mnPrioEmbedded <= mnPrioAntiAlias )
+ nLoadFlags |= FT_LOAD_NO_BITMAP;
+
+ FT_Error rc = -1;
+#if (FTVERSION <= 2008)
+ // #88364# freetype<=2005 prefers autohinting to embedded bitmaps
+ // => first we have to try without hinting
+ if( (nLoadFlags & (FT_LOAD_NO_HINTING | FT_LOAD_NO_BITMAP)) == 0 )
+ {
+ rc = FT_Load_Glyph( maFaceFT, nGlyphIndex, nLoadFlags|FT_LOAD_NO_HINTING );
+ if( (rc==FT_Err_Ok) && (maFaceFT->glyph->format != FT_GLYPH_FORMAT_BITMAP) )
+ rc = -1; // mark as "loading embedded bitmap" was unsuccessful
+ nLoadFlags |= FT_LOAD_NO_BITMAP;
+ }
+
+ if( rc != FT_Err_Ok )
+#endif
+ rc = FT_Load_Glyph( maFaceFT, nGlyphIndex, nLoadFlags );
+
+ if( rc != FT_Err_Ok )
+ return false;
+
+ if( mbArtBold && pFTEmbolden )
+ (*pFTEmbolden)( maFaceFT->glyph );
+
+ FT_Glyph pGlyphFT;
+ rc = FT_Get_Glyph( maFaceFT->glyph, &pGlyphFT );
+ if( rc != FT_Err_Ok )
+ return false;
+
+ int nAngle = ApplyGlyphTransform( nGlyphFlags, pGlyphFT, true );
+
+ if( mbArtItalic )
+ {
+ FT_Matrix aMatrix;
+ aMatrix.xx = aMatrix.yy = 0x10000L;
+ if( nFTVERSION >= 2102 ) // Freetype 2.1.2 API swapped xy with yx
+ aMatrix.xy = 0x6000L, aMatrix.yx = 0;
+ else
+ aMatrix.yx = 0x6000L, aMatrix.xy = 0;
+ FT_Glyph_Transform( pGlyphFT, &aMatrix, NULL );
+ }
+
+ if( pGlyphFT->format == FT_GLYPH_FORMAT_OUTLINE )
+ ((FT_OutlineGlyph)pGlyphFT)->outline.flags |= FT_OUTLINE_HIGH_PRECISION;
+
+ bool bEmbedded = (pGlyphFT->format == FT_GLYPH_FORMAT_BITMAP);
+ if( !bEmbedded )
+ {
+ rc = FT_Glyph_To_Bitmap( &pGlyphFT, FT_RENDER_MODE_NORMAL, NULL, sal_True );
+ if( rc != FT_Err_Ok )
+ {
+ FT_Done_Glyph( pGlyphFT );
+ return false;
+ }
+ }
+
+ const FT_BitmapGlyph pBmpGlyphFT = reinterpret_cast<const FT_BitmapGlyph>(pGlyphFT);
+ rRawBitmap.mnXOffset = +pBmpGlyphFT->left;
+ rRawBitmap.mnYOffset = -pBmpGlyphFT->top;
+
+ const FT_Bitmap& rBitmapFT = pBmpGlyphFT->bitmap;
+ rRawBitmap.mnHeight = rBitmapFT.rows;
+ rRawBitmap.mnWidth = rBitmapFT.width;
+ rRawBitmap.mnBitCount = 8;
+ rRawBitmap.mnScanlineSize = bEmbedded ? rBitmapFT.width : rBitmapFT.pitch;
+ if( mbArtBold && !pFTEmbolden )
+ {
+ ++rRawBitmap.mnWidth;
+ ++rRawBitmap.mnScanlineSize;
+ }
+ rRawBitmap.mnScanlineSize = (rRawBitmap.mnScanlineSize + 3) & -4;
+
+ const sal_uLong nNeededSize = rRawBitmap.mnScanlineSize * rRawBitmap.mnHeight;
+ if( rRawBitmap.mnAllocated < nNeededSize )
+ {
+ delete[] rRawBitmap.mpBits;
+ rRawBitmap.mnAllocated = 2*nNeededSize;
+ rRawBitmap.mpBits = new unsigned char[ rRawBitmap.mnAllocated ];
+ }
+
+ const unsigned char* pSrc = rBitmapFT.buffer;
+ unsigned char* pDest = rRawBitmap.mpBits;
+ if( !bEmbedded )
+ {
+ for( int y = rRawBitmap.mnHeight, x; --y >= 0 ; )
+ {
+ for( x = 0; x < rBitmapFT.width; ++x )
+ *(pDest++) = *(pSrc++);
+ for(; x < int(rRawBitmap.mnScanlineSize); ++x )
+ *(pDest++) = 0;
+ }
+ }
+ else
+ {
+ for( int y = rRawBitmap.mnHeight, x; --y >= 0 ; )
+ {
+ unsigned char nSrc = 0;
+ for( x = 0; x < rBitmapFT.width; ++x, nSrc+=nSrc )
+ {
+ if( (x & 7) == 0 )
+ nSrc = *(pSrc++);
+ *(pDest++) = (0x7F - nSrc) >> 8;
+ }
+ for(; x < int(rRawBitmap.mnScanlineSize); ++x )
+ *(pDest++) = 0;
+ }
+ }
+
+ if( mbArtBold && !pFTEmbolden )
+ {
+ // overlay with glyph image shifted by one left pixel
+ unsigned char* p = rRawBitmap.mpBits;
+ for( sal_uLong y=0; y < rRawBitmap.mnHeight; y++ )
+ {
+ unsigned char nLastByte = 0;
+ for( sal_uLong x=0; x < rRawBitmap.mnWidth; x++ )
+ {
+ unsigned char nTmp = p[x];
+ p[x] |= p[x] | nLastByte;
+ nLastByte = nTmp;
+ }
+ p += rRawBitmap.mnScanlineSize;
+ }
+ }
+
+ if( !bEmbedded && mbUseGamma )
+ {
+ unsigned char* p = rRawBitmap.mpBits;
+ for( sal_uLong y=0; y < rRawBitmap.mnHeight; y++ )
+ {
+ for( sal_uLong x=0; x < rRawBitmap.mnWidth; x++ )
+ {
+ p[x] = aGammaTable[ p[x] ];
+ }
+ p += rRawBitmap.mnScanlineSize;
+ }
+ }
+
+ FT_Done_Glyph( pGlyphFT );
+
+ // special case for 0/90/180/270 degree orientation
+ switch( nAngle )
+ {
+ case -900:
+ case +900:
+ case +1800:
+ case +2700:
+ rRawBitmap.Rotate( nAngle );
+ break;
+ }
+
+ return true;
+}
+
+// -----------------------------------------------------------------------
+// determine unicode ranges in font
+// -----------------------------------------------------------------------
+
+const ImplFontCharMap* ServerFont::GetImplFontCharMap( void ) const
+{
+ const ImplFontCharMap* pIFCMap = mpFontInfo->GetImplFontCharMap();
+ return pIFCMap;
+}
+
+const ImplFontCharMap* FtFontInfo::GetImplFontCharMap( void )
+{
+ // check if the charmap is already cached
+ if( mpFontCharMap )
+ return mpFontCharMap;
+
+ // get the charmap and cache it
+ CmapResult aCmapResult;
+ bool bOK = GetFontCodeRanges( aCmapResult );
+ if( bOK )
+ mpFontCharMap = new ImplFontCharMap( aCmapResult );
+ else
+ mpFontCharMap = ImplFontCharMap::GetDefaultMap();
+ mpFontCharMap->AddReference();
+ return mpFontCharMap;
+}
+
+// TODO: merge into method GetFontCharMap()
+bool FtFontInfo::GetFontCodeRanges( CmapResult& rResult ) const
+{
+ rResult.mbSymbolic = IsSymbolFont();
+
+ // TODO: is the full CmapResult needed on platforms calling this?
+ if( FT_IS_SFNT( maFaceFT ) )
+ {
+ sal_uLong nLength = 0;
+ const unsigned char* pCmap = GetTable( "cmap", &nLength );
+ if( pCmap && (nLength > 0) )
+ if( ParseCMAP( pCmap, nLength, rResult ) )
+ return true;
+ }
+
+ typedef std::vector<sal_uInt32> U32Vector;
+ U32Vector aCodes;
+
+ // FT's coverage is available since FT>=2.1.0 (OOo-baseline>=2.1.4 => ok)
+ aCodes.reserve( 0x1000 );
+ FT_UInt nGlyphIndex;
+ for( sal_uInt32 cCode = FT_Get_First_Char( maFaceFT, &nGlyphIndex );; )
+ {
+ if( !nGlyphIndex )
+ break;
+ aCodes.push_back( cCode ); // first code inside range
+ sal_uInt32 cNext = cCode;
+ do cNext = FT_Get_Next_Char( maFaceFT, cCode, &nGlyphIndex ); while( cNext == ++cCode );
+ aCodes.push_back( cCode ); // first code outside range
+ cCode = cNext;
+ }
+
+ const int nCount = aCodes.size();
+ if( !nCount) {
+ if( !rResult.mbSymbolic )
+ return false;
+
+ // we usually get here for Type1 symbol fonts
+ aCodes.push_back( 0xF020 );
+ aCodes.push_back( 0xF100 );
+ }
+
+ sal_uInt32* pCodes = new sal_uInt32[ nCount ];
+ for( int i = 0; i < nCount; ++i )
+ pCodes[i] = aCodes[i];
+ rResult.mpRangeCodes = pCodes;
+ rResult.mnRangeCount = nCount / 2;
+ return true;
+}
+
+bool ServerFont::GetFontCapabilities(vcl::FontCapabilities &rFontCapabilities) const
+{
+ bool bRet = false;
+
+ sal_uLong nLength = 0;
+ // load GSUB table
+ const FT_Byte* pGSUB = mpFontInfo->GetTable("GSUB", &nLength);
+ if (pGSUB)
+ vcl::getTTScripts(rFontCapabilities.maGSUBScriptTags, pGSUB, nLength);
+
+ // load OS/2 table
+ const FT_Byte* pOS2 = mpFontInfo->GetTable("OS/2", &nLength);
+ if (pOS2)
+ {
+ bRet = vcl::getTTCoverage(
+ rFontCapabilities.maUnicodeRange,
+ rFontCapabilities.maCodePageRange,
+ pOS2, nLength);
+ }
+
+ return bRet;
+}
+
+// -----------------------------------------------------------------------
+// kerning stuff
+// -----------------------------------------------------------------------
+
+int ServerFont::GetGlyphKernValue( int nGlyphLeft, int nGlyphRight ) const
+{
+ // if no kerning info is available from Freetype
+ // then we may have to use extra info provided by e.g. psprint
+ if( !FT_HAS_KERNING( maFaceFT ) || !FT_IS_SFNT( maFaceFT ) )
+ {
+ int nKernVal = mpFontInfo->GetExtraGlyphKernValue( nGlyphLeft, nGlyphRight );
+ if( !nKernVal )
+ return 0;
+ // scale the kern value to match the font size
+ const ImplFontSelectData& rFSD = GetFontSelData();
+ nKernVal *= rFSD.mnWidth ? rFSD.mnWidth : rFSD.mnHeight;
+ return (nKernVal + 500) / 1000;
+ }
+
+ // when font faces of different sizes share the same maFaceFT
+ // then we have to make sure that it uses the correct maSizeFT
+ if( maSizeFT )
+ pFTActivateSize( maSizeFT );
+
+ // use Freetype's kerning info
+ FT_Vector aKernVal;
+ FT_Error rcFT = FT_Get_Kerning( maFaceFT, nGlyphLeft, nGlyphRight,
+ FT_KERNING_DEFAULT, &aKernVal );
+ int nResult = (rcFT == FT_Err_Ok) ? (aKernVal.x + 32) >> 6 : 0;
+ return nResult;
+}
+
+// -----------------------------------------------------------------------
+
+sal_uLong ServerFont::GetKernPairs( ImplKernPairData** ppKernPairs ) const
+{
+ // if no kerning info is available in the font file
+ *ppKernPairs = NULL;
+ if( !FT_HAS_KERNING( maFaceFT ) || !FT_IS_SFNT( maFaceFT ) )
+ {
+ // then we have may have extra kerning info from e.g. psprint
+ int nCount = mpFontInfo->GetExtraKernPairs( ppKernPairs );
+ // scale the kern values to match the font size
+ const ImplFontSelectData& rFSD = GetFontSelData();
+ int nFontWidth = rFSD.mnWidth ? rFSD.mnWidth : rFSD.mnHeight;
+ ImplKernPairData* pKernPair = *ppKernPairs;
+ for( int i = nCount; --i >= 0; ++pKernPair )
+ {
+ long& rVal = pKernPair->mnKern;
+ rVal = ((rVal * nFontWidth) + 500) / 1000;
+ }
+ return nCount;
+ }
+
+ // when font faces of different sizes share the same maFaceFT
+ // then we have to make sure that it uses the correct maSizeFT
+ if( maSizeFT )
+ pFTActivateSize( maSizeFT );
+
+ // first figure out which glyph pairs are involved in kerning
+ sal_uLong nKernLength = 0;
+ const FT_Byte* const pKern = mpFontInfo->GetTable( "kern", &nKernLength );
+ if( !pKern )
+ return 0;
+
+ // combine TTF/OTF tables from the font file to build a vector of
+ // unicode kerning pairs using Freetype's glyph kerning calculation
+ // for the kerning value
+
+ // TODO: is it worth to share the glyph->unicode mapping between
+ // different instances of the same font face?
+
+ typedef std::vector<ImplKernPairData> KernVector;
+ KernVector aKernGlyphVector;
+ ImplKernPairData aKernPair;
+ aKernPair.mnKern = 0; // To prevent "is used uninitialized" warning...
+
+ const FT_Byte* pBuffer = pKern;
+ sal_uLong nVersion = GetUShort( pBuffer+0 );
+ sal_uInt16 nTableCnt = GetUShort( pBuffer+2 );
+
+ // Microsoft/Old TrueType style kern table
+ if ( nVersion == 0 )
+ {
+ pBuffer += 4;
+
+ for( sal_uInt16 nTableIdx = 0; nTableIdx < nTableCnt; ++nTableIdx )
+ {
+ // sal_uInt16 nSubVersion = GetUShort( pBuffer+0 );
+ // sal_uInt16 nSubLength = GetUShort( pBuffer+2 );
+ sal_uInt16 nSubCoverage = GetUShort( pBuffer+4 );
+ pBuffer += 6;
+ if( (nSubCoverage&0x03) != 0x01 ) // no interest in minimum info here
+ continue;
+ switch( nSubCoverage >> 8 )
+ {
+ case 0: // version 0, kerning format 0
+ {
+ sal_uInt16 nPairs = GetUShort( pBuffer );
+ pBuffer += 8; // skip search hints
+ aKernGlyphVector.reserve( aKernGlyphVector.size() + nPairs );
+ for( int i = 0; i < nPairs; ++i )
+ {
+ aKernPair.mnChar1 = GetUShort( pBuffer+0 );
+ aKernPair.mnChar2 = GetUShort( pBuffer+2 );
+ //long nUnscaledKern= GetSShort( pBuffer );
+ pBuffer += 6;
+ aKernGlyphVector.push_back( aKernPair );
+ }
+ }
+ break;
+
+ case 2: // version 0, kerning format 2
+ {
+ const FT_Byte* pSubTable = pBuffer;
+ //sal_uInt16 nRowWidth = GetUShort( pBuffer+0 );
+ sal_uInt16 nOfsLeft = GetUShort( pBuffer+2 );
+ sal_uInt16 nOfsRight = GetUShort( pBuffer+4 );
+ sal_uInt16 nOfsArray = GetUShort( pBuffer+6 );
+ pBuffer += 8;
+
+ const FT_Byte* pTmp = pSubTable + nOfsLeft;
+ sal_uInt16 nFirstLeft = GetUShort( pTmp+0 );
+ sal_uInt16 nLastLeft = GetUShort( pTmp+2 ) + nFirstLeft - 1;
+
+ pTmp = pSubTable + nOfsRight;
+ sal_uInt16 nFirstRight = GetUShort( pTmp+0 );
+ sal_uInt16 nLastRight = GetUShort( pTmp+2 ) + nFirstRight - 1;
+
+ sal_uLong nPairs = (sal_uLong)(nLastLeft - nFirstLeft + 1) * (nLastRight - nFirstRight + 1);
+ aKernGlyphVector.reserve( aKernGlyphVector.size() + nPairs );
+
+ pTmp = pSubTable + nOfsArray;
+ for( int nLeft = nFirstLeft; nLeft < nLastLeft; ++nLeft )
+ {
+ aKernPair.mnChar1 = nLeft;
+ for( int nRight = 0; nRight < nLastRight; ++nRight )
+ {
+ if( GetUShort( pTmp ) != 0 )
+ {
+ aKernPair.mnChar2 = nRight;
+ aKernGlyphVector.push_back( aKernPair );
+ }
+ pTmp += 2;
+ }
+ }
+ }
+ break;
+ }
+ }
+ }
+
+ // Apple New style kern table
+ pBuffer = pKern;
+ nVersion = NEXT_U32( pBuffer );
+ nTableCnt = NEXT_U32( pBuffer );
+ if ( nVersion == 0x00010000 )
+ {
+ for( sal_uInt16 nTableIdx = 0; nTableIdx < nTableCnt; ++nTableIdx )
+ {
+ /*sal_uLong nLength =*/ NEXT_U32( pBuffer );
+ sal_uInt16 nCoverage = NEXT_U16( pBuffer );
+ /*sal_uInt16 nTupleIndex =*/ NEXT_U16( pBuffer );
+
+ // Kerning sub-table format, 0 through 3
+ sal_uInt8 nSubTableFormat = nCoverage & 0x00FF;
+
+ switch( nSubTableFormat )
+ {
+ case 0: // version 0, kerning format 0
+ {
+ sal_uInt16 nPairs = NEXT_U16( pBuffer );
+ pBuffer += 6; // skip search hints
+ aKernGlyphVector.reserve( aKernGlyphVector.size() + nPairs );
+ for( int i = 0; i < nPairs; ++i )
+ {
+ aKernPair.mnChar1 = NEXT_U16( pBuffer );
+ aKernPair.mnChar2 = NEXT_U16( pBuffer );
+ /*long nUnscaledKern=*/ NEXT_S16( pBuffer );
+ aKernGlyphVector.push_back( aKernPair );
+ }
+ }
+ break;
+
+ case 2: // version 0, kerning format 2
+ {
+ const FT_Byte* pSubTable = pBuffer;
+ /*sal_uInt16 nRowWidth =*/ NEXT_U16( pBuffer );
+ sal_uInt16 nOfsLeft = NEXT_U16( pBuffer );
+ sal_uInt16 nOfsRight = NEXT_U16( pBuffer );
+ sal_uInt16 nOfsArray = NEXT_U16( pBuffer );
+
+ const FT_Byte* pTmp = pSubTable + nOfsLeft;
+ sal_uInt16 nFirstLeft = NEXT_U16( pTmp );
+ sal_uInt16 nLastLeft = NEXT_U16( pTmp ) + nFirstLeft - 1;
+
+ pTmp = pSubTable + nOfsRight;
+ sal_uInt16 nFirstRight = NEXT_U16( pTmp );
+ sal_uInt16 nLastRight = NEXT_U16( pTmp ) + nFirstRight - 1;
+
+ sal_uLong nPairs = (sal_uLong)(nLastLeft - nFirstLeft + 1) * (nLastRight - nFirstRight + 1);
+ aKernGlyphVector.reserve( aKernGlyphVector.size() + nPairs );
+
+ pTmp = pSubTable + nOfsArray;
+ for( int nLeft = nFirstLeft; nLeft < nLastLeft; ++nLeft )
+ {
+ aKernPair.mnChar1 = nLeft;
+ for( int nRight = 0; nRight < nLastRight; ++nRight )
+ {
+ if( NEXT_S16( pTmp ) != 0 )
+ {
+ aKernPair.mnChar2 = nRight;
+ aKernGlyphVector.push_back( aKernPair );
+ }
+ }
+ }
+ }
+ break;
+
+ default:
+ fprintf( stderr, "gcach_ftyp.cxx: Found unsupported Apple-style kern subtable type %d.\n", nSubTableFormat );
+ break;
+ }
+ }
+ }
+
+ // now create VCL's ImplKernPairData[] format for all glyph pairs
+ sal_uLong nKernCount = aKernGlyphVector.size();
+ if( nKernCount )
+ {
+ // prepare glyphindex to character mapping
+ // TODO: this is needed to support VCL's existing kerning infrastructure,
+ // eliminate it up by redesigning kerning infrastructure to work with glyph indizes
+ typedef boost::unordered_multimap<sal_uInt16,sal_Unicode> Cmap;
+ Cmap aCmap;
+ for( sal_Unicode aChar = 0x0020; aChar < 0xFFFE; ++aChar )
+ {
+ sal_uInt16 nGlyphIndex = GetGlyphIndex( aChar );
+ if( nGlyphIndex )
+ aCmap.insert( Cmap::value_type( nGlyphIndex, aChar ) );
+ }
+
+ // translate both glyph indizes in kerning pairs to characters
+ // problem is that these are 1:n mappings...
+ KernVector aKernCharVector;
+ aKernCharVector.reserve( nKernCount );
+ KernVector::iterator it;
+ for( it = aKernGlyphVector.begin(); it != aKernGlyphVector.end(); ++it )
+ {
+ FT_Vector aKernVal;
+ FT_Error rcFT = FT_Get_Kerning( maFaceFT, it->mnChar1, it->mnChar2,
+ FT_KERNING_DEFAULT, &aKernVal );
+ aKernPair.mnKern = aKernVal.x >> 6;
+ if( (aKernPair.mnKern == 0) || (rcFT != FT_Err_Ok) )
+ continue;
+
+ typedef std::pair<Cmap::iterator,Cmap::iterator> CPair;
+ const CPair p1 = aCmap.equal_range( it->mnChar1 );
+ const CPair p2 = aCmap.equal_range( it->mnChar2 );
+ for( Cmap::const_iterator i1 = p1.first; i1 != p1.second; ++i1 )
+ {
+ aKernPair.mnChar1 = (*i1).second;
+ for( Cmap::const_iterator i2 = p2.first; i2 != p2.second; ++i2 )
+ {
+ aKernPair.mnChar2 = (*i2).second;
+ aKernCharVector.push_back( aKernPair );
+ }
+ }
+ }
+
+ // now move the resulting vector into VCL's ImplKernPairData[] format
+ nKernCount = aKernCharVector.size();
+ ImplKernPairData* pTo = new ImplKernPairData[ nKernCount ];
+ *ppKernPairs = pTo;
+ for( it = aKernCharVector.begin(); it != aKernCharVector.end(); ++it, ++pTo )
+ {
+ pTo->mnChar1 = it->mnChar1;
+ pTo->mnChar2 = it->mnChar2;
+ pTo->mnKern = it->mnKern;
+ }
+ }
+
+ return nKernCount;
+}
+
+// -----------------------------------------------------------------------
+// outline stuff
+// -----------------------------------------------------------------------
+
+class PolyArgs
+{
+public:
+ PolyArgs( PolyPolygon& rPolyPoly, sal_uInt16 nMaxPoints );
+ ~PolyArgs();
+
+ void AddPoint( long nX, long nY, PolyFlags);
+ void ClosePolygon();
+
+ long GetPosX() const { return maPosition.x;}
+ long GetPosY() const { return maPosition.y;}
+
+private:
+ PolyPolygon& mrPolyPoly;
+
+ Point* mpPointAry;
+ sal_uInt8* mpFlagAry;
+
+ FT_Vector maPosition;
+ sal_uInt16 mnMaxPoints;
+ sal_uInt16 mnPoints;
+ sal_uInt16 mnPoly;
+ long mnHeight;
+ bool bHasOffline;
+};
+
+// -----------------------------------------------------------------------
+
+PolyArgs::PolyArgs( PolyPolygon& rPolyPoly, sal_uInt16 nMaxPoints )
+: mrPolyPoly(rPolyPoly),
+ mnMaxPoints(nMaxPoints),
+ mnPoints(0),
+ mnPoly(0),
+ mnHeight(0),
+ bHasOffline(false)
+{
+ mpPointAry = new Point[ mnMaxPoints ];
+ mpFlagAry = new sal_uInt8 [ mnMaxPoints ];
+}
+
+// -----------------------------------------------------------------------
+
+
+PolyArgs::~PolyArgs()
+{
+ delete[] mpFlagAry;
+ delete[] mpPointAry;
+}
+
+// -----------------------------------------------------------------------
+
+void PolyArgs::AddPoint( long nX, long nY, PolyFlags aFlag )
+{
+ DBG_ASSERT( (mnPoints < mnMaxPoints), "FTGlyphOutline: AddPoint overflow!" );
+ if( mnPoints >= mnMaxPoints )
+ return;
+
+ maPosition.x = nX;
+ maPosition.y = nY;
+ mpPointAry[ mnPoints ] = Point( nX, nY );
+ mpFlagAry[ mnPoints++ ]= aFlag;
+ bHasOffline |= (aFlag != POLY_NORMAL);
+}
+
+// -----------------------------------------------------------------------
+
+void PolyArgs::ClosePolygon()
+{
+ if( !mnPoly++ )
+ return;
+
+ // freetype seems to always close the polygon with an ON_CURVE point
+ // PolyPoly wants to close the polygon itself => remove last point
+ DBG_ASSERT( (mnPoints >= 2), "FTGlyphOutline: PolyFinishNum failed!" );
+ --mnPoints;
+ DBG_ASSERT( (mpPointAry[0]==mpPointAry[mnPoints]), "FTGlyphOutline: PolyFinishEq failed!" );
+ DBG_ASSERT( (mpFlagAry[0]==POLY_NORMAL), "FTGlyphOutline: PolyFinishFE failed!" );
+ DBG_ASSERT( (mpFlagAry[mnPoints]==POLY_NORMAL), "FTGlyphOutline: PolyFinishFS failed!" );
+
+ Polygon aPoly( mnPoints, mpPointAry, (bHasOffline ? mpFlagAry : NULL) );
+
+ // #i35928#
+ // This may be a invalid polygons, e.g. the last point is a control point.
+ // So close the polygon (and add the first point again) if the last point
+ // is a control point or different from first.
+ // #i48298#
+ // Now really duplicating the first point, to close or correct the
+ // polygon. Also no longer duplicating the flags, but enforcing
+ // POLY_NORMAL for the newly added last point.
+ const sal_uInt16 nPolySize(aPoly.GetSize());
+ if(nPolySize)
+ {
+ if((aPoly.HasFlags() && POLY_CONTROL == aPoly.GetFlags(nPolySize - 1))
+ || (aPoly.GetPoint(nPolySize - 1) != aPoly.GetPoint(0)))
+ {
+ aPoly.SetSize(nPolySize + 1);
+ aPoly.SetPoint(aPoly.GetPoint(0), nPolySize);
+
+ if(aPoly.HasFlags())
+ {
+ aPoly.SetFlags(nPolySize, POLY_NORMAL);
+ }
+ }
+ }
+
+ mrPolyPoly.Insert( aPoly );
+ mnPoints = 0;
+ bHasOffline = false;
+}
+
+// -----------------------------------------------------------------------
+
+extern "C" {
+
+// TODO: wait till all compilers accept that calling conventions
+// for functions are the same independent of implementation constness,
+// then uncomment the const-tokens in the function interfaces below
+static int FT_move_to( FT_Vector_CPtr p0, void* vpPolyArgs )
+{
+ PolyArgs& rA = *reinterpret_cast<PolyArgs*>(vpPolyArgs);
+
+ // move_to implies a new polygon => finish old polygon first
+ rA.ClosePolygon();
+
+ rA.AddPoint( p0->x, p0->y, POLY_NORMAL );
+ return 0;
+}
+
+static int FT_line_to( FT_Vector_CPtr p1, void* vpPolyArgs )
+{
+ PolyArgs& rA = *reinterpret_cast<PolyArgs*>(vpPolyArgs);
+ rA.AddPoint( p1->x, p1->y, POLY_NORMAL );
+ return 0;
+}
+
+static int FT_conic_to( FT_Vector_CPtr p1, FT_Vector_CPtr p2, void* vpPolyArgs )
+{
+ PolyArgs& rA = *reinterpret_cast<PolyArgs*>(vpPolyArgs);
+
+ // VCL's Polygon only knows cubic beziers
+ const long nX1 = (2 * rA.GetPosX() + 4 * p1->x + 3) / 6;
+ const long nY1 = (2 * rA.GetPosY() + 4 * p1->y + 3) / 6;
+ rA.AddPoint( nX1, nY1, POLY_CONTROL );
+
+ const long nX2 = (2 * p2->x + 4 * p1->x + 3) / 6;
+ const long nY2 = (2 * p2->y + 4 * p1->y + 3) / 6;
+ rA.AddPoint( nX2, nY2, POLY_CONTROL );
+
+ rA.AddPoint( p2->x, p2->y, POLY_NORMAL );
+ return 0;
+}
+
+static int FT_cubic_to( FT_Vector_CPtr p1, FT_Vector_CPtr p2, FT_Vector_CPtr p3, void* vpPolyArgs )
+{
+ PolyArgs& rA = *reinterpret_cast<PolyArgs*>(vpPolyArgs);
+ rA.AddPoint( p1->x, p1->y, POLY_CONTROL );
+ rA.AddPoint( p2->x, p2->y, POLY_CONTROL );
+ rA.AddPoint( p3->x, p3->y, POLY_NORMAL );
+ return 0;
+}
+
+} // extern "C"
+
+// -----------------------------------------------------------------------
+
+bool ServerFont::GetGlyphOutline( int nGlyphIndex,
+ ::basegfx::B2DPolyPolygon& rB2DPolyPoly ) const
+{
+ if( maSizeFT )
+ pFTActivateSize( maSizeFT );
+
+ rB2DPolyPoly.clear();
+
+ int nGlyphFlags;
+ SplitGlyphFlags( *this, nGlyphIndex, nGlyphFlags );
+
+ FT_Int nLoadFlags = FT_LOAD_DEFAULT | FT_LOAD_IGNORE_TRANSFORM;
+
+#ifdef FT_LOAD_TARGET_LIGHT
+ // enable "light hinting" if available
+ if( nFTVERSION >= 2103 )
+ nLoadFlags |= FT_LOAD_TARGET_LIGHT;
+#endif
+
+ FT_Error rc = FT_Load_Glyph( maFaceFT, nGlyphIndex, nLoadFlags );
+ if( rc != FT_Err_Ok )
+ return false;
+
+ if( mbArtBold && pFTEmbolden )
+ (*pFTEmbolden)( maFaceFT->glyph );
+
+ FT_Glyph pGlyphFT;
+ rc = FT_Get_Glyph( maFaceFT->glyph, &pGlyphFT );
+ if( rc != FT_Err_Ok )
+ return false;
+
+ if( pGlyphFT->format != FT_GLYPH_FORMAT_OUTLINE )
+ return false;
+
+ if( mbArtItalic )
+ {
+ FT_Matrix aMatrix;
+ aMatrix.xx = aMatrix.yy = 0x10000L;
+ if( nFTVERSION >= 2102 ) // Freetype 2.1.2 API swapped xy with yx
+ aMatrix.xy = 0x6000L, aMatrix.yx = 0;
+ else
+ aMatrix.yx = 0x6000L, aMatrix.xy = 0;
+ FT_Glyph_Transform( pGlyphFT, &aMatrix, NULL );
+ }
+
+ FT_Outline& rOutline = reinterpret_cast<FT_OutlineGlyphRec*>(pGlyphFT)->outline;
+ if( !rOutline.n_points ) // blank glyphs are ok
+ return true;
+
+ long nMaxPoints = 1 + rOutline.n_points * 3;
+ PolyPolygon aToolPolyPolygon;
+ PolyArgs aPolyArg( aToolPolyPolygon, nMaxPoints );
+
+ /*int nAngle =*/ ApplyGlyphTransform( nGlyphFlags, pGlyphFT, false );
+
+ FT_Outline_Funcs aFuncs;
+ aFuncs.move_to = &FT_move_to;
+ aFuncs.line_to = &FT_line_to;
+ aFuncs.conic_to = &FT_conic_to;
+ aFuncs.cubic_to = &FT_cubic_to;
+ aFuncs.shift = 0;
+ aFuncs.delta = 0;
+ rc = FT_Outline_Decompose( &rOutline, &aFuncs, (void*)&aPolyArg );
+ aPolyArg.ClosePolygon(); // close last polygon
+ FT_Done_Glyph( pGlyphFT );
+
+ // convert to basegfx polypolygon
+ // TODO: get rid of the intermediate tools polypolygon
+ rB2DPolyPoly = aToolPolyPolygon.getB2DPolyPolygon();
+ rB2DPolyPoly.transform(basegfx::tools::createScaleB2DHomMatrix( +1.0/(1<<6), -1.0/(1<<6) ));
+
+ return true;
+}
+
+// -----------------------------------------------------------------------
+
+bool ServerFont::ApplyGSUB( const ImplFontSelectData& rFSD )
+{
+#define MKTAG(s) ((((((s[0]<<8)+s[1])<<8)+s[2])<<8)+s[3])
+
+ typedef std::vector<sal_uLong> ReqFeatureTagList;
+ ReqFeatureTagList aReqFeatureTagList;
+ if( rFSD.mbVertical )
+ aReqFeatureTagList.push_back( MKTAG("vert") );
+ sal_uLong nRequestedScript = 0; //MKTAG("hani");//### TODO: where to get script?
+ sal_uLong nRequestedLangsys = 0; //MKTAG("ZHT"); //### TODO: where to get langsys?
+ // TODO: request more features depending on script and language system
+
+ if( aReqFeatureTagList.empty()) // nothing to do
+ return true;
+
+ // load GSUB table into memory
+ sal_uLong nLength = 0;
+ const FT_Byte* const pGsubBase = mpFontInfo->GetTable( "GSUB", &nLength );
+ if( !pGsubBase )
+ return false;
+
+ // parse GSUB header
+ const FT_Byte* pGsubHeader = pGsubBase;
+ const sal_uInt16 nOfsScriptList = GetUShort( pGsubHeader+4 );
+ const sal_uInt16 nOfsFeatureTable = GetUShort( pGsubHeader+6 );
+ const sal_uInt16 nOfsLookupList = GetUShort( pGsubHeader+8 );
+ pGsubHeader += 10;
+
+ typedef std::vector<sal_uInt16> UshortList;
+ UshortList aFeatureIndexList;
+ UshortList aFeatureOffsetList;
+
+ // parse Script Table
+ const FT_Byte* pScriptHeader = pGsubBase + nOfsScriptList;
+ const sal_uInt16 nCntScript = GetUShort( pScriptHeader+0 );
+ pScriptHeader += 2;
+ for( sal_uInt16 nScriptIndex = 0; nScriptIndex < nCntScript; ++nScriptIndex )
+ {
+ const sal_uLong nScriptTag = GetUInt( pScriptHeader+0 ); // e.g. hani/arab/kana/hang
+ const sal_uInt16 nOfsScriptTable= GetUShort( pScriptHeader+4 );
+ pScriptHeader += 6; //###
+ if( (nScriptTag != nRequestedScript) && (nRequestedScript != 0) )
+ continue;
+
+ const FT_Byte* pScriptTable = pGsubBase + nOfsScriptList + nOfsScriptTable;
+ const sal_uInt16 nDefaultLangsysOfs = GetUShort( pScriptTable+0 );
+ const sal_uInt16 nCntLangSystem = GetUShort( pScriptTable+2 );
+ pScriptTable += 4;
+ sal_uInt16 nLangsysOffset = 0;
+
+ for( sal_uInt16 nLangsysIndex = 0; nLangsysIndex < nCntLangSystem; ++nLangsysIndex )
+ {
+ const sal_uLong nTag = GetUInt( pScriptTable+0 ); // e.g. KOR/ZHS/ZHT/JAN
+ const sal_uInt16 nOffset= GetUShort( pScriptTable+4 );
+ pScriptTable += 6;
+ if( (nTag != nRequestedLangsys) && (nRequestedLangsys != 0) )
+ continue;
+ nLangsysOffset = nOffset;
+ break;
+ }
+
+ if( (nDefaultLangsysOfs != 0) && (nDefaultLangsysOfs != nLangsysOffset) )
+ {
+ const FT_Byte* pLangSys = pGsubBase + nOfsScriptList + nOfsScriptTable + nDefaultLangsysOfs;
+ const sal_uInt16 nReqFeatureIdx = GetUShort( pLangSys+2 );
+ const sal_uInt16 nCntFeature = GetUShort( pLangSys+4 );
+ pLangSys += 6;
+ aFeatureIndexList.push_back( nReqFeatureIdx );
+ for( sal_uInt16 i = 0; i < nCntFeature; ++i )
+ {
+ const sal_uInt16 nFeatureIndex = GetUShort( pLangSys );
+ pLangSys += 2;
+ aFeatureIndexList.push_back( nFeatureIndex );
+ }
+ }
+
+ if( nLangsysOffset != 0 )
+ {
+ const FT_Byte* pLangSys = pGsubBase + nOfsScriptList + nOfsScriptTable + nLangsysOffset;
+ const sal_uInt16 nReqFeatureIdx = GetUShort( pLangSys+2 );
+ const sal_uInt16 nCntFeature = GetUShort( pLangSys+4 );
+ pLangSys += 6;
+ aFeatureIndexList.push_back( nReqFeatureIdx );
+ for( sal_uInt16 i = 0; i < nCntFeature; ++i )
+ {
+ const sal_uInt16 nFeatureIndex = GetUShort( pLangSys );
+ pLangSys += 2;
+ aFeatureIndexList.push_back( nFeatureIndex );
+ }
+ }
+ }
+
+ if( aFeatureIndexList.empty() )
+ return true;
+
+ UshortList aLookupIndexList;
+ UshortList aLookupOffsetList;
+
+ // parse Feature Table
+ const FT_Byte* pFeatureHeader = pGsubBase + nOfsFeatureTable;
+ const sal_uInt16 nCntFeature = GetUShort( pFeatureHeader );
+ pFeatureHeader += 2;
+ for( sal_uInt16 nFeatureIndex = 0; nFeatureIndex < nCntFeature; ++nFeatureIndex )
+ {
+ const sal_uLong nTag = GetUInt( pFeatureHeader+0 ); // e.g. locl/vert/trad/smpl/liga/fina/...
+ const sal_uInt16 nOffset= GetUShort( pFeatureHeader+4 );
+ pFeatureHeader += 6;
+
+ // short circuit some feature lookups
+ if( aFeatureIndexList[0] != nFeatureIndex ) // required feature?
+ {
+ const int nRequested = std::count( aFeatureIndexList.begin(), aFeatureIndexList.end(), nFeatureIndex);
+ if( !nRequested ) // ignore features that are not requested
+ continue;
+ const int nAvailable = std::count( aReqFeatureTagList.begin(), aReqFeatureTagList.end(), nTag);
+ if( !nAvailable ) // some fonts don't provide features they request!
+ continue;
+ }
+
+ const FT_Byte* pFeatureTable = pGsubBase + nOfsFeatureTable + nOffset;
+ const sal_uInt16 nCntLookups = GetUShort( pFeatureTable+0 );
+ pFeatureTable += 2;
+ for( sal_uInt16 i = 0; i < nCntLookups; ++i )
+ {
+ const sal_uInt16 nLookupIndex = GetUShort( pFeatureTable );
+ pFeatureTable += 2;
+ aLookupIndexList.push_back( nLookupIndex );
+ }
+ if( nCntLookups == 0 ) //### hack needed by Mincho/Gothic/Mingliu/Simsun/...
+ aLookupIndexList.push_back( 0 );
+ }
+
+ // parse Lookup List
+ const FT_Byte* pLookupHeader = pGsubBase + nOfsLookupList;
+ const sal_uInt16 nCntLookupTable = GetUShort( pLookupHeader );
+ pLookupHeader += 2;
+ for( sal_uInt16 nLookupIdx = 0; nLookupIdx < nCntLookupTable; ++nLookupIdx )
+ {
+ const sal_uInt16 nOffset = GetUShort( pLookupHeader );
+ pLookupHeader += 2;
+ if( std::count( aLookupIndexList.begin(), aLookupIndexList.end(), nLookupIdx ) )
+ aLookupOffsetList.push_back( nOffset );
+ }
+
+ UshortList::const_iterator lookup_it = aLookupOffsetList.begin();
+ for(; lookup_it != aLookupOffsetList.end(); ++lookup_it )
+ {
+ const sal_uInt16 nOfsLookupTable = *lookup_it;
+ const FT_Byte* pLookupTable = pGsubBase + nOfsLookupList + nOfsLookupTable;
+ const sal_uInt16 eLookupType = GetUShort( pLookupTable+0 );
+ const sal_uInt16 nCntLookupSubtable = GetUShort( pLookupTable+4 );
+ pLookupTable += 6;
+
+ // TODO: switch( eLookupType )
+ if( eLookupType != 1 ) // TODO: once we go beyond SingleSubst
+ continue;
+
+ for( sal_uInt16 nSubTableIdx = 0; nSubTableIdx < nCntLookupSubtable; ++nSubTableIdx )
+ {
+ const sal_uInt16 nOfsSubLookupTable = GetUShort( pLookupTable );
+ pLookupTable += 2;
+ const FT_Byte* pSubLookup = pGsubBase + nOfsLookupList + nOfsLookupTable + nOfsSubLookupTable;
+
+ const sal_uInt16 nFmtSubstitution = GetUShort( pSubLookup+0 );
+ const sal_uInt16 nOfsCoverage = GetUShort( pSubLookup+2 );
+ pSubLookup += 4;
+
+ typedef std::pair<sal_uInt16,sal_uInt16> GlyphSubst;
+ typedef std::vector<GlyphSubst> SubstVector;
+ SubstVector aSubstVector;
+
+ const FT_Byte* pCoverage = pGsubBase + nOfsLookupList + nOfsLookupTable + nOfsSubLookupTable + nOfsCoverage;
+ const sal_uInt16 nFmtCoverage = GetUShort( pCoverage+0 );
+ pCoverage += 2;
+ switch( nFmtCoverage )
+ {
+ case 1: // Coverage Format 1
+ {
+ const sal_uInt16 nCntGlyph = GetUShort( pCoverage );
+ pCoverage += 2;
+ aSubstVector.reserve( nCntGlyph );
+ for( sal_uInt16 i = 0; i < nCntGlyph; ++i )
+ {
+ const sal_uInt16 nGlyphId = GetUShort( pCoverage );
+ pCoverage += 2;
+ aSubstVector.push_back( GlyphSubst( nGlyphId, 0 ) );
+ }
+ }
+ break;
+
+ case 2: // Coverage Format 2
+ {
+ const sal_uInt16 nCntRange = GetUShort( pCoverage );
+ pCoverage += 2;
+ for( int i = nCntRange; --i >= 0; )
+ {
+ const sal_uInt32 nGlyph0 = GetUShort( pCoverage+0 );
+ const sal_uInt32 nGlyph1 = GetUShort( pCoverage+2 );
+ const sal_uInt16 nCovIdx = GetUShort( pCoverage+4 );
+ pCoverage += 6;
+ for( sal_uInt32 j = nGlyph0; j <= nGlyph1; ++j )
+ aSubstVector.push_back( GlyphSubst( static_cast<sal_uInt16>(j + nCovIdx), 0 ) );
+ }
+ }
+ break;
+ }
+
+ SubstVector::iterator it( aSubstVector.begin() );
+
+ switch( nFmtSubstitution )
+ {
+ case 1: // Single Substitution Format 1
+ {
+ const sal_uInt16 nDeltaGlyphId = GetUShort( pSubLookup );
+ pSubLookup += 2;
+ for(; it != aSubstVector.end(); ++it )
+ (*it).second = (*it).first + nDeltaGlyphId;
+ }
+ break;
+
+ case 2: // Single Substitution Format 2
+ {
+ const sal_uInt16 nCntGlyph = GetUShort( pSubLookup );
+ pSubLookup += 2;
+ for( int i = nCntGlyph; (it != aSubstVector.end()) && (--i>=0); ++it )
+ {
+ const sal_uInt16 nGlyphId = GetUShort( pSubLookup );
+ pSubLookup += 2;
+ (*it).second = nGlyphId;
+ }
+ }
+ break;
+ }
+
+ DBG_ASSERT( (it == aSubstVector.end()), "lookup<->coverage table mismatch" );
+ // now apply the glyph substitutions that have been collected in this subtable
+ for( it = aSubstVector.begin(); it != aSubstVector.end(); ++it )
+ maGlyphSubstitution[ (*it).first ] = (*it).second;
+ }
+ }
+
+ return true;
+}
+
+const unsigned char* ServerFont::GetTable(const char* pName, sal_uLong* pLength)
+{
+ return mpFontInfo->GetTable( pName, pLength );
+}
+
+#ifdef ENABLE_GRAPHITE
+GraphiteFaceWrapper* ServerFont::GetGraphiteFace() const
+{
+ return mpFontInfo->GetGraphiteFace();
+}
+#endif
+
+// =======================================================================
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/vcl/generic/glyphs/gcach_ftyp.hxx b/vcl/generic/glyphs/gcach_ftyp.hxx
new file mode 100644
index 000000000000..7386bb3d1e82
--- /dev/null
+++ b/vcl/generic/glyphs/gcach_ftyp.hxx
@@ -0,0 +1,201 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*************************************************************************
+ *
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * Copyright 2000, 2010 Oracle and/or its affiliates.
+ *
+ * OpenOffice.org - a multi-platform office productivity suite
+ *
+ * This file is part of OpenOffice.org.
+ *
+ * OpenOffice.org is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License version 3
+ * only, as published by the Free Software Foundation.
+ *
+ * OpenOffice.org is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License version 3 for more details
+ * (a copy is included in the LICENSE file that accompanied this code).
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * version 3 along with OpenOffice.org. If not, see
+ * <http://www.openoffice.org/license.html>
+ * for a copy of the LGPLv3 License.
+ *
+ ************************************************************************/
+
+#ifndef _SV_GCACHFTYP_HXX
+#define _SV_GCACHFTYP_HXX
+
+#include "generic/glyphcache.hxx"
+
+#include <rtl/textcvt.h>
+
+class FreetypeServerFont;
+#ifdef ENABLE_GRAPHITE
+class GraphiteFaceWrapper;
+#endif
+
+// -----------------------------------------------------------------------
+
+// FtFontFile has the responsibility that a font file is only mapped once.
+// (#86621#) the old directly ft-managed solution caused it to be mapped
+// in up to nTTC*nSizes*nOrientation*nSynthetic times
+class FtFontFile
+{
+public:
+ static FtFontFile* FindFontFile( const ::rtl::OString& rNativeFileName );
+
+ bool Map();
+ void Unmap();
+
+ const unsigned char* GetBuffer() const { return mpFileMap; }
+ int GetFileSize() const { return mnFileSize; }
+ const ::rtl::OString* GetFileName() const { return &maNativeFileName; }
+ int GetLangBoost() const { return mnLangBoost; }
+
+private:
+ FtFontFile( const ::rtl::OString& rNativeFileName );
+
+ const ::rtl::OString maNativeFileName;
+ const unsigned char* mpFileMap;
+ int mnFileSize;
+ int mnRefCount;
+ int mnLangBoost;
+};
+
+// -----------------------------------------------------------------------
+
+// FtFontInfo corresponds to an unscaled font face
+class FtFontInfo
+{
+public:
+ FtFontInfo( const ImplDevFontAttributes&,
+ const ::rtl::OString& rNativeFileName,
+ int nFaceNum, sal_IntPtr nFontId, int nSynthetic,
+ const ExtraKernInfo* );
+ ~FtFontInfo();
+
+ const unsigned char* GetTable( const char*, sal_uLong* pLength=0 ) const;
+
+ FT_FaceRec_* GetFaceFT();
+#ifdef ENABLE_GRAPHITE
+ GraphiteFaceWrapper* GetGraphiteFace();
+#endif
+ void ReleaseFaceFT( FT_FaceRec_* );
+
+ const ::rtl::OString* GetFontFileName() const { return mpFontFile->GetFileName(); }
+ int GetFaceNum() const { return mnFaceNum; }
+ int GetSynthetic() const { return mnSynthetic; }
+ sal_IntPtr GetFontId() const { return mnFontId; }
+ bool IsSymbolFont() const { return maDevFontAttributes.IsSymbolFont(); }
+ const ImplFontAttributes& GetFontAttributes() const { return maDevFontAttributes; }
+
+ void AnnounceFont( ImplDevFontList* );
+
+ int GetGlyphIndex( sal_UCS4 cChar ) const;
+ void CacheGlyphIndex( sal_UCS4 cChar, int nGI ) const;
+
+ bool GetFontCodeRanges( CmapResult& ) const;
+ const ImplFontCharMap* GetImplFontCharMap( void );
+
+ bool HasExtraKerning() const;
+ int GetExtraKernPairs( ImplKernPairData** ) const;
+ int GetExtraGlyphKernValue( int nLeftGlyph, int nRightGlyph ) const;
+
+private:
+ FT_FaceRec_* maFaceFT;
+ FtFontFile* mpFontFile;
+ const int mnFaceNum;
+ int mnRefCount;
+ const int mnSynthetic;
+#ifdef ENABLE_GRAPHITE
+ bool mbCheckedGraphite;
+ GraphiteFaceWrapper * mpGraphiteFace;
+#endif
+ sal_IntPtr mnFontId;
+ ImplDevFontAttributes maDevFontAttributes;
+
+ const ImplFontCharMap* mpFontCharMap;
+
+ // cache unicode->glyphid mapping because looking it up is expensive
+ // TODO: change to boost::unordered_multimap when a use case requires a m:n mapping
+ typedef ::boost::unordered_map<int,int> Int2IntMap;
+ mutable Int2IntMap* mpChar2Glyph;
+ mutable Int2IntMap* mpGlyph2Char;
+ void InitHashes() const;
+
+ const ExtraKernInfo* mpExtraKernInfo;
+};
+
+// these two inlines are very important for performance
+
+inline int FtFontInfo::GetGlyphIndex( sal_UCS4 cChar ) const
+{
+ if( !mpChar2Glyph )
+ return -1;
+ Int2IntMap::const_iterator it = mpChar2Glyph->find( cChar );
+ if( it == mpChar2Glyph->end() )
+ return -1;
+ return it->second;
+}
+
+inline void FtFontInfo::CacheGlyphIndex( sal_UCS4 cChar, int nIndex ) const
+{
+ if( !mpChar2Glyph )
+ InitHashes();
+ (*mpChar2Glyph)[ cChar ] = nIndex;
+ (*mpGlyph2Char)[ nIndex ] = cChar;
+}
+
+// -----------------------------------------------------------------------
+
+class FreetypeManager
+{
+public:
+ FreetypeManager();
+ ~FreetypeManager();
+
+ void AddFontFile( const rtl::OString& rNormalizedName,
+ int nFaceNum, sal_IntPtr nFontId, const ImplDevFontAttributes&,
+ const ExtraKernInfo* );
+ void AnnounceFonts( ImplDevFontList* ) const;
+ void ClearFontList();
+
+ ServerFont* CreateFont( const ImplFontSelectData& );
+
+private:
+ typedef ::boost::unordered_map<sal_IntPtr,FtFontInfo*> FontList;
+ FontList maFontList;
+
+ sal_IntPtr mnMaxFontId;
+ sal_IntPtr mnNextFontId;
+};
+
+// -----------------------------------------------------------------------
+
+class ImplFTSFontData : public ImplFontData
+{
+private:
+ FtFontInfo* mpFtFontInfo;
+ enum { IFTSFONT_MAGIC = 0x1F150A1C };
+
+public:
+ ImplFTSFontData( FtFontInfo*, const ImplDevFontAttributes& );
+
+ FtFontInfo* GetFtFontInfo() const { return mpFtFontInfo; }
+
+ virtual ImplFontEntry* CreateFontInstance( ImplFontSelectData& ) const;
+ virtual ImplFontData* Clone() const { return new ImplFTSFontData( *this ); }
+ virtual sal_IntPtr GetFontId() const { return mpFtFontInfo->GetFontId(); }
+
+ static bool CheckFontData( const ImplFontData& r ) { return r.CheckMagic( IFTSFONT_MAGIC ); }
+};
+
+// -----------------------------------------------------------------------
+
+#endif // _SV_GCACHFTYP_HXX
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/vcl/generic/glyphs/gcach_layout.cxx b/vcl/generic/glyphs/gcach_layout.cxx
new file mode 100644
index 000000000000..ae5ad511268d
--- /dev/null
+++ b/vcl/generic/glyphs/gcach_layout.cxx
@@ -0,0 +1,669 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*************************************************************************
+ *
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * Copyright 2000, 2010 Oracle and/or its affiliates.
+ *
+ * OpenOffice.org - a multi-platform office productivity suite
+ *
+ * This file is part of OpenOffice.org.
+ *
+ * OpenOffice.org is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License version 3
+ * only, as published by the Free Software Foundation.
+ *
+ * OpenOffice.org is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License version 3 for more details
+ * (a copy is included in the LICENSE file that accompanied this code).
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * version 3 along with OpenOffice.org. If not, see
+ * <http://www.openoffice.org/license.html>
+ * for a copy of the LGPLv3 License.
+ *
+ ************************************************************************/
+
+// MARKER(update_precomp.py): autogen include statement, do not remove
+#include "precompiled_vcl.hxx"
+
+#define ENABLE_ICU_LAYOUT
+#include <gcach_ftyp.hxx>
+#include <sallayout.hxx>
+#include <salgdi.hxx>
+
+#include <vcl/svapp.hxx>
+
+#include <sal/alloca.h>
+
+#if OSL_DEBUG_LEVEL > 1
+#include <cstdio>
+#endif
+#include <rtl/instance.hxx>
+
+namespace { struct SimpleLayoutEngine : public rtl::Static< ServerFontLayoutEngine, SimpleLayoutEngine > {}; }
+
+// =======================================================================
+// layout implementation for ServerFont
+// =======================================================================
+
+ServerFontLayout::ServerFontLayout( ServerFont& rFont )
+: mrServerFont( rFont )
+{}
+
+void ServerFontLayout::DrawText( SalGraphics& rSalGraphics ) const
+{
+ rSalGraphics.DrawServerFontLayout( *this );
+}
+
+// -----------------------------------------------------------------------
+
+bool ServerFontLayout::LayoutText( ImplLayoutArgs& rArgs )
+{
+ ServerFontLayoutEngine* pLE = NULL;
+ if( !(rArgs.mnFlags & SAL_LAYOUT_COMPLEX_DISABLED) )
+ pLE = mrServerFont.GetLayoutEngine();
+ if( !pLE )
+ pLE = &SimpleLayoutEngine::get();
+
+ bool bRet = (*pLE)( *this, rArgs );
+ return bRet;
+}
+
+// -----------------------------------------------------------------------
+
+void ServerFontLayout::AdjustLayout( ImplLayoutArgs& rArgs )
+{
+ GenericSalLayout::AdjustLayout( rArgs );
+
+ // apply asian kerning if the glyphs are not already formatted
+ if( (rArgs.mnFlags & SAL_LAYOUT_KERNING_ASIAN)
+ && !(rArgs.mnFlags & SAL_LAYOUT_VERTICAL) )
+ if( (rArgs.mpDXArray != NULL) || (rArgs.mnLayoutWidth != 0) )
+ ApplyAsianKerning( rArgs.mpStr, rArgs.mnLength );
+
+ // insert kashidas where requested by the formatting array
+ if( (rArgs.mnFlags & SAL_LAYOUT_KASHIDA_JUSTIFICATON) && rArgs.mpDXArray )
+ {
+ int nKashidaIndex = mrServerFont.GetGlyphIndex( 0x0640 );
+ if( nKashidaIndex != 0 )
+ {
+ const GlyphMetric& rGM = mrServerFont.GetGlyphMetric( nKashidaIndex );
+ KashidaJustify( nKashidaIndex, rGM.GetCharWidth() );
+ // TODO: kashida-GSUB/GPOS
+ }
+ }
+}
+
+// =======================================================================
+
+bool ServerFontLayoutEngine::operator()( ServerFontLayout& rLayout, ImplLayoutArgs& rArgs )
+{
+ ServerFont& rFont = rLayout.GetServerFont();
+
+ Point aNewPos( 0, 0 );
+ int nOldGlyphId = -1;
+ int nGlyphWidth = 0;
+ GlyphItem aPrevItem;
+ bool bRightToLeft;
+ for( int nCharPos = -1; rArgs.GetNextPos( &nCharPos, &bRightToLeft ); )
+ {
+ sal_UCS4 cChar = rArgs.mpStr[ nCharPos ];
+ if( (cChar >= 0xD800) && (cChar <= 0xDFFF) )
+ {
+ if( cChar >= 0xDC00 ) // this part of a surrogate pair was already processed
+ continue;
+ cChar = 0x10000 + ((cChar - 0xD800) << 10)
+ + (rArgs.mpStr[ nCharPos+1 ] - 0xDC00);
+ }
+
+ if( bRightToLeft )
+ cChar = GetMirroredChar( cChar );
+ int nGlyphIndex = rFont.GetGlyphIndex( cChar );
+ // when glyph fallback is needed update LayoutArgs
+ if( !nGlyphIndex ) {
+ rArgs.NeedFallback( nCharPos, bRightToLeft );
+ if( cChar >= 0x10000 ) // handle surrogate pairs
+ rArgs.NeedFallback( nCharPos+1, bRightToLeft );
+ }
+
+ // apply pair kerning to prev glyph if requested
+ if( SAL_LAYOUT_KERNING_PAIRS & rArgs.mnFlags )
+ {
+ int nKernValue = rFont.GetGlyphKernValue( nOldGlyphId, nGlyphIndex );
+ nGlyphWidth += nKernValue;
+ aPrevItem.mnNewWidth = nGlyphWidth;
+ }
+
+ // finish previous glyph
+ if( nOldGlyphId >= 0 )
+ rLayout.AppendGlyph( aPrevItem );
+ aNewPos.X() += nGlyphWidth;
+
+ // prepare GlyphItem for appending it in next round
+ nOldGlyphId = nGlyphIndex;
+ const GlyphMetric& rGM = rFont.GetGlyphMetric( nGlyphIndex );
+ nGlyphWidth = rGM.GetCharWidth();
+ int nGlyphFlags = bRightToLeft ? GlyphItem::IS_RTL_GLYPH : 0;
+ aPrevItem = GlyphItem( nCharPos, nGlyphIndex, aNewPos, nGlyphFlags, nGlyphWidth );
+ }
+
+ // append last glyph item if any
+ if( nOldGlyphId >= 0 )
+ rLayout.AppendGlyph( aPrevItem );
+
+ return true;
+}
+
+// =======================================================================
+// bridge to ICU LayoutEngine
+// =======================================================================
+
+#ifdef ENABLE_ICU_LAYOUT
+
+#define bool_t signed char
+
+// disable warnings in icu layout headers
+#if defined __SUNPRO_CC
+#pragma disable_warn
+#endif
+
+#include <layout/LayoutEngine.h>
+#include <layout/LEFontInstance.h>
+#include <layout/LEScripts.h>
+
+// enable warnings again
+#if defined __SUNPRO_CC
+#pragma enable_warn
+#endif
+
+#include <unicode/uscript.h>
+#include <unicode/ubidi.h>
+
+using namespace U_ICU_NAMESPACE;
+
+static const LEGlyphID ICU_DELETED_GLYPH = 0xFFFF;
+static const LEGlyphID ICU_MARKED_GLYPH = 0xFFFE;
+
+// -----------------------------------------------------------------------
+
+class IcuFontFromServerFont
+: public LEFontInstance
+{
+private:
+ ServerFont& mrServerFont;
+
+public:
+ IcuFontFromServerFont( ServerFont& rFont )
+ : mrServerFont( rFont )
+ {}
+
+ virtual const void* getFontTable(LETag tableTag) const;
+ virtual le_int32 getUnitsPerEM() const;
+ virtual float getXPixelsPerEm() const;
+ virtual float getYPixelsPerEm() const;
+ virtual float getScaleFactorX() const;
+ virtual float getScaleFactorY() const;
+
+ using LEFontInstance::mapCharToGlyph;
+ virtual LEGlyphID mapCharToGlyph( LEUnicode32 ch ) const;
+
+ virtual le_int32 getAscent() const;
+ virtual le_int32 getDescent() const;
+ virtual le_int32 getLeading() const;
+
+ virtual void getGlyphAdvance( LEGlyphID glyph, LEPoint &advance ) const;
+ virtual le_bool getGlyphPoint( LEGlyphID glyph, le_int32 pointNumber, LEPoint& point ) const;
+};
+
+// -----------------------------------------------------------------------
+
+const void* IcuFontFromServerFont::getFontTable( LETag nICUTableTag ) const
+{
+ char pTagName[5];
+ pTagName[0] = (char)(nICUTableTag >> 24);
+ pTagName[1] = (char)(nICUTableTag >> 16);
+ pTagName[2] = (char)(nICUTableTag >> 8);
+ pTagName[3] = (char)(nICUTableTag);
+ pTagName[4] = 0;
+
+ sal_uLong nLength;
+ const unsigned char* pBuffer = mrServerFont.GetTable( pTagName, &nLength );
+#ifdef VERBOSE_DEBUG
+ fprintf(stderr,"IcuGetTable(\"%s\") => %p\n", pTagName, pBuffer);
+ int mnHeight = mrServerFont.GetFontSelData().mnHeight;
+ const char* pName = mrServerFont.GetFontFileName()->getStr();
+ fprintf(stderr,"font( h=%d, \"%s\" )\n", mnHeight, pName );
+#endif
+ return (const void*)pBuffer;
+}
+
+// -----------------------------------------------------------------------
+
+le_int32 IcuFontFromServerFont::getUnitsPerEM() const
+{
+ return mrServerFont.GetEmUnits();
+}
+
+// -----------------------------------------------------------------------
+
+float IcuFontFromServerFont::getXPixelsPerEm() const
+{
+ const ImplFontSelectData& r = mrServerFont.GetFontSelData();
+ float fX = r.mnWidth ? r.mnWidth : r.mnHeight;
+ return fX;
+}
+
+// -----------------------------------------------------------------------
+
+float IcuFontFromServerFont::getYPixelsPerEm() const
+{
+ float fY = mrServerFont.GetFontSelData().mnHeight;
+ return fY;
+}
+
+// -----------------------------------------------------------------------
+
+float IcuFontFromServerFont::getScaleFactorX() const
+{
+ return 1.0;
+}
+
+// -----------------------------------------------------------------------
+
+float IcuFontFromServerFont::getScaleFactorY() const
+{
+ return 1.0;
+}
+
+// -----------------------------------------------------------------------
+
+LEGlyphID IcuFontFromServerFont::mapCharToGlyph( LEUnicode32 ch ) const
+{
+ LEGlyphID nGlyphIndex = mrServerFont.GetRawGlyphIndex( ch );
+ return nGlyphIndex;
+}
+
+// -----------------------------------------------------------------------
+
+le_int32 IcuFontFromServerFont::getAscent() const
+{
+ const FT_Size_Metrics& rMetrics = mrServerFont.GetMetricsFT();
+ le_int32 nAscent = (+rMetrics.ascender + 32) >> 6;
+ return nAscent;
+}
+
+// -----------------------------------------------------------------------
+
+le_int32 IcuFontFromServerFont::getDescent() const
+{
+ const FT_Size_Metrics& rMetrics = mrServerFont.GetMetricsFT();
+ le_int32 nDescent = (-rMetrics.descender + 32) >> 6;
+ return nDescent;
+}
+
+// -----------------------------------------------------------------------
+
+le_int32 IcuFontFromServerFont::getLeading() const
+{
+ const FT_Size_Metrics& rMetrics = mrServerFont.GetMetricsFT();
+ le_int32 nLeading = ((rMetrics.height - rMetrics.ascender + rMetrics.descender) + 32) >> 6;
+ return nLeading;
+}
+
+// -----------------------------------------------------------------------
+
+void IcuFontFromServerFont::getGlyphAdvance( LEGlyphID nGlyphIndex,
+ LEPoint &advance ) const
+{
+ if( (nGlyphIndex == ICU_MARKED_GLYPH)
+ || (nGlyphIndex == ICU_DELETED_GLYPH) )
+ {
+ // deleted glyph or mark glyph has not advance
+ advance.fX = 0;
+ }
+ else
+ {
+ const GlyphMetric& rGM = mrServerFont.GetGlyphMetric( nGlyphIndex );
+ advance.fX = rGM.GetCharWidth();
+ }
+
+ advance.fY = 0;
+}
+
+// -----------------------------------------------------------------------
+
+le_bool IcuFontFromServerFont::getGlyphPoint( LEGlyphID,
+ le_int32
+#if OSL_DEBUG_LEVEL > 1
+pointNumber
+#endif
+ ,
+ LEPoint& ) const
+{
+ //TODO: replace dummy implementation
+#if OSL_DEBUG_LEVEL > 1
+ fprintf(stderr,"getGlyphPoint(%d)\n", pointNumber );
+#endif
+ return false;
+}
+
+// =======================================================================
+
+class IcuLayoutEngine : public ServerFontLayoutEngine
+{
+private:
+ IcuFontFromServerFont maIcuFont;
+
+ le_int32 meScriptCode;
+ LayoutEngine* mpIcuLE;
+
+public:
+ IcuLayoutEngine( ServerFont& );
+ virtual ~IcuLayoutEngine();
+
+ virtual bool operator()( ServerFontLayout&, ImplLayoutArgs& );
+};
+
+// -----------------------------------------------------------------------
+
+IcuLayoutEngine::IcuLayoutEngine( ServerFont& rServerFont )
+: maIcuFont( rServerFont ),
+ meScriptCode( USCRIPT_INVALID_CODE ),
+ mpIcuLE( NULL )
+{}
+
+// -----------------------------------------------------------------------
+
+IcuLayoutEngine::~IcuLayoutEngine()
+{
+ if( mpIcuLE )
+ delete mpIcuLE;
+}
+
+// -----------------------------------------------------------------------
+
+static bool lcl_CharIsJoiner(sal_Unicode cChar)
+{
+ return ((cChar == 0x200C) || (cChar == 0x200D));
+}
+
+//See https://bugs.freedesktop.org/show_bug.cgi?id=31016
+#define ARABIC_BANDAID
+
+bool IcuLayoutEngine::operator()( ServerFontLayout& rLayout, ImplLayoutArgs& rArgs )
+{
+ LEUnicode* pIcuChars;
+ if( sizeof(LEUnicode) == sizeof(*rArgs.mpStr) )
+ pIcuChars = (LEUnicode*)rArgs.mpStr;
+ else
+ {
+ // this conversion will only be needed when either
+ // ICU's or OOo's unicodes stop being unsigned shorts
+ // TODO: watch out for surrogates!
+ pIcuChars = (LEUnicode*)alloca( rArgs.mnLength * sizeof(LEUnicode) );
+ for( xub_StrLen ic = 0; ic < rArgs.mnLength; ++ic )
+ pIcuChars[ic] = static_cast<LEUnicode>( rArgs.mpStr[ic] );
+ }
+
+ // allocate temporary arrays, note: round to even
+ int nGlyphCapacity = (3 * (rArgs.mnEndCharPos - rArgs.mnMinCharPos ) | 15) + 1;
+
+ struct IcuPosition{ float fX, fY; };
+ const int nAllocSize = sizeof(LEGlyphID) + sizeof(le_int32) + sizeof(IcuPosition);
+ LEGlyphID* pIcuGlyphs = (LEGlyphID*)alloca( (nGlyphCapacity * nAllocSize) + sizeof(IcuPosition) );
+ le_int32* pCharIndices = (le_int32*)((char*)pIcuGlyphs + nGlyphCapacity * sizeof(LEGlyphID) );
+ IcuPosition* pGlyphPositions = (IcuPosition*)((char*)pCharIndices + nGlyphCapacity * sizeof(le_int32) );
+
+ ServerFont& rFont = rLayout.GetServerFont();
+
+ UErrorCode rcI18n = U_ZERO_ERROR;
+ LEErrorCode rcIcu = LE_NO_ERROR;
+ Point aNewPos( 0, 0 );
+ for( int nGlyphCount = 0;; )
+ {
+ int nMinRunPos, nEndRunPos;
+ bool bRightToLeft;
+ if( !rArgs.GetNextRun( &nMinRunPos, &nEndRunPos, &bRightToLeft ) )
+ break;
+
+ // find matching script
+ // TODO: split up bidi run into script runs
+ le_int32 eScriptCode = -1;
+ for( int i = nMinRunPos; i < nEndRunPos; ++i )
+ {
+ eScriptCode = uscript_getScript( pIcuChars[i], &rcI18n );
+ if( (eScriptCode > 0) && (eScriptCode != latnScriptCode) )
+ break;
+ }
+ if( eScriptCode < 0 ) // TODO: handle errors better
+ eScriptCode = latnScriptCode;
+
+ // get layout engine matching to this script
+ // no engine change necessary if script is latin
+ if( !mpIcuLE || ((eScriptCode != meScriptCode) && (eScriptCode > USCRIPT_INHERITED)) )
+ {
+ // TODO: cache multiple layout engines when multiple scripts are used
+ delete mpIcuLE;
+ meScriptCode = eScriptCode;
+ le_int32 eLangCode = 0; // TODO: get better value
+ mpIcuLE = LayoutEngine::layoutEngineFactory( &maIcuFont, eScriptCode, eLangCode, rcIcu );
+ if( LE_FAILURE(rcIcu) )
+ {
+ delete mpIcuLE;
+ mpIcuLE = NULL;
+ }
+ }
+
+ // fall back to default layout if needed
+ if( !mpIcuLE )
+ break;
+
+ // run ICU layout engine
+ // TODO: get enough context, remove extra glyps below
+ int nRawRunGlyphCount = mpIcuLE->layoutChars( pIcuChars,
+ nMinRunPos, nEndRunPos - nMinRunPos, rArgs.mnLength,
+ bRightToLeft, aNewPos.X(), aNewPos.Y(), rcIcu );
+ if( LE_FAILURE(rcIcu) )
+ return false;
+
+ // import layout info from icu
+ mpIcuLE->getGlyphs( pIcuGlyphs, rcIcu );
+ mpIcuLE->getCharIndices( pCharIndices, rcIcu );
+ mpIcuLE->getGlyphPositions( &pGlyphPositions->fX, rcIcu );
+ mpIcuLE->reset(); // TODO: get rid of this, PROBLEM: crash at exit when removed
+ if( LE_FAILURE(rcIcu) )
+ return false;
+
+ // layout bidi/script runs and export them to a ServerFontLayout
+ // convert results to GlyphItems
+ int nLastCharPos = -1;
+ int nClusterMinPos = -1;
+ int nClusterMaxPos = -1;
+ bool bClusterStart = true;
+ int nFilteredRunGlyphCount = 0;
+ const IcuPosition* pPos = pGlyphPositions;
+ for( int i = 0; i < nRawRunGlyphCount; ++i, ++pPos )
+ {
+ LEGlyphID nGlyphIndex = pIcuGlyphs[i];
+ // ignore glyphs which were marked or deleted by ICU
+ if( (nGlyphIndex == ICU_MARKED_GLYPH)
+ || (nGlyphIndex == ICU_DELETED_GLYPH) )
+ continue;
+
+ // adjust the relative char pos
+ int nCharPos = pCharIndices[i];
+ if( nCharPos >= 0 ) {
+ nCharPos += nMinRunPos;
+ // ICU seems to return bad pCharIndices
+ // for some combinations of ICU+font+text
+ // => better give up now than crash later
+ if( nCharPos >= nEndRunPos )
+ continue;
+ }
+
+ // if needed request glyph fallback by updating LayoutArgs
+ if( !nGlyphIndex )
+ {
+ if( nCharPos >= 0 )
+ {
+ rArgs.NeedFallback( nCharPos, bRightToLeft );
+ if ( (nCharPos > 0) && lcl_CharIsJoiner(rArgs.mpStr[nCharPos-1]) )
+ rArgs.NeedFallback( nCharPos-1, bRightToLeft );
+ else if ( (nCharPos + 1 < nEndRunPos) && lcl_CharIsJoiner(rArgs.mpStr[nCharPos+1]) )
+ rArgs.NeedFallback( nCharPos+1, bRightToLeft );
+ }
+
+ if( SAL_LAYOUT_FOR_FALLBACK & rArgs.mnFlags )
+ continue;
+ }
+
+
+ // apply vertical flags, etc.
+ bool bDiacritic = false;
+ if( nCharPos >= 0 )
+ {
+ sal_UCS4 aChar = rArgs.mpStr[ nCharPos ];
+ nGlyphIndex = rFont.FixupGlyphIndex( nGlyphIndex, aChar );
+
+ // #i99367# HACK: try to detect all diacritics
+ if( aChar>=0x0300 && aChar<0x2100 )
+ bDiacritic = IsDiacritic( aChar );
+ }
+
+ // get glyph position and its metrics
+ aNewPos = Point( (int)(pPos->fX+0.5), (int)(pPos->fY+0.5) );
+ const GlyphMetric& rGM = rFont.GetGlyphMetric( nGlyphIndex );
+ int nGlyphWidth = rGM.GetCharWidth();
+ int nNewWidth = nGlyphWidth;
+ if( nGlyphWidth <= 0 )
+ bDiacritic |= true;
+ // #i99367# force all diacritics to zero width
+ // TODO: we need mnOrigWidth/mnLogicWidth/mnNewWidth
+ else if( bDiacritic )
+ nGlyphWidth = nNewWidth = 0;
+ else
+ {
+ // Hack, find next +ve width glyph and calculate current
+ // glyph width by substracting the two posituons
+ const IcuPosition* pNextPos = pPos+1;
+ for ( int j = i + 1; j <= nRawRunGlyphCount; ++j, ++pNextPos )
+ {
+ if ( j == nRawRunGlyphCount )
+ {
+ nNewWidth = static_cast<int>(pNextPos->fX - pPos->fX);
+ break;
+ }
+
+ LEGlyphID nNextGlyphIndex = pIcuGlyphs[j];
+ if( (nNextGlyphIndex == ICU_MARKED_GLYPH)
+ || (nNextGlyphIndex == ICU_DELETED_GLYPH) )
+ continue;
+
+ const GlyphMetric& rNextGM = rFont.GetGlyphMetric( nNextGlyphIndex );
+ int nNextGlyphWidth = rNextGM.GetCharWidth();
+ if ( nNextGlyphWidth > 0 )
+ {
+ nNewWidth = static_cast<int>(pNextPos->fX - pPos->fX);
+ break;
+ }
+ }
+ }
+
+ // heuristic to detect glyph clusters
+ bool bInCluster = true;
+ if( nLastCharPos == -1 )
+ {
+ nClusterMinPos = nClusterMaxPos = nCharPos;
+ bInCluster = false;
+ }
+ else if( !bRightToLeft )
+ {
+ // left-to-right case
+ if( nClusterMinPos > nCharPos )
+ nClusterMinPos = nCharPos; // extend cluster
+ else if( nCharPos <= nClusterMaxPos )
+ /*NOTHING*/; // inside cluster
+ else if( bDiacritic )
+ nClusterMaxPos = nCharPos; // add diacritic to cluster
+ else {
+ nClusterMinPos = nClusterMaxPos = nCharPos; // new cluster
+ bInCluster = false;
+ }
+ }
+ else
+ {
+ // right-to-left case
+ if( nClusterMaxPos < nCharPos )
+ nClusterMaxPos = nCharPos; // extend cluster
+ else if( nCharPos >= nClusterMinPos )
+ /*NOTHING*/; // inside cluster
+ else if( bDiacritic )
+ {
+ nClusterMinPos = nCharPos; // ICU often has [diacritic* baseglyph*]
+ if( bClusterStart ) {
+ nClusterMaxPos = nCharPos;
+ bInCluster = false;
+ }
+ }
+ else
+ {
+ nClusterMinPos = nClusterMaxPos = nCharPos; // new cluster
+ bInCluster = !bClusterStart;
+ }
+ }
+
+ long nGlyphFlags = 0;
+ if( bInCluster )
+ nGlyphFlags |= GlyphItem::IS_IN_CLUSTER;
+ if( bRightToLeft )
+ nGlyphFlags |= GlyphItem::IS_RTL_GLYPH;
+ if( bDiacritic )
+ nGlyphFlags |= GlyphItem::IS_DIACRITIC;
+
+ // add resulting glyph item to layout
+ GlyphItem aGI( nCharPos, nGlyphIndex, aNewPos, nGlyphFlags, nGlyphWidth );
+#ifdef ARABIC_BANDAID
+ aGI.mnNewWidth = nNewWidth;
+#endif
+ rLayout.AppendGlyph( aGI );
+ ++nFilteredRunGlyphCount;
+ nLastCharPos = nCharPos;
+ bClusterStart = !aGI.IsDiacritic(); // TODO: only needed in RTL-codepath
+ }
+ aNewPos = Point( (int)(pPos->fX+0.5), (int)(pPos->fY+0.5) );
+ nGlyphCount += nFilteredRunGlyphCount;
+ }
+
+ // sort glyphs in visual order
+ // and then in logical order (e.g. diacritics after cluster start)
+ rLayout.SortGlyphItems();
+
+ // determine need for kashida justification
+ if( (rArgs.mpDXArray || rArgs.mnLayoutWidth)
+ && ((meScriptCode == arabScriptCode) || (meScriptCode == syrcScriptCode)) )
+ rArgs.mnFlags |= SAL_LAYOUT_KASHIDA_JUSTIFICATON;
+
+ return true;
+}
+
+#endif // ENABLE_ICU_LAYOUT
+
+// =======================================================================
+
+ServerFontLayoutEngine* ServerFont::GetLayoutEngine()
+{
+ // find best layout engine for font, platform, script and language
+#ifdef ENABLE_ICU_LAYOUT
+ if( !mpLayoutEngine && FT_IS_SFNT( maFaceFT ) )
+ mpLayoutEngine = new IcuLayoutEngine( *this );
+#endif // ENABLE_ICU_LAYOUT
+
+ return mpLayoutEngine;
+}
+
+// =======================================================================
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/vcl/generic/glyphs/gcach_rbmp.cxx b/vcl/generic/glyphs/gcach_rbmp.cxx
new file mode 100644
index 000000000000..a5dd5aebacf1
--- /dev/null
+++ b/vcl/generic/glyphs/gcach_rbmp.cxx
@@ -0,0 +1,277 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*************************************************************************
+ *
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * Copyright 2000, 2010 Oracle and/or its affiliates.
+ *
+ * OpenOffice.org - a multi-platform office productivity suite
+ *
+ * This file is part of OpenOffice.org.
+ *
+ * OpenOffice.org is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License version 3
+ * only, as published by the Free Software Foundation.
+ *
+ * OpenOffice.org is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License version 3 for more details
+ * (a copy is included in the LICENSE file that accompanied this code).
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * version 3 along with OpenOffice.org. If not, see
+ * <http://www.openoffice.org/license.html>
+ * for a copy of the LGPLv3 License.
+ *
+ ************************************************************************/
+
+// MARKER(update_precomp.py): autogen include statement, do not remove
+#include "precompiled_vcl.hxx"
+
+#include "generic/glyphcache.hxx"
+#include <string.h>
+
+//------------------------------------------------------------------------
+
+RawBitmap::RawBitmap()
+: mpBits(0), mnAllocated(0)
+{}
+
+//------------------------------------------------------------------------
+
+RawBitmap::~RawBitmap()
+{
+ delete[] mpBits;
+ mpBits = 0;
+ mnAllocated = 0;
+}
+
+//------------------------------------------------------------------------
+
+// used by 90 and 270 degree rotations on 8 bit deep bitmaps
+static void ImplRotate8_90( unsigned char* p1, const unsigned char* p2,
+ int xmax, int ymax, int dx, int dy, int nPad )
+{
+ for( int y = ymax; --y >= 0; p2 += dy )
+ {
+ for( int x = xmax; --x >= 0; p2 += dx )
+ *(p1++) = *p2;
+ for( int i = nPad; --i >= 0; )
+ *(p1++) = 0;
+ }
+}
+
+//------------------------------------------------------------------------
+
+// used by inplace 180 degree rotation on 8 bit deep bitmaps
+static void ImplRotate8_180( unsigned char* p1, int xmax, int ymax, int nPad )
+{
+ unsigned char* p2 = p1 + ymax * (xmax + nPad);
+ for( int y = ymax/2; --y >= 0; )
+ {
+ p2 -= nPad;
+ for( int x = xmax; --x >= 0; )
+ {
+ unsigned char cTmp = *(--p2);
+ *p2 = *p1;
+ *(p1++) = cTmp;
+ }
+ p1 += nPad;
+ }
+
+ // reverse middle line
+ p2 -= nPad;
+ while( p1 < p2 )
+ {
+ unsigned char cTmp = *(--p2);
+ *p2 = *p1;
+ *(p1++) = cTmp;
+ }
+}
+
+//------------------------------------------------------------------------
+
+// used by 90 or 270 degree rotations on 1 bit deep bitmaps
+static void ImplRotate1_90( unsigned char* p1, const unsigned char* p2,
+ int xmax, int ymax, int dx, int nShift, int nDeltaShift, int nPad )
+{
+ for( int y = ymax; --y >= 0; )
+ {
+ unsigned nTemp = 1;
+ const unsigned char* p20 = p2;
+ for( int x = xmax; --x >= 0; p2 += dx )
+ {
+ // build bitwise and store when byte finished
+ nTemp += nTemp + ((*p2 >> nShift) & 1);
+ if( nTemp >= 0x100U )
+ {
+ *(p1++) = (unsigned char)nTemp;
+ nTemp = 1;
+ }
+ }
+ p2 = p20;
+
+ // store left aligned remainder if needed
+ if( nTemp > 1 )
+ {
+ for(; nTemp < 0x100U; nTemp += nTemp ) ;
+ *(p1++) = (unsigned char)nTemp;
+ }
+ // pad scanline with zeroes
+ for( int i = nPad; --i >= 0;)
+ *(p1++) = 0;
+
+ // increase/decrease shift, but keep bound inside 0 to 7
+ nShift += nDeltaShift;
+ if( nShift != (nShift & 7) )
+ p2 -= nDeltaShift;
+ nShift &= 7;
+ }
+}
+
+//------------------------------------------------------------------------
+
+// used by 180 degrees rotations on 1 bit deep bitmaps
+static void ImplRotate1_180( unsigned char* p1, const unsigned char* p2,
+ int xmax, int ymax, int nPad )
+{
+ --p2;
+ for( int y = ymax; --y >= 0; )
+ {
+ p2 -= nPad;
+
+ unsigned nTemp = 1;
+ unsigned nInp = (0x100 + *p2) >> (-xmax & 7);
+ for( int x = xmax; --x >= 0; )
+ {
+ // build bitwise and store when byte finished
+ nTemp += nTemp + (nInp & 1);
+ if( nTemp >= 0x100 )
+ {
+ *(p1++) = (unsigned char)nTemp;
+ nTemp = 1;
+ }
+ // update input byte if needed (and available)
+ if( (nInp >>= 1) <= 1 && ((y != 0) || (x != 0)) )
+ nInp = 0x100 + *(--p2);
+ }
+
+ // store left aligned remainder if needed
+ if( nTemp > 1 )
+ {
+ for(; nTemp < 0x100; nTemp += nTemp ) ;
+ *(p1++) = (unsigned char)nTemp;
+ }
+ // scanline pad is already clean
+ p1 += nPad;
+ }
+}
+
+//------------------------------------------------------------------------
+
+bool RawBitmap::Rotate( int nAngle )
+{
+ sal_uLong nNewScanlineSize = 0;
+ sal_uLong nNewHeight = 0;
+ sal_uLong nNewWidth = 0;
+
+ // do inplace rotation or prepare double buffered rotation
+ switch( nAngle )
+ {
+ case 0: // nothing to do
+ case 3600:
+ return true;
+ default: // non rectangular angles not allowed
+ return false;
+ case 1800: // rotate by 180 degrees
+ mnXOffset = -(mnXOffset + mnWidth);
+ mnYOffset = -(mnYOffset + mnHeight);
+ if( mnBitCount == 8 )
+ {
+ ImplRotate8_180( mpBits, mnWidth, mnHeight, mnScanlineSize-mnWidth );
+ return true;
+ }
+ nNewWidth = mnWidth;
+ nNewHeight = mnHeight;
+ nNewScanlineSize = mnScanlineSize;
+ break;
+ case +900: // left by 90 degrees
+ case -900:
+ case 2700: // right by 90 degrees
+ nNewWidth = mnHeight;
+ nNewHeight = mnWidth;
+ if( mnBitCount==1 )
+ nNewScanlineSize = (nNewWidth + 7) / 8;
+ else
+ nNewScanlineSize = (nNewWidth + 3) & -4;
+ break;
+ }
+
+ unsigned int nBufSize = nNewHeight * nNewScanlineSize;
+ unsigned char* pBuf = new unsigned char[ nBufSize ];
+ if( !pBuf )
+ return false;
+
+ memset( pBuf, 0, nBufSize );
+ int i;
+
+ // dispatch non-inplace rotations
+ switch( nAngle )
+ {
+ case 1800: // rotate by 180 degrees
+ // we know we only need to deal with 1 bit depth
+ ImplRotate1_180( pBuf, mpBits + mnHeight * mnScanlineSize,
+ mnWidth, mnHeight, mnScanlineSize - (mnWidth + 7) / 8 );
+ break;
+ case +900: // rotate left by 90 degrees
+ i = mnXOffset;
+ mnXOffset = mnYOffset;
+ mnYOffset = -nNewHeight - i;
+ if( mnBitCount == 8 )
+ ImplRotate8_90( pBuf, mpBits + mnWidth - 1,
+ nNewWidth, nNewHeight, +mnScanlineSize, -1-mnHeight*mnScanlineSize,
+ nNewScanlineSize - nNewWidth );
+ else
+ ImplRotate1_90( pBuf, mpBits + (mnWidth - 1) / 8,
+ nNewWidth, nNewHeight, +mnScanlineSize,
+ (-mnWidth & 7), +1, nNewScanlineSize - (nNewWidth + 7) / 8 );
+ break;
+ case 2700: // rotate right by 90 degrees
+ case -900:
+ i = mnXOffset;
+ mnXOffset = -(nNewWidth + mnYOffset);
+ mnYOffset = i;
+ if( mnBitCount == 8 )
+ ImplRotate8_90( pBuf, mpBits + mnScanlineSize * (mnHeight-1),
+ nNewWidth, nNewHeight, -mnScanlineSize, +1+mnHeight*mnScanlineSize,
+ nNewScanlineSize - nNewWidth );
+ else
+ ImplRotate1_90( pBuf, mpBits + mnScanlineSize * (mnHeight-1),
+ nNewWidth, nNewHeight, -mnScanlineSize,
+ +7, -1, nNewScanlineSize - (nNewWidth + 7) / 8 );
+ break;
+ }
+
+ mnWidth = nNewWidth;
+ mnHeight = nNewHeight;
+ mnScanlineSize = nNewScanlineSize;
+
+ if( nBufSize < mnAllocated )
+ {
+ memcpy( mpBits, pBuf, nBufSize );
+ delete[] pBuf;
+ }
+ else
+ {
+ delete[] mpBits;
+ mpBits = pBuf;
+ mnAllocated = nBufSize;
+ }
+
+ return true;
+}
+
+//------------------------------------------------------------------------
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/vcl/generic/glyphs/glyphcache.cxx b/vcl/generic/glyphs/glyphcache.cxx
new file mode 100644
index 000000000000..5322b6502310
--- /dev/null
+++ b/vcl/generic/glyphs/glyphcache.cxx
@@ -0,0 +1,503 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*************************************************************************
+ *
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * Copyright 2000, 2010 Oracle and/or its affiliates.
+ *
+ * OpenOffice.org - a multi-platform office productivity suite
+ *
+ * This file is part of OpenOffice.org.
+ *
+ * OpenOffice.org is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License version 3
+ * only, as published by the Free Software Foundation.
+ *
+ * OpenOffice.org is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License version 3 for more details
+ * (a copy is included in the LICENSE file that accompanied this code).
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * version 3 along with OpenOffice.org. If not, see
+ * <http://www.openoffice.org/license.html>
+ * for a copy of the LGPLv3 License.
+ *
+ ************************************************************************/
+
+// MARKER(update_precomp.py): autogen include statement, do not remove
+#include "precompiled_vcl.hxx"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <math.h>
+#include <vcl/salbtype.hxx>
+#include <gcach_ftyp.hxx>
+
+#include <vcl/svapp.hxx>
+#include <vcl/bitmap.hxx>
+#include <outfont.hxx>
+
+#ifdef ENABLE_GRAPHITE
+#include <graphite_features.hxx>
+#endif
+
+#include <rtl/ustring.hxx> // used only for string=>hashvalue
+#include <osl/file.hxx>
+#include <tools/debug.hxx>
+
+// =======================================================================
+// GlyphCache
+// =======================================================================
+
+static GlyphCache* pInstance = NULL;
+
+GlyphCache::GlyphCache( GlyphCachePeer& rPeer )
+: mrPeer( rPeer ),
+ mnMaxSize( 1500000 ),
+ mnBytesUsed(sizeof(GlyphCache)),
+ mnLruIndex(0),
+ mnGlyphCount(0),
+ mpCurrentGCFont(NULL),
+ mpFtManager(NULL)
+{
+ pInstance = this;
+ mpFtManager = new FreetypeManager;
+}
+
+// -----------------------------------------------------------------------
+
+GlyphCache::~GlyphCache()
+{
+ InvalidateAllGlyphs();
+ for( FontList::iterator it = maFontList.begin(), end = maFontList.end(); it != end; ++it )
+ {
+ ServerFont* pServerFont = it->second;
+ mrPeer.RemovingFont(*pServerFont);
+ delete pServerFont;
+ }
+ if( mpFtManager )
+ delete mpFtManager;
+}
+
+// -----------------------------------------------------------------------
+
+void GlyphCache::InvalidateAllGlyphs()
+{
+ // an application about to exit can omit garbage collecting the heap
+ // since it makes things slower and introduces risks if the heap was not perfect
+ // for debugging, for memory grinding or leak checking the env allows to force GC
+ const char* pEnv = getenv( "SAL_FORCE_GC_ON_EXIT" );
+ if( pEnv && (*pEnv != '0') )
+ {
+ // uncache of all glyph shapes and metrics
+ for( FontList::iterator it = maFontList.begin(); it != maFontList.end(); ++it )
+ delete const_cast<ServerFont*>( it->second );
+ maFontList.clear();
+ mpCurrentGCFont = NULL;
+ }
+}
+
+// -----------------------------------------------------------------------
+
+inline
+size_t GlyphCache::IFSD_Hash::operator()( const ImplFontSelectData& rFontSelData ) const
+{
+ // TODO: is it worth to improve this hash function?
+ sal_IntPtr nFontId = reinterpret_cast<sal_IntPtr>( rFontSelData.mpFontData );
+#ifdef ENABLE_GRAPHITE
+ if (rFontSelData.maTargetName.Search(grutils::GrFeatureParser::FEAT_PREFIX)
+ != STRING_NOTFOUND)
+ {
+ rtl::OString aFeatName = rtl::OUStringToOString( rFontSelData.maTargetName, RTL_TEXTENCODING_UTF8 );
+ nFontId ^= aFeatName.hashCode();
+ }
+#endif
+ size_t nHash = nFontId << 8;
+ nHash += rFontSelData.mnHeight;
+ nHash += rFontSelData.mnOrientation;
+ nHash += rFontSelData.mbVertical;
+ nHash += rFontSelData.meItalic;
+ nHash += rFontSelData.meWeight;
+#ifdef ENABLE_GRAPHITE
+ nHash += rFontSelData.meLanguage;
+#endif
+ return nHash;
+}
+
+// -----------------------------------------------------------------------
+
+bool GlyphCache::IFSD_Equal::operator()( const ImplFontSelectData& rA, const ImplFontSelectData& rB) const
+{
+ // check font ids
+ sal_IntPtr nFontIdA = reinterpret_cast<sal_IntPtr>( rA.mpFontData );
+ sal_IntPtr nFontIdB = reinterpret_cast<sal_IntPtr>( rB.mpFontData );
+ if( nFontIdA != nFontIdB )
+ return false;
+
+ // compare with the requested metrics
+ if( (rA.mnHeight != rB.mnHeight)
+ || (rA.mnOrientation != rB.mnOrientation)
+ || (rA.mbVertical != rB.mbVertical)
+ || (rA.mbNonAntialiased != rB.mbNonAntialiased) )
+ return false;
+
+ if( (rA.meItalic != rB.meItalic)
+ || (rA.meWeight != rB.meWeight) )
+ return false;
+
+ // NOTE: ignoring meFamily deliberately
+
+ // compare with the requested width, allow default width
+ if( (rA.mnWidth != rB.mnWidth)
+ && ((rA.mnHeight != rB.mnWidth) || (rA.mnWidth != 0)) )
+ return false;
+#ifdef ENABLE_GRAPHITE
+ if (rA.meLanguage != rB.meLanguage)
+ return false;
+ // check for features
+ if ((rA.maTargetName.Search(grutils::GrFeatureParser::FEAT_PREFIX)
+ != STRING_NOTFOUND ||
+ rB.maTargetName.Search(grutils::GrFeatureParser::FEAT_PREFIX)
+ != STRING_NOTFOUND) && rA.maTargetName != rB.maTargetName)
+ return false;
+#endif
+ return true;
+}
+
+// -----------------------------------------------------------------------
+
+GlyphCache& GlyphCache::GetInstance()
+{
+ return *pInstance;
+}
+
+// -----------------------------------------------------------------------
+
+void GlyphCache::AddFontFile( const rtl::OString& rNormalizedName, int nFaceNum,
+ sal_IntPtr nFontId, const ImplDevFontAttributes& rDFA, const ExtraKernInfo* pExtraKern )
+{
+ if( mpFtManager )
+ mpFtManager->AddFontFile( rNormalizedName, nFaceNum, nFontId, rDFA, pExtraKern );
+}
+
+// -----------------------------------------------------------------------
+
+void GlyphCache::AnnounceFonts( ImplDevFontList* pList ) const
+{
+ if( mpFtManager )
+ mpFtManager->AnnounceFonts( pList );
+}
+
+// -----------------------------------------------------------------------
+
+ServerFont* GlyphCache::CacheFont( const ImplFontSelectData& rFontSelData )
+{
+ // a serverfont request has pFontData
+ if( rFontSelData.mpFontData == NULL )
+ return NULL;
+ // a serverfont request has a fontid > 0
+ sal_IntPtr nFontId = rFontSelData.mpFontData->GetFontId();
+ if( nFontId <= 0 )
+ return NULL;
+
+ // the FontList's key mpFontData member is reinterpreted as font id
+ ImplFontSelectData aFontSelData = rFontSelData;
+ aFontSelData.mpFontData = reinterpret_cast<ImplFontData*>( nFontId );
+ FontList::iterator it = maFontList.find( aFontSelData );
+ if( it != maFontList.end() )
+ {
+ ServerFont* pFound = it->second;
+ if( pFound )
+ pFound->AddRef();
+ return pFound;
+ }
+
+ // font not cached yet => create new font item
+ ServerFont* pNew = NULL;
+ if( mpFtManager )
+ pNew = mpFtManager->CreateFont( aFontSelData );
+
+ if( pNew )
+ {
+ maFontList[ aFontSelData ] = pNew;
+ mnBytesUsed += pNew->GetByteCount();
+
+ // enable garbage collection for new font
+ if( !mpCurrentGCFont )
+ {
+ mpCurrentGCFont = pNew;
+ pNew->mpNextGCFont = pNew;
+ pNew->mpPrevGCFont = pNew;
+ }
+ else
+ {
+ pNew->mpNextGCFont = mpCurrentGCFont;
+ pNew->mpPrevGCFont = mpCurrentGCFont->mpPrevGCFont;
+ pNew->mpPrevGCFont->mpNextGCFont = pNew;
+ mpCurrentGCFont->mpPrevGCFont = pNew;
+ }
+ }
+
+ return pNew;
+}
+
+// -----------------------------------------------------------------------
+
+void GlyphCache::UncacheFont( ServerFont& rServerFont )
+{
+ // the interface for rServerFont must be const because a
+ // user who wants to release it only got const ServerFonts.
+ // The caching algorithm needs a non-const object
+ ServerFont* pFont = const_cast<ServerFont*>( &rServerFont );
+ if( (pFont->Release() <= 0)
+ && (mnMaxSize <= (mnBytesUsed + mrPeer.GetByteCount())) )
+ {
+ mpCurrentGCFont = pFont;
+ GarbageCollect();
+ }
+}
+
+// -----------------------------------------------------------------------
+
+void GlyphCache::GarbageCollect()
+{
+ // when current GC font has been destroyed get another one
+ if( !mpCurrentGCFont )
+ {
+ FontList::iterator it = maFontList.begin();
+ if( it != maFontList.end() )
+ mpCurrentGCFont = it->second;
+ }
+
+ // unless there is no other font to collect
+ if( !mpCurrentGCFont )
+ return;
+
+ // prepare advance to next font for garbage collection
+ ServerFont* const pServerFont = mpCurrentGCFont;
+ mpCurrentGCFont = pServerFont->mpNextGCFont;
+
+ if( (pServerFont == mpCurrentGCFont) // no other fonts
+ || (pServerFont->GetRefCount() > 0) ) // font still used
+ {
+ // try to garbage collect at least a few bytes
+ pServerFont->GarbageCollect( mnLruIndex - mnGlyphCount/2 );
+ }
+ else // current GC font is unreferenced
+ {
+ DBG_ASSERT( (pServerFont->GetRefCount() == 0),
+ "GlyphCache::GC detected RefCount underflow" );
+
+ // free all pServerFont related data
+ pServerFont->GarbageCollect( mnLruIndex+0x10000000 );
+ if( pServerFont == mpCurrentGCFont )
+ mpCurrentGCFont = NULL;
+ const ImplFontSelectData& rIFSD = pServerFont->GetFontSelData();
+ maFontList.erase( rIFSD );
+ mrPeer.RemovingFont( *pServerFont );
+ mnBytesUsed -= pServerFont->GetByteCount();
+
+ // remove font from list of garbage collected fonts
+ if( pServerFont->mpPrevGCFont )
+ pServerFont->mpPrevGCFont->mpNextGCFont = pServerFont->mpNextGCFont;
+ if( pServerFont->mpNextGCFont )
+ pServerFont->mpNextGCFont->mpPrevGCFont = pServerFont->mpPrevGCFont;
+ if( pServerFont == mpCurrentGCFont )
+ mpCurrentGCFont = NULL;
+
+ delete pServerFont;
+ }
+}
+
+// -----------------------------------------------------------------------
+
+inline void GlyphCache::UsingGlyph( ServerFont&, GlyphData& rGlyphData )
+{
+ rGlyphData.SetLruValue( mnLruIndex++ );
+}
+
+// -----------------------------------------------------------------------
+
+inline void GlyphCache::AddedGlyph( ServerFont& rServerFont, GlyphData& rGlyphData )
+{
+ ++mnGlyphCount;
+ mnBytesUsed += sizeof( rGlyphData );
+ UsingGlyph( rServerFont, rGlyphData );
+ GrowNotify();
+}
+
+// -----------------------------------------------------------------------
+
+void GlyphCache::GrowNotify()
+{
+ if( (mnBytesUsed + mrPeer.GetByteCount()) > mnMaxSize )
+ GarbageCollect();
+}
+
+// -----------------------------------------------------------------------
+
+inline void GlyphCache::RemovingGlyph( ServerFont& rSF, GlyphData& rGD, int nGlyphIndex )
+{
+ mrPeer.RemovingGlyph( rSF, rGD, nGlyphIndex );
+ mnBytesUsed -= sizeof( GlyphData );
+ --mnGlyphCount;
+}
+
+// -----------------------------------------------------------------------
+
+void ServerFont::ReleaseFromGarbageCollect()
+{
+ // remove from GC list
+ ServerFont* pPrev = mpPrevGCFont;
+ ServerFont* pNext = mpNextGCFont;
+ if( pPrev ) pPrev->mpNextGCFont = pNext;
+ if( pNext ) pNext->mpPrevGCFont = pPrev;
+ mpPrevGCFont = NULL;
+ mpNextGCFont = NULL;
+}
+
+// -----------------------------------------------------------------------
+
+long ServerFont::Release() const
+{
+ DBG_ASSERT( mnRefCount > 0, "ServerFont: RefCount underflow" );
+ return --mnRefCount;
+}
+
+// -----------------------------------------------------------------------
+
+GlyphData& ServerFont::GetGlyphData( int nGlyphIndex )
+{
+ // usually the GlyphData is cached
+ GlyphList::iterator it = maGlyphList.find( nGlyphIndex );
+ if( it != maGlyphList.end() ) {
+ GlyphData& rGlyphData = it->second;
+ GlyphCache::GetInstance().UsingGlyph( *this, rGlyphData );
+ return rGlyphData;
+ }
+
+ // sometimes not => we need to create and initialize it ourselves
+ GlyphData& rGlyphData = maGlyphList[ nGlyphIndex ];
+ mnBytesUsed += sizeof( GlyphData );
+ InitGlyphData( nGlyphIndex, rGlyphData );
+ GlyphCache::GetInstance().AddedGlyph( *this, rGlyphData );
+ return rGlyphData;
+}
+
+// -----------------------------------------------------------------------
+
+void ServerFont::GarbageCollect( long nMinLruIndex )
+{
+ GlyphList::iterator it_next = maGlyphList.begin();
+ while( it_next != maGlyphList.end() )
+ {
+ GlyphList::iterator it = it_next++;
+ GlyphData& rGD = it->second;
+ if( (nMinLruIndex - rGD.GetLruValue()) > 0 )
+ {
+ OSL_ASSERT( mnBytesUsed >= sizeof(GlyphData) );
+ mnBytesUsed -= sizeof( GlyphData );
+ GlyphCache::GetInstance().RemovingGlyph( *this, rGD, it->first );
+ maGlyphList.erase( it );
+ it_next = maGlyphList.begin();
+ }
+ }
+}
+
+bool ServerFont::IsGlyphInvisible( int nGlyphIndex )
+{
+ if (!mbCollectedZW)
+ {
+ mnZWJ = GetGlyphIndex( 0x200D );
+ mnZWNJ = GetGlyphIndex( 0x200C );
+ mbCollectedZW = true;
+ }
+
+ if( !nGlyphIndex ) // don't hide the NotDef glyph
+ return false;
+ if( (nGlyphIndex == mnZWNJ) || (nGlyphIndex == mnZWJ) )
+ return true;
+
+ return false;
+}
+
+// =======================================================================
+
+ImplServerFontEntry::ImplServerFontEntry( ImplFontSelectData& rFSD )
+: ImplFontEntry( rFSD )
+, mpServerFont( NULL )
+, mbGotFontOptions( false )
+{}
+
+// -----------------------------------------------------------------------
+
+ImplServerFontEntry::~ImplServerFontEntry()
+{
+ // TODO: remove the ServerFont here instead of in the GlyphCache
+}
+
+// =======================================================================
+
+ExtraKernInfo::ExtraKernInfo( sal_IntPtr nFontId )
+: mbInitialized( false ),
+ mnFontId( nFontId ),
+ maUnicodeKernPairs( 0 )
+{}
+
+//--------------------------------------------------------------------------
+
+bool ExtraKernInfo::HasKernPairs() const
+{
+ if( !mbInitialized )
+ Initialize();
+ return !maUnicodeKernPairs.empty();
+}
+
+//--------------------------------------------------------------------------
+
+int ExtraKernInfo::GetUnscaledKernPairs( ImplKernPairData** ppKernPairs ) const
+{
+ if( !mbInitialized )
+ Initialize();
+
+ // return early if no kerning available
+ if( maUnicodeKernPairs.empty() )
+ return 0;
+
+ // allocate kern pair table
+ int nKernCount = maUnicodeKernPairs.size();
+ *ppKernPairs = new ImplKernPairData[ nKernCount ];
+
+ // fill in unicode kern pairs with the kern value scaled to the font width
+ ImplKernPairData* pKernData = *ppKernPairs;
+ UnicodeKernPairs::const_iterator it = maUnicodeKernPairs.begin();
+ for(; it != maUnicodeKernPairs.end(); ++it )
+ *(pKernData++) = *it;
+
+ return nKernCount;
+}
+
+//--------------------------------------------------------------------------
+
+int ExtraKernInfo::GetUnscaledKernValue( sal_Unicode cLeft, sal_Unicode cRight ) const
+{
+ if( !mbInitialized )
+ Initialize();
+
+ if( maUnicodeKernPairs.empty() )
+ return 0;
+
+ ImplKernPairData aKernPair = { cLeft, cRight, 0 };
+ UnicodeKernPairs::const_iterator it = maUnicodeKernPairs.find( aKernPair );
+ if( it == maUnicodeKernPairs.end() )
+ return 0;
+
+ int nUnscaledValue = (*it).mnKern;
+ return nUnscaledValue;
+}
+
+// =======================================================================
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/vcl/generic/glyphs/graphite_serverfont.cxx b/vcl/generic/glyphs/graphite_serverfont.cxx
new file mode 100644
index 000000000000..fd5babf5efe8
--- /dev/null
+++ b/vcl/generic/glyphs/graphite_serverfont.cxx
@@ -0,0 +1,154 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*************************************************************************
+ *
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * Copyright 2000, 2010 Oracle and/or its affiliates.
+ *
+ * OpenOffice.org - a multi-platform office productivity suite
+ *
+ * This file is part of OpenOffice.org.
+ *
+ * OpenOffice.org is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License version 3
+ * only, as published by the Free Software Foundation.
+ *
+ * OpenOffice.org is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License version 3 for more details
+ * (a copy is included in the LICENSE file that accompanied this code).
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * version 3 along with OpenOffice.org. If not, see
+ * <http://www.openoffice.org/license.html>
+ * for a copy of the LGPLv3 License.
+ *
+ ************************************************************************/
+
+// MARKER(update_precomp.py): autogen include statement, do not remove
+#include "precompiled_vcl.hxx"
+
+// We need this to enable namespace support in libgrengine headers.
+#define GR_NAMESPACE
+
+// Header files
+//
+
+// Platform
+#include <i18npool/mslangid.hxx>
+#include <sallayout.hxx>
+// Module
+#include "gcach_ftyp.hxx"
+#include "generic/glyphcache.hxx"
+#include <graphite_features.hxx>
+#include <graphite_serverfont.hxx>
+
+float freetypeServerFontAdvance(const void* appFontHandle, gr_uint16 glyphId)
+{
+ ServerFont * pServerFont =
+ const_cast<ServerFont*>
+ (reinterpret_cast<const ServerFont*>(appFontHandle));
+ if (pServerFont)
+ {
+ return static_cast<float>(pServerFont->GetGlyphMetric(glyphId).GetCharWidth());
+ }
+ return .0f;
+}
+
+//
+// An implementation of the GraphiteLayout interface to enable Graphite enabled fonts to be used.
+//
+
+GraphiteServerFontLayout::GraphiteServerFontLayout(ServerFont& rServerFont) throw()
+ : ServerFontLayout(rServerFont),
+ maImpl(rServerFont.GetGraphiteFace()->face(),
+ rServerFont),
+ mpFeatures(NULL)
+{
+ gr_font * pFont = rServerFont.GetGraphiteFace()->font(rServerFont.GetFontSelData().mnHeight);
+ if (!pFont)
+ {
+ pFont = gr_make_font_with_advance_fn(
+ // need to use mnHeight here, mfExactHeight can give wrong values
+ static_cast<float>(rServerFont.GetFontSelData().mnHeight),
+ &rServerFont,
+ freetypeServerFontAdvance,
+ rServerFont.GetGraphiteFace()->face());
+ rServerFont.GetGraphiteFace()->addFont(rServerFont.GetFontSelData().mnHeight, pFont);
+ }
+ maImpl.SetFont(pFont);
+ rtl::OString aLang("");
+ if (rServerFont.GetFontSelData().meLanguage != LANGUAGE_DONTKNOW)
+ {
+ aLang = MsLangId::convertLanguageToIsoByteString(
+ rServerFont.GetFontSelData().meLanguage );
+ }
+ rtl::OString name = rtl::OUStringToOString(
+ rServerFont.GetFontSelData().maTargetName, RTL_TEXTENCODING_UTF8 );
+#ifdef DEBUG
+ printf("GraphiteServerFontLayout %lx %s size %d %f\n", (long unsigned int)this, name.getStr(),
+ rServerFont.GetMetricsFT().x_ppem,
+ rServerFont.GetFontSelData().mfExactHeight);
+#endif
+ sal_Int32 nFeat = name.indexOf(grutils::GrFeatureParser::FEAT_PREFIX) + 1;
+ if (nFeat > 0)
+ {
+ rtl::OString aFeat = name.copy(nFeat, name.getLength() - nFeat);
+ mpFeatures = new grutils::GrFeatureParser(
+ rServerFont.GetGraphiteFace()->face(), aFeat, aLang);
+#ifdef DEBUG
+ if (mpFeatures)
+ printf("GraphiteServerFontLayout %s/%s/%s %x language %d features %d errors\n",
+ rtl::OUStringToOString( rServerFont.GetFontSelData().maName,
+ RTL_TEXTENCODING_UTF8 ).getStr(),
+ rtl::OUStringToOString( rServerFont.GetFontSelData().maTargetName,
+ RTL_TEXTENCODING_UTF8 ).getStr(),
+ rtl::OUStringToOString( rServerFont.GetFontSelData().maSearchName,
+ RTL_TEXTENCODING_UTF8 ).getStr(),
+ rServerFont.GetFontSelData().meLanguage,
+ (int)mpFeatures->numFeatures(), mpFeatures->parseErrors());
+#endif
+ }
+ else
+ {
+ mpFeatures = new grutils::GrFeatureParser(
+ rServerFont.GetGraphiteFace()->face(), aLang);
+ }
+ maImpl.SetFeatures(mpFeatures);
+}
+
+GraphiteServerFontLayout::~GraphiteServerFontLayout() throw()
+{
+ delete mpFeatures;
+ mpFeatures = NULL;
+}
+
+bool GraphiteServerFontLayout::IsGraphiteEnabledFont(ServerFont& rServerFont)
+{
+ if (rServerFont.GetGraphiteFace())
+ {
+#ifdef DEBUG
+ printf("IsGraphiteEnabledFont\n");
+#endif
+ return true;
+ }
+ return false;
+}
+
+sal_GlyphId GraphiteLayoutImpl::getKashidaGlyph(int & width)
+{
+ int nKashidaIndex = mrServerFont.GetGlyphIndex( 0x0640 );
+ if( nKashidaIndex != 0 )
+ {
+ const GlyphMetric& rGM = mrServerFont.GetGlyphMetric( nKashidaIndex );
+ width = rGM.GetCharWidth();
+ }
+ else
+ {
+ width = 0;
+ }
+ return nKashidaIndex;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/vcl/generic/print/bitmap_gfx.cxx b/vcl/generic/print/bitmap_gfx.cxx
new file mode 100644
index 000000000000..f0b1bd4fe966
--- /dev/null
+++ b/vcl/generic/print/bitmap_gfx.cxx
@@ -0,0 +1,735 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*************************************************************************
+ *
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * Copyright 2000, 2010 Oracle and/or its affiliates.
+ *
+ * OpenOffice.org - a multi-platform office productivity suite
+ *
+ * This file is part of OpenOffice.org.
+ *
+ * OpenOffice.org is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License version 3
+ * only, as published by the Free Software Foundation.
+ *
+ * OpenOffice.org is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License version 3 for more details
+ * (a copy is included in the LICENSE file that accompanied this code).
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * version 3 along with OpenOffice.org. If not, see
+ * <http://www.openoffice.org/license.html>
+ * for a copy of the LGPLv3 License.
+ *
+ ************************************************************************/
+
+// MARKER(update_precomp.py): autogen include statement, do not remove
+#include "precompiled_vcl.hxx"
+
+#include "psputil.hxx"
+
+#include "generic/printergfx.hxx"
+#include "vcl/strhelper.hxx"
+
+namespace psp {
+
+const sal_uInt32 nLineLength = 80;
+const sal_uInt32 nBufferSize = 16384;
+
+/*
+ *
+ * Bitmap compression / Hex encoding / Ascii85 Encoding
+ *
+ */
+
+PrinterBmp::~PrinterBmp ()
+{ /* dont need this, but C50 does */ }
+
+/* virtual base class */
+
+class ByteEncoder
+{
+private:
+
+public:
+
+ virtual void EncodeByte (sal_uInt8 nByte) = 0;
+ virtual ~ByteEncoder () = 0;
+};
+
+ByteEncoder::~ByteEncoder ()
+{ /* dont need this, but the C50 does */ }
+
+/* HexEncoder */
+
+class HexEncoder : public ByteEncoder
+{
+private:
+
+ osl::File* mpFile;
+ sal_uInt32 mnColumn;
+ sal_uInt32 mnOffset;
+ sal_Char mpFileBuffer[nBufferSize + 16];
+
+ HexEncoder (); /* dont use */
+
+public:
+
+ HexEncoder (osl::File* pFile);
+ virtual ~HexEncoder ();
+ void WriteAscii (sal_uInt8 nByte);
+ virtual void EncodeByte (sal_uInt8 nByte);
+ void FlushLine ();
+};
+
+HexEncoder::HexEncoder (osl::File* pFile) :
+ mpFile (pFile),
+ mnColumn (0),
+ mnOffset (0)
+{}
+
+HexEncoder::~HexEncoder ()
+{
+ FlushLine ();
+ if (mnColumn > 0)
+ WritePS (mpFile, "\n");
+}
+
+void
+HexEncoder::WriteAscii (sal_uInt8 nByte)
+{
+ sal_uInt32 nOff = psp::getHexValueOf (nByte, mpFileBuffer + mnOffset);
+ mnColumn += nOff;
+ mnOffset += nOff;
+
+ if (mnColumn >= nLineLength)
+ {
+ mnOffset += psp::appendStr ("\n", mpFileBuffer + mnOffset);
+ mnColumn = 0;
+ }
+ if (mnOffset >= nBufferSize)
+ FlushLine ();
+}
+
+void
+HexEncoder::EncodeByte (sal_uInt8 nByte)
+{
+ WriteAscii (nByte);
+}
+
+void
+HexEncoder::FlushLine ()
+{
+ if (mnOffset > 0)
+ {
+ WritePS (mpFile, mpFileBuffer, mnOffset);
+ mnOffset = 0;
+ }
+}
+
+/* Ascii85 encoder, is abi compatible with HexEncoder but writes a ~> to
+ indicate end of data EOD */
+
+class Ascii85Encoder : public ByteEncoder
+{
+private:
+
+ osl::File* mpFile;
+ sal_uInt32 mnByte;
+ sal_uInt8 mpByteBuffer[4];
+
+ sal_uInt32 mnColumn;
+ sal_uInt32 mnOffset;
+ sal_Char mpFileBuffer[nBufferSize + 16];
+
+ Ascii85Encoder (); /* dont use */
+
+ inline void PutByte (sal_uInt8 nByte);
+ inline void PutEOD ();
+ void ConvertToAscii85 ();
+ void FlushLine ();
+
+public:
+
+ Ascii85Encoder (osl::File* pFile);
+ virtual ~Ascii85Encoder ();
+ virtual void EncodeByte (sal_uInt8 nByte);
+ void WriteAscii (sal_uInt8 nByte);
+};
+
+Ascii85Encoder::Ascii85Encoder (osl::File* pFile) :
+ mpFile (pFile),
+ mnByte (0),
+ mnColumn (0),
+ mnOffset (0)
+{}
+
+inline void
+Ascii85Encoder::PutByte (sal_uInt8 nByte)
+{
+ mpByteBuffer [mnByte++] = nByte;
+}
+
+inline void
+Ascii85Encoder::PutEOD ()
+{
+ WritePS (mpFile, "~>\n");
+}
+
+void
+Ascii85Encoder::ConvertToAscii85 ()
+{
+ if (mnByte < 4)
+ std::memset (mpByteBuffer + mnByte, 0, (4 - mnByte) * sizeof(sal_uInt8));
+
+ sal_uInt32 nByteValue = mpByteBuffer[0] * 256 * 256 * 256
+ + mpByteBuffer[1] * 256 * 256
+ + mpByteBuffer[2] * 256
+ + mpByteBuffer[3];
+
+ if (nByteValue == 0 && mnByte == 4)
+ {
+ /* special case of 4 Bytes in row */
+ mpFileBuffer [mnOffset] = 'z';
+
+ mnOffset += 1;
+ mnColumn += 1;
+ }
+ else
+ {
+ /* real ascii85 encoding */
+ mpFileBuffer [mnOffset + 4] = (nByteValue % 85) + 33;
+ nByteValue /= 85;
+ mpFileBuffer [mnOffset + 3] = (nByteValue % 85) + 33;
+ nByteValue /= 85;
+ mpFileBuffer [mnOffset + 2] = (nByteValue % 85) + 33;
+ nByteValue /= 85;
+ mpFileBuffer [mnOffset + 1] = (nByteValue % 85) + 33;
+ nByteValue /= 85;
+ mpFileBuffer [mnOffset + 0] = (nByteValue % 85) + 33;
+
+ mnColumn += (mnByte + 1);
+ mnOffset += (mnByte + 1);
+
+ /* insert a newline if necessary */
+ if (mnColumn > nLineLength)
+ {
+ sal_uInt32 nEolOff = mnColumn - nLineLength;
+ sal_uInt32 nBufOff = mnOffset - nEolOff;
+
+ std::memmove (mpFileBuffer + nBufOff + 1, mpFileBuffer + nBufOff, nEolOff);
+ mpFileBuffer[ nBufOff ] = '\n';
+
+ mnOffset++;
+ mnColumn = nEolOff;
+ }
+ }
+
+ mnByte = 0;
+}
+
+void
+Ascii85Encoder::WriteAscii (sal_uInt8 nByte)
+{
+ PutByte (nByte);
+ if (mnByte == 4)
+ ConvertToAscii85 ();
+
+ if (mnColumn >= nLineLength)
+ {
+ mnOffset += psp::appendStr ("\n", mpFileBuffer + mnOffset);
+ mnColumn = 0;
+ }
+ if (mnOffset >= nBufferSize)
+ FlushLine ();
+}
+
+void
+Ascii85Encoder::EncodeByte (sal_uInt8 nByte)
+{
+ WriteAscii (nByte);
+}
+
+void
+Ascii85Encoder::FlushLine ()
+{
+ if (mnOffset > 0)
+ {
+ WritePS (mpFile, mpFileBuffer, mnOffset);
+ mnOffset = 0;
+ }
+}
+
+Ascii85Encoder::~Ascii85Encoder ()
+{
+ if (mnByte > 0)
+ ConvertToAscii85 ();
+ if (mnOffset > 0)
+ FlushLine ();
+ PutEOD ();
+}
+
+/* LZW encoder */
+
+class LZWEncoder : public Ascii85Encoder
+{
+private:
+
+ struct LZWCTreeNode
+ {
+ LZWCTreeNode* mpBrother; // next node with same parent
+ LZWCTreeNode* mpFirstChild; // first son
+ sal_uInt16 mnCode; // code for the string
+ sal_uInt16 mnValue; // pixelvalue
+ };
+
+ LZWCTreeNode* mpTable; // LZW compression data
+ LZWCTreeNode* mpPrefix; // the compression is as same as the TIFF compression
+ sal_uInt16 mnDataSize;
+ sal_uInt16 mnClearCode;
+ sal_uInt16 mnEOICode;
+ sal_uInt16 mnTableSize;
+ sal_uInt16 mnCodeSize;
+ sal_uInt32 mnOffset;
+ sal_uInt32 mdwShift;
+
+ LZWEncoder ();
+ void WriteBits (sal_uInt16 nCode, sal_uInt16 nCodeLen);
+
+public:
+
+ LZWEncoder (osl::File* pOutputFile);
+ ~LZWEncoder ();
+
+ virtual void EncodeByte (sal_uInt8 nByte);
+};
+
+LZWEncoder::LZWEncoder(osl::File* pOutputFile) :
+ Ascii85Encoder (pOutputFile)
+{
+ mnDataSize = 8;
+
+ mnClearCode = 1 << mnDataSize;
+ mnEOICode = mnClearCode + 1;
+ mnTableSize = mnEOICode + 1;
+ mnCodeSize = mnDataSize + 1;
+
+ mnOffset = 32; // free bits in dwShift
+ mdwShift = 0;
+
+ mpTable = new LZWCTreeNode[ 4096 ];
+
+ for (sal_uInt32 i = 0; i < 4096; i++)
+ {
+ mpTable[i].mpBrother = NULL;
+ mpTable[i].mpFirstChild = NULL;
+ mpTable[i].mnCode = i;
+ mpTable[i].mnValue = (sal_uInt8)mpTable[i].mnCode;
+ }
+
+ mpPrefix = NULL;
+
+ WriteBits( mnClearCode, mnCodeSize );
+}
+
+LZWEncoder::~LZWEncoder()
+{
+ if (mpPrefix)
+ WriteBits (mpPrefix->mnCode, mnCodeSize);
+
+ WriteBits (mnEOICode, mnCodeSize);
+
+ delete[] mpTable;
+}
+
+void
+LZWEncoder::WriteBits (sal_uInt16 nCode, sal_uInt16 nCodeLen)
+{
+ mdwShift |= (nCode << (mnOffset - nCodeLen));
+ mnOffset -= nCodeLen;
+ while (mnOffset < 24)
+ {
+ WriteAscii ((sal_uInt8)(mdwShift >> 24));
+ mdwShift <<= 8;
+ mnOffset += 8;
+ }
+ if (nCode == 257 && mnOffset != 32)
+ WriteAscii ((sal_uInt8)(mdwShift >> 24));
+}
+
+void
+LZWEncoder::EncodeByte (sal_uInt8 nByte )
+{
+ LZWCTreeNode* p;
+ sal_uInt16 i;
+ sal_uInt8 nV;
+
+ if (!mpPrefix)
+ {
+ mpPrefix = mpTable + nByte;
+ }
+ else
+ {
+ nV = nByte;
+ for (p = mpPrefix->mpFirstChild; p != NULL; p = p->mpBrother)
+ {
+ if (p->mnValue == nV)
+ break;
+ }
+
+ if (p != NULL)
+ {
+ mpPrefix = p;
+ }
+ else
+ {
+ WriteBits (mpPrefix->mnCode, mnCodeSize);
+
+ if (mnTableSize == 409)
+ {
+ WriteBits (mnClearCode, mnCodeSize);
+
+ for (i = 0; i < mnClearCode; i++)
+ mpTable[i].mpFirstChild = NULL;
+
+ mnCodeSize = mnDataSize + 1;
+ mnTableSize = mnEOICode + 1;
+ }
+ else
+ {
+ if(mnTableSize == (sal_uInt16)((1 << mnCodeSize) - 1))
+ mnCodeSize++;
+
+ p = mpTable + (mnTableSize++);
+ p->mpBrother = mpPrefix->mpFirstChild;
+ mpPrefix->mpFirstChild = p;
+ p->mnValue = nV;
+ p->mpFirstChild = NULL;
+ }
+
+ mpPrefix = mpTable + nV;
+ }
+ }
+}
+
+/*
+ *
+ * bitmap handling routines
+ *
+ */
+
+void
+PrinterGfx::DrawBitmap (const Rectangle& rDest, const Rectangle& rSrc,
+ const PrinterBmp& rBitmap)
+{
+ double fScaleX = (double)rDest.GetWidth() / (double)rSrc.GetWidth();
+ double fScaleY = (double)rDest.GetHeight() / (double)rSrc.GetHeight();
+
+ PSGSave ();
+ PSTranslate (rDest.BottomLeft());
+ PSScale (fScaleX, fScaleY);
+
+ if (mnPSLevel >= 2)
+ {
+ if (rBitmap.GetDepth() == 1)
+ {
+ DrawPS2MonoImage (rBitmap, rSrc);
+ }
+ else
+ if (rBitmap.GetDepth() == 8 && mbColor)
+ {
+ // if the palette is larger than the image itself print it as a truecolor
+ // image to save diskspace. This is important for printing transparent
+ // bitmaps that are disassembled into small pieces
+ sal_Int32 nImageSz = rSrc.GetWidth() * rSrc.GetHeight();
+ sal_Int32 nPaletteSz = rBitmap.GetPaletteEntryCount();
+ if ((nImageSz < nPaletteSz) || (nImageSz < 24) )
+ DrawPS2TrueColorImage (rBitmap, rSrc);
+ else
+ DrawPS2PaletteImage (rBitmap, rSrc);
+ }
+ else
+ if (rBitmap.GetDepth() == 24 && mbColor)
+ {
+ DrawPS2TrueColorImage (rBitmap, rSrc);
+ }
+ else
+ {
+ DrawPS2GrayImage (rBitmap, rSrc);
+ }
+ }
+ else
+ {
+ DrawPS1GrayImage (rBitmap, rSrc);
+ }
+
+ PSGRestore ();
+}
+
+/* XXX does not work XXX */
+void
+PrinterGfx::DrawBitmap (const Rectangle& rDest, const Rectangle& rSrc,
+ const PrinterBmp& /*rBitmap*/, const PrinterBmp& /*rTransBitmap*/)
+{
+ double fScaleX = (double)rDest.GetWidth() / (double)rSrc.GetWidth();
+ double fScaleY = (double)rDest.GetHeight() / (double)rSrc.GetHeight();
+
+ PSGSave ();
+ PSTranslate (rDest.BottomLeft());
+ PSScale (fScaleX, fScaleY);
+ PSGRestore ();
+}
+
+/* XXX does not work XXX */
+void
+PrinterGfx::DrawMask (const Rectangle& rDest, const Rectangle& rSrc,
+ const PrinterBmp &/*rBitmap*/, PrinterColor& /*rMaskColor*/)
+{
+ double fScaleX = (double)rDest.GetWidth() / (double)rSrc.GetWidth();
+ double fScaleY = (double)rDest.GetHeight() / (double)rSrc.GetHeight();
+
+ PSGSave ();
+ PSTranslate (rDest.BottomLeft());
+ PSScale (fScaleX, fScaleY);
+ PSGRestore ();
+}
+
+/*
+ *
+ * Implementation: PS Level 1
+ *
+ */
+
+void
+PrinterGfx::DrawPS1GrayImage (const PrinterBmp& rBitmap, const Rectangle& rArea)
+{
+ sal_uInt32 nWidth = rArea.GetWidth();
+ sal_uInt32 nHeight = rArea.GetHeight();
+
+ sal_Char pGrayImage [512];
+ sal_Int32 nChar = 0;
+
+ // image header
+ nChar += psp::getValueOf (nWidth, pGrayImage + nChar);
+ nChar += psp::appendStr (" ", pGrayImage + nChar);
+ nChar += psp::getValueOf (nHeight, pGrayImage + nChar);
+ nChar += psp::appendStr (" 8 ", pGrayImage + nChar);
+ nChar += psp::appendStr ("[ 1 0 0 1 0 ", pGrayImage + nChar);
+ nChar += psp::getValueOf (nHeight, pGrayImage + nChar);
+ nChar += psp::appendStr ("]", pGrayImage + nChar);
+ nChar += psp::appendStr (" {currentfile ", pGrayImage + nChar);
+ nChar += psp::getValueOf (nWidth, pGrayImage + nChar);
+ nChar += psp::appendStr (" string readhexstring pop}\n", pGrayImage + nChar);
+ nChar += psp::appendStr ("image\n", pGrayImage + nChar);
+
+ WritePS (mpPageBody, pGrayImage);
+
+ // image body
+ HexEncoder* pEncoder = new HexEncoder (mpPageBody);
+
+ for (long nRow = rArea.Top(); nRow <= rArea.Bottom(); nRow++)
+ {
+ for (long nColumn = rArea.Left(); nColumn <= rArea.Right(); nColumn++)
+ {
+ sal_uChar nByte = rBitmap.GetPixelGray (nRow, nColumn);
+ pEncoder->EncodeByte (nByte);
+ }
+ }
+
+ delete pEncoder;
+
+ WritePS (mpPageBody, "\n");
+}
+
+/*
+ *
+ * Implementation: PS Level 2
+ *
+ */
+
+void
+PrinterGfx::writePS2ImageHeader (const Rectangle& rArea, psp::ImageType nType)
+{
+ sal_Int32 nChar = 0;
+ sal_Char pImage [512];
+
+ sal_Int32 nDictType = 0;
+ switch (nType)
+ {
+ case psp::TrueColorImage: nDictType = 0; break;
+ case psp::PaletteImage: nDictType = 1; break;
+ case psp::GrayScaleImage: nDictType = 2; break;
+ case psp::MonochromeImage: nDictType = 3; break;
+ default: break;
+ }
+ sal_Int32 nCompressType = mbCompressBmp ? 1 : 0;
+
+ nChar += psp::getValueOf (rArea.GetWidth(), pImage + nChar);
+ nChar += psp::appendStr (" ", pImage + nChar);
+ nChar += psp::getValueOf (rArea.GetHeight(), pImage + nChar);
+ nChar += psp::appendStr (" ", pImage + nChar);
+ nChar += psp::getValueOf (nDictType, pImage + nChar);
+ nChar += psp::appendStr (" ", pImage + nChar);
+ nChar += psp::getValueOf (nCompressType, pImage + nChar);
+ nChar += psp::appendStr (" psp_imagedict image\n", pImage + nChar);
+
+ WritePS (mpPageBody, pImage);
+}
+
+void
+PrinterGfx::writePS2Colorspace(const PrinterBmp& rBitmap, psp::ImageType nType)
+{
+ switch (nType)
+ {
+ case psp::GrayScaleImage:
+
+ WritePS (mpPageBody, "/DeviceGray setcolorspace\n");
+ break;
+
+ case psp::TrueColorImage:
+
+ WritePS (mpPageBody, "/DeviceRGB setcolorspace\n");
+ break;
+
+ case psp::MonochromeImage:
+ case psp::PaletteImage:
+ {
+
+ sal_Int32 nChar = 0;
+ sal_Char pImage [4096];
+
+ const sal_uInt32 nSize = rBitmap.GetPaletteEntryCount();
+
+ nChar += psp::appendStr ("[/Indexed /DeviceRGB ", pImage + nChar);
+ nChar += psp::getValueOf (nSize - 1, pImage + nChar);
+ if (mbCompressBmp)
+ nChar += psp::appendStr ("\npsp_lzwstring\n", pImage + nChar);
+ else
+ nChar += psp::appendStr ("\npsp_ascii85string\n", pImage + nChar);
+ WritePS (mpPageBody, pImage);
+
+ ByteEncoder* pEncoder = mbCompressBmp ? new LZWEncoder(mpPageBody)
+ : new Ascii85Encoder(mpPageBody);
+ for (sal_uInt32 i = 0; i < nSize; i++)
+ {
+ PrinterColor aColor = rBitmap.GetPaletteColor(i);
+
+ pEncoder->EncodeByte (aColor.GetRed());
+ pEncoder->EncodeByte (aColor.GetGreen());
+ pEncoder->EncodeByte (aColor.GetBlue());
+ }
+ delete pEncoder;
+
+ WritePS (mpPageBody, "pop ] setcolorspace\n");
+ }
+ break;
+ default: break;
+ }
+}
+
+void
+PrinterGfx::DrawPS2GrayImage (const PrinterBmp& rBitmap, const Rectangle& rArea)
+{
+ writePS2Colorspace(rBitmap, psp::GrayScaleImage);
+ writePS2ImageHeader(rArea, psp::GrayScaleImage);
+
+ ByteEncoder* pEncoder = mbCompressBmp ? new LZWEncoder(mpPageBody)
+ : new Ascii85Encoder(mpPageBody);
+
+ for (long nRow = rArea.Top(); nRow <= rArea.Bottom(); nRow++)
+ {
+ for (long nColumn = rArea.Left(); nColumn <= rArea.Right(); nColumn++)
+ {
+ sal_uChar nByte = rBitmap.GetPixelGray (nRow, nColumn);
+ pEncoder->EncodeByte (nByte);
+ }
+ }
+
+ delete pEncoder;
+}
+
+void
+PrinterGfx::DrawPS2MonoImage (const PrinterBmp& rBitmap, const Rectangle& rArea)
+{
+ writePS2Colorspace(rBitmap, psp::MonochromeImage);
+ writePS2ImageHeader(rArea, psp::MonochromeImage);
+
+ ByteEncoder* pEncoder = mbCompressBmp ? new LZWEncoder(mpPageBody)
+ : new Ascii85Encoder(mpPageBody);
+
+ for (long nRow = rArea.Top(); nRow <= rArea.Bottom(); nRow++)
+ {
+ long nBitPos = 0;
+ sal_uChar nBit = 0;
+ sal_uChar nByte = 0;
+
+ for (long nColumn = rArea.Left(); nColumn <= rArea.Right(); nColumn++)
+ {
+ nBit = rBitmap.GetPixelIdx (nRow, nColumn);
+ nByte |= nBit << (7 - nBitPos);
+
+ if (++nBitPos == 8)
+ {
+ pEncoder->EncodeByte (nByte);
+ nBitPos = 0;
+ nByte = 0;
+ }
+ }
+ // keep the row byte aligned
+ if (nBitPos != 0)
+ pEncoder->EncodeByte (nByte);
+ }
+
+ delete pEncoder;
+}
+
+void
+PrinterGfx::DrawPS2PaletteImage (const PrinterBmp& rBitmap, const Rectangle& rArea)
+{
+ writePS2Colorspace(rBitmap, psp::PaletteImage);
+ writePS2ImageHeader(rArea, psp::PaletteImage);
+
+ ByteEncoder* pEncoder = mbCompressBmp ? new LZWEncoder(mpPageBody)
+ : new Ascii85Encoder(mpPageBody);
+
+ for (long nRow = rArea.Top(); nRow <= rArea.Bottom(); nRow++)
+ {
+ for (long nColumn = rArea.Left(); nColumn <= rArea.Right(); nColumn++)
+ {
+ sal_uChar nByte = rBitmap.GetPixelIdx (nRow, nColumn);
+ pEncoder->EncodeByte (nByte);
+ }
+ }
+
+ delete pEncoder;
+}
+
+void
+PrinterGfx::DrawPS2TrueColorImage (const PrinterBmp& rBitmap, const Rectangle& rArea)
+{
+ writePS2Colorspace(rBitmap, psp::TrueColorImage);
+ writePS2ImageHeader(rArea, psp::TrueColorImage);
+
+ ByteEncoder* pEncoder = mbCompressBmp ? new LZWEncoder(mpPageBody)
+ : new Ascii85Encoder(mpPageBody);
+
+ for (long nRow = rArea.Top(); nRow <= rArea.Bottom(); nRow++)
+ {
+ for (long nColumn = rArea.Left(); nColumn <= rArea.Right(); nColumn++)
+ {
+ PrinterColor aColor = rBitmap.GetPixelRGB (nRow, nColumn);
+ pEncoder->EncodeByte (aColor.GetRed());
+ pEncoder->EncodeByte (aColor.GetGreen());
+ pEncoder->EncodeByte (aColor.GetBlue());
+ }
+ }
+
+ delete pEncoder;
+}
+
+} /* namespace psp */
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/vcl/generic/print/common_gfx.cxx b/vcl/generic/print/common_gfx.cxx
new file mode 100644
index 000000000000..f2dc2acaaab1
--- /dev/null
+++ b/vcl/generic/print/common_gfx.cxx
@@ -0,0 +1,1287 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*************************************************************************
+ *
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * Copyright 2000, 2010 Oracle and/or its affiliates.
+ *
+ * OpenOffice.org - a multi-platform office productivity suite
+ *
+ * This file is part of OpenOffice.org.
+ *
+ * OpenOffice.org is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License version 3
+ * only, as published by the Free Software Foundation.
+ *
+ * OpenOffice.org is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License version 3 for more details
+ * (a copy is included in the LICENSE file that accompanied this code).
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * version 3 along with OpenOffice.org. If not, see
+ * <http://www.openoffice.org/license.html>
+ * for a copy of the LGPLv3 License.
+ *
+ ************************************************************************/
+
+// MARKER(update_precomp.py): autogen include statement, do not remove
+#include "precompiled_vcl.hxx"
+
+#include "psputil.hxx"
+#include "glyphset.hxx"
+
+#include "generic/printergfx.hxx"
+#include "generic/printerjob.hxx"
+#include "vcl/fontmanager.hxx"
+#include "vcl/strhelper.hxx"
+#include "vcl/printerinfomanager.hxx"
+
+#include "tools/debug.hxx"
+#include "tools/color.hxx"
+#include "tools/poly.hxx"
+
+using namespace psp ;
+
+static const sal_Int32 nMaxTextColumn = 80;
+
+GraphicsStatus::GraphicsStatus() :
+ mbArtItalic( false ),
+ mbArtBold( false ),
+ mnTextHeight( 0 ),
+ mnTextWidth( 0 ),
+ mfLineWidth( -1 )
+{
+}
+
+/*
+ * non graphics graphics routines
+ */
+
+sal_Bool
+PrinterGfx::Init (PrinterJob &rPrinterJob)
+{
+ mpPageHeader = rPrinterJob.GetCurrentPageHeader ();
+ mpPageBody = rPrinterJob.GetCurrentPageBody ();
+ mnDepth = rPrinterJob.GetDepth ();
+ mnPSLevel = rPrinterJob.GetPostscriptLevel ();
+ mbColor = rPrinterJob.IsColorPrinter ();
+
+ mnDpi = rPrinterJob.GetResolution();
+ rPrinterJob.GetScale (mfScaleX, mfScaleY);
+ const PrinterInfo& rInfo( PrinterInfoManager::get().getPrinterInfo( rPrinterJob.GetPrinterName() ) );
+ if( mpFontSubstitutes )
+ delete const_cast< ::boost::unordered_map<fontID,fontID>* >(mpFontSubstitutes);
+ if( rInfo.m_bPerformFontSubstitution )
+ mpFontSubstitutes = new ::boost::unordered_map< fontID, fontID >( rInfo.m_aFontSubstitutions );
+ else
+ mpFontSubstitutes = NULL;
+ mbUploadPS42Fonts = rInfo.m_pParser ? ( rInfo.m_pParser->isType42Capable() ? sal_True : sal_False ) : sal_False;
+
+ return sal_True;
+}
+
+sal_Bool
+PrinterGfx::Init (const JobData& rData)
+{
+ mpPageHeader = NULL;
+ mpPageBody = NULL;
+ mnDepth = rData.m_nColorDepth;
+ mnPSLevel = rData.m_nPSLevel ? rData.m_nPSLevel : (rData.m_pParser ? rData.m_pParser->getLanguageLevel() : 2 );
+ mbColor = rData.m_nColorDevice ? ( rData.m_nColorDevice == -1 ? sal_False : sal_True ) : (( rData.m_pParser ? (rData.m_pParser->isColorDevice() ? sal_True : sal_False ) : sal_True ) );
+ int nRes = rData.m_aContext.getRenderResolution();
+ mnDpi = nRes;
+ mfScaleX = (double)72.0 / (double)mnDpi;
+ mfScaleY = (double)72.0 / (double)mnDpi;
+ const PrinterInfo& rInfo( PrinterInfoManager::get().getPrinterInfo( rData.m_aPrinterName ) );
+ if( mpFontSubstitutes )
+ delete const_cast< ::boost::unordered_map<fontID,fontID>* >(mpFontSubstitutes);
+ if( rInfo.m_bPerformFontSubstitution )
+ mpFontSubstitutes = new ::boost::unordered_map< fontID, fontID >( rInfo.m_aFontSubstitutions );
+ else
+ mpFontSubstitutes = NULL;
+ mbUploadPS42Fonts = rInfo.m_pParser ? ( rInfo.m_pParser->isType42Capable() ? sal_True : sal_False ) : sal_False;
+
+ return sal_True;
+}
+
+void
+PrinterGfx::GetResolution (sal_Int32 &rDpiX, sal_Int32 &rDpiY) const
+{
+ rDpiX = mnDpi;
+ rDpiY = mnDpi;
+}
+
+sal_uInt16
+PrinterGfx::GetBitCount ()
+{
+ return mnDepth;
+}
+
+PrinterGfx::PrinterGfx() :
+ mpPageHeader (NULL),
+ mpPageBody (NULL),
+ mnFontID (0),
+ mnFallbackID (0),
+ mnTextAngle (0),
+ mbTextVertical (false),
+ mrFontMgr (PrintFontManager::get()),
+ mbCompressBmp (sal_True),
+ maFillColor (0xff,0,0),
+ maTextColor (0,0,0),
+ maLineColor (0, 0xff, 0),
+ mpFontSubstitutes( NULL ),
+ mbStrictSO52Compatibility( false )
+{
+ maVirtualStatus.mfLineWidth = 1.0;
+ maVirtualStatus.mnTextHeight = 12;
+ maVirtualStatus.mnTextWidth = 0;
+
+ maGraphicsStack.push_back( GraphicsStatus() );
+}
+
+PrinterGfx::~PrinterGfx()
+{
+ /*
+ * the original reasoning why mpFontSubstitutes is a pointer was
+ * that applications should release all PrinterGfx when printers change
+ * because they are really invalid; the corresponding printers may have
+ * changed their settings or even not exist anymore.
+ *
+ * Alas, this is not always done real time. So we keep a local copy of
+ * the font substitutes now in case of bad timing.
+ */
+ delete const_cast< ::boost::unordered_map<fontID,fontID>* >(mpFontSubstitutes);
+}
+
+void
+PrinterGfx::Clear()
+{
+ mpPageHeader = NULL;
+ mpPageBody = NULL;
+ mnFontID = 0;
+ maVirtualStatus = GraphicsStatus();
+ maVirtualStatus.mnTextHeight = 12;
+ maVirtualStatus.mnTextWidth = 0;
+ maVirtualStatus.mfLineWidth = 1.0;
+ mbTextVertical = false;
+ maLineColor = PrinterColor();
+ maFillColor = PrinterColor();
+ maTextColor = PrinterColor();
+ mbCompressBmp = sal_True;
+ mnDpi = 300;
+ mnDepth = 24;
+ mnPSLevel = 2;
+ mbColor = sal_True;
+ mnTextAngle = 0;
+
+ maClipRegion.clear();
+ maGraphicsStack.clear();
+ maGraphicsStack.push_back( GraphicsStatus() );
+}
+
+/*
+ * clip region handling
+ */
+
+void
+PrinterGfx::ResetClipRegion()
+{
+ maClipRegion.clear();
+ PSGRestore ();
+ PSGSave (); // get "clean" clippath
+}
+
+void
+PrinterGfx::BeginSetClipRegion( sal_uInt32 )
+{
+ maClipRegion.clear();
+}
+
+sal_Bool
+PrinterGfx::UnionClipRegion (sal_Int32 nX,sal_Int32 nY,sal_Int32 nDX,sal_Int32 nDY)
+{
+ if( nDX && nDY )
+ maClipRegion.push_back (Rectangle(Point(nX,nY ), Size(nDX,nDY)));
+ return sal_True;
+}
+
+sal_Bool
+PrinterGfx::JoinVerticalClipRectangles( std::list< Rectangle >::iterator& it,
+ Point& rOldPoint, sal_Int32& rColumn )
+{
+ sal_Bool bSuccess = sal_False;
+
+ std::list< Rectangle >::iterator tempit, nextit;
+ nextit = it;
+ ++nextit;
+ std::list< Point > leftside, rightside;
+
+ Rectangle aLastRect( *it );
+ leftside.push_back( Point( it->Left(), it->Top() ) );
+ rightside.push_back( Point( it->Right()+1, it->Top() ) );
+ while( nextit != maClipRegion.end() )
+ {
+ tempit = nextit;
+ ++tempit;
+ if( nextit->Top() == aLastRect.Bottom()+1 )
+ {
+ if(
+ ( nextit->Left() >= aLastRect.Left() && nextit->Left() <= aLastRect.Right() ) // left endpoint touches last rectangle
+ ||
+ ( nextit->Right() >= aLastRect.Left() && nextit->Right() <= aLastRect.Right() ) // right endpoint touches last rectangle
+ ||
+ ( nextit->Left() <= aLastRect.Left() && nextit->Right() >= aLastRect.Right() ) // whole line touches last rectangle
+ )
+ {
+ if( aLastRect.GetHeight() > 1 ||
+ abs( aLastRect.Left() - nextit->Left() ) > 2 ||
+ abs( aLastRect.Right() - nextit->Right() ) > 2
+ )
+ {
+ leftside.push_back( Point( aLastRect.Left(), aLastRect.Bottom()+1 ) );
+ rightside.push_back( Point( aLastRect.Right()+1, aLastRect.Bottom()+1 ) );
+ }
+ aLastRect = *nextit;
+ leftside.push_back( aLastRect.TopLeft() );
+ rightside.push_back( aLastRect.TopRight() );
+ maClipRegion.erase( nextit );
+ }
+ }
+ nextit = tempit;
+ }
+ if( leftside.size() > 1 )
+ {
+ // push the last coordinates
+ leftside.push_back( Point( aLastRect.Left(), aLastRect.Bottom()+1 ) );
+ rightside.push_back( Point( aLastRect.Right()+1, aLastRect.Bottom()+1 ) );
+
+ // cool, we can concatenate rectangles
+ int nDX = -65536, nDY = 65536;
+ int nNewDX = 0, nNewDY = 0;
+
+ Point aLastPoint = leftside.front();
+ PSBinMoveTo (aLastPoint, rOldPoint, rColumn);
+ leftside.pop_front();
+ while( leftside.begin() != leftside.end() )
+ {
+ Point aPoint (leftside.front());
+ leftside.pop_front();
+ // may have been the last one
+ if( leftside.begin() != leftside.end() )
+ {
+ nNewDX = aPoint.X() - aLastPoint.X();
+ nNewDY = aPoint.Y() - aLastPoint.Y();
+ if( nNewDX == 0 && nDX == 0 )
+ continue;
+ if( nDX != 0 && nNewDX != 0 &&
+ (double)nNewDY/(double)nNewDX == (double)nDY/(double)nDX )
+ continue;
+ }
+ PSBinLineTo (aPoint, rOldPoint, rColumn);
+ aLastPoint = aPoint;
+ }
+
+ aLastPoint = rightside.back();
+ nDX = -65536;
+ nDY = 65536;
+ PSBinLineTo (aLastPoint, rOldPoint, rColumn);
+ rightside.pop_back();
+ while( rightside.begin() != rightside.end() )
+ {
+ Point aPoint (rightside.back());
+ rightside.pop_back();
+ if( rightside.begin() != rightside.end() )
+ {
+ nNewDX = aPoint.X() - aLastPoint.X();
+ nNewDY = aPoint.Y() - aLastPoint.Y();
+ if( nNewDX == 0 && nDX == 0 )
+ continue;
+ if( nDX != 0 && nNewDX != 0 &&
+ (double)nNewDY/(double)nNewDX == (double)nDY/(double)nDX )
+ continue;
+ }
+ PSBinLineTo (aPoint, rOldPoint, rColumn);
+ }
+
+ tempit = it;
+ ++tempit;
+ maClipRegion.erase( it );
+ it = tempit;
+ bSuccess = sal_True;
+ }
+ return bSuccess;
+}
+
+void
+PrinterGfx::EndSetClipRegion()
+{
+ PSGRestore ();
+ PSGSave (); // get "clean" clippath
+
+ PSBinStartPath ();
+ Point aOldPoint (0, 0);
+ sal_Int32 nColumn = 0;
+
+ std::list< Rectangle >::iterator it = maClipRegion.begin();
+ while( it != maClipRegion.end() )
+ {
+ // try to concatenate adjacent rectangles
+ // first try in y direction, then in x direction
+ if( ! JoinVerticalClipRectangles( it, aOldPoint, nColumn ) )
+ {
+ // failed, so it is a single rectangle
+ PSBinMoveTo (it->TopLeft(), aOldPoint, nColumn );
+ PSBinLineTo (Point( it->Left(), it->Bottom()+1 ), aOldPoint, nColumn );
+ PSBinLineTo (Point( it->Right()+1, it->Bottom()+1 ), aOldPoint, nColumn );
+ PSBinLineTo (Point( it->Right()+1, it->Top() ), aOldPoint, nColumn );
+ ++it;
+ }
+ }
+
+ PSBinEndPath ();
+
+ WritePS (mpPageBody, "closepath clip newpath\n");
+ maClipRegion.clear();
+}
+
+/*
+ * draw graphic primitives
+ */
+
+void
+PrinterGfx::DrawRect (const Rectangle& rRectangle )
+{
+ char pRect [128];
+ sal_Int32 nChar = 0;
+
+ nChar = psp::getValueOf (rRectangle.TopLeft().X(), pRect);
+ nChar += psp::appendStr (" ", pRect + nChar);
+ nChar += psp::getValueOf (rRectangle.TopLeft().Y(), pRect + nChar);
+ nChar += psp::appendStr (" ", pRect + nChar);
+ nChar += psp::getValueOf (rRectangle.GetWidth(), pRect + nChar);
+ nChar += psp::appendStr (" ", pRect + nChar);
+ nChar += psp::getValueOf (rRectangle.GetHeight(), pRect + nChar);
+ nChar += psp::appendStr (" ", pRect + nChar);
+
+ if( maFillColor.Is() )
+ {
+ PSSetColor (maFillColor);
+ PSSetColor ();
+ WritePS (mpPageBody, pRect, nChar);
+ WritePS (mpPageBody, "rectfill\n");
+ }
+ if( maLineColor.Is() )
+ {
+ PSSetColor (maLineColor);
+ PSSetColor ();
+ PSSetLineWidth ();
+ WritePS (mpPageBody, pRect, nChar);
+ WritePS (mpPageBody, "rectstroke\n");
+ }
+}
+
+void
+PrinterGfx::DrawLine (const Point& rFrom, const Point& rTo)
+{
+ if( maLineColor.Is() )
+ {
+ PSSetColor (maLineColor);
+ PSSetColor ();
+ PSSetLineWidth ();
+
+ PSMoveTo (rFrom);
+ PSLineTo (rTo);
+ WritePS (mpPageBody, "stroke\n" );
+ }
+}
+
+void
+PrinterGfx::DrawPixel (const Point& rPoint, const PrinterColor& rPixelColor)
+{
+ if( rPixelColor.Is() )
+ {
+ PSSetColor (rPixelColor);
+ PSSetColor ();
+
+ PSMoveTo (rPoint);
+ PSLineTo (Point (rPoint.X ()+1, rPoint.Y ()));
+ PSLineTo (Point (rPoint.X ()+1, rPoint.Y ()+1));
+ PSLineTo (Point (rPoint.X (), rPoint.Y ()+1));
+ WritePS (mpPageBody, "fill\n" );
+ }
+}
+
+void
+PrinterGfx::DrawPolyLine (sal_uInt32 nPoints, const Point* pPath)
+{
+ if( maLineColor.Is() && nPoints && pPath )
+ {
+ PSSetColor (maLineColor);
+ PSSetColor ();
+ PSSetLineWidth ();
+
+ PSBinCurrentPath (nPoints, pPath);
+
+ WritePS (mpPageBody, "stroke\n" );
+ }
+}
+
+void
+PrinterGfx::DrawPolygon (sal_uInt32 nPoints, const Point* pPath)
+{
+ // premature end of operation
+ if (!(nPoints > 1) || (pPath == NULL) || !(maFillColor.Is() || maLineColor.Is()))
+ return;
+
+ // setup closed path
+ Point aPoint( 0, 0 );
+ sal_Int32 nColumn( 0 );
+
+ PSBinStartPath();
+ PSBinMoveTo( pPath[0], aPoint, nColumn );
+ for( unsigned int n = 1; n < nPoints; n++ )
+ PSBinLineTo( pPath[n], aPoint, nColumn );
+ if( pPath[0] != pPath[nPoints-1] )
+ PSBinLineTo( pPath[0], aPoint, nColumn );
+ PSBinEndPath();
+
+ // fill the polygon first, then draw the border, note that fill and
+ // stroke reset the currentpath
+
+ // if fill and stroke, save the current path
+ if( maFillColor.Is() && maLineColor.Is())
+ PSGSave();
+
+ if (maFillColor.Is ())
+ {
+ PSSetColor (maFillColor);
+ PSSetColor ();
+ WritePS (mpPageBody, "eofill\n");
+ }
+
+ // restore the current path
+ if( maFillColor.Is() && maLineColor.Is())
+ PSGRestore();
+
+ if (maLineColor.Is ())
+ {
+ PSSetColor (maLineColor);
+ PSSetColor ();
+ PSSetLineWidth ();
+ WritePS (mpPageBody, "stroke\n");
+ }
+}
+
+void
+PrinterGfx::DrawPolyPolygon (sal_uInt32 nPoly, const sal_uInt32* pSizes, const Point** pPaths )
+{
+ // sanity check
+ if ( !nPoly || !pPaths || !(maFillColor.Is() || maLineColor.Is()))
+ return;
+
+
+ // setup closed path
+ for( unsigned int i = 0; i < nPoly; i++ )
+ {
+ Point aPoint( 0, 0 );
+ sal_Int32 nColumn( 0 );
+
+ PSBinStartPath();
+ PSBinMoveTo( pPaths[i][0], aPoint, nColumn );
+ for( unsigned int n = 1; n < pSizes[i]; n++ )
+ PSBinLineTo( pPaths[i][n], aPoint, nColumn );
+ if( pPaths[i][0] != pPaths[i][pSizes[i]-1] )
+ PSBinLineTo( pPaths[i][0], aPoint, nColumn );
+ PSBinEndPath();
+ }
+
+ // if eofill and stroke, save the current path
+ if( maFillColor.Is() && maLineColor.Is())
+ PSGSave();
+
+ // first draw area
+ if( maFillColor.Is() )
+ {
+ PSSetColor (maFillColor);
+ PSSetColor ();
+ WritePS (mpPageBody, "eofill\n");
+ }
+
+ // restore the current path
+ if( maFillColor.Is() && maLineColor.Is())
+ PSGRestore();
+
+ // now draw outlines
+ if( maLineColor.Is() )
+ {
+ PSSetColor (maLineColor);
+ PSSetColor ();
+ PSSetLineWidth ();
+ WritePS (mpPageBody, "stroke\n");
+ }
+}
+
+/*
+ * Bezier Polygon Drawing methods.
+ */
+
+void
+PrinterGfx::DrawPolyLineBezier (sal_uInt32 nPoints, const Point* pPath, const sal_uInt8* pFlgAry)
+{
+ const sal_uInt32 nBezString= 1024;
+ sal_Char pString[nBezString];
+
+ if ( nPoints > 1 && maLineColor.Is() && pPath )
+ {
+ PSSetColor (maLineColor);
+ PSSetColor ();
+ PSSetLineWidth ();
+
+ snprintf(pString, nBezString, "%li %li moveto\n", pPath[0].X(), pPath[0].Y());
+ WritePS(mpPageBody, pString);
+
+ // Handle the drawing of mixed lines mixed with curves
+ // - a normal point followed by a normal point is a line
+ // - a normal point followed by 2 control points and a normal point is a curve
+ for (unsigned int i=1; i<nPoints;)
+ {
+ if (pFlgAry[i] != POLY_CONTROL) //If the next point is a POLY_NORMAL, we're drawing a line
+ {
+ snprintf(pString, nBezString, "%li %li lineto\n", pPath[i].X(), pPath[i].Y());
+ i++;
+ }
+ else //Otherwise we're drawing a spline
+ {
+ if (i+2 >= nPoints)
+ return; //Error: wrong sequence of contol/normal points somehow
+ if ((pFlgAry[i] == POLY_CONTROL) && (pFlgAry[i+1] == POLY_CONTROL) &&
+ (pFlgAry[i+2] != POLY_CONTROL))
+ {
+ snprintf(pString, nBezString, "%li %li %li %li %li %li curveto\n",
+ pPath[i].X(), pPath[i].Y(),
+ pPath[i+1].X(), pPath[i+1].Y(),
+ pPath[i+2].X(), pPath[i+2].Y());
+ }
+ else
+ {
+ OSL_FAIL( "PrinterGfx::DrawPolyLineBezier: Strange output" );
+ }
+ i+=3;
+ }
+ WritePS(mpPageBody, pString);
+ }
+
+ // now draw outlines
+ WritePS (mpPageBody, "stroke\n");
+ }
+}
+
+void
+PrinterGfx::DrawPolygonBezier (sal_uInt32 nPoints, const Point* pPath, const sal_uInt8* pFlgAry)
+{
+ const sal_uInt32 nBezString = 1024;
+ sal_Char pString[nBezString];
+ // premature end of operation
+ if (!(nPoints > 1) || (pPath == NULL) || !(maFillColor.Is() || maLineColor.Is()))
+ return;
+
+ snprintf(pString, nBezString, "%li %li moveto\n", pPath[0].X(), pPath[0].Y());
+ WritePS(mpPageBody, pString); //Move to the starting point for the PolyPoygon
+ for (unsigned int i=1; i < nPoints;)
+ {
+ if (pFlgAry[i] != POLY_CONTROL)
+ {
+ snprintf(pString, nBezString, "%li %li lineto\n", pPath[i].X(), pPath[i].Y());
+ WritePS(mpPageBody, pString);
+ i++;
+ }
+ else
+ {
+ if (i+2 >= nPoints)
+ return; //Error: wrong sequence of contol/normal points somehow
+ if ((pFlgAry[i] == POLY_CONTROL) && (pFlgAry[i+1] == POLY_CONTROL) &&
+ (pFlgAry[i+2] != POLY_CONTROL))
+ {
+ snprintf(pString, nBezString, "%li %li %li %li %li %li curveto\n",
+ pPath[i].X(), pPath[i].Y(),
+ pPath[i+1].X(), pPath[i+1].Y(),
+ pPath[i+2].X(), pPath[i+2].Y());
+ WritePS(mpPageBody, pString);
+ }
+ else
+ {
+ OSL_FAIL( "PrinterGfx::DrawPolygonBezier: Strange output" );
+ }
+ i+=3;
+ }
+ }
+
+ // if fill and stroke, save the current path
+ if( maFillColor.Is() && maLineColor.Is())
+ PSGSave();
+
+ if (maFillColor.Is ())
+ {
+ PSSetColor (maFillColor);
+ PSSetColor ();
+ WritePS (mpPageBody, "eofill\n");
+ }
+
+ // restore the current path
+ if( maFillColor.Is() && maLineColor.Is())
+ PSGRestore();
+}
+
+void
+PrinterGfx::DrawPolyPolygonBezier (sal_uInt32 nPoly, const sal_uInt32 * pPoints, const Point* const * pPtAry, const sal_uInt8* const* pFlgAry)
+{
+ const sal_uInt32 nBezString = 1024;
+ sal_Char pString[nBezString];
+ if ( !nPoly || !pPtAry || !pPoints || !(maFillColor.Is() || maLineColor.Is()))
+ return;
+
+
+ for (unsigned int i=0; i<nPoly;i++)
+ {
+ sal_uInt32 nPoints = pPoints[i];
+ // sanity check
+ if( nPoints == 0 || pPtAry[i] == NULL )
+ continue;
+
+ snprintf(pString, nBezString, "%li %li moveto\n", pPtAry[i][0].X(), pPtAry[i][0].Y()); //Move to the starting point
+ WritePS(mpPageBody, pString);
+ for (unsigned int j=1; j < nPoints;)
+ {
+ // if no flag array exists for this polygon, then it must be a regular
+ // polygon without beziers
+ if ( ! pFlgAry[i] || pFlgAry[i][j] != POLY_CONTROL)
+ {
+ snprintf(pString, nBezString, "%li %li lineto\n", pPtAry[i][j].X(), pPtAry[i][j].Y());
+ WritePS(mpPageBody, pString);
+ j++;
+ }
+ else
+ {
+ if (j+2 >= nPoints)
+ break; //Error: wrong sequence of contol/normal points somehow
+ if ((pFlgAry[i][j] == POLY_CONTROL) && (pFlgAry[i][j+1] == POLY_CONTROL) && (pFlgAry[i][j+2] != POLY_CONTROL))
+ {
+ snprintf(pString, nBezString, "%li %li %li %li %li %li curveto\n",
+ pPtAry[i][j].X(), pPtAry[i][j].Y(),
+ pPtAry[i][j+1].X(), pPtAry[i][j+1].Y(),
+ pPtAry[i][j+2].X(), pPtAry[i][j+2].Y());
+ WritePS(mpPageBody, pString);
+ }
+ else
+ {
+ OSL_FAIL( "PrinterGfx::DrawPolyPolygonBezier: Strange output" );
+ }
+ j+=3;
+ }
+ }
+ }
+
+ // if fill and stroke, save the current path
+ if( maFillColor.Is() && maLineColor.Is())
+ PSGSave();
+
+ if (maFillColor.Is ())
+ {
+ PSSetColor (maFillColor);
+ PSSetColor ();
+ WritePS (mpPageBody, "eofill\n");
+ }
+
+ // restore the current path
+ if( maFillColor.Is() && maLineColor.Is())
+ PSGRestore();
+}
+
+
+/*
+ * postscript generating routines
+ */
+void
+PrinterGfx::PSGSave ()
+{
+ WritePS (mpPageBody, "gsave\n" );
+ GraphicsStatus aNewState;
+ if( maGraphicsStack.begin() != maGraphicsStack.end() )
+ aNewState = maGraphicsStack.front();
+ maGraphicsStack.push_front( aNewState );
+}
+
+void
+PrinterGfx::PSGRestore ()
+{
+ WritePS (mpPageBody, "grestore\n" );
+ if( maGraphicsStack.begin() == maGraphicsStack.end() )
+ WritePS (mpPageBody, "Error: too many grestores\n" );
+ else
+ maGraphicsStack.pop_front();
+}
+
+void
+PrinterGfx::PSSetLineWidth ()
+{
+ if( currentState().mfLineWidth != maVirtualStatus.mfLineWidth )
+ {
+ char pBuffer[128];
+ sal_Int32 nChar = 0;
+
+ currentState().mfLineWidth = maVirtualStatus.mfLineWidth;
+ nChar = psp::getValueOfDouble (pBuffer, maVirtualStatus.mfLineWidth, 5);
+ nChar += psp::appendStr (" setlinewidth\n", pBuffer + nChar);
+ WritePS (mpPageBody, pBuffer, nChar);
+ }
+}
+
+void
+PrinterGfx::PSSetColor ()
+{
+ PrinterColor& rColor( maVirtualStatus.maColor );
+
+ if( currentState().maColor != rColor )
+ {
+ currentState().maColor = rColor;
+
+ char pBuffer[128];
+ sal_Int32 nChar = 0;
+
+ if( mbColor )
+ {
+ nChar = psp::getValueOfDouble (pBuffer,
+ (double)rColor.GetRed() / 255.0, 5);
+ nChar += psp::appendStr (" ", pBuffer + nChar);
+ nChar += psp::getValueOfDouble (pBuffer + nChar,
+ (double)rColor.GetGreen() / 255.0, 5);
+ nChar += psp::appendStr (" ", pBuffer + nChar);
+ nChar += psp::getValueOfDouble (pBuffer + nChar,
+ (double)rColor.GetBlue() / 255.0, 5);
+ nChar += psp::appendStr (" setrgbcolor\n", pBuffer + nChar );
+ }
+ else
+ {
+ Color aColor( rColor.GetRed(), rColor.GetGreen(), rColor.GetBlue() );
+ sal_uInt8 nCol = aColor.GetLuminance();
+ nChar = psp::getValueOfDouble( pBuffer, (double)nCol / 255.0, 5 );
+ nChar += psp::appendStr( " setgray\n", pBuffer + nChar );
+ }
+
+ WritePS (mpPageBody, pBuffer, nChar);
+ }
+}
+
+void
+PrinterGfx::PSSetFont ()
+{
+ GraphicsStatus& rCurrent( currentState() );
+ if( maVirtualStatus.maFont != rCurrent.maFont ||
+ maVirtualStatus.mnTextHeight != rCurrent.mnTextHeight ||
+ maVirtualStatus.maEncoding != rCurrent.maEncoding ||
+ maVirtualStatus.mnTextWidth != rCurrent.mnTextWidth ||
+ maVirtualStatus.mbArtBold != rCurrent.mbArtBold ||
+ maVirtualStatus.mbArtItalic != rCurrent.mbArtItalic
+ )
+ {
+ rCurrent.maFont = maVirtualStatus.maFont;
+ rCurrent.maEncoding = maVirtualStatus.maEncoding;
+ rCurrent.mnTextWidth = maVirtualStatus.mnTextWidth;
+ rCurrent.mnTextHeight = maVirtualStatus.mnTextHeight;
+ rCurrent.mbArtItalic = maVirtualStatus.mbArtItalic;
+ rCurrent.mbArtBold = maVirtualStatus.mbArtBold;
+
+ sal_Int32 nTextHeight = rCurrent.mnTextHeight;
+ sal_Int32 nTextWidth = rCurrent.mnTextWidth ? rCurrent.mnTextWidth
+ : rCurrent.mnTextHeight;
+
+ sal_Char pSetFont [256];
+ sal_Int32 nChar = 0;
+
+ // postscript based fonts need reencoding
+ if ( ( rCurrent.maEncoding == RTL_TEXTENCODING_MS_1252)
+ || ( rCurrent.maEncoding == RTL_TEXTENCODING_ISO_8859_1)
+ || ( rCurrent.maEncoding >= RTL_TEXTENCODING_USER_START
+ && rCurrent.maEncoding <= RTL_TEXTENCODING_USER_END)
+ )
+ {
+ rtl::OString aReencodedFont =
+ psp::GlyphSet::GetReencodedFontName (rCurrent.maEncoding,
+ rCurrent.maFont);
+
+ nChar += psp::appendStr ("(", pSetFont + nChar);
+ nChar += psp::appendStr (aReencodedFont.getStr(),
+ pSetFont + nChar);
+ nChar += psp::appendStr (") cvn findfont ",
+ pSetFont + nChar);
+ }
+ else
+ // tt based fonts mustn't reencode, the encoding is implied by the fontname
+ // same for symbol type1 fonts, dont try to touch them
+ {
+ nChar += psp::appendStr ("(", pSetFont + nChar);
+ nChar += psp::appendStr (rCurrent.maFont.getStr(),
+ pSetFont + nChar);
+ nChar += psp::appendStr (") cvn findfont ",
+ pSetFont + nChar);
+ }
+
+ if( ! rCurrent.mbArtItalic )
+ {
+ nChar += psp::getValueOf (nTextWidth, pSetFont + nChar);
+ nChar += psp::appendStr (" ", pSetFont + nChar);
+ nChar += psp::getValueOf (-nTextHeight, pSetFont + nChar);
+ nChar += psp::appendStr (" matrix scale makefont setfont\n", pSetFont + nChar);
+ }
+ else // skew 15 degrees to right
+ {
+ nChar += psp::appendStr ( " [", pSetFont + nChar);
+ nChar += psp::getValueOf (nTextWidth, pSetFont + nChar);
+ nChar += psp::appendStr (" 0 ", pSetFont + nChar);
+ nChar += psp::getValueOfDouble (pSetFont + nChar, 0.27*(double)nTextWidth, 3 );
+ nChar += psp::appendStr ( " ", pSetFont + nChar);
+ nChar += psp::getValueOf (-nTextHeight, pSetFont + nChar);
+
+ nChar += psp::appendStr (" 0 0] makefont setfont\n", pSetFont + nChar);
+ }
+
+ WritePS (mpPageBody, pSetFont);
+ }
+}
+
+void
+PrinterGfx::PSRotate (sal_Int32 nAngle)
+{
+ sal_Int32 nPostScriptAngle = -nAngle;
+ while( nPostScriptAngle < 0 )
+ nPostScriptAngle += 3600;
+
+ if (nPostScriptAngle == 0)
+ return;
+
+ sal_Int32 nFullAngle = nPostScriptAngle / 10;
+ sal_Int32 nTenthAngle = nPostScriptAngle % 10;
+
+ sal_Char pRotate [48];
+ sal_Int32 nChar = 0;
+
+ nChar = psp::getValueOf (nFullAngle, pRotate);
+ nChar += psp::appendStr (".", pRotate + nChar);
+ nChar += psp::getValueOf (nTenthAngle, pRotate + nChar);
+ nChar += psp::appendStr (" rotate\n", pRotate + nChar);
+
+ WritePS (mpPageBody, pRotate);
+}
+
+void
+PrinterGfx::PSPointOp (const Point& rPoint, const sal_Char* pOperator)
+{
+ sal_Char pPSCommand [48];
+ sal_Int32 nChar = 0;
+
+ nChar = psp::getValueOf (rPoint.X(), pPSCommand);
+ nChar += psp::appendStr (" ", pPSCommand + nChar);
+ nChar += psp::getValueOf (rPoint.Y(), pPSCommand + nChar);
+ nChar += psp::appendStr (" ", pPSCommand + nChar);
+ nChar += psp::appendStr (pOperator, pPSCommand + nChar);
+ nChar += psp::appendStr ("\n", pPSCommand + nChar);
+
+ DBG_ASSERT (nChar < 48, "Buffer overflow in PSPointOp");
+
+ WritePS (mpPageBody, pPSCommand);
+}
+
+void
+PrinterGfx::PSTranslate (const Point& rPoint)
+{
+ PSPointOp (rPoint, "translate");
+}
+
+void
+PrinterGfx::PSMoveTo (const Point& rPoint)
+{
+ PSPointOp (rPoint, "moveto");
+}
+
+void
+PrinterGfx::PSLineTo (const Point& rPoint)
+{
+ PSPointOp (rPoint, "lineto");
+}
+
+void
+PrinterGfx::PSRMoveTo (sal_Int32 nDx, sal_Int32 nDy)
+{
+ Point aPoint(nDx, nDy);
+ PSPointOp (aPoint, "rmoveto");
+}
+
+/* get a compressed representation of the path information */
+
+#define DEBUG_BINPATH 0
+
+void
+PrinterGfx::PSBinLineTo (const Point& rCurrent, Point& rOld, sal_Int32& nColumn)
+{
+#if (DEBUG_BINPATH == 1)
+ PSLineTo (rCurrent);
+#else
+ PSBinPath (rCurrent, rOld, lineto, nColumn);
+#endif
+}
+
+void
+PrinterGfx::PSBinMoveTo (const Point& rCurrent, Point& rOld, sal_Int32& nColumn)
+{
+#if (DEBUG_BINPATH == 1)
+ PSMoveTo (rCurrent);
+#else
+ PSBinPath (rCurrent, rOld, moveto, nColumn);
+#endif
+}
+
+void
+PrinterGfx::PSBinStartPath ()
+{
+#if (DEBUG_BINPATH == 1)
+ WritePS (mpPageBody, "% PSBinStartPath\n");
+#else
+ WritePS (mpPageBody, "readpath\n" );
+#endif
+}
+
+void
+PrinterGfx::PSBinEndPath ()
+{
+#if (DEBUG_BINPATH == 1)
+ WritePS (mpPageBody, "% PSBinEndPath\n");
+#else
+ WritePS (mpPageBody, "~\n");
+#endif
+}
+
+void
+PrinterGfx::PSBinCurrentPath (sal_uInt32 nPoints, const Point* pPath)
+{
+ // create the path
+ Point aPoint (0, 0);
+ sal_Int32 nColumn = 0;
+
+ PSBinStartPath ();
+ PSBinMoveTo (*pPath, aPoint, nColumn);
+ for (unsigned int i = 1; i < nPoints; i++)
+ PSBinLineTo (pPath[i], aPoint, nColumn);
+ PSBinEndPath ();
+}
+
+void
+PrinterGfx::PSBinPath (const Point& rCurrent, Point& rOld,
+ pspath_t eType, sal_Int32& nColumn)
+{
+ sal_Char pPath[48];
+ sal_Int32 nChar;
+
+ // create the hex representation of the dx and dy path shift, store the field
+ // width as it is needed for the building the command
+ sal_Int32 nXPrec = getAlignedHexValueOf (rCurrent.X() - rOld.X(), pPath + 1);
+ sal_Int32 nYPrec = getAlignedHexValueOf (rCurrent.Y() - rOld.Y(), pPath + 1 + nXPrec);
+ pPath [ 1 + nXPrec + nYPrec ] = 0;
+
+ // build the command, it is a char with bit represention 000cxxyy
+ // c represents the char, xx and yy repr. the field width of the dx and dy shift,
+ // dx and dy represent the number of bytes to read after the opcode
+ sal_Char cCmd = (eType == lineto ? (sal_Char)0x00 : (sal_Char)0x10);
+ switch (nYPrec)
+ {
+ case 2: break;
+ case 4: cCmd |= 0x01; break;
+ case 6: cCmd |= 0x02; break;
+ case 8: cCmd |= 0x03; break;
+ default: OSL_FAIL("invalid x precision in binary path");
+ }
+ switch (nXPrec)
+ {
+ case 2: break;
+ case 4: cCmd |= 0x04; break;
+ case 6: cCmd |= 0x08; break;
+ case 8: cCmd |= 0x0c; break;
+ default: OSL_FAIL("invalid y precision in binary path");
+ }
+ cCmd += 'A';
+ pPath[0] = cCmd;
+
+ // write the command to file,
+ // line breaking at column nMaxTextColumn (80)
+ nChar = 1 + nXPrec + nYPrec;
+ if ((nColumn + nChar) > nMaxTextColumn)
+ {
+ sal_Int32 nSegment = nMaxTextColumn - nColumn;
+
+ WritePS (mpPageBody, pPath, nSegment);
+ WritePS (mpPageBody, "\n", 1);
+ WritePS (mpPageBody, pPath + nSegment, nChar - nSegment);
+
+ nColumn = nChar - nSegment;
+ }
+ else
+ {
+ WritePS (mpPageBody, pPath, nChar);
+
+ nColumn += nChar;
+ }
+
+ rOld = rCurrent;
+}
+
+void
+PrinterGfx::PSScale (double fScaleX, double fScaleY)
+{
+ sal_Char pScale [48];
+ sal_Int32 nChar = 0;
+
+ nChar = psp::getValueOfDouble (pScale, fScaleX, 5);
+ nChar += psp::appendStr (" ", pScale + nChar);
+ nChar += psp::getValueOfDouble (pScale + nChar, fScaleY, 5);
+ nChar += psp::appendStr (" scale\n", pScale + nChar);
+
+ WritePS (mpPageBody, pScale);
+}
+
+/* psshowtext helper routines: draw an hex string for show/xshow */
+void
+PrinterGfx::PSHexString (const sal_uChar* pString, sal_Int16 nLen)
+{
+ sal_Char pHexString [128];
+ sal_Int32 nChar = 0;
+
+ nChar = psp::appendStr ("<", pHexString);
+ for (int i = 0; i < nLen; i++)
+ {
+ if (nChar >= (nMaxTextColumn - 1))
+ {
+ nChar += psp::appendStr ("\n", pHexString + nChar);
+ WritePS (mpPageBody, pHexString, nChar);
+ nChar = 0;
+ }
+ nChar += psp::getHexValueOf ((sal_Int32)pString[i], pHexString + nChar);
+ }
+
+ nChar += psp::appendStr (">\n", pHexString + nChar);
+ WritePS (mpPageBody, pHexString, nChar);
+}
+
+/* psshowtext helper routines: draw an array for xshow ps operator */
+void
+PrinterGfx::PSDeltaArray (const sal_Int32 *pArray, sal_Int16 nEntries)
+{
+ sal_Char pPSArray [128];
+ sal_Int32 nChar = 0;
+
+ nChar = psp::appendStr ("[", pPSArray + nChar);
+ nChar += psp::getValueOf (pArray[0], pPSArray + nChar);
+
+ for (int i = 1; i < nEntries; i++)
+ {
+ if (nChar >= (nMaxTextColumn - 1))
+ {
+ nChar += psp::appendStr ("\n", pPSArray + nChar);
+ WritePS (mpPageBody, pPSArray, nChar);
+ nChar = 0;
+ }
+
+ nChar += psp::appendStr (" ", pPSArray + nChar);
+ nChar += psp::getValueOf (pArray[i] - pArray[i-1], pPSArray + nChar);
+ }
+
+ nChar += psp::appendStr (" 0]\n", pPSArray + nChar);
+ WritePS (mpPageBody, pPSArray);
+}
+
+/* the DrawText equivalent, pDeltaArray may be NULL. For Type1 fonts or single byte
+ * fonts in general nBytes and nGlyphs is the same. For printer resident Composite
+ * fonts it may be different (these fonts may be SJIS encoded for example) */
+void
+PrinterGfx::PSShowText (const sal_uChar* pStr, sal_Int16 nGlyphs, sal_Int16 nBytes,
+ const sal_Int32* pDeltaArray)
+{
+ PSSetColor (maTextColor);
+ PSSetColor ();
+ PSSetFont ();
+ // rotate the user coordinate system
+ if (mnTextAngle != 0)
+ {
+ PSGSave ();
+ PSRotate (mnTextAngle);
+ }
+
+ sal_Char pBuffer[256];
+ if( maVirtualStatus.mbArtBold )
+ {
+ sal_Int32 nLW = maVirtualStatus.mnTextWidth;
+ if( nLW == 0 )
+ nLW = maVirtualStatus.mnTextHeight;
+ else
+ nLW = nLW < maVirtualStatus.mnTextHeight ? nLW : maVirtualStatus.mnTextHeight;
+ psp::getValueOfDouble( pBuffer, (double)nLW / 30.0 );
+ }
+ // dispatch to the drawing method
+ if (pDeltaArray == NULL)
+ {
+ PSHexString (pStr, nBytes);
+
+ if( maVirtualStatus.mbArtBold )
+ {
+ WritePS( mpPageBody, pBuffer );
+ WritePS( mpPageBody, " bshow\n" );
+ }
+ else
+ WritePS (mpPageBody, "show\n");
+ }
+ else
+ {
+ PSHexString (pStr, nBytes);
+ PSDeltaArray (pDeltaArray, nGlyphs - 1);
+ if( maVirtualStatus.mbArtBold )
+ {
+ WritePS( mpPageBody, pBuffer );
+ WritePS( mpPageBody, " bxshow\n" );
+ }
+ else
+ WritePS (mpPageBody, "xshow\n");
+ }
+
+ // restore the user coordinate system
+ if (mnTextAngle != 0)
+ PSGRestore ();
+}
+
+void
+PrinterGfx::PSComment( const sal_Char* pComment )
+{
+ const sal_Char* pLast = pComment;
+ while( pComment && *pComment )
+ {
+ while( *pComment && *pComment != '\n' && *pComment != '\r' )
+ pComment++;
+ if( pComment - pLast > 1 )
+ {
+ WritePS( mpPageBody, "% ", 2 );
+ WritePS( mpPageBody, pLast, pComment - pLast );
+ WritePS( mpPageBody, "\n", 1 );
+ }
+ if( *pComment )
+ pLast = ++pComment;
+ }
+}
+
+sal_Bool
+PrinterGfx::DrawEPS( const Rectangle& rBoundingBox, void* pPtr, sal_uInt32 nSize )
+{
+ if( nSize == 0 )
+ return sal_True;
+ if( ! mpPageBody )
+ return sal_False;
+
+ sal_Bool bSuccess = sal_False;
+
+ // first search the BoundingBox of the EPS data
+ SvMemoryStream aStream( pPtr, nSize, STREAM_READ );
+ aStream.Seek( STREAM_SEEK_TO_BEGIN );
+ ByteString aLine;
+
+ rtl::OString aDocTitle;
+ double fLeft = 0, fRight = 0, fTop = 0, fBottom = 0;
+ bool bEndComments = false;
+ while( ! aStream.IsEof()
+ && ( ( fLeft == 0 && fRight == 0 && fTop == 0 && fBottom == 0 ) ||
+ ( aDocTitle.getLength() == 0 && bEndComments == false ) )
+ )
+ {
+ aStream.ReadLine( aLine );
+ if( aLine.Len() > 1 && aLine.GetChar( 0 ) == '%' )
+ {
+ char cChar = aLine.GetChar(1);
+ if( cChar == '%' )
+ {
+ if( aLine.CompareIgnoreCaseToAscii( "%%BoundingBox:", 14 ) == COMPARE_EQUAL )
+ {
+ aLine = WhitespaceToSpace( aLine.GetToken( 1, ':' ) );
+ if( aLine.Len() && aLine.Search( "atend" ) == STRING_NOTFOUND )
+ {
+ fLeft = StringToDouble( GetCommandLineToken( 0, aLine ) );
+ fBottom = StringToDouble( GetCommandLineToken( 1, aLine ) );
+ fRight = StringToDouble( GetCommandLineToken( 2, aLine ) );
+ fTop = StringToDouble( GetCommandLineToken( 3, aLine ) );
+ }
+ }
+ else if( aLine.CompareIgnoreCaseToAscii( "%%Title:", 8 ) == COMPARE_EQUAL )
+ aDocTitle = WhitespaceToSpace( aLine.Copy( 8 ) );
+ else if( aLine.CompareIgnoreCaseToAscii( "%%EndComments", 13 ) == COMPARE_EQUAL )
+ bEndComments = true;
+ }
+ else if( cChar == ' ' || cChar == '\t' || cChar == '\r' || cChar == '\n' )
+ bEndComments = true;
+ }
+ else
+ bEndComments = true;
+ }
+
+ static sal_uInt16 nEps = 0;
+ if( ! aDocTitle.getLength() )
+ aDocTitle = rtl::OString::valueOf(static_cast<sal_Int32>(nEps++));
+
+ if( fLeft != fRight && fTop != fBottom )
+ {
+ double fScaleX = (double)rBoundingBox.GetWidth()/(fRight-fLeft);
+ double fScaleY = -(double)rBoundingBox.GetHeight()/(fTop-fBottom);
+ Point aTranslatePoint( (int)(rBoundingBox.Left()-fLeft*fScaleX),
+ (int)(rBoundingBox.Bottom()+1-fBottom*fScaleY) );
+ // prepare EPS
+ WritePS( mpPageBody,
+ "/b4_Inc_state save def\n"
+ "/dict_count countdictstack def\n"
+ "/op_count count 1 sub def\n"
+ "userdict begin\n"
+ "/showpage {} def\n"
+ "0 setgray 0 setlinecap 1 setlinewidth 0 setlinejoin\n"
+ "10 setmiterlimit [] 0 setdash newpath\n"
+ "/languagelevel where\n"
+ "{pop languagelevel\n"
+ "1 ne\n"
+ " {false setstrokeadjust false setoverprint\n"
+ " } if\n"
+ "}if\n" );
+ // set up clip path and scale
+ BeginSetClipRegion( 1 );
+ UnionClipRegion( rBoundingBox.Left(), rBoundingBox.Top(), rBoundingBox.GetWidth(), rBoundingBox.GetHeight() );
+ EndSetClipRegion();
+ PSTranslate( aTranslatePoint );
+ PSScale( fScaleX, fScaleY );
+
+ // DSC requires BeginDocument
+ WritePS( mpPageBody, "%%BeginDocument: " );
+ WritePS( mpPageBody, aDocTitle );
+ WritePS( mpPageBody, "\n" );
+
+ // write the EPS data
+ sal_uInt64 nOutLength;
+ mpPageBody->write( pPtr, nSize, nOutLength );
+ bSuccess = nOutLength == nSize;
+
+ // corresponding EndDocument
+ if( ((char*)pPtr)[ nSize-1 ] != '\n' )
+ WritePS( mpPageBody, "\n" );
+ WritePS( mpPageBody, "%%EndDocument\n" );
+
+ // clean up EPS
+ WritePS( mpPageBody,
+ "count op_count sub {pop} repeat\n"
+ "countdictstack dict_count sub {end} repeat\n"
+ "b4_Inc_state restore\n" );
+ }
+ return bSuccess;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/vcl/generic/print/fontsubst.cxx b/vcl/generic/print/fontsubst.cxx
new file mode 100644
index 000000000000..5c0dff6f25be
--- /dev/null
+++ b/vcl/generic/print/fontsubst.cxx
@@ -0,0 +1,227 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*************************************************************************
+ *
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * Copyright 2000, 2010 Oracle and/or its affiliates.
+ *
+ * OpenOffice.org - a multi-platform office productivity suite
+ *
+ * This file is part of OpenOffice.org.
+ *
+ * OpenOffice.org is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License version 3
+ * only, as published by the Free Software Foundation.
+ *
+ * OpenOffice.org is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License version 3 for more details
+ * (a copy is included in the LICENSE file that accompanied this code).
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * version 3 along with OpenOffice.org. If not, see
+ * <http://www.openoffice.org/license.html>
+ * for a copy of the LGPLv3 License.
+ *
+ ************************************************************************/
+
+// MARKER(update_precomp.py): autogen include statement, do not remove
+#include "precompiled_vcl.hxx"
+#include "generic/geninst.h"
+#include "generic/pspgraphics.h"
+#include "generic/glyphcache.hxx"
+
+#include "vcl/sysdata.hxx"
+#include "outfont.hxx"
+#include <i18npool/mslangid.hxx>
+
+#include "generic/printergfx.hxx"
+#include "salbmp.hxx"
+#include "impfont.hxx"
+#include "outfont.hxx"
+#include "outdev.h"
+#include "fontsubset.hxx"
+#include "salprn.hxx"
+#include "region.h"
+
+// ===========================================================================
+// platform specific font substitution hooks
+// ===========================================================================
+
+class FcPreMatchSubstititution
+: public ImplPreMatchFontSubstitution
+{
+public:
+ bool FindFontSubstitute( ImplFontSelectData& ) const;
+};
+
+class FcGlyphFallbackSubstititution
+: public ImplGlyphFallbackFontSubstitution
+{
+ // TODO: add a cache
+public:
+ bool FindFontSubstitute( ImplFontSelectData&, OUString& rMissingCodes ) const;
+};
+
+void RegisterFontSubstitutors( ImplDevFontList* pList )
+{
+ // 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 FcPreMatchSubstititution aSubstPreMatch;
+ pList->SetPreMatchHook( &aSubstPreMatch );
+ }
+
+ // register glyph fallback substitutions (unless disabled by bit1)
+ if( (nDisableBits & 2) == 0 )
+ {
+ static FcGlyphFallbackSubstititution aSubstFallback;
+ pList->SetFallbackHook( &aSubstFallback );
+ }
+}
+
+// -----------------------------------------------------------------------
+
+static ImplFontSelectData GetFcSubstitute(const ImplFontSelectData &rFontSelData, OUString& rMissingCodes )
+{
+ ImplFontSelectData aRet(rFontSelData);
+
+ const rtl::OString aLangAttrib = MsLangId::convertLanguageToIsoByteString( rFontSelData.meLanguage );
+
+ FontItalic eItalic = rFontSelData.GetSlant();
+ FontWeight eWeight = rFontSelData.GetWeight();
+ FontWidth eWidth = rFontSelData.GetWidthType();
+ FontPitch ePitch = rFontSelData.GetPitch();
+
+ const psp::PrintFontManager& rMgr = psp::PrintFontManager::get();
+ aRet.maSearchName = rMgr.Substitute( rFontSelData.maTargetName, rMissingCodes, aLangAttrib, eItalic, eWeight, eWidth, ePitch);
+
+ aRet.meItalic = eItalic;
+ aRet.meWeight = eWeight;
+ aRet.meWidthType = eWidth;
+ aRet.mePitch = ePitch;
+
+ return aRet;
+}
+
+namespace
+{
+ bool uselessmatch(const ImplFontSelectData &rOrig, const ImplFontSelectData &rNew)
+ {
+ return
+ (
+ rOrig.maTargetName == rNew.maSearchName &&
+ rOrig.meWeight == rNew.meWeight &&
+ rOrig.meItalic == rNew.meItalic &&
+ rOrig.mePitch == rNew.mePitch &&
+ rOrig.meWidthType == rNew.meWidthType
+ );
+ }
+}
+
+//--------------------------------------------------------------------------
+
+bool FcPreMatchSubstititution::FindFontSubstitute( ImplFontSelectData &rFontSelData ) const
+{
+ // We dont' 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( 0 == rFontSelData.maSearchName.CompareIgnoreCaseToAscii( "starsymbol", 10)
+ || 0 == rFontSelData.maSearchName.CompareIgnoreCaseToAscii( "opensymbol", 10) )
+ return false;
+
+ //Note: see fdo#41556 if you feel compelled to cache the results here,
+ //remember that 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 on all the input and be don't just
+ //return the original selection data with the fontname updated
+ rtl::OUString aDummy;
+ const ImplFontSelectData aOut = GetFcSubstitute( rFontSelData, aDummy );
+
+ if( !aOut.maSearchName.Len() )
+ return false;
+
+ const bool bHaveSubstitute = !uselessmatch( rFontSelData, aOut );
+
+#ifdef DEBUG
+ const ByteString aOrigName( rFontSelData.maTargetName, RTL_TEXTENCODING_UTF8 );
+ const ByteString aSubstName( aOut.maSearchName, RTL_TEXTENCODING_UTF8 );
+ printf( "FcPreMatchSubstititution \"%s\" bipw=%d%d%d%d -> ",
+ aOrigName.GetBuffer(), rFontSelData.meWeight, rFontSelData.meItalic,
+ rFontSelData.mePitch, rFontSelData.meWidthType );
+ if( !bHaveSubstitute )
+ printf( "no substitute available\n" );
+ else
+ printf( "\"%s\" bipw=%d%d%d%d\n", aSubstName.GetBuffer(),
+ aOut.meWeight, aOut.meItalic, aOut.mePitch, aOut.meWidthType );
+#endif
+
+ if( bHaveSubstitute )
+ rFontSelData = aOut;
+
+ return bHaveSubstitute;
+}
+
+// -----------------------------------------------------------------------
+
+bool FcGlyphFallbackSubstititution::FindFontSubstitute( ImplFontSelectData& rFontSelData,
+ rtl::OUString& rMissingCodes ) const
+{
+ // We dont' 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( 0 == rFontSelData.maSearchName.CompareIgnoreCaseToAscii( "starsymbol", 10)
+ || 0 == rFontSelData.maSearchName.CompareIgnoreCaseToAscii( "opensymbol", 10) )
+ return false;
+
+ const ImplFontSelectData 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.Len() )
+ return false;
+
+ const bool bHaveSubstitute = !uselessmatch( rFontSelData, aOut );
+
+#ifdef DEBUG
+ const ByteString aOrigName( rFontSelData.maTargetName, RTL_TEXTENCODING_UTF8 );
+ const ByteString aSubstName( aOut.maSearchName, RTL_TEXTENCODING_UTF8 );
+ printf( "FcGFSubstititution \"%s\" bipw=%d%d%d%d ->",
+ aOrigName.GetBuffer(), rFontSelData.meWeight, rFontSelData.meItalic,
+ rFontSelData.mePitch, rFontSelData.meWidthType );
+ if( !bHaveSubstitute )
+ printf( "no substitute available\n" );
+ else
+ printf( "\"%s\" bipw=%d%d%d%d\n", aSubstName.GetBuffer(),
+ aOut.meWeight, aOut.meItalic, aOut.mePitch, aOut.meWidthType );
+#endif
+
+ if( bHaveSubstitute )
+ rFontSelData = aOut;
+
+ return bHaveSubstitute;
+}
+
+// ===========================================================================
+
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/vcl/generic/print/genprnpsp.cxx b/vcl/generic/print/genprnpsp.cxx
new file mode 100644
index 000000000000..c0b88fe001b7
--- /dev/null
+++ b/vcl/generic/print/genprnpsp.cxx
@@ -0,0 +1,1418 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*************************************************************************
+ *
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * Copyright 2000, 2010 Oracle and/or its affiliates.
+ *
+ * OpenOffice.org - a multi-platform office productivity suite
+ *
+ * This file is part of OpenOffice.org.
+ *
+ * OpenOffice.org is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License version 3
+ * only, as published by the Free Software Foundation.
+ *
+ * OpenOffice.org is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License version 3 for more details
+ * (a copy is included in the LICENSE file that accompanied this code).
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * version 3 along with OpenOffice.org. If not, see
+ * <http://www.openoffice.org/license.html>
+ * for a copy of the LGPLv3 License.
+ *
+ ************************************************************************/
+
+// MARKER(update_precomp.py): autogen include statement, do not remove
+#include "precompiled_vcl.hxx"
+
+/**
+ this file implements the sal printer interface ( SalPrinter, SalInfoPrinter
+ and some printer relevant methods of SalInstance and SalGraphicsData )
+
+ as aunderlying library the printer features of psprint are used.
+
+ The query methods of a SalInfoPrinter are implemented by querying psprint
+
+ The job methods of a SalPrinter are implemented by calling psprint
+ printer job functions.
+ */
+
+#include <unistd.h>
+#include <sys/wait.h>
+#include <sys/stat.h>
+
+#include "rtl/ustring.hxx"
+
+#include "osl/module.h"
+
+#include "vcl/svapp.hxx"
+#include "vcl/print.hxx"
+#include "vcl/pdfwriter.hxx"
+#include "vcl/printerinfomanager.hxx"
+
+#include "saldatabasic.hxx"
+#include "generic/genprn.h"
+#include "generic/geninst.h"
+#include "generic/pspgraphics.h"
+
+#include "jobset.h"
+#include "print.h"
+#include "salptype.hxx"
+
+#include <com/sun/star/beans/PropertyValue.hpp>
+
+using namespace psp;
+using namespace com::sun::star;
+
+using ::rtl::OUString;
+using ::rtl::OUStringHash;
+using ::rtl::OUStringToOString;
+
+/*
+ * static helpers
+ */
+
+static oslModule driverLib = NULL;
+extern "C"
+{
+typedef int(*setupFunction)(PrinterInfo&);
+static setupFunction pSetupFunction = NULL;
+typedef int(*faxFunction)(String&);
+static faxFunction pFaxNrFunction = NULL;
+}
+
+static String getPdfDir( const PrinterInfo& rInfo )
+{
+ String aDir;
+ sal_Int32 nIndex = 0;
+ while( nIndex != -1 )
+ {
+ OUString aToken( rInfo.m_aFeatures.getToken( 0, ',', nIndex ) );
+ if( ! aToken.compareToAscii( "pdf=", 4 ) )
+ {
+ sal_Int32 nPos = 0;
+ aDir = aToken.getToken( 1, '=', nPos );
+ if( ! aDir.Len() )
+ aDir = String( ByteString( getenv( "HOME" ) ), osl_getThreadTextEncoding() );
+ break;
+ }
+ }
+ return aDir;
+}
+
+static void getPaLib()
+{
+ if( ! driverLib )
+ {
+ OUString aLibName( RTL_CONSTASCII_USTRINGPARAM( _XSALSET_LIBNAME ) );
+ driverLib = osl_loadModuleRelative( (oslGenericFunction)getPaLib, aLibName.pData, SAL_LOADMODULE_DEFAULT );
+ if ( !driverLib )
+ {
+ return;
+ }
+
+ pSetupFunction = (setupFunction)osl_getAsciiFunctionSymbol( driverLib, "Sal_SetupPrinterDriver" );
+ if ( !pSetupFunction )
+ fprintf( stderr, "could not resolve Sal_SetupPrinterDriver\n" );
+
+ pFaxNrFunction = (faxFunction)osl_getAsciiFunctionSymbol( driverLib, "Sal_queryFaxNumber" );
+ if ( !pFaxNrFunction )
+ fprintf( stderr, "could not resolve Sal_queryFaxNumber\n" );
+ }
+}
+
+inline int PtTo10Mu( int nPoints ) { return (int)((((double)nPoints)*35.27777778)+0.5); }
+
+inline int TenMuToPt( int nUnits ) { return (int)((((double)nUnits)/35.27777778)+0.5); }
+
+static void copyJobDataToJobSetup( ImplJobSetup* pJobSetup, JobData& rData )
+{
+ pJobSetup->meOrientation = (Orientation)(rData.m_eOrientation == orientation::Landscape ? ORIENTATION_LANDSCAPE : ORIENTATION_PORTRAIT);
+
+ // copy page size
+ String aPaper;
+ int width, height;
+
+ rData.m_aContext.getPageSize( aPaper, width, height );
+ pJobSetup->mePaperFormat = PaperInfo::fromPSName(OUStringToOString( aPaper, RTL_TEXTENCODING_ISO_8859_1 ));
+
+ pJobSetup->mnPaperWidth = 0;
+ pJobSetup->mnPaperHeight = 0;
+ if( pJobSetup->mePaperFormat == PAPER_USER )
+ {
+ // transform to 100dth mm
+ width = PtTo10Mu( width );
+ height = PtTo10Mu( height );
+
+ if( rData.m_eOrientation == psp::orientation::Portrait )
+ {
+ pJobSetup->mnPaperWidth = width;
+ pJobSetup->mnPaperHeight= height;
+ }
+ else
+ {
+ pJobSetup->mnPaperWidth = height;
+ pJobSetup->mnPaperHeight= width;
+ }
+ }
+
+ // copy input slot
+ const PPDKey* pKey = NULL;
+ const PPDValue* pValue = NULL;
+
+ pJobSetup->mnPaperBin = 0;
+ if( rData.m_pParser )
+ pKey = rData.m_pParser->getKey( String( RTL_CONSTASCII_USTRINGPARAM( "InputSlot" ) ) );
+ if( pKey )
+ pValue = rData.m_aContext.getValue( pKey );
+ if( pKey && pValue )
+ {
+ for( pJobSetup->mnPaperBin = 0;
+ pValue != pKey->getValue( pJobSetup->mnPaperBin ) &&
+ pJobSetup->mnPaperBin < pKey->countValues();
+ pJobSetup->mnPaperBin++ )
+ ;
+ if( pJobSetup->mnPaperBin >= pKey->countValues() )
+ pJobSetup->mnPaperBin = 0;
+ }
+
+ // copy duplex
+ pKey = NULL;
+ pValue = NULL;
+
+ pJobSetup->meDuplexMode = DUPLEX_UNKNOWN;
+ if( rData.m_pParser )
+ pKey = rData.m_pParser->getKey( String( RTL_CONSTASCII_USTRINGPARAM( "Duplex" ) ) );
+ if( pKey )
+ pValue = rData.m_aContext.getValue( pKey );
+ if( pKey && pValue )
+ {
+ if( pValue->m_aOption.EqualsIgnoreCaseAscii( "None" ) ||
+ pValue->m_aOption.EqualsIgnoreCaseAscii( "Simplex", 0, 7 )
+ )
+ {
+ pJobSetup->meDuplexMode = DUPLEX_OFF;
+ }
+ else if( pValue->m_aOption.EqualsIgnoreCaseAscii( "DuplexNoTumble" ) )
+ {
+ pJobSetup->meDuplexMode = DUPLEX_LONGEDGE;
+ }
+ else if( pValue->m_aOption.EqualsIgnoreCaseAscii( "DuplexTumble" ) )
+ {
+ pJobSetup->meDuplexMode = DUPLEX_SHORTEDGE;
+ }
+ }
+
+ // copy the whole context
+ if( pJobSetup->mpDriverData )
+ rtl_freeMemory( pJobSetup->mpDriverData );
+
+ int nBytes;
+ void* pBuffer = NULL;
+ if( rData.getStreamBuffer( pBuffer, nBytes ) )
+ {
+ pJobSetup->mnDriverDataLen = nBytes;
+ pJobSetup->mpDriverData = (sal_uInt8*)pBuffer;
+ }
+ else
+ {
+ pJobSetup->mnDriverDataLen = 0;
+ pJobSetup->mpDriverData = NULL;
+ }
+}
+
+static bool passFileToCommandLine( const String& rFilename, const String& rCommandLine, bool bRemoveFile = true )
+{
+ bool bSuccess = false;
+
+ rtl_TextEncoding aEncoding = osl_getThreadTextEncoding();
+ ByteString aCmdLine( rCommandLine, aEncoding );
+ ByteString aFilename( rFilename, aEncoding );
+
+ bool bPipe = aCmdLine.Search( "(TMP)" ) != STRING_NOTFOUND ? false : true;
+
+ // setup command line for exec
+ if( ! bPipe )
+ while( aCmdLine.SearchAndReplace( "(TMP)", aFilename ) != STRING_NOTFOUND )
+ ;
+
+#if OSL_DEBUG_LEVEL > 1
+ fprintf( stderr, "%s commandline: \"%s\"\n",
+ bPipe ? "piping to" : "executing",
+ aCmdLine.GetBuffer() );
+ struct stat aStat;
+ if( stat( aFilename.GetBuffer(), &aStat ) )
+ fprintf( stderr, "stat( %s ) failed\n", aFilename.GetBuffer() );
+ fprintf( stderr, "Tmp file %s has modes: 0%03lo\n", aFilename.GetBuffer(), (long)aStat.st_mode );
+#endif
+ const char* argv[4];
+ if( ! ( argv[ 0 ] = getenv( "SHELL" ) ) )
+ argv[ 0 ] = "/bin/sh";
+ argv[ 1 ] = "-c";
+ argv[ 2 ] = aCmdLine.GetBuffer();
+ argv[ 3 ] = 0;
+
+ bool bHavePipes = false;
+ int pid, fd[2];
+
+ if( bPipe )
+ bHavePipes = pipe( fd ) ? false : true;
+ if( ( pid = fork() ) > 0 )
+ {
+ if( bPipe && bHavePipes )
+ {
+ close( fd[0] );
+ char aBuffer[ 2048 ];
+ FILE* fp = fopen( aFilename.GetBuffer(), "r" );
+ while (fp && !feof(fp))
+ {
+ size_t nBytesRead = fread(aBuffer, 1, sizeof( aBuffer ), fp);
+ if (nBytesRead )
+ {
+ size_t nBytesWritten = write(fd[1], aBuffer, nBytesRead);
+ OSL_ENSURE(nBytesWritten == nBytesRead, "short write");
+ if (nBytesWritten != nBytesRead)
+ break;
+ }
+ }
+ fclose( fp );
+ close( fd[ 1 ] );
+ }
+ int status = 0;
+ waitpid( pid, &status, 0 );
+ if( ! status )
+ bSuccess = true;
+ }
+ else if( ! pid )
+ {
+ if( bPipe && bHavePipes )
+ {
+ close( fd[1] );
+ if( fd[0] != STDIN_FILENO ) // not probable, but who knows :)
+ dup2( fd[0], STDIN_FILENO );
+ }
+ execv( argv[0], const_cast<char**>(argv) );
+ fprintf( stderr, "failed to execute \"%s\"\n", aCmdLine.GetBuffer() );
+ _exit( 1 );
+ }
+ else
+ fprintf( stderr, "failed to fork\n" );
+
+ // clean up the mess
+ if( bRemoveFile )
+ unlink( aFilename.GetBuffer() );
+
+ return bSuccess;
+}
+
+static bool sendAFax( const String& rFaxNumber, const String& rFileName, const String& rCommand )
+{
+ std::list< OUString > aFaxNumbers;
+
+ if( ! rFaxNumber.Len() )
+ {
+ getPaLib();
+ if( pFaxNrFunction )
+ {
+ String aNewNr;
+ if( pFaxNrFunction( aNewNr ) )
+ aFaxNumbers.push_back( OUString( aNewNr ) );
+ }
+ }
+ else
+ {
+ sal_Int32 nIndex = 0;
+ OUString aFaxes( rFaxNumber );
+ OUString aBeginToken( RTL_CONSTASCII_USTRINGPARAM("<Fax#>") );
+ OUString aEndToken( RTL_CONSTASCII_USTRINGPARAM("</Fax#>") );
+ while( nIndex != -1 )
+ {
+ nIndex = aFaxes.indexOf( aBeginToken, nIndex );
+ if( nIndex != -1 )
+ {
+ sal_Int32 nBegin = nIndex + aBeginToken.getLength();
+ nIndex = aFaxes.indexOf( aEndToken, nIndex );
+ if( nIndex != -1 )
+ {
+ aFaxNumbers.push_back( aFaxes.copy( nBegin, nIndex-nBegin ) );
+ nIndex += aEndToken.getLength();
+ }
+ }
+ }
+ }
+
+ bool bSuccess = true;
+ if( aFaxNumbers.begin() != aFaxNumbers.end() )
+ {
+ while( aFaxNumbers.begin() != aFaxNumbers.end() && bSuccess )
+ {
+ String aCmdLine( rCommand );
+ String aFaxNumber( aFaxNumbers.front() );
+ aFaxNumbers.pop_front();
+ while( aCmdLine.SearchAndReplace( String( RTL_CONSTASCII_USTRINGPARAM( "(PHONE)" ) ), aFaxNumber ) != STRING_NOTFOUND )
+ ;
+#if OSL_DEBUG_LEVEL > 1
+ fprintf( stderr, "sending fax to \"%s\"\n", OUStringToOString( aFaxNumber, osl_getThreadTextEncoding() ).getStr() );
+#endif
+ bSuccess = passFileToCommandLine( rFileName, aCmdLine, false );
+ }
+ }
+ else
+ bSuccess = false;
+
+ // clean up temp file
+ unlink( ByteString( rFileName, osl_getThreadTextEncoding() ).GetBuffer() );
+
+ return bSuccess;
+}
+
+static bool createPdf( const String& rToFile, const String& rFromFile, const String& rCommandLine )
+{
+ String aCommandLine( rCommandLine );
+ while( aCommandLine.SearchAndReplace( String( RTL_CONSTASCII_USTRINGPARAM( "(OUTFILE)" ) ), rToFile ) != STRING_NOTFOUND )
+ ;
+ return passFileToCommandLine( rFromFile, aCommandLine );
+}
+
+/*
+ * SalInstance
+ */
+
+SalInfoPrinter* GenericInstance::CreateInfoPrinter( SalPrinterQueueInfo* pQueueInfo,
+ ImplJobSetup* pJobSetup )
+{
+ mbPrinterInit = true;
+ // create and initialize SalInfoPrinter
+ PspSalInfoPrinter* pPrinter = new PspSalInfoPrinter;
+
+ if( pJobSetup )
+ {
+ PrinterInfoManager& rManager( PrinterInfoManager::get() );
+ PrinterInfo aInfo( rManager.getPrinterInfo( pQueueInfo->maPrinterName ) );
+ pPrinter->m_aJobData = aInfo;
+ pPrinter->m_aPrinterGfx.Init( pPrinter->m_aJobData );
+
+ if( pJobSetup->mpDriverData )
+ JobData::constructFromStreamBuffer( pJobSetup->mpDriverData, pJobSetup->mnDriverDataLen, aInfo );
+
+ pJobSetup->mnSystem = JOBSETUP_SYSTEM_UNIX;
+ pJobSetup->maPrinterName = pQueueInfo->maPrinterName;
+ pJobSetup->maDriver = aInfo.m_aDriverName;
+ copyJobDataToJobSetup( pJobSetup, aInfo );
+
+ // set/clear backwards compatibility flag
+ bool bStrictSO52Compatibility = false;
+ boost::unordered_map<rtl::OUString, rtl::OUString, rtl::OUStringHash >::const_iterator compat_it =
+ pJobSetup->maValueMap.find( rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "StrictSO52Compatibility" ) ) );
+
+ if( compat_it != pJobSetup->maValueMap.end() )
+ {
+ if( compat_it->second.equalsIgnoreAsciiCaseAscii( "true" ) )
+ bStrictSO52Compatibility = true;
+ }
+ pPrinter->m_aPrinterGfx.setStrictSO52Compatibility( bStrictSO52Compatibility );
+ }
+
+
+ return pPrinter;
+}
+
+void GenericInstance::DestroyInfoPrinter( SalInfoPrinter* pPrinter )
+{
+ delete pPrinter;
+}
+
+SalPrinter* GenericInstance::CreatePrinter( SalInfoPrinter* pInfoPrinter )
+{
+ mbPrinterInit = true;
+ // create and initialize SalPrinter
+ PspSalPrinter* pPrinter = new PspSalPrinter( pInfoPrinter );
+ pPrinter->m_aJobData = static_cast<PspSalInfoPrinter*>(pInfoPrinter)->m_aJobData;
+
+ return pPrinter;
+}
+
+void GenericInstance::DestroyPrinter( SalPrinter* pPrinter )
+{
+ delete pPrinter;
+}
+
+void GenericInstance::GetPrinterQueueInfo( ImplPrnQueueList* pList )
+{
+ mbPrinterInit = true;
+ PrinterInfoManager& rManager( PrinterInfoManager::get() );
+ static const char* pNoSyncDetection = getenv( "SAL_DISABLE_SYNCHRONOUS_PRINTER_DETECTION" );
+ if( ! pNoSyncDetection || ! *pNoSyncDetection )
+ {
+ // #i62663# synchronize possible asynchronouse printer detection now
+ rManager.checkPrintersChanged( true );
+ }
+ ::std::list< OUString > aPrinters;
+ rManager.listPrinters( aPrinters );
+
+ for( ::std::list< OUString >::iterator it = aPrinters.begin(); it != aPrinters.end(); ++it )
+ {
+ const PrinterInfo& rInfo( rManager.getPrinterInfo( *it ) );
+ // Neuen Eintrag anlegen
+ SalPrinterQueueInfo* pInfo = new SalPrinterQueueInfo;
+ pInfo->maPrinterName = *it;
+ pInfo->maDriver = rInfo.m_aDriverName;
+ pInfo->maLocation = rInfo.m_aLocation;
+ pInfo->maComment = rInfo.m_aComment;
+ pInfo->mpSysData = NULL;
+
+ sal_Int32 nIndex = 0;
+ while( nIndex != -1 )
+ {
+ String aToken( rInfo.m_aFeatures.getToken( 0, ',', nIndex ) );
+ if( aToken.CompareToAscii( "pdf=", 4 ) == COMPARE_EQUAL )
+ {
+ pInfo->maLocation = getPdfDir( rInfo );
+ break;
+ }
+ }
+
+ pList->Add( pInfo );
+ }
+}
+
+void GenericInstance::DeletePrinterQueueInfo( SalPrinterQueueInfo* pInfo )
+{
+ delete pInfo;
+}
+
+void GenericInstance::GetPrinterQueueState( SalPrinterQueueInfo* )
+{
+ mbPrinterInit = true;
+}
+
+String GenericInstance::GetDefaultPrinter()
+{
+ mbPrinterInit = true;
+ PrinterInfoManager& rManager( PrinterInfoManager::get() );
+ return rManager.getDefaultPrinter();
+}
+
+PspSalInfoPrinter::PspSalInfoPrinter()
+{
+ m_pGraphics = NULL;
+ m_bPapersInit = false;
+}
+
+PspSalInfoPrinter::~PspSalInfoPrinter()
+{
+ if( m_pGraphics )
+ {
+ delete m_pGraphics;
+ m_pGraphics = NULL;
+ }
+}
+
+void PspSalInfoPrinter::InitPaperFormats( const ImplJobSetup* )
+{
+ m_aPaperFormats.clear();
+ m_bPapersInit = true;
+
+ if( m_aJobData.m_pParser )
+ {
+ const PPDKey* pKey = m_aJobData.m_pParser->getKey( String( RTL_CONSTASCII_USTRINGPARAM( "PageSize" ) ) );
+ if( pKey )
+ {
+ int nValues = pKey->countValues();
+ for( int i = 0; i < nValues; i++ )
+ {
+ const PPDValue* pValue = pKey->getValue( i );
+ int nWidth = 0, nHeight = 0;
+ m_aJobData.m_pParser->getPaperDimension( pValue->m_aOption, nWidth, nHeight );
+ PaperInfo aInfo(PtTo10Mu( nWidth ), PtTo10Mu( nHeight ));
+ m_aPaperFormats.push_back( aInfo );
+ }
+ }
+ }
+}
+
+int PspSalInfoPrinter::GetLandscapeAngle( const ImplJobSetup* )
+{
+ return 900;
+}
+
+SalGraphics* PspSalInfoPrinter::GetGraphics()
+{
+ // return a valid pointer only once
+ // the reasoning behind this is that we could have different
+ // SalGraphics that can run in multiple threads
+ // (future plans)
+ SalGraphics* pRet = NULL;
+ if( ! m_pGraphics )
+ {
+ m_pGraphics = new PspGraphics( &m_aJobData, &m_aPrinterGfx, NULL, false, this );
+ m_pGraphics->SetLayout( 0 );
+ pRet = m_pGraphics;
+ }
+ return pRet;
+}
+
+void PspSalInfoPrinter::ReleaseGraphics( SalGraphics* pGraphics )
+{
+ if( pGraphics == m_pGraphics )
+ {
+ delete pGraphics;
+ m_pGraphics = NULL;
+ }
+ return;
+}
+
+sal_Bool PspSalInfoPrinter::Setup( SalFrame* pFrame, ImplJobSetup* pJobSetup )
+{
+ if( ! pFrame || ! pJobSetup )
+ return sal_False;
+
+ getPaLib();
+
+ if( ! pSetupFunction )
+ return sal_False;
+
+ PrinterInfoManager& rManager = PrinterInfoManager::get();
+
+ PrinterInfo aInfo( rManager.getPrinterInfo( pJobSetup->maPrinterName ) );
+ if ( pJobSetup->mpDriverData )
+ {
+ SetData( ~0, pJobSetup );
+ JobData::constructFromStreamBuffer( pJobSetup->mpDriverData, pJobSetup->mnDriverDataLen, aInfo );
+ }
+
+ if( pSetupFunction( aInfo ) )
+ {
+ rtl_freeMemory( pJobSetup->mpDriverData );
+ pJobSetup->mpDriverData = NULL;
+
+ int nBytes;
+ void* pBuffer = NULL;
+ aInfo.getStreamBuffer( pBuffer, nBytes );
+ pJobSetup->mnDriverDataLen = nBytes;
+ pJobSetup->mpDriverData = (sal_uInt8*)pBuffer;
+
+ // copy everything to job setup
+ copyJobDataToJobSetup( pJobSetup, aInfo );
+ JobData::constructFromStreamBuffer( pJobSetup->mpDriverData, pJobSetup->mnDriverDataLen, m_aJobData );
+ return sal_True;
+ }
+ return sal_False;
+}
+
+// This function gets the driver data and puts it into pJobSetup
+// If pJobSetup->mpDriverData is NOT NULL, then the independend
+// data should be merged into the driver data
+// If pJobSetup->mpDriverData IS NULL, then the driver defaults
+// should be merged into the independent data
+sal_Bool PspSalInfoPrinter::SetPrinterData( ImplJobSetup* pJobSetup )
+{
+ // set/clear backwards compatibility flag
+ bool bStrictSO52Compatibility = false;
+ boost::unordered_map<rtl::OUString, rtl::OUString, rtl::OUStringHash >::const_iterator compat_it =
+ pJobSetup->maValueMap.find( rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "StrictSO52Compatibility" ) ) );
+
+ if( compat_it != pJobSetup->maValueMap.end() )
+ {
+ if( compat_it->second.equalsIgnoreAsciiCaseAscii( "true" ) )
+ bStrictSO52Compatibility = true;
+ }
+ m_aPrinterGfx.setStrictSO52Compatibility( bStrictSO52Compatibility );
+
+ if( pJobSetup->mpDriverData )
+ return SetData( ~0, pJobSetup );
+
+ copyJobDataToJobSetup( pJobSetup, m_aJobData );
+
+ return sal_True;
+}
+
+// This function merges the independ driver data
+// and sets the new independ data in pJobSetup
+// Only the data must be changed, where the bit
+// in nGetDataFlags is set
+sal_Bool PspSalInfoPrinter::SetData(
+ sal_uLong nSetDataFlags,
+ ImplJobSetup* pJobSetup )
+{
+ JobData aData;
+ JobData::constructFromStreamBuffer( pJobSetup->mpDriverData, pJobSetup->mnDriverDataLen, aData );
+
+ if( aData.m_pParser )
+ {
+ const PPDKey* pKey;
+ const PPDValue* pValue;
+
+ // merge papersize if necessary
+ if( nSetDataFlags & SAL_JOBSET_PAPERSIZE )
+ {
+ String aPaper;
+
+ if( pJobSetup->mePaperFormat == PAPER_USER )
+ aPaper = aData.m_pParser->matchPaper(
+ TenMuToPt( pJobSetup->mnPaperWidth ),
+ TenMuToPt( pJobSetup->mnPaperHeight ) );
+ else
+ aPaper = rtl::OStringToOUString(PaperInfo::toPSName(pJobSetup->mePaperFormat), RTL_TEXTENCODING_ISO_8859_1);
+
+ pKey = aData.m_pParser->getKey( String( RTL_CONSTASCII_USTRINGPARAM( "PageSize" ) ) );
+ pValue = pKey ? pKey->getValueCaseInsensitive( aPaper ) : NULL;
+
+ // some PPD files do not specify the standard paper names (e.g. C5 instead of EnvC5)
+ // try to find the correct paper anyway using the size
+ if( pKey && ! pValue && pJobSetup->mePaperFormat != PAPER_USER )
+ {
+ PaperInfo aInfo( pJobSetup->mePaperFormat );
+ aPaper = aData.m_pParser->matchPaper(
+ TenMuToPt( aInfo.getWidth() ),
+ TenMuToPt( aInfo.getHeight() ) );
+ pValue = pKey->getValueCaseInsensitive( aPaper );
+ }
+
+ if( ! ( pKey && pValue && aData.m_aContext.setValue( pKey, pValue, false ) == pValue ) )
+ return sal_False;
+ }
+
+ // merge paperbin if necessary
+ if( nSetDataFlags & SAL_JOBSET_PAPERBIN )
+ {
+ pKey = aData.m_pParser->getKey( String( RTL_CONSTASCII_USTRINGPARAM( "InputSlot" ) ) );
+ if( pKey )
+ {
+ int nPaperBin = pJobSetup->mnPaperBin;
+ if( nPaperBin >= pKey->countValues() )
+ pValue = pKey->getDefaultValue();
+ else
+ pValue = pKey->getValue( pJobSetup->mnPaperBin );
+
+ // may fail due to constraints;
+ // real paper bin is copied back to jobsetup in that case
+ aData.m_aContext.setValue( pKey, pValue );
+ }
+ // if printer has no InputSlot key simply ignore this setting
+ // (e.g. SGENPRT has no InputSlot)
+ }
+
+ // merge orientation if necessary
+ if( nSetDataFlags & SAL_JOBSET_ORIENTATION )
+ aData.m_eOrientation = pJobSetup->meOrientation == ORIENTATION_LANDSCAPE ? orientation::Landscape : orientation::Portrait;
+
+ // merge duplex if necessary
+ if( nSetDataFlags & SAL_JOBSET_DUPLEXMODE )
+ {
+ pKey = aData.m_pParser->getKey( String( RTL_CONSTASCII_USTRINGPARAM( "Duplex" ) ) );
+ if( pKey )
+ {
+ pValue = NULL;
+ switch( pJobSetup->meDuplexMode )
+ {
+ case DUPLEX_OFF:
+ pValue = pKey->getValue( String( RTL_CONSTASCII_USTRINGPARAM( "None" ) ) );
+ if( pValue == NULL )
+ pValue = pKey->getValue( String( RTL_CONSTASCII_USTRINGPARAM( "SimplexNoTumble" ) ) );
+ break;
+ case DUPLEX_SHORTEDGE:
+ pValue = pKey->getValue( String( RTL_CONSTASCII_USTRINGPARAM( "DuplexTumble" ) ) );
+ break;
+ case DUPLEX_LONGEDGE:
+ pValue = pKey->getValue( String( RTL_CONSTASCII_USTRINGPARAM( "DuplexNoTumble" ) ) );
+ break;
+ case DUPLEX_UNKNOWN:
+ default:
+ pValue = 0;
+ break;
+ }
+ if( ! pValue )
+ pValue = pKey->getDefaultValue();
+ aData.m_aContext.setValue( pKey, pValue );
+ }
+ }
+
+ m_aJobData = aData;
+ copyJobDataToJobSetup( pJobSetup, aData );
+ return sal_True;
+ }
+
+ return sal_False;
+}
+
+void PspSalInfoPrinter::GetPageInfo(
+ const ImplJobSetup* pJobSetup,
+ long& rOutWidth, long& rOutHeight,
+ long& rPageOffX, long& rPageOffY,
+ long& rPageWidth, long& rPageHeight )
+{
+ if( ! pJobSetup )
+ return;
+
+ JobData aData;
+ JobData::constructFromStreamBuffer( pJobSetup->mpDriverData, pJobSetup->mnDriverDataLen, aData );
+
+ // get the selected page size
+ if( aData.m_pParser )
+ {
+
+ String aPaper;
+ int width, height;
+ int left = 0, top = 0, right = 0, bottom = 0;
+ int nDPI = aData.m_aContext.getRenderResolution();
+
+
+ if( aData.m_eOrientation == psp::orientation::Portrait )
+ {
+ aData.m_aContext.getPageSize( aPaper, width, height );
+ aData.m_pParser->getMargins( aPaper, left, right, top, bottom );
+ }
+ else
+ {
+ aData.m_aContext.getPageSize( aPaper, height, width );
+ aData.m_pParser->getMargins( aPaper, top, bottom, right, left );
+ }
+
+ rPageWidth = width * nDPI / 72;
+ rPageHeight = height * nDPI / 72;
+ rPageOffX = left * nDPI / 72;
+ rPageOffY = top * nDPI / 72;
+ rOutWidth = ( width - left - right ) * nDPI / 72;
+ rOutHeight = ( height - top - bottom ) * nDPI / 72;
+ }
+}
+
+sal_uLong PspSalInfoPrinter::GetPaperBinCount( const ImplJobSetup* pJobSetup )
+{
+ if( ! pJobSetup )
+ return 0;
+
+ JobData aData;
+ JobData::constructFromStreamBuffer( pJobSetup->mpDriverData, pJobSetup->mnDriverDataLen, aData );
+
+ const PPDKey* pKey = aData.m_pParser ? aData.m_pParser->getKey( String( RTL_CONSTASCII_USTRINGPARAM( "InputSlot" ) ) ): NULL;
+ return pKey ? pKey->countValues() : 0;
+}
+
+String PspSalInfoPrinter::GetPaperBinName( const ImplJobSetup* pJobSetup, sal_uLong nPaperBin )
+{
+ JobData aData;
+ JobData::constructFromStreamBuffer( pJobSetup->mpDriverData, pJobSetup->mnDriverDataLen, aData );
+
+ String aRet;
+ if( aData.m_pParser )
+ {
+ const PPDKey* pKey = aData.m_pParser ? aData.m_pParser->getKey( String( RTL_CONSTASCII_USTRINGPARAM( "InputSlot" ) ) ): NULL;
+ if( ! pKey || nPaperBin >= (sal_uLong)pKey->countValues() )
+ aRet = aData.m_pParser->getDefaultInputSlot();
+ else
+ {
+ const PPDValue* pValue = pKey->getValue( nPaperBin );
+ if( pValue )
+ aRet = aData.m_pParser->translateOption( pKey->getKey(), pValue->m_aOption );
+ }
+ }
+
+ return aRet;
+}
+
+sal_uLong PspSalInfoPrinter::GetCapabilities( const ImplJobSetup* pJobSetup, sal_uInt16 nType )
+{
+ switch( nType )
+ {
+ case PRINTER_CAPABILITIES_SUPPORTDIALOG:
+ return 1;
+ case PRINTER_CAPABILITIES_COPIES:
+ return 0xffff;
+ case PRINTER_CAPABILITIES_COLLATECOPIES:
+ {
+ // see if the PPD contains a value to set Collate to True
+ JobData aData;
+ JobData::constructFromStreamBuffer( pJobSetup->mpDriverData, pJobSetup->mnDriverDataLen, aData );
+
+ const PPDKey* pKey = aData.m_pParser ? aData.m_pParser->getKey( String( RTL_CONSTASCII_USTRINGPARAM( "Collate" ) ) ) : NULL;
+ const PPDValue* pVal = pKey ? pKey->getValue( String( RTL_CONSTASCII_USTRINGPARAM( "True" ) ) ) : NULL;
+
+ // PPDs don't mention the number of possible collated copies.
+ // so let's guess as many as we want ?
+ return pVal ? 0xffff : 0;
+ }
+ case PRINTER_CAPABILITIES_SETORIENTATION:
+ return 1;
+ case PRINTER_CAPABILITIES_SETDUPLEX:
+ return 1;
+ case PRINTER_CAPABILITIES_SETPAPERBIN:
+ return 1;
+ case PRINTER_CAPABILITIES_SETPAPERSIZE:
+ return 1;
+ case PRINTER_CAPABILITIES_SETPAPER:
+ return 0;
+ case PRINTER_CAPABILITIES_FAX:
+ return PrinterInfoManager::get().checkFeatureToken( pJobSetup->maPrinterName, "fax" ) ? 1 : 0;
+ case PRINTER_CAPABILITIES_PDF:
+ if( PrinterInfoManager::get().checkFeatureToken( pJobSetup->maPrinterName, "pdf" ) )
+ return 1;
+ else
+ {
+ // see if the PPD contains a value to set Collate to True
+ JobData aData = PrinterInfoManager::get().getPrinterInfo( pJobSetup->maPrinterName );
+ if( pJobSetup->mpDriverData )
+ JobData::constructFromStreamBuffer( pJobSetup->mpDriverData, pJobSetup->mnDriverDataLen, aData );
+ return aData.m_nPDFDevice > 0 ? 1 : 0;
+ }
+ case PRINTER_CAPABILITIES_EXTERNALDIALOG:
+ return PrinterInfoManager::get().checkFeatureToken( pJobSetup->maPrinterName, "external_dialog" ) ? 1 : 0;
+ case PRINTER_CAPABILITIES_USEPULLMODEL:
+ {
+ // see if the PPD contains a value to set Collate to True
+ JobData aData = PrinterInfoManager::get().getPrinterInfo( pJobSetup->maPrinterName );
+ if( pJobSetup->mpDriverData )
+ JobData::constructFromStreamBuffer( pJobSetup->mpDriverData, pJobSetup->mnDriverDataLen, aData );
+ return aData.m_nPDFDevice > 0 ? 1 : 0;
+ }
+ default: break;
+ };
+ return 0;
+}
+
+/*
+ * SalPrinter
+ */
+ PspSalPrinter::PspSalPrinter( SalInfoPrinter* pInfoPrinter )
+ : m_bFax( false ),
+ m_bPdf( false ),
+ m_bSwallowFaxNo( false ),
+ m_bIsPDFWriterJob( false ),
+ m_pGraphics( NULL ),
+ m_nCopies( 1 ),
+ m_bCollate( false ),
+ m_pInfoPrinter( pInfoPrinter )
+{
+}
+
+PspSalPrinter::~PspSalPrinter()
+{
+}
+
+static String getTmpName()
+{
+ rtl::OUString aTmp, aSys;
+ osl_createTempFile( NULL, NULL, &aTmp.pData );
+ osl_getSystemPathFromFileURL( aTmp.pData, &aSys.pData );
+
+ return aSys;
+}
+
+sal_Bool PspSalPrinter::StartJob(
+ const XubString* pFileName,
+ const XubString& rJobName,
+ const XubString& rAppName,
+ sal_uLong nCopies,
+ bool bCollate,
+ bool bDirect,
+ ImplJobSetup* pJobSetup )
+{
+ GetSalData()->m_pInstance->jobStartedPrinterUpdate();
+
+ m_bFax = false;
+ m_bPdf = false;
+ m_aFileName = pFileName ? *pFileName : String();
+ m_aTmpFile = String();
+ m_nCopies = nCopies;
+ m_bCollate = bCollate;
+
+ JobData::constructFromStreamBuffer( pJobSetup->mpDriverData, pJobSetup->mnDriverDataLen, m_aJobData );
+ if( m_nCopies > 1 )
+ {
+ // in case user did not do anything (m_nCopies=1)
+ // take the default from jobsetup
+ m_aJobData.m_nCopies = m_nCopies;
+ m_aJobData.setCollate( bCollate );
+ }
+
+ // check wether this printer is configured as fax
+ int nMode = 0;
+ const PrinterInfo& rInfo( PrinterInfoManager::get().getPrinterInfo( m_aJobData.m_aPrinterName ) );
+ sal_Int32 nIndex = 0;
+ while( nIndex != -1 )
+ {
+ OUString aToken( rInfo.m_aFeatures.getToken( 0, ',', nIndex ) );
+ if( ! aToken.compareToAscii( "fax", 3 ) )
+ {
+ m_bFax = true;
+ m_aTmpFile = getTmpName();
+ nMode = S_IRUSR | S_IWUSR;
+
+ ::boost::unordered_map< ::rtl::OUString, ::rtl::OUString, ::rtl::OUStringHash >::const_iterator it;
+ it = pJobSetup->maValueMap.find( ::rtl::OUString(RTL_CONSTASCII_USTRINGPARAM("FAX#")) );
+ if( it != pJobSetup->maValueMap.end() )
+ m_aFaxNr = it->second;
+
+ sal_Int32 nPos = 0;
+ m_bSwallowFaxNo = ! aToken.getToken( 1, '=', nPos ).compareToAscii( "swallow", 7 ) ? true : false;
+
+ break;
+ }
+ if( ! aToken.compareToAscii( "pdf=", 4 ) )
+ {
+ m_bPdf = true;
+ m_aTmpFile = getTmpName();
+ nMode = S_IRUSR | S_IWUSR;
+
+ if( ! m_aFileName.Len() )
+ {
+ m_aFileName = getPdfDir( rInfo );
+ m_aFileName.Append( '/' );
+ m_aFileName.Append( rJobName );
+ m_aFileName.AppendAscii( ".pdf" );
+ }
+ break;
+ }
+ }
+ m_aPrinterGfx.Init( m_aJobData );
+
+ // set/clear backwards compatibility flag
+ bool bStrictSO52Compatibility = false;
+ boost::unordered_map<rtl::OUString, rtl::OUString, rtl::OUStringHash >::const_iterator compat_it =
+ pJobSetup->maValueMap.find( rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "StrictSO52Compatibility" ) ) );
+
+ if( compat_it != pJobSetup->maValueMap.end() )
+ {
+ if( compat_it->second.equalsIgnoreAsciiCaseAscii( "true" ) )
+ bStrictSO52Compatibility = true;
+ }
+ m_aPrinterGfx.setStrictSO52Compatibility( bStrictSO52Compatibility );
+
+ return m_aPrintJob.StartJob( m_aTmpFile.Len() ? m_aTmpFile : m_aFileName, nMode, rJobName, rAppName, m_aJobData, &m_aPrinterGfx, bDirect ) ? sal_True : sal_False;
+}
+
+sal_Bool PspSalPrinter::EndJob()
+{
+ sal_Bool bSuccess = sal_False;
+ if( m_bIsPDFWriterJob )
+ bSuccess = sal_True;
+ else
+ {
+ bSuccess = m_aPrintJob.EndJob();
+
+ if( bSuccess )
+ {
+ // check for fax
+ if( m_bFax )
+ {
+
+ const PrinterInfo& rInfo( PrinterInfoManager::get().getPrinterInfo( m_aJobData.m_aPrinterName ) );
+ // sendAFax removes the file after use
+ bSuccess = sendAFax( m_aFaxNr, m_aTmpFile, rInfo.m_aCommand );
+ }
+ else if( m_bPdf )
+ {
+ const PrinterInfo& rInfo( PrinterInfoManager::get().getPrinterInfo( m_aJobData.m_aPrinterName ) );
+ bSuccess = createPdf( m_aFileName, m_aTmpFile, rInfo.m_aCommand );
+ }
+ }
+ }
+ GetSalData()->m_pInstance->jobEndedPrinterUpdate();
+ return bSuccess;
+}
+
+sal_Bool PspSalPrinter::AbortJob()
+{
+ sal_Bool bAbort = m_aPrintJob.AbortJob() ? sal_True : sal_False;
+ GetSalData()->m_pInstance->jobEndedPrinterUpdate();
+ return bAbort;
+}
+
+SalGraphics* PspSalPrinter::StartPage( ImplJobSetup* pJobSetup, sal_Bool )
+{
+ JobData::constructFromStreamBuffer( pJobSetup->mpDriverData, pJobSetup->mnDriverDataLen, m_aJobData );
+ m_pGraphics = new PspGraphics( &m_aJobData, &m_aPrinterGfx, m_bFax ? &m_aFaxNr : NULL, m_bSwallowFaxNo, m_pInfoPrinter );
+ m_pGraphics->SetLayout( 0 );
+ if( m_nCopies > 1 )
+ {
+ // in case user did not do anything (m_nCopies=1)
+ // take the default from jobsetup
+ m_aJobData.m_nCopies = m_nCopies;
+ m_aJobData.setCollate( m_nCopies > 1 && m_bCollate );
+ }
+
+ m_aPrintJob.StartPage( m_aJobData );
+ m_aPrinterGfx.Init( m_aPrintJob );
+
+ return m_pGraphics;
+}
+
+sal_Bool PspSalPrinter::EndPage()
+{
+ sal_Bool bResult = m_aPrintJob.EndPage();
+ m_aPrinterGfx.Clear();
+ return bResult ? sal_True : sal_False;
+}
+
+sal_uLong PspSalPrinter::GetErrorCode()
+{
+ return 0;
+}
+
+struct PDFNewJobParameters
+{
+ Size maPageSize;
+ sal_uInt16 mnPaperBin;
+
+ PDFNewJobParameters( const Size& i_rSize = Size(),
+ sal_uInt16 i_nPaperBin = 0xffff )
+ : maPageSize( i_rSize ), mnPaperBin( i_nPaperBin ) {}
+
+ bool operator!=(const PDFNewJobParameters& rComp ) const
+ {
+ Size aCompLSSize( rComp.maPageSize.Height(), rComp.maPageSize.Width() );
+ return
+ (maPageSize != rComp.maPageSize && maPageSize != aCompLSSize)
+ || mnPaperBin != rComp.mnPaperBin
+ ;
+ }
+
+ bool operator==(const PDFNewJobParameters& rComp) const
+ {
+ return ! this->operator!=(rComp);
+ }
+};
+
+struct PDFPrintFile
+{
+ rtl::OUString maTmpURL;
+ PDFNewJobParameters maParameters;
+
+ PDFPrintFile( const rtl::OUString& i_rURL, const PDFNewJobParameters& i_rNewParameters )
+ : maTmpURL( i_rURL )
+ , maParameters( i_rNewParameters ) {}
+};
+
+sal_Bool PspSalPrinter::StartJob( const String* i_pFileName, const String& i_rJobName, const String& i_rAppName,
+ ImplJobSetup* i_pSetupData, vcl::PrinterController& i_rController )
+{
+ OSL_TRACE( "StartJob with controller: pFilename = %s", i_pFileName ? rtl::OUStringToOString( *i_pFileName, RTL_TEXTENCODING_UTF8 ).getStr() : "<nil>" );
+ // mark for endjob
+ m_bIsPDFWriterJob = true;
+ // reset IsLastPage
+ i_rController.setLastPage( sal_False );
+
+ // update job data
+ if( i_pSetupData )
+ JobData::constructFromStreamBuffer( i_pSetupData->mpDriverData, i_pSetupData->mnDriverDataLen, m_aJobData );
+
+ OSL_ASSERT( m_aJobData.m_nPDFDevice > 0 );
+ m_aJobData.m_nPDFDevice = 1;
+
+ // possibly create one job for collated output
+ sal_Bool bSinglePrintJobs = sal_False;
+ beans::PropertyValue* pSingleValue = i_rController.getValue( rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "PrintCollateAsSingleJobs" ) ) );
+ if( pSingleValue )
+ {
+ pSingleValue->Value >>= bSinglePrintJobs;
+ }
+
+ int nCopies = i_rController.getPrinter()->GetCopyCount();
+ bool bCollate = i_rController.getPrinter()->IsCollateCopy();
+
+ // notify start of real print job
+ i_rController.jobStarted();
+
+ // setup PDFWriter context
+ vcl::PDFWriter::PDFWriterContext aContext;
+ aContext.Version = vcl::PDFWriter::PDF_1_4;
+ aContext.Tagged = false;
+ aContext.EmbedStandardFonts = true;
+ aContext.DocumentLocale = Application::GetSettings().GetLocale();
+ aContext.ColorMode = i_rController.getPrinter()->GetPrinterOptions().IsConvertToGreyscales()
+ ? vcl::PDFWriter::DrawGreyscale : vcl::PDFWriter::DrawColor;
+
+ // prepare doc info
+ aContext.DocumentInfo.Title = i_rJobName;
+ aContext.DocumentInfo.Creator = i_rAppName;
+ aContext.DocumentInfo.Producer = i_rAppName;
+
+ // define how we handle metafiles in PDFWriter
+ vcl::PDFWriter::PlayMetafileContext aMtfContext;
+ aMtfContext.m_bOnlyLosslessCompression = true;
+
+ boost::shared_ptr<vcl::PDFWriter> pWriter;
+ std::vector< PDFPrintFile > aPDFFiles;
+ boost::shared_ptr<Printer> pPrinter( i_rController.getPrinter() );
+ int nAllPages = i_rController.getFilteredPageCount();
+ i_rController.createProgressDialog();
+ bool bAborted = false;
+ PDFNewJobParameters aLastParm;
+
+ aContext.DPIx = pPrinter->ImplGetDPIX();
+ aContext.DPIy = pPrinter->ImplGetDPIY();
+ for( int nPage = 0; nPage < nAllPages && ! bAborted; nPage++ )
+ {
+ if( nPage == nAllPages-1 )
+ i_rController.setLastPage( sal_True );
+
+ // get the page's metafile
+ GDIMetaFile aPageFile;
+ vcl::PrinterController::PageSize aPageSize = i_rController.getFilteredPageFile( nPage, aPageFile );
+ if( i_rController.isProgressCanceled() )
+ {
+ bAborted = true;
+ if( nPage != nAllPages-1 )
+ {
+ i_rController.createProgressDialog();
+ i_rController.setLastPage( sal_True );
+ i_rController.getFilteredPageFile( nPage, aPageFile );
+ }
+ }
+ else
+ {
+ pPrinter->SetMapMode( MapMode( MAP_100TH_MM ) );
+ pPrinter->SetPaperSizeUser( aPageSize.aSize, true );
+ PDFNewJobParameters aNewParm( pPrinter->GetPaperSize(), pPrinter->GetPaperBin() );
+
+ // create PDF writer on demand
+ // either on first page
+ // or on paper format change - cups does not support multiple paper formats per job (yet?)
+ // so we need to start a new job to get a new paper format from the printer
+ // orientation switches (that is switch of height and width) is handled transparently by CUPS
+ if( ! pWriter ||
+ (aNewParm != aLastParm && ! i_pFileName ) )
+ {
+ if( pWriter )
+ {
+ pWriter->Emit();
+ }
+ // produce PDF file
+ OUString aPDFUrl;
+ if( i_pFileName )
+ aPDFUrl = *i_pFileName;
+ else
+ osl_createTempFile( NULL, NULL, &aPDFUrl.pData );
+ // normalize to file URL
+ if( aPDFUrl.compareToAscii( "file:", 5 ) != 0 )
+ {
+ // this is not a file URL, but it should
+ // form it into a osl friendly file URL
+ rtl::OUString aTmp;
+ osl_getFileURLFromSystemPath( aPDFUrl.pData, &aTmp.pData );
+ aPDFUrl = aTmp;
+ }
+ // save current file and paper format
+ aLastParm = aNewParm;
+ aPDFFiles.push_back( PDFPrintFile( aPDFUrl, aNewParm ) );
+ // update context
+ aContext.URL = aPDFUrl;
+
+ // create and initialize PDFWriter
+ #if defined __SUNPRO_CC
+ #pragma disable_warn
+ #endif
+ pWriter.reset( new vcl::PDFWriter( aContext, uno::Reference< beans::XMaterialHolder >() ) );
+ #if defined __SUNPRO_CC
+ #pragma enable_warn
+ #endif
+ }
+
+ pWriter->NewPage( TenMuToPt( aNewParm.maPageSize.Width() ),
+ TenMuToPt( aNewParm.maPageSize.Height() ),
+ vcl::PDFWriter::Portrait );
+
+ pWriter->PlayMetafile( aPageFile, aMtfContext, NULL );
+ }
+ }
+
+ // emit the last file
+ if( pWriter )
+ pWriter->Emit();
+
+ // handle collate, copy count and multiple jobs correctly
+ int nOuterJobs = 1;
+ if( bSinglePrintJobs )
+ {
+ nOuterJobs = nCopies;
+ m_aJobData.m_nCopies = 1;
+ }
+ else
+ {
+ if( bCollate )
+ {
+ if( aPDFFiles.size() == 1 && pPrinter->HasSupport( SUPPORT_COLLATECOPY ) )
+ {
+ m_aJobData.setCollate( true );
+ m_aJobData.m_nCopies = nCopies;
+ }
+ else
+ {
+ nOuterJobs = nCopies;
+ m_aJobData.m_nCopies = 1;
+ }
+ }
+ else
+ {
+ m_aJobData.setCollate( false );
+ m_aJobData.m_nCopies = nCopies;
+ }
+ }
+
+ // spool files
+ if( ! i_pFileName && ! bAborted )
+ {
+ bool bFirstJob = true;
+ for( int nCurJob = 0; nCurJob < nOuterJobs; nCurJob++ )
+ {
+ for( size_t i = 0; i < aPDFFiles.size(); i++ )
+ {
+ oslFileHandle pFile = NULL;
+ osl_openFile( aPDFFiles[i].maTmpURL.pData, &pFile, osl_File_OpenFlag_Read );
+ if (pFile && (osl_setFilePos(pFile, osl_Pos_Absolut, 0) == osl_File_E_None))
+ {
+ std::vector< char > buffer( 0x10000, 0 );
+ // update job data with current page size
+ Size aPageSize( aPDFFiles[i].maParameters.maPageSize );
+ m_aJobData.setPaper( TenMuToPt( aPageSize.Width() ), TenMuToPt( aPageSize.Height() ) );
+ // update job data with current paperbin
+ m_aJobData.setPaperBin( aPDFFiles[i].maParameters.mnPaperBin );
+
+ // spool current file
+ FILE* fp = PrinterInfoManager::get().startSpool( pPrinter->GetName(), i_rController.isDirectPrint() );
+ if( fp )
+ {
+ sal_uInt64 nBytesRead = 0;
+ do
+ {
+ osl_readFile( pFile, &buffer[0], buffer.size(), &nBytesRead );
+ if( nBytesRead > 0 )
+ {
+ size_t nBytesWritten = fwrite(&buffer[0], 1, nBytesRead, fp);
+ OSL_ENSURE(nBytesRead == nBytesWritten, "short write");
+ if (nBytesRead != nBytesWritten)
+ break;
+ }
+ } while( nBytesRead == buffer.size() );
+ rtl::OUStringBuffer aBuf( i_rJobName.Len() + 8 );
+ aBuf.append( i_rJobName );
+ if( i > 0 || nCurJob > 0 )
+ {
+ aBuf.append( sal_Unicode(' ') );
+ aBuf.append( sal_Int32( i + nCurJob * aPDFFiles.size() ) );
+ }
+ PrinterInfoManager::get().endSpool( pPrinter->GetName(), aBuf.makeStringAndClear(), fp, m_aJobData, bFirstJob );
+ bFirstJob = false;
+ }
+ }
+ osl_closeFile( pFile );
+ }
+ }
+ }
+
+ // job has been spooled
+ i_rController.setJobState( bAborted ? view::PrintableState_JOB_ABORTED : view::PrintableState_JOB_SPOOLED );
+
+ // clean up the temporary PDF files
+ if( ! i_pFileName || bAborted )
+ {
+ for( size_t i = 0; i < aPDFFiles.size(); i++ )
+ {
+ osl_removeFile( aPDFFiles[i].maTmpURL.pData );
+ OSL_TRACE( "removed print PDF file %s", rtl::OUStringToOString( aPDFFiles[i].maTmpURL, RTL_TEXTENCODING_UTF8 ).getStr() );
+ }
+ }
+
+ return sal_True;
+}
+
+
+class PrinterUpdate
+{
+ static Timer* pPrinterUpdateTimer;
+ static int nActiveJobs;
+
+ static void doUpdate();
+ DECL_STATIC_LINK( PrinterUpdate, UpdateTimerHdl, void* );
+public:
+ static void update(GenericInstance &rInstance);
+ static void jobStarted() { nActiveJobs++; }
+ static void jobEnded();
+};
+
+Timer* PrinterUpdate::pPrinterUpdateTimer = NULL;
+int PrinterUpdate::nActiveJobs = 0;
+
+void PrinterUpdate::doUpdate()
+{
+ ::psp::PrinterInfoManager& rManager( ::psp::PrinterInfoManager::get() );
+ GenericInstance *pInst = static_cast<GenericInstance *>( GetSalData()->m_pInstance );
+ if( pInst && rManager.checkPrintersChanged( false ) )
+ pInst->PostPrintersChanged();
+}
+
+// -----------------------------------------------------------------------
+
+IMPL_STATIC_LINK_NOINSTANCE( PrinterUpdate, UpdateTimerHdl, void*, EMPTYARG )
+{
+ if( nActiveJobs < 1 )
+ {
+ doUpdate();
+ delete pPrinterUpdateTimer;
+ pPrinterUpdateTimer = NULL;
+ }
+ else
+ pPrinterUpdateTimer->Start();
+
+ return 0;
+}
+
+void PrinterUpdate::update(GenericInstance &rInstance)
+{
+ if( Application::GetSettings().GetMiscSettings().GetDisablePrinting() )
+ return;
+
+ if( ! rInstance.isPrinterInit() )
+ {
+ // #i45389# start background printer detection
+ psp::PrinterInfoManager::get();
+ return;
+ }
+
+ if( nActiveJobs < 1 )
+ doUpdate();
+ else if( ! pPrinterUpdateTimer )
+ {
+ pPrinterUpdateTimer = new Timer();
+ pPrinterUpdateTimer->SetTimeout( 500 );
+ pPrinterUpdateTimer->SetTimeoutHdl( STATIC_LINK( NULL, PrinterUpdate, UpdateTimerHdl ) );
+ pPrinterUpdateTimer->Start();
+ }
+}
+
+void GenericInstance::updatePrinterUpdate()
+{
+ PrinterUpdate::update(*this);
+}
+
+void GenericInstance::jobStartedPrinterUpdate()
+{
+ PrinterUpdate::jobStarted();
+}
+
+void PrinterUpdate::jobEnded()
+{
+ nActiveJobs--;
+ if( nActiveJobs < 1 )
+ {
+ if( pPrinterUpdateTimer )
+ {
+ pPrinterUpdateTimer->Stop();
+ delete pPrinterUpdateTimer;
+ pPrinterUpdateTimer = NULL;
+ doUpdate();
+ }
+ }
+}
+
+void GenericInstance::jobEndedPrinterUpdate()
+{
+ PrinterUpdate::jobEnded();
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/vcl/generic/print/glyphset.cxx b/vcl/generic/print/glyphset.cxx
new file mode 100644
index 000000000000..b040e631e766
--- /dev/null
+++ b/vcl/generic/print/glyphset.cxx
@@ -0,0 +1,949 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*************************************************************************
+ *
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * Copyright 2000, 2010 Oracle and/or its affiliates.
+ *
+ * OpenOffice.org - a multi-platform office productivity suite
+ *
+ * This file is part of OpenOffice.org.
+ *
+ * OpenOffice.org is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License version 3
+ * only, as published by the Free Software Foundation.
+ *
+ * OpenOffice.org is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License version 3 for more details
+ * (a copy is included in the LICENSE file that accompanied this code).
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * version 3 along with OpenOffice.org. If not, see
+ * <http://www.openoffice.org/license.html>
+ * for a copy of the LGPLv3 License.
+ *
+ ************************************************************************/
+
+// MARKER(update_precomp.py): autogen include statement, do not remove
+#include "precompiled_vcl.hxx"
+
+#include "glyphset.hxx"
+#include "psputil.hxx"
+
+#include "sft.hxx"
+
+#include "generic/printergfx.hxx"
+#include "fontsubset.hxx"
+#include "vcl/fontmanager.hxx"
+
+#include "osl/thread.h"
+
+#include "sal/alloca.h"
+
+#include "rtl/ustring.hxx"
+#include "rtl/strbuf.hxx"
+
+#include <set>
+#include <map>
+#include <algorithm>
+
+using namespace vcl;
+using namespace psp;
+
+using ::rtl::OUString;
+using ::rtl::OString;
+using ::rtl::OStringBuffer;
+using ::rtl::OUStringToOString;
+
+GlyphSet::GlyphSet ()
+ : mnFontID (-1),
+ mbVertical (0),
+ mbUseFontEncoding (false)
+{}
+
+GlyphSet::GlyphSet (sal_Int32 nFontID, sal_Bool bVertical)
+ : mnFontID (nFontID),
+ mbVertical (bVertical)
+{
+ PrintFontManager &rMgr = PrintFontManager::get();
+ meBaseType = rMgr.getFontType (mnFontID);
+ maBaseName = OUStringToOString (rMgr.getPSName(mnFontID),
+ RTL_TEXTENCODING_ASCII_US);
+ mnBaseEncoding = rMgr.getFontEncoding(mnFontID);
+ mbUseFontEncoding = rMgr.getUseOnlyFontEncoding(mnFontID);
+}
+
+GlyphSet::~GlyphSet ()
+{
+ /* FIXME delete the glyphlist ??? */
+}
+
+sal_Int32
+GlyphSet::GetFontID ()
+{
+ return mnFontID;
+}
+
+fonttype::type
+GlyphSet::GetFontType ()
+{
+ return meBaseType;
+}
+
+sal_Bool
+GlyphSet::IsVertical ()
+{
+ return mbVertical;
+}
+
+sal_Bool
+GlyphSet::SetFont (sal_Int32 nFontID, sal_Bool bVertical)
+{
+ if (mnFontID != -1)
+ return sal_False;
+
+ mnFontID = nFontID;
+ mbVertical = bVertical;
+
+ PrintFontManager &rMgr = PrintFontManager::get();
+ meBaseType = rMgr.getFontType (mnFontID);
+ maBaseName = OUStringToOString (rMgr.getPSName(mnFontID),
+ RTL_TEXTENCODING_ASCII_US);
+ mnBaseEncoding = rMgr.getFontEncoding(mnFontID);
+ mbUseFontEncoding = rMgr.getUseOnlyFontEncoding(mnFontID);
+
+ return sal_True;
+}
+
+sal_Bool
+GlyphSet::GetCharID (
+ sal_Unicode nChar,
+ sal_uChar* nOutGlyphID,
+ sal_Int32* nOutGlyphSetID
+ )
+{
+ return LookupCharID (nChar, nOutGlyphID, nOutGlyphSetID)
+ || AddCharID (nChar, nOutGlyphID, nOutGlyphSetID);
+}
+
+sal_Bool
+GlyphSet::GetGlyphID (
+ sal_uInt32 nGlyph,
+ sal_Unicode nUnicode,
+ sal_uChar* nOutGlyphID,
+ sal_Int32* nOutGlyphSetID
+ )
+{
+ return LookupGlyphID (nGlyph, nOutGlyphID, nOutGlyphSetID)
+ || AddGlyphID (nGlyph, nUnicode, nOutGlyphID, nOutGlyphSetID);
+}
+
+sal_Bool
+GlyphSet::LookupCharID (
+ sal_Unicode nChar,
+ sal_uChar* nOutGlyphID,
+ sal_Int32* nOutGlyphSetID
+ )
+{
+ char_list_t::iterator aGlyphSet;
+ sal_Int32 nGlyphSetID;
+
+ // loop thru all the font subsets
+ for (aGlyphSet = maCharList.begin(), nGlyphSetID = 1;
+ aGlyphSet != maCharList.end();
+ ++aGlyphSet, nGlyphSetID++)
+ {
+ // check every subset if it contains the queried unicode char
+ char_map_t::const_iterator aGlyph = (*aGlyphSet).find (nChar);
+ if (aGlyph != (*aGlyphSet).end())
+ {
+ // success: found the unicode char, return the glyphid and the glyphsetid
+ *nOutGlyphSetID = nGlyphSetID;
+ *nOutGlyphID = (*aGlyph).second;
+ return sal_True;
+ }
+ }
+
+ *nOutGlyphSetID = -1;
+ *nOutGlyphID = 0;
+ return sal_False;
+}
+
+sal_Bool
+GlyphSet::LookupGlyphID (
+ sal_uInt32 nGlyph,
+ sal_uChar* nOutGlyphID,
+ sal_Int32* nOutGlyphSetID
+ )
+{
+ glyph_list_t::iterator aGlyphSet;
+ sal_Int32 nGlyphSetID;
+
+ // loop thru all the font subsets
+ for (aGlyphSet = maGlyphList.begin(), nGlyphSetID = 1;
+ aGlyphSet != maGlyphList.end();
+ ++aGlyphSet, nGlyphSetID++)
+ {
+ // check every subset if it contains the queried unicode char
+ glyph_map_t::const_iterator aGlyph = (*aGlyphSet).find (nGlyph);
+ if (aGlyph != (*aGlyphSet).end())
+ {
+ // success: found the glyph id, return the mapped glyphid and the glyphsetid
+ *nOutGlyphSetID = nGlyphSetID;
+ *nOutGlyphID = (*aGlyph).second;
+ return sal_True;
+ }
+ }
+
+ *nOutGlyphSetID = -1;
+ *nOutGlyphID = 0;
+ return sal_False;
+}
+
+sal_uChar
+GlyphSet::GetAnsiMapping (sal_Unicode nUnicodeChar)
+{
+ static rtl_UnicodeToTextConverter aConverter =
+ rtl_createUnicodeToTextConverter(RTL_TEXTENCODING_MS_1252);
+ static rtl_UnicodeToTextContext aContext =
+ rtl_createUnicodeToTextContext( aConverter );
+
+ sal_Char nAnsiChar;
+ sal_uInt32 nCvtInfo;
+ sal_Size nCvtChars;
+ const sal_uInt32 nCvtFlags = RTL_UNICODETOTEXT_FLAGS_UNDEFINED_ERROR
+ | RTL_UNICODETOTEXT_FLAGS_INVALID_ERROR;
+
+ sal_Size nSize = rtl_convertUnicodeToText( aConverter, aContext,
+ &nUnicodeChar, 1, &nAnsiChar, 1,
+ nCvtFlags, &nCvtInfo, &nCvtChars );
+
+ return nSize == 1 ? (sal_uChar)nAnsiChar : (sal_uChar)0;
+}
+
+sal_uChar
+GlyphSet::GetSymbolMapping (sal_Unicode nUnicodeChar)
+{
+ if (0x0000 < nUnicodeChar && nUnicodeChar < 0x0100)
+ return (sal_uChar)nUnicodeChar;
+ if (0xf000 < nUnicodeChar && nUnicodeChar < 0xf100)
+ return (sal_uChar)nUnicodeChar;
+
+ return 0;
+}
+
+void
+GlyphSet::AddNotdef (char_map_t &rCharMap)
+{
+ if (rCharMap.size() == 0)
+ rCharMap[0] = 0;
+}
+
+void
+GlyphSet::AddNotdef (glyph_map_t &rGlyphMap)
+{
+ if (rGlyphMap.size() == 0)
+ rGlyphMap[0] = 0;
+}
+sal_Bool
+GlyphSet::AddCharID (
+ sal_Unicode nChar,
+ sal_uChar* nOutGlyphID,
+ sal_Int32* nOutGlyphSetID
+ )
+{
+ sal_uChar nMappedChar;
+
+ // XXX important: avoid to reencode type1 symbol fonts
+ if (mnBaseEncoding == RTL_TEXTENCODING_SYMBOL)
+ nMappedChar = GetSymbolMapping (nChar);
+ else
+ nMappedChar = GetAnsiMapping (nChar);
+
+ // create an empty glyphmap that is reserved for iso1252 encoded glyphs
+ // (or -- unencoded -- symbol glyphs) and a second map that takes any other
+ if (maCharList.empty())
+ {
+ char_map_t aMap, aMapp;
+
+ maCharList.push_back (aMap);
+ maCharList.push_back (aMapp);
+ }
+ // if the last map is full, create a new one
+ if ((!nMappedChar) && (maCharList.back().size() == 255))
+ {
+ char_map_t aMap;
+ maCharList.push_back (aMap);
+ }
+
+ // insert a new glyph in the font subset
+ if (nMappedChar)
+ {
+ // always put iso1252 chars into the first map, map them on itself
+ char_map_t& aGlyphSet = maCharList.front();
+ AddNotdef (aGlyphSet);
+
+ aGlyphSet [nChar] = nMappedChar;
+ *nOutGlyphSetID = 1;
+ *nOutGlyphID = nMappedChar;
+ }
+ else
+ {
+ // other chars are just appended to the list
+ char_map_t& aGlyphSet = maCharList.back();
+ AddNotdef (aGlyphSet);
+
+ int nSize = aGlyphSet.size();
+
+ aGlyphSet [nChar] = nSize;
+ *nOutGlyphSetID = maCharList.size();
+ *nOutGlyphID = aGlyphSet [nChar];
+ }
+
+ return sal_True;
+}
+
+sal_Bool
+GlyphSet::AddGlyphID (
+ sal_uInt32 nGlyph,
+ sal_Unicode nUnicode,
+ sal_uChar* nOutGlyphID,
+ sal_Int32* nOutGlyphSetID
+ )
+{
+ sal_uChar nMappedChar;
+
+ // XXX important: avoid to reencode type1 symbol fonts
+ if (mnBaseEncoding == RTL_TEXTENCODING_SYMBOL)
+ nMappedChar = GetSymbolMapping (nUnicode);
+ else
+ nMappedChar = GetAnsiMapping (nUnicode);
+
+ // create an empty glyphmap that is reserved for iso1252 encoded glyphs
+ // (or -- unencoded -- symbol glyphs) and a second map that takes any other
+ if (maGlyphList.empty())
+ {
+ glyph_map_t aMap, aMapp;
+
+ maGlyphList.push_back (aMap);
+ maGlyphList.push_back (aMapp);
+ }
+ // if the last map is full, create a new one
+ if ((!nMappedChar) && (maGlyphList.back().size() == 255))
+ {
+ glyph_map_t aMap;
+ maGlyphList.push_back (aMap);
+ }
+
+ // insert a new glyph in the font subset
+ if (nMappedChar)
+ {
+ // always put iso1252 chars into the first map, map them on itself
+ glyph_map_t& aGlyphSet = maGlyphList.front();
+ AddNotdef (aGlyphSet);
+
+ aGlyphSet [nGlyph] = nMappedChar;
+ *nOutGlyphSetID = 1;
+ *nOutGlyphID = nMappedChar;
+ }
+ else
+ {
+ // other chars are just appended to the list
+ glyph_map_t& aGlyphSet = maGlyphList.back();
+ AddNotdef (aGlyphSet);
+
+ int nSize = aGlyphSet.size();
+
+ aGlyphSet [nGlyph] = nSize;
+ *nOutGlyphSetID = maGlyphList.size();
+ *nOutGlyphID = aGlyphSet [nGlyph];
+ }
+
+ return sal_True;
+}
+
+OString
+GlyphSet::GetCharSetName (sal_Int32 nGlyphSetID)
+{
+ if (meBaseType == fonttype::TrueType)
+ {
+ OStringBuffer aSetName( maBaseName.getLength() + 32 );
+ aSetName.append( maBaseName );
+ aSetName.append( "FID" );
+ aSetName.append( mnFontID );
+ aSetName.append( mbVertical ? "VCSet" : "HCSet" );
+ aSetName.append( nGlyphSetID );
+ return aSetName.makeStringAndClear();
+ }
+ else
+ /* (meBaseType == fonttype::Type1 || meBaseType == fonttype::Builtin) */
+ {
+ return maBaseName;
+ }
+}
+
+OString
+GlyphSet::GetGlyphSetName (sal_Int32 nGlyphSetID)
+{
+ if (meBaseType == fonttype::TrueType)
+ {
+ OStringBuffer aSetName( maBaseName.getLength() + 32 );
+ aSetName.append( maBaseName );
+ aSetName.append( "FID" );
+ aSetName.append( mnFontID );
+ aSetName.append( mbVertical ? "VGSet" : "HGSet" );
+ aSetName.append( nGlyphSetID );
+ return aSetName.makeStringAndClear();
+ }
+ else
+ /* (meBaseType == fonttype::Type1 || meBaseType == fonttype::Builtin) */
+ {
+ return maBaseName;
+ }
+}
+
+sal_Int32
+GlyphSet::GetGlyphSetEncoding (sal_Int32 nGlyphSetID)
+{
+ if (meBaseType == fonttype::TrueType)
+ return RTL_TEXTENCODING_DONTKNOW;
+ else
+ {
+ /* (meBaseType == fonttype::Type1 || meBaseType == fonttype::Builtin) */
+ if (mnBaseEncoding == RTL_TEXTENCODING_SYMBOL)
+ return RTL_TEXTENCODING_SYMBOL;
+ else
+ return nGlyphSetID == 1 ? RTL_TEXTENCODING_MS_1252
+ : RTL_TEXTENCODING_USER_START + nGlyphSetID;
+ }
+}
+
+OString
+GlyphSet::GetGlyphSetEncodingName (rtl_TextEncoding nEnc, const OString &rFontName)
+{
+ if ( nEnc == RTL_TEXTENCODING_MS_1252
+ || nEnc == RTL_TEXTENCODING_ISO_8859_1)
+ {
+ return OString("ISO1252Encoding");
+ }
+ else
+ if (nEnc >= RTL_TEXTENCODING_USER_START && nEnc <= RTL_TEXTENCODING_USER_END)
+ {
+ return rFontName
+ + OString("Enc")
+ + OString::valueOf ((sal_Int32)(nEnc - RTL_TEXTENCODING_USER_START));
+ }
+ else
+ {
+ return OString();
+ }
+}
+
+OString
+GlyphSet::GetGlyphSetEncodingName (sal_Int32 nGlyphSetID)
+{
+ return GetGlyphSetEncodingName (GetGlyphSetEncoding(nGlyphSetID), maBaseName);
+}
+
+void
+GlyphSet::PSDefineReencodedFont (osl::File* pOutFile, sal_Int32 nGlyphSetID)
+{
+ // only for ps fonts
+ if ((meBaseType != fonttype::Builtin) && (meBaseType != fonttype::Type1))
+ return;
+
+ sal_Char pEncodingVector [256];
+ sal_Int32 nSize = 0;
+
+ nSize += psp::appendStr ("(", pEncodingVector + nSize);
+ nSize += psp::appendStr (GetReencodedFontName(nGlyphSetID).getStr(),
+ pEncodingVector + nSize);
+ nSize += psp::appendStr (") cvn (", pEncodingVector + nSize);
+ nSize += psp::appendStr (maBaseName.getStr(),
+ pEncodingVector + nSize);
+ nSize += psp::appendStr (") cvn ", pEncodingVector + nSize);
+ nSize += psp::appendStr (GetGlyphSetEncodingName(nGlyphSetID).getStr(),
+ pEncodingVector + nSize);
+ nSize += psp::appendStr (" psp_definefont\n",
+ pEncodingVector + nSize);
+
+ psp::WritePS (pOutFile, pEncodingVector);
+}
+
+OString
+GlyphSet::GetReencodedFontName (rtl_TextEncoding nEnc, const OString &rFontName)
+{
+ if ( nEnc == RTL_TEXTENCODING_MS_1252
+ || nEnc == RTL_TEXTENCODING_ISO_8859_1)
+ {
+ return rFontName
+ + OString("-iso1252");
+ }
+ else
+ if (nEnc >= RTL_TEXTENCODING_USER_START && nEnc <= RTL_TEXTENCODING_USER_END)
+ {
+ return rFontName
+ + OString("-enc")
+ + OString::valueOf ((sal_Int32)(nEnc - RTL_TEXTENCODING_USER_START));
+ }
+ else
+ {
+ return OString();
+ }
+}
+
+OString
+GlyphSet::GetReencodedFontName (sal_Int32 nGlyphSetID)
+{
+ return GetReencodedFontName (GetGlyphSetEncoding(nGlyphSetID), maBaseName);
+}
+
+void GlyphSet::DrawGlyphs(
+ PrinterGfx& rGfx,
+ const Point& rPoint,
+ const sal_uInt32* pGlyphIds,
+ const sal_Unicode* pUnicodes,
+ sal_Int16 nLen,
+ const sal_Int32* pDeltaArray )
+{
+ sal_uChar *pGlyphID = (sal_uChar*)alloca (nLen * sizeof(sal_uChar));
+ sal_Int32 *pGlyphSetID = (sal_Int32*)alloca (nLen * sizeof(sal_Int32));
+ std::set< sal_Int32 > aGlyphSet;
+
+ // convert unicode to font glyph id and font subset
+ for (int nChar = 0; nChar < nLen; nChar++)
+ {
+ GetGlyphID (pGlyphIds[nChar], pUnicodes[nChar], pGlyphID + nChar, pGlyphSetID + nChar);
+ aGlyphSet.insert (pGlyphSetID[nChar]);
+ }
+
+ // loop over all glyph sets to detect substrings that can be xshown together
+ // without changing the postscript font
+ sal_Int32 *pDeltaSubset = (sal_Int32*)alloca (nLen * sizeof(sal_Int32));
+ sal_uChar *pGlyphSubset = (sal_uChar*)alloca (nLen * sizeof(sal_uChar));
+
+ std::set< sal_Int32 >::iterator aSet;
+ for (aSet = aGlyphSet.begin(); aSet != aGlyphSet.end(); ++aSet)
+ {
+ Point aPoint = rPoint;
+ sal_Int32 nOffset = 0;
+ sal_Int32 nGlyphs = 0;
+ sal_Int32 nChar;
+
+ // get offset to first glyph
+ for (nChar = 0; (nChar < nLen) && (pGlyphSetID[nChar] != *aSet); nChar++)
+ {
+ nOffset = pDeltaArray [nChar];
+ }
+
+ // loop over all chars to extract those that share the current glyph set
+ for (nChar = 0; nChar < nLen; nChar++)
+ {
+ if (pGlyphSetID[nChar] == *aSet)
+ {
+ pGlyphSubset [nGlyphs] = pGlyphID [nChar];
+ // the offset to the next glyph is determined by the glyph in
+ // front of the next glyph with the same glyphset id
+ // most often, this will be the current glyph
+ while ((nChar + 1) < nLen)
+ {
+ if (pGlyphSetID[nChar + 1] == *aSet)
+ break;
+ else
+ nChar += 1;
+ }
+ pDeltaSubset [nGlyphs] = pDeltaArray[nChar] - nOffset;
+
+ nGlyphs += 1;
+ }
+ }
+
+ // show the text using the PrinterGfx text api
+ aPoint.Move (nOffset, 0);
+
+ OString aGlyphSetName(GetGlyphSetName(*aSet));
+ rGfx.PSSetFont (aGlyphSetName, GetGlyphSetEncoding(*aSet));
+ rGfx.PSMoveTo (aPoint);
+ rGfx.PSShowText (pGlyphSubset, nGlyphs, nGlyphs, nGlyphs > 1 ? pDeltaSubset : NULL);
+ }
+}
+
+void
+GlyphSet::DrawText (PrinterGfx &rGfx, const Point& rPoint,
+ const sal_Unicode* pStr, sal_Int16 nLen, const sal_Int32* pDeltaArray)
+{
+ // dispatch to the impl method
+ if (pDeltaArray == NULL)
+ ImplDrawText (rGfx, rPoint, pStr, nLen);
+ else
+ ImplDrawText (rGfx, rPoint, pStr, nLen, pDeltaArray);
+}
+
+void
+GlyphSet::ImplDrawText (PrinterGfx &rGfx, const Point& rPoint,
+ const sal_Unicode* pStr, sal_Int16 nLen)
+{
+ rGfx.PSMoveTo (rPoint);
+
+ if( mbUseFontEncoding )
+ {
+ OString aPSName( OUStringToOString( rGfx.GetFontMgr().getPSName( mnFontID ), RTL_TEXTENCODING_ISO_8859_1 ) );
+ OString aBytes( OUStringToOString( OUString( pStr, nLen ), mnBaseEncoding ) );
+ rGfx.PSSetFont( aPSName, mnBaseEncoding );
+ rGfx.PSShowText( (const unsigned char*)aBytes.getStr(), nLen, aBytes.getLength() );
+ return;
+ }
+
+ int nChar;
+ sal_uChar *pGlyphID = (sal_uChar*)alloca (nLen * sizeof(sal_uChar));
+ sal_Int32 *pGlyphSetID = (sal_Int32*)alloca (nLen * sizeof(sal_Int32));
+
+ // convert unicode to glyph id and char set (font subset)
+ for (nChar = 0; nChar < nLen; nChar++)
+ GetCharID (pStr[nChar], pGlyphID + nChar, pGlyphSetID + nChar);
+
+ // loop over the string to draw subsequent pieces of chars
+ // with the same postscript font
+ for (nChar = 0; nChar < nLen; /* atend */)
+ {
+ sal_Int32 nGlyphSetID = pGlyphSetID [nChar];
+ sal_Int32 nGlyphs = 1;
+ for (int nNextChar = nChar + 1; nNextChar < nLen; nNextChar++)
+ {
+ if (pGlyphSetID[nNextChar] == nGlyphSetID)
+ nGlyphs++;
+ else
+ break;
+ }
+
+ // show the text using the PrinterGfx text api
+ OString aGlyphSetName(GetCharSetName(nGlyphSetID));
+ rGfx.PSSetFont (aGlyphSetName, GetGlyphSetEncoding(nGlyphSetID));
+ rGfx.PSShowText (pGlyphID + nChar, nGlyphs, nGlyphs);
+
+ nChar += nGlyphs;
+ }
+}
+
+void
+GlyphSet::ImplDrawText (PrinterGfx &rGfx, const Point& rPoint,
+ const sal_Unicode* pStr, sal_Int16 nLen, const sal_Int32* pDeltaArray)
+{
+ if( mbUseFontEncoding )
+ {
+ OString aPSName( OUStringToOString( rGfx.GetFontMgr().getPSName( mnFontID ), RTL_TEXTENCODING_ISO_8859_1 ) );
+ OString aBytes( OUStringToOString( OUString( pStr, nLen ), mnBaseEncoding ) );
+ rGfx.PSMoveTo( rPoint );
+ rGfx.PSSetFont( aPSName, mnBaseEncoding );
+ rGfx.PSShowText( (const unsigned char*)aBytes.getStr(), nLen, aBytes.getLength(), pDeltaArray );
+ return;
+ }
+
+ sal_uChar *pGlyphID = (sal_uChar*)alloca (nLen * sizeof(sal_uChar));
+ sal_Int32 *pGlyphSetID = (sal_Int32*)alloca (nLen * sizeof(sal_Int32));
+ std::set< sal_Int32 > aGlyphSet;
+
+ // convert unicode to font glyph id and font subset
+ for (int nChar = 0; nChar < nLen; nChar++)
+ {
+ GetCharID (pStr[nChar], pGlyphID + nChar, pGlyphSetID + nChar);
+ aGlyphSet.insert (pGlyphSetID[nChar]);
+ }
+
+ // loop over all glyph sets to detect substrings that can be xshown together
+ // without changing the postscript font
+ sal_Int32 *pDeltaSubset = (sal_Int32*)alloca (nLen * sizeof(sal_Int32));
+ sal_uChar *pGlyphSubset = (sal_uChar*)alloca (nLen * sizeof(sal_uChar));
+
+ std::set< sal_Int32 >::iterator aSet;
+ for (aSet = aGlyphSet.begin(); aSet != aGlyphSet.end(); ++aSet)
+ {
+ Point aPoint = rPoint;
+ sal_Int32 nOffset = 0;
+ sal_Int32 nGlyphs = 0;
+ sal_Int32 nChar;
+
+ // get offset to first glyph
+ for (nChar = 0; (nChar < nLen) && (pGlyphSetID[nChar] != *aSet); nChar++)
+ {
+ nOffset = pDeltaArray [nChar];
+ }
+
+ // loop over all chars to extract those that share the current glyph set
+ for (nChar = 0; nChar < nLen; nChar++)
+ {
+ if (pGlyphSetID[nChar] == *aSet)
+ {
+ pGlyphSubset [nGlyphs] = pGlyphID [nChar];
+ // the offset to the next glyph is determined by the glyph in
+ // front of the next glyph with the same glyphset id
+ // most often, this will be the current glyph
+ while ((nChar + 1) < nLen)
+ {
+ if (pGlyphSetID[nChar + 1] == *aSet)
+ break;
+ else
+ nChar += 1;
+ }
+ pDeltaSubset [nGlyphs] = pDeltaArray[nChar] - nOffset;
+
+ nGlyphs += 1;
+ }
+ }
+
+ // show the text using the PrinterGfx text api
+ aPoint.Move (nOffset, 0);
+
+ OString aGlyphSetName(GetCharSetName(*aSet));
+ rGfx.PSSetFont (aGlyphSetName, GetGlyphSetEncoding(*aSet));
+ rGfx.PSMoveTo (aPoint);
+ rGfx.PSShowText (pGlyphSubset, nGlyphs, nGlyphs, nGlyphs > 1 ? pDeltaSubset : NULL);
+ }
+}
+
+sal_Bool
+GlyphSet::PSUploadEncoding(osl::File* pOutFile, PrinterGfx &rGfx)
+{
+ // only for ps fonts
+ if ((meBaseType != fonttype::Builtin) && (meBaseType != fonttype::Type1))
+ return sal_False;
+ if (mnBaseEncoding == RTL_TEXTENCODING_SYMBOL)
+ return sal_False;
+
+ PrintFontManager &rMgr = rGfx.GetFontMgr();
+
+ // loop thru all the font subsets
+ sal_Int32 nGlyphSetID = 0;
+ char_list_t::iterator aGlyphSet;
+ for (aGlyphSet = maCharList.begin(); aGlyphSet != maCharList.end(); ++aGlyphSet)
+ {
+ ++nGlyphSetID;
+
+ if (nGlyphSetID == 1) // latin1 page uses global reencoding table
+ {
+ PSDefineReencodedFont (pOutFile, nGlyphSetID);
+ continue;
+ }
+ if ((*aGlyphSet).size() == 0) // empty set, doesn't need reencoding
+ {
+ continue;
+ }
+
+ // create reencoding table
+
+ sal_Char pEncodingVector [256];
+ sal_Int32 nSize = 0;
+
+ nSize += psp::appendStr ("/",
+ pEncodingVector + nSize);
+ nSize += psp::appendStr (GetGlyphSetEncodingName(nGlyphSetID).getStr(),
+ pEncodingVector + nSize);
+ nSize += psp::appendStr (" [ ",
+ pEncodingVector + nSize);
+
+ // need a list of glyphs, sorted by glyphid
+ typedef std::map< sal_uInt8, sal_Unicode > ps_mapping_t;
+ typedef ps_mapping_t::value_type ps_value_t;
+ ps_mapping_t aSortedGlyphSet;
+
+ char_map_t::const_iterator aUnsortedGlyph;
+ for (aUnsortedGlyph = (*aGlyphSet).begin();
+ aUnsortedGlyph != (*aGlyphSet).end();
+ ++aUnsortedGlyph)
+ {
+ aSortedGlyphSet.insert(ps_value_t((*aUnsortedGlyph).second,
+ (*aUnsortedGlyph).first));
+ }
+
+ ps_mapping_t::const_iterator aSortedGlyph;
+ // loop thru all the glyphs in the subset
+ for (aSortedGlyph = (aSortedGlyphSet).begin();
+ aSortedGlyph != (aSortedGlyphSet).end();
+ ++aSortedGlyph)
+ {
+ nSize += psp::appendStr ("/",
+ pEncodingVector + nSize);
+
+ std::list< OString > aName( rMgr.getAdobeNameFromUnicode((*aSortedGlyph).second) );
+
+ if( aName.begin() != aName.end() )
+ nSize += psp::appendStr ( aName.front().getStr(), pEncodingVector + nSize);
+ else
+ nSize += psp::appendStr (".notdef", pEncodingVector + nSize );
+ nSize += psp::appendStr (" ", pEncodingVector + nSize);
+ // flush line
+ if (nSize >= 70)
+ {
+ nSize += psp::appendStr ("\n", pEncodingVector + nSize);
+ psp::WritePS (pOutFile, pEncodingVector);
+ nSize = 0;
+ }
+ }
+
+ nSize += psp::appendStr ("] def\n", pEncodingVector + nSize);
+ psp::WritePS (pOutFile, pEncodingVector);
+
+ PSDefineReencodedFont (pOutFile, nGlyphSetID);
+ }
+
+ return sal_True;
+}
+
+struct EncEntry
+{
+ sal_uChar aEnc;
+ long aGID;
+
+ EncEntry() : aEnc( 0 ), aGID( 0 ) {}
+
+ bool operator<( const EncEntry& rRight ) const
+ { return aEnc < rRight.aEnc; }
+};
+
+static void CreatePSUploadableFont( TrueTypeFont* pSrcFont, FILE* pTmpFile,
+ const char* pGlyphSetName, int nGlyphCount,
+ /*const*/ sal_uInt16* pRequestedGlyphs, /*const*/ sal_uChar* pEncoding,
+ bool bAllowType42, bool /*bAllowCID*/ )
+{
+ // match the font-subset to the printer capabilities
+ // TODO: allow CFF for capable printers
+ int nTargetMask = FontSubsetInfo::TYPE1_PFA | FontSubsetInfo::TYPE3_FONT;
+ if( bAllowType42 )
+ nTargetMask |= FontSubsetInfo::TYPE42_FONT;
+
+ std::vector< EncEntry > aSorted( nGlyphCount, EncEntry() );
+ for( int i = 0; i < nGlyphCount; i++ )
+ {
+ aSorted[i].aEnc = pEncoding[i];
+ aSorted[i].aGID = pRequestedGlyphs[i];
+ }
+
+ std::stable_sort( aSorted.begin(), aSorted.end() );
+
+ std::vector< sal_uChar > aEncoding( nGlyphCount );
+ std::vector< long > aRequestedGlyphs( nGlyphCount );
+
+ for( int i = 0; i < nGlyphCount; i++ )
+ {
+ aEncoding[i] = aSorted[i].aEnc;
+ aRequestedGlyphs[i] = aSorted[i].aGID;
+ }
+
+ FontSubsetInfo aInfo;
+ aInfo.LoadFont( pSrcFont );
+
+ aInfo.CreateFontSubset( nTargetMask, pTmpFile, pGlyphSetName,
+ &aRequestedGlyphs[0], &aEncoding[0], nGlyphCount, NULL );
+}
+
+sal_Bool
+GlyphSet::PSUploadFont (osl::File& rOutFile, PrinterGfx &rGfx, bool bAllowType42, std::list< OString >& rSuppliedFonts )
+{
+ // only for truetype fonts
+ if (meBaseType != fonttype::TrueType)
+ return sal_False;
+
+ TrueTypeFont *pTTFont;
+ OString aTTFileName (rGfx.GetFontMgr().getFontFileSysPath(mnFontID));
+ int nFace = rGfx.GetFontMgr().getFontFaceNumber(mnFontID);
+ sal_Int32 nSuccess = OpenTTFontFile(aTTFileName.getStr(), nFace < 0 ? 0 : nFace, &pTTFont);
+ if (nSuccess != SF_OK)
+ return sal_False;
+ FILE* pTmpFile = tmpfile();
+ if (pTmpFile == NULL)
+ return sal_False;
+
+ // array of unicode source characters
+ sal_Unicode pUChars[256];
+
+ // encoding vector maps character encoding to the ordinal number
+ // of the glyph in the output file
+ sal_uChar pEncoding[256];
+ sal_uInt16 pTTGlyphMapping[256];
+ const bool bAllowCID = false; // TODO: nPSLanguageLevel>=3
+
+ // loop thru all the font subsets
+ sal_Int32 nCharSetID;
+ char_list_t::iterator aCharSet;
+ for (aCharSet = maCharList.begin(), nCharSetID = 1;
+ aCharSet != maCharList.end();
+ ++aCharSet, nCharSetID++)
+ {
+ if ((*aCharSet).size() == 0)
+ continue;
+
+ // loop thru all the chars in the subset
+ char_map_t::const_iterator aChar;
+ sal_Int32 n = 0;
+ for (aChar = (*aCharSet).begin(); aChar != (*aCharSet).end(); ++aChar)
+ {
+ pUChars [n] = (*aChar).first;
+ pEncoding [n] = (*aChar).second;
+ n++;
+ }
+ // create a mapping from the unicode chars to the char encoding in
+ // source TrueType font
+ MapString (pTTFont, pUChars, (*aCharSet).size(), pTTGlyphMapping, mbVertical);
+
+ // create the current subset
+ OString aCharSetName = GetCharSetName(nCharSetID);
+ fprintf( pTmpFile, "%%%%BeginResource: font %s\n", aCharSetName.getStr() );
+ CreatePSUploadableFont( pTTFont, pTmpFile, aCharSetName.getStr(), (*aCharSet).size(),
+ pTTGlyphMapping, pEncoding, bAllowType42, bAllowCID );
+ fprintf( pTmpFile, "%%%%EndResource\n" );
+ rSuppliedFonts.push_back( aCharSetName );
+ }
+
+ // loop thru all the font glyph subsets
+ sal_Int32 nGlyphSetID;
+ glyph_list_t::iterator aGlyphSet;
+ for (aGlyphSet = maGlyphList.begin(), nGlyphSetID = 1;
+ aGlyphSet != maGlyphList.end();
+ ++aGlyphSet, nGlyphSetID++)
+ {
+ if ((*aGlyphSet).size() == 0)
+ continue;
+
+ // loop thru all the glyphs in the subset
+ glyph_map_t::const_iterator aGlyph;
+ sal_Int32 n = 0;
+ for (aGlyph = (*aGlyphSet).begin(); aGlyph != (*aGlyphSet).end(); ++aGlyph)
+ {
+ pTTGlyphMapping [n] = (*aGlyph).first;
+ pEncoding [n] = (*aGlyph).second;
+ n++;
+ }
+
+ // create the current subset
+ OString aGlyphSetName = GetGlyphSetName(nGlyphSetID);
+ fprintf( pTmpFile, "%%%%BeginResource: font %s\n", aGlyphSetName.getStr() );
+ CreatePSUploadableFont( pTTFont, pTmpFile, aGlyphSetName.getStr(), (*aGlyphSet).size(),
+ pTTGlyphMapping, pEncoding, bAllowType42, bAllowCID );
+ fprintf( pTmpFile, "%%%%EndResource\n" );
+ rSuppliedFonts.push_back( aGlyphSetName );
+ }
+
+ // copy the file into the page header
+ rewind(pTmpFile);
+ fflush(pTmpFile);
+
+ sal_uChar pBuffer[0x2000];
+ sal_uInt64 nIn;
+ sal_uInt64 nOut;
+ do
+ {
+ nIn = fread(pBuffer, 1, sizeof(pBuffer), pTmpFile);
+ rOutFile.write (pBuffer, nIn, nOut);
+ }
+ while ((nIn == nOut) && !feof(pTmpFile));
+
+ // cleanup
+ CloseTTFont (pTTFont);
+ fclose (pTmpFile);
+
+ return sal_True;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/vcl/generic/print/glyphset.hxx b/vcl/generic/print/glyphset.hxx
new file mode 100644
index 000000000000..c8b009c91155
--- /dev/null
+++ b/vcl/generic/print/glyphset.hxx
@@ -0,0 +1,137 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*************************************************************************
+ *
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * Copyright 2000, 2010 Oracle and/or its affiliates.
+ *
+ * OpenOffice.org - a multi-platform office productivity suite
+ *
+ * This file is part of OpenOffice.org.
+ *
+ * OpenOffice.org is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License version 3
+ * only, as published by the Free Software Foundation.
+ *
+ * OpenOffice.org is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License version 3 for more details
+ * (a copy is included in the LICENSE file that accompanied this code).
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * version 3 along with OpenOffice.org. If not, see
+ * <http://www.openoffice.org/license.html>
+ * for a copy of the LGPLv3 License.
+ *
+ ************************************************************************/
+
+#ifndef _PSPRINT_GLYPHSET_HXX_
+#define _PSPRINT_GLYPHSET_HXX_
+
+#include "vcl/fontmanager.hxx"
+
+#include "osl/file.hxx"
+
+#include "rtl/string.hxx"
+
+#include "tools/gen.hxx"
+
+#include <list>
+#include <boost/unordered_map.hpp>
+
+namespace psp {
+
+class PrinterGfx;
+class PrintFontManager;
+
+class GlyphSet
+{
+private:
+
+ sal_Int32 mnFontID;
+ sal_Bool mbVertical;
+ rtl::OString maBaseName;
+ fonttype::type meBaseType;
+ rtl_TextEncoding mnBaseEncoding;
+ bool mbUseFontEncoding;
+
+ typedef boost::unordered_map< sal_Unicode, sal_uInt8 > char_map_t;
+ typedef std::list< char_map_t > char_list_t;
+ typedef boost::unordered_map< sal_uInt32, sal_uInt8 > glyph_map_t;
+ typedef std::list< glyph_map_t > glyph_list_t;
+
+ char_list_t maCharList;
+ glyph_list_t maGlyphList;
+
+ rtl::OString GetGlyphSetName (sal_Int32 nGlyphSetID);
+ rtl::OString GetCharSetName (sal_Int32 nGlyphSetID);
+ sal_Int32 GetGlyphSetEncoding (sal_Int32 nGlyphSetID);
+ rtl::OString GetGlyphSetEncodingName (sal_Int32 nGlyphSetID);
+
+ rtl::OString GetReencodedFontName (sal_Int32 nGlyphSetID);
+ void PSDefineReencodedFont (osl::File* pOutFile,
+ sal_Int32 nGlyphSetID);
+
+ sal_Bool GetCharID (sal_Unicode nChar,
+ sal_uChar* nOutGlyphID, sal_Int32* nOutGlyphSetID);
+ sal_Bool LookupCharID (sal_Unicode nChar,
+ sal_uChar* nOutGlyphID, sal_Int32* nOutGlyphSetID);
+ sal_Bool AddCharID (sal_Unicode nChar,
+ sal_uChar* nOutGlyphID,
+ sal_Int32* nOutGlyphSetID);
+ sal_Bool GetGlyphID (sal_uInt32 nGlyph, sal_Unicode nUnicode,
+ sal_uChar* nOutGlyphID, sal_Int32* nOutGlyphSetID);
+ sal_Bool LookupGlyphID (sal_uInt32 nGlyph,
+ sal_uChar* nOutGlyphID, sal_Int32* nOutGlyphSetID);
+ sal_Bool AddGlyphID (sal_uInt32 nGlyph, sal_Unicode nUnicode,
+ sal_uChar* nOutGlyphID,
+ sal_Int32* nOutGlyphSetID);
+ void AddNotdef (char_map_t &rCharMap);
+ void AddNotdef (glyph_map_t &rGlyphMap);
+ sal_uChar GetAnsiMapping (sal_Unicode nUnicodeChar);
+ sal_uChar GetSymbolMapping (sal_Unicode nUnicodeChar);
+
+ void ImplDrawText (PrinterGfx &rGfx, const Point& rPoint,
+ const sal_Unicode* pStr, sal_Int16 nLen);
+ void ImplDrawText (PrinterGfx &rGfx, const Point& rPoint,
+ const sal_Unicode* pStr, sal_Int16 nLen,
+ const sal_Int32* pDeltaArray);
+
+public:
+
+ GlyphSet ();
+ GlyphSet (sal_Int32 nFontID, sal_Bool bVertical);
+ ~GlyphSet ();
+
+ sal_Int32 GetFontID ();
+ fonttype::type GetFontType ();
+ static rtl::OString
+ GetReencodedFontName (rtl_TextEncoding nEnc,
+ const rtl::OString &rFontName);
+ static rtl::OString
+ GetGlyphSetEncodingName (rtl_TextEncoding nEnc,
+ const rtl::OString &rFontName);
+ sal_Bool IsVertical ();
+
+ sal_Bool SetFont (sal_Int32 nFontID, sal_Bool bVertical);
+
+ void DrawText (PrinterGfx &rGfx, const Point& rPoint,
+ const sal_Unicode* pStr, sal_Int16 nLen,
+ const sal_Int32* pDeltaArray = NULL);
+ void DrawGlyphs (PrinterGfx& rGfx,
+ const Point& rPoint,
+ const sal_uInt32* pGlyphIds,
+ const sal_Unicode* pUnicodes,
+ sal_Int16 nLen,
+ const sal_Int32* pDeltaArray );
+ sal_Bool PSUploadEncoding(osl::File* pOutFile, PrinterGfx &rGfx);
+ sal_Bool PSUploadFont (osl::File& rOutFile, PrinterGfx &rGfx, bool bAsType42, std::list< rtl::OString >& rSuppliedFonts );
+};
+
+
+} /* namespace psp */
+
+#endif
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/vcl/generic/print/printerjob.cxx b/vcl/generic/print/printerjob.cxx
new file mode 100644
index 000000000000..5b3d8668082e
--- /dev/null
+++ b/vcl/generic/print/printerjob.cxx
@@ -0,0 +1,1196 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*************************************************************************
+ *
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * Copyright 2000, 2010 Oracle and/or its affiliates.
+ *
+ * OpenOffice.org - a multi-platform office productivity suite
+ *
+ * This file is part of OpenOffice.org.
+ *
+ * OpenOffice.org is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License version 3
+ * only, as published by the Free Software Foundation.
+ *
+ * OpenOffice.org is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License version 3 for more details
+ * (a copy is included in the LICENSE file that accompanied this code).
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * version 3 along with OpenOffice.org. If not, see
+ * <http://www.openoffice.org/license.html>
+ * for a copy of the LGPLv3 License.
+ *
+ ************************************************************************/
+
+// MARKER(update_precomp.py): autogen include statement, do not remove
+#include "precompiled_vcl.hxx"
+
+#include <stdio.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <pwd.h>
+
+#include "psputil.hxx"
+#include "glyphset.hxx"
+
+#include "generic/printerjob.hxx"
+#include "generic/printergfx.hxx"
+#include "vcl/ppdparser.hxx"
+#include "vcl/strhelper.hxx"
+#include "vcl/printerinfomanager.hxx"
+
+#include "rtl/ustring.hxx"
+#include "rtl/strbuf.hxx"
+#include "rtl/ustrbuf.hxx"
+
+#include "osl/thread.h"
+#include "sal/alloca.h"
+#include <sal/macros.h>
+
+#include <algorithm>
+#include <vector>
+
+using namespace psp;
+
+using ::rtl::OUString;
+using ::rtl::OUStringToOString;
+using ::rtl::OString;
+using ::rtl::OStringBuffer;
+
+// forward declaration
+
+#define nBLOCKSIZE 0x2000
+
+namespace psp
+{
+
+sal_Bool
+AppendPS (FILE* pDst, osl::File* pSrc, sal_uChar* pBuffer,
+ sal_uInt32 nBlockSize = nBLOCKSIZE)
+{
+ if ((pDst == NULL) || (pSrc == NULL))
+ return sal_False;
+
+ if (pSrc->setPos(osl_Pos_Absolut, 0) != osl::FileBase::E_None)
+ return sal_False;
+
+ if (nBlockSize == 0)
+ nBlockSize = nBLOCKSIZE;
+ if (pBuffer == NULL)
+ pBuffer = (sal_uChar*)alloca (nBlockSize);
+
+ sal_uInt64 nIn = 0;
+ sal_uInt64 nOut = 0;
+ do
+ {
+ pSrc->read (pBuffer, nBlockSize, nIn);
+ if (nIn > 0)
+ nOut = fwrite (pBuffer, 1, sal::static_int_cast<sal_uInt32>(nIn), pDst);
+ }
+ while ((nIn > 0) && (nIn == nOut));
+
+ return sal_True;
+}
+
+} // namespace psp
+
+/*
+ * private convenience routines for file handling
+ */
+
+osl::File*
+PrinterJob::CreateSpoolFile (const rtl::OUString& rName, const rtl::OUString& rExtension)
+{
+ osl::File::RC nError = osl::File::E_None;
+ osl::File* pFile = NULL;
+
+ rtl::OUString aFile = rName + rExtension;
+ rtl::OUString aFileURL;
+ nError = osl::File::getFileURLFromSystemPath( aFile, aFileURL );
+ if (nError != osl::File::E_None)
+ return NULL;
+ aFileURL = maSpoolDirName + rtl::OUString(RTL_CONSTASCII_USTRINGPARAM ("/")) + aFileURL;
+
+ pFile = new osl::File (aFileURL);
+ nError = pFile->open (osl_File_OpenFlag_Read | osl_File_OpenFlag_Write | osl_File_OpenFlag_Create);
+ if (nError != osl::File::E_None)
+ {
+ delete pFile;
+ return NULL;
+ }
+
+ pFile->setAttributes (aFileURL,
+ osl_File_Attribute_OwnWrite | osl_File_Attribute_OwnRead);
+ return pFile;
+}
+
+/*
+ * public methods of PrinterJob: for use in PrinterGfx
+ */
+
+void
+PrinterJob::GetScale (double &rXScale, double &rYScale) const
+{
+ rXScale = mfXScale;
+ rYScale = mfYScale;
+}
+
+sal_uInt16
+PrinterJob::GetDepth () const
+{
+ sal_Int32 nLevel = GetPostscriptLevel();
+ sal_Bool bColor = IsColorPrinter ();
+
+ return nLevel > 1 && bColor ? 24 : 8;
+}
+
+sal_uInt16
+PrinterJob::GetPostscriptLevel (const JobData *pJobData) const
+{
+ sal_uInt16 nPSLevel = 2;
+
+ if( pJobData == NULL )
+ pJobData = &m_aLastJobData;
+
+ if( pJobData->m_nPSLevel )
+ nPSLevel = pJobData->m_nPSLevel;
+ else
+ if( pJobData->m_pParser )
+ nPSLevel = pJobData->m_pParser->getLanguageLevel();
+
+ return nPSLevel;
+}
+
+sal_Bool
+PrinterJob::IsColorPrinter () const
+{
+ sal_Bool bColor = sal_False;
+
+ if( m_aLastJobData.m_nColorDevice )
+ bColor = m_aLastJobData.m_nColorDevice == -1 ? sal_False : sal_True;
+ else if( m_aLastJobData.m_pParser )
+ bColor = m_aLastJobData.m_pParser->isColorDevice() ? sal_True : sal_False;
+
+ return bColor;
+}
+
+osl::File*
+PrinterJob::GetDocumentHeader ()
+{
+ return mpJobHeader;
+}
+
+osl::File*
+PrinterJob::GetDocumentTrailer ()
+{
+ return mpJobTrailer;
+}
+
+osl::File*
+PrinterJob::GetCurrentPageHeader ()
+{
+ return maHeaderList.back();
+}
+
+osl::File*
+PrinterJob::GetCurrentPageBody ()
+{
+ return maPageList.back();
+}
+
+/*
+ * public methods of PrinterJob: the actual job / spool handling
+ */
+
+PrinterJob::PrinterJob () :
+ mpJobHeader( NULL ),
+ mpJobTrailer( NULL ),
+ m_bQuickJob( false )
+{
+}
+
+namespace psp
+{
+
+/* return the username in the given buffer */
+sal_Bool
+getUserName (char* pName, int nSize)
+{
+ struct passwd *pPWEntry;
+ struct passwd aPWEntry;
+ sal_Char pPWBuffer[256];
+
+ sal_Bool bSuccess = sal_False;
+
+#ifdef FREEBSD
+ pPWEntry = getpwuid( getuid());
+#else
+ if (getpwuid_r(getuid(), &aPWEntry, pPWBuffer, sizeof(pPWBuffer), &pPWEntry) != 0)
+ pPWEntry = NULL;
+#endif
+
+ if (pPWEntry != NULL && pPWEntry->pw_name != NULL)
+ {
+ sal_Int32 nLen = strlen(pPWEntry->pw_name);
+ if (nLen > 0 && nLen < nSize)
+ {
+ memcpy (pName, pPWEntry->pw_name, nLen);
+ pName[nLen] = '\0';
+
+ bSuccess = sal_True;
+ }
+ }
+
+ // wipe the passwd off the stack
+ memset (pPWBuffer, 0, sizeof(pPWBuffer));
+
+ return bSuccess;
+}
+
+/* remove all our temporary files, uses external program "rm", since
+ osl functionality is inadequate */
+void
+removeSpoolDir (const rtl::OUString& rSpoolDir)
+{
+ rtl::OUString aSysPath;
+ if( osl::File::E_None != osl::File::getSystemPathFromFileURL( rSpoolDir, aSysPath ) )
+ {
+ // Conversion did not work, as this is quite a dangerous action,
+ // we should abort here ....
+ OSL_FAIL( "psprint: couldn't remove spool directory" );
+ return;
+ }
+ rtl::OString aSysPathByte =
+ rtl::OUStringToOString (aSysPath, osl_getThreadTextEncoding());
+ sal_Char pSystem [128];
+ sal_Int32 nChar = 0;
+
+ nChar = psp::appendStr ("rm -rf ", pSystem);
+ nChar += psp::appendStr (aSysPathByte.getStr(), pSystem + nChar);
+
+ if (system (pSystem) == -1)
+ OSL_FAIL( "psprint: couldn't remove spool directory" );
+}
+
+/* creates a spool directory with a "pidgin random" value based on
+ current system time */
+rtl::OUString
+createSpoolDir ()
+{
+ TimeValue aCur;
+ osl_getSystemTime( &aCur );
+ sal_Int32 nRand = aCur.Seconds ^ (aCur.Nanosec/1000);
+
+ rtl::OUString aTmpDir;
+ osl_getTempDirURL( &aTmpDir.pData );
+
+ do
+ {
+ rtl::OUStringBuffer aDir( aTmpDir.getLength() + 16 );
+ aDir.append( aTmpDir );
+ aDir.appendAscii( "/psp" );
+ aDir.append(nRand);
+ rtl::OUString aResult = aDir.makeStringAndClear();
+ if( osl::Directory::create( aResult ) == osl::FileBase::E_None )
+ {
+ osl::File::setAttributes( aResult,
+ osl_File_Attribute_OwnWrite
+ | osl_File_Attribute_OwnRead
+ | osl_File_Attribute_OwnExe );
+ return aResult;
+ }
+ nRand++;
+ } while( nRand );
+ return rtl::OUString();
+}
+
+} // namespace psp
+
+PrinterJob::~PrinterJob ()
+{
+ std::list< osl::File* >::iterator pPage;
+ for (pPage = maPageList.begin(); pPage != maPageList.end(); ++pPage)
+ {
+ //(*pPage)->remove();
+ delete *pPage;
+ }
+ for (pPage = maHeaderList.begin(); pPage != maHeaderList.end(); ++pPage)
+ {
+ //(*pPage)->remove();
+ delete *pPage;
+ }
+ // mpJobHeader->remove();
+ delete mpJobHeader;
+ // mpJobTrailer->remove();
+ delete mpJobTrailer;
+
+ // XXX should really call osl::remove routines
+ if( maSpoolDirName.getLength() )
+ removeSpoolDir (maSpoolDirName);
+
+ // osl::Directory::remove (maSpoolDirName);
+}
+
+namespace psp
+{
+
+// get locale invariant, 7bit clean current local time string
+sal_Char*
+getLocalTime(sal_Char* pBuffer)
+{
+ time_t nTime = time (NULL);
+ struct tm aTime;
+ struct tm *pLocalTime = localtime_r (&nTime, &aTime);
+
+ return asctime_r(pLocalTime, pBuffer);
+}
+
+}
+
+static bool isAscii( const rtl::OUString& rStr )
+{
+ sal_Int32 nLen = rStr.getLength();
+ for( sal_Int32 i = 0; i < nLen; i++ )
+ if( rStr[i] > 127 )
+ return false;
+ return true;
+}
+
+sal_Bool
+PrinterJob::StartJob (
+ const rtl::OUString& rFileName,
+ int nMode,
+ const rtl::OUString& rJobName,
+ const rtl::OUString& rAppName,
+ const JobData& rSetupData,
+ PrinterGfx* pGraphics,
+ bool bIsQuickJob
+ )
+{
+ m_bQuickJob = bIsQuickJob;
+ mnMaxWidthPt = mnMaxHeightPt = 0;
+ mnLandscapes = mnPortraits = 0;
+ m_pGraphics = pGraphics;
+ InitPaperSize (rSetupData);
+
+ // create file container for document header and trailer
+ maFileName = rFileName;
+ mnFileMode = nMode;
+ maSpoolDirName = createSpoolDir ();
+ maJobTitle = rJobName;
+
+ rtl::OUString aExt(RTL_CONSTASCII_USTRINGPARAM (".ps"));
+ mpJobHeader = CreateSpoolFile (rtl::OUString(RTL_CONSTASCII_USTRINGPARAM("psp_head")), aExt);
+ mpJobTrailer = CreateSpoolFile (rtl::OUString(RTL_CONSTASCII_USTRINGPARAM("psp_tail")), aExt);
+ if( ! (mpJobHeader && mpJobTrailer) ) // existing files are removed in destructor
+ return sal_False;
+
+ // write document header according to Document Structuring Conventions (DSC)
+ WritePS (mpJobHeader,
+ "%!PS-Adobe-3.0\n"
+ "%%BoundingBox: (atend)\n" );
+
+ rtl::OUString aFilterWS;
+
+ // Creator (this application)
+ aFilterWS = WhitespaceToSpace( rAppName, sal_False );
+ WritePS (mpJobHeader, "%%Creator: (");
+ WritePS (mpJobHeader, aFilterWS);
+ WritePS (mpJobHeader, ")\n");
+
+ // For (user name)
+ sal_Char pUserName[64];
+ if (getUserName(pUserName, sizeof(pUserName)))
+ {
+ WritePS (mpJobHeader, "%%For: (");
+ WritePS (mpJobHeader, pUserName);
+ WritePS (mpJobHeader, ")\n");
+ }
+
+ // Creation Date (locale independent local time)
+ sal_Char pCreationDate [256];
+ WritePS (mpJobHeader, "%%CreationDate: (");
+ getLocalTime(pCreationDate);
+ for( unsigned int i = 0; i < SAL_N_ELEMENTS(pCreationDate); i++ )
+ {
+ if( pCreationDate[i] == '\n' )
+ {
+ pCreationDate[i] = 0;
+ break;
+ }
+ }
+ WritePS (mpJobHeader, pCreationDate );
+ WritePS (mpJobHeader, ")\n");
+
+ // Document Title
+ /* #i74335#
+ * The title should be clean ascii; rJobName however may
+ * contain any Unicode character. So implement the following
+ * algorithm:
+ * use rJobName, if it contains only ascii
+ * use the filename, if it contains only ascii
+ * else omit %%Title
+ */
+ aFilterWS = WhitespaceToSpace( rJobName, sal_False );
+ rtl::OUString aTitle( aFilterWS );
+ if( ! isAscii( aTitle ) )
+ {
+ sal_Int32 nIndex = 0;
+ while( nIndex != -1 )
+ aTitle = rFileName.getToken( 0, '/', nIndex );
+ aTitle = WhitespaceToSpace( aTitle, sal_False );
+ if( ! isAscii( aTitle ) )
+ aTitle = rtl::OUString();
+ }
+
+ maJobTitle = aFilterWS;
+ if( aTitle.getLength() )
+ {
+ WritePS (mpJobHeader, "%%Title: (");
+ WritePS (mpJobHeader, aTitle);
+ WritePS (mpJobHeader, ")\n");
+ }
+
+ // Language Level
+ sal_Char pLevel[16];
+ sal_Int32 nSz = getValueOf(GetPostscriptLevel(&rSetupData), pLevel);
+ pLevel[nSz++] = '\n';
+ pLevel[nSz ] = '\0';
+ WritePS (mpJobHeader, "%%LanguageLevel: ");
+ WritePS (mpJobHeader, pLevel);
+
+ // Other
+ WritePS (mpJobHeader, "%%DocumentData: Clean7Bit\n");
+ WritePS (mpJobHeader, "%%Pages: (atend)\n");
+ WritePS (mpJobHeader, "%%Orientation: (atend)\n");
+ WritePS (mpJobHeader, "%%PageOrder: Ascend\n");
+ WritePS (mpJobHeader, "%%EndComments\n");
+
+ // write Prolog
+ writeProlog (mpJobHeader, rSetupData);
+
+ // mark last job setup as not set
+ m_aLastJobData.m_pParser = NULL;
+ m_aLastJobData.m_aContext.setParser( NULL );
+
+ return sal_True;
+}
+
+sal_Bool
+PrinterJob::EndJob ()
+{
+ // no pages ? that really means no print job
+ if( maPageList.empty() )
+ return sal_False;
+
+ // write document setup (done here because it
+ // includes the accumulated fonts
+ if( mpJobHeader )
+ writeSetup( mpJobHeader, m_aDocumentJobData );
+ m_pGraphics->OnEndJob();
+ if( ! (mpJobHeader && mpJobTrailer) )
+ return sal_False;
+
+ // write document trailer according to Document Structuring Conventions (DSC)
+ rtl::OStringBuffer aTrailer(512);
+ aTrailer.append( "%%Trailer\n" );
+ aTrailer.append( "%%BoundingBox: 0 0 " );
+ aTrailer.append( (sal_Int32)mnMaxWidthPt );
+ aTrailer.append( " " );
+ aTrailer.append( (sal_Int32)mnMaxHeightPt );
+ if( mnLandscapes > mnPortraits )
+ aTrailer.append("\n%%Orientation: Landscape");
+ else
+ aTrailer.append("\n%%Orientation: Portrait");
+ aTrailer.append( "\n%%Pages: " );
+ aTrailer.append( (sal_Int32)maPageList.size() );
+ aTrailer.append( "\n%%EOF\n" );
+ WritePS (mpJobTrailer, aTrailer.getStr());
+
+ /*
+ * spool the set of files to their final destination, this is U**X dependent
+ */
+
+ FILE* pDestFILE = NULL;
+
+ /* create a destination either as file or as a pipe */
+ sal_Bool bSpoolToFile = maFileName.getLength() > 0 ? sal_True : sal_False;
+ if (bSpoolToFile)
+ {
+ const rtl::OString aFileName = rtl::OUStringToOString (maFileName,
+ osl_getThreadTextEncoding());
+ if( mnFileMode )
+ {
+ int nFile = open( aFileName.getStr(), O_CREAT | O_EXCL | O_RDWR, mnFileMode );
+ if( nFile != -1 )
+ {
+ pDestFILE = fdopen( nFile, "w" );
+ if( pDestFILE == NULL )
+ {
+ close( nFile );
+ unlink( aFileName.getStr() );
+ return sal_False;
+ }
+ }
+ else
+ chmod( aFileName.getStr(), mnFileMode );
+ }
+ if (pDestFILE == NULL)
+ pDestFILE = fopen (aFileName.getStr(), "w");
+
+ if (pDestFILE == NULL)
+ return sal_False;
+ }
+ else
+ {
+ PrinterInfoManager& rPrinterInfoManager = PrinterInfoManager::get ();
+ pDestFILE = rPrinterInfoManager.startSpool( m_aLastJobData.m_aPrinterName, m_bQuickJob );
+ if (pDestFILE == NULL)
+ return sal_False;
+ }
+
+ /* spool the document parts to the destination */
+
+ sal_uChar pBuffer[ nBLOCKSIZE ];
+
+ AppendPS (pDestFILE, mpJobHeader, pBuffer);
+ mpJobHeader->close();
+
+ sal_Bool bSuccess = sal_True;
+ std::list< osl::File* >::iterator pPageBody;
+ std::list< osl::File* >::iterator pPageHead;
+ for (pPageBody = maPageList.begin(), pPageHead = maHeaderList.begin();
+ pPageBody != maPageList.end() && pPageHead != maHeaderList.end();
+ ++pPageBody, ++pPageHead)
+ {
+ if( *pPageHead )
+ {
+ osl::File::RC nError = (*pPageHead)->open(osl_File_OpenFlag_Read);
+ if (nError == osl::File::E_None)
+ {
+ AppendPS (pDestFILE, *pPageHead, pBuffer);
+ (*pPageHead)->close();
+ }
+ }
+ else
+ bSuccess = sal_False;
+ if( *pPageBody )
+ {
+ osl::File::RC nError = (*pPageBody)->open(osl_File_OpenFlag_Read);
+ if (nError == osl::File::E_None)
+ {
+ AppendPS (pDestFILE, *pPageBody, pBuffer);
+ (*pPageBody)->close();
+ }
+ }
+ else
+ bSuccess = sal_False;
+ }
+
+ AppendPS (pDestFILE, mpJobTrailer, pBuffer);
+ mpJobTrailer->close();
+
+ /* well done */
+
+ if (bSpoolToFile)
+ fclose (pDestFILE);
+ else
+ {
+ PrinterInfoManager& rPrinterInfoManager = PrinterInfoManager::get();
+ if (0 == rPrinterInfoManager.endSpool( m_aLastJobData.m_aPrinterName,
+ maJobTitle, pDestFILE, m_aDocumentJobData, true ))
+ {
+ bSuccess = sal_False;
+ }
+ }
+
+ return bSuccess;
+}
+
+sal_Bool
+PrinterJob::AbortJob ()
+{
+ m_pGraphics->OnEndJob();
+ return sal_False;
+}
+
+void
+PrinterJob::InitPaperSize (const JobData& rJobSetup)
+{
+ int nRes = rJobSetup.m_aContext.getRenderResolution ();
+
+ String aPaper;
+ int nWidth, nHeight;
+ rJobSetup.m_aContext.getPageSize (aPaper, nWidth, nHeight);
+
+ int nLeft = 0, nRight = 0, nUpper = 0, nLower = 0;
+ const PPDParser* pParser = rJobSetup.m_aContext.getParser();
+ if (pParser != NULL)
+ pParser->getMargins (aPaper, nLeft, nRight, nUpper, nLower);
+
+ mnResolution = nRes;
+
+ mnWidthPt = nWidth;
+ mnHeightPt = nHeight;
+
+ if( mnWidthPt > mnMaxWidthPt )
+ mnMaxWidthPt = mnWidthPt;
+ if( mnHeightPt > mnMaxHeightPt )
+ mnMaxHeightPt = mnHeightPt;
+
+ mnLMarginPt = nLeft;
+ mnRMarginPt = nRight;
+ mnTMarginPt = nUpper;
+ mnBMarginPt = nLower;
+
+ mfXScale = (double)72.0 / (double)mnResolution;
+ mfYScale = -1.0 * (double)72.0 / (double)mnResolution;
+}
+
+
+sal_Bool
+PrinterJob::StartPage (const JobData& rJobSetup)
+{
+ InitPaperSize (rJobSetup);
+
+ rtl::OUString aPageNo = rtl::OUString::valueOf ((sal_Int32)maPageList.size()+1); // sequential page number must start with 1
+ rtl::OUString aExt = aPageNo + rtl::OUString(RTL_CONSTASCII_USTRINGPARAM (".ps"));
+
+ osl::File* pPageHeader = CreateSpoolFile (
+ rtl::OUString(RTL_CONSTASCII_USTRINGPARAM("psp_pghead")), aExt);
+ osl::File* pPageBody = CreateSpoolFile (
+ rtl::OUString(RTL_CONSTASCII_USTRINGPARAM("psp_pgbody")), aExt);
+
+ maHeaderList.push_back (pPageHeader);
+ maPageList.push_back (pPageBody);
+
+ if( ! (pPageHeader && pPageBody) )
+ return sal_False;
+
+ // write page header according to Document Structuring Conventions (DSC)
+ WritePS (pPageHeader, "%%Page: ");
+ WritePS (pPageHeader, aPageNo);
+ WritePS (pPageHeader, " ");
+ WritePS (pPageHeader, aPageNo);
+ WritePS (pPageHeader, "\n");
+
+ if( rJobSetup.m_eOrientation == orientation::Landscape )
+ {
+ WritePS (pPageHeader, "%%PageOrientation: Landscape\n");
+ mnLandscapes++;
+ }
+ else
+ {
+ WritePS (pPageHeader, "%%PageOrientation: Portrait\n");
+ mnPortraits++;
+ }
+
+ sal_Char pBBox [256];
+ sal_Int32 nChar = 0;
+
+ nChar = psp::appendStr ("%%PageBoundingBox: ", pBBox);
+ nChar += psp::getValueOf (mnLMarginPt, pBBox + nChar);
+ nChar += psp::appendStr (" ", pBBox + nChar);
+ nChar += psp::getValueOf (mnBMarginPt, pBBox + nChar);
+ nChar += psp::appendStr (" ", pBBox + nChar);
+ nChar += psp::getValueOf (mnWidthPt - mnRMarginPt, pBBox + nChar);
+ nChar += psp::appendStr (" ", pBBox + nChar);
+ nChar += psp::getValueOf (mnHeightPt - mnTMarginPt, pBBox + nChar);
+ nChar += psp::appendStr ("\n", pBBox + nChar);
+
+ WritePS (pPageHeader, pBBox);
+
+ /* #i7262# #i65491# write setup only before first page
+ * (to %%Begin(End)Setup, instead of %%Begin(End)PageSetup)
+ * don't do this in StartJob since the jobsetup there may be
+ * different.
+ */
+ bool bWriteFeatures = true;
+ if( 1 == maPageList.size() )
+ {
+ m_aDocumentJobData = rJobSetup;
+ bWriteFeatures = false;
+ }
+
+ if ( writePageSetup( pPageHeader, rJobSetup, bWriteFeatures ) )
+ {
+ m_aLastJobData = rJobSetup;
+ return true;
+ }
+
+ return false;
+}
+
+sal_Bool
+PrinterJob::EndPage ()
+{
+ m_pGraphics->OnEndPage();
+
+ osl::File* pPageHeader = maHeaderList.back();
+ osl::File* pPageBody = maPageList.back();
+
+ if( ! (pPageBody && pPageHeader) )
+ return sal_False;
+
+ // copy page to paper and write page trailer according to DSC
+
+ sal_Char pTrailer[256];
+ sal_Int32 nChar = 0;
+ nChar = psp::appendStr ("grestore grestore\n", pTrailer);
+ nChar += psp::appendStr ("showpage\n", pTrailer + nChar);
+ nChar += psp::appendStr ("%%PageTrailer\n\n", pTrailer + nChar);
+ WritePS (pPageBody, pTrailer);
+
+ // this page is done for now, close it to avoid having too many open fd's
+
+ pPageHeader->close();
+ pPageBody->close();
+
+ return sal_True;
+}
+
+sal_uInt32
+PrinterJob::GetErrorCode ()
+{
+ /* TODO */
+ return 0;
+}
+
+struct less_ppd_key : public ::std::binary_function<double, double, bool>
+{
+ bool operator()(const PPDKey* left, const PPDKey* right)
+ { return left->getOrderDependency() < right->getOrderDependency(); }
+};
+
+static bool writeFeature( osl::File* pFile, const PPDKey* pKey, const PPDValue* pValue, bool bUseIncluseFeature )
+{
+ if( ! pKey || ! pValue )
+ return true;
+
+ OStringBuffer aFeature(256);
+ aFeature.append( "[{\n" );
+ if( bUseIncluseFeature )
+ aFeature.append( "%%IncludeFeature:" );
+ else
+ aFeature.append( "%%BeginFeature:" );
+ aFeature.append( " *" );
+ aFeature.append( OUStringToOString( pKey->getKey(), RTL_TEXTENCODING_ASCII_US ) );
+ aFeature.append( ' ' );
+ aFeature.append( OUStringToOString( pValue->m_aOption, RTL_TEXTENCODING_ASCII_US ) );
+ if( !bUseIncluseFeature )
+ {
+ aFeature.append( '\n' );
+ aFeature.append( OUStringToOString( pValue->m_aValue, RTL_TEXTENCODING_ASCII_US ) );
+ aFeature.append( "\n%%EndFeature" );
+ }
+ aFeature.append( "\n} stopped cleartomark\n" );
+ sal_uInt64 nWritten = 0;
+ return pFile->write( aFeature.getStr(), aFeature.getLength(), nWritten )
+ || nWritten != (sal_uInt64)aFeature.getLength() ? false : true;
+}
+
+bool PrinterJob::writeFeatureList( osl::File* pFile, const JobData& rJob, bool bDocumentSetup )
+{
+ bool bSuccess = true;
+
+ // emit features ordered to OrderDependency
+ // ignore features that are set to default
+
+ // sanity check
+ if( rJob.m_pParser == rJob.m_aContext.getParser() &&
+ rJob.m_pParser &&
+ ( m_aLastJobData.m_pParser == rJob.m_pParser || m_aLastJobData.m_pParser == NULL )
+ )
+ {
+ int i;
+ int nKeys = rJob.m_aContext.countValuesModified();
+ ::std::vector< const PPDKey* > aKeys( nKeys );
+ for( i = 0; i < nKeys; i++ )
+ aKeys[i] = rJob.m_aContext.getModifiedKey( i );
+ ::std::sort( aKeys.begin(), aKeys.end(), less_ppd_key() );
+
+ for( i = 0; i < nKeys && bSuccess; i++ )
+ {
+ const PPDKey* pKey = aKeys[i];
+ bool bEmit = false;
+ if( bDocumentSetup )
+ {
+ if( pKey->getSetupType() == PPDKey::DocumentSetup )
+ bEmit = true;
+ }
+ if( pKey->getSetupType() == PPDKey::PageSetup ||
+ pKey->getSetupType() == PPDKey::AnySetup )
+ bEmit = true;
+ if( bEmit )
+ {
+ const PPDValue* pValue = rJob.m_aContext.getValue( pKey );
+ if( pValue
+ && pValue->m_eType == eInvocation
+ && ( m_aLastJobData.m_pParser == NULL
+ || m_aLastJobData.m_aContext.getValue( pKey ) != pValue
+ || bDocumentSetup
+ )
+ )
+ {
+ // try to avoid PS level 2 feature commands if level is set to 1
+ if( GetPostscriptLevel( &rJob ) == 1 )
+ {
+ bool bHavePS2 =
+ ( pValue->m_aValue.SearchAscii( "<<" ) != STRING_NOTFOUND )
+ ||
+ ( pValue->m_aValue.SearchAscii( ">>" ) != STRING_NOTFOUND );
+ if( bHavePS2 )
+ continue;
+ }
+ bSuccess = writeFeature( pFile, pKey, pValue, PrinterInfoManager::get().getUseIncludeFeature() );
+ }
+ }
+ }
+ }
+ else
+ bSuccess = false;
+
+ return bSuccess;
+}
+
+bool PrinterJob::writePageSetup( osl::File* pFile, const JobData& rJob, bool bWriteFeatures )
+{
+ bool bSuccess = true;
+
+ WritePS (pFile, "%%BeginPageSetup\n%\n");
+ if ( bWriteFeatures )
+ bSuccess = writeFeatureList( pFile, rJob, false );
+ WritePS (pFile, "%%EndPageSetup\n");
+
+ sal_Char pTranslate [128];
+ sal_Int32 nChar = 0;
+
+ if( rJob.m_eOrientation == orientation::Portrait )
+ {
+ nChar = psp::appendStr ("gsave\n[", pTranslate);
+ nChar += psp::getValueOfDouble ( pTranslate + nChar, mfXScale, 5);
+ nChar += psp::appendStr (" 0 0 ", pTranslate + nChar);
+ nChar += psp::getValueOfDouble ( pTranslate + nChar, mfYScale, 5);
+ nChar += psp::appendStr (" ", pTranslate + nChar);
+ nChar += psp::getValueOf (mnRMarginPt, pTranslate + nChar);
+ nChar += psp::appendStr (" ", pTranslate + nChar);
+ nChar += psp::getValueOf (mnHeightPt-mnTMarginPt,
+ pTranslate + nChar);
+ nChar += psp::appendStr ("] concat\ngsave\n",
+ pTranslate + nChar);
+ }
+ else
+ {
+ nChar = psp::appendStr ("gsave\n", pTranslate);
+ nChar += psp::appendStr ("[ 0 ", pTranslate + nChar);
+ nChar += psp::getValueOfDouble ( pTranslate + nChar, -mfYScale, 5);
+ nChar += psp::appendStr (" ", pTranslate + nChar);
+ nChar += psp::getValueOfDouble ( pTranslate + nChar, mfXScale, 5);
+ nChar += psp::appendStr (" 0 ", pTranslate + nChar );
+ nChar += psp::getValueOfDouble ( pTranslate + nChar, mnLMarginPt, 5 );
+ nChar += psp::appendStr (" ", pTranslate + nChar);
+ nChar += psp::getValueOf (mnBMarginPt, pTranslate + nChar );
+ nChar += psp::appendStr ("] concat\ngsave\n",
+ pTranslate + nChar);
+ }
+
+ WritePS (pFile, pTranslate);
+
+ return bSuccess;
+}
+
+void PrinterJob::writeJobPatch( osl::File* pFile, const JobData& rJobData )
+{
+ if( ! PrinterInfoManager::get().getUseJobPatch() )
+ return;
+
+ const PPDKey* pKey = NULL;
+
+ if( rJobData.m_pParser )
+ pKey = rJobData.m_pParser->getKey( OUString( RTL_CONSTASCII_USTRINGPARAM( "JobPatchFile" ) ) );
+ if( ! pKey )
+ return;
+
+ // order the patch files
+ // according to PPD spec the JobPatchFile options must be int
+ // and should be emitted in order
+ std::list< sal_Int32 > patch_order;
+ int nValueCount = pKey->countValues();
+ for( int i = 0; i < nValueCount; i++ )
+ {
+ const PPDValue* pVal = pKey->getValue( i );
+ patch_order.push_back( pVal->m_aOption.ToInt32() );
+ if( patch_order.back() == 0 && ! pVal->m_aOption.EqualsAscii( "0" ) )
+ {
+ WritePS( pFile, "% Warning: left out JobPatchFile option \"" );
+ OString aOption = OUStringToOString( pVal->m_aOption, RTL_TEXTENCODING_ASCII_US );
+ WritePS( pFile, aOption.getStr() );
+ WritePS( pFile,
+ "\"\n% as it violates the PPD spec;\n"
+ "% JobPatchFile options need to be numbered for ordering.\n" );
+ }
+ }
+
+ patch_order.sort();
+ patch_order.unique();
+
+ while( patch_order.begin() != patch_order.end() )
+ {
+ // note: this discards patch files not adhering to the "int" scheme
+ // as there won't be a value for them
+ writeFeature( pFile, pKey, pKey->getValue( OUString::valueOf( patch_order.front() ) ), false );
+ patch_order.pop_front();
+ }
+}
+
+bool PrinterJob::writeProlog (osl::File* pFile, const JobData& rJobData )
+{
+ WritePS( pFile, "%%BeginProlog\n" );
+
+ // JobPatchFile feature needs to be emitted at begin of prolog
+ writeJobPatch( pFile, rJobData );
+
+ static const sal_Char pProlog[] = {
+ "%%BeginResource: procset PSPrint-Prolog 1.0 0\n"
+ "/ISO1252Encoding [\n"
+ "/.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef\n"
+ "/.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef\n"
+ "/.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef\n"
+ "/.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef\n"
+ "/space /exclam /quotedbl /numbersign /dollar /percent /ampersand /quotesingle\n"
+ "/parenleft /parenright /asterisk /plus /comma /hyphen /period /slash\n"
+ "/zero /one /two /three /four /five /six /seven\n"
+ "/eight /nine /colon /semicolon /less /equal /greater /question\n"
+ "/at /A /B /C /D /E /F /G\n"
+ "/H /I /J /K /L /M /N /O\n"
+ "/P /Q /R /S /T /U /V /W\n"
+ "/X /Y /Z /bracketleft /backslash /bracketright /asciicircum /underscore\n"
+ "/grave /a /b /c /d /e /f /g\n"
+ "/h /i /j /k /l /m /n /o\n"
+ "/p /q /r /s /t /u /v /w\n"
+ "/x /y /z /braceleft /bar /braceright /asciitilde /unused\n"
+ "/Euro /unused /quotesinglbase /florin /quotedblbase /ellipsis /dagger /daggerdbl\n"
+ "/circumflex /perthousand /Scaron /guilsinglleft /OE /unused /Zcaron /unused\n"
+ "/unused /quoteleft /quoteright /quotedblleft /quotedblright /bullet /endash /emdash\n"
+ "/tilde /trademark /scaron /guilsinglright /oe /unused /zcaron /Ydieresis\n"
+ "/space /exclamdown /cent /sterling /currency /yen /brokenbar /section\n"
+ "/dieresis /copyright /ordfeminine /guillemotleft /logicalnot /hyphen /registered /macron\n"
+ "/degree /plusminus /twosuperior /threesuperior /acute /mu /paragraph /periodcentered\n"
+ "/cedilla /onesuperior /ordmasculine /guillemotright /onequarter /onehalf /threequarters /questiondown\n"
+ "/Agrave /Aacute /Acircumflex /Atilde /Adieresis /Aring /AE /Ccedilla\n"
+ "/Egrave /Eacute /Ecircumflex /Edieresis /Igrave /Iacute /Icircumflex /Idieresis\n"
+ "/Eth /Ntilde /Ograve /Oacute /Ocircumflex /Otilde /Odieresis /multiply\n"
+ "/Oslash /Ugrave /Uacute /Ucircumflex /Udieresis /Yacute /Thorn /germandbls\n"
+ "/agrave /aacute /acircumflex /atilde /adieresis /aring /ae /ccedilla\n"
+ "/egrave /eacute /ecircumflex /edieresis /igrave /iacute /icircumflex /idieresis\n"
+ "/eth /ntilde /ograve /oacute /ocircumflex /otilde /odieresis /divide\n"
+ "/oslash /ugrave /uacute /ucircumflex /udieresis /yacute /thorn /ydieresis] def\n"
+ "\n"
+ "/psp_definefont { exch dup findfont dup length dict begin { 1 index /FID ne\n"
+ "{ def } { pop pop } ifelse } forall /Encoding 3 -1 roll def\n"
+ "currentdict end exch pop definefont pop } def\n"
+ "\n"
+ "/pathdict dup 8 dict def load begin\n"
+ "/rcmd { { currentfile 1 string readstring pop 0 get dup 32 gt { exit }\n"
+ "{ pop } ifelse } loop dup 126 eq { pop exit } if 65 sub dup 16#3 and 1\n"
+ "add exch dup 16#C and -2 bitshift 16#3 and 1 add exch 16#10 and 16#10\n"
+ "eq 3 1 roll exch } def\n"
+ "/rhex { dup 1 sub exch currentfile exch string readhexstring pop dup 0\n"
+ "get dup 16#80 and 16#80 eq dup 3 1 roll { 16#7f and } if 2 index 0 3\n"
+ "-1 roll put 3 1 roll 0 0 1 5 -1 roll { 2 index exch get add 256 mul }\n"
+ "for 256 div exch pop exch { neg } if } def\n"
+ "/xcmd { rcmd exch rhex exch rhex exch 5 -1 roll add exch 4 -1 roll add\n"
+ "1 index 1 index 5 -1 roll { moveto } { lineto } ifelse } def end\n"
+ "/readpath { 0 0 pathdict begin { xcmd } loop end pop pop } def\n"
+ "\n"
+ "systemdict /languagelevel known not {\n"
+ "/xshow { exch dup length 0 1 3 -1 roll 1 sub { dup 3 index exch get\n"
+ "exch 2 index exch get 1 string dup 0 4 -1 roll put currentpoint 3 -1\n"
+ "roll show moveto 0 rmoveto } for pop pop } def\n"
+ "/rectangle { 4 -2 roll moveto 1 index 0 rlineto 0 exch rlineto neg 0\n"
+ "rlineto closepath } def\n"
+ "/rectfill { rectangle fill } def\n"
+ "/rectstroke { rectangle stroke } def } if\n"
+ "/bshow { currentlinewidth 3 1 roll currentpoint 3 index show moveto\n"
+ "setlinewidth false charpath stroke setlinewidth } def\n"
+ "/bxshow { currentlinewidth 4 1 roll setlinewidth exch dup length 1 sub\n"
+ "0 1 3 -1 roll { 1 string 2 index 2 index get 1 index exch 0 exch put dup\n"
+ "currentpoint 3 -1 roll show moveto currentpoint 3 -1 roll false charpath\n"
+ "stroke moveto 2 index exch get 0 rmoveto } for pop pop setlinewidth } def\n"
+ "\n"
+ "/psp_lzwfilter { currentfile /ASCII85Decode filter /LZWDecode filter } def\n"
+ "/psp_ascii85filter { currentfile /ASCII85Decode filter } def\n"
+ "/psp_lzwstring { psp_lzwfilter 1024 string readstring } def\n"
+ "/psp_ascii85string { psp_ascii85filter 1024 string readstring } def\n"
+ "/psp_imagedict {\n"
+ "/psp_bitspercomponent { 3 eq { 1 }{ 8 } ifelse } def\n"
+ "/psp_decodearray { [ [0 1 0 1 0 1] [0 255] [0 1] [0 255] ] exch get }\n"
+ "def 7 dict dup\n"
+ "/ImageType 1 put dup\n"
+ "/Width 7 -1 roll put dup\n"
+ "/Height 5 index put dup\n"
+ "/BitsPerComponent 4 index psp_bitspercomponent put dup\n"
+ "/Decode 5 -1 roll psp_decodearray put dup\n"
+ "/ImageMatrix [1 0 0 1 0 0] dup 5 8 -1 roll put put dup\n"
+ "/DataSource 4 -1 roll 1 eq { psp_lzwfilter } { psp_ascii85filter } ifelse put\n"
+ "} def\n"
+ "%%EndResource\n"
+ "%%EndProlog\n"
+ };
+ static const sal_Char pSO52CompatProlog[] = {
+ "%%BeginResource: procset PSPrint-Prolog 1.0 0\n"
+ "/ISO1252Encoding [\n"
+ "/.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef\n"
+ "/.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef\n"
+ "/.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef\n"
+ "/.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef\n"
+ "/space /exclam /quotedbl /numbersign /dollar /percent /ampersand /quoteright\n"
+ "/parenleft /parenright /asterisk /plus /comma /minus /period /slash\n"
+ "/zero /one /two /three /four /five /six /seven\n"
+ "/eight /nine /colon /semicolon /less /equal /greater /question\n"
+ "/at /A /B /C /D /E /F /G\n"
+ "/H /I /J /K /L /M /N /O\n"
+ "/P /Q /R /S /T /U /V /W\n"
+ "/X /Y /Z /bracketleft /backslash /bracketright /asciicircum /underscore\n"
+ "/grave /a /b /c /d /e /f /g\n"
+ "/h /i /j /k /l /m /n /o\n"
+ "/p /q /r /s /t /u /v /w\n"
+ "/x /y /z /braceleft /bar /braceright /asciitilde /unused\n"
+ "/Euro /unused /quotesinglbase /florin /quotedblbase /ellipsis /dagger /daggerdbl\n"
+ "/circumflex /perthousand /Scaron /guilsinglleft /OE /unused /Zcaron /unused\n"
+ "/unused /quoteleft /quoteright /quotedblleft /quotedblright /bullet /endash /emdash\n"
+ "/tilde /trademark /scaron /guilsinglright /oe /unused /zcaron /Ydieresis\n"
+ "/space /exclamdown /cent /sterling /currency /yen /brokenbar /section\n"
+ "/dieresis /copyright /ordfeminine /guillemotleft /logicalnot /hyphen /registered /macron\n"
+ "/degree /plusminus /twosuperior /threesuperior /acute /mu /paragraph /periodcentered\n"
+ "/cedilla /onesuperior /ordmasculine /guillemotright /onequarter /onehalf /threequarters /questiondown\n"
+ "/Agrave /Aacute /Acircumflex /Atilde /Adieresis /Aring /AE /Ccedilla\n"
+ "/Egrave /Eacute /Ecircumflex /Edieresis /Igrave /Iacute /Icircumflex /Idieresis\n"
+ "/Eth /Ntilde /Ograve /Oacute /Ocircumflex /Otilde /Odieresis /multiply\n"
+ "/Oslash /Ugrave /Uacute /Ucircumflex /Udieresis /Yacute /Thorn /germandbls\n"
+ "/agrave /aacute /acircumflex /atilde /adieresis /aring /ae /ccedilla\n"
+ "/egrave /eacute /ecircumflex /edieresis /igrave /iacute /icircumflex /idieresis\n"
+ "/eth /ntilde /ograve /oacute /ocircumflex /otilde /odieresis /divide\n"
+ "/oslash /ugrave /uacute /ucircumflex /udieresis /yacute /thorn /ydieresis] def\n"
+ "\n"
+ "/psp_definefont { exch dup findfont dup length dict begin { 1 index /FID ne\n"
+ "{ def } { pop pop } ifelse } forall /Encoding 3 -1 roll def\n"
+ "currentdict end exch pop definefont pop } def\n"
+ "\n"
+ "/pathdict dup 8 dict def load begin\n"
+ "/rcmd { { currentfile 1 string readstring pop 0 get dup 32 gt { exit }\n"
+ "{ pop } ifelse } loop dup 126 eq { pop exit } if 65 sub dup 16#3 and 1\n"
+ "add exch dup 16#C and -2 bitshift 16#3 and 1 add exch 16#10 and 16#10\n"
+ "eq 3 1 roll exch } def\n"
+ "/rhex { dup 1 sub exch currentfile exch string readhexstring pop dup 0\n"
+ "get dup 16#80 and 16#80 eq dup 3 1 roll { 16#7f and } if 2 index 0 3\n"
+ "-1 roll put 3 1 roll 0 0 1 5 -1 roll { 2 index exch get add 256 mul }\n"
+ "for 256 div exch pop exch { neg } if } def\n"
+ "/xcmd { rcmd exch rhex exch rhex exch 5 -1 roll add exch 4 -1 roll add\n"
+ "1 index 1 index 5 -1 roll { moveto } { lineto } ifelse } def end\n"
+ "/readpath { 0 0 pathdict begin { xcmd } loop end pop pop } def\n"
+ "\n"
+ "systemdict /languagelevel known not {\n"
+ "/xshow { exch dup length 0 1 3 -1 roll 1 sub { dup 3 index exch get\n"
+ "exch 2 index exch get 1 string dup 0 4 -1 roll put currentpoint 3 -1\n"
+ "roll show moveto 0 rmoveto } for pop pop } def\n"
+ "/rectangle { 4 -2 roll moveto 1 index 0 rlineto 0 exch rlineto neg 0\n"
+ "rlineto closepath } def\n"
+ "/rectfill { rectangle fill } def\n"
+ "/rectstroke { rectangle stroke } def } if\n"
+ "/bshow { currentlinewidth 3 1 roll currentpoint 3 index show moveto\n"
+ "setlinewidth false charpath stroke setlinewidth } def\n"
+ "/bxshow { currentlinewidth 4 1 roll setlinewidth exch dup length 1 sub\n"
+ "0 1 3 -1 roll { 1 string 2 index 2 index get 1 index exch 0 exch put dup\n"
+ "currentpoint 3 -1 roll show moveto currentpoint 3 -1 roll false charpath\n"
+ "stroke moveto 2 index exch get 0 rmoveto } for pop pop setlinewidth } def\n"
+ "\n"
+ "/psp_lzwfilter { currentfile /ASCII85Decode filter /LZWDecode filter } def\n"
+ "/psp_ascii85filter { currentfile /ASCII85Decode filter } def\n"
+ "/psp_lzwstring { psp_lzwfilter 1024 string readstring } def\n"
+ "/psp_ascii85string { psp_ascii85filter 1024 string readstring } def\n"
+ "/psp_imagedict {\n"
+ "/psp_bitspercomponent { 3 eq { 1 }{ 8 } ifelse } def\n"
+ "/psp_decodearray { [ [0 1 0 1 0 1] [0 255] [0 1] [0 255] ] exch get }\n"
+ "def 7 dict dup\n"
+ "/ImageType 1 put dup\n"
+ "/Width 7 -1 roll put dup\n"
+ "/Height 5 index put dup\n"
+ "/BitsPerComponent 4 index psp_bitspercomponent put dup\n"
+ "/Decode 5 -1 roll psp_decodearray put dup\n"
+ "/ImageMatrix [1 0 0 1 0 0] dup 5 8 -1 roll put put dup\n"
+ "/DataSource 4 -1 roll 1 eq { psp_lzwfilter } { psp_ascii85filter } ifelse put\n"
+ "} def\n"
+ "%%EndResource\n"
+ "%%EndProlog\n"
+ };
+ WritePS (pFile, m_pGraphics && m_pGraphics->getStrictSO52Compatibility() ? pSO52CompatProlog : pProlog);
+
+ return true;
+}
+
+bool PrinterJob::writeSetup( osl::File* pFile, const JobData& rJob )
+{
+ WritePS (pFile, "%%BeginSetup\n%\n");
+
+ // download fonts
+ std::list< rtl::OString > aFonts[2];
+ m_pGraphics->writeResources( pFile, aFonts[0], aFonts[1] );
+
+ for( int i = 0; i < 2; i++ )
+ {
+ if( !aFonts[i].empty() )
+ {
+ std::list< rtl::OString >::const_iterator it = aFonts[i].begin();
+ rtl::OStringBuffer aLine( 256 );
+ if( i == 0 )
+ aLine.append( "%%DocumentSuppliedResources: font " );
+ else
+ aLine.append( "%%DocumentNeededResources: font " );
+ aLine.append( *it );
+ aLine.append( "\n" );
+ WritePS ( pFile, aLine.getStr() );
+ while( (++it) != aFonts[i].end() )
+ {
+ aLine.setLength(0);
+ aLine.append( "%%+ font " );
+ aLine.append( *it );
+ aLine.append( "\n" );
+ WritePS ( pFile, aLine.getStr() );
+ }
+ }
+ }
+
+ bool bSuccess = true;
+ // in case of external print dialog the number of copies is prepended
+ // to the job, let us not complicate things by emitting our own copy count
+ bool bExternalDialog = PrinterInfoManager::get().checkFeatureToken( GetPrinterName(), "external_dialog" );
+ if( ! bExternalDialog && rJob.m_nCopies > 1 )
+ {
+ // setup code
+ rtl::OStringBuffer aLine(RTL_CONSTASCII_STRINGPARAM("/#copies "));
+ aLine.append(static_cast<sal_Int32>(rJob.m_nCopies));
+ aLine.append(RTL_CONSTASCII_STRINGPARAM(" def\n"));
+ sal_uInt64 nWritten = 0;
+ bSuccess = pFile->write(aLine.getStr(), aLine.getLength(), nWritten)
+ || nWritten != static_cast<sal_uInt64>(aLine.getLength()) ?
+ false : true;
+
+ if( bSuccess && GetPostscriptLevel( &rJob ) >= 2 )
+ WritePS (pFile, "<< /NumCopies null /Policies << /NumCopies 1 >> >> setpagedevice\n" );
+ }
+
+ bool bFeatureSuccess = writeFeatureList( pFile, rJob, true );
+
+ WritePS (pFile, "%%EndSetup\n");
+
+ return bSuccess && bFeatureSuccess;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/vcl/generic/print/psheader.ps b/vcl/generic/print/psheader.ps
new file mode 100644
index 000000000000..6a0e350d9ddc
--- /dev/null
+++ b/vcl/generic/print/psheader.ps
@@ -0,0 +1,368 @@
+%*************************************************************************
+%
+% DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+%
+% Copyright 2000, 2010 Oracle and/or its affiliates.
+%
+% OpenOffice.org - a multi-platform office productivity suite
+%
+% This file is part of OpenOffice.org.
+%
+% OpenOffice.org is free software: you can redistribute it and/or modify
+% it under the terms of the GNU Lesser General Public License version 3
+% only, as published by the Free Software Foundation.
+%
+% OpenOffice.org is distributed in the hope that it will be useful,
+% but WITHOUT ANY WARRANTY; without even the implied warranty of
+% MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+% GNU Lesser General Public License version 3 for more details
+% (a copy is included in the LICENSE file that accompanied this code).
+%
+% You should have received a copy of the GNU Lesser General Public License
+% version 3 along with OpenOffice.org. If not, see
+% <http://www.openoffice.org/license.html>
+% for a copy of the LGPLv3 License.
+%
+%*************************************************************************
+
+%
+%
+% readpath
+%
+% The intention of readpath is to save disk space since the vcl clip region routines
+% produce a huge amount of lineto/moveto commands
+%
+% The principal idea is to maintain the current point on stack and to provide only deltas
+% in the command. These deltas are added to the current point. The new point is used for
+% the lineto and moveto command and saved on stack for the next command.
+%
+% pathdict implements binary/hex representation of lineto and moveto commands.
+% The command consists of a 1byte opcode to switch between lineto and moveto and the size
+% of the following delta-x and delta-y values. The opcode is read with /rcmd, the two
+% coordinates are read with /rhex. The whole command is executed with /xcmd
+%
+%
+
+/pathdict dup 8 dict def load
+begin
+
+ % the command is of the bit format cxxyy
+ % with c=0 meaning lineto
+ % c=1 meaning moveto
+ % xx is a 2bit value for the number of bytes for x position
+ % yy is the same for y, values are off by one: 00 means 1; 11 means 4 !
+ % the command has been added to 'A' to be always in the ascii character
+ % range. the command is followed by 2*xx + 2*yy hexchars.
+ % '~' denotes the special case of EOD
+ /rcmd {
+ {
+ currentfile 1 string readstring % s bool
+ pop % s
+ 0 get % s[0]
+ % --- check wether s[0] is CR, LF ...
+ dup 32 gt % s > ' ' ? then read on
+ { exit }
+ { pop }
+ ifelse
+ }
+ loop
+
+ dup 126 eq { pop exit } if % -- Exit loop if cmd is '~'
+ 65 sub % cmd=s[0]-'A'
+ % -- Separate yy bits
+ dup 16#3 and 1 add % cmd yy
+ % -- Separate xx bits
+ exch % yy cmd
+ dup 16#C and -2 bitshift
+ 16#3 and 1 add exch % yy xx cmd
+ % -- Separate command bit
+ 16#10 and 16#10 eq % yy xx bool
+ 3 1 roll exch % bool xx yy
+ } def
+
+ % length rhex -- reads a signed hex value of given length
+ % the left most bit of char 0 is considered as the sign (0 means '+', 1 means '-')
+ % the rest of the bits is considered to be the abs value. Please note that this
+ % does not match the C binary representation of integers
+ /rhex {
+ dup 1 sub exch % l-1 l
+ currentfile exch string readhexstring % l-1 substring[l] bool
+ pop
+ dup 0 get dup % l-1 s s[0] s[0]
+ % -- Extract the sign
+ 16#80 and 16#80 eq dup % l-1 s s[0] sign=- sign=-
+ % -- Mask out the sign bit and put value back
+ 3 1 roll % l-1 s sign=- s[0] sign=-
+ { 16#7f and } if % l-1 s sign=- +s[0]
+ 2 index 0 % l-1 s sign=- +s[0] s 0
+ 3 -1 roll put % l-1 s sign=- s 0 +s[0]
+ % -- Read loop: add to prev sum, mul with 256
+ 3 1 roll 0 % sign=- l-1 s Sum=0
+ 0 1 5 -1 roll % sign=- s Sum=0 0 1 l-1
+ { % sign=- s Sum idx
+ 2 index exch % sign=- s Sum s idx
+ get % sign=- s Sum s[idx]
+ add 256 mul % sign=- s Sum=(s[idx]+Sum)*256
+ }
+ for
+ % -- mul was once too often, weave in the sign
+ 256 div % sign=- s Sum/256
+ exch pop % sign=- Sum/256
+ exch { neg } if % (sign=- ? -Sum : Sum)
+ } def
+
+ % execute a single command, the former x and y position is already on stack
+ % only offsets are read from cmdstring
+ /xcmd { % x y
+ rcmd % x y bool wx wy
+ exch rhex % x y bool wy Dx
+ exch rhex % x y bool Dx Dy
+ exch 5 -1 roll % y bool Dy Dx x
+ add exch % y bool X Dy
+ 4 -1 roll add % bool X Y
+ 1 index 1 index % bool X Y X Y
+ 5 -1 roll % X Y X Y bool
+ { moveto }
+ { lineto }
+ ifelse % X Y
+ } def
+end
+
+/readpath
+{
+ 0 0 % push initial-x initial-y
+ pathdict begin
+ { xcmd } loop
+ end
+ pop pop % pop final-x final-y
+} def
+
+%
+%
+% if languagelevel is not in the systemdict then its level 1 interpreter:
+% provide compatibility routines
+%
+%
+
+systemdict /languagelevel known not
+{
+ % string numarray xxshow -
+ % does only work for single byte fonts
+ /xshow {
+ exch dup % a s s
+ length 0 1 % a s l(s) 1 1
+ 3 -1 roll 1 sub % a s 0 1 l(s)-1
+ { % a s idx
+ dup % a s idx idx
+ % -- extract the delta offset
+ 3 index exch get % a s idx a[idx]
+ % -- extract the character
+ exch % a s a[idx] idx
+ 2 index exch get % a s a[idx] s[idx]
+ % -- create a tmp string for show
+ 1 string dup 0 % a s a[idx] s[idx] s1 s1 0
+ 4 -1 roll % a s a[idx] s1 s1 0 s[idx]
+ put % a s a[idx] s1
+ % -- store the current point
+ currentpoint 3 -1 roll % a s a[idx] x y s1
+ % -- draw the character
+ show % a s a[idx] x y
+ % -- move to the offset
+ moveto 0 rmoveto % a s
+ }
+ for
+ pop pop % -
+ } def
+
+ % x y width height rectfill
+ % x y width height rectshow
+ % in contrast to the languagelevel 2 operator
+ % they use and change the currentpath
+ /rectangle {
+ 4 -2 roll % width height x y
+ moveto % width height
+ 1 index 0 rlineto % width height % rmoveto(width, 0)
+ 0 exch rlineto % width % rmoveto(0, height)
+ neg 0 rlineto % - % rmoveto(-width, 0)
+ closepath
+ } def
+
+ /rectfill { rectangle fill } def
+ /rectstroke { rectangle stroke } def
+}
+if
+
+% -- small test program
+% 75 75 moveto /Times-Roman findfont 12 scalefont setfont
+% <292a2b2c2d2e2f30313233343536373839>
+% [5 5 6 6 6 6 6 6 6 6 6 6 7 7 7 7 5] xshow <21>[0] xshow
+% showpage
+
+%
+%
+% shortcuts for image header with compression
+%
+%
+
+/psp_lzwfilter {
+ currentfile /ASCII85Decode filter /LZWDecode filter
+} def
+/psp_ascii85filter {
+ currentfile /ASCII85Decode filter
+} def
+/psp_lzwstring {
+ psp_lzwfilter 1024 string readstring
+} def
+/psp_ascii85string {
+ psp_ascii85filter 1024 string readstring
+} def
+/psp_imagedict {
+ /psp_bitspercomponent {
+ 3 eq
+ { 1 }
+ { 8 }
+ ifelse
+ } def
+ /psp_decodearray {
+ [ [0 1 0 1 0 1] [0 255] [0 1] [0 255] ] exch get
+ } def
+
+ 7 dict dup
+ /ImageType 1 put dup
+ /Width 7 -1 roll put dup
+ /Height 5 index put dup
+ /BitsPerComponent 4 index
+ psp_bitspercomponent put dup
+ /Decode 5 -1 roll
+ psp_decodearray put dup
+ /ImageMatrix [1 0 0 1 0 0] dup
+ 5 8 -1 roll put put dup
+ /DataSource 4 -1 roll
+ 1 eq
+ { psp_lzwfilter }
+ { psp_ascii85filter }
+ ifelse put
+} def
+
+
+%
+%
+% font encoding and reencoding
+%
+%
+
+/ISO1252Encoding [
+ /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef
+ /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef
+ /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef
+ /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef /.notdef
+ /space /exclam /quotedbl /numbersign /dollar /percent /ampersand /quotesingle
+ /parenleft /parenright /asterisk /plus /comma /hyphen /period /slash
+ /zero /one /two /three /four /five /six /seven
+ /eight /nine /colon /semicolon /less /equal /greater /question
+ /at /A /B /C /D /E /F /G
+ /H /I /J /K /L /M /N /O
+ /P /Q /R /S /T /U /V /W
+ /X /Y /Z /bracketleft /backslash /bracketright /asciicircum /underscore
+ /grave /a /b /c /d /e /f /g
+ /h /i /j /k /l /m /n /o
+ /p /q /r /s /t /u /v /w
+ /x /y /z /braceleft /bar /braceright /asciitilde /unused
+ /Euro /unused /quotesinglbase /florin /quotedblbase /ellipsis /dagger /daggerdbl
+ /circumflex /perthousand /Scaron /guilsinglleft /OE /unused /Zcaron /unused
+ /unused /quoteleft /quoteright /quotedblleft /quotedblright /bullet /endash /emdash
+ /tilde /trademark /scaron /guilsinglright /oe /unused /zcaron /Ydieresis
+ /space /exclamdown /cent /sterling /currency /yen /brokenbar /section
+ /dieresis /copyright /ordfeminine /guillemotleft /logicalnot /hyphen /registered /macron
+ /degree /plusminus /twosuperior /threesuperior /acute /mu /paragraph /periodcentered
+ /cedilla /onesuperior /ordmasculine /guillemotright /onequarter /onehalf /threequarters /questiondown
+ /Agrave /Aacute /Acircumflex /Atilde /Adieresis /Aring /AE /Ccedilla
+ /Egrave /Eacute /Ecircumflex /Edieresis /Igrave /Iacute /Icircumflex /Idieresis
+ /Eth /Ntilde /Ograve /Oacute /Ocircumflex /Otilde /Odieresis /multiply
+ /Oslash /Ugrave /Uacute /Ucircumflex /Udieresis /Yacute /Thorn /germandbls
+ /agrave /aacute /acircumflex /atilde /adieresis /aring /ae /ccedilla
+ /egrave /eacute /ecircumflex /edieresis /igrave /iacute /icircumflex /idieresis
+ /eth /ntilde /ograve /oacute /ocircumflex /otilde /odieresis /divide
+ /oslash /ugrave /uacute /ucircumflex /udieresis /yacute /thorn /ydieresis
+] def
+
+% /fontname /encoding psp_findfont
+/psp_findfont {
+ exch dup % encoding fontname fontname
+ findfont % encoding fontname
+ dup length dict
+ begin
+ {
+ 1 index /FID ne
+ { def }
+ { pop pop }
+ ifelse
+ } forall
+ /Encoding 3 -1 roll def
+ currentdict
+ end
+ /psp_reencodedfont exch definefont
+} def
+
+% bshow shows a text in artificial bold
+% this is achieved by first showing the text
+% then stroking its outline over it with
+% the linewidth set to the second parameter
+% usage: (string) num bshow
+
+/bshow {
+ currentlinewidth % save current linewidth
+ 3 1 roll % move it to the last stack position
+ currentpoint % save the current point
+ 3 index % copy the string to show
+ show % show it
+ moveto % move to the original coordinates again
+ setlinewidth % set the linewidth
+ false charpath % create the outline path of the shown string
+ stroke % and stroke it
+ setlinewidth % reset the stored linewidth
+} def
+
+% bxshow shows a text with a delta array in artificial bold
+% that is it does what bshow does for show
+% usage: (string) [deltaarray] num bxshow
+
+/bxshow {
+ currentlinewidth % save linewidth
+ 4 1 roll % move it to the last stack position
+ setlinewidth % set the new linewidth
+ exch % exchange string and delta array
+ dup
+ length % get length of string
+ 1 sub % prepare parameters for {} for
+ 0 1
+ 3 -1 roll
+ {
+ 1 string % create a string object length 1
+ 2 index % get the text
+ 2 index % get charpos (for index variable)
+ get % have char value at charpos
+ 1 index % prepare string for put
+ exch
+ 0
+ exch
+ put % put into string of length 1
+ dup % duplicate the it
+ currentpoint % save current position
+ 3 -1 roll % prepare show
+ show % show the character
+ moveto % move back to beginning
+ currentpoint % save current position
+ 3 -1 roll % prepare outline path of character
+ false charpath
+ stroke % stroke it
+ moveto % move back
+ % now move to next point
+ 2 index % get advance array
+ exch % get charpos
+ get % get advance element
+ 0 rmoveto % advance current position
+ } for
+ pop pop % remove string and delta array
+ setlinewidth % restore linewidth
+} def
diff --git a/vcl/generic/print/pspgraphics.cxx b/vcl/generic/print/pspgraphics.cxx
new file mode 100644
index 000000000000..c99ae54b4ed5
--- /dev/null
+++ b/vcl/generic/print/pspgraphics.cxx
@@ -0,0 +1,1413 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*************************************************************************
+ *
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * Copyright 2000, 2010 Oracle and/or its affiliates.
+ *
+ * OpenOffice.org - a multi-platform office productivity suite
+ *
+ * This file is part of OpenOffice.org.
+ *
+ * OpenOffice.org is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License version 3
+ * only, as published by the Free Software Foundation.
+ *
+ * OpenOffice.org is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License version 3 for more details
+ * (a copy is included in the LICENSE file that accompanied this code).
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * version 3 along with OpenOffice.org. If not, see
+ * <http://www.openoffice.org/license.html>
+ * for a copy of the LGPLv3 License.
+ *
+ ************************************************************************/
+
+// MARKER(update_precomp.py): autogen include statement, do not remove
+#include "precompiled_vcl.hxx"
+
+#include <stdlib.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <sys/mman.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+
+#include "generic/geninst.h"
+#include "generic/pspgraphics.h"
+#include "generic/glyphcache.hxx"
+
+#include "vcl/jobdata.hxx"
+#include "vcl/printerinfomanager.hxx"
+#include "vcl/bmpacc.hxx"
+#include "vcl/svapp.hxx"
+#include "vcl/sysdata.hxx"
+
+#include "generic/printergfx.hxx"
+#include "salbmp.hxx"
+#include "impfont.hxx"
+#include "outfont.hxx"
+#include "fontsubset.hxx"
+#include "salprn.hxx"
+#include "region.h"
+
+#ifdef ENABLE_GRAPHITE
+#include <graphite_layout.hxx>
+#include <graphite_serverfont.hxx>
+#endif
+
+using namespace psp;
+
+using ::rtl::OUString;
+using ::rtl::OString;
+
+// ----- Implementation of PrinterBmp by means of SalBitmap/BitmapBuffer ---------------
+
+class SalPrinterBmp : public psp::PrinterBmp
+{
+ private:
+ BitmapBuffer* mpBmpBuffer;
+
+ FncGetPixel mpFncGetPixel;
+ Scanline mpScanAccess;
+ sal_PtrDiff mnScanOffset;
+
+ sal_uInt32 ColorOf (BitmapColor& rColor) const;
+ sal_uInt8 GrayOf (BitmapColor& rColor) const;
+
+ SalPrinterBmp ();
+
+ public:
+
+ SalPrinterBmp (BitmapBuffer* pBitmap);
+ virtual ~SalPrinterBmp ();
+ virtual sal_uInt32 GetPaletteColor (sal_uInt32 nIdx) const;
+ virtual sal_uInt32 GetPaletteEntryCount () const;
+ virtual sal_uInt32 GetPixelRGB (sal_uInt32 nRow, sal_uInt32 nColumn) const;
+ virtual sal_uInt8 GetPixelGray (sal_uInt32 nRow, sal_uInt32 nColumn) const;
+ virtual sal_uInt8 GetPixelIdx (sal_uInt32 nRow, sal_uInt32 nColumn) const;
+ virtual sal_uInt32 GetWidth () const;
+ virtual sal_uInt32 GetHeight() const;
+ virtual sal_uInt32 GetDepth () const;
+};
+
+SalPrinterBmp::SalPrinterBmp (BitmapBuffer* pBuffer) :
+ mpBmpBuffer (pBuffer)
+{
+ DBG_ASSERT (mpBmpBuffer, "SalPrinterBmp::SalPrinterBmp () can't acquire Bitmap");
+
+ // calibrate scanline buffer
+ if( BMP_SCANLINE_ADJUSTMENT( mpBmpBuffer->mnFormat ) == BMP_FORMAT_TOP_DOWN )
+ {
+ mpScanAccess = mpBmpBuffer->mpBits;
+ mnScanOffset = mpBmpBuffer->mnScanlineSize;
+ }
+ else
+ {
+ mpScanAccess = mpBmpBuffer->mpBits
+ + (mpBmpBuffer->mnHeight - 1) * mpBmpBuffer->mnScanlineSize;
+ mnScanOffset = - mpBmpBuffer->mnScanlineSize;
+ }
+
+ // request read access to the pixels
+ switch( BMP_SCANLINE_FORMAT( mpBmpBuffer->mnFormat ) )
+ {
+ case BMP_FORMAT_1BIT_MSB_PAL:
+ mpFncGetPixel = BitmapReadAccess::GetPixelFor_1BIT_MSB_PAL; break;
+ case BMP_FORMAT_1BIT_LSB_PAL:
+ mpFncGetPixel = BitmapReadAccess::GetPixelFor_1BIT_LSB_PAL; break;
+ case BMP_FORMAT_4BIT_MSN_PAL:
+ mpFncGetPixel = BitmapReadAccess::GetPixelFor_4BIT_MSN_PAL; break;
+ case BMP_FORMAT_4BIT_LSN_PAL:
+ mpFncGetPixel = BitmapReadAccess::GetPixelFor_4BIT_LSN_PAL; break;
+ case BMP_FORMAT_8BIT_PAL:
+ mpFncGetPixel = BitmapReadAccess::GetPixelFor_8BIT_PAL; break;
+ case BMP_FORMAT_8BIT_TC_MASK:
+ mpFncGetPixel = BitmapReadAccess::GetPixelFor_8BIT_TC_MASK; break;
+ case BMP_FORMAT_16BIT_TC_MSB_MASK:
+ mpFncGetPixel = BitmapReadAccess::GetPixelFor_16BIT_TC_MSB_MASK; break;
+ case BMP_FORMAT_16BIT_TC_LSB_MASK:
+ mpFncGetPixel = BitmapReadAccess::GetPixelFor_16BIT_TC_LSB_MASK; break;
+ case BMP_FORMAT_24BIT_TC_BGR:
+ mpFncGetPixel = BitmapReadAccess::GetPixelFor_24BIT_TC_BGR; break;
+ case BMP_FORMAT_24BIT_TC_RGB:
+ mpFncGetPixel = BitmapReadAccess::GetPixelFor_24BIT_TC_RGB; break;
+ case BMP_FORMAT_24BIT_TC_MASK:
+ mpFncGetPixel = BitmapReadAccess::GetPixelFor_24BIT_TC_MASK; break;
+ case BMP_FORMAT_32BIT_TC_ABGR:
+ mpFncGetPixel = BitmapReadAccess::GetPixelFor_32BIT_TC_ABGR; break;
+ case BMP_FORMAT_32BIT_TC_ARGB:
+ mpFncGetPixel = BitmapReadAccess::GetPixelFor_32BIT_TC_ARGB; break;
+ case BMP_FORMAT_32BIT_TC_BGRA:
+ mpFncGetPixel = BitmapReadAccess::GetPixelFor_32BIT_TC_BGRA; break;
+ case BMP_FORMAT_32BIT_TC_RGBA:
+ mpFncGetPixel = BitmapReadAccess::GetPixelFor_32BIT_TC_RGBA; break;
+ case BMP_FORMAT_32BIT_TC_MASK:
+ mpFncGetPixel = BitmapReadAccess::GetPixelFor_32BIT_TC_MASK; break;
+
+ default:
+ OSL_FAIL("Error: SalPrinterBmp::SalPrinterBmp() unknown bitmap format");
+ break;
+ }
+}
+
+SalPrinterBmp::~SalPrinterBmp ()
+{
+}
+
+sal_uInt32
+SalPrinterBmp::GetWidth () const
+{
+ return mpBmpBuffer->mnWidth;
+}
+
+sal_uInt32
+SalPrinterBmp::GetHeight () const
+{
+ return mpBmpBuffer->mnHeight;
+}
+
+sal_uInt32
+SalPrinterBmp::GetDepth () const
+{
+ sal_uInt32 nDepth;
+
+ switch (mpBmpBuffer->mnBitCount)
+ {
+ case 1:
+ nDepth = 1;
+ break;
+
+ case 4:
+ case 8:
+ nDepth = 8;
+ break;
+
+ case 16:
+ case 24:
+ case 32:
+ nDepth = 24;
+ break;
+
+ default:
+ nDepth = 1;
+ OSL_FAIL("Error: unsupported bitmap depth in SalPrinterBmp::GetDepth()");
+ break;
+ }
+
+ return nDepth;
+}
+
+sal_uInt32
+SalPrinterBmp::ColorOf (BitmapColor& rColor) const
+{
+ if (rColor.IsIndex())
+ return ColorOf (mpBmpBuffer->maPalette[rColor.GetIndex()]);
+ else
+ return ((rColor.GetBlue()) & 0x000000ff)
+ | ((rColor.GetGreen() << 8) & 0x0000ff00)
+ | ((rColor.GetRed() << 16) & 0x00ff0000);
+}
+
+sal_uInt8
+SalPrinterBmp::GrayOf (BitmapColor& rColor) const
+{
+ if (rColor.IsIndex())
+ return GrayOf (mpBmpBuffer->maPalette[rColor.GetIndex()]);
+ else
+ return ( rColor.GetBlue() * 28UL
+ + rColor.GetGreen() * 151UL
+ + rColor.GetRed() * 77UL ) >> 8;
+}
+
+sal_uInt32
+SalPrinterBmp::GetPaletteEntryCount () const
+{
+ return mpBmpBuffer->maPalette.GetEntryCount ();
+}
+
+sal_uInt32
+SalPrinterBmp::GetPaletteColor (sal_uInt32 nIdx) const
+{
+ return ColorOf (mpBmpBuffer->maPalette[nIdx]);
+}
+
+sal_uInt32
+SalPrinterBmp::GetPixelRGB (sal_uInt32 nRow, sal_uInt32 nColumn) const
+{
+ Scanline pScan = mpScanAccess + nRow * mnScanOffset;
+ BitmapColor aColor = mpFncGetPixel (pScan, nColumn, mpBmpBuffer->maColorMask);
+
+ return ColorOf (aColor);
+}
+
+sal_uInt8
+SalPrinterBmp::GetPixelGray (sal_uInt32 nRow, sal_uInt32 nColumn) const
+{
+ Scanline pScan = mpScanAccess + nRow * mnScanOffset;
+ BitmapColor aColor = mpFncGetPixel (pScan, nColumn, mpBmpBuffer->maColorMask);
+
+ return GrayOf (aColor);
+}
+
+sal_uInt8
+SalPrinterBmp::GetPixelIdx (sal_uInt32 nRow, sal_uInt32 nColumn) const
+{
+ Scanline pScan = mpScanAccess + nRow * mnScanOffset;
+ BitmapColor aColor = mpFncGetPixel (pScan, nColumn, mpBmpBuffer->maColorMask);
+
+ if (aColor.IsIndex())
+ return aColor.GetIndex();
+ else
+ return 0;
+}
+
+/*******************************************************
+ * PspGraphics *
+ *******************************************************/
+
+PspGraphics::~PspGraphics()
+{
+ ReleaseFonts();
+}
+
+void PspGraphics::GetResolution( sal_Int32 &rDPIX, sal_Int32 &rDPIY )
+{
+ if (m_pJobData != NULL)
+ {
+ int x = m_pJobData->m_aContext.getRenderResolution();
+
+ rDPIX = x;
+ rDPIY = x;
+ }
+}
+
+sal_uInt16 PspGraphics::GetBitCount() const
+{
+ return m_pPrinterGfx->GetBitCount();
+}
+
+long PspGraphics::GetGraphicsWidth() const
+{
+ return 0;
+}
+
+void PspGraphics::ResetClipRegion()
+{
+ m_pPrinterGfx->ResetClipRegion();
+}
+
+bool PspGraphics::setClipRegion( const Region& i_rClip )
+{
+ // TODO: support polygonal clipregions here
+ m_pPrinterGfx->BeginSetClipRegion( i_rClip.GetRectCount() );
+
+ ImplRegionInfo aInfo;
+ long nX, nY, nW, nH;
+ bool bRegionRect = i_rClip.ImplGetFirstRect(aInfo, nX, nY, nW, nH );
+ while( bRegionRect )
+ {
+ if ( nW && nH )
+ {
+ m_pPrinterGfx->UnionClipRegion( nX, nY, nW, nH );
+ }
+ bRegionRect = i_rClip.ImplGetNextRect( aInfo, nX, nY, nW, nH );
+ }
+ m_pPrinterGfx->EndSetClipRegion();
+ return true;
+}
+
+void PspGraphics::SetLineColor()
+{
+ m_pPrinterGfx->SetLineColor ();
+}
+
+void PspGraphics::SetLineColor( SalColor nSalColor )
+{
+ psp::PrinterColor aColor (SALCOLOR_RED (nSalColor),
+ SALCOLOR_GREEN (nSalColor),
+ SALCOLOR_BLUE (nSalColor));
+ m_pPrinterGfx->SetLineColor (aColor);
+}
+
+void PspGraphics::SetFillColor()
+{
+ m_pPrinterGfx->SetFillColor ();
+}
+
+void PspGraphics::SetFillColor( SalColor nSalColor )
+{
+ psp::PrinterColor aColor (SALCOLOR_RED (nSalColor),
+ SALCOLOR_GREEN (nSalColor),
+ SALCOLOR_BLUE (nSalColor));
+ m_pPrinterGfx->SetFillColor (aColor);
+}
+
+void PspGraphics::SetROPLineColor( SalROPColor )
+{
+ DBG_ASSERT( 0, "Error: PrinterGfx::SetROPLineColor() not implemented" );
+}
+
+void PspGraphics::SetROPFillColor( SalROPColor )
+{
+ DBG_ASSERT( 0, "Error: PrinterGfx::SetROPFillColor() not implemented" );
+}
+
+void PspGraphics::SetXORMode( bool bSet, bool )
+{
+ (void)bSet;
+ DBG_ASSERT( !bSet, "Error: PrinterGfx::SetXORMode() not implemented" );
+}
+
+void PspGraphics::drawPixel( long nX, long nY )
+{
+ m_pPrinterGfx->DrawPixel (Point(nX, nY));
+}
+
+void PspGraphics::drawPixel( long nX, long nY, SalColor nSalColor )
+{
+ psp::PrinterColor aColor (SALCOLOR_RED (nSalColor),
+ SALCOLOR_GREEN (nSalColor),
+ SALCOLOR_BLUE (nSalColor));
+ m_pPrinterGfx->DrawPixel (Point(nX, nY), aColor);
+}
+
+void PspGraphics::drawLine( long nX1, long nY1, long nX2, long nY2 )
+{
+ m_pPrinterGfx->DrawLine (Point(nX1, nY1), Point(nX2, nY2));
+}
+
+void PspGraphics::drawRect( long nX, long nY, long nDX, long nDY )
+{
+ m_pPrinterGfx->DrawRect (Rectangle(Point(nX, nY), Size(nDX, nDY)));
+}
+
+void PspGraphics::drawPolyLine( sal_uLong nPoints, const SalPoint *pPtAry )
+{
+ m_pPrinterGfx->DrawPolyLine (nPoints, (Point*)pPtAry);
+}
+
+void PspGraphics::drawPolygon( sal_uLong nPoints, const SalPoint* pPtAry )
+{
+ // Point must be equal to SalPoint! see vcl/inc/salgtype.hxx
+ m_pPrinterGfx->DrawPolygon (nPoints, (Point*)pPtAry);
+}
+
+void PspGraphics::drawPolyPolygon( sal_uInt32 nPoly,
+ const sal_uInt32 *pPoints,
+ PCONSTSALPOINT *pPtAry )
+{
+ m_pPrinterGfx->DrawPolyPolygon (nPoly, pPoints, (const Point**)pPtAry);
+}
+
+bool PspGraphics::drawPolyPolygon( const ::basegfx::B2DPolyPolygon&, double /*fTransparency*/ )
+{
+ // TODO: implement and advertise OutDevSupport_B2DDraw support
+ return false;
+}
+
+bool PspGraphics::drawPolyLine( const basegfx::B2DPolygon&, double /*fTranspareny*/, const basegfx::B2DVector& /*rLineWidths*/, basegfx::B2DLineJoin /*eJoin*/)
+{
+ // TODO: a PS printer can draw B2DPolyLines almost directly
+ return false;
+}
+
+sal_Bool PspGraphics::drawPolyLineBezier( sal_uLong nPoints, const SalPoint* pPtAry, const sal_uInt8* pFlgAry )
+{
+ m_pPrinterGfx->DrawPolyLineBezier (nPoints, (Point*)pPtAry, pFlgAry);
+ return sal_True;
+}
+
+sal_Bool PspGraphics::drawPolygonBezier( sal_uLong nPoints, const SalPoint* pPtAry, const sal_uInt8* pFlgAry )
+{
+ m_pPrinterGfx->DrawPolygonBezier (nPoints, (Point*)pPtAry, pFlgAry);
+ return sal_True;
+}
+
+sal_Bool PspGraphics::drawPolyPolygonBezier( sal_uInt32 nPoly,
+ const sal_uInt32* pPoints,
+ const SalPoint* const* pPtAry,
+ const sal_uInt8* const* pFlgAry )
+{
+ // Point must be equal to SalPoint! see vcl/inc/salgtype.hxx
+ m_pPrinterGfx->DrawPolyPolygonBezier (nPoly, pPoints, (Point**)pPtAry, (sal_uInt8**)pFlgAry);
+ return sal_True;
+}
+
+void PspGraphics::invert( sal_uLong,
+ const SalPoint*,
+ SalInvert )
+{
+ DBG_ASSERT( 0, "Error: PrinterGfx::Invert() not implemented" );
+}
+sal_Bool PspGraphics::drawEPS( long nX, long nY, long nWidth, long nHeight, void* pPtr, sal_uLong nSize )
+{
+ return m_pPrinterGfx->DrawEPS( Rectangle( Point( nX, nY ), Size( nWidth, nHeight ) ), pPtr, nSize );
+}
+
+void PspGraphics::copyBits( const SalTwoRect*,
+ SalGraphics* )
+{
+ OSL_FAIL( "Error: PrinterGfx::CopyBits() not implemented" );
+}
+
+void PspGraphics::copyArea ( long,long,long,long,long,long,sal_uInt16 )
+{
+ OSL_FAIL( "Error: PrinterGfx::CopyArea() not implemented" );
+}
+
+void PspGraphics::drawBitmap( const SalTwoRect* pPosAry, const SalBitmap& rSalBitmap )
+{
+ Rectangle aSrc (Point(pPosAry->mnSrcX, pPosAry->mnSrcY),
+ Size(pPosAry->mnSrcWidth, pPosAry->mnSrcHeight));
+ Rectangle aDst (Point(pPosAry->mnDestX, pPosAry->mnDestY),
+ Size(pPosAry->mnDestWidth, pPosAry->mnDestHeight));
+
+ BitmapBuffer* pBuffer= const_cast<SalBitmap&>(rSalBitmap).AcquireBuffer(sal_True);
+
+ SalPrinterBmp aBmp (pBuffer);
+ m_pPrinterGfx->DrawBitmap (aDst, aSrc, aBmp);
+
+ const_cast<SalBitmap&>(rSalBitmap).ReleaseBuffer (pBuffer, sal_True);
+}
+
+void PspGraphics::drawBitmap( const SalTwoRect*,
+ const SalBitmap&,
+ const SalBitmap& )
+{
+ OSL_FAIL("Error: no PrinterGfx::DrawBitmap() for transparent bitmap");
+}
+
+void PspGraphics::drawBitmap( const SalTwoRect*,
+ const SalBitmap&,
+ SalColor )
+{
+ OSL_FAIL("Error: no PrinterGfx::DrawBitmap() for transparent color");
+}
+
+void PspGraphics::drawMask( const SalTwoRect*,
+ const SalBitmap &,
+ SalColor )
+{
+ OSL_FAIL("Error: PrinterGfx::DrawMask() not implemented");
+}
+
+SalBitmap* PspGraphics::getBitmap( long, long, long, long )
+{
+ DBG_WARNING ("Warning: PrinterGfx::GetBitmap() not implemented");
+ return NULL;
+}
+
+SalColor PspGraphics::getPixel( long, long )
+{
+ OSL_FAIL("Warning: PrinterGfx::GetPixel() not implemented");
+ return 0;
+}
+
+void PspGraphics::invert(long,long,long,long,SalInvert)
+{
+ OSL_FAIL("Warning: PrinterGfx::Invert() not implemented");
+}
+
+//==========================================================================
+
+class ImplPspFontData : public ImplFontData
+{
+private:
+ enum { PSPFD_MAGIC = 0xb5bf01f0 };
+ sal_IntPtr mnFontId;
+
+public:
+ ImplPspFontData( const psp::FastPrintFontInfo& );
+ virtual sal_IntPtr GetFontId() const { return mnFontId; }
+ virtual ImplFontData* Clone() const { return new ImplPspFontData( *this ); }
+ virtual ImplFontEntry* CreateFontInstance( ImplFontSelectData& ) const;
+ static bool CheckFontData( const ImplFontData& r ) { return r.CheckMagic( PSPFD_MAGIC ); }
+};
+
+//--------------------------------------------------------------------------
+
+ImplPspFontData::ImplPspFontData( const psp::FastPrintFontInfo& rInfo )
+: ImplFontData( PspGraphics::Info2DevFontAttributes(rInfo), PSPFD_MAGIC ),
+ mnFontId( rInfo.m_nID )
+{}
+
+//--------------------------------------------------------------------------
+
+ImplFontEntry* ImplPspFontData::CreateFontInstance( ImplFontSelectData& rFSD ) const
+{
+ ImplServerFontEntry* pEntry = new ImplServerFontEntry( rFSD );
+ return pEntry;
+}
+
+//==========================================================================
+
+class PspFontLayout : public GenericSalLayout
+{
+public:
+ PspFontLayout( ::psp::PrinterGfx& );
+ virtual bool LayoutText( ImplLayoutArgs& );
+ virtual void InitFont() const;
+ virtual void DrawText( SalGraphics& ) const;
+private:
+ ::psp::PrinterGfx& mrPrinterGfx;
+ sal_IntPtr mnFontID;
+ int mnFontHeight;
+ int mnFontWidth;
+ bool mbVertical;
+ bool mbArtItalic;
+ bool mbArtBold;
+};
+
+//--------------------------------------------------------------------------
+
+PspFontLayout::PspFontLayout( ::psp::PrinterGfx& rGfx )
+: mrPrinterGfx( rGfx )
+{
+ mnFontID = mrPrinterGfx.GetFontID();
+ mnFontHeight = mrPrinterGfx.GetFontHeight();
+ mnFontWidth = mrPrinterGfx.GetFontWidth();
+ mbVertical = mrPrinterGfx.GetFontVertical();
+ mbArtItalic = mrPrinterGfx.GetArtificialItalic();
+ mbArtBold = mrPrinterGfx.GetArtificialBold();
+}
+
+//--------------------------------------------------------------------------
+
+bool PspFontLayout::LayoutText( ImplLayoutArgs& rArgs )
+{
+ mbVertical = ((rArgs.mnFlags & SAL_LAYOUT_VERTICAL) != 0);
+
+ long nUnitsPerPixel = 1;
+ int nOldGlyphId = -1;
+ long nGlyphWidth = 0;
+ int nCharPos = -1;
+ Point aNewPos( 0, 0 );
+ GlyphItem aPrevItem;
+ rtl_TextEncoding aFontEnc = mrPrinterGfx.GetFontMgr().getFontEncoding( mnFontID );
+ for(;;)
+ {
+ bool bRightToLeft;
+ if( !rArgs.GetNextPos( &nCharPos, &bRightToLeft ) )
+ break;
+
+ sal_Unicode cChar = rArgs.mpStr[ nCharPos ];
+ if( bRightToLeft )
+ cChar = GetMirroredChar( cChar );
+ // symbol font aliasing: 0x0020-0x00ff -> 0xf020 -> 0xf0ff
+ if( aFontEnc == RTL_TEXTENCODING_SYMBOL )
+ if( cChar < 256 )
+ cChar += 0xf000;
+ int nGlyphIndex = cChar; // printer glyphs = unicode
+
+ // update fallback_runs if needed
+ psp::CharacterMetric aMetric;
+ mrPrinterGfx.GetFontMgr().getMetrics( mnFontID, cChar, cChar, &aMetric, mbVertical );
+ if( aMetric.width == -1 && aMetric.height == -1 )
+ rArgs.NeedFallback( nCharPos, bRightToLeft );
+
+ // apply pair kerning to prev glyph if requested
+ if( SAL_LAYOUT_KERNING_PAIRS & rArgs.mnFlags )
+ {
+ if( nOldGlyphId > 0 )
+ {
+ const std::list< KernPair >& rKernPairs = mrPrinterGfx.getKernPairs(mbVertical);
+ for( std::list< KernPair >::const_iterator it = rKernPairs.begin();
+ it != rKernPairs.end(); ++it )
+ {
+ if( it->first == nOldGlyphId && it->second == nGlyphIndex )
+ {
+ int nTextScale = mrPrinterGfx.GetFontWidth();
+ if( ! nTextScale )
+ nTextScale = mrPrinterGfx.GetFontHeight();
+ int nKern = (mbVertical ? it->kern_y : it->kern_x) * nTextScale;
+ nGlyphWidth += nKern;
+ aPrevItem.mnNewWidth = nGlyphWidth;
+ break;
+ }
+ }
+ }
+ }
+
+ // finish previous glyph
+ if( nOldGlyphId >= 0 )
+ AppendGlyph( aPrevItem );
+ nOldGlyphId = nGlyphIndex;
+ aNewPos.X() += nGlyphWidth;
+
+ // prepare GlyphItem for appending it in next round
+ nUnitsPerPixel = mrPrinterGfx.GetCharWidth( cChar, cChar, &nGlyphWidth );
+ int nGlyphFlags = bRightToLeft ? GlyphItem::IS_RTL_GLYPH : 0;
+ nGlyphIndex |= GF_ISCHAR;
+ aPrevItem = GlyphItem( nCharPos, nGlyphIndex, aNewPos, nGlyphFlags, nGlyphWidth );
+ }
+
+ // append last glyph item if any
+ if( nOldGlyphId >= 0 )
+ AppendGlyph( aPrevItem );
+
+ SetOrientation( mrPrinterGfx.GetFontAngle() );
+ SetUnitsPerPixel( nUnitsPerPixel );
+ return (nOldGlyphId >= 0);
+}
+
+class PspServerFontLayout : public ServerFontLayout
+{
+public:
+ PspServerFontLayout( psp::PrinterGfx&, ServerFont& rFont, const ImplLayoutArgs& rArgs );
+
+ virtual void InitFont() const;
+ const sal_Unicode* getTextPtr() const { return maText.getStr() - mnMinCharPos; }
+ int getMinCharPos() const { return mnMinCharPos; }
+ int getMaxCharPos() const { return mnMinCharPos+maText.getLength()-1; }
+private:
+ ::psp::PrinterGfx& mrPrinterGfx;
+ sal_IntPtr mnFontID;
+ int mnFontHeight;
+ int mnFontWidth;
+ bool mbVertical;
+ bool mbArtItalic;
+ bool mbArtBold;
+ rtl::OUString maText;
+ int mnMinCharPos;
+};
+
+PspServerFontLayout::PspServerFontLayout( ::psp::PrinterGfx& rGfx, ServerFont& rFont, const ImplLayoutArgs& rArgs )
+ : ServerFontLayout( rFont ),
+ mrPrinterGfx( rGfx )
+{
+ mnFontID = mrPrinterGfx.GetFontID();
+ mnFontHeight = mrPrinterGfx.GetFontHeight();
+ mnFontWidth = mrPrinterGfx.GetFontWidth();
+ mbVertical = mrPrinterGfx.GetFontVertical();
+ mbArtItalic = mrPrinterGfx.GetArtificialItalic();
+ mbArtBold = mrPrinterGfx.GetArtificialBold();
+ maText = OUString( rArgs.mpStr + rArgs.mnMinCharPos, rArgs.mnEndCharPos - rArgs.mnMinCharPos+1 );
+ mnMinCharPos = rArgs.mnMinCharPos;
+}
+
+void PspServerFontLayout::InitFont() const
+{
+ mrPrinterGfx.SetFont( mnFontID, mnFontHeight, mnFontWidth,
+ mnOrientation, mbVertical, mbArtItalic, mbArtBold );
+}
+
+//--------------------------------------------------------------------------
+
+static void DrawPrinterLayout( const SalLayout& rLayout, ::psp::PrinterGfx& rGfx, bool bIsPspServerFontLayout )
+{
+ const int nMaxGlyphs = 200;
+ sal_uInt32 aGlyphAry[ nMaxGlyphs ]; // TODO: use sal_GlyphId
+ sal_Int32 aWidthAry[ nMaxGlyphs ];
+ sal_Int32 aIdxAry [ nMaxGlyphs ];
+ sal_Unicode aUnicodes[ nMaxGlyphs ];
+ int aCharPosAry [ nMaxGlyphs ];
+
+ Point aPos;
+ long nUnitsPerPixel = rLayout.GetUnitsPerPixel();
+ const sal_Unicode* pText = NULL;
+ int nMinCharPos = 0;
+ int nMaxCharPos = 0;
+ if (bIsPspServerFontLayout)
+ {
+ const PspServerFontLayout * pPspLayout = dynamic_cast<const PspServerFontLayout*>(&rLayout);
+#ifdef ENABLE_GRAPHITE
+ const GraphiteServerFontLayout * pGrLayout = dynamic_cast<const GraphiteServerFontLayout*>(&rLayout);
+#endif
+ if (pPspLayout)
+ {
+ pText = pPspLayout->getTextPtr();
+ nMinCharPos = pPspLayout->getMinCharPos();
+ nMaxCharPos = pPspLayout->getMaxCharPos();
+ }
+#ifdef ENABLE_GRAPHITE
+ else if (pGrLayout)
+ {
+ }
+#endif
+ }
+ for( int nStart = 0;; )
+ {
+ int nGlyphCount = rLayout.GetNextGlyphs( nMaxGlyphs, aGlyphAry, aPos, nStart, aWidthAry, pText ? aCharPosAry : NULL );
+ if( !nGlyphCount )
+ break;
+
+ sal_Int32 nXOffset = 0;
+ for( int i = 0; i < nGlyphCount; ++i )
+ {
+ nXOffset += aWidthAry[ i ];
+ aIdxAry[ i ] = nXOffset / nUnitsPerPixel;
+ sal_Int32 nGlyphIdx = aGlyphAry[i] & (GF_IDXMASK | GF_ROTMASK);
+ if( pText )
+ aUnicodes[i] = (aCharPosAry[i] >= nMinCharPos && aCharPosAry[i] <= nMaxCharPos) ? pText[ aCharPosAry[i] ] : 0;
+ else
+ aUnicodes[i] = (aGlyphAry[i] & GF_ISCHAR) ? nGlyphIdx : 0;
+ aGlyphAry[i] = nGlyphIdx;
+ }
+
+ rGfx.DrawGlyphs( aPos, (sal_uInt32 *)aGlyphAry, aUnicodes, nGlyphCount, aIdxAry );
+ }
+}
+
+//--------------------------------------------------------------------------
+
+void PspFontLayout::InitFont() const
+{
+ mrPrinterGfx.SetFont( mnFontID, mnFontHeight, mnFontWidth,
+ mnOrientation, mbVertical, mbArtItalic, mbArtBold );
+}
+
+//--------------------------------------------------------------------------
+
+void PspFontLayout::DrawText( SalGraphics& ) const
+{
+ DrawPrinterLayout( *this, mrPrinterGfx, false );
+}
+
+void PspGraphics::DrawServerFontLayout( const ServerFontLayout& rLayout )
+{
+ // print complex text
+ DrawPrinterLayout( rLayout, *m_pPrinterGfx, true );
+}
+
+const ImplFontCharMap* PspGraphics::GetImplFontCharMap() const
+{
+ if( !m_pServerFont[0] )
+ return NULL;
+
+ const ImplFontCharMap* pIFCMap = m_pServerFont[0]->GetImplFontCharMap();
+ return pIFCMap;
+}
+
+bool PspGraphics::GetImplFontCapabilities(vcl::FontCapabilities &rFontCapabilities) const
+{
+ if (!m_pServerFont[0])
+ return false;
+ return m_pServerFont[0]->GetFontCapabilities(rFontCapabilities);
+}
+
+sal_uInt16 PspGraphics::SetFont( ImplFontSelectData *pEntry, int nFallbackLevel )
+{
+ // release all fonts that are to be overridden
+ for( int i = nFallbackLevel; i < MAX_FALLBACK; ++i )
+ {
+ if( m_pServerFont[i] != NULL )
+ {
+ // old server side font is no longer referenced
+ GlyphCache::GetInstance().UncacheFont( *m_pServerFont[i] );
+ m_pServerFont[i] = NULL;
+ }
+ }
+
+ // return early if there is no new font
+ if( !pEntry )
+ return 0;
+
+ sal_IntPtr nID = pEntry->mpFontData ? pEntry->mpFontData->GetFontId() : 0;
+
+ // determine which font attributes need to be emulated
+ bool bArtItalic = false;
+ bool bArtBold = false;
+ if( pEntry->meItalic == ITALIC_OBLIQUE || pEntry->meItalic == ITALIC_NORMAL )
+ {
+ FontItalic eItalic = m_pPrinterGfx->GetFontMgr().getFontItalic( nID );
+ if( eItalic != ITALIC_NORMAL && eItalic != ITALIC_OBLIQUE )
+ bArtItalic = true;
+ }
+ int nWeight = (int)pEntry->meWeight;
+ int nRealWeight = (int)m_pPrinterGfx->GetFontMgr().getFontWeight( nID );
+ if( nRealWeight <= (int)WEIGHT_MEDIUM && nWeight > (int)WEIGHT_MEDIUM )
+ {
+ bArtBold = true;
+ }
+
+ // also set the serverside font for layouting
+ m_bFontVertical = pEntry->mbVertical;
+ if( pEntry->mpFontData )
+ {
+ // requesting a font provided by builtin rasterizer
+ ServerFont* pServerFont = GlyphCache::GetInstance().CacheFont( *pEntry );
+ if( pServerFont != NULL )
+ {
+ if( pServerFont->TestFont() )
+ m_pServerFont[ nFallbackLevel ] = pServerFont;
+ else
+ GlyphCache::GetInstance().UncacheFont( *pServerFont );
+ }
+ }
+
+ // set the printer font
+ return m_pPrinterGfx->SetFont( nID,
+ pEntry->mnHeight,
+ pEntry->mnWidth,
+ pEntry->mnOrientation,
+ pEntry->mbVertical,
+ bArtItalic,
+ bArtBold
+ );
+}
+
+void PspGraphics::SetTextColor( SalColor nSalColor )
+{
+ psp::PrinterColor aColor (SALCOLOR_RED (nSalColor),
+ SALCOLOR_GREEN (nSalColor),
+ SALCOLOR_BLUE (nSalColor));
+ m_pPrinterGfx->SetTextColor (aColor);
+}
+
+bool PspGraphics::AddTempDevFont( ImplDevFontList*, const String&,const String& )
+{
+ return false;
+}
+
+void PspGraphics::GetDevFontList( ImplDevFontList *pList )
+{
+ ::std::list< psp::fontID > aList;
+ psp::PrintFontManager& rMgr = psp::PrintFontManager::get();
+ rMgr.getFontList( aList, m_pJobData->m_pParser, m_pInfoPrinter->m_bCompatMetrics );
+
+ ::std::list< psp::fontID >::iterator it;
+ psp::FastPrintFontInfo aInfo;
+ for (it = aList.begin(); it != aList.end(); ++it)
+ if (rMgr.getFontFastInfo (*it, aInfo))
+ AnnounceFonts( pList, aInfo );
+
+ // register platform specific font substitutions if available
+ if( rMgr.hasFontconfig() )
+ GenericInstance::RegisterFontSubstitutors( pList );
+}
+
+void PspGraphics::GetDevFontSubstList( OutputDevice* pOutDev )
+{
+ const psp::PrinterInfo& rInfo = psp::PrinterInfoManager::get().getPrinterInfo( m_pJobData->m_aPrinterName );
+ if( rInfo.m_bPerformFontSubstitution )
+ {
+ for( boost::unordered_map< rtl::OUString, rtl::OUString, rtl::OUStringHash >::const_iterator it = rInfo.m_aFontSubstitutes.begin(); it != rInfo.m_aFontSubstitutes.end(); ++it )
+ pOutDev->ImplAddDevFontSubstitute( it->first, it->second, FONT_SUBSTITUTE_ALWAYS );
+ }
+}
+
+void PspGraphics::GetFontMetric( ImplFontMetricData *pMetric, int )
+{
+ const psp::PrintFontManager& rMgr = psp::PrintFontManager::get();
+ psp::PrintFontInfo aInfo;
+
+ if (rMgr.getFontInfo (m_pPrinterGfx->GetFontID(), aInfo))
+ {
+ ImplDevFontAttributes aDFA = Info2DevFontAttributes( aInfo );
+ static_cast<ImplFontAttributes&>(*pMetric) = aDFA;
+ pMetric->mbDevice = aDFA.mbDevice;
+ pMetric->mbScalableFont = true;
+
+ pMetric->mnOrientation = m_pPrinterGfx->GetFontAngle();
+ pMetric->mnSlant = 0;
+
+ sal_Int32 nTextHeight = m_pPrinterGfx->GetFontHeight();
+ sal_Int32 nTextWidth = m_pPrinterGfx->GetFontWidth();
+ if( ! nTextWidth )
+ nTextWidth = nTextHeight;
+
+ pMetric->mnWidth = nTextWidth;
+ pMetric->mnAscent = ( aInfo.m_nAscend * nTextHeight + 500 ) / 1000;
+ pMetric->mnDescent = ( aInfo.m_nDescend * nTextHeight + 500 ) / 1000;
+ pMetric->mnIntLeading = ( aInfo.m_nLeading * nTextHeight + 500 ) / 1000;
+ pMetric->mnExtLeading = 0;
+ }
+}
+
+sal_uLong PspGraphics::GetKernPairs( sal_uLong nPairs, ImplKernPairData *pKernPairs )
+{
+ const ::std::list< ::psp::KernPair >& rPairs( m_pPrinterGfx->getKernPairs() );
+ sal_uLong nHavePairs = rPairs.size();
+ if( pKernPairs && nPairs )
+ {
+ ::std::list< ::psp::KernPair >::const_iterator it;
+ unsigned int i;
+ int nTextScale = m_pPrinterGfx->GetFontWidth();
+ if( ! nTextScale )
+ nTextScale = m_pPrinterGfx->GetFontHeight();
+ for( i = 0, it = rPairs.begin(); i < nPairs && i < nHavePairs; i++, ++it )
+ {
+ pKernPairs[i].mnChar1 = it->first;
+ pKernPairs[i].mnChar2 = it->second;
+ pKernPairs[i].mnKern = it->kern_x * nTextScale / 1000;
+ }
+
+ }
+ return nHavePairs;
+}
+
+sal_Bool PspGraphics::GetGlyphBoundRect( sal_GlyphId nGlyphIndex, Rectangle& rRect )
+{
+ int nLevel = nGlyphIndex >> GF_FONTSHIFT;
+ if( nLevel >= MAX_FALLBACK )
+ return sal_False;
+
+ ServerFont* pSF = m_pServerFont[ nLevel ];
+ if( !pSF )
+ return sal_False;
+
+ nGlyphIndex &= GF_IDXMASK;
+ const GlyphMetric& rGM = pSF->GetGlyphMetric( nGlyphIndex );
+ rRect = Rectangle( rGM.GetOffset(), rGM.GetSize() );
+ return sal_True;
+}
+
+sal_Bool PspGraphics::GetGlyphOutline( sal_GlyphId nGlyphIndex,
+ ::basegfx::B2DPolyPolygon& rB2DPolyPoly )
+{
+ int nLevel = nGlyphIndex >> GF_FONTSHIFT;
+ if( nLevel >= MAX_FALLBACK )
+ return sal_False;
+
+ ServerFont* pSF = m_pServerFont[ nLevel ];
+ if( !pSF )
+ return sal_False;
+
+ nGlyphIndex &= GF_IDXMASK;
+ if( pSF->GetGlyphOutline( nGlyphIndex, rB2DPolyPoly ) )
+ return sal_True;
+
+ return sal_False;
+}
+
+SalLayout* PspGraphics::GetTextLayout( ImplLayoutArgs& rArgs, int nFallbackLevel )
+{
+ // workaround for printers not handling glyph indexing for non-TT fonts
+ int nFontId = m_pPrinterGfx->GetFontID();
+ if( psp::fonttype::TrueType != psp::PrintFontManager::get().getFontType( nFontId ) )
+ rArgs.mnFlags |= SAL_LAYOUT_DISABLE_GLYPH_PROCESSING;
+ else if( nFallbackLevel > 0 )
+ rArgs.mnFlags &= ~SAL_LAYOUT_DISABLE_GLYPH_PROCESSING;
+
+ GenericSalLayout* pLayout = NULL;
+
+ if( m_pServerFont[ nFallbackLevel ]
+ && !(rArgs.mnFlags & SAL_LAYOUT_DISABLE_GLYPH_PROCESSING) )
+ {
+#ifdef ENABLE_GRAPHITE
+ // Is this a Graphite font?
+ if (GraphiteServerFontLayout::IsGraphiteEnabledFont(*m_pServerFont[nFallbackLevel]))
+ {
+ pLayout = new GraphiteServerFontLayout(*m_pServerFont[nFallbackLevel]);
+ }
+ else
+#endif
+ pLayout = new PspServerFontLayout( *m_pPrinterGfx, *m_pServerFont[nFallbackLevel], rArgs );
+ }
+ else
+ pLayout = new PspFontLayout( *m_pPrinterGfx );
+
+ return pLayout;
+}
+
+//--------------------------------------------------------------------------
+
+sal_Bool PspGraphics::CreateFontSubset(
+ const rtl::OUString& rToFile,
+ const ImplFontData* pFont,
+ sal_Int32* pGlyphIDs,
+ sal_uInt8* pEncoding,
+ sal_Int32* pWidths,
+ int nGlyphCount,
+ FontSubsetInfo& rInfo
+ )
+{
+ // in this context the pFont->GetFontId() is a valid PSP
+ // font since they are the only ones left after the PDF
+ // export has filtered its list of subsettable fonts (for
+ // which this method was created). The correct way would
+ // be to have the GlyphCache search for the ImplFontData pFont
+ psp::fontID aFont = pFont->GetFontId();
+
+ psp::PrintFontManager& rMgr = psp::PrintFontManager::get();
+ bool bSuccess = rMgr.createFontSubset( rInfo,
+ aFont,
+ rToFile,
+ pGlyphIDs,
+ pEncoding,
+ pWidths,
+ nGlyphCount );
+ return bSuccess;
+}
+
+//--------------------------------------------------------------------------
+
+const void* PspGraphics::GetEmbedFontData( const ImplFontData* pFont, const sal_Ucs* pUnicodes, sal_Int32* pWidths, FontSubsetInfo& rInfo, long* pDataLen )
+{
+ // in this context the pFont->GetFontId() is a valid PSP
+ // font since they are the only ones left after the PDF
+ // export has filtered its list of subsettable fonts (for
+ // which this method was created). The correct way would
+ // be to have the GlyphCache search for the ImplFontData pFont
+ psp::fontID aFont = pFont->GetFontId();
+ return PspGraphics::DoGetEmbedFontData( aFont, pUnicodes, pWidths, rInfo, pDataLen );
+}
+
+//--------------------------------------------------------------------------
+
+void PspGraphics::FreeEmbedFontData( const void* pData, long nLen )
+{
+ PspGraphics::DoFreeEmbedFontData( pData, nLen );
+}
+
+//--------------------------------------------------------------------------
+
+const Ucs2SIntMap* PspGraphics::GetFontEncodingVector( const ImplFontData* pFont, const Ucs2OStrMap** pNonEncoded )
+{
+ // in this context the pFont->GetFontId() is a valid PSP
+ // font since they are the only ones left after the PDF
+ // export has filtered its list of subsettable fonts (for
+ // which this method was created). The correct way would
+ // be to have the GlyphCache search for the ImplFontData pFont
+ psp::fontID aFont = pFont->GetFontId();
+ return PspGraphics::DoGetFontEncodingVector( aFont, pNonEncoded );
+}
+
+//--------------------------------------------------------------------------
+
+void PspGraphics::GetGlyphWidths( const ImplFontData* pFont,
+ bool bVertical,
+ Int32Vector& rWidths,
+ Ucs2UIntMap& rUnicodeEnc )
+{
+ // in this context the pFont->GetFontId() is a valid PSP
+ // font since they are the only ones left after the PDF
+ // export has filtered its list of subsettable fonts (for
+ // which this method was created). The correct way would
+ // be to have the GlyphCache search for the ImplFontData pFont
+ psp::fontID aFont = pFont->GetFontId();
+ PspGraphics::DoGetGlyphWidths( aFont, bVertical, rWidths, rUnicodeEnc );
+}
+
+
+// static helpers of PspGraphics
+
+const void* PspGraphics::DoGetEmbedFontData( fontID aFont, const sal_Ucs* pUnicodes, sal_Int32* pWidths, FontSubsetInfo& rInfo, long* pDataLen )
+{
+ psp::PrintFontManager& rMgr = psp::PrintFontManager::get();
+
+ psp::PrintFontInfo aFontInfo;
+ if( ! rMgr.getFontInfo( aFont, aFontInfo ) )
+ return NULL;
+
+ // fill in font info
+ rInfo.m_nAscent = aFontInfo.m_nAscend;
+ rInfo.m_nDescent = aFontInfo.m_nDescend;
+ rInfo.m_aPSName = rMgr.getPSName( aFont );
+
+ int xMin, yMin, xMax, yMax;
+ rMgr.getFontBoundingBox( aFont, xMin, yMin, xMax, yMax );
+
+ psp::CharacterMetric aMetrics[256];
+ sal_Ucs aUnicodes[256];
+ if( aFontInfo.m_aEncoding == RTL_TEXTENCODING_SYMBOL && aFontInfo.m_eType == psp::fonttype::Type1 )
+ {
+ for( int i = 0; i < 256; i++ )
+ aUnicodes[i] = pUnicodes[i] < 0x0100 ? pUnicodes[i] + 0xf000 : pUnicodes[i];
+ pUnicodes = aUnicodes;
+ }
+ if( ! rMgr.getMetrics( aFont, pUnicodes, 256, aMetrics ) )
+ return NULL;
+
+ OString aSysPath = rMgr.getFontFileSysPath( aFont );
+ struct stat aStat;
+ if( stat( aSysPath.getStr(), &aStat ) )
+ return NULL;
+ int fd = open( aSysPath.getStr(), O_RDONLY );
+ if( fd < 0 )
+ return NULL;
+ void* pFile = mmap( NULL, aStat.st_size, PROT_READ, MAP_SHARED, fd, 0 );
+ close( fd );
+ if( pFile == MAP_FAILED )
+ return NULL;
+
+ *pDataLen = aStat.st_size;
+
+ rInfo.m_aFontBBox = Rectangle( Point( xMin, yMin ), Size( xMax-xMin, yMax-yMin ) );
+ rInfo.m_nCapHeight = yMax; // Well ...
+
+ for( int i = 0; i < 256; i++ )
+ pWidths[i] = (aMetrics[i].width > 0 ? aMetrics[i].width : 0);
+
+ switch( aFontInfo.m_eType )
+ {
+ case psp::fonttype::TrueType:
+ rInfo.m_nFontType = FontSubsetInfo::SFNT_TTF;
+ break;
+ case psp::fonttype::Type1: {
+ const bool bPFA = ((*(unsigned char*)pFile) < 0x80);
+ rInfo.m_nFontType = bPFA ? FontSubsetInfo::TYPE1_PFA : FontSubsetInfo::TYPE1_PFB;
+ }
+ break;
+ default:
+ return NULL;
+ }
+
+ return pFile;
+}
+
+void PspGraphics::DoFreeEmbedFontData( const void* pData, long nLen )
+{
+ if( pData )
+ munmap( (char*)pData, nLen );
+}
+
+const Ucs2SIntMap* PspGraphics::DoGetFontEncodingVector( fontID aFont, const Ucs2OStrMap** pNonEncoded )
+{
+ psp::PrintFontManager& rMgr = psp::PrintFontManager::get();
+
+ psp::PrintFontInfo aFontInfo;
+ if( ! rMgr.getFontInfo( aFont, aFontInfo ) )
+ {
+ if( pNonEncoded )
+ *pNonEncoded = NULL;
+ return NULL;
+ }
+
+ return rMgr.getEncodingMap( aFont, pNonEncoded );
+}
+
+void PspGraphics::DoGetGlyphWidths( psp::fontID aFont,
+ bool bVertical,
+ Int32Vector& rWidths,
+ Ucs2UIntMap& rUnicodeEnc )
+{
+ psp::PrintFontManager& rMgr = psp::PrintFontManager::get();
+ rMgr.getGlyphWidths( aFont, bVertical, rWidths, rUnicodeEnc );
+}
+// ----------------------------------------------------------------------------
+
+ImplDevFontAttributes PspGraphics::Info2DevFontAttributes( const psp::FastPrintFontInfo& rInfo )
+{
+ ImplDevFontAttributes aDFA;
+ aDFA.maName = rInfo.m_aFamilyName;
+ aDFA.maStyleName = rInfo.m_aStyleName;
+ aDFA.meFamily = rInfo.m_eFamilyStyle;
+ aDFA.meWeight = rInfo.m_eWeight;
+ aDFA.meItalic = rInfo.m_eItalic;
+ aDFA.meWidthType = rInfo.m_eWidth;
+ aDFA.mePitch = rInfo.m_ePitch;
+ aDFA.mbSymbolFlag = (rInfo.m_aEncoding == RTL_TEXTENCODING_SYMBOL);
+ aDFA.mbSubsettable = rInfo.m_bSubsettable;
+ aDFA.mbEmbeddable = rInfo.m_bEmbeddable;
+
+ switch( rInfo.m_eType )
+ {
+ case psp::fonttype::Builtin:
+ aDFA.mnQuality = 1024;
+ aDFA.mbDevice = true;
+ break;
+ case psp::fonttype::TrueType:
+ aDFA.mnQuality = 512;
+ aDFA.mbDevice = false;
+ break;
+ case psp::fonttype::Type1:
+ aDFA.mnQuality = 0;
+ aDFA.mbDevice = false;
+ break;
+ default:
+ aDFA.mnQuality = 0;
+ aDFA.mbDevice = false;
+ break;
+ }
+
+ aDFA.mbOrientation = true;
+
+ // add font family name aliases
+ ::std::list< OUString >::const_iterator it = rInfo.m_aAliases.begin();
+ bool bHasMapNames = false;
+ for(; it != rInfo.m_aAliases.end(); ++it )
+ {
+ if( bHasMapNames )
+ aDFA.maMapNames.Append( ';' );
+ aDFA.maMapNames.Append( (*it).getStr() );
+ bHasMapNames = true;
+ }
+
+#if OSL_DEBUG_LEVEL > 2
+ if( bHasMapNames )
+ {
+ ByteString aOrigName( aDFA.maName, osl_getThreadTextEncoding() );
+ ByteString aAliasNames( aDFA.maMapNames, osl_getThreadTextEncoding() );
+ fprintf( stderr, "using alias names \"%s\" for font family \"%s\"\n",
+ aAliasNames.GetBuffer(), aOrigName.GetBuffer() );
+ }
+#endif
+
+ return aDFA;
+}
+
+// -----------------------------------------------------------------------
+
+void PspGraphics::AnnounceFonts( ImplDevFontList* pFontList, const psp::FastPrintFontInfo& aInfo )
+{
+ int nQuality = 0;
+
+ if( aInfo.m_eType == psp::fonttype::TrueType )
+ {
+ // asian type 1 fonts are not known
+ psp::PrintFontManager& rMgr = psp::PrintFontManager::get();
+ ByteString aFileName( rMgr.getFontFileSysPath( aInfo.m_nID ) );
+ int nPos = aFileName.SearchBackward( '_' );
+ if( nPos == STRING_NOTFOUND || aFileName.GetChar( nPos+1 ) == '.' )
+ nQuality += 5;
+ else
+ {
+ static const char* pLangBoost = NULL;
+ static bool bOnce = true;
+ if( bOnce )
+ {
+ bOnce = false;
+ const LanguageType aLang = Application::GetSettings().GetUILanguage();
+ switch( aLang )
+ {
+ case LANGUAGE_JAPANESE:
+ pLangBoost = "jan";
+ break;
+ case LANGUAGE_CHINESE:
+ case LANGUAGE_CHINESE_SIMPLIFIED:
+ case LANGUAGE_CHINESE_SINGAPORE:
+ pLangBoost = "zhs";
+ break;
+ case LANGUAGE_CHINESE_TRADITIONAL:
+ case LANGUAGE_CHINESE_HONGKONG:
+ case LANGUAGE_CHINESE_MACAU:
+ pLangBoost = "zht";
+ break;
+ case LANGUAGE_KOREAN:
+ case LANGUAGE_KOREAN_JOHAB:
+ pLangBoost = "kor";
+ break;
+ }
+ }
+
+ if( pLangBoost )
+ if( aFileName.Copy( nPos+1, 3 ).EqualsIgnoreCaseAscii( pLangBoost ) )
+ nQuality += 10;
+ }
+ }
+
+ ImplPspFontData* pFD = new ImplPspFontData( aInfo );
+ pFD->mnQuality += nQuality;
+ pFontList->Add( pFD );
+}
+
+bool PspGraphics::filterText( const String& rOrig, String& rNewText, xub_StrLen nIndex, xub_StrLen& rLen, xub_StrLen& rCutStart, xub_StrLen& rCutStop )
+{
+ if( ! m_pPhoneNr )
+ return false;
+
+ rCutStop = rCutStart = STRING_NOTFOUND;
+
+#define FAX_PHONE_TOKEN "@@#"
+#define FAX_PHONE_TOKEN_LENGTH 3
+#define FAX_END_TOKEN "@@"
+#define FAX_END_TOKEN_LENGTH 2
+
+ bool bRet = false;
+ bool bStarted = false;
+ bool bStopped = false;
+ sal_uInt16 nPos;
+ sal_uInt16 nStart = 0;
+ sal_uInt16 nStop = rLen;
+ String aPhone = rOrig.Copy( nIndex, rLen );
+
+ if( ! m_bPhoneCollectionActive )
+ {
+ if( ( nPos = aPhone.SearchAscii( FAX_PHONE_TOKEN ) ) != STRING_NOTFOUND )
+ {
+ nStart = nPos;
+ m_bPhoneCollectionActive = true;
+ m_aPhoneCollection.Erase();
+ bRet = true;
+ bStarted = true;
+ }
+ }
+ if( m_bPhoneCollectionActive )
+ {
+ bRet = true;
+ nPos = bStarted ? nStart + FAX_PHONE_TOKEN_LENGTH : 0;
+ if( ( nPos = aPhone.SearchAscii( FAX_END_TOKEN, nPos ) ) != STRING_NOTFOUND )
+ {
+ m_bPhoneCollectionActive = false;
+ nStop = nPos + FAX_END_TOKEN_LENGTH;
+ bStopped = true;
+ }
+ int nTokenStart = nStart + (bStarted ? FAX_PHONE_TOKEN_LENGTH : 0);
+ int nTokenStop = nStop - (bStopped ? FAX_END_TOKEN_LENGTH : 0);
+ m_aPhoneCollection += aPhone.Copy( nTokenStart, nTokenStop - nTokenStart );
+ if( ! m_bPhoneCollectionActive )
+ {
+ m_pPhoneNr->AppendAscii( "<Fax#>" );
+ m_pPhoneNr->Append( m_aPhoneCollection );
+ m_pPhoneNr->AppendAscii( "</Fax#>" );
+ m_aPhoneCollection.Erase();
+ }
+ }
+ if( m_aPhoneCollection.Len() > 1024 )
+ {
+ m_bPhoneCollectionActive = false;
+ m_aPhoneCollection.Erase();
+ bRet = false;
+ }
+
+ if( bRet && m_bSwallowFaxNo )
+ {
+ rLen -= nStop - nStart;
+ rCutStart = nStart+nIndex;
+ rCutStop = nStop+nIndex;
+ if( rCutStart )
+ rNewText = rOrig.Copy( 0, rCutStart );
+ rNewText += rOrig.Copy( rCutStop );
+ }
+
+ return bRet && m_bSwallowFaxNo;
+}
+
+bool PspGraphics::drawAlphaBitmap( const SalTwoRect&,
+ const SalBitmap&,
+ const SalBitmap& )
+{
+ return false;
+}
+
+bool PspGraphics::drawAlphaRect( long, long, long, long, sal_uInt8 )
+{
+ return false;
+}
+
+SystemGraphicsData PspGraphics::GetGraphicsData() const
+{
+ SystemGraphicsData aRes;
+ aRes.nSize = sizeof(aRes);
+ aRes.hDrawable = 0;
+ aRes.pXRenderFormat = 0;
+ return aRes;
+}
+
+SystemFontData PspGraphics::GetSysFontData( int nFallbacklevel ) const
+{
+ SystemFontData aSysFontData;
+
+ if (nFallbacklevel >= MAX_FALLBACK) nFallbacklevel = MAX_FALLBACK - 1;
+ if (nFallbacklevel < 0 ) nFallbacklevel = 0;
+
+ aSysFontData.nSize = sizeof( SystemFontData );
+ aSysFontData.nFontId = 0;
+ aSysFontData.nFontFlags = 0;
+ aSysFontData.bFakeBold = false;
+ aSysFontData.bFakeItalic = false;
+ aSysFontData.bAntialias = true;
+ return aSysFontData;
+}
+
+bool PspGraphics::supportsOperation( OutDevSupportType ) const
+{
+ return false;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/vcl/generic/print/psputil.cxx b/vcl/generic/print/psputil.cxx
new file mode 100644
index 000000000000..2664bac8d022
--- /dev/null
+++ b/vcl/generic/print/psputil.cxx
@@ -0,0 +1,271 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*************************************************************************
+ *
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * Copyright 2000, 2010 Oracle and/or its affiliates.
+ *
+ * OpenOffice.org - a multi-platform office productivity suite
+ *
+ * This file is part of OpenOffice.org.
+ *
+ * OpenOffice.org is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License version 3
+ * only, as published by the Free Software Foundation.
+ *
+ * OpenOffice.org is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License version 3 for more details
+ * (a copy is included in the LICENSE file that accompanied this code).
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * version 3 along with OpenOffice.org. If not, see
+ * <http://www.openoffice.org/license.html>
+ * for a copy of the LGPLv3 License.
+ *
+ ************************************************************************/
+
+// MARKER(update_precomp.py): autogen include statement, do not remove
+#include "precompiled_vcl.hxx"
+#include <string.h>
+#include <tools/debug.hxx>
+#include <rtl/instance.hxx>
+#include "psputil.hxx"
+
+namespace psp {
+
+/*
+ * string convenience routines
+ */
+
+sal_Int32
+getHexValueOf (sal_Int32 nValue, sal_Char* pBuffer)
+{
+ const static sal_Char pHex [0x10] = {
+ '0', '1', '2', '3', '4', '5', '6', '7',
+ '8', '9', 'A', 'B', 'C', 'D', 'E', 'F' };
+
+ pBuffer[0] = pHex [(nValue & 0xF0) >> 4];
+ pBuffer[1] = pHex [(nValue & 0x0F) ];
+
+ return 2;
+}
+
+sal_Int32
+getAlignedHexValueOf (sal_Int32 nValue, sal_Char* pBuffer)
+{
+ // get sign
+ sal_Bool bNegative = nValue < 0;
+ nValue = bNegative ? -nValue : nValue;
+
+ // get required buffer size, must be a multiple of two
+ sal_Int32 nPrecision;
+ if (nValue < 0x80)
+ nPrecision = 2;
+ else
+ if (nValue < 0x8000)
+ nPrecision = 4;
+ else
+ if (nValue < 0x800000)
+ nPrecision = 6;
+ else
+ nPrecision = 8;
+
+ // convert the int into its hex representation, write it into the buffer
+ sal_Int32 nRet = nPrecision;
+ while (nPrecision)
+ {
+ nPrecision -= getHexValueOf (nValue % 256, pBuffer + nPrecision - 2 );
+ nValue /= 256;
+ }
+
+ // set sign bit
+ if (bNegative)
+ {
+ switch (pBuffer[0])
+ {
+ case '0' : pBuffer[0] = '8'; break;
+ case '1' : pBuffer[0] = '9'; break;
+ case '2' : pBuffer[0] = 'A'; break;
+ case '3' : pBuffer[0] = 'B'; break;
+ case '4' : pBuffer[0] = 'C'; break;
+ case '5' : pBuffer[0] = 'D'; break;
+ case '6' : pBuffer[0] = 'E'; break;
+ case '7' : pBuffer[0] = 'F'; break;
+ default: OSL_FAIL("Already a signed value");
+ }
+ }
+
+ // report precision
+ return nRet;
+}
+
+
+sal_Int32
+getValueOf (sal_Int32 nValue, sal_Char* pBuffer)
+{
+ sal_Int32 nChar = 0;
+ if (nValue < 0)
+ {
+ pBuffer [nChar++] = '-';
+ nValue *= -1;
+ }
+ else
+ if (nValue == 0)
+ {
+ pBuffer [nChar++] = '0';
+ return nChar;
+ }
+
+ sal_Char pInvBuffer [32];
+ sal_Int32 nInvChar = 0;
+ while (nValue > 0)
+ {
+ pInvBuffer [nInvChar++] = '0' + nValue % 10;
+ nValue /= 10;
+ }
+ while (nInvChar > 0)
+ {
+ pBuffer [nChar++] = pInvBuffer [--nInvChar];
+ }
+
+ return nChar;
+}
+
+sal_Int32
+appendStr (const sal_Char* pSrc, sal_Char* pDst)
+{
+ sal_Int32 nBytes = strlen (pSrc);
+ strncpy (pDst, pSrc, nBytes + 1);
+
+ return nBytes;
+}
+
+sal_Int32
+appendStr (const sal_Char* pSrc, sal_Char* pDst, sal_Int32 nBytes)
+{
+ strncpy (pDst, pSrc, nBytes);
+ pDst [nBytes] = '\0';
+ return nBytes;
+}
+
+/*
+ * copy strings to file
+ */
+
+sal_Bool
+WritePS (osl::File* pFile, const sal_Char* pString)
+{
+ sal_uInt64 nInLength = rtl_str_getLength (pString);
+ sal_uInt64 nOutLength = 0;
+
+ if (nInLength > 0 && pFile)
+ pFile->write (pString, nInLength, nOutLength);
+
+ return nInLength == nOutLength;
+}
+
+sal_Bool
+WritePS (osl::File* pFile, const sal_Char* pString, sal_uInt64 nInLength)
+{
+ sal_uInt64 nOutLength = 0;
+
+ if (nInLength > 0 && pFile)
+ pFile->write (pString, nInLength, nOutLength);
+
+ return nInLength == nOutLength;
+}
+
+sal_Bool
+WritePS (osl::File* pFile, const rtl::OString &rString)
+{
+ sal_uInt64 nInLength = rString.getLength();
+ sal_uInt64 nOutLength = 0;
+
+ if (nInLength > 0 && pFile)
+ pFile->write (rString.getStr(), nInLength, nOutLength);
+
+ return nInLength == nOutLength;
+}
+
+sal_Bool
+WritePS (osl::File* pFile, const rtl::OUString &rString)
+{
+ return WritePS (pFile, rtl::OUStringToOString(rString, RTL_TEXTENCODING_ASCII_US));
+}
+
+/*
+ * cache converter for use in postscript drawing routines
+ */
+
+ConverterFactory::ConverterFactory()
+{
+}
+
+ConverterFactory::~ConverterFactory ()
+{
+ for( std::map< rtl_TextEncoding, rtl_UnicodeToTextConverter >::const_iterator it = m_aConverters.begin(); it != m_aConverters.end(); ++it )
+ rtl_destroyUnicodeToTextConverter (it->second);
+}
+
+rtl_UnicodeToTextConverter
+ConverterFactory::Get (rtl_TextEncoding nEncoding)
+{
+ if (rtl_isOctetTextEncoding( nEncoding ))
+ {
+ std::map< rtl_TextEncoding, rtl_UnicodeToTextConverter >::const_iterator it =
+ m_aConverters.find( nEncoding );
+ rtl_UnicodeToTextConverter aConverter;
+ if (it == m_aConverters.end())
+ {
+ aConverter = rtl_createUnicodeToTextConverter (nEncoding);
+ m_aConverters[nEncoding] = aConverter;
+ }
+ else
+ aConverter = it->second;
+ return aConverter;
+ }
+ return NULL;
+}
+
+// wrapper for rtl_convertUnicodeToText that handles the usual cases for
+// textconversion in drawtext
+sal_Size
+ConverterFactory::Convert (const sal_Unicode *pText, int nTextLen,
+ sal_uChar *pBuffer, sal_Size nBufferSize, rtl_TextEncoding nEncoding)
+{
+ const sal_uInt32 nCvtFlags = RTL_UNICODETOTEXT_FLAGS_UNDEFINED_QUESTIONMARK
+ | RTL_UNICODETOTEXT_FLAGS_INVALID_QUESTIONMARK ;
+ sal_uInt32 nCvtInfo;
+ sal_Size nCvtChars;
+
+ rtl_UnicodeToTextConverter aConverter = Get (nEncoding);
+ rtl_UnicodeToTextContext aContext = rtl_createUnicodeToTextContext (aConverter);
+
+ sal_Size nSize = rtl_convertUnicodeToText (aConverter, aContext,
+ pText, nTextLen, (sal_Char*)pBuffer, nBufferSize,
+ nCvtFlags, &nCvtInfo, &nCvtChars);
+
+ rtl_destroyUnicodeToTextContext (aConverter, aContext);
+
+ return nSize;
+}
+
+namespace
+{
+ class theConverterFactory
+ : public rtl::Static<ConverterFactory, theConverterFactory>
+ {
+ };
+}
+
+ConverterFactory& GetConverterFactory()
+{
+ return theConverterFactory::get();
+}
+
+
+} /* namespace psp */
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/vcl/generic/print/psputil.hxx b/vcl/generic/print/psputil.hxx
new file mode 100644
index 000000000000..2aaffda6519f
--- /dev/null
+++ b/vcl/generic/print/psputil.hxx
@@ -0,0 +1,80 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*************************************************************************
+ *
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * Copyright 2000, 2010 Oracle and/or its affiliates.
+ *
+ * OpenOffice.org - a multi-platform office productivity suite
+ *
+ * This file is part of OpenOffice.org.
+ *
+ * OpenOffice.org is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License version 3
+ * only, as published by the Free Software Foundation.
+ *
+ * OpenOffice.org is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License version 3 for more details
+ * (a copy is included in the LICENSE file that accompanied this code).
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * version 3 along with OpenOffice.org. If not, see
+ * <http://www.openoffice.org/license.html>
+ * for a copy of the LGPLv3 License.
+ *
+ ************************************************************************/
+
+#ifndef _PSPRINT_PRINTERUTIL_HXX_
+#define _PSPRINT_PRINTERUTIL_HXX_
+
+#include "osl/file.hxx"
+
+#include "rtl/ustring.hxx"
+#include "rtl/string.hxx"
+#include "rtl/tencinfo.h"
+#include "rtl/textcvt.h"
+
+#include <map>
+
+namespace psp {
+
+/*
+ * string convenience routines
+ * sizeof(pBuffer) must be at least 2 Bytes, 0x00 <= nValue <= 0xFF,
+ * effective buffer of get*ValueOf() is NOT NULL-terminated
+ */
+sal_Int32 getHexValueOf (sal_Int32 nValue, sal_Char* pBuffer);
+sal_Int32 getAlignedHexValueOf (sal_Int32 nValue, sal_Char* pBuffer);
+sal_Int32 getValueOf (sal_Int32 nValue, sal_Char* pBuffer);
+sal_Int32 appendStr (const sal_Char* pSrc, sal_Char* pDst);
+sal_Int32 appendStr (const sal_Char* pSrc, sal_Char* pDst, sal_Int32 nBytes);
+
+sal_Bool WritePS (osl::File* pFile, const sal_Char* pString);
+sal_Bool WritePS (osl::File* pFile, const sal_Char* pString, sal_uInt64 nInLength);
+sal_Bool WritePS (osl::File* pFile, const rtl::OString &rString);
+sal_Bool WritePS (osl::File* pFile, const rtl::OUString &rString);
+
+class ConverterFactory
+{
+
+public:
+ ConverterFactory();
+ ~ConverterFactory();
+ rtl_UnicodeToTextConverter Get (rtl_TextEncoding nEncoding);
+ sal_Size Convert (const sal_Unicode *pText, int nTextLen,
+ sal_uChar *pBuffer, sal_Size nBufferSize,
+ rtl_TextEncoding nEncoding);
+private:
+
+ std::map< rtl_TextEncoding, rtl_UnicodeToTextConverter > m_aConverters;
+};
+
+ConverterFactory& GetConverterFactory ();
+
+} /* namespace psp */
+
+#endif /* _PSPRINT_PRINTERUTIL_HXX_ */
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/vcl/generic/print/text_gfx.cxx b/vcl/generic/print/text_gfx.cxx
new file mode 100644
index 000000000000..ca79b86564b3
--- /dev/null
+++ b/vcl/generic/print/text_gfx.cxx
@@ -0,0 +1,869 @@
+/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*************************************************************************
+ *
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * Copyright 2000, 2010 Oracle and/or its affiliates.
+ *
+ * OpenOffice.org - a multi-platform office productivity suite
+ *
+ * This file is part of OpenOffice.org.
+ *
+ * OpenOffice.org is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU Lesser General Public License version 3
+ * only, as published by the Free Software Foundation.
+ *
+ * OpenOffice.org is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License version 3 for more details
+ * (a copy is included in the LICENSE file that accompanied this code).
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * version 3 along with OpenOffice.org. If not, see
+ * <http://www.openoffice.org/license.html>
+ * for a copy of the LGPLv3 License.
+ *
+ ************************************************************************/
+
+// MARKER(update_precomp.py): autogen include statement, do not remove
+#include "precompiled_vcl.hxx"
+
+#include <math.h>
+
+#include "psputil.hxx"
+#include "glyphset.hxx"
+
+#include "generic/printergfx.hxx"
+#include "vcl/fontmanager.hxx"
+#include "vcl/helper.hxx"
+
+#include "osl/thread.h"
+
+#include "sal/alloca.h"
+
+using namespace psp ;
+
+namespace psp {
+/*
+ container for a font and its helper fonts:
+ 1st font is the font substitute e.g. helvetica substitutes arial on the printer
+ 2nd is the font itself
+ 3rd is a fallback font, usually a font with unicode glyph repertoir (e.g. andale)
+ symbol fonts (adobe-fontspecific) may need special glyphmapping
+ (symbol page vc. latin page)
+*/
+class Font3
+{
+ private:
+
+ #define Font3Size 3
+
+ fontID mpFont [Font3Size];
+ bool mbSymbol;
+
+ public:
+
+ fontID GetFont (int nIdx) const
+ { return nIdx < Font3Size ? mpFont[nIdx] : -1 ; }
+ bool IsSymbolFont () const
+ { return mbSymbol; }
+
+ Font3 (const PrinterGfx &rGfx);
+ ~Font3 () {}
+};
+
+Font3::Font3(const PrinterGfx &rGfx)
+{
+ mpFont[0] = rGfx.getFontSubstitute();
+ mpFont[1] = rGfx.GetFontID();
+ mpFont[2] = rGfx.getFallbackID();
+ // mpFont[2] = rGfx.GetFontID();
+
+ PrintFontManager &rMgr = PrintFontManager::get();
+ mbSymbol = mpFont[1] != -1 ?
+ rMgr.getFontEncoding(mpFont[1]) == RTL_TEXTENCODING_SYMBOL : false;
+}
+
+} // namespace psp
+
+static int getVerticalDeltaAngle( sal_Unicode nChar )
+{
+ int nAngle = 0;
+ if( ( nChar >= 0x1100 && nChar < 0x11fa ) ||
+ ( nChar >= 0x3000 && nChar < 0xfb00 ) ||
+ ( nChar >= 0xfe20 && nChar < 0xfe70 ) ||
+ ( nChar >= 0xff00 && nChar < 0xff64 )
+ )
+ {
+ /* #i52932# remember:
+ nChar == 0x2010 || nChar == 0x2015
+ nChar == 0x2016 || nChar == 0x2026
+
+ are nAngle = 0 also, but already handled in the first if
+ */
+ if( ( nChar >= 0x3008 && nChar < 0x3019 && nChar != 0x3012 ) ||
+ nChar == 0xff3b || nChar == 0xff3d ||
+ (nChar >= 0xff6b && nChar < 0xff64 ) ||
+ nChar == 0xffe3
+ )
+ nAngle = 0;
+ else if( nChar == 0x30fc )
+ nAngle = -900;
+ else
+ nAngle = 900;
+ }
+ return nAngle;
+}
+
+void
+PrinterGfx::PSUploadPS1Font (sal_Int32 nFontID)
+{
+ std::list< sal_Int32 >::iterator aFont;
+ // already in the document header ?
+ for (aFont = maPS1Font.begin(); aFont != maPS1Font.end(); ++aFont )
+ if( nFontID == *aFont )
+ return;
+
+ // no occurrenc yet, mark for download
+ // add the fontid to the list
+ maPS1Font.push_back (nFontID);
+}
+
+/*
+ * implement text handling printer routines,
+ */
+
+sal_uInt16
+PrinterGfx::SetFont(
+ sal_Int32 nFontID,
+ sal_Int32 nHeight,
+ sal_Int32 nWidth,
+ sal_Int32 nAngle,
+ bool bVertical,
+ bool bArtItalic,
+ bool bArtBold
+ )
+{
+ // font and encoding will be set by drawText again immediately
+ // before PSShowText
+ mnFontID = nFontID;
+ maVirtualStatus.maFont = rtl::OString();
+ maVirtualStatus.maEncoding = RTL_TEXTENCODING_DONTKNOW;
+ maVirtualStatus.mnTextHeight = nHeight;
+ maVirtualStatus.mnTextWidth = nWidth;
+ maVirtualStatus.mbArtItalic = bArtItalic;
+ maVirtualStatus.mbArtBold = bArtBold;
+ mnTextAngle = nAngle;
+ mbTextVertical = bVertical;
+
+ return 0;
+}
+
+sal_uInt16
+PrinterGfx::SetFallbackFont ( sal_Int32 nFontID )
+{
+ mnFallbackID = nFontID;
+ return 0;
+}
+
+void PrinterGfx::drawGlyphs(
+ const Point& rPoint,
+ sal_uInt32* pGlyphIds,
+ sal_Unicode* pUnicodes,
+ sal_Int16 nLen,
+ sal_Int32* pDeltaArray
+ )
+{
+
+ // draw the string
+ // search for a glyph set matching the set font
+ std::list< GlyphSet >::iterator aIter;
+ for (aIter = maPS3Font.begin(); aIter != maPS3Font.end(); ++aIter)
+ if ( ((*aIter).GetFontID() == mnFontID)
+ && ((*aIter).IsVertical() == mbTextVertical))
+ {
+ (*aIter).DrawGlyphs (*this, rPoint, pGlyphIds, pUnicodes, nLen, pDeltaArray);
+ break;
+ }
+
+ // not found ? create a new one
+ if (aIter == maPS3Font.end())
+ {
+ maPS3Font.push_back (GlyphSet(mnFontID, mbTextVertical));
+ maPS3Font.back().DrawGlyphs (*this, rPoint, pGlyphIds, pUnicodes, nLen, pDeltaArray);
+ }
+}
+
+void PrinterGfx::DrawGlyphs(
+ const Point& rPoint,
+ sal_GlyphId* pGlyphIds,
+ sal_Unicode* pUnicodes,
+ sal_Int16 nLen,
+ sal_Int32* pDeltaArray
+ )
+{
+ if( nLen <= 0 )
+ return;
+
+ if ( !mrFontMgr.isFontDownloadingAllowed( mnFontID ) )
+ {
+ LicenseWarning(rPoint, pUnicodes, nLen, pDeltaArray);
+ return;
+ }
+
+ if( mrFontMgr.getFontType( mnFontID ) != fonttype::TrueType )
+ {
+ DrawText( rPoint, pUnicodes, nLen, pDeltaArray );
+ return;
+ }
+
+ // move and rotate the user coordinate system
+ // avoid the gsave/grestore for the simple cases since it allows
+ // reuse of the current font if it hasn't changed
+ sal_Int32 nCurrentTextAngle = mnTextAngle;
+ Point aPoint( rPoint );
+
+ if (nCurrentTextAngle != 0)
+ {
+ PSGSave ();
+ PSTranslate (rPoint);
+ PSRotate (nCurrentTextAngle);
+ mnTextAngle = 0;
+ aPoint = Point( 0, 0 );
+ }
+
+ if( mbTextVertical )
+ {
+ // vertical glyphs can have an additional rotation ... sigh.
+ // so break up text in chunks of normal glyphs and print out
+ // specially rotated glyphs extra
+ sal_uInt32* pTempGlyphIds = (sal_uInt32*)alloca(sizeof(sal_Int32)*nLen);
+ sal_Int32* pTempDelta = (sal_Int32*)alloca(sizeof(sal_Int32)*nLen);
+ sal_Unicode* pTempUnicodes = (sal_Unicode*)alloca(sizeof(sal_Unicode)*nLen);
+ sal_Int16 nTempLen = 0;
+ sal_Int32 nTempFirstDelta = 0;
+ Point aRotPoint;
+ sal_Int32 nTextHeight = maVirtualStatus.mnTextHeight;
+ sal_Int32 nTextWidth = maVirtualStatus.mnTextWidth ? maVirtualStatus.mnTextWidth : maVirtualStatus.mnTextHeight;
+ sal_Int32 nAscend = mrFontMgr.getFontAscend( mnFontID );
+ sal_Int32 nDescend = mrFontMgr.getFontDescend( mnFontID );
+
+ nDescend = nDescend * nTextHeight / 1000;
+ nAscend = nAscend * nTextHeight / 1000;
+
+ for( sal_Int16 i = 0; i < nLen; i++ )
+ {
+ const sal_GlyphId nRot = pGlyphIds[i] & GF_ROTMASK;
+ if( nRot == GF_NONE )
+ {
+ pTempUnicodes[nTempLen] = pUnicodes[i];
+ pTempGlyphIds[nTempLen] = pGlyphIds[i];
+ if( nTempLen > 0 )
+ pTempDelta[nTempLen-1] = pDeltaArray[i-1]-nTempFirstDelta;
+ else
+ {
+ // the first element in pDeltaArray shows
+ // the offset of the second character
+ // so if the first glyph is normal
+ // then we do not need to move the delta indices
+ // else we have to move them down by one and
+ // recalculate aPoint and all deltas
+ if( i != 0 )
+ nTempFirstDelta = pDeltaArray[ i-1 ];
+ }
+ nTempLen++;
+ }
+ else
+ {
+ sal_Int32 nOffset = i > 0 ? pDeltaArray[i-1] : 0;
+ sal_Int32 nRotAngle = 0;
+ switch( nRot )
+ {
+ case GF_ROTR:
+ nRotAngle = 2700;
+ aRotPoint = Point( -nAscend*nTextWidth/nTextHeight, -nDescend*nTextWidth/nTextHeight - nOffset );
+ break;
+ case GF_VERT:
+ nRotAngle = 1800;
+ aRotPoint = Point( -nOffset, (nAscend+nDescend) );
+ break;
+ case GF_ROTL:
+ nRotAngle = 900;
+ aRotPoint = Point( -nDescend*nTextWidth/nTextHeight, nOffset + nAscend*nTextWidth/nTextHeight );
+ break;
+ }
+ sal_GlyphId nRotGlyphId = pGlyphIds[i];
+ sal_Unicode nRotUnicode = pUnicodes[i];
+ sal_Int32 nRotDelta = 0;
+
+ // transform matrix to new individual direction
+ PSGSave ();
+ GraphicsStatus aSaveStatus = maVirtualStatus;
+ if( nRot != 2 ) // switch font aspect
+ {
+ maVirtualStatus.mnTextWidth = nTextHeight;
+ maVirtualStatus.mnTextHeight = nTextWidth;
+ }
+ if( aPoint.X() || aPoint.Y() )
+ PSTranslate( aPoint );
+ PSRotate (nRotAngle);
+ // draw the rotated glyph
+ drawGlyphs( aRotPoint, &nRotGlyphId, &nRotUnicode, 1, &nRotDelta );
+
+ // restore previous state
+ maVirtualStatus = aSaveStatus;
+ PSGRestore();
+ }
+ }
+
+ pGlyphIds = pTempGlyphIds;
+ pUnicodes = pTempUnicodes;
+ pDeltaArray = pTempDelta;
+ nLen = nTempLen;
+
+ aPoint.X() += nTempFirstDelta;
+ }
+
+ if( nLen > 0 )
+ drawGlyphs( aPoint, pGlyphIds, pUnicodes, nLen, pDeltaArray );
+
+ // restore the user coordinate system
+ if (nCurrentTextAngle != 0)
+ {
+ PSGRestore ();
+ mnTextAngle = nCurrentTextAngle;
+ }
+}
+
+void
+PrinterGfx::DrawText (
+ const Point& rPoint,
+ const sal_Unicode* pStr,
+ sal_Int16 nLen,
+ const sal_Int32* pDeltaArray
+ )
+{
+ fontID nRestoreFont = mnFontID;
+
+ // setup font[substitutes] and map the string into the symbol area in case of
+ // symbol font
+ Font3 aFont(*this);
+ sal_Unicode *pEffectiveStr;
+ if ( aFont.IsSymbolFont() )
+ {
+ pEffectiveStr = (sal_Unicode*)alloca(nLen * sizeof(pStr[0]));
+ for (int i = 0; i < nLen; i++)
+ pEffectiveStr[i] = pStr[i] < 256 ? pStr[i] + 0xF000 : pStr[i];
+ }
+ else
+ {
+ pEffectiveStr = const_cast<sal_Unicode*>(pStr);
+ }
+
+ fontID *pFontMap = (fontID*) alloca(nLen * sizeof(fontID));
+ sal_Int32 *pCharWidth = (sal_Int32*) alloca(nLen * sizeof(sal_Int32));
+
+ for( int n = 0; n < nLen; n++ )
+ {
+ CharacterMetric aBBox;
+ pFontMap[n] = getCharMetric (aFont, pEffectiveStr[n], &aBBox);
+ pCharWidth[n] = getCharWidth (mbTextVertical, pEffectiveStr[n], &aBBox);
+ }
+
+ // setup a new delta array, use virtual resolution of 1000
+ sal_Int32* pNewDeltaArray = (sal_Int32*)alloca( sizeof( sal_Int32 )*nLen );
+ if ( pDeltaArray != 0)
+ {
+ for (int i = 0; i < nLen - 1; i++)
+ pNewDeltaArray[i] = 1000 * pDeltaArray[i];
+ pNewDeltaArray[nLen - 1] = 0;
+ }
+ else
+ {
+ pNewDeltaArray[0] = pCharWidth[0];
+ for (int i = 1; i < nLen; i++)
+ pNewDeltaArray[i] = pNewDeltaArray[i-1] + pCharWidth[i];
+ }
+
+ // move and rotate the user coordinate system
+ // avoid the gsave/grestore for the simple cases since it allows
+ // reuse of the current font if it hasn't changed
+ sal_Int32 nCurrentTextAngle = mnTextAngle;
+ sal_Int32 nCurrentPointX;
+ sal_Int32 nCurrentPointY;
+
+ if (nCurrentTextAngle != 0)
+ {
+ PSGSave ();
+ PSTranslate (rPoint);
+ PSRotate (nCurrentTextAngle);
+ mnTextAngle = 0;
+
+ nCurrentPointX = 0;
+ nCurrentPointY = 0;
+ }
+ else
+ {
+ nCurrentPointX = rPoint.X();
+ nCurrentPointY = rPoint.Y();
+ }
+
+ // draw the string
+ sal_Int32 nDelta = 0;
+ for (int nTo = 0; nTo < nLen; )
+ {
+ int nFrom = nTo;
+ fontID nFont = pFontMap[ nFrom ];
+
+ while ((nTo < nLen) && (nFont == pFontMap[nTo]))
+ {
+ pNewDeltaArray[ nTo ] = (sal_Int32)(((0.5 + pNewDeltaArray[ nTo ]) / 1000.0) - nDelta);
+ nTo++ ;
+ }
+
+ SetFont( nFont,
+ maVirtualStatus.mnTextHeight, maVirtualStatus.mnTextWidth,
+ mnTextAngle,
+ mbTextVertical,
+ maVirtualStatus.mbArtItalic,
+ maVirtualStatus.mbArtBold
+ );
+
+ if (mbTextVertical)
+ {
+ drawVerticalizedText(
+ Point(nCurrentPointX + nDelta, nCurrentPointY),
+ pEffectiveStr + nFrom, nTo - nFrom,
+ pNewDeltaArray + nFrom );
+ }
+ else
+ {
+ drawText(
+ Point(nCurrentPointX + nDelta, nCurrentPointY),
+ pEffectiveStr + nFrom, nTo - nFrom,
+ pDeltaArray == NULL ? NULL : pNewDeltaArray + nFrom );
+ }
+ nDelta += pNewDeltaArray[ nTo - 1 ];
+ }
+
+ // restore the user coordinate system
+ if (nCurrentTextAngle != 0)
+ {
+ PSGRestore ();
+ mnTextAngle = nCurrentTextAngle;
+ }
+
+ // restore the original font settings
+ SetFont( nRestoreFont,
+ maVirtualStatus.mnTextHeight, maVirtualStatus.mnTextWidth,
+ mnTextAngle, mbTextVertical,
+ maVirtualStatus.mbArtItalic,
+ maVirtualStatus.mbArtBold
+ );
+}
+
+void PrinterGfx::drawVerticalizedText(
+ const Point& rPoint,
+ const sal_Unicode* pStr,
+ sal_Int16 nLen,
+ const sal_Int32* pDeltaArray
+ )
+{
+ sal_Int32* pDelta = (sal_Int32*)alloca( nLen * sizeof(sal_Int32) );
+
+ int nTextScale = maVirtualStatus.mnTextWidth ? maVirtualStatus.mnTextWidth : maVirtualStatus.mnTextHeight;
+ int nNormalAngle = mnTextAngle;
+ int nDeltaAngle, nLastPos = 0;
+
+ double fSin = sin( -2.0*M_PI*nNormalAngle/3600 );
+ double fCos = cos( -2.0*M_PI*nNormalAngle/3600 );
+
+ PrintFontManager &rMgr = PrintFontManager::get();
+ PrintFontInfo aInfo;
+ rMgr.getFontInfo( mnFontID, aInfo );
+
+ bool* pGsubFlags = (bool*)alloca( nLen * sizeof(bool) );
+ rMgr.hasVerticalSubstitutions( mnFontID, pStr, nLen, pGsubFlags );
+
+ Point aPoint( rPoint );
+ for( int i = 0; i < nLen; )
+ {
+ while( ( nDeltaAngle = getVerticalDeltaAngle( pStr[i] ) ) == 0 && i < nLen )
+ i++;
+ if( i <= nLen && i > nLastPos )
+ {
+ for( int n = nLastPos; n < i; n++ )
+ pDelta[n] = pDeltaArray[n] - (aPoint.X() - rPoint.X() );
+
+ SetFont( mnFontID,
+ maVirtualStatus.mnTextHeight, maVirtualStatus.mnTextWidth,
+ nNormalAngle, mbTextVertical,
+ maVirtualStatus.mbArtItalic,
+ maVirtualStatus.mbArtBold );
+ drawText( aPoint, pStr + nLastPos, i - nLastPos, pDelta + nLastPos );
+
+ aPoint.X() = (sal_Int32)(rPoint.X() + ((double)pDeltaArray[i-1] * fCos));
+ aPoint.Y() = (sal_Int32)(rPoint.Y() + ((double)pDeltaArray[i-1] * fSin));
+ }
+ if( i < nLen )
+ {
+ int nOldWidth = maVirtualStatus.mnTextWidth;
+ int nOldHeight = maVirtualStatus.mnTextHeight;
+ SetFont( mnFontID,
+ nTextScale,
+ maVirtualStatus.mnTextHeight,
+ nNormalAngle + nDeltaAngle,
+ mbTextVertical,
+ maVirtualStatus.mbArtItalic,
+ maVirtualStatus.mbArtBold );
+
+ double nA = nTextScale * aInfo.m_nAscend / 1000.0;
+ double nD = nTextScale * aInfo.m_nDescend / 1000.0;
+ double fStretch = (double)maVirtualStatus.mnTextWidth / maVirtualStatus.mnTextHeight;
+ if( !pGsubFlags[i] )
+ nD *= fStretch;
+
+ Point aPos( aPoint );
+ switch( nDeltaAngle )
+ {
+ case +900:
+ aPos.X() += (sal_Int32)(+nA * fCos + nD * fSin);
+ aPos.Y() += (sal_Int32)(-nA * fSin + nD * fCos);
+ break;
+ case -900:
+ aPos.X() += (sal_Int32)(+nA * fSin + nD * fCos);
+ aPos.Y() += (sal_Int32)(-(nTextScale*fStretch - nD) * fCos);
+ break;
+ }
+ drawText( aPos, pStr+i, 1, NULL );
+ if( i < nLen-1 && pDeltaArray )
+ {
+ aPoint.X() = (sal_Int32)(rPoint.X() + ((double)pDeltaArray[i] * fCos));
+ aPoint.Y() = (sal_Int32)(rPoint.Y() + ((double)pDeltaArray[i] * fSin));
+ }
+
+ // swap text width/height again
+ SetFont( mnFontID,
+ nOldHeight,
+ nOldWidth,
+ nNormalAngle,
+ mbTextVertical,
+ maVirtualStatus.mbArtItalic,
+ maVirtualStatus.mbArtBold );
+ }
+ i++;
+ nLastPos = i;
+ }
+ mnTextAngle = nNormalAngle;
+}
+
+void
+PrinterGfx::LicenseWarning(const Point& rPoint, const sal_Unicode* pStr,
+ sal_Int16 nLen, const sal_Int32* pDeltaArray)
+{
+ // treat it like a builtin font in case a user has that font also in the
+ // printer. This is not so unlikely as it may seem; no print embedding
+ // licensed fonts are often used (or so they say) in companies:
+ // they are installed on displays and printers, but get not embedded in
+ // they are installed on displays and printers, but get not embedded in
+ // print files or documents because they are not licensed for use outside
+ // the company.
+ rtl::OString aMessage( "The font " );
+ aMessage += rtl::OUStringToOString( mrFontMgr.getPSName(mnFontID),
+ RTL_TEXTENCODING_ASCII_US );
+ aMessage += " could not be downloaded\nbecause its license does not allow for that";
+ PSComment( aMessage.getStr() );
+
+ rtl::OString aFontName = rtl::OUStringToOString(
+ mrFontMgr.getPSName(mnFontID),
+ RTL_TEXTENCODING_ASCII_US);
+ PSSetFont (aFontName, RTL_TEXTENCODING_ISO_8859_1);
+
+ sal_Size nSize = 4 * nLen;
+ sal_uChar* pBuffer = (sal_uChar*)alloca (nSize* sizeof(sal_uChar));
+
+ ConverterFactory &rCvt = GetConverterFactory ();
+ nSize = rCvt.Convert (pStr, nLen, pBuffer, nSize, RTL_TEXTENCODING_ISO_8859_1);
+
+ PSMoveTo (rPoint);
+ PSShowText (pBuffer, nLen, nSize, pDeltaArray);
+}
+
+void
+PrinterGfx::drawText(
+ const Point& rPoint,
+ const sal_Unicode* pStr,
+ sal_Int16 nLen,
+ const sal_Int32* pDeltaArray
+ )
+{
+ if (!(nLen > 0))
+ return;
+
+ fonttype::type eType = mrFontMgr.getFontType (mnFontID);
+
+ if (eType == fonttype::Type1)
+ PSUploadPS1Font (mnFontID);
+
+ if ( eType == fonttype::TrueType
+ && !mrFontMgr.isFontDownloadingAllowed(mnFontID))
+ {
+ LicenseWarning(rPoint, pStr, nLen, pDeltaArray);
+ return;
+ }
+
+ if( mrFontMgr.getUseOnlyFontEncoding( mnFontID ) )
+ {
+ GlyphSet aGSet( mnFontID, mbTextVertical );
+ aGSet.DrawText( *this, rPoint, pStr, nLen, pDeltaArray );
+ return;
+ }
+
+ // search for a glyph set matching the set font
+ std::list< GlyphSet >::iterator aIter;
+ for (aIter = maPS3Font.begin(); aIter != maPS3Font.end(); ++aIter)
+ if ( ((*aIter).GetFontID() == mnFontID)
+ && ((*aIter).IsVertical() == mbTextVertical))
+ {
+ (*aIter).DrawText (*this, rPoint, pStr, nLen, pDeltaArray);
+ break;
+ }
+
+ // not found ? create a new one
+ if (aIter == maPS3Font.end())
+ {
+ maPS3Font.push_back (GlyphSet(mnFontID, mbTextVertical));
+ maPS3Font.back().DrawText (*this, rPoint, pStr, nLen, pDeltaArray);
+ }
+}
+
+int
+PrinterGfx::getCharWidth (sal_Bool b_vert, sal_Unicode n_char, CharacterMetric *p_bbox)
+{
+ b_vert = b_vert && (getVerticalDeltaAngle(n_char) != 0);
+ int w = b_vert ? p_bbox->height : p_bbox->width;
+ w *= maVirtualStatus.mnTextWidth ? maVirtualStatus.mnTextWidth : maVirtualStatus.mnTextHeight;
+ return w;
+}
+
+fontID
+PrinterGfx::getCharMetric (const Font3 &rFont, sal_Unicode n_char, CharacterMetric *p_bbox)
+{
+ p_bbox->width = -1;
+ p_bbox->height = -1;
+
+ for (fontID n = 0; n < 3; n++)
+ {
+ fontID n_font = rFont.GetFont(n);
+ if (n_font != -1)
+ {
+ if( mbStrictSO52Compatibility )
+ {
+ fonttype::type eType = mrFontMgr.getFontType( n_font );
+ if( (eType == fonttype::Builtin || eType == fonttype::Type1) )
+ {
+ // note: any character exchanged here MUST also be changed
+ // in the compatibility ISO encoding vector in the prolog
+ // in printerjob.cxx
+ sal_Unicode aRepl = 0;
+ if( n_char == 0x2d )
+ aRepl = 0x2212;
+ else if( n_char == 0x27 )
+ aRepl = 0x2019;
+ /*
+ additional characters that may need backwards compatibility:
+ ISO5589 StdEnc Unicode suggested n_char -> aRepl
+ 0264 0302 0x00B4 0x00B4 (acute) -> 0x2019 (quiteright)
+ 0246 - 0x00A6 0x00A6 (brokenbar) -> 0x007C (bar)
+ 0225 0267 0x0095 0x0095 () -> 0x2022 (bullet)
+ 0140 0301 0x0060 0x0060 (grave) -> ?
+ */
+ if( aRepl )
+ {
+ mrFontMgr.getMetrics( n_font, aRepl, aRepl, p_bbox );
+ if (p_bbox->width >= 0 && p_bbox->height >= 0)
+ return n_font;
+ }
+ }
+ }
+ mrFontMgr.getMetrics( n_font, n_char, n_char, p_bbox );
+ }
+ if (p_bbox->width >= 0 && p_bbox->height >= 0)
+ return n_font;
+ }
+ if (n_char != '?')
+ return getCharMetric (rFont, '?', p_bbox);
+
+ return rFont.GetFont(0) != -1 ? rFont.GetFont(0) : rFont.GetFont(1);
+}
+
+fontID
+PrinterGfx::getFontSubstitute () const
+{
+ if( mpFontSubstitutes )
+ {
+ ::boost::unordered_map< fontID, fontID >::const_iterator it =
+ mpFontSubstitutes->find( mnFontID );
+ if( it != mpFontSubstitutes->end() )
+ return it->second;
+ }
+
+ return -1;
+}
+
+sal_Int32
+PrinterGfx::GetCharWidth (sal_Unicode nFrom, sal_Unicode nTo, long *pWidthArray)
+{
+ Font3 aFont(*this);
+ if (aFont.IsSymbolFont() && (nFrom < 256) && (nTo < 256))
+ {
+ nFrom += 0xF000;
+ nTo += 0xF000;
+ }
+
+ for( int n = 0; n < (nTo - nFrom + 1); n++ )
+ {
+ CharacterMetric aBBox;
+ getCharMetric (aFont, n + nFrom, &aBBox);
+ pWidthArray[n] = getCharWidth (mbTextVertical, n + nFrom, &aBBox);
+ }
+
+ // returned metrics have postscript precision
+ return 1000;
+}
+
+const ::std::list< KernPair >& PrinterGfx::getKernPairs( bool bVertical ) const
+{
+ /*
+ * Note: this is only a 80% solution: if a font is only
+ * partially substituted in a string due to missing glyphs
+ * the results may not be perfect; the more so the more the
+ * substitution differs from the original metricwise. But
+ * vcl only asks for KernPairs for each font once and NOT
+ * in a string context this is the best we can do.
+ * In future the kerning should be done on a per string basis.
+ */
+ fontID nFont = mnFontID;
+ if( mpFontSubstitutes )
+ {
+ ::boost::unordered_map< fontID, fontID >::const_iterator it =
+ mpFontSubstitutes->find( mnFontID );
+ if( it != mpFontSubstitutes->end() )
+ nFont = it->second;
+ }
+ return mrFontMgr.getKernPairs( nFont, bVertical );
+}
+
+/*
+ * advanced glyph handling
+ */
+
+sal_Bool
+PrinterGfx::GetGlyphBoundRect (sal_Unicode /*c*/, Rectangle& /*rOutRect*/)
+{
+ return 0;
+}
+
+sal_uInt32
+PrinterGfx::GetGlyphOutline (sal_Unicode /*c*/,
+ sal_uInt16 **/*ppPolySizes*/, Point **/*ppPoints*/, sal_uInt8 **/*ppFlags*/)
+{
+ return 0;
+}
+
+/*
+ * spool the converted truetype fonts to the page header after the page body is
+ * complete
+ * for Type1 fonts spool additional reencoding vectors that are necessary to access the
+ * whole font
+ */
+
+void
+PrinterGfx::OnEndPage ()
+{
+}
+
+void
+PrinterGfx::OnEndJob ()
+{
+ maPS3Font.clear();
+ maPS1Font.clear();
+}
+
+void
+PrinterGfx::writeResources( osl::File* pFile, std::list< rtl::OString >& rSuppliedFonts, std::list< rtl::OString >& rNeededFonts )
+{
+ // write all type 1 fonts
+ std::list< sal_Int32 >::iterator aFont;
+ // already in the document header ?
+ for (aFont = maPS1Font.begin(); aFont != maPS1Font.end(); ++aFont)
+ {
+ const rtl::OString& rSysPath (mrFontMgr.getFontFileSysPath(*aFont) );
+ rtl::OUString aUNCPath;
+ osl::File::getFileURLFromSystemPath (OStringToOUString (rSysPath, osl_getThreadTextEncoding()), aUNCPath);
+ osl::File aFontFile (aUNCPath);
+
+ // provide the pfb or pfa font as a (pfa-)font resource
+ rtl::OString aPostScriptName =
+ rtl::OUStringToOString ( mrFontMgr.getPSName(*aFont),
+ RTL_TEXTENCODING_ASCII_US );
+
+ WritePS (pFile, "%%BeginResource: font ");
+ WritePS (pFile, aPostScriptName.getStr());
+ WritePS (pFile, "\n");
+
+ osl::File::RC nError = aFontFile.open(osl_File_OpenFlag_Read);
+ if (nError == osl::File::E_None)
+ {
+ convertPfbToPfa (aFontFile, *pFile);
+ aFontFile.close ();
+
+ char lastchar = '\n';
+
+ if (pFile->setPos(osl_Pos_Current, -1) == osl::FileBase::E_None)
+ {
+ sal_uInt64 uBytes(1);
+ pFile->read((void *)(&lastchar), uBytes, uBytes);
+ }
+
+ if (lastchar != '\n')
+ WritePS (pFile, "\n");
+ }
+ WritePS (pFile, "%%EndResource\n");
+ rSuppliedFonts.push_back( aPostScriptName );
+ }
+
+ // write glyphsets and reencodings
+ std::list< GlyphSet >::iterator aIter;
+ for (aIter = maPS3Font.begin(); aIter != maPS3Font.end(); ++aIter)
+ {
+ if (aIter->GetFontType() == fonttype::TrueType)
+ {
+ aIter->PSUploadFont (*pFile, *this, mbUploadPS42Fonts ? true : false, rSuppliedFonts );
+ }
+ else
+ // ( aIter->GetFontType() == fonttype::Type1
+ // || aIter->GetFontType() == fonttype::Builtin )
+ {
+ aIter->PSUploadEncoding (pFile, *this);
+ if( aIter->GetFontType() == fonttype::Builtin )
+ rNeededFonts.push_back(
+ rtl::OUStringToOString(
+ mrFontMgr.getPSName( aIter->GetFontID() ),
+ RTL_TEXTENCODING_ASCII_US ) );
+ }
+ }
+}
+
+bool PrinterGfx::getStrictSO52Compatibility() const
+{
+ return mbStrictSO52Compatibility;
+}
+
+void PrinterGfx::setStrictSO52Compatibility( bool bCompat)
+{
+ mbStrictSO52Compatibility = bCompat;
+}
+
+/* vim:set shiftwidth=4 softtabstop=4 expandtab: */