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

PSFontUtils.java 30KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715
  1. /*
  2. * Licensed to the Apache Software Foundation (ASF) under one or more
  3. * contributor license agreements. See the NOTICE file distributed with
  4. * this work for additional information regarding copyright ownership.
  5. * The ASF licenses this file to You under the Apache License, Version 2.0
  6. * (the "License"); you may not use this file except in compliance with
  7. * the License. You may obtain a copy of the License at
  8. *
  9. * http://www.apache.org/licenses/LICENSE-2.0
  10. *
  11. * Unless required by applicable law or agreed to in writing, software
  12. * distributed under the License is distributed on an "AS IS" BASIS,
  13. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  14. * See the License for the specific language governing permissions and
  15. * limitations under the License.
  16. */
  17. /* $Id$ */
  18. package org.apache.fop.render.ps;
  19. import java.io.FileNotFoundException;
  20. import java.io.IOException;
  21. import java.io.InputStream;
  22. import java.net.MalformedURLException;
  23. import java.util.HashMap;
  24. import java.util.HashSet;
  25. import java.util.Locale;
  26. import java.util.Map;
  27. import java.util.Set;
  28. import javax.xml.transform.Source;
  29. import javax.xml.transform.stream.StreamSource;
  30. import org.apache.commons.logging.Log;
  31. import org.apache.commons.logging.LogFactory;
  32. import org.apache.xmlgraphics.fonts.Glyphs;
  33. import org.apache.xmlgraphics.ps.DSCConstants;
  34. import org.apache.xmlgraphics.ps.PSGenerator;
  35. import org.apache.xmlgraphics.ps.PSResource;
  36. import org.apache.xmlgraphics.ps.dsc.ResourceTracker;
  37. import org.apache.fop.fonts.Base14Font;
  38. import org.apache.fop.fonts.CIDFontType;
  39. import org.apache.fop.fonts.CIDSubset;
  40. import org.apache.fop.fonts.CMapSegment;
  41. import org.apache.fop.fonts.CustomFont;
  42. import org.apache.fop.fonts.EmbeddingMode;
  43. import org.apache.fop.fonts.Font;
  44. import org.apache.fop.fonts.FontInfo;
  45. import org.apache.fop.fonts.FontType;
  46. import org.apache.fop.fonts.LazyFont;
  47. import org.apache.fop.fonts.MultiByteFont;
  48. import org.apache.fop.fonts.SingleByteEncoding;
  49. import org.apache.fop.fonts.SingleByteFont;
  50. import org.apache.fop.fonts.Typeface;
  51. import org.apache.fop.fonts.truetype.FontFileReader;
  52. import org.apache.fop.fonts.truetype.TTFFile;
  53. import org.apache.fop.fonts.truetype.TTFFile.PostScriptVersion;
  54. import org.apache.fop.fonts.truetype.TTFOutputStream;
  55. import org.apache.fop.fonts.truetype.TTFSubSetFile;
  56. import org.apache.fop.render.ps.fonts.PSTTFOutputStream;
  57. import org.apache.fop.util.HexEncoder;
  58. /**
  59. * Utility code for font handling in PostScript.
  60. */
  61. public class PSFontUtils extends org.apache.xmlgraphics.ps.PSFontUtils {
  62. /** logging instance */
  63. protected static final Log log = LogFactory.getLog(PSFontUtils.class);
  64. /**
  65. * Generates the PostScript code for the font dictionary. This method should only be
  66. * used if no "resource optimization" is performed, i.e. when the fonts are not embedded
  67. * in a second pass.
  68. * @param gen PostScript generator to use for output
  69. * @param fontInfo available fonts
  70. * @return a Map of PSResource instances representing all defined fonts (key: font key)
  71. * @throws IOException in case of an I/O problem
  72. */
  73. public static Map writeFontDict(PSGenerator gen, FontInfo fontInfo)
  74. throws IOException {
  75. return writeFontDict(gen, fontInfo, null);
  76. }
  77. /**
  78. * Generates the PostScript code for the font dictionary. This method should only be
  79. * used if no "resource optimization" is performed, i.e. when the fonts are not embedded
  80. * in a second pass.
  81. * @param gen PostScript generator to use for output
  82. * @param fontInfo available fonts
  83. * @param eventProducer to report events
  84. * @return a Map of PSResource instances representing all defined fonts (key: font key)
  85. * @throws IOException in case of an I/O problem
  86. */
  87. public static Map writeFontDict(PSGenerator gen, FontInfo fontInfo,
  88. PSEventProducer eventProducer) throws IOException {
  89. return writeFontDict(gen, fontInfo, fontInfo.getFonts(), true, eventProducer);
  90. }
  91. /**
  92. * Generates the PostScript code for the font dictionary. This method assumes all used
  93. * fonts and characters are known, i.e. when PostScript is generated with resource
  94. * optimization turned on.
  95. * @param gen PostScript generator to use for output
  96. * @param fontInfo available fonts
  97. * @param fonts the set of fonts to work with
  98. * @param eventProducer the event producer
  99. * @return a Map of PSResource instances representing all defined fonts (key: font key)
  100. * @throws IOException in case of an I/O problem
  101. */
  102. public static Map writeFontDict(PSGenerator gen, FontInfo fontInfo, Map<String, Typeface> fonts,
  103. PSEventProducer eventProducer) throws IOException {
  104. return writeFontDict(gen, fontInfo, fonts, false, eventProducer);
  105. }
  106. /**
  107. * Generates the PostScript code for the font dictionary.
  108. * @param gen PostScript generator to use for output
  109. * @param fontInfo available fonts
  110. * @param fonts the set of fonts to work with
  111. * @param encodeAllCharacters true if all characters shall be encoded using additional,
  112. * generated encodings.
  113. * @return a Map of PSResource instances representing all defined fonts (key: font key)
  114. * @throws IOException in case of an I/O problem
  115. */
  116. private static Map writeFontDict(PSGenerator gen, FontInfo fontInfo,
  117. Map<String, Typeface> fonts, boolean encodeAllCharacters, PSEventProducer eventProducer)
  118. throws IOException {
  119. gen.commentln("%FOPBeginFontDict");
  120. Map fontResources = new HashMap();
  121. for (String key : fonts.keySet()) {
  122. Typeface tf = getTypeFace(fontInfo, fonts, key);
  123. PSResource fontRes = new PSResource(PSResource.TYPE_FONT, tf.getEmbedFontName());
  124. PSFontResource fontResource = embedFont(gen, tf, fontRes, eventProducer);
  125. fontResources.put(key, fontResource);
  126. if (tf instanceof SingleByteFont) {
  127. SingleByteFont sbf = (SingleByteFont)tf;
  128. if (encodeAllCharacters) {
  129. sbf.encodeAllUnencodedCharacters();
  130. }
  131. for (int i = 0, c = sbf.getAdditionalEncodingCount(); i < c; i++) {
  132. SingleByteEncoding encoding = sbf.getAdditionalEncoding(i);
  133. defineEncoding(gen, encoding);
  134. String postFix = "_" + (i + 1);
  135. PSResource derivedFontRes;
  136. if (tf.getFontType() == FontType.TRUETYPE
  137. && sbf.getTrueTypePostScriptVersion() != PostScriptVersion.V2) {
  138. derivedFontRes = defineDerivedTrueTypeFont(gen, eventProducer,
  139. tf.getEmbedFontName(), tf.getEmbedFontName() + postFix, encoding,
  140. sbf.getCMap());
  141. } else {
  142. derivedFontRes = defineDerivedFont(gen, tf.getEmbedFontName(),
  143. tf.getEmbedFontName() + postFix, encoding.getName());
  144. }
  145. fontResources.put(key + postFix,
  146. PSFontResource.createFontResource(derivedFontRes));
  147. }
  148. }
  149. }
  150. gen.commentln("%FOPEndFontDict");
  151. reencodeFonts(gen, fonts);
  152. return fontResources;
  153. }
  154. private static void reencodeFonts(PSGenerator gen, Map<String, Typeface> fonts)
  155. throws IOException {
  156. ResourceTracker tracker = gen.getResourceTracker();
  157. if (!tracker.isResourceSupplied(WINANSI_ENCODING_RESOURCE)) {
  158. //Only out Base 14 fonts still use that
  159. defineWinAnsiEncoding(gen);
  160. }
  161. gen.commentln("%FOPBeginFontReencode");
  162. //Rewrite font encodings
  163. for (String key : fonts.keySet()) {
  164. Typeface tf = fonts.get(key);
  165. if (tf instanceof LazyFont) {
  166. tf = ((LazyFont)tf).getRealFont();
  167. if (tf == null) {
  168. continue;
  169. }
  170. }
  171. if (null == tf.getEncodingName()) {
  172. //ignore (ZapfDingbats and Symbol used to run through here, kept for safety reasons)
  173. } else if ("SymbolEncoding".equals(tf.getEncodingName())) {
  174. //ignore (no encoding redefinition)
  175. } else if ("ZapfDingbatsEncoding".equals(tf.getEncodingName())) {
  176. //ignore (no encoding redefinition)
  177. } else {
  178. if (tf instanceof Base14Font) {
  179. //Our Base 14 fonts don't use the default encoding
  180. redefineFontEncoding(gen, tf.getEmbedFontName(), tf.getEncodingName());
  181. } else if (tf instanceof SingleByteFont) {
  182. SingleByteFont sbf = (SingleByteFont)tf;
  183. if (!sbf.isUsingNativeEncoding()) {
  184. //Font has been configured to use an encoding other than the default one
  185. redefineFontEncoding(gen, tf.getEmbedFontName(), tf.getEncodingName());
  186. }
  187. }
  188. }
  189. }
  190. gen.commentln("%FOPEndFontReencode");
  191. }
  192. private static Typeface getTypeFace(FontInfo fontInfo, Map<String, Typeface> fonts,
  193. String key) {
  194. Typeface tf = fonts.get(key);
  195. if (tf instanceof LazyFont) {
  196. tf = ((LazyFont)tf).getRealFont();
  197. }
  198. if (tf == null) {
  199. //This is to avoid an NPE if a malconfigured font is in the configuration but not
  200. //used in the document. If it were used, we wouldn't get this far.
  201. String fallbackKey = fontInfo.getInternalFontKey(Font.DEFAULT_FONT);
  202. tf = fonts.get(fallbackKey);
  203. }
  204. return tf;
  205. }
  206. private static PSFontResource embedFont(PSGenerator gen, Typeface tf, PSResource fontRes,
  207. PSEventProducer eventProducer) throws IOException {
  208. FontType fontType = tf.getFontType();
  209. PSFontResource fontResource = null;
  210. if (!(fontType == FontType.TYPE1 || fontType == FontType.TRUETYPE
  211. || fontType == FontType.TYPE0) || !(tf instanceof CustomFont)) {
  212. gen.writeDSCComment(DSCConstants.INCLUDE_RESOURCE, fontRes);
  213. fontResource = PSFontResource.createFontResource(fontRes);
  214. return fontResource;
  215. }
  216. CustomFont cf = (CustomFont)tf;
  217. if (isEmbeddable(cf)) {
  218. InputStream in = getInputStreamOnFont(gen, cf);
  219. if (in == null) {
  220. gen.commentln("%WARNING: Could not embed font: " + cf.getEmbedFontName());
  221. log.warn("Font " + cf.getEmbedFontName() + " is marked as supplied in the"
  222. + " PostScript file but could not be embedded!");
  223. gen.writeDSCComment(DSCConstants.INCLUDE_RESOURCE, fontRes);
  224. fontResource = PSFontResource.createFontResource(fontRes);
  225. return fontResource;
  226. }
  227. if (fontType == FontType.TYPE0) {
  228. if (gen.embedIdentityH()) {
  229. checkPostScriptLevel3(gen, eventProducer);
  230. /*
  231. * First CID-keyed font to be embedded; add
  232. * %%IncludeResource: comment for ProcSet CIDInit.
  233. */
  234. gen.includeProcsetCIDInitResource();
  235. }
  236. PSResource cidFontResource = embedType2CIDFont(gen,
  237. (MultiByteFont) tf, in);
  238. fontResource = PSFontResource.createFontResource(fontRes,
  239. gen.getProcsetCIDInitResource(), gen.getIdentityHCMapResource(),
  240. cidFontResource);
  241. }
  242. gen.writeDSCComment(DSCConstants.BEGIN_RESOURCE, fontRes);
  243. if (fontType == FontType.TYPE1) {
  244. embedType1Font(gen, in);
  245. fontResource = PSFontResource.createFontResource(fontRes);
  246. } else if (fontType == FontType.TRUETYPE) {
  247. embedTrueTypeFont(gen, (SingleByteFont) tf, in);
  248. fontResource = PSFontResource.createFontResource(fontRes);
  249. } else {
  250. composeType0Font(gen, (MultiByteFont) tf, in);
  251. }
  252. gen.writeDSCComment(DSCConstants.END_RESOURCE);
  253. gen.getResourceTracker().registerSuppliedResource(fontRes);
  254. }
  255. return fontResource;
  256. }
  257. private static void checkPostScriptLevel3(PSGenerator gen, PSEventProducer eventProducer) {
  258. if (gen.getPSLevel() < 3) {
  259. if (eventProducer != null) {
  260. eventProducer.postscriptLevel3Needed(gen);
  261. } else {
  262. throw new IllegalStateException("PostScript Level 3 is"
  263. + " required to use TrueType fonts,"
  264. + " configured level is "
  265. + gen.getPSLevel());
  266. }
  267. }
  268. }
  269. private static void embedTrueTypeFont(PSGenerator gen,
  270. SingleByteFont font, InputStream fontStream) throws IOException {
  271. /* See Adobe Technical Note #5012, "The Type 42 Font Format Specification" */
  272. gen.commentln("%!PS-TrueTypeFont-65536-65536-1"); // TODO TrueType & font versions
  273. gen.writeln("11 dict begin");
  274. if (font.getEmbeddingMode() == EmbeddingMode.AUTO) {
  275. font.setEmbeddingMode(EmbeddingMode.SUBSET);
  276. }
  277. FontFileReader reader = new FontFileReader(fontStream);
  278. TTFFile ttfFile = new TTFFile();
  279. ttfFile.readFont(reader, font.getFullName());
  280. createType42DictionaryEntries(gen, font, font.getCMap(), ttfFile);
  281. gen.writeln("FontName currentdict end definefont pop");
  282. }
  283. private static void createType42DictionaryEntries(PSGenerator gen, CustomFont font,
  284. CMapSegment[] cmap, TTFFile ttfFile) throws IOException {
  285. gen.write("/FontName /");
  286. gen.write(font.getEmbedFontName());
  287. gen.writeln(" def");
  288. gen.writeln("/PaintType 0 def");
  289. gen.writeln("/FontMatrix [1 0 0 1 0 0] def");
  290. writeFontBBox(gen, font);
  291. gen.writeln("/FontType 42 def");
  292. gen.writeln("/Encoding 256 array");
  293. gen.writeln("0 1 255{1 index exch/.notdef put}for");
  294. boolean buildCharStrings;
  295. Set<String> glyphNames = new HashSet<String>();
  296. if (font.getFontType() == FontType.TYPE0 && font.getEmbeddingMode() != EmbeddingMode.FULL) {
  297. //"/Encoding" is required but ignored for CID fonts
  298. //so we keep it minimal to save space
  299. buildCharStrings = false;
  300. } else {
  301. buildCharStrings = true;
  302. for (int i = 0; i < Glyphs.WINANSI_ENCODING.length; i++) {
  303. gen.write("dup ");
  304. gen.write(i);
  305. gen.write(" /");
  306. String glyphName = Glyphs.charToGlyphName(Glyphs.WINANSI_ENCODING[i]);
  307. if (glyphName.equals("")) {
  308. gen.write(Glyphs.NOTDEF);
  309. } else {
  310. gen.write(glyphName);
  311. glyphNames.add(glyphName);
  312. }
  313. gen.writeln(" put");
  314. }
  315. }
  316. gen.writeln("readonly def");
  317. TTFOutputStream ttfOut = new PSTTFOutputStream(gen);
  318. ttfFile.stream(ttfOut);
  319. buildCharStrings(gen, buildCharStrings, cmap, glyphNames, font);
  320. }
  321. private static void buildCharStrings(PSGenerator gen, boolean buildCharStrings,
  322. CMapSegment[] cmap, Set<String> glyphNames, CustomFont font) throws IOException {
  323. gen.write("/CharStrings ");
  324. if (!buildCharStrings) {
  325. gen.write(1);
  326. } else if (font.getEmbeddingMode() != EmbeddingMode.FULL) {
  327. int charCount = 1; //1 for .notdef
  328. for (CMapSegment segment : cmap) {
  329. charCount += segment.getUnicodeEnd() - segment.getUnicodeStart() + 1;
  330. }
  331. gen.write(charCount);
  332. } else {
  333. gen.write(font.getCMap().length);
  334. }
  335. gen.writeln(" dict dup begin");
  336. gen.write("/");
  337. gen.write(Glyphs.NOTDEF);
  338. gen.writeln(" 0 def"); // .notdef always has to be at index 0
  339. if (!buildCharStrings) {
  340. // If we're not building the full CharStrings we can end here
  341. gen.writeln("end readonly def");
  342. return;
  343. }
  344. if (font.getEmbeddingMode() != EmbeddingMode.FULL) {
  345. //Only performed in singly-byte mode, ignored for CID fonts
  346. for (CMapSegment segment : cmap) {
  347. int glyphIndex = segment.getGlyphStartIndex();
  348. for (int ch = segment.getUnicodeStart(); ch <= segment.getUnicodeEnd(); ch++) {
  349. char ch16 = (char)ch; //TODO Handle Unicode characters beyond 16bit
  350. String glyphName = Glyphs.charToGlyphName(ch16);
  351. if ("".equals(glyphName)) {
  352. glyphName = "u" + Integer.toHexString(ch).toUpperCase(Locale.ENGLISH);
  353. }
  354. writeGlyphDefs(gen, glyphName, glyphIndex);
  355. glyphIndex++;
  356. }
  357. }
  358. } else {
  359. for (String name : glyphNames) {
  360. writeGlyphDefs(gen, name,
  361. getGlyphIndex(Glyphs.getUnicodeSequenceForGlyphName(name).charAt(0),
  362. font.getCMap()));
  363. }
  364. }
  365. gen.writeln("end readonly def");
  366. }
  367. private static void writeGlyphDefs(PSGenerator gen, String glyphName, int glyphIndex)
  368. throws IOException {
  369. gen.write("/");
  370. gen.write(glyphName);
  371. gen.write(" ");
  372. gen.write(glyphIndex);
  373. gen.writeln(" def");
  374. }
  375. private static int getGlyphIndex(char c, CMapSegment[] cmap) {
  376. for (CMapSegment segment : cmap) {
  377. if (segment.getUnicodeStart() <= c && c <= segment.getUnicodeEnd()) {
  378. return segment.getGlyphStartIndex() + c - segment.getUnicodeStart();
  379. }
  380. }
  381. return 0;
  382. }
  383. private static void composeType0Font(PSGenerator gen, MultiByteFont font,
  384. InputStream fontStream) throws IOException {
  385. String psName = font.getEmbedFontName();
  386. gen.write("/");
  387. gen.write(psName);
  388. gen.write(" /Identity-H [/");
  389. gen.write(psName);
  390. gen.writeln("] composefont pop");
  391. }
  392. private static PSResource embedType2CIDFont(PSGenerator gen,
  393. MultiByteFont font, InputStream fontStream) throws IOException {
  394. assert font.getCIDType() == CIDFontType.CIDTYPE2;
  395. String psName = font.getEmbedFontName();
  396. gen.write("%%BeginResource: CIDFont ");
  397. gen.writeln(psName);
  398. gen.write("%%Title: (");
  399. gen.write(psName);
  400. gen.writeln(" Adobe Identity 0)");
  401. gen.writeln("%%Version: 1"); // TODO use font revision?
  402. gen.writeln("/CIDInit /ProcSet findresource begin");
  403. gen.writeln("20 dict begin");
  404. gen.write("/CIDFontName /");
  405. gen.write(psName);
  406. gen.writeln(" def");
  407. gen.writeln("/CIDFontVersion 1 def"); // TODO same as %%Version above
  408. gen.write("/CIDFontType ");
  409. gen.write(font.getCIDType().getValue());
  410. gen.writeln(" def");
  411. gen.writeln("/CIDSystemInfo 3 dict dup begin");
  412. gen.writeln(" /Registry (Adobe) def");
  413. gen.writeln(" /Ordering (Identity) def");
  414. gen.writeln(" /Supplement 0 def");
  415. gen.writeln("end def");
  416. // TODO UIDBase (and UIDOffset in CMap) necessary if PostScript Level 1 & 2
  417. // interpreters are to be supported
  418. // (Level 1: with composite font extensions; Level 2: those that do not offer
  419. // native mode support for CID-keyed fonts)
  420. // TODO XUID (optional but strongly recommended)
  421. // TODO /FontInfo
  422. gen.write("/CIDCount ");
  423. CIDSubset cidSubset = font.getCIDSubset();
  424. int subsetSize = cidSubset.getSubsetSize();
  425. gen.write(subsetSize);
  426. gen.writeln(" def");
  427. gen.writeln("/GDBytes 2 def"); // TODO always 2?
  428. gen.writeln("/CIDMap [<");
  429. int colCount = 0;
  430. int lineCount = 1;
  431. for (int cid = 0; cid < subsetSize; cid++) {
  432. if (colCount++ == 20) {
  433. gen.newLine();
  434. colCount = 1;
  435. if (lineCount++ == 800) {
  436. gen.writeln("> <");
  437. lineCount = 1;
  438. }
  439. }
  440. String gid;
  441. if (font.getEmbeddingMode() != EmbeddingMode.FULL) {
  442. gid = HexEncoder.encode(cid, 4);
  443. } else {
  444. gid = HexEncoder.encode(cidSubset.getGlyphIndexForSubsetIndex(cid), 4);
  445. }
  446. gen.write(gid);
  447. }
  448. gen.writeln(">] def");
  449. FontFileReader reader = new FontFileReader(fontStream);
  450. TTFFile ttfFile;
  451. if (font.getEmbeddingMode() != EmbeddingMode.FULL) {
  452. ttfFile = new TTFSubSetFile();
  453. ttfFile.readFont(reader, font.getTTCName(), font.getUsedGlyphs());
  454. } else {
  455. ttfFile = new TTFFile();
  456. ttfFile.readFont(reader, font.getTTCName());
  457. }
  458. createType42DictionaryEntries(gen, font, new CMapSegment[0], ttfFile);
  459. gen.writeln("CIDFontName currentdict end /CIDFont defineresource pop");
  460. gen.writeln("end");
  461. gen.writeln("%%EndResource");
  462. PSResource cidFontResource = new PSResource(PSResource.TYPE_CIDFONT, psName);
  463. gen.getResourceTracker().registerSuppliedResource(cidFontResource);
  464. return cidFontResource;
  465. }
  466. private static void writeFontBBox(PSGenerator gen, CustomFont font) throws IOException {
  467. int[] bbox = font.getFontBBox();
  468. gen.write("/FontBBox[");
  469. for (int i = 0; i < 4; i++) {
  470. gen.write(" ");
  471. gen.write(bbox[i]);
  472. }
  473. gen.writeln(" ] def");
  474. }
  475. private static boolean isEmbeddable(CustomFont font) {
  476. return font.isEmbeddable();
  477. }
  478. private static InputStream getInputStreamOnFont(PSGenerator gen, CustomFont font)
  479. throws IOException {
  480. if (isEmbeddable(font)) {
  481. Source source = font.getEmbedFileSource();
  482. if (source == null && font.getEmbedResourceName() != null) {
  483. source = new StreamSource(PSFontUtils.class
  484. .getResourceAsStream(font.getEmbedResourceName()));
  485. }
  486. if (source == null) {
  487. return null;
  488. }
  489. InputStream in = null;
  490. if (source instanceof StreamSource) {
  491. in = ((StreamSource) source).getInputStream();
  492. }
  493. if (in == null && source.getSystemId() != null) {
  494. try {
  495. in = new java.net.URL(source.getSystemId()).openStream();
  496. } catch (MalformedURLException e) {
  497. new FileNotFoundException(
  498. "File not found. URL could not be resolved: "
  499. + e.getMessage());
  500. }
  501. }
  502. if (in == null) {
  503. return null;
  504. }
  505. //Make sure the InputStream is decorated with a BufferedInputStream
  506. if (!(in instanceof java.io.BufferedInputStream)) {
  507. in = new java.io.BufferedInputStream(in);
  508. }
  509. return in;
  510. } else {
  511. return null;
  512. }
  513. }
  514. /**
  515. * Determines the set of fonts that will be supplied with the PS file and registers them
  516. * with the resource tracker. All the fonts that are being processed are returned as a Map.
  517. * @param resTracker the resource tracker
  518. * @param fontInfo available fonts
  519. * @param fonts the set of fonts to work with
  520. * @return a Map of PSResource instances representing all defined fonts (key: font key)
  521. */
  522. public static Map determineSuppliedFonts(ResourceTracker resTracker,
  523. FontInfo fontInfo, Map<String, Typeface> fonts) {
  524. Map fontResources = new java.util.HashMap();
  525. for (String key : fonts.keySet()) {
  526. Typeface tf = getTypeFace(fontInfo, fonts, key);
  527. PSResource fontRes = new PSResource("font", tf.getEmbedFontName());
  528. fontResources.put(key, fontRes);
  529. FontType fontType = tf.getFontType();
  530. if (fontType == FontType.TYPE1 || fontType == FontType.TRUETYPE
  531. || fontType == FontType.TYPE0) {
  532. if (tf instanceof CustomFont) {
  533. CustomFont cf = (CustomFont)tf;
  534. if (isEmbeddable(cf)) {
  535. if (fontType == FontType.TYPE0) {
  536. resTracker.registerSuppliedResource(
  537. new PSResource(PSResource.TYPE_CIDFONT, tf.getEmbedFontName()));
  538. resTracker.registerSuppliedResource(
  539. new PSResource(PSResource.TYPE_CMAP, "Identity-H"));
  540. }
  541. resTracker.registerSuppliedResource(fontRes);
  542. }
  543. if (tf instanceof SingleByteFont) {
  544. SingleByteFont sbf = (SingleByteFont)tf;
  545. for (int i = 0, c = sbf.getAdditionalEncodingCount(); i < c; i++) {
  546. SingleByteEncoding encoding = sbf.getAdditionalEncoding(i);
  547. PSResource encodingRes = new PSResource(
  548. PSResource.TYPE_ENCODING, encoding.getName());
  549. resTracker.registerSuppliedResource(encodingRes);
  550. PSResource derivedFontRes = new PSResource(
  551. PSResource.TYPE_FONT, tf.getEmbedFontName() + "_" + (i + 1));
  552. resTracker.registerSuppliedResource(derivedFontRes);
  553. }
  554. }
  555. }
  556. }
  557. }
  558. return fontResources;
  559. }
  560. /**
  561. * Defines the single-byte encoding for use in PostScript files.
  562. * @param gen the PostScript generator
  563. * @param encoding the single-byte encoding
  564. * @return the PSResource instance that represents the encoding
  565. * @throws IOException In case of an I/O problem
  566. */
  567. public static PSResource defineEncoding(PSGenerator gen, SingleByteEncoding encoding)
  568. throws IOException {
  569. PSResource res = new PSResource(PSResource.TYPE_ENCODING, encoding.getName());
  570. gen.writeDSCComment(DSCConstants.BEGIN_RESOURCE, res);
  571. gen.writeln("/" + encoding.getName() + " [");
  572. String[] charNames = encoding.getCharNameMap();
  573. for (int i = 0; i < 256; i++) {
  574. if (i > 0) {
  575. if ((i % 5) == 0) {
  576. gen.newLine();
  577. } else {
  578. gen.write(" ");
  579. }
  580. }
  581. String glyphname = null;
  582. if (i < charNames.length) {
  583. glyphname = charNames[i];
  584. }
  585. if (glyphname == null || "".equals(glyphname)) {
  586. glyphname = Glyphs.NOTDEF;
  587. }
  588. gen.write("/");
  589. gen.write(glyphname);
  590. }
  591. gen.newLine();
  592. gen.writeln("] def");
  593. gen.writeDSCComment(DSCConstants.END_RESOURCE);
  594. gen.getResourceTracker().registerSuppliedResource(res);
  595. return res;
  596. }
  597. /**
  598. * Derives a new font based on an existing font with a given encoding. The encoding must
  599. * have been registered before.
  600. * @param gen the PostScript generator
  601. * @param baseFontName the font name of the font to derive from
  602. * @param fontName the font name of the new font to be define
  603. * @param encoding the new encoding (must be predefined in the PS file)
  604. * @return the PSResource representing the derived font
  605. * @throws IOException In case of an I/O problem
  606. */
  607. public static PSResource defineDerivedFont
  608. (PSGenerator gen, String baseFontName, String fontName, String encoding)
  609. throws IOException {
  610. PSResource res = new PSResource(PSResource.TYPE_FONT, fontName);
  611. gen.writeDSCComment(DSCConstants.BEGIN_RESOURCE, res);
  612. gen.commentln("%XGCDependencies: font " + baseFontName);
  613. gen.commentln("%XGC+ encoding " + encoding);
  614. gen.writeln("/" + baseFontName + " findfont");
  615. gen.writeln("dup length dict begin");
  616. gen.writeln(" {1 index /FID ne {def} {pop pop} ifelse} forall");
  617. gen.writeln(" /Encoding " + encoding + " def");
  618. gen.writeln(" currentdict");
  619. gen.writeln("end");
  620. gen.writeln("/" + fontName + " exch definefont pop");
  621. gen.writeDSCComment(DSCConstants.END_RESOURCE);
  622. gen.getResourceTracker().registerSuppliedResource(res);
  623. return res;
  624. }
  625. private static PSResource defineDerivedTrueTypeFont(PSGenerator gen,
  626. PSEventProducer eventProducer, String baseFontName, String fontName,
  627. SingleByteEncoding encoding, CMapSegment[] cmap) throws IOException {
  628. checkPostScriptLevel3(gen, eventProducer);
  629. PSResource res = new PSResource(PSResource.TYPE_FONT, fontName);
  630. gen.writeDSCComment(DSCConstants.BEGIN_RESOURCE, res);
  631. gen.commentln("%XGCDependencies: font " + baseFontName);
  632. gen.commentln("%XGC+ encoding " + encoding.getName());
  633. gen.writeln("/" + baseFontName + " findfont");
  634. gen.writeln("dup length dict begin");
  635. gen.writeln(" {1 index /FID ne {def} {pop pop} ifelse} forall");
  636. gen.writeln(" /Encoding " + encoding.getName() + " def");
  637. gen.writeln(" /CharStrings 256 dict dup begin");
  638. String[] charNameMap = encoding.getCharNameMap();
  639. char[] unicodeCharMap = encoding.getUnicodeCharMap();
  640. assert charNameMap.length == unicodeCharMap.length;
  641. for (int i = 0; i < charNameMap.length; i++) {
  642. String glyphName = charNameMap[i];
  643. gen.write(" /");
  644. gen.write(glyphName);
  645. gen.write(" ");
  646. if (glyphName.equals(".notdef")) {
  647. gen.write(0);
  648. } else {
  649. gen.write(getGlyphIndex(unicodeCharMap[i], cmap));
  650. }
  651. gen.writeln(" def");
  652. }
  653. gen.writeln(" end readonly def");
  654. gen.writeln(" currentdict");
  655. gen.writeln("end");
  656. gen.writeln("/" + fontName + " exch definefont pop");
  657. gen.writeDSCComment(DSCConstants.END_RESOURCE);
  658. gen.getResourceTracker().registerSuppliedResource(res);
  659. return res;
  660. }
  661. }