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.

PDFResources.java 13KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381
  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.pdf;
  19. import java.io.IOException;
  20. import java.io.OutputStream;
  21. import java.util.LinkedHashMap;
  22. import java.util.LinkedHashSet;
  23. import java.util.Map;
  24. import java.util.Set;
  25. import org.apache.xmlgraphics.java2d.color.profile.ColorProfileUtil;
  26. import org.apache.fop.events.EventBroadcaster;
  27. import org.apache.fop.fonts.FontDescriptor;
  28. import org.apache.fop.fonts.FontInfo;
  29. import org.apache.fop.fonts.Typeface;
  30. import org.apache.fop.fonts.base14.Symbol;
  31. import org.apache.fop.fonts.base14.ZapfDingbats;
  32. /**
  33. * Class representing a /Resources object.
  34. *
  35. * /Resources object contain a list of references to the fonts, patterns,
  36. * shadings, etc., for the document.
  37. */
  38. public class PDFResources extends PDFDictionary {
  39. /**
  40. * /Font objects keyed by their internal name
  41. */
  42. protected Map<String, PDFDictionary> fonts = new LinkedHashMap<String, PDFDictionary>();
  43. /**
  44. * Set of XObjects
  45. */
  46. protected Set<PDFXObject> xObjects = new LinkedHashSet<PDFXObject>();
  47. /** Map of color spaces (key: color space name) */
  48. protected Map<LazyName, PDFColorSpace> colorSpaces = new LinkedHashMap<LazyName, PDFColorSpace>();
  49. /** Map of ICC color spaces (key: ICC profile description) */
  50. protected Map<String, PDFICCBasedColorSpace> iccColorSpaces = new LinkedHashMap<String, PDFICCBasedColorSpace>();
  51. private PDFResources parent;
  52. private PDFDictionary fontsObj;
  53. private Map<String, PDFDictionary> fontsObjDict = new LinkedHashMap<String, PDFDictionary>();
  54. /** Named properties */
  55. protected Map<String, PDFReference> properties = new LinkedHashMap<String, PDFReference>();
  56. protected Set<PDFResourceContext> contexts = new LinkedHashSet<PDFResourceContext>();
  57. /**
  58. * create a /Resources object.
  59. */
  60. public PDFResources(PDFDocument doc) {
  61. /* generic creation of object */
  62. super();
  63. setObjectNumber(doc);
  64. }
  65. public void addContext(PDFResourceContext c) {
  66. contexts.add(c);
  67. }
  68. public void setParentResources(PDFResources p) {
  69. parent = p;
  70. }
  71. public PDFResources getParentResources() {
  72. return parent;
  73. }
  74. /**
  75. * add font object to resources list.
  76. *
  77. * @param font the PDFFont to add
  78. */
  79. public void addFont(PDFFont font) {
  80. addFont(font.getName(), font);
  81. }
  82. public void addFont(String name, PDFDictionary font) {
  83. if (fontsObj != null) {
  84. fontsObj.put(name, font);
  85. fontsObjDict.put(name, font);
  86. } else {
  87. fonts.put(name, font);
  88. }
  89. }
  90. public void createFontsAsObj() {
  91. fontsObj = new PDFDictionary();
  92. getDocument().registerTrailerObject(fontsObj);
  93. put("Font", fontsObj);
  94. }
  95. /**
  96. * Add the fonts in the font info to this PDF document's Font Resources.
  97. *
  98. * @param doc PDF document to add fonts to
  99. * @param fontInfo font info object to get font information from
  100. * @param eventBroadcaster Event broadcaster.
  101. */
  102. public void addFonts(PDFDocument doc, FontInfo fontInfo, EventBroadcaster eventBroadcaster) {
  103. Map<String, Typeface> usedFonts = fontInfo.getUsedFonts();
  104. for (Map.Entry<String, Typeface> e : usedFonts.entrySet()) {
  105. String f = e.getKey();
  106. Typeface font = e.getValue();
  107. //Check if the font actually had any mapping operations. If not, it is an indication
  108. //that it has never actually been used and therefore doesn't have to be embedded.
  109. if (font.hadMappingOperations()) {
  110. FontDescriptor desc = null;
  111. if (font instanceof FontDescriptor) {
  112. desc = (FontDescriptor)font;
  113. }
  114. String encoding = font.getEncodingName();
  115. if (font instanceof Symbol || font instanceof ZapfDingbats) {
  116. encoding = null; //Symbolic fonts shouldn't specify an encoding value in PDF
  117. }
  118. addFont(doc.getFactory().makeFont(
  119. f, font.getEmbedFontName(), encoding, font, desc, eventBroadcaster));
  120. }
  121. }
  122. }
  123. /**
  124. * Add an XObject to the resources.
  125. *
  126. * @param xObject the XObject to add
  127. */
  128. public void addXObject(PDFXObject xObject) {
  129. this.xObjects.add(xObject);
  130. }
  131. /**
  132. * Add a ColorSpace dictionary to the resources.
  133. * @param colorSpace the color space
  134. */
  135. public void addColorSpace(PDFColorSpace colorSpace) {
  136. this.colorSpaces.put(new LazyName(colorSpace), colorSpace);
  137. if (colorSpace instanceof PDFICCBasedColorSpace) {
  138. PDFICCBasedColorSpace icc = (PDFICCBasedColorSpace)colorSpace;
  139. String desc = ColorProfileUtil.getICCProfileDescription(
  140. icc.getICCStream().getICCProfile());
  141. this.iccColorSpaces.put(desc, icc);
  142. }
  143. }
  144. static class LazyName {
  145. private PDFColorSpace colorSpace;
  146. public LazyName(PDFColorSpace colorSpace) {
  147. this.colorSpace = colorSpace;
  148. }
  149. public PDFName getName() {
  150. return new PDFName(colorSpace.getName());
  151. }
  152. }
  153. /**
  154. * Returns a ICCBased color space by profile name.
  155. * @param desc the name of the color space
  156. * @return the requested color space or null if it wasn't found
  157. */
  158. public PDFICCBasedColorSpace getICCColorSpaceByProfileName(String desc) {
  159. PDFICCBasedColorSpace cs = this.iccColorSpaces.get(desc);
  160. return cs;
  161. }
  162. /**
  163. * Returns a color space by name.
  164. * @param name the name of the color space
  165. * @return the requested color space or null if it wasn't found
  166. */
  167. public PDFColorSpace getColorSpace(PDFName name) {
  168. for (Map.Entry<LazyName, PDFColorSpace> x : colorSpaces.entrySet()) {
  169. if (x.getKey().getName().equals(name)) {
  170. return x.getValue();
  171. }
  172. }
  173. return null;
  174. }
  175. /**
  176. * Add a named property.
  177. *
  178. * @param name name of property
  179. * @param property reference to property value
  180. */
  181. public void addProperty(String name, PDFReference property) {
  182. this.properties.put(name, property);
  183. }
  184. /**
  185. * Get a named property.
  186. *
  187. * @param name name of property
  188. */
  189. public PDFReference getProperty(String name) {
  190. return this.properties.get(name);
  191. }
  192. @Override
  193. public int output(OutputStream stream) throws IOException {
  194. populateDictionary();
  195. return super.output(stream);
  196. }
  197. private void populateDictionary() {
  198. if (parent != null && parent.fontsObj != null) {
  199. put("Font", parent.fontsObj);
  200. }
  201. if (!this.fonts.isEmpty() || (parent != null && !parent.fonts.isEmpty())) {
  202. PDFDictionary dict = new PDFDictionary(this);
  203. /* construct PDF dictionary of font object references */
  204. for (Map.Entry<String, PDFDictionary> entry : fonts.entrySet()) {
  205. dict.put(entry.getKey(), entry.getValue());
  206. }
  207. if (parent != null) {
  208. for (Map.Entry<String, PDFDictionary> entry : parent.fonts.entrySet()) {
  209. dict.put(entry.getKey(), entry.getValue());
  210. }
  211. for (Map.Entry<String, PDFDictionary> entry : parent.fontsObjDict.entrySet()) {
  212. dict.put(entry.getKey(), entry.getValue());
  213. }
  214. }
  215. put("Font", dict);
  216. }
  217. Set<PDFPattern> patterns = new LinkedHashSet<PDFPattern>();
  218. Set<PDFShading> shadings = new LinkedHashSet<PDFShading>();
  219. Set<PDFGState> gstates = new LinkedHashSet<PDFGState>();
  220. for (PDFResourceContext c : contexts) {
  221. xObjects.addAll(c.getXObjects());
  222. patterns.addAll(c.getPatterns());
  223. shadings.addAll(c.getShadings());
  224. gstates.addAll(c.getGStates());
  225. }
  226. if (parent != null) {
  227. xObjects.addAll(parent.xObjects);
  228. for (PDFResourceContext c : parent.contexts) {
  229. patterns.addAll(c.getPatterns());
  230. shadings.addAll(c.getShadings());
  231. gstates.addAll(c.getGStates());
  232. }
  233. }
  234. if (!shadings.isEmpty()) {
  235. PDFDictionary dict = (PDFDictionary) get("Shading");
  236. if (dict == null) {
  237. dict = new PDFDictionary(this);
  238. }
  239. for (PDFShading shading : shadings) {
  240. dict.put(shading.getName(), shading);
  241. }
  242. put("Shading", dict);
  243. }
  244. if (!patterns.isEmpty()) {
  245. PDFDictionary dict = (PDFDictionary) get("Pattern");
  246. if (dict == null) {
  247. dict = new PDFDictionary(this);
  248. }
  249. for (PDFPattern pattern : patterns) {
  250. dict.put(pattern.getName(), pattern);
  251. }
  252. put("Pattern", dict);
  253. }
  254. PDFArray procset = new PDFArray(this);
  255. procset.add(new PDFName("PDF"));
  256. procset.add(new PDFName("ImageB"));
  257. procset.add(new PDFName("ImageC"));
  258. procset.add(new PDFName("Text"));
  259. put("ProcSet", procset);
  260. if (!xObjects.isEmpty()) {
  261. PDFDictionary dict = (PDFDictionary) get("XObject");
  262. if (dict == null) {
  263. dict = new PDFDictionary(this);
  264. }
  265. for (PDFXObject xObject : xObjects) {
  266. dict.put(xObject.getName().toString(), xObject);
  267. }
  268. put("XObject", dict);
  269. }
  270. if (!gstates.isEmpty()) {
  271. PDFDictionary dict = (PDFDictionary) get("ExtGState");
  272. if (dict == null) {
  273. dict = new PDFDictionary(this);
  274. }
  275. for (PDFGState gstate : gstates) {
  276. dict.put(gstate.getName(), gstate);
  277. }
  278. put("ExtGState", dict);
  279. }
  280. if (!this.colorSpaces.isEmpty() || (parent != null && !parent.colorSpaces.isEmpty())) {
  281. PDFDictionary dict = (PDFDictionary)this.get("ColorSpace");
  282. if (dict == null) {
  283. dict = new PDFDictionary(this);
  284. }
  285. if (parent != null) {
  286. for (PDFColorSpace colorSpace : parent.colorSpaces.values()) {
  287. dict.put(colorSpace.getName(), colorSpace);
  288. }
  289. }
  290. for (PDFColorSpace colorSpace : colorSpaces.values()) {
  291. dict.put(colorSpace.getName(), colorSpace);
  292. }
  293. put("ColorSpace", dict);
  294. }
  295. if (!properties.isEmpty()) {
  296. PDFDictionary dict = new PDFDictionary(this);
  297. for (Map.Entry<String, PDFReference> stringPDFReferenceEntry : properties.entrySet()) {
  298. dict.put(stringPDFReferenceEntry.getKey(), stringPDFReferenceEntry.getValue());
  299. }
  300. put("Properties", dict);
  301. }
  302. }
  303. @Override
  304. public void getChildren(Set<PDFObject> children) {
  305. getChildren(children, false);
  306. }
  307. private void getChildren(Set<PDFObject> children, boolean isParent) {
  308. super.getChildren(children);
  309. for (PDFDictionary f : fonts.values()) {
  310. children.add(f);
  311. f.getChildren(children);
  312. }
  313. for (PDFResourceContext c : contexts) {
  314. for (PDFXObject x : c.getXObjects()) {
  315. children.add(x);
  316. x.getChildren(children);
  317. }
  318. for (PDFPattern x : c.getPatterns()) {
  319. children.add(x);
  320. x.getChildren(children);
  321. }
  322. for (PDFShading x : c.getShadings()) {
  323. children.add(x);
  324. x.getChildren(children);
  325. }
  326. for (PDFGState x : c.getGStates()) {
  327. children.add(x);
  328. x.getChildren(children);
  329. }
  330. }
  331. if (!isParent) {
  332. for (PDFColorSpace x : colorSpaces.values()) {
  333. children.add((PDFObject)x);
  334. ((PDFObject)x).getChildren(children);
  335. }
  336. }
  337. if (parent != null) {
  338. parent.getChildren(children, true);
  339. }
  340. }
  341. }