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.

PCLDocumentHandler.java 14KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395
  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.pcl;
  19. import java.awt.Color;
  20. import java.awt.Dimension;
  21. import java.awt.Graphics2D;
  22. import java.awt.Rectangle;
  23. import java.awt.RenderingHints;
  24. import java.awt.image.BufferedImage;
  25. import java.io.BufferedInputStream;
  26. import java.io.BufferedOutputStream;
  27. import java.io.ByteArrayOutputStream;
  28. import java.io.IOException;
  29. import java.io.InputStream;
  30. import java.io.OutputStream;
  31. import java.net.URI;
  32. import java.util.Map;
  33. import org.apache.commons.io.IOUtils;
  34. import org.apache.commons.logging.Log;
  35. import org.apache.commons.logging.LogFactory;
  36. import org.apache.xmlgraphics.io.TempResourceURIGenerator;
  37. import org.apache.xmlgraphics.util.UnitConv;
  38. import org.apache.fop.apps.FopFactoryConfig;
  39. import org.apache.fop.apps.MimeConstants;
  40. import org.apache.fop.fonts.FontInfo;
  41. import org.apache.fop.fonts.Typeface;
  42. import org.apache.fop.render.intermediate.AbstractBinaryWritingIFDocumentHandler;
  43. import org.apache.fop.render.intermediate.IFContext;
  44. import org.apache.fop.render.intermediate.IFDocumentHandlerConfigurator;
  45. import org.apache.fop.render.intermediate.IFException;
  46. import org.apache.fop.render.intermediate.IFPainter;
  47. import org.apache.fop.render.java2d.Java2DPainter;
  48. import org.apache.fop.render.java2d.Java2DUtil;
  49. import org.apache.fop.render.pcl.PCLRendererConfig.PCLRendererConfigParser;
  50. import org.apache.fop.render.pcl.extensions.PCLElementMapping;
  51. import org.apache.fop.render.pcl.fonts.PCLSoftFontManager;
  52. /**
  53. * {@link org.apache.fop.render.intermediate.IFDocumentHandler} implementation
  54. * that produces PCL 5.
  55. */
  56. public class PCLDocumentHandler extends AbstractBinaryWritingIFDocumentHandler
  57. implements PCLConstants {
  58. /** logging instance */
  59. private static Log log = LogFactory.getLog(PCLDocumentHandler.class);
  60. /** the temporary file in case of two-pass processing */
  61. private URI tempURI;
  62. private static final TempResourceURIGenerator TEMP_URI_GENERATOR = new TempResourceURIGenerator("pcl-optimize");
  63. /** Utility class for handling all sorts of peripheral tasks around PCL generation. */
  64. protected PCLRenderingUtil pclUtil;
  65. /** The PCL generator */
  66. private PCLGenerator gen;
  67. private PCLPageDefinition currentPageDefinition;
  68. /** contains the pageWith of the last printed page */
  69. private long pageWidth;
  70. /** contains the pageHeight of the last printed page */
  71. private long pageHeight;
  72. /** the current page image (only set when all-bitmap painting is activated) */
  73. private BufferedImage currentImage;
  74. /**
  75. * Default constructor.
  76. */
  77. public PCLDocumentHandler(IFContext context) {
  78. super(context);
  79. this.pclUtil = new PCLRenderingUtil(context.getUserAgent());
  80. }
  81. /** {@inheritDoc} */
  82. public boolean supportsPagesOutOfOrder() {
  83. return false;
  84. }
  85. /** {@inheritDoc} */
  86. public String getMimeType() {
  87. return MimeConstants.MIME_PCL;
  88. }
  89. /** {@inheritDoc} */
  90. public IFDocumentHandlerConfigurator getConfigurator() {
  91. return new PCLRendererConfigurator(getUserAgent(), new PCLRendererConfigParser());
  92. }
  93. /** {@inheritDoc} */
  94. @Override
  95. public void setDefaultFontInfo(FontInfo fontInfo) {
  96. FontInfo fi = Java2DUtil.buildDefaultJava2DBasedFontInfo(fontInfo, getUserAgent());
  97. setFontInfo(fi);
  98. }
  99. PCLRenderingUtil getPCLUtil() {
  100. return this.pclUtil;
  101. }
  102. PCLGenerator getPCLGenerator() {
  103. return this.gen;
  104. }
  105. /** @return the target resolution */
  106. protected int getResolution() {
  107. int resolution = Math.round(getUserAgent().getTargetResolution());
  108. if (resolution <= 300) {
  109. return 300;
  110. } else {
  111. return 600;
  112. }
  113. }
  114. //----------------------------------------------------------------------------------------------
  115. /** {@inheritDoc} */
  116. @Override
  117. public void startDocument() throws IFException {
  118. super.startDocument();
  119. try {
  120. final OutputStream out;
  121. if (pclUtil.isOptimizeResources()) {
  122. tempURI = TEMP_URI_GENERATOR.generate();
  123. out = new BufferedOutputStream(getUserAgent().getResourceResolver().getOutputStream(tempURI));
  124. } else {
  125. out = this.outputStream;
  126. }
  127. this.gen = new PCLGenerator(out, getResolution());
  128. this.gen.setDitheringQuality(pclUtil.getDitheringQuality());
  129. if (!pclUtil.isPJLDisabled()) {
  130. gen.universalEndOfLanguage();
  131. gen.writeText("@PJL COMMENT Produced by " + getUserAgent().getProducer() + "\n");
  132. if (getUserAgent().getTitle() != null) {
  133. gen.writeText("@PJL JOB NAME = \"" + getUserAgent().getTitle() + "\"\n");
  134. }
  135. gen.writeText("@PJL SET RESOLUTION = " + getResolution() + "\n");
  136. gen.writeText("@PJL ENTER LANGUAGE = PCL\n");
  137. }
  138. gen.resetPrinter();
  139. gen.setUnitOfMeasure(getResolution());
  140. gen.setRasterGraphicsResolution(getResolution());
  141. } catch (IOException e) {
  142. throw new IFException("I/O error in startDocument()", e);
  143. }
  144. }
  145. /** {@inheritDoc} */
  146. @Override
  147. public void endDocumentHeader() throws IFException {
  148. }
  149. /** {@inheritDoc} */
  150. @Override
  151. public void endDocument() throws IFException {
  152. try {
  153. gen.separateJobs();
  154. gen.resetPrinter();
  155. if (!pclUtil.isPJLDisabled()) {
  156. gen.universalEndOfLanguage();
  157. }
  158. if (pclUtil.isOptimizeResources()) {
  159. IOUtils.closeQuietly(gen.getOutputStream());
  160. rewritePCLFile();
  161. }
  162. } catch (IOException ioe) {
  163. throw new IFException("I/O error in endDocument()", ioe);
  164. }
  165. super.endDocument();
  166. }
  167. private void rewritePCLFile() throws IOException {
  168. InputStream in = new BufferedInputStream(getUserAgent().getResourceResolver().getResource(tempURI));
  169. long offset = 0;
  170. for (Map.Entry<PCLSoftFontManager, Map<Typeface, Long>> fontManagerMapEntry : gen.fontManagerMap.entrySet()) {
  171. PCLSoftFontManager softFontManager = fontManagerMapEntry.getKey();
  172. for (Map.Entry<Typeface, Long> fontEntry : fontManagerMapEntry.getValue().entrySet()) {
  173. ByteArrayOutputStream fontData = softFontManager.makeSoftFont(fontEntry.getKey(), null);
  174. long pos = fontEntry.getValue();
  175. copy(in, pos - offset);
  176. outputStream.write(fontData.toByteArray());
  177. offset = pos;
  178. }
  179. }
  180. copy(in, Long.MAX_VALUE);
  181. this.outputStream.flush();
  182. IOUtils.closeQuietly(in);
  183. }
  184. private void copy(InputStream is, long len) throws IOException {
  185. while (len > 0) {
  186. int bufsize = (int) Math.min(1024, len);
  187. byte[] buf = new byte[bufsize];
  188. if (is.read(buf) == -1) {
  189. return;
  190. }
  191. outputStream.write(buf);
  192. len -= bufsize;
  193. }
  194. }
  195. /** {@inheritDoc} */
  196. public void startPageSequence(String id) throws IFException {
  197. //nop
  198. }
  199. /** {@inheritDoc} */
  200. public void endPageSequence() throws IFException {
  201. //nop
  202. }
  203. /** {@inheritDoc} */
  204. public void startPage(int index, String name, String pageMasterName, Dimension size)
  205. throws IFException {
  206. try {
  207. //Paper source
  208. Object paperSource = getContext().getForeignAttribute(
  209. PCLElementMapping.PCL_PAPER_SOURCE);
  210. if (paperSource != null) {
  211. gen.selectPaperSource(Integer.parseInt(paperSource.toString()));
  212. }
  213. //Output bin
  214. Object outputBin = getContext().getForeignAttribute(
  215. PCLElementMapping.PCL_OUTPUT_BIN);
  216. if (outputBin != null) {
  217. gen.selectOutputBin(Integer.parseInt(outputBin.toString()));
  218. }
  219. // Is Page duplex?
  220. Object pageDuplex = getContext().getForeignAttribute(
  221. PCLElementMapping.PCL_DUPLEX_MODE);
  222. if (pageDuplex != null) {
  223. gen.selectDuplexMode(Integer.parseInt(pageDuplex.toString()));
  224. }
  225. //Page size
  226. final long pagewidth = size.width;
  227. final long pageheight = size.height;
  228. selectPageFormat(pagewidth, pageheight);
  229. } catch (IOException ioe) {
  230. throw new IFException("I/O error in startPage()", ioe);
  231. }
  232. }
  233. /** {@inheritDoc} */
  234. public IFPainter startPageContent() throws IFException {
  235. if (pclUtil.getRenderingMode() == PCLRenderingMode.BITMAP) {
  236. return createAllBitmapPainter();
  237. } else {
  238. return new PCLPainter(this, this.currentPageDefinition);
  239. }
  240. }
  241. private IFPainter createAllBitmapPainter() {
  242. double scale = gen.getMaximumBitmapResolution()
  243. / FopFactoryConfig.DEFAULT_TARGET_RESOLUTION;
  244. Rectangle printArea = this.currentPageDefinition.getLogicalPageRect();
  245. int bitmapWidth = (int)Math.ceil(
  246. UnitConv.mpt2px(printArea.width, gen.getMaximumBitmapResolution()));
  247. int bitmapHeight = (int)Math.ceil(
  248. UnitConv.mpt2px(printArea.height, gen.getMaximumBitmapResolution()));
  249. this.currentImage = createBufferedImage(bitmapWidth, bitmapHeight);
  250. Graphics2D graphics2D = this.currentImage.createGraphics();
  251. if (!PCLGenerator.isJAIAvailable()) {
  252. RenderingHints hints = new RenderingHints(null);
  253. //These hints don't seem to make a difference :-( Not seeing any dithering on Sun Java.
  254. hints.put(RenderingHints.KEY_DITHERING,
  255. RenderingHints.VALUE_DITHER_ENABLE);
  256. graphics2D.addRenderingHints(hints);
  257. }
  258. //Ensure white page background
  259. graphics2D.setBackground(Color.WHITE);
  260. graphics2D.clearRect(0, 0, bitmapWidth, bitmapHeight);
  261. graphics2D.setRenderingHint(RenderingHints.KEY_FRACTIONALMETRICS,
  262. RenderingHints.VALUE_FRACTIONALMETRICS_ON);
  263. graphics2D.setRenderingHint(RenderingHints.KEY_STROKE_CONTROL,
  264. RenderingHints.VALUE_STROKE_PURE);
  265. graphics2D.scale(scale / 1000f, scale / 1000f);
  266. graphics2D.translate(-printArea.x, -printArea.y);
  267. return new Java2DPainter(graphics2D, getContext(), getFontInfo(), this);
  268. }
  269. private BufferedImage createBufferedImage(int bitmapWidth, int bitmapHeight) {
  270. int bitmapType;
  271. if (PCLGenerator.isJAIAvailable()) {
  272. //TYPE_BYTE_GRAY was used to work around the lack of dithering when using
  273. //TYPE_BYTE_BINARY. Adding RenderingHints didn't help.
  274. bitmapType = BufferedImage.TYPE_BYTE_GRAY;
  275. //bitmapType = BufferedImage.TYPE_INT_RGB; //Use to enable Batik gradients
  276. } else {
  277. bitmapType = BufferedImage.TYPE_BYTE_BINARY;
  278. }
  279. return new BufferedImage(
  280. bitmapWidth, bitmapHeight, bitmapType);
  281. }
  282. /** {@inheritDoc} */
  283. public void endPageContent() throws IFException {
  284. if (this.currentImage != null) {
  285. try {
  286. Rectangle printArea = this.currentPageDefinition.getLogicalPageRect();
  287. gen.setCursorPos(0, 0);
  288. gen.paintBitmap(this.currentImage, printArea.getSize(), true, pclUtil);
  289. } catch (IOException ioe) {
  290. throw new IFException("I/O error while encoding page image", ioe);
  291. } finally {
  292. this.currentImage = null;
  293. }
  294. }
  295. }
  296. /** {@inheritDoc} */
  297. public void endPage() throws IFException {
  298. try {
  299. //Eject page
  300. gen.formFeed();
  301. } catch (IOException ioe) {
  302. throw new IFException("I/O error in endPage()", ioe);
  303. }
  304. }
  305. /** {@inheritDoc} */
  306. public void handleExtensionObject(Object extension) throws IFException {
  307. if (false) {
  308. //TODO Handle extensions
  309. } else {
  310. log.debug("Don't know how to handle extension object. Ignoring: "
  311. + extension + " (" + extension.getClass().getName() + ")");
  312. }
  313. }
  314. private void selectPageFormat(long pagewidth, long pageheight) throws IOException {
  315. //Only set the page format if it changes (otherwise duplex printing won't work)
  316. if ((pagewidth != this.pageWidth) || (pageheight != this.pageHeight)) {
  317. this.pageWidth = pagewidth;
  318. this.pageHeight = pageheight;
  319. this.currentPageDefinition = PCLPageDefinition.getPageDefinition(
  320. pagewidth, pageheight, 1000);
  321. if (this.currentPageDefinition == null) {
  322. this.currentPageDefinition = PCLPageDefinition.getDefaultPageDefinition();
  323. log.warn("Paper type could not be determined. Falling back to: "
  324. + this.currentPageDefinition.getName());
  325. }
  326. if (log.isDebugEnabled()) {
  327. log.debug("page size: " + currentPageDefinition.getPhysicalPageSize());
  328. log.debug("logical page: " + currentPageDefinition.getLogicalPageRect());
  329. }
  330. if (this.currentPageDefinition.isLandscapeFormat()) {
  331. gen.writeCommand("&l1O"); //Landscape Orientation
  332. } else {
  333. gen.writeCommand("&l0O"); //Portrait Orientation
  334. }
  335. gen.selectPageSize(this.currentPageDefinition.getSelector());
  336. gen.clearHorizontalMargins();
  337. gen.setTopMargin(0);
  338. }
  339. }
  340. }