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.

AbstractBitmapDocumentHandler.java 13KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338
  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.bitmap;
  19. import java.awt.Dimension;
  20. import java.awt.Graphics2D;
  21. import java.awt.RenderingHints;
  22. import java.awt.geom.Point2D;
  23. import java.awt.image.BufferedImage;
  24. import java.io.IOException;
  25. import java.io.OutputStream;
  26. import java.util.Map;
  27. import org.apache.commons.io.IOUtils;
  28. import org.apache.commons.logging.Log;
  29. import org.apache.commons.logging.LogFactory;
  30. import org.apache.xmlgraphics.image.writer.ImageWriter;
  31. import org.apache.xmlgraphics.image.writer.ImageWriterRegistry;
  32. import org.apache.xmlgraphics.image.writer.MultiImageWriter;
  33. import org.apache.fop.apps.FopFactoryConfig;
  34. import org.apache.fop.fonts.FontInfo;
  35. import org.apache.fop.render.intermediate.AbstractBinaryWritingIFDocumentHandler;
  36. import org.apache.fop.render.intermediate.IFContext;
  37. import org.apache.fop.render.intermediate.IFDocumentHandlerConfigurator;
  38. import org.apache.fop.render.intermediate.IFException;
  39. import org.apache.fop.render.intermediate.IFPainter;
  40. import org.apache.fop.render.java2d.Java2DPainter;
  41. import org.apache.fop.render.java2d.Java2DUtil;
  42. /**
  43. * Abstract {@link org.apache.fop.render.intermediate.IFDocumentHandler} implementation
  44. * for producing bitmap images.
  45. */
  46. public abstract class AbstractBitmapDocumentHandler extends AbstractBinaryWritingIFDocumentHandler {
  47. /** logging instance */
  48. private static Log log = LogFactory.getLog(AbstractBitmapDocumentHandler.class);
  49. /**
  50. * Rendering Options key for the controlling the required bitmap size to create.
  51. * This is used to create thumbnails, for example. If used, the target resolution is ignored.
  52. * Value type: java.awt.Dimension (size in pixels)
  53. */
  54. public static final String TARGET_BITMAP_SIZE = "target-bitmap-size";
  55. private ImageWriter imageWriter;
  56. private MultiImageWriter multiImageWriter;
  57. /** Helper class for generating multiple files */
  58. private MultiFileRenderingUtil multiFileUtil;
  59. private int pageCount;
  60. private Dimension currentPageDimensions;
  61. private BufferedImage currentImage;
  62. private BitmapRenderingSettings bitmapSettings = new BitmapRenderingSettings();
  63. private double scaleFactor = 1.0;
  64. private Dimension targetBitmapSize;
  65. /**
  66. * Default constructor.
  67. */
  68. public AbstractBitmapDocumentHandler(IFContext context) {
  69. super(context);
  70. //Set target resolution
  71. int dpi = Math.round(context.getUserAgent().getTargetResolution());
  72. getSettings().setResolution(dpi);
  73. Map renderingOptions = getUserAgent().getRendererOptions();
  74. setTargetBitmapSize((Dimension)renderingOptions.get(TARGET_BITMAP_SIZE));
  75. }
  76. /** {@inheritDoc} */
  77. public boolean supportsPagesOutOfOrder() {
  78. return false;
  79. }
  80. /** {@inheritDoc} */
  81. public abstract String getMimeType();
  82. /**
  83. * Returns the default file extension for the supported image type.
  84. * @return the default file extension (ex. "png")
  85. */
  86. public abstract String getDefaultExtension();
  87. /** {@inheritDoc} */
  88. public abstract IFDocumentHandlerConfigurator getConfigurator();
  89. /**
  90. * Returns the settings for bitmap rendering.
  91. * @return the settings object
  92. */
  93. public BitmapRenderingSettings getSettings() {
  94. return this.bitmapSettings;
  95. }
  96. /** {@inheritDoc} */
  97. public void setDefaultFontInfo(FontInfo fontInfo) {
  98. FontInfo fi = Java2DUtil.buildDefaultJava2DBasedFontInfo(fontInfo, getUserAgent());
  99. setFontInfo(fi);
  100. }
  101. /**
  102. * Sets the target bitmap size (in pixels) of the bitmap that should be produced. Normally,
  103. * the bitmap size is calculated automatically based on the page size and the target
  104. * resolution. But for example, if you want to create thumbnails or small preview bitmaps
  105. * from pages it is more practical (and efficient) to set the required bitmap size.
  106. * @param size the target bitmap size (in pixels)
  107. */
  108. public void setTargetBitmapSize(Dimension size) {
  109. this.targetBitmapSize = size;
  110. }
  111. //----------------------------------------------------------------------------------------------
  112. /** {@inheritDoc} */
  113. public void startDocument() throws IFException {
  114. super.startDocument();
  115. try {
  116. // Creates writer
  117. this.imageWriter = ImageWriterRegistry.getInstance().getWriterFor(getMimeType());
  118. if (this.imageWriter == null) {
  119. BitmapRendererEventProducer eventProducer
  120. = BitmapRendererEventProducer.Provider.get(
  121. getUserAgent().getEventBroadcaster());
  122. eventProducer.noImageWriterFound(this, getMimeType());
  123. }
  124. if (this.imageWriter.supportsMultiImageWriter()) {
  125. this.multiImageWriter = this.imageWriter.createMultiImageWriter(outputStream);
  126. } else {
  127. this.multiFileUtil = new MultiFileRenderingUtil(getDefaultExtension(),
  128. getUserAgent().getOutputFile());
  129. }
  130. this.pageCount = 0;
  131. } catch (IOException e) {
  132. throw new IFException("I/O error in startDocument()", e);
  133. }
  134. }
  135. /** {@inheritDoc} */
  136. public void endDocumentHeader() throws IFException {
  137. }
  138. /** {@inheritDoc} */
  139. public void endDocument() throws IFException {
  140. try {
  141. if (this.multiImageWriter != null) {
  142. this.multiImageWriter.close();
  143. }
  144. this.multiImageWriter = null;
  145. this.imageWriter = null;
  146. } catch (IOException ioe) {
  147. throw new IFException("I/O error in endDocument()", ioe);
  148. }
  149. super.endDocument();
  150. }
  151. /** {@inheritDoc} */
  152. public void startPageSequence(String id) throws IFException {
  153. //nop
  154. }
  155. /** {@inheritDoc} */
  156. public void endPageSequence() throws IFException {
  157. //nop
  158. }
  159. /** {@inheritDoc} */
  160. public void startPage(int index, String name, String pageMasterName, Dimension size)
  161. throws IFException {
  162. this.pageCount++;
  163. this.currentPageDimensions = new Dimension(size);
  164. }
  165. /** {@inheritDoc} */
  166. public IFPainter startPageContent() throws IFException {
  167. int bitmapWidth;
  168. int bitmapHeight;
  169. double scale;
  170. Point2D offset = null;
  171. if (targetBitmapSize != null) {
  172. //Fit the generated page proportionally into the given rectangle (in pixels)
  173. double scale2w = 1000 * targetBitmapSize.width
  174. / this.currentPageDimensions.getWidth();
  175. double scale2h = 1000 * targetBitmapSize.height
  176. / this.currentPageDimensions.getHeight();
  177. bitmapWidth = targetBitmapSize.width;
  178. bitmapHeight = targetBitmapSize.height;
  179. //Centering the page in the given bitmap
  180. offset = new Point2D.Double();
  181. if (scale2w < scale2h) {
  182. scale = scale2w;
  183. double h = this.currentPageDimensions.height * scale / 1000;
  184. offset.setLocation(0, (bitmapHeight - h) / 2.0);
  185. } else {
  186. scale = scale2h;
  187. double w = this.currentPageDimensions.width * scale / 1000;
  188. offset.setLocation((bitmapWidth - w) / 2.0, 0);
  189. }
  190. } else {
  191. //Normal case: just scale according to the target resolution
  192. scale = scaleFactor
  193. * getUserAgent().getTargetResolution()
  194. / FopFactoryConfig.DEFAULT_TARGET_RESOLUTION;
  195. bitmapWidth = (int) ((this.currentPageDimensions.width * scale / 1000f) + 0.5f);
  196. bitmapHeight = (int) ((this.currentPageDimensions.height * scale / 1000f) + 0.5f);
  197. }
  198. //Set up bitmap to paint on
  199. if (currentImage == null || currentImage.getWidth() != bitmapWidth
  200. || currentImage.getHeight() != bitmapHeight) {
  201. currentImage = createBufferedImage(bitmapWidth, bitmapHeight);
  202. }
  203. Graphics2D graphics2D = this.currentImage.createGraphics();
  204. // draw page background
  205. if (!getSettings().hasTransparentPageBackground()) {
  206. graphics2D.setBackground(getSettings().getPageBackgroundColor());
  207. graphics2D.setPaint(getSettings().getPageBackgroundColor());
  208. graphics2D.fillRect(0, 0, bitmapWidth, bitmapHeight);
  209. }
  210. //Set rendering hints
  211. graphics2D.setRenderingHint(RenderingHints.KEY_FRACTIONALMETRICS,
  212. RenderingHints.VALUE_FRACTIONALMETRICS_ON);
  213. if (getSettings().isAntiAliasingEnabled()
  214. && this.currentImage.getColorModel().getPixelSize() > 1) {
  215. graphics2D.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
  216. RenderingHints.VALUE_ANTIALIAS_ON);
  217. graphics2D.setRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING,
  218. RenderingHints.VALUE_TEXT_ANTIALIAS_ON);
  219. } else {
  220. graphics2D.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
  221. RenderingHints.VALUE_ANTIALIAS_OFF);
  222. graphics2D.setRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING,
  223. RenderingHints.VALUE_TEXT_ANTIALIAS_OFF);
  224. }
  225. if (getSettings().isQualityRenderingEnabled()) {
  226. graphics2D.setRenderingHint(RenderingHints.KEY_RENDERING,
  227. RenderingHints.VALUE_RENDER_QUALITY);
  228. } else {
  229. graphics2D.setRenderingHint(RenderingHints.KEY_RENDERING,
  230. RenderingHints.VALUE_RENDER_SPEED);
  231. }
  232. graphics2D.setRenderingHint(RenderingHints.KEY_STROKE_CONTROL,
  233. RenderingHints.VALUE_STROKE_PURE);
  234. //Set up initial coordinate system for the page
  235. if (offset != null) {
  236. graphics2D.translate(offset.getX(), offset.getY());
  237. }
  238. graphics2D.scale(scale / 1000f, scale / 1000f);
  239. return new Java2DPainter(graphics2D, getContext(), getFontInfo(), this);
  240. }
  241. /**
  242. * Creates a new BufferedImage.
  243. * @param bitmapWidth the desired width in pixels
  244. * @param bitmapHeight the desired height in pixels
  245. * @return the new BufferedImage instance
  246. */
  247. protected BufferedImage createBufferedImage(int bitmapWidth, int bitmapHeight) {
  248. return new BufferedImage(bitmapWidth, bitmapHeight, getSettings().getBufferedImageType());
  249. }
  250. /** {@inheritDoc} */
  251. public void endPageContent() throws IFException {
  252. try {
  253. if (this.multiImageWriter == null) {
  254. switch (this.pageCount) {
  255. case 1:
  256. this.imageWriter.writeImage(
  257. this.currentImage, this.outputStream,
  258. getSettings().getWriterParams());
  259. IOUtils.closeQuietly(this.outputStream);
  260. this.outputStream = null;
  261. break;
  262. default:
  263. OutputStream out = this.multiFileUtil.createOutputStream(this.pageCount - 1);
  264. if (out == null) {
  265. BitmapRendererEventProducer eventProducer
  266. = BitmapRendererEventProducer.Provider.get(
  267. getUserAgent().getEventBroadcaster());
  268. eventProducer.stoppingAfterFirstPageNoFilename(this);
  269. } else {
  270. try {
  271. this.imageWriter.writeImage(
  272. this.currentImage, out,
  273. getSettings().getWriterParams());
  274. } finally {
  275. IOUtils.closeQuietly(out);
  276. }
  277. }
  278. }
  279. } else {
  280. this.multiImageWriter.writeImage(this.currentImage,
  281. getSettings().getWriterParams());
  282. }
  283. } catch (IOException ioe) {
  284. throw new IFException("I/O error while encoding BufferedImage", ioe);
  285. }
  286. }
  287. /** {@inheritDoc} */
  288. public void endPage() throws IFException {
  289. this.currentPageDimensions = null;
  290. }
  291. /** {@inheritDoc} */
  292. public void handleExtensionObject(Object extension) throws IFException {
  293. log.debug("Don't know how to handle extension object. Ignoring: "
  294. + extension + " (" + extension.getClass().getName() + ")");
  295. }
  296. }