/*
 * Licensed to the Apache Software Foundation (ASF) under one or more
 * contributor license agreements.  See the NOTICE file distributed with
 * this work for additional information regarding copyright ownership.
 * The ASF licenses this file to You under the Apache License, Version 2.0
 * (the "License"); you may not use this file except in compliance with
 * the License.  You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

/* $Id: PCLDocumentHandler.java 1756387 2016-08-15 14:15:27Z ssteiner $ */

package org.apache.fop.render.pcl;

import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics2D;
import java.awt.Rectangle;
import java.awt.RenderingHints;
import java.awt.image.BufferedImage;
import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.URI;
import java.util.Map;

import org.apache.commons.io.IOUtils;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

import org.apache.xmlgraphics.io.TempResourceURIGenerator;
import org.apache.xmlgraphics.util.UnitConv;

import org.apache.fop.apps.FopFactoryConfig;
import org.apache.fop.apps.MimeConstants;
import org.apache.fop.fonts.FontInfo;
import org.apache.fop.fonts.Typeface;
import org.apache.fop.render.intermediate.AbstractBinaryWritingIFDocumentHandler;
import org.apache.fop.render.intermediate.IFContext;
import org.apache.fop.render.intermediate.IFDocumentHandlerConfigurator;
import org.apache.fop.render.intermediate.IFException;
import org.apache.fop.render.intermediate.IFPainter;
import org.apache.fop.render.java2d.Java2DPainter;
import org.apache.fop.render.java2d.Java2DUtil;
import org.apache.fop.render.pcl.PCLRendererConfig.PCLRendererConfigParser;
import org.apache.fop.render.pcl.extensions.PCLElementMapping;
import org.apache.fop.render.pcl.fonts.PCLSoftFontManager;

IFDocumentHandler implementation that produces PCL 5.
/** * {@link org.apache.fop.render.intermediate.IFDocumentHandler} implementation * that produces PCL 5. */
public class PCLDocumentHandler extends AbstractBinaryWritingIFDocumentHandler implements PCLConstants {
logging instance
/** logging instance */
private static Log log = LogFactory.getLog(PCLDocumentHandler.class);
the temporary file in case of two-pass processing
/** the temporary file in case of two-pass processing */
private URI tempURI; private static final TempResourceURIGenerator TEMP_URI_GENERATOR = new TempResourceURIGenerator("pcl-optimize");
Utility class for handling all sorts of peripheral tasks around PCL generation.
/** Utility class for handling all sorts of peripheral tasks around PCL generation. */
protected PCLRenderingUtil pclUtil;
The PCL generator
/** The PCL generator */
private PCLGenerator gen; private PCLPageDefinition currentPageDefinition;
contains the pageWith of the last printed page
/** contains the pageWith of the last printed page */
private long pageWidth;
contains the pageHeight of the last printed page
/** contains the pageHeight of the last printed page */
private long pageHeight;
the current page image (only set when all-bitmap painting is activated)
/** the current page image (only set when all-bitmap painting is activated) */
private BufferedImage currentImage;
Default constructor.
/** * Default constructor. */
public PCLDocumentHandler(IFContext context) { super(context); this.pclUtil = new PCLRenderingUtil(context.getUserAgent()); }
{@inheritDoc}
/** {@inheritDoc} */
public boolean supportsPagesOutOfOrder() { return false; }
{@inheritDoc}
/** {@inheritDoc} */
public String getMimeType() { return MimeConstants.MIME_PCL; }
{@inheritDoc}
/** {@inheritDoc} */
public IFDocumentHandlerConfigurator getConfigurator() { return new PCLRendererConfigurator(getUserAgent(), new PCLRendererConfigParser()); }
{@inheritDoc}
/** {@inheritDoc} */
@Override public void setDefaultFontInfo(FontInfo fontInfo) { FontInfo fi = Java2DUtil.buildDefaultJava2DBasedFontInfo(fontInfo, getUserAgent()); setFontInfo(fi); } PCLRenderingUtil getPCLUtil() { return this.pclUtil; } PCLGenerator getPCLGenerator() { return this.gen; }
Returns:the target resolution
/** @return the target resolution */
protected int getResolution() { int resolution = Math.round(getUserAgent().getTargetResolution()); if (resolution <= 300) { return 300; } else { return 600; } } //----------------------------------------------------------------------------------------------
{@inheritDoc}
/** {@inheritDoc} */
@Override public void startDocument() throws IFException { super.startDocument(); try { final OutputStream out; if (pclUtil.isOptimizeResources()) { tempURI = TEMP_URI_GENERATOR.generate(); out = new BufferedOutputStream(getUserAgent().getResourceResolver().getOutputStream(tempURI)); } else { out = this.outputStream; } this.gen = new PCLGenerator(out, getResolution()); this.gen.setDitheringQuality(pclUtil.getDitheringQuality()); if (!pclUtil.isPJLDisabled()) { gen.universalEndOfLanguage(); gen.writeText("@PJL COMMENT Produced by " + getUserAgent().getProducer() + "\n"); if (getUserAgent().getTitle() != null) { gen.writeText("@PJL JOB NAME = \"" + getUserAgent().getTitle() + "\"\n"); } gen.writeText("@PJL SET RESOLUTION = " + getResolution() + "\n"); gen.writeText("@PJL ENTER LANGUAGE = PCL\n"); } gen.resetPrinter(); gen.setUnitOfMeasure(getResolution()); gen.setRasterGraphicsResolution(getResolution()); } catch (IOException e) { throw new IFException("I/O error in startDocument()", e); } }
{@inheritDoc}
/** {@inheritDoc} */
@Override public void endDocumentHeader() throws IFException { }
{@inheritDoc}
/** {@inheritDoc} */
@Override public void endDocument() throws IFException { try { gen.separateJobs(); gen.resetPrinter(); if (!pclUtil.isPJLDisabled()) { gen.universalEndOfLanguage(); } if (pclUtil.isOptimizeResources()) { IOUtils.closeQuietly(gen.getOutputStream()); rewritePCLFile(); } } catch (IOException ioe) { throw new IFException("I/O error in endDocument()", ioe); } super.endDocument(); } private void rewritePCLFile() throws IOException { InputStream in = new BufferedInputStream(getUserAgent().getResourceResolver().getResource(tempURI)); long offset = 0; for (Map.Entry<PCLSoftFontManager, Map<Typeface, Long>> fontManagerMapEntry : gen.fontManagerMap.entrySet()) { PCLSoftFontManager softFontManager = fontManagerMapEntry.getKey(); for (Map.Entry<Typeface, Long> fontEntry : fontManagerMapEntry.getValue().entrySet()) { ByteArrayOutputStream fontData = softFontManager.makeSoftFont(fontEntry.getKey(), null); long pos = fontEntry.getValue(); copy(in, pos - offset); outputStream.write(fontData.toByteArray()); offset = pos; } } copy(in, Long.MAX_VALUE); this.outputStream.flush(); IOUtils.closeQuietly(in); } private void copy(InputStream is, long len) throws IOException { while (len > 0) { int bufsize = (int) Math.min(1024, len); byte[] buf = new byte[bufsize]; if (is.read(buf) == -1) { return; } outputStream.write(buf); len -= bufsize; } }
{@inheritDoc}
/** {@inheritDoc} */
public void startPageSequence(String id) throws IFException { //nop }
{@inheritDoc}
/** {@inheritDoc} */
public void endPageSequence() throws IFException { //nop }
{@inheritDoc}
/** {@inheritDoc} */
public void startPage(int index, String name, String pageMasterName, Dimension size) throws IFException { try { //Paper source Object paperSource = getContext().getForeignAttribute( PCLElementMapping.PCL_PAPER_SOURCE); if (paperSource != null) { gen.selectPaperSource(Integer.parseInt(paperSource.toString())); } //Output bin Object outputBin = getContext().getForeignAttribute( PCLElementMapping.PCL_OUTPUT_BIN); if (outputBin != null) { gen.selectOutputBin(Integer.parseInt(outputBin.toString())); } // Is Page duplex? Object pageDuplex = getContext().getForeignAttribute( PCLElementMapping.PCL_DUPLEX_MODE); if (pageDuplex != null) { gen.selectDuplexMode(Integer.parseInt(pageDuplex.toString())); } //Page size final long pagewidth = size.width; final long pageheight = size.height; selectPageFormat(pagewidth, pageheight); } catch (IOException ioe) { throw new IFException("I/O error in startPage()", ioe); } }
{@inheritDoc}
/** {@inheritDoc} */
public IFPainter startPageContent() throws IFException { if (pclUtil.getRenderingMode() == PCLRenderingMode.BITMAP) { return createAllBitmapPainter(); } else { return new PCLPainter(this, this.currentPageDefinition); } } private IFPainter createAllBitmapPainter() { double scale = gen.getMaximumBitmapResolution() / FopFactoryConfig.DEFAULT_TARGET_RESOLUTION; Rectangle printArea = this.currentPageDefinition.getLogicalPageRect(); int bitmapWidth = (int)Math.ceil( UnitConv.mpt2px(printArea.width, gen.getMaximumBitmapResolution())); int bitmapHeight = (int)Math.ceil( UnitConv.mpt2px(printArea.height, gen.getMaximumBitmapResolution())); this.currentImage = createBufferedImage(bitmapWidth, bitmapHeight); Graphics2D graphics2D = this.currentImage.createGraphics(); if (!PCLGenerator.isJAIAvailable()) { RenderingHints hints = new RenderingHints(null); //These hints don't seem to make a difference :-( Not seeing any dithering on Sun Java. hints.put(RenderingHints.KEY_DITHERING, RenderingHints.VALUE_DITHER_ENABLE); graphics2D.addRenderingHints(hints); } //Ensure white page background graphics2D.setBackground(Color.WHITE); graphics2D.clearRect(0, 0, bitmapWidth, bitmapHeight); graphics2D.setRenderingHint(RenderingHints.KEY_FRACTIONALMETRICS, RenderingHints.VALUE_FRACTIONALMETRICS_ON); graphics2D.setRenderingHint(RenderingHints.KEY_STROKE_CONTROL, RenderingHints.VALUE_STROKE_PURE); graphics2D.scale(scale / 1000f, scale / 1000f); graphics2D.translate(-printArea.x, -printArea.y); return new Java2DPainter(graphics2D, getContext(), getFontInfo(), this); } private BufferedImage createBufferedImage(int bitmapWidth, int bitmapHeight) { int bitmapType; if (PCLGenerator.isJAIAvailable()) { //TYPE_BYTE_GRAY was used to work around the lack of dithering when using //TYPE_BYTE_BINARY. Adding RenderingHints didn't help. bitmapType = BufferedImage.TYPE_BYTE_GRAY; //bitmapType = BufferedImage.TYPE_INT_RGB; //Use to enable Batik gradients } else { bitmapType = BufferedImage.TYPE_BYTE_BINARY; } return new BufferedImage( bitmapWidth, bitmapHeight, bitmapType); }
{@inheritDoc}
/** {@inheritDoc} */
public void endPageContent() throws IFException { if (this.currentImage != null) { try { Rectangle printArea = this.currentPageDefinition.getLogicalPageRect(); gen.setCursorPos(0, 0); gen.paintBitmap(this.currentImage, printArea.getSize(), true, pclUtil); } catch (IOException ioe) { throw new IFException("I/O error while encoding page image", ioe); } finally { this.currentImage = null; } } }
{@inheritDoc}
/** {@inheritDoc} */
public void endPage() throws IFException { try { //Eject page gen.formFeed(); } catch (IOException ioe) { throw new IFException("I/O error in endPage()", ioe); } }
{@inheritDoc}
/** {@inheritDoc} */
public void handleExtensionObject(Object extension) throws IFException { if (false) { //TODO Handle extensions } else { log.debug("Don't know how to handle extension object. Ignoring: " + extension + " (" + extension.getClass().getName() + ")"); } } private void selectPageFormat(long pagewidth, long pageheight) throws IOException { //Only set the page format if it changes (otherwise duplex printing won't work) if ((pagewidth != this.pageWidth) || (pageheight != this.pageHeight)) { this.pageWidth = pagewidth; this.pageHeight = pageheight; this.currentPageDefinition = PCLPageDefinition.getPageDefinition( pagewidth, pageheight, 1000); if (this.currentPageDefinition == null) { this.currentPageDefinition = PCLPageDefinition.getDefaultPageDefinition(); log.warn("Paper type could not be determined. Falling back to: " + this.currentPageDefinition.getName()); } if (log.isDebugEnabled()) { log.debug("page size: " + currentPageDefinition.getPhysicalPageSize()); log.debug("logical page: " + currentPageDefinition.getLogicalPageRect()); } if (this.currentPageDefinition.isLandscapeFormat()) { gen.writeCommand("&l1O"); //Landscape Orientation } else { gen.writeCommand("&l0O"); //Portrait Orientation } gen.selectPageSize(this.currentPageDefinition.getSelector()); gen.clearHorizontalMargins(); gen.setTopMargin(0); } } }