Nevar pievienot vairāk kā 25 tēmas Tēmai ir jāsākas ar burtu vai ciparu, tā var saturēt domu zīmes ('-') un var būt līdz 35 simboliem gara.

Java2DRenderer.java 43KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193
  1. /*
  2. * Copyright 1999-2005 The Apache Software Foundation.
  3. *
  4. * Licensed under the Apache License, Version 2.0 (the "License");
  5. * you may not use this file except in compliance with the License.
  6. * You may obtain a copy of the License at
  7. *
  8. * http://www.apache.org/licenses/LICENSE-2.0
  9. *
  10. * Unless required by applicable law or agreed to in writing, software
  11. * distributed under the License is distributed on an "AS IS" BASIS,
  12. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13. * See the License for the specific language governing permissions and
  14. * limitations under the License.
  15. */
  16. /* $Id$ */
  17. package org.apache.fop.render.java2d;
  18. // Java
  19. import java.awt.Color;
  20. import java.awt.Graphics;
  21. import java.awt.Graphics2D;
  22. import java.awt.Rectangle;
  23. import java.awt.RenderingHints;
  24. import java.awt.color.ColorSpace;
  25. import java.awt.geom.AffineTransform;
  26. import java.awt.geom.Line2D;
  27. import java.awt.geom.Rectangle2D;
  28. import java.awt.image.BufferedImage;
  29. import java.awt.image.ColorModel;
  30. import java.awt.image.ComponentColorModel;
  31. import java.awt.image.DataBuffer;
  32. import java.awt.image.DataBufferByte;
  33. import java.awt.image.PixelInterleavedSampleModel;
  34. import java.awt.image.Raster;
  35. import java.awt.image.SampleModel;
  36. import java.awt.image.WritableRaster;
  37. import java.awt.print.PageFormat;
  38. import java.awt.print.Printable;
  39. import java.awt.print.PrinterException;
  40. import java.io.IOException;
  41. import java.io.OutputStream;
  42. import java.util.Iterator;
  43. import java.util.List;
  44. import java.util.Map;
  45. import org.apache.fop.apps.FOPException;
  46. import org.apache.fop.apps.FOUserAgent;
  47. import org.apache.fop.area.Area;
  48. import org.apache.fop.area.Block;
  49. import org.apache.fop.area.BlockViewport;
  50. import org.apache.fop.area.CTM;
  51. import org.apache.fop.area.PageViewport;
  52. import org.apache.fop.area.RegionViewport;
  53. import org.apache.fop.area.Trait;
  54. import org.apache.fop.area.inline.Character;
  55. import org.apache.fop.area.inline.ForeignObject;
  56. import org.apache.fop.area.inline.Image;
  57. import org.apache.fop.area.inline.InlineArea;
  58. import org.apache.fop.area.inline.InlineBlockParent;
  59. import org.apache.fop.area.inline.InlineParent;
  60. import org.apache.fop.area.inline.Leader;
  61. import org.apache.fop.area.inline.Space;
  62. import org.apache.fop.area.inline.TextArea;
  63. import org.apache.fop.area.inline.Viewport;
  64. import org.apache.fop.datatypes.ColorType;
  65. import org.apache.fop.fo.Constants;
  66. import org.apache.fop.fonts.Font;
  67. import org.apache.fop.fonts.FontInfo;
  68. import org.apache.fop.fonts.FontMetrics;
  69. import org.apache.fop.image.FopImage;
  70. import org.apache.fop.image.ImageFactory;
  71. import org.apache.fop.image.XMLImage;
  72. import org.apache.fop.render.AbstractRenderer;
  73. import org.apache.fop.render.Graphics2DAdapter;
  74. import org.apache.fop.render.RendererContext;
  75. import org.apache.fop.render.pdf.CTMHelper;
  76. import org.apache.fop.traits.BorderProps;
  77. import org.w3c.dom.Document;
  78. /**
  79. * The <code>Java2DRenderer</code> class provides the abstract technical
  80. * foundation for all rendering with the Java2D API. Renderers like
  81. * <code>AWTRenderer</code> subclass it and provide the concrete output paths.
  82. * <p>
  83. * A lot of the logic is performed by <code>AbstractRenderer</code>. The
  84. * class-variables <code>currentIPPosition</code> and
  85. * <code>currentBPPosition</code> hold the position of the currently rendered
  86. * area.
  87. * <p>
  88. * <code>Java2DGraphicsState state</code> holds the <code>Graphics2D</code>,
  89. * which is used along the whole rendering. <code>state</code> also acts as a
  90. * stack (<code>state.push()</code> and <code>state.pop()</code>).
  91. * <p>
  92. * The rendering process is basically always the same:
  93. * <p>
  94. * <code>void renderXXXXX(Area area) {
  95. * //calculate the currentPosition
  96. * state.updateFont(name, size, null);
  97. * state.updateColor(ct, false, null);
  98. * state.getGraph.draw(new Shape(args));
  99. * }</code>
  100. *
  101. */
  102. public abstract class Java2DRenderer extends AbstractRenderer implements Printable {
  103. /** The scale factor for the image size, values: ]0 ; 1] */
  104. protected double scaleFactor = 1;
  105. /** The page width in pixels */
  106. protected int pageWidth = 0;
  107. /** The page height in pixels */
  108. protected int pageHeight = 0;
  109. /** List of Viewports */
  110. protected List pageViewportList = new java.util.ArrayList();
  111. /** The 0-based current page number */
  112. private int currentPageNumber = 0;
  113. /** The 0-based total number of rendered pages */
  114. private int numberOfPages;
  115. /** true if antialiasing is set */
  116. protected boolean antialiasing = true;
  117. /** true if qualityRendering is set */
  118. protected boolean qualityRendering = true;
  119. /** The current state, holds a Graphics2D and its context */
  120. protected Java2DGraphicsState state;
  121. /** a Line2D.Float used to draw text decorations and leaders */
  122. protected Line2D.Float line = new Line2D.Float();
  123. /** Font configuration */
  124. protected FontInfo fontInfo;
  125. protected Map fontNames = new java.util.Hashtable();
  126. protected Map fontStyles = new java.util.Hashtable();
  127. /** true if the renderer has finished rendering all the pages */
  128. public boolean renderingDone;
  129. /** Default constructor */
  130. public Java2DRenderer() {
  131. }
  132. /**
  133. * @see org.apache.fop.render.Renderer#setUserAgent(org.apache.fop.apps.FOUserAgent)
  134. */
  135. public void setUserAgent(FOUserAgent foUserAgent) {
  136. super.setUserAgent(foUserAgent);
  137. userAgent.setRendererOverride(this); // for document regeneration
  138. }
  139. /** @return the FOUserAgent */
  140. public FOUserAgent getUserAgent() {
  141. return userAgent;
  142. }
  143. /**
  144. * @see org.apache.fop.render.Renderer#setupFontInfo(org.apache.fop.fonts.FontInfo)
  145. */
  146. public void setupFontInfo(FontInfo inFontInfo) {
  147. // create a temp Image to test font metrics on
  148. fontInfo = inFontInfo;
  149. BufferedImage fontImage = new BufferedImage(100, 100,
  150. BufferedImage.TYPE_INT_RGB);
  151. FontSetup.setup(fontInfo, fontImage.createGraphics());
  152. }
  153. /** @see org.apache.fop.render.Renderer#getGraphics2DAdapter() */
  154. public Graphics2DAdapter getGraphics2DAdapter() {
  155. return new Java2DGraphics2DAdapter(state);
  156. }
  157. /**
  158. * Sets the new scale factor.
  159. * @param newScaleFactor ]0 ; 1]
  160. */
  161. public void setScaleFactor(double newScaleFactor) {
  162. scaleFactor = newScaleFactor;
  163. }
  164. public double getScaleFactor() {
  165. return scaleFactor;
  166. }
  167. public void startRenderer(OutputStream out) throws IOException {
  168. // do nothing by default
  169. }
  170. public void stopRenderer() throws IOException {
  171. log.debug("Java2DRenderer stopped");
  172. renderingDone = true;
  173. numberOfPages = currentPageNumber;
  174. // TODO set all vars to null for gc
  175. if (numberOfPages == 0) {
  176. new FOPException("No page could be rendered");
  177. }
  178. }
  179. /**
  180. * @return The 0-based current page number
  181. */
  182. public int getCurrentPageNumber() {
  183. return currentPageNumber;
  184. }
  185. /**
  186. * @param c the 0-based current page number
  187. */
  188. public void setCurrentPageNumber(int c) {
  189. this.currentPageNumber = c;
  190. }
  191. /**
  192. * @return The 0-based total number of rendered pages
  193. */
  194. public int getNumberOfPages() {
  195. return numberOfPages;
  196. }
  197. /**
  198. * Clears the ViewportList.
  199. * Used if the document is reloaded.
  200. */
  201. public void clearViewportList() {
  202. pageViewportList.clear();
  203. setCurrentPageNumber(0);
  204. }
  205. /**
  206. * This method override only stores the PageViewport in a List. No actual
  207. * rendering is performed here. A renderer override renderPage() to get the
  208. * freshly produced PageViewport, and rendere them on the fly (producing the
  209. * desired BufferedImages by calling getPageImage(), which lazily starts the
  210. * rendering process).
  211. *
  212. * @param pageViewport the <code>PageViewport</code> object supplied by
  213. * the Area Tree
  214. * @see org.apache.fop.render.Renderer
  215. */
  216. public void renderPage(PageViewport pageViewport)
  217. throws IOException, FOPException {
  218. // TODO clone
  219. pageViewportList.add(pageViewport.clone());
  220. currentPageNumber++;
  221. }
  222. /**
  223. * Generates a desired page from the renderer's page viewport list.
  224. *
  225. * @param pageViewport the PageViewport to be rendered
  226. * @return the <code>java.awt.image.BufferedImage</code> corresponding to
  227. * the page or null if the page doesn't exist.
  228. */
  229. public BufferedImage getPageImage(PageViewport pageViewport) {
  230. Rectangle2D bounds = pageViewport.getViewArea();
  231. pageWidth = (int) Math.round(bounds.getWidth() / 1000f);
  232. pageHeight = (int) Math.round(bounds.getHeight() / 1000f);
  233. log.info(
  234. "Rendering Page " + pageViewport.getPageNumberString()
  235. + " (pageWidth " + pageWidth + ", pageHeight "
  236. + pageHeight + ")");
  237. double scaleX = scaleFactor
  238. * (25.4 / FOUserAgent.DEFAULT_TARGET_RESOLUTION)
  239. / userAgent.getTargetPixelUnitToMillimeter();
  240. double scaleY = scaleFactor
  241. * (25.4 / FOUserAgent.DEFAULT_TARGET_RESOLUTION)
  242. / userAgent.getTargetPixelUnitToMillimeter();
  243. int bitmapWidth = (int) ((pageWidth * scaleX) + 0.5);
  244. int bitmapHeight = (int) ((pageHeight * scaleY) + 0.5);
  245. BufferedImage currentPageImage = new BufferedImage(
  246. bitmapWidth, bitmapHeight, BufferedImage.TYPE_INT_ARGB);
  247. // FIXME TYPE_BYTE_BINARY ?
  248. Graphics2D graphics = currentPageImage.createGraphics();
  249. graphics.setRenderingHint(RenderingHints.KEY_FRACTIONALMETRICS,
  250. RenderingHints.VALUE_FRACTIONALMETRICS_ON);
  251. if (antialiasing) {
  252. graphics.setRenderingHint(RenderingHints.KEY_ANTIALIASING,
  253. RenderingHints.VALUE_ANTIALIAS_ON);
  254. graphics.setRenderingHint(RenderingHints.KEY_TEXT_ANTIALIASING,
  255. RenderingHints.VALUE_TEXT_ANTIALIAS_ON);
  256. }
  257. if (qualityRendering) {
  258. graphics.setRenderingHint(RenderingHints.KEY_RENDERING,
  259. RenderingHints.VALUE_RENDER_QUALITY);
  260. }
  261. // transform page based on scale factor supplied
  262. AffineTransform at = graphics.getTransform();
  263. at.scale(scaleX, scaleY);
  264. graphics.setTransform(at);
  265. // draw page frame
  266. graphics.setColor(Color.white);
  267. graphics.fillRect(0, 0, pageWidth, pageHeight);
  268. graphics.setColor(Color.black);
  269. graphics.drawRect(-1, -1, pageWidth + 2, pageHeight + 2);
  270. graphics.drawLine(pageWidth + 2, 0, pageWidth + 2, pageHeight + 2);
  271. graphics.drawLine(pageWidth + 3, 1, pageWidth + 3, pageHeight + 3);
  272. graphics.drawLine(0, pageHeight + 2, pageWidth + 2, pageHeight + 2);
  273. graphics.drawLine(1, pageHeight + 3, pageWidth + 3, pageHeight + 3);
  274. state = new Java2DGraphicsState(graphics, this.fontInfo, at);
  275. // reset the current Positions
  276. currentBPPosition = 0;
  277. currentIPPosition = 0;
  278. // this toggles the rendering of all areas
  279. renderPageAreas(pageViewport.getPage());
  280. return currentPageImage;
  281. }
  282. /**
  283. * Returns the page viewport
  284. * @param pageNum the page number
  285. * @exception FOPException If the page is out of range.
  286. */
  287. public PageViewport getPageViewport(int pageNum) throws FOPException {
  288. if (pageNum < 0 || pageNum >= pageViewportList.size()) {
  289. throw new FOPException("Requested page number is out of range: " + pageNum
  290. + "; only " + pageViewportList.size()
  291. + " page(s) available.");
  292. }
  293. return (PageViewport) pageViewportList.get(pageNum);
  294. }
  295. /**
  296. * Generates a desired page from the renderer's page viewport list.
  297. *
  298. * @param pageNum the 0-based page number to generate
  299. * @return the <code>java.awt.image.BufferedImage</code> corresponding to
  300. * the page or null if the page doesn't exist.
  301. * @throws FOPException
  302. */
  303. public BufferedImage getPageImage(int pageNum) throws FOPException {
  304. return getPageImage(getPageViewport(pageNum));
  305. }
  306. /**
  307. * Handle the traits for a region
  308. * This is used to draw the traits for the given page region.
  309. * (See Sect. 6.4.1.2 of XSL-FO spec.)
  310. * @param region the RegionViewport whose region is to be drawn
  311. * @TODO This is a copy from AbstractPathOrientedRenderer. Put this method in AbstractRenderer
  312. */
  313. protected void handleRegionTraits(RegionViewport region) {
  314. Rectangle2D viewArea = region.getViewArea();
  315. float startx = (float)(viewArea.getX() / 1000f);
  316. float starty = (float)(viewArea.getY() / 1000f);
  317. float width = (float)(viewArea.getWidth() / 1000f);
  318. float height = (float)(viewArea.getHeight() / 1000f);
  319. if (region.getRegionReference().getRegionClass() == FO_REGION_BODY) {
  320. currentBPPosition = region.getBorderAndPaddingWidthBefore();
  321. currentIPPosition = region.getBorderAndPaddingWidthStart();
  322. }
  323. drawBackAndBorders(region, startx, starty, width, height);
  324. }
  325. /**
  326. * Render an inline viewport.
  327. * This renders an inline viewport by clipping if necessary.
  328. * @param viewport the viewport to handle
  329. * @TODO This is a copy from AbstractPathOrientedRenderer. Put this method in AbstractRenderer
  330. */
  331. public void renderViewport(Viewport viewport) {
  332. float x = currentIPPosition / 1000f;
  333. float y = (currentBPPosition + viewport.getOffset()) / 1000f;
  334. float width = viewport.getIPD() / 1000f;
  335. float height = viewport.getBPD() / 1000f;
  336. // TODO: Calculate the border rect correctly.
  337. float borderPaddingStart = viewport.getBorderAndPaddingWidthStart() / 1000f;
  338. float borderPaddingBefore = viewport.getBorderAndPaddingWidthBefore() / 1000f;
  339. float bpwidth = borderPaddingStart
  340. + (viewport.getBorderAndPaddingWidthEnd() / 1000f);
  341. float bpheight = borderPaddingBefore
  342. + (viewport.getBorderAndPaddingWidthAfter() / 1000f);
  343. drawBackAndBorders(viewport, x, y, width + bpwidth, height + bpheight);
  344. if (viewport.getClip()) {
  345. saveGraphicsState();
  346. clipRect(x + borderPaddingStart, y + borderPaddingBefore, width, height);
  347. }
  348. super.renderViewport(viewport);
  349. if (viewport.getClip()) {
  350. restoreGraphicsState();
  351. }
  352. }
  353. /** Saves the graphics state of the rendering engine. */
  354. protected void saveGraphicsState() {
  355. // push (and save) the current graphics state
  356. state.push();
  357. }
  358. /** Restores the last graphics state of the rendering engine. */
  359. protected void restoreGraphicsState() {
  360. state.pop();
  361. }
  362. /**
  363. * @see org.apache.fop.render.AbstractRenderer#startVParea(CTM, Rectangle2D)
  364. */
  365. protected void startVParea(CTM ctm, Rectangle2D clippingRect) {
  366. saveGraphicsState();
  367. if (clippingRect != null) {
  368. clipRect((float)clippingRect.getX() / 1000f,
  369. (float)clippingRect.getY() / 1000f,
  370. (float)clippingRect.getWidth() / 1000f,
  371. (float)clippingRect.getHeight() / 1000f);
  372. }
  373. // Set the given CTM in the graphics state
  374. //state.setTransform(new AffineTransform(CTMHelper.toPDFArray(ctm)));
  375. state.transform(new AffineTransform(CTMHelper.toPDFArray(ctm)));
  376. }
  377. /**
  378. * @see org.apache.fop.render.AbstractRenderer#endVParea()
  379. */
  380. protected void endVParea() {
  381. restoreGraphicsState();
  382. }
  383. /**
  384. * @see org.apache.fop.render.AbstractRenderer
  385. * @TODO This is a copy from AbstractPathOrientedRenderer. Put this method in AbstractRenderer
  386. */
  387. protected void renderInlineSpace(Space space) {
  388. space.setBPD(0);
  389. renderInlineAreaBackAndBorders(space);
  390. super.renderInlineSpace(space);
  391. }
  392. /**
  393. * @see org.apache.fop.render.AbstractRenderer
  394. * @TODO This is a copy from AbstractPathOrientedRenderer. Put this method in AbstractRenderer
  395. */
  396. protected void renderInlineParent(InlineParent ip) {
  397. renderInlineAreaBackAndBorders(ip);
  398. super.renderInlineParent(ip);
  399. }
  400. /**
  401. * @see org.apache.fop.render.AbstractRenderer
  402. * @TODO This is a copy from AbstractPathOrientedRenderer. Put this method in AbstractRenderer
  403. */
  404. protected void renderInlineBlockParent(InlineBlockParent ibp) {
  405. renderInlineAreaBackAndBorders(ibp);
  406. super.renderInlineBlockParent(ibp);
  407. }
  408. /**
  409. * @see org.apache.fop.render.AbstractRenderer#renderBlockViewport(BlockViewport,
  410. * List)
  411. */
  412. protected void renderBlockViewport(BlockViewport bv, List children) {
  413. // clip and position viewport if necessary
  414. // save positions
  415. int saveIP = currentIPPosition;
  416. int saveBP = currentBPPosition;
  417. CTM ctm = bv.getCTM();
  418. int borderPaddingStart = bv.getBorderAndPaddingWidthStart();
  419. int borderPaddingBefore = bv.getBorderAndPaddingWidthBefore();
  420. float x, y;
  421. x = (float) (bv.getXOffset() + containingIPPosition) / 1000f;
  422. y = (float) (bv.getYOffset() + containingBPPosition) / 1000f;
  423. // This is the content-rect
  424. float width = (float) bv.getIPD() / 1000f;
  425. float height = (float) bv.getBPD() / 1000f;
  426. if (bv.getPositioning() == Block.ABSOLUTE
  427. || bv.getPositioning() == Block.FIXED) {
  428. currentIPPosition = bv.getXOffset();
  429. currentBPPosition = bv.getYOffset();
  430. // TODO not tested yet
  431. // For FIXED, we need to break out of the current viewports to the
  432. // one established by the page. We save the state stack for
  433. // restoration
  434. // after the block-container has been painted. See below.
  435. List breakOutList = null;
  436. if (bv.getPositioning() == Block.FIXED) {
  437. log.debug("Block.FIXED --> break out");
  438. breakOutList = new java.util.ArrayList();
  439. Graphics2D graph;
  440. while (true) {
  441. graph = state.getGraph();
  442. if (state.pop() == null) {
  443. break;
  444. }
  445. breakOutList.add(0, graph); // Insert because of
  446. // stack-popping
  447. log.debug("Adding to break out list: " + graph);
  448. }
  449. }
  450. CTM tempctm = new CTM(containingIPPosition, containingBPPosition);
  451. ctm = tempctm.multiply(ctm);
  452. // Adjust for spaces (from margin or indirectly by start-indent etc.
  453. x += bv.getSpaceStart() / 1000f;
  454. currentIPPosition += bv.getSpaceStart();
  455. y += bv.getSpaceBefore() / 1000f;
  456. currentBPPosition += bv.getSpaceBefore();
  457. float bpwidth = (borderPaddingStart + bv
  458. .getBorderAndPaddingWidthEnd()) / 1000f;
  459. float bpheight = (borderPaddingBefore + bv
  460. .getBorderAndPaddingWidthAfter()) / 1000f;
  461. drawBackAndBorders(bv, x, y, width + bpwidth, height + bpheight);
  462. // Now adjust for border/padding
  463. currentIPPosition += borderPaddingStart;
  464. currentBPPosition += borderPaddingBefore;
  465. Rectangle2D clippingRect = null;
  466. if (bv.getClip()) {
  467. clippingRect = new Rectangle(currentIPPosition, currentBPPosition,
  468. bv.getIPD(), bv.getBPD());
  469. }
  470. startVParea(ctm, clippingRect);
  471. currentIPPosition = 0;
  472. currentBPPosition = 0;
  473. renderBlocks(bv, children);
  474. endVParea();
  475. if (breakOutList != null) {
  476. log.debug(
  477. "Block.FIXED --> restoring context after break-out");
  478. Graphics2D graph;
  479. Iterator i = breakOutList.iterator();
  480. while (i.hasNext()) {
  481. graph = (Graphics2D) i.next();
  482. log.debug("Restoring: " + graph);
  483. state.push();
  484. }
  485. }
  486. currentIPPosition = saveIP;
  487. currentBPPosition = saveBP;
  488. } else { // orientation = Block.STACK or RELATIVE
  489. currentBPPosition += bv.getSpaceBefore();
  490. // borders and background in the old coordinate system
  491. handleBlockTraits(bv);
  492. //Advance to start of content area
  493. currentIPPosition += bv.getStartIndent();
  494. CTM tempctm = new CTM(containingIPPosition, currentBPPosition
  495. + containingBPPosition);
  496. ctm = tempctm.multiply(ctm);
  497. // Now adjust for border/padding
  498. x += borderPaddingStart / 1000f;
  499. y += borderPaddingBefore / 1000f;
  500. Rectangle2D clippingRect = null;
  501. if (bv.getClip()) {
  502. clippingRect = new Rectangle(currentIPPosition, currentBPPosition,
  503. bv.getIPD(), bv.getBPD());
  504. }
  505. startVParea(ctm, clippingRect);
  506. currentIPPosition = 0;
  507. currentBPPosition = 0;
  508. renderBlocks(bv, children);
  509. endVParea();
  510. currentIPPosition = saveIP;
  511. currentBPPosition = saveBP;
  512. currentBPPosition += (int)(bv.getAllocBPD());
  513. }
  514. }
  515. /**
  516. * Clip an area. write a clipping operation given coordinates in the current
  517. * transform. Coordinates are in points.
  518. *
  519. * @param x the x coordinate
  520. * @param y the y coordinate
  521. * @param width the width of the area
  522. * @param height the height of the area
  523. */
  524. protected void clipRect(float x, float y, float width, float height) {
  525. Rectangle2D rect = new Rectangle2D.Float(x, y, width, height);
  526. state.updateClip(rect);
  527. }
  528. /**
  529. * Draw the background and borders. This draws the background and border
  530. * traits for an area given the position.
  531. *
  532. * @param area the area whose traits are used
  533. * @param startx the start x position
  534. * @param starty the start y position
  535. * @param width the width of the area
  536. * @param height the height of the area
  537. */
  538. protected void drawBackAndBorders(Area area, float startx, float starty,
  539. float width, float height) {
  540. BorderProps bpsBefore = (BorderProps) area
  541. .getTrait(Trait.BORDER_BEFORE);
  542. BorderProps bpsAfter = (BorderProps) area.getTrait(Trait.BORDER_AFTER);
  543. BorderProps bpsStart = (BorderProps) area.getTrait(Trait.BORDER_START);
  544. BorderProps bpsEnd = (BorderProps) area.getTrait(Trait.BORDER_END);
  545. // draw background
  546. Trait.Background back;
  547. back = (Trait.Background) area.getTrait(Trait.BACKGROUND);
  548. if (back != null) {
  549. // Calculate padding rectangle
  550. float sx = startx;
  551. float sy = starty;
  552. float paddRectWidth = width;
  553. float paddRectHeight = height;
  554. if (bpsStart != null) {
  555. sx += bpsStart.width / 1000f;
  556. paddRectWidth -= bpsStart.width / 1000f;
  557. }
  558. if (bpsBefore != null) {
  559. sy += bpsBefore.width / 1000f;
  560. paddRectHeight -= bpsBefore.width / 1000f;
  561. }
  562. if (bpsEnd != null) {
  563. paddRectWidth -= bpsEnd.width / 1000f;
  564. }
  565. if (bpsAfter != null) {
  566. paddRectHeight -= bpsAfter.width / 1000f;
  567. }
  568. if (back.getColor() != null) {
  569. drawBackground(back, sx, sy, paddRectWidth, paddRectHeight);
  570. }
  571. // background image
  572. if (back.getFopImage() != null) {
  573. FopImage fopimage = back.getFopImage();
  574. if (fopimage != null && fopimage.load(FopImage.DIMENSIONS)) {
  575. saveGraphicsState();
  576. clipRect(sx, sy, paddRectWidth, paddRectHeight);
  577. int horzCount = (int) ((paddRectWidth * 1000 / fopimage
  578. .getIntrinsicWidth()) + 1.0f);
  579. int vertCount = (int) ((paddRectHeight * 1000 / fopimage
  580. .getIntrinsicHeight()) + 1.0f);
  581. if (back.getRepeat() == EN_NOREPEAT) {
  582. horzCount = 1;
  583. vertCount = 1;
  584. } else if (back.getRepeat() == EN_REPEATX) {
  585. vertCount = 1;
  586. } else if (back.getRepeat() == EN_REPEATY) {
  587. horzCount = 1;
  588. }
  589. // change from points to millipoints
  590. sx *= 1000;
  591. sy *= 1000;
  592. if (horzCount == 1) {
  593. sx += back.getHoriz();
  594. }
  595. if (vertCount == 1) {
  596. sy += back.getVertical();
  597. }
  598. for (int x = 0; x < horzCount; x++) {
  599. for (int y = 0; y < vertCount; y++) {
  600. // place once
  601. Rectangle2D pos;
  602. pos = new Rectangle2D.Float(sx
  603. + (x * fopimage.getIntrinsicWidth()), sy
  604. + (y * fopimage.getIntrinsicHeight()),
  605. fopimage.getIntrinsicWidth(), fopimage
  606. .getIntrinsicHeight());
  607. putImage(back.getURL(), pos); // TODO test
  608. }
  609. }
  610. restoreGraphicsState();
  611. } else {
  612. log.warn(
  613. "Can't find background image: " + back.getURL());
  614. }
  615. }
  616. }
  617. // draw border
  618. // BORDER_BEFORE
  619. if (bpsBefore != null) {
  620. int borderWidth = (int) Math.round((bpsBefore.width / 1000f));
  621. state.updateColor(bpsBefore.color);
  622. state.getGraph().fillRect((int) startx, (int) starty, (int) width,
  623. borderWidth);
  624. }
  625. // BORDER_AFTER
  626. if (bpsAfter != null) {
  627. int borderWidth = (int) Math.round((bpsAfter.width / 1000f));
  628. float sy = starty + height;
  629. state.updateColor(bpsAfter.color);
  630. state.getGraph().fillRect((int) startx,
  631. (int) (starty + height - borderWidth), (int) width,
  632. borderWidth);
  633. }
  634. // BORDER_START
  635. if (bpsStart != null) {
  636. int borderWidth = (int) Math.round((bpsStart.width / 1000f));
  637. state.updateColor(bpsStart.color);
  638. state.getGraph().fillRect((int) startx, (int) starty, borderWidth,
  639. (int) height);
  640. }
  641. // BORDER_END
  642. if (bpsEnd != null) {
  643. int borderWidth = (int) Math.round((bpsEnd.width / 1000f));
  644. float sx = startx + width;
  645. state.updateColor(bpsEnd.color);
  646. state.getGraph().fillRect((int) (startx + width - borderWidth),
  647. (int) starty, borderWidth, (int) height);
  648. }
  649. }
  650. /**
  651. * Draw the Background Rectangle of a given area.
  652. *
  653. * @param back the Trait.Background
  654. * @param sx x coordinate of the rectangle to be filled.
  655. * @param sy y the y coordinate of the rectangle to be filled.
  656. * @param paddRectWidth the width of the rectangle to be filled.
  657. * @param paddRectHeight the height of the rectangle to be filled.
  658. */
  659. protected void drawBackground(Trait.Background back, float sx, float sy,
  660. float paddRectWidth, float paddRectHeight) {
  661. state.updateColor(back.getColor());
  662. state.getGraph().fillRect((int) sx, (int) sy, (int) paddRectWidth,
  663. (int) paddRectHeight);
  664. }
  665. /**
  666. * Common method to render the background and borders for any inline area.
  667. * The all borders and padding are drawn outside the specified area.
  668. * @param area the inline area for which the background, border and padding is to be
  669. * rendered
  670. * @TODO This is a copy from AbstractPathOrientedRenderer. Put this method in AbstractRenderer
  671. */
  672. protected void renderInlineAreaBackAndBorders(InlineArea area) {
  673. float x = currentIPPosition / 1000f;
  674. float y = (currentBPPosition + area.getOffset()) / 1000f;
  675. float width = area.getIPD() / 1000f;
  676. float height = area.getBPD() / 1000f;
  677. float borderPaddingStart = area.getBorderAndPaddingWidthStart() / 1000f;
  678. float borderPaddingBefore = area.getBorderAndPaddingWidthBefore() / 1000f;
  679. float bpwidth = borderPaddingStart
  680. + (area.getBorderAndPaddingWidthEnd() / 1000f);
  681. float bpheight = borderPaddingBefore
  682. + (area.getBorderAndPaddingWidthAfter() / 1000f);
  683. if (height != 0.0f || bpheight != 0.0f && bpwidth != 0.0f) {
  684. drawBackAndBorders(area, x, y - borderPaddingBefore
  685. , width + bpwidth
  686. , height + bpheight);
  687. }
  688. }
  689. /**
  690. * Handle block traits. The block could be any sort of block with any
  691. * positioning so this should render the traits such as border and
  692. * background in its position.
  693. *
  694. * @param block the block to render the traits
  695. */
  696. protected void handleBlockTraits(Block block) {
  697. // copied from pdf
  698. int borderPaddingStart = block.getBorderAndPaddingWidthStart();
  699. int borderPaddingBefore = block.getBorderAndPaddingWidthBefore();
  700. float startx = currentIPPosition / 1000f;
  701. float starty = currentBPPosition / 1000f;
  702. float width = block.getIPD() / 1000f;
  703. float height = block.getBPD() / 1000f;
  704. startx += block.getStartIndent() / 1000f;
  705. startx -= block.getBorderAndPaddingWidthStart() / 1000f;
  706. width += borderPaddingStart / 1000f;
  707. width += block.getBorderAndPaddingWidthEnd() / 1000f;
  708. height += borderPaddingBefore / 1000f;
  709. height += block.getBorderAndPaddingWidthAfter() / 1000f;
  710. drawBackAndBorders(block, startx, starty, width, height);
  711. }
  712. /**
  713. * @see org.apache.fop.render.AbstractRenderer#renderText(TextArea)
  714. */
  715. public void renderText(TextArea text) {
  716. renderInlineAreaBackAndBorders(text);
  717. float x = currentIPPosition + text.getBorderAndPaddingWidthStart();
  718. float y = currentBPPosition + text.getOffset() + text.getBaselineOffset(); // baseline
  719. String name = (String) text.getTrait(Trait.FONT_NAME);
  720. int size = ((Integer) text.getTrait(Trait.FONT_SIZE)).intValue();
  721. state.updateFont(name, size, null);
  722. ColorType ct = (ColorType) text.getTrait(Trait.COLOR);
  723. state.updateColor(ct, false, null);
  724. String s = text.getText();
  725. state.getGraph().drawString(s, x / 1000f, y / 1000f);
  726. // getLogger().debug("renderText(): \"" + s + "\", x: "
  727. // + x + ", y: " + y + state);
  728. // rendering text decorations
  729. FontMetrics metrics = fontInfo.getMetricsFor(name);
  730. Font fs = new Font(name, metrics, size);
  731. super.renderText(text);
  732. renderTextDecoration(fs, text, y, x);
  733. }
  734. /**
  735. * @see org.apache.fop.render.AbstractRenderer#renderCharacter(Character)
  736. */
  737. public void renderCharacter(Character ch) {
  738. renderInlineAreaBackAndBorders(ch);
  739. float x = currentIPPosition + ch.getBorderAndPaddingWidthStart();
  740. float y = currentBPPosition + ch.getOffset() + ch.getBaselineOffset(); // baseline
  741. String name = (String) ch.getTrait(Trait.FONT_NAME);
  742. int size = ((Integer) ch.getTrait(Trait.FONT_SIZE)).intValue();
  743. state.updateFont(name, size, null);
  744. ColorType ct = (ColorType) ch.getTrait(Trait.COLOR);
  745. state.updateColor(ct, false, null);
  746. String s = ch.getChar();
  747. state.getGraph().drawString(s, x / 1000f, y / 1000f);
  748. // getLogger().debug( "renderCharacter(): \"" + s + "\", x: "
  749. // + x + ", y: " + y + state);
  750. // rendering text decorations
  751. FontMetrics metrics = fontInfo.getMetricsFor(name);
  752. Font fs = new Font(name, metrics, size);
  753. renderTextDecoration(fs, ch, y, x);
  754. super.renderCharacter(ch);
  755. }
  756. /**
  757. * Paints the text decoration marks.
  758. *
  759. * @param fs Current font
  760. * @param inline inline area to paint the marks for
  761. * @param baseline position of the baseline
  762. * @param startIPD start IPD
  763. */
  764. protected void renderTextDecoration(Font fs, InlineArea inline,
  765. float baseline, float startIPD) {
  766. boolean hasTextDeco = inline.hasUnderline() || inline.hasOverline()
  767. || inline.hasLineThrough();
  768. if (hasTextDeco) {
  769. state.updateStroke((fs.getDescender() / (-8 * 1000f)),
  770. Constants.EN_SOLID);
  771. float endIPD = startIPD + inline.getIPD();
  772. if (inline.hasUnderline()) {
  773. ColorType ct = (ColorType) inline
  774. .getTrait(Trait.UNDERLINE_COLOR);
  775. state.updateColor(ct, false, null);
  776. float y = baseline - fs.getDescender() / 2;
  777. line.setLine(startIPD / 1000f, y / 1000f, endIPD / 1000f,
  778. y / 1000f);
  779. state.getGraph().draw(line);
  780. }
  781. if (inline.hasOverline()) {
  782. ColorType ct = (ColorType) inline
  783. .getTrait(Trait.OVERLINE_COLOR);
  784. state.updateColor(ct, false, null);
  785. float y = (float) (baseline - (1.1 * fs.getCapHeight()));
  786. line.setLine(startIPD / 1000f, y / 1000f, endIPD / 1000f,
  787. y / 1000f);
  788. state.getGraph().draw(line);
  789. }
  790. if (inline.hasLineThrough()) {
  791. ColorType ct = (ColorType) inline
  792. .getTrait(Trait.LINETHROUGH_COLOR);
  793. state.updateColor(ct, false, null);
  794. float y = (float) (baseline - (0.45 * fs.getCapHeight()));
  795. line.setLine(startIPD / 1000f, y / 1000f, endIPD / 1000f,
  796. y / 1000f);
  797. state.getGraph().draw(line);
  798. }
  799. }
  800. }
  801. /**
  802. * Render leader area. This renders a leader area which is an area with a
  803. * rule.
  804. *
  805. * @param area the leader area to render
  806. */
  807. public void renderLeader(Leader area) {
  808. renderInlineAreaBackAndBorders(area);
  809. // TODO leader-length: 25%, 50%, 75%, 100% not working yet
  810. // TODO Colors do not work on Leaders yet
  811. float startx = (currentIPPosition + area.getBorderAndPaddingWidthStart()) / 1000f;
  812. float starty = ((currentBPPosition + area.getOffset()) / 1000f);
  813. float endx = (currentIPPosition + area.getBorderAndPaddingWidthStart()
  814. + area.getIPD()) / 1000f;
  815. ColorType ct = (ColorType) area.getTrait(Trait.COLOR);
  816. state.updateColor(ct, true, null);
  817. line.setLine(startx, starty, endx, starty);
  818. float thickness = area.getRuleThickness() / 1000f;
  819. int style = area.getRuleStyle();
  820. switch (style) {
  821. case EN_SOLID:
  822. case EN_DOTTED:
  823. case EN_DASHED:
  824. state.updateStroke(thickness, style);
  825. state.getGraph().draw(line);
  826. break;
  827. case EN_DOUBLE:
  828. state.updateStroke(thickness / 3f, EN_SOLID); // only a third
  829. // upper Leader
  830. line.setLine(startx, starty, endx, starty);
  831. state.getGraph().draw(line);
  832. // lower Leader
  833. line.setLine(startx, starty + 2 * thickness, endx, starty + 2
  834. * thickness);
  835. state.getGraph().draw(line);
  836. break;
  837. case EN_GROOVE:
  838. // The rule looks as though it were carved into the canvas.
  839. // (Top/left half of the rule's thickness is the
  840. // color specified; the other half is white.)
  841. state.updateStroke(thickness / 2f, EN_SOLID); // only the half
  842. // upper Leader
  843. line.setLine(startx, starty, endx, starty);
  844. state.getGraph().draw(line);
  845. // lower Leader
  846. line.setLine(startx, starty + thickness, endx, starty + thickness);
  847. state.getGraph().setColor(Color.white);
  848. state.getGraph().draw(line);
  849. // TODO the implementation could be nicer, f.eg. with triangles at
  850. // the tip of the lines. See also RenderX's implementation (looks
  851. // like a button)
  852. break;
  853. case EN_RIDGE:
  854. // The opposite of "groove", the rule looks as though it were
  855. // coming out of the canvas. (Bottom/right half of the rule's
  856. // thickness is the color specified; the other half is white.)
  857. state.updateStroke(thickness / 2f, EN_SOLID); // only the half
  858. // lower Leader
  859. line.setLine(startx, starty + thickness, endx, starty + thickness);
  860. state.getGraph().draw(line);
  861. // upperLeader
  862. line.setLine(startx, starty, endx, starty);
  863. state.getGraph().setColor(Color.white);
  864. state.getGraph().draw(line);
  865. // TODO the implementation could be nicer, f.eg. with triangles at
  866. // the tip of the lines. See also RenderX's implementation (looks
  867. // like a button)
  868. break;
  869. case EN_NONE:
  870. // No rule is drawn
  871. break;
  872. } // end switch
  873. super.renderLeader(area);
  874. }
  875. /**
  876. * @see org.apache.fop.render.AbstractRenderer#renderImage(Image,
  877. * Rectangle2D)
  878. */
  879. public void renderImage(Image image, Rectangle2D pos) {
  880. // endTextObject();
  881. String url = image.getURL();
  882. putImage(url, pos);
  883. }
  884. /**
  885. * Draws an image
  886. *
  887. * @param pUrl URL of the bitmap
  888. * @param pos Position of the bitmap
  889. */
  890. protected void putImage(String pUrl, Rectangle2D pos) {
  891. int x = currentIPPosition; // TODO + area.getXOffset();
  892. int y = currentBPPosition;
  893. String url = ImageFactory.getURL(pUrl);
  894. ImageFactory fact = ImageFactory.getInstance();
  895. FopImage fopimage = fact.getImage(url, userAgent);
  896. if (fopimage == null) {
  897. return;
  898. }
  899. if (!fopimage.load(FopImage.DIMENSIONS)) {
  900. return;
  901. }
  902. int w = fopimage.getWidth();
  903. int h = fopimage.getHeight();
  904. String mime = fopimage.getMimeType();
  905. if ("text/xml".equals(mime)) {
  906. if (!fopimage.load(FopImage.ORIGINAL_DATA)) {
  907. return;
  908. }
  909. Document doc = ((XMLImage) fopimage).getDocument();
  910. String ns = ((XMLImage) fopimage).getNameSpace();
  911. renderDocument(doc, ns, pos);
  912. } else if ("image/svg+xml".equals(mime)) {
  913. if (!fopimage.load(FopImage.ORIGINAL_DATA)) {
  914. return;
  915. }
  916. Document doc = ((XMLImage) fopimage).getDocument();
  917. String ns = ((XMLImage) fopimage).getNameSpace();
  918. renderDocument(doc, ns, pos);
  919. } else if ("image/eps".equals(mime)) {
  920. log.warn("EPS images are not supported by this renderer");
  921. } else if ("image/jpeg".equals(mime)) {
  922. if (!fopimage.load(FopImage.ORIGINAL_DATA)) {
  923. return;
  924. }
  925. // TODO Load JPEGs rather through fopimage.load(FopImage.BITMAP),
  926. // but JpegImage will need to be extended for that
  927. // url = url.substring(7);
  928. // url = "C:/eclipse/myWorkbenches/fop4/xml-fop/examples/fo" + url;
  929. java.awt.Image awtImage = new javax.swing.ImageIcon(url).getImage();
  930. state.getGraph().drawImage(awtImage,
  931. (int)(x / 1000f), (int)(y / 1000f),
  932. (int)(pos.getWidth() / 1000f), (int)(pos.getHeight() / 1000f), null);
  933. } else {
  934. if (!fopimage.load(FopImage.BITMAP)) {
  935. log.warn("Loading of bitmap failed: " + url);
  936. return;
  937. }
  938. byte[] raw = fopimage.getBitmaps();
  939. // TODO Hardcoded color and sample models, FIX ME!
  940. ColorModel cm = new ComponentColorModel(
  941. ColorSpace.getInstance(ColorSpace.CS_LINEAR_RGB),
  942. new int[] {8, 8, 8},
  943. false, false,
  944. ColorModel.OPAQUE, DataBuffer.TYPE_BYTE);
  945. SampleModel sampleModel = new PixelInterleavedSampleModel(
  946. DataBuffer.TYPE_BYTE, w, h, 3, w * 3, new int[] { 0, 1, 2 });
  947. DataBuffer dbuf = new DataBufferByte(raw, w * h * 3);
  948. WritableRaster raster = Raster.createWritableRaster(sampleModel,
  949. dbuf, null);
  950. java.awt.Image awtImage;
  951. // Combine the color model and raster into a buffered image
  952. awtImage = new BufferedImage(cm, raster, false, null);
  953. state.getGraph().drawImage(awtImage,
  954. (int)(x / 1000f), (int)(y / 1000f),
  955. (int)(pos.getWidth() / 1000f), (int)(pos.getHeight() / 1000f), null);
  956. }
  957. }
  958. /**
  959. * @see org.apache.fop.render.AbstractRenderer#renderForeignObject(ForeignObject,
  960. * Rectangle2D)
  961. */
  962. public void renderForeignObject(ForeignObject fo, Rectangle2D pos) {
  963. Document doc = fo.getDocument();
  964. String ns = fo.getNameSpace();
  965. renderDocument(doc, ns, pos);
  966. }
  967. /**
  968. * Renders an XML document (SVG for example).
  969. *
  970. * @param doc DOM document representing the XML document
  971. * @param ns Namespace for the document
  972. * @param pos Position on the page
  973. */
  974. public void renderDocument(Document doc, String ns, Rectangle2D pos) {
  975. RendererContext context;
  976. context = new RendererContext(this, getMimeType());
  977. context.setUserAgent(userAgent);
  978. context.setProperty(Java2DRendererContextConstants.JAVA2D_STATE, state);
  979. context.setProperty(Java2DRendererContextConstants.XPOS,
  980. new Integer(currentIPPosition + (int)pos.getX()));
  981. context.setProperty(Java2DRendererContextConstants.YPOS,
  982. new Integer(currentBPPosition + (int)pos.getY()));
  983. context.setProperty(Java2DRendererContextConstants.WIDTH,
  984. new Integer((int)pos.getWidth()));
  985. context.setProperty(Java2DRendererContextConstants.HEIGHT,
  986. new Integer((int) pos.getHeight()));
  987. renderXML(context, doc, ns);
  988. }
  989. /**
  990. * @see java.awt.print.Printable#print(java.awt.Graphics,
  991. * java.awt.print.PageFormat, int)
  992. */
  993. public int print(Graphics g, PageFormat pageFormat, int pageIndex)
  994. throws PrinterException {
  995. if (pageIndex >= getNumberOfPages()) {
  996. return NO_SUCH_PAGE;
  997. }
  998. Graphics2D graphics = (Graphics2D) g;
  999. Java2DGraphicsState oldState = state;
  1000. BufferedImage image;
  1001. try {
  1002. PageViewport viewport = getPageViewport(pageIndex);
  1003. AffineTransform at = graphics.getTransform();
  1004. state = new Java2DGraphicsState(graphics, this.fontInfo, at);
  1005. // reset the current Positions
  1006. currentBPPosition = 0;
  1007. currentIPPosition = 0;
  1008. renderPageAreas(viewport.getPage());
  1009. return PAGE_EXISTS;
  1010. } catch (FOPException e) {
  1011. log.error(e);
  1012. return NO_SUCH_PAGE;
  1013. } finally {
  1014. oldState = state;
  1015. }
  1016. }
  1017. }