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.

AFPRenderer.java 64KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816
  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.afp;
  19. import java.awt.Color;
  20. import java.awt.Point;
  21. import java.awt.Rectangle;
  22. import java.awt.geom.AffineTransform;
  23. import java.awt.geom.Point2D;
  24. import java.awt.geom.Rectangle2D;
  25. import java.awt.image.RenderedImage;
  26. import java.io.FileNotFoundException;
  27. import java.io.IOException;
  28. import java.io.InputStream;
  29. import java.io.OutputStream;
  30. import java.io.UnsupportedEncodingException;
  31. import java.util.ArrayList;
  32. import java.util.HashMap;
  33. import java.util.Iterator;
  34. import java.util.List;
  35. import java.util.Map;
  36. import org.apache.commons.io.IOUtils;
  37. import org.apache.commons.io.output.ByteArrayOutputStream;
  38. import org.apache.xmlgraphics.image.codec.tiff.TIFFImage;
  39. import org.apache.xmlgraphics.image.loader.ImageException;
  40. import org.apache.xmlgraphics.image.loader.ImageFlavor;
  41. import org.apache.xmlgraphics.image.loader.ImageInfo;
  42. import org.apache.xmlgraphics.image.loader.ImageManager;
  43. import org.apache.xmlgraphics.image.loader.ImageSessionContext;
  44. import org.apache.xmlgraphics.image.loader.impl.ImageGraphics2D;
  45. import org.apache.xmlgraphics.image.loader.impl.ImageRawCCITTFax;
  46. import org.apache.xmlgraphics.image.loader.impl.ImageRendered;
  47. import org.apache.xmlgraphics.image.loader.impl.ImageXMLDOM;
  48. import org.apache.xmlgraphics.image.loader.util.ImageUtil;
  49. import org.apache.xmlgraphics.ps.ImageEncodingHelper;
  50. import org.apache.fop.apps.FOUserAgent;
  51. import org.apache.fop.apps.MimeConstants;
  52. import org.apache.fop.area.Block;
  53. import org.apache.fop.area.BlockViewport;
  54. import org.apache.fop.area.BodyRegion;
  55. import org.apache.fop.area.CTM;
  56. import org.apache.fop.area.OffDocumentItem;
  57. import org.apache.fop.area.PageViewport;
  58. import org.apache.fop.area.RegionReference;
  59. import org.apache.fop.area.RegionViewport;
  60. import org.apache.fop.area.Trait;
  61. import org.apache.fop.area.inline.Image;
  62. import org.apache.fop.area.inline.Leader;
  63. import org.apache.fop.area.inline.SpaceArea;
  64. import org.apache.fop.area.inline.TextArea;
  65. import org.apache.fop.area.inline.WordArea;
  66. import org.apache.fop.datatypes.URISpecification;
  67. import org.apache.fop.events.ResourceEventProducer;
  68. import org.apache.fop.fo.Constants;
  69. import org.apache.fop.fo.extensions.ExtensionAttachment;
  70. import org.apache.fop.fonts.FontInfo;
  71. import org.apache.fop.fonts.FontTriplet;
  72. import org.apache.fop.fonts.base14.Courier;
  73. import org.apache.fop.fonts.base14.Helvetica;
  74. import org.apache.fop.fonts.base14.TimesRoman;
  75. import org.apache.fop.render.AbstractPathOrientedRenderer;
  76. import org.apache.fop.render.Graphics2DAdapter;
  77. import org.apache.fop.render.RendererContext;
  78. import org.apache.fop.render.afp.extensions.AFPElementMapping;
  79. import org.apache.fop.render.afp.extensions.AFPPageSetup;
  80. import org.apache.fop.render.afp.fonts.AFPFont;
  81. import org.apache.fop.render.afp.fonts.AFPFontInfo;
  82. import org.apache.fop.render.afp.fonts.CharacterSet;
  83. import org.apache.fop.render.afp.fonts.FopCharacterSet;
  84. import org.apache.fop.render.afp.fonts.OutlineFont;
  85. import org.apache.fop.render.afp.modca.AFPConstants;
  86. import org.apache.fop.render.afp.modca.AFPDataStream;
  87. import org.apache.fop.render.afp.modca.ImageObject;
  88. import org.apache.fop.render.afp.modca.PageObject;
  89. /**
  90. * This is an implementation of a FOP Renderer that renders areas to AFP.
  91. * <p>
  92. * A renderer is primarily designed to convert a given area tree into the output
  93. * document format. It should be able to produce pages and fill the pages with
  94. * the text and graphical content. Usually the output is sent to an output
  95. * stream. Some output formats may support extra information that is not
  96. * available from the area tree or depends on the destination of the document.
  97. * Each renderer is given an area tree to render to its output format. The area
  98. * tree is simply a representation of the pages and the placement of text and
  99. * graphical objects on those pages.
  100. * </p>
  101. * <p>
  102. * The renderer will be given each page as it is ready and an output stream to
  103. * write the data out. All pages are supplied in the order they appear in the
  104. * document. In order to save memory it is possible to render the pages out of
  105. * order. Any page that is not ready to be rendered is setup by the renderer
  106. * first so that it can reserve a space or reference for when the page is ready
  107. * to be rendered.The renderer is responsible for managing the output format and
  108. * associated data and flow.
  109. * </p>
  110. * <p>
  111. * Each renderer is totally responsible for its output format. Because font
  112. * metrics (and therefore layout) are obtained in two different ways depending
  113. * on the renderer, the renderer actually sets up the fonts being used. The font
  114. * metrics are used during the layout process to determine the size of
  115. * characters.
  116. * </p>
  117. * <p>
  118. * The render context is used by handlers. It contains information about the
  119. * current state of the renderer, such as the page, the position, and any other
  120. * miscellaneous objects that are required to draw into the page.
  121. * </p>
  122. * <p>
  123. * A renderer is created by implementing the Renderer interface. However, the
  124. * AbstractRenderer does most of what is needed, including iterating through the
  125. * tree parts, so it is this that is extended. This means that this object only
  126. * need to implement the basic functionality such as text, images, and lines.
  127. * AbstractRenderer's methods can easily be overridden to handle things in a
  128. * different way or do some extra processing.
  129. * </p>
  130. * <p>
  131. * The relevant AreaTree structures that will need to be rendered are Page,
  132. * Viewport, Region, Span, Block, Line, Inline. A renderer implementation
  133. * renders each individual page, clips and aligns child areas to a viewport,
  134. * handle all types of inline area, text, image etc and draws various lines and
  135. * rectangles.
  136. * </p>
  137. *
  138. * Note: There are specific extensions that have been added to the
  139. * FO. They are specific to their location within the FO and have to be
  140. * processed accordingly (ie. at the start or end of the page).
  141. *
  142. */
  143. public class AFPRenderer extends AbstractPathOrientedRenderer {
  144. /**
  145. * The default afp renderer output resolution
  146. */
  147. private static final int DEFAULT_DPI_RESOLUTION = 240;
  148. /**
  149. * The afp factor for calculating resolutions (e.g. 72000/240 = 300)
  150. */
  151. private static final int DPI_CONVERSION_FACTOR = 72000;
  152. /**
  153. * The afp data stream object responsible for generating afp data
  154. */
  155. private AFPDataStream afpDataStream = null;
  156. /**
  157. * The map of afp root extensions
  158. */
  159. // UNUSED
  160. // private HashMap rootExtensionMap = null;
  161. /**
  162. * The map of page segments
  163. */
  164. private HashMap pageSegmentsMap = null;
  165. /**
  166. * The fonts on the current page
  167. */
  168. private HashMap currentPageFonts = null;
  169. /**
  170. * The current color object
  171. */
  172. private Color currentColor = null;
  173. /**
  174. * The page font number counter, used to determine the next font reference
  175. */
  176. private int pageFontCounter = 0;
  177. /**
  178. * The current font family
  179. */
  180. // UNUSED
  181. // private String currentFontFamily = "";
  182. /**
  183. * The current font size
  184. */
  185. private int currentFontSize = 0;
  186. /**
  187. * The Options to be set on the AFPRenderer
  188. */
  189. // UNUSED
  190. // private Map afpOptions = null;
  191. /**
  192. * The page width
  193. */
  194. private int pageWidth = 0;
  195. /**
  196. * The page height
  197. */
  198. private int pageHeight = 0;
  199. /**
  200. * The current page sequence id
  201. */
  202. // UNUSED
  203. // private String pageSequenceId = null;
  204. /**
  205. * The portrait rotation
  206. */
  207. private int portraitRotation = 0;
  208. /**
  209. * The landscape rotation
  210. */
  211. private int landscapeRotation = 270;
  212. /**
  213. * The line cache, avoids drawing duplicate lines in tables.
  214. */
  215. // UNUSED
  216. // private HashSet lineCache = null;
  217. /**
  218. * The current x position for line drawing
  219. */
  220. // UNUSED
  221. // private float x;
  222. /**
  223. * The current y position for line drawing
  224. */
  225. // UNUSED
  226. // private float y;
  227. /**
  228. * The map of saved incomplete pages
  229. */
  230. private Map pages = null;
  231. /**
  232. * Flag to the set the output object type for images
  233. */
  234. private boolean colorImages = false;
  235. /**
  236. * Default value for image depth
  237. */
  238. private int bitsPerPixel = 8;
  239. /**
  240. * The output resolution
  241. */
  242. private int resolution = DEFAULT_DPI_RESOLUTION;
  243. /**
  244. * Constructor for AFPRenderer.
  245. */
  246. public AFPRenderer() {
  247. super();
  248. }
  249. /**
  250. * Set up the font info
  251. *
  252. * @param inFontInfo font info to set up
  253. */
  254. public void setupFontInfo(FontInfo inFontInfo) {
  255. this.fontInfo = inFontInfo;
  256. int num = 1;
  257. if (super.embedFontInfoList != null && super.embedFontInfoList.size() > 0) {
  258. for (Iterator it = super.embedFontInfoList.iterator(); it.hasNext();) {
  259. AFPFontInfo afi = (AFPFontInfo)it.next();
  260. AFPFont bf = (AFPFont)afi.getAFPFont();
  261. for (Iterator it2 = afi.getFontTriplets().iterator(); it2.hasNext();) {
  262. FontTriplet ft = (FontTriplet)it2.next();
  263. this.fontInfo.addFontProperties("F" + num, ft.getName()
  264. , ft.getStyle(), ft.getWeight());
  265. this.fontInfo.addMetrics("F" + num, bf);
  266. num++;
  267. }
  268. }
  269. } else {
  270. AFPEventProducer eventProducer = AFPEventProducer.Provider.get(
  271. getUserAgent().getEventBroadcaster());
  272. eventProducer.warnDefaultFontSetup(this);
  273. }
  274. if (this.fontInfo.fontLookup("sans-serif", "normal", 400) == null) {
  275. CharacterSet cs = new FopCharacterSet("T1V10500", "Cp500", "CZH200 ",
  276. 1, new Helvetica());
  277. AFPFont bf = new OutlineFont("Helvetica", cs);
  278. this.fontInfo.addFontProperties("F" + num, "sans-serif", "normal", 400);
  279. this.fontInfo.addMetrics("F" + num, bf);
  280. num++;
  281. }
  282. if (this.fontInfo.fontLookup("serif", "normal", 400) == null) {
  283. CharacterSet cs = new FopCharacterSet("T1V10500", "Cp500", "CZN200 ",
  284. 1, new TimesRoman());
  285. AFPFont bf = new OutlineFont("Helvetica", cs);
  286. this.fontInfo.addFontProperties("F" + num, "serif", "normal", 400);
  287. this.fontInfo.addMetrics("F" + num, bf);
  288. num++;
  289. }
  290. if (this.fontInfo.fontLookup("monospace", "normal", 400) == null) {
  291. CharacterSet cs = new FopCharacterSet("T1V10500", "Cp500", "CZ4200 ",
  292. 1, new Courier());
  293. AFPFont bf = new OutlineFont("Helvetica", cs);
  294. this.fontInfo.addFontProperties("F" + num, "monospace", "normal", 400);
  295. this.fontInfo.addMetrics("F" + num, bf);
  296. num++;
  297. }
  298. if (this.fontInfo.fontLookup("any", "normal", 400) == null) {
  299. FontTriplet ft = this.fontInfo.fontLookup("sans-serif", "normal", 400);
  300. this.fontInfo.addFontProperties(
  301. this.fontInfo.getInternalFontKey(ft), "any", "normal", 400);
  302. }
  303. }
  304. /**
  305. * {@inheritDoc}
  306. */
  307. public void setUserAgent(FOUserAgent agent) {
  308. super.setUserAgent(agent);
  309. }
  310. /**
  311. * {@inheritDoc}
  312. */
  313. public void startRenderer(OutputStream outputStream) throws IOException {
  314. currentPageFonts = new HashMap();
  315. currentColor = new Color(255, 255, 255);
  316. afpDataStream = new AFPDataStream();
  317. afpDataStream.setPortraitRotation(portraitRotation);
  318. afpDataStream.setLandscapeRotation(landscapeRotation);
  319. afpDataStream.startDocument(outputStream);
  320. }
  321. /**
  322. * {@inheritDoc}
  323. */
  324. public void stopRenderer() throws IOException {
  325. afpDataStream.endDocument();
  326. }
  327. /**
  328. * {@inheritDoc}
  329. */
  330. public boolean supportsOutOfOrder() {
  331. //return false;
  332. return true;
  333. }
  334. /**
  335. * Prepare a page for rendering. This is called if the renderer supports
  336. * out of order rendering. The renderer should prepare the page so that a
  337. * page further on in the set of pages can be rendered. The body of the
  338. * page should not be rendered. The page will be rendered at a later time
  339. * by the call to render page.
  340. *
  341. * {@inheritDoc}
  342. */
  343. public void preparePage(PageViewport page) {
  344. // initializeRootExtensions(page);
  345. // this.currentFontFamily = "";
  346. this.currentFontSize = 0;
  347. this.pageFontCounter = 0;
  348. this.currentPageFonts.clear();
  349. // this.lineCache = new HashSet();
  350. Rectangle2D bounds = page.getViewArea();
  351. this.pageWidth = mpts2units(bounds.getWidth());
  352. this.pageHeight = mpts2units(bounds.getHeight());
  353. // renderPageGroupExtensions(page);
  354. final int pageRotation = 0;
  355. this.afpDataStream.startPage(pageWidth, pageHeight, pageRotation,
  356. getResolution(), getResolution());
  357. renderPageObjectExtensions(page);
  358. if (this.pages == null) {
  359. this.pages = new HashMap();
  360. }
  361. this.pages.put(page, afpDataStream.savePage());
  362. }
  363. /**
  364. * {@inheritDoc}
  365. */
  366. public void processOffDocumentItem(OffDocumentItem odi) {
  367. // TODO
  368. }
  369. /** {@inheritDoc} */
  370. public Graphics2DAdapter getGraphics2DAdapter() {
  371. return new AFPGraphics2DAdapter();
  372. }
  373. /**
  374. * {@inheritDoc}
  375. */
  376. public void startVParea(CTM ctm, Rectangle2D clippingRect) {
  377. // dummy not used
  378. }
  379. /**
  380. * {@inheritDoc}
  381. */
  382. public void endVParea() {
  383. // dummy not used
  384. }
  385. /**
  386. * Renders a region viewport. <p>
  387. *
  388. * The region may clip the area and it establishes a position from where
  389. * the region is placed.</p>
  390. *
  391. * @param port The region viewport to be rendered
  392. */
  393. public void renderRegionViewport(RegionViewport port) {
  394. if (port != null) {
  395. Rectangle2D view = port.getViewArea();
  396. // The CTM will transform coordinates relative to
  397. // this region-reference area into page coords, so
  398. // set origin for the region to 0,0.
  399. currentBPPosition = 0;
  400. currentIPPosition = 0;
  401. RegionReference regionReference = port.getRegionReference();
  402. handleRegionTraits(port);
  403. /*
  404. _afpDataStream.startOverlay(mpts2units(view.getX())
  405. , mpts2units(view.getY())
  406. , mpts2units(view.getWidth())
  407. , mpts2units(view.getHeight())
  408. , rotation);
  409. */
  410. pushViewPortPos(new ViewPortPos(view, regionReference.getCTM()));
  411. if (regionReference.getRegionClass() == FO_REGION_BODY) {
  412. renderBodyRegion((BodyRegion) regionReference);
  413. } else {
  414. renderRegion(regionReference);
  415. }
  416. /*
  417. _afpDataStream.endOverlay();
  418. */
  419. popViewPortPos();
  420. }
  421. }
  422. /**
  423. * {@inheritDoc}
  424. */
  425. protected void renderBlockViewport(BlockViewport bv, List children) {
  426. // clip and position viewport if necessary
  427. // save positions
  428. int saveIP = currentIPPosition;
  429. int saveBP = currentBPPosition;
  430. CTM ctm = bv.getCTM();
  431. int borderPaddingStart = bv.getBorderAndPaddingWidthStart();
  432. int borderPaddingBefore = bv.getBorderAndPaddingWidthBefore();
  433. //This is the content-rect
  434. float width = (float)bv.getIPD() / 1000f;
  435. float height = (float)bv.getBPD() / 1000f;
  436. if (bv.getPositioning() == Block.ABSOLUTE
  437. || bv.getPositioning() == Block.FIXED) {
  438. //For FIXED, we need to break out of the current viewports to the
  439. //one established by the page. We save the state stack for restoration
  440. //after the block-container has been painted. See below.
  441. List breakOutList = null;
  442. if (bv.getPositioning() == Block.FIXED) {
  443. breakOutList = breakOutOfStateStack();
  444. }
  445. AffineTransform positionTransform = new AffineTransform();
  446. positionTransform.translate(bv.getXOffset(), bv.getYOffset());
  447. //"left/"top" (bv.getX/YOffset()) specify the position of the content rectangle
  448. positionTransform.translate(-borderPaddingStart, -borderPaddingBefore);
  449. //skipping fox:transform here
  450. //saveGraphicsState();
  451. //Viewport position
  452. //concatenateTransformationMatrix(mptToPt(positionTransform));
  453. //Background and borders
  454. float bpwidth = (borderPaddingStart + bv.getBorderAndPaddingWidthEnd()) / 1000f;
  455. float bpheight = (borderPaddingBefore + bv.getBorderAndPaddingWidthAfter()) / 1000f;
  456. Point2D ptSrc = new Point(0, 0);
  457. Point2D ptDst = positionTransform.transform(ptSrc, null);
  458. Rectangle2D borderRect = new Rectangle2D.Double(ptDst.getX(), ptDst.getY(),
  459. 1000 * (width + bpwidth), 1000 * (height + bpheight));
  460. pushViewPortPos(new ViewPortPos(borderRect, new CTM(positionTransform)));
  461. drawBackAndBorders(bv, 0, 0, width + bpwidth, height + bpheight);
  462. //Shift to content rectangle after border painting
  463. AffineTransform contentRectTransform = new AffineTransform();
  464. contentRectTransform.translate(borderPaddingStart, borderPaddingBefore);
  465. //concatenateTransformationMatrix(mptToPt(contentRectTransform));
  466. ptSrc = new Point(0, 0);
  467. ptDst = contentRectTransform.transform(ptSrc, null);
  468. Rectangle2D contentRect = new Rectangle2D.Double(ptDst.getX(), ptDst.getY(),
  469. 1000 * width, 1000 * height);
  470. pushViewPortPos(new ViewPortPos(contentRect, new CTM(contentRectTransform)));
  471. //Clipping is not supported, yet
  472. //Rectangle2D clippingRect = null;
  473. //clippingRect = new Rectangle(0, 0, bv.getIPD(), bv.getBPD());
  474. //saveGraphicsState();
  475. //Set up coordinate system for content rectangle
  476. AffineTransform contentTransform = ctm.toAffineTransform();
  477. //concatenateTransformationMatrix(mptToPt(contentTransform));
  478. contentRect = new Rectangle2D.Double(0, 0, 1000 * width, 1000 * height);
  479. pushViewPortPos(new ViewPortPos(contentRect, new CTM(contentTransform)));
  480. currentIPPosition = 0;
  481. currentBPPosition = 0;
  482. renderBlocks(bv, children);
  483. popViewPortPos();
  484. popViewPortPos();
  485. //restoreGraphicsState();
  486. popViewPortPos();
  487. //restoreGraphicsState();
  488. if (breakOutList != null) {
  489. restoreStateStackAfterBreakOut(breakOutList);
  490. }
  491. currentIPPosition = saveIP;
  492. currentBPPosition = saveBP;
  493. } else {
  494. currentBPPosition += bv.getSpaceBefore();
  495. //borders and background in the old coordinate system
  496. handleBlockTraits(bv);
  497. //Advance to start of content area
  498. currentIPPosition += bv.getStartIndent();
  499. CTM tempctm = new CTM(containingIPPosition, currentBPPosition);
  500. ctm = tempctm.multiply(ctm);
  501. //Now adjust for border/padding
  502. currentBPPosition += borderPaddingBefore;
  503. Rectangle2D clippingRect = null;
  504. clippingRect = new Rectangle(currentIPPosition, currentBPPosition,
  505. bv.getIPD(), bv.getBPD());
  506. //startVParea(ctm, clippingRect);
  507. pushViewPortPos(new ViewPortPos(clippingRect, ctm));
  508. currentIPPosition = 0;
  509. currentBPPosition = 0;
  510. renderBlocks(bv, children);
  511. //endVParea();
  512. popViewPortPos();
  513. currentIPPosition = saveIP;
  514. currentBPPosition = saveBP;
  515. currentBPPosition += (int)(bv.getAllocBPD());
  516. }
  517. }
  518. /** {@inheritDoc} */
  519. protected void concatenateTransformationMatrix(AffineTransform at) {
  520. //Not used here since AFPRenderer defines its own renderBlockViewport() method.
  521. throw new UnsupportedOperationException("NYI");
  522. }
  523. /**
  524. * {@inheritDoc}
  525. */
  526. public void renderPage(PageViewport pageViewport) {
  527. // initializeRootExtensions(page);
  528. // this.currentFontFamily = "";
  529. this.currentFontSize = 0;
  530. this.pageFontCounter = 0;
  531. this.currentPageFonts.clear();
  532. // this.lineCache = new HashSet();
  533. Rectangle2D bounds = pageViewport.getViewArea();
  534. this.pageWidth = mpts2units(bounds.getWidth());
  535. this.pageHeight = mpts2units(bounds.getHeight());
  536. if (pages != null && pages.containsKey(pageViewport)) {
  537. this.afpDataStream.restorePage((PageObject) pages.remove(pageViewport));
  538. } else {
  539. // renderPageGroupExtensions(page);
  540. final int pageRotation = 0;
  541. this.afpDataStream.startPage(pageWidth, pageHeight, pageRotation,
  542. getResolution(), getResolution());
  543. renderPageObjectExtensions(pageViewport);
  544. }
  545. pushViewPortPos(new ViewPortPos());
  546. renderPageAreas(pageViewport.getPage());
  547. Iterator i = currentPageFonts.values().iterator();
  548. while (i.hasNext()) {
  549. AFPFontAttributes afpFontAttributes = (AFPFontAttributes) i.next();
  550. afpDataStream.createFont(
  551. (byte)afpFontAttributes.getFontReference(),
  552. afpFontAttributes.getFont(),
  553. afpFontAttributes.getPointSize());
  554. }
  555. try {
  556. afpDataStream.endPage();
  557. } catch (IOException ioex) {
  558. // TODO What shall we do?
  559. }
  560. popViewPortPos();
  561. }
  562. /**
  563. * {@inheritDoc}
  564. */
  565. public void clip() {
  566. // TODO
  567. }
  568. /**
  569. * {@inheritDoc}
  570. */
  571. public void clipRect(float x, float y, float width, float height) {
  572. // TODO
  573. }
  574. /**
  575. * {@inheritDoc}
  576. */
  577. public void moveTo(float x, float y) {
  578. // TODO
  579. }
  580. /**
  581. * {@inheritDoc}
  582. */
  583. public void lineTo(float x, float y) {
  584. // TODO
  585. }
  586. /**
  587. * {@inheritDoc}
  588. */
  589. public void closePath() {
  590. // TODO
  591. }
  592. /**
  593. * {@inheritDoc}
  594. */
  595. public void fillRect(float x, float y, float width, float height) {
  596. /*
  597. afpDataStream.createShading(
  598. pts2units(x),
  599. pts2units(y),
  600. pts2units(width),
  601. pts2units(height),
  602. currentColor.getRed(),
  603. currentColor.getGreen(),
  604. currentColor.getBlue());
  605. */
  606. afpDataStream.createLine(
  607. pts2units(x),
  608. pts2units(y),
  609. pts2units(x + width),
  610. pts2units(y),
  611. pts2units(height),
  612. currentColor);
  613. }
  614. /**
  615. * {@inheritDoc}
  616. */
  617. public void drawBorderLine(float x1, float y1, float x2, float y2,
  618. boolean horz, boolean startOrBefore, int style, Color col) {
  619. float w = x2 - x1;
  620. float h = y2 - y1;
  621. if ((w < 0) || (h < 0)) {
  622. log.error("Negative extent received. Border won't be painted.");
  623. return;
  624. }
  625. switch (style) {
  626. case Constants.EN_DOUBLE:
  627. if (horz) {
  628. float h3 = h / 3;
  629. float ym1 = y1;
  630. float ym2 = ym1 + h3 + h3;
  631. afpDataStream.createLine(
  632. pts2units(x1),
  633. pts2units(ym1),
  634. pts2units(x2),
  635. pts2units(ym1),
  636. pts2units(h3),
  637. col
  638. );
  639. afpDataStream.createLine(
  640. pts2units(x1),
  641. pts2units(ym2),
  642. pts2units(x2),
  643. pts2units(ym2),
  644. pts2units(h3),
  645. col
  646. );
  647. } else {
  648. float w3 = w / 3;
  649. float xm1 = x1;
  650. float xm2 = xm1 + w3 + w3;
  651. afpDataStream.createLine(
  652. pts2units(xm1),
  653. pts2units(y1),
  654. pts2units(xm1),
  655. pts2units(y2),
  656. pts2units(w3),
  657. col
  658. );
  659. afpDataStream.createLine(
  660. pts2units(xm2),
  661. pts2units(y1),
  662. pts2units(xm2),
  663. pts2units(y2),
  664. pts2units(w3),
  665. col
  666. );
  667. }
  668. break;
  669. case Constants.EN_DASHED:
  670. if (horz) {
  671. float w2 = 2 * h;
  672. while (x1 + w2 < x2) {
  673. afpDataStream.createLine(
  674. pts2units(x1),
  675. pts2units(y1),
  676. pts2units(x1 + w2),
  677. pts2units(y1),
  678. pts2units(h),
  679. col
  680. );
  681. x1 += 2 * w2;
  682. }
  683. } else {
  684. float h2 = 2 * w;
  685. while (y1 + h2 < y2) {
  686. afpDataStream.createLine(
  687. pts2units(x1),
  688. pts2units(y1),
  689. pts2units(x1),
  690. pts2units(y1 + h2),
  691. pts2units(w),
  692. col
  693. );
  694. y1 += 2 * h2;
  695. }
  696. }
  697. break;
  698. case Constants.EN_DOTTED:
  699. if (horz) {
  700. while (x1 + h < x2) {
  701. afpDataStream.createLine(
  702. pts2units(x1),
  703. pts2units(y1),
  704. pts2units(x1 + h),
  705. pts2units(y1),
  706. pts2units(h),
  707. col
  708. );
  709. x1 += 2 * h;
  710. }
  711. } else {
  712. while (y1 + w < y2) {
  713. afpDataStream.createLine(
  714. pts2units(x1),
  715. pts2units(y1),
  716. pts2units(x1),
  717. pts2units(y1 + w),
  718. pts2units(w),
  719. col
  720. );
  721. y1 += 2 * w;
  722. }
  723. }
  724. break;
  725. case Constants.EN_GROOVE:
  726. case Constants.EN_RIDGE:
  727. {
  728. float colFactor = (style == EN_GROOVE ? 0.4f : -0.4f);
  729. if (horz) {
  730. Color uppercol = lightenColor(col, -colFactor);
  731. Color lowercol = lightenColor(col, colFactor);
  732. float h3 = h / 3;
  733. float ym1 = y1;
  734. afpDataStream.createLine(
  735. pts2units(x1),
  736. pts2units(ym1),
  737. pts2units(x2),
  738. pts2units(ym1),
  739. pts2units(h3),
  740. uppercol
  741. );
  742. afpDataStream.createLine(
  743. pts2units(x1),
  744. pts2units(ym1 + h3),
  745. pts2units(x2),
  746. pts2units(ym1 + h3),
  747. pts2units(h3),
  748. col
  749. );
  750. afpDataStream.createLine(
  751. pts2units(x1),
  752. pts2units(ym1 + h3 + h3),
  753. pts2units(x2),
  754. pts2units(ym1 + h3 + h3),
  755. pts2units(h3),
  756. lowercol
  757. );
  758. } else {
  759. Color leftcol = lightenColor(col, -colFactor);
  760. Color rightcol = lightenColor(col, colFactor);
  761. float w3 = w / 3;
  762. float xm1 = x1 + (w3 / 2);
  763. afpDataStream.createLine(
  764. pts2units(xm1),
  765. pts2units(y1),
  766. pts2units(xm1),
  767. pts2units(y2),
  768. pts2units(w3),
  769. leftcol
  770. );
  771. afpDataStream.createLine(
  772. pts2units(xm1 + w3),
  773. pts2units(y1),
  774. pts2units(xm1 + w3),
  775. pts2units(y2),
  776. pts2units(w3),
  777. col
  778. );
  779. afpDataStream.createLine(
  780. pts2units(xm1 + w3 + w3),
  781. pts2units(y1),
  782. pts2units(xm1 + w3 + w3),
  783. pts2units(y2),
  784. pts2units(w3),
  785. rightcol
  786. );
  787. }
  788. break;
  789. }
  790. case Constants.EN_HIDDEN:
  791. break;
  792. case Constants.EN_INSET:
  793. case Constants.EN_OUTSET:
  794. default:
  795. afpDataStream.createLine(
  796. pts2units(x1),
  797. pts2units(y1),
  798. pts2units(horz ? x2 : x1),
  799. pts2units(horz ? y1 : y2),
  800. pts2units(Math.abs(horz ? (y2 - y1) : (x2 - x1))),
  801. col
  802. );
  803. }
  804. }
  805. /**
  806. * {@inheritDoc}
  807. */
  808. protected RendererContext createRendererContext(int x, int y, int width, int height,
  809. Map foreignAttributes) {
  810. RendererContext context;
  811. context = super.createRendererContext(x, y, width, height, foreignAttributes);
  812. context.setProperty(AFPRendererContextConstants.AFP_GRAYSCALE,
  813. Boolean.valueOf(!this.colorImages));
  814. return context;
  815. }
  816. private static final ImageFlavor[] FLAVORS = new ImageFlavor[]
  817. {ImageFlavor.RAW_CCITTFAX,
  818. ImageFlavor.GRAPHICS2D,
  819. ImageFlavor.BUFFERED_IMAGE,
  820. ImageFlavor.RENDERED_IMAGE,
  821. ImageFlavor.XML_DOM};
  822. /** {@inheritDoc} */
  823. public void drawImage(String uri, Rectangle2D pos, Map foreignAttributes) {
  824. uri = URISpecification.getURL(uri);
  825. Rectangle posInt = new Rectangle(
  826. (int)pos.getX(),
  827. (int)pos.getY(),
  828. (int)pos.getWidth(),
  829. (int)pos.getHeight());
  830. Point origin = new Point(currentIPPosition, currentBPPosition);
  831. int x = origin.x + posInt.x;
  832. int y = origin.y + posInt.y;
  833. String name = null;
  834. if (pageSegmentsMap != null) {
  835. name = (String) pageSegmentsMap.get(uri);
  836. }
  837. if (name != null) {
  838. afpDataStream.createIncludePageSegment(name, mpts2units(x), mpts2units(y));
  839. } else {
  840. ImageManager manager = getUserAgent().getFactory().getImageManager();
  841. ImageInfo info = null;
  842. try {
  843. ImageSessionContext sessionContext = getUserAgent().getImageSessionContext();
  844. info = manager.getImageInfo(uri, sessionContext);
  845. //Only now fully load/prepare the image
  846. Map hints = ImageUtil.getDefaultHints(sessionContext);
  847. org.apache.xmlgraphics.image.loader.Image img = manager.getImage(
  848. info, FLAVORS, hints, sessionContext);
  849. //...and process the image
  850. if (img instanceof ImageGraphics2D) {
  851. ImageGraphics2D imageG2D = (ImageGraphics2D)img;
  852. RendererContext context = createRendererContext(
  853. posInt.x, posInt.y,
  854. posInt.width, posInt.height, foreignAttributes);
  855. getGraphics2DAdapter().paintImage(imageG2D.getGraphics2DImagePainter(),
  856. context,
  857. origin.x + posInt.x, origin.y + posInt.y,
  858. posInt.width, posInt.height);
  859. } else if (img instanceof ImageRendered) {
  860. ImageRendered imgRend = (ImageRendered)img;
  861. RenderedImage ri = imgRend.getRenderedImage();
  862. drawBufferedImage(ri, getResolution(),
  863. posInt.x + currentIPPosition,
  864. posInt.y + currentBPPosition,
  865. posInt.width,
  866. posInt.height);
  867. } else if (img instanceof ImageRawCCITTFax) {
  868. ImageRawCCITTFax ccitt = (ImageRawCCITTFax)img;
  869. int afpx = mpts2units(posInt.x + currentIPPosition);
  870. int afpy = mpts2units(posInt.y + currentBPPosition);
  871. int afpw = mpts2units(posInt.getWidth());
  872. int afph = mpts2units(posInt.getHeight());
  873. int afpres = getResolution();
  874. ImageObject io = afpDataStream.getImageObject(afpx, afpy, afpw, afph,
  875. afpres, afpres);
  876. io.setImageParameters(
  877. (int) (ccitt.getSize().getDpiHorizontal() * 10),
  878. (int) (ccitt.getSize().getDpiVertical() * 10),
  879. ccitt.getSize().getWidthPx(),
  880. ccitt.getSize().getHeightPx());
  881. int compression = ccitt.getCompression();
  882. switch (compression) {
  883. case TIFFImage.COMP_FAX_G3_1D :
  884. io.setImageEncoding((byte) 0x80);
  885. break;
  886. case TIFFImage.COMP_FAX_G3_2D :
  887. io.setImageEncoding((byte) 0x81);
  888. break;
  889. case TIFFImage.COMP_FAX_G4_2D :
  890. io.setImageEncoding((byte) 0x82);
  891. break;
  892. default:
  893. throw new IllegalStateException(
  894. "Invalid compression scheme: " + compression);
  895. }
  896. InputStream in = ccitt.createInputStream();
  897. try {
  898. byte[] buf = IOUtils.toByteArray(in);
  899. io.setImageData(buf);
  900. } finally {
  901. IOUtils.closeQuietly(in);
  902. }
  903. } else if (img instanceof ImageXMLDOM) {
  904. ImageXMLDOM imgXML = (ImageXMLDOM)img;
  905. renderDocument(imgXML.getDocument(), imgXML.getRootNamespace(),
  906. pos, foreignAttributes);
  907. } else {
  908. throw new UnsupportedOperationException("Unsupported image type: " + img);
  909. }
  910. } catch (ImageException ie) {
  911. ResourceEventProducer eventProducer = ResourceEventProducer.Provider.get(
  912. getUserAgent().getEventBroadcaster());
  913. eventProducer.imageError(this, (info != null ? info.toString() : uri), ie, null);
  914. } catch (FileNotFoundException fe) {
  915. ResourceEventProducer eventProducer = ResourceEventProducer.Provider.get(
  916. getUserAgent().getEventBroadcaster());
  917. eventProducer.imageNotFound(this, (info != null ? info.toString() : uri), fe, null);
  918. } catch (IOException ioe) {
  919. ResourceEventProducer eventProducer = ResourceEventProducer.Provider.get(
  920. getUserAgent().getEventBroadcaster());
  921. eventProducer.imageIOError(this, (info != null ? info.toString() : uri), ioe, null);
  922. }
  923. /*
  924. ImageFactory fact = userAgent.getFactory().getImageFactory();
  925. FopImage fopimage = fact.getImage(url, userAgent);
  926. if (fopimage == null) {
  927. return;
  928. }
  929. if (!fopimage.load(FopImage.DIMENSIONS)) {
  930. return;
  931. }
  932. String mime = fopimage.getMimeType();
  933. if ("text/xml".equals(mime) || MimeConstants.MIME_SVG.equals(mime)) {
  934. if (!fopimage.load(FopImage.ORIGINAL_DATA)) {
  935. return;
  936. }
  937. Document doc = ((XMLImage) fopimage).getDocument();
  938. String ns = ((XMLImage) fopimage).getNameSpace();
  939. renderDocument(doc, ns, pos, foreignAttributes);
  940. } else if (MimeConstants.MIME_EPS.equals(mime)) {
  941. log.warn("EPS images are not supported by this renderer");
  942. */
  943. /*
  944. * } else if (MimeConstants.MIME_JPEG.equals(mime)) { if
  945. * (!fopimage.load(FopImage.ORIGINAL_DATA)) { return; }
  946. * fact.releaseImage(url, userAgent);
  947. *
  948. * int x = mpts2units(pos.getX() + currentIPPosition); int y =
  949. * mpts2units(pos.getY() + currentBPPosition); int w =
  950. * mpts2units(pos.getWidth()); int h =
  951. * mpts2units(pos.getHeight()); ImageObject io =
  952. * _afpDataStream.getImageObject(); io.setImageViewport(x, y, w,
  953. * h); io.setImageParameters(
  954. * (int)(fopimage.getHorizontalResolution() * 10),
  955. * (int)(fopimage.getVerticalResolution() * 10),
  956. * fopimage.getWidth(), fopimage.getHeight() );
  957. * io.setImageIDESize((byte)fopimage.getBitsPerPixel());
  958. * io.setImageEncoding((byte)0x83);
  959. * io.setImageData(fopimage.getRessourceBytes());
  960. *//*
  961. } else if (MimeConstants.MIME_TIFF.equals(mime)
  962. && fopimage instanceof TIFFImage) {
  963. TIFFImage tiffImage = (TIFFImage) fopimage;
  964. int x = mpts2units(pos.getX() + currentIPPosition);
  965. int y = mpts2units(pos.getY() + currentBPPosition);
  966. int w = mpts2units(pos.getWidth());
  967. int h = mpts2units(pos.getHeight());
  968. ImageObject io = afpDataStream.getImageObject(x, y, w, h,
  969. getResolution(), getResolution());
  970. io.setImageParameters(
  971. (int)(fopimage.getHorizontalResolution() * 10),
  972. (int)(fopimage.getVerticalResolution() * 10),
  973. fopimage.getWidth(),
  974. fopimage.getHeight()
  975. );
  976. if (tiffImage.getStripCount() == 1) {
  977. int comp = tiffImage.getCompression();
  978. if (comp == 3) {
  979. if (!fopimage.load(FopImage.ORIGINAL_DATA)) {
  980. return;
  981. }
  982. io.setImageEncoding((byte)0x81);
  983. io.setImageData(fopimage.getRessourceBytes());
  984. } else if (comp == 4) {
  985. if (!fopimage.load(FopImage.ORIGINAL_DATA)) {
  986. return;
  987. }
  988. io.setImageEncoding((byte)0x82);
  989. io.setImageData(fopimage.getRessourceBytes());
  990. } else {
  991. if (!fopimage.load(FopImage.BITMAP)) {
  992. return;
  993. }
  994. convertToGrayScaleImage(io, fopimage.getBitmaps(),
  995. fopimage.getWidth(), fopimage.getHeight());
  996. }
  997. } else {
  998. if (!fopimage.load(FopImage.BITMAP)) {
  999. return;
  1000. }
  1001. convertToGrayScaleImage(io, fopimage.getBitmaps(),
  1002. fopimage.getWidth(), fopimage.getHeight());
  1003. }
  1004. } else {
  1005. if (!fopimage.load(FopImage.BITMAP)) {
  1006. return;
  1007. }
  1008. fact.releaseImage(url, userAgent);
  1009. int x = mpts2units(pos.getX() + currentIPPosition);
  1010. int y = mpts2units(pos.getY() + currentBPPosition);
  1011. int w = mpts2units(pos.getWidth());
  1012. int h = mpts2units(pos.getHeight());
  1013. ImageObject io = afpDataStream.getImageObject(x, y, w, h,
  1014. getResolution(), getResolution());
  1015. io.setImageParameters(
  1016. (int)(fopimage.getHorizontalResolution() * 10),
  1017. (int)(fopimage.getVerticalResolution() * 10),
  1018. fopimage.getWidth(),
  1019. fopimage.getHeight()
  1020. );
  1021. if (colorImages) {
  1022. io.setImageIDESize((byte)24);
  1023. io.setImageData(fopimage.getBitmaps());
  1024. } else {
  1025. convertToGrayScaleImage(io, fopimage.getBitmaps(),
  1026. fopimage.getWidth(), fopimage.getHeight());
  1027. }
  1028. }*/
  1029. }
  1030. }
  1031. /**
  1032. * Writes a RenderedImage to an OutputStream as raw sRGB bitmaps.
  1033. *
  1034. * @param image
  1035. * the RenderedImage
  1036. * @param out
  1037. * the OutputStream
  1038. * @throws IOException
  1039. * In case of an I/O error.
  1040. */
  1041. public static void writeImage(RenderedImage image, OutputStream out)
  1042. throws IOException {
  1043. ImageEncodingHelper.encodeRenderedImageAsRGB(image, out);
  1044. }
  1045. /**
  1046. * Draws a BufferedImage to AFP.
  1047. *
  1048. * @param image
  1049. * the RenderedImage
  1050. * @param imageResolution
  1051. * the resolution of the BufferedImage
  1052. * @param x
  1053. * the x coordinate (in mpt)
  1054. * @param y
  1055. * the y coordinate (in mpt)
  1056. * @param w
  1057. * the width of the viewport (in mpt)
  1058. * @param h
  1059. * the height of the viewport (in mpt)
  1060. */
  1061. public void drawBufferedImage(RenderedImage image, int imageResolution, int x,
  1062. int y, int w, int h) {
  1063. int afpx = mpts2units(x);
  1064. int afpy = mpts2units(y);
  1065. int afpw = mpts2units(w);
  1066. int afph = mpts2units(h);
  1067. int afpres = getResolution();
  1068. ByteArrayOutputStream baout = new ByteArrayOutputStream();
  1069. try {
  1070. // Serialize image
  1071. //TODO Eventually, this should be changed not to buffer as this increases the
  1072. //memory consumption (see PostScript output)
  1073. writeImage(image, baout);
  1074. byte[] buf = baout.toByteArray();
  1075. // Generate image
  1076. ImageObject io = afpDataStream.getImageObject(afpx, afpy, afpw,
  1077. afph, afpres, afpres);
  1078. io.setImageParameters(imageResolution, imageResolution,
  1079. image.getWidth(), image.getHeight());
  1080. if (colorImages) {
  1081. io.setImageIDESize((byte)24);
  1082. io.setImageData(buf);
  1083. } else {
  1084. // TODO Teach it how to handle grayscale BufferedImages directly
  1085. // because this is pretty inefficient
  1086. convertToGrayScaleImage(io, buf,
  1087. image.getWidth(), image.getHeight(), this.bitsPerPixel);
  1088. }
  1089. } catch (IOException ioe) {
  1090. ResourceEventProducer eventProducer = ResourceEventProducer.Provider.get(
  1091. getUserAgent().getEventBroadcaster());
  1092. eventProducer.imageWritingError(this, ioe);
  1093. }
  1094. }
  1095. /**
  1096. * Establishes a new foreground or fill color.
  1097. * {@inheritDoc}
  1098. */
  1099. public void updateColor(Color col, boolean fill) {
  1100. if (fill) {
  1101. currentColor = col;
  1102. }
  1103. }
  1104. /**
  1105. * Restores the state stack after a break out.
  1106. * @param breakOutList the state stack to restore.
  1107. */
  1108. public void restoreStateStackAfterBreakOut(List breakOutList) {
  1109. }
  1110. /**
  1111. * Breaks out of the state stack to handle fixed block-containers.
  1112. * @return the saved state stack to recreate later
  1113. */
  1114. public List breakOutOfStateStack() {
  1115. return null;
  1116. }
  1117. /** Saves the graphics state of the rendering engine. */
  1118. public void saveGraphicsState() {
  1119. }
  1120. /** Restores the last graphics state of the rendering engine. */
  1121. public void restoreGraphicsState() {
  1122. }
  1123. /** Indicates the beginning of a text object. */
  1124. public void beginTextObject() {
  1125. }
  1126. /** Indicates the end of a text object. */
  1127. public void endTextObject() {
  1128. }
  1129. /**
  1130. * {@inheritDoc}
  1131. */
  1132. public void renderImage(Image image, Rectangle2D pos) {
  1133. String url = image.getURL();
  1134. drawImage(url, pos);
  1135. }
  1136. /**
  1137. * {@inheritDoc}
  1138. */
  1139. public void renderText(TextArea text) {
  1140. renderInlineAreaBackAndBorders(text);
  1141. String name = getInternalFontNameForArea(text);
  1142. currentFontSize = ((Integer) text.getTrait(Trait.FONT_SIZE)).intValue();
  1143. AFPFont tf = (AFPFont) fontInfo.getFonts().get(name);
  1144. Color col = (Color) text.getTrait(Trait.COLOR);
  1145. int vsci = mpts2units(tf.getWidth(' ', currentFontSize) / 1000
  1146. + text.getTextWordSpaceAdjust()
  1147. + text.getTextLetterSpaceAdjust());
  1148. // word.getOffset() = only height of text itself
  1149. // currentBlockIPPosition: 0 for beginning of line; nonzero
  1150. // where previous line area failed to take up entire allocated space
  1151. int rx = currentIPPosition + text.getBorderAndPaddingWidthStart();
  1152. int bl = currentBPPosition + text.getOffset() + text.getBaselineOffset();
  1153. // Set letterSpacing
  1154. //float ls = fs.getLetterSpacing() / this.currentFontSize;
  1155. String worddata = text.getText();
  1156. // Create an AFPFontAttributes object from the current font details
  1157. AFPFontAttributes afpFontAttributes = new AFPFontAttributes(name, tf, currentFontSize);
  1158. if (!currentPageFonts.containsKey(afpFontAttributes.getFontKey())) {
  1159. // Font not found on current page, so add the new one
  1160. pageFontCounter++;
  1161. afpFontAttributes.setFontReference(pageFontCounter);
  1162. currentPageFonts.put(
  1163. afpFontAttributes.getFontKey(),
  1164. afpFontAttributes);
  1165. } else {
  1166. // Use the previously stored font attributes
  1167. afpFontAttributes = (AFPFontAttributes) currentPageFonts.get(
  1168. afpFontAttributes.getFontKey());
  1169. }
  1170. // Try and get the encoding to use for the font
  1171. String encoding = null;
  1172. try {
  1173. encoding = tf.getCharacterSet(currentFontSize).getEncoding();
  1174. } catch (Throwable ex) {
  1175. encoding = AFPConstants.EBCIDIC_ENCODING;
  1176. log.warn(
  1177. "renderText():: Error getting encoding for font '"
  1178. + tf.getFullName()
  1179. + "' - using default encoding "
  1180. + encoding);
  1181. }
  1182. try {
  1183. afpDataStream.createText(
  1184. afpFontAttributes.getFontReference(),
  1185. mpts2units(rx),
  1186. mpts2units(bl),
  1187. col,
  1188. vsci,
  1189. mpts2units(text.getTextLetterSpaceAdjust()),
  1190. worddata.getBytes(encoding));
  1191. } catch (UnsupportedEncodingException usee) {
  1192. log.error(
  1193. "renderText:: Font "
  1194. + afpFontAttributes.getFontKey()
  1195. + " caused UnsupportedEncodingException");
  1196. }
  1197. super.renderText(text);
  1198. renderTextDecoration(tf, currentFontSize, text, bl, rx);
  1199. }
  1200. /**
  1201. * {@inheritDoc}
  1202. */
  1203. public void renderWord(WordArea word) {
  1204. // UNUSED
  1205. // String name = getInternalFontNameForArea(word.getParentArea());
  1206. // int size = ((Integer)
  1207. // word.getParentArea().getTrait(Trait.FONT_SIZE)).intValue();
  1208. // AFPFont tf = (AFPFont) fontInfo.getFonts().get(name);
  1209. //
  1210. // String s = word.getWord();
  1211. //
  1212. // FontMetrics metrics = fontInfo.getMetricsFor(name);
  1213. super.renderWord(word);
  1214. }
  1215. /**
  1216. * {@inheritDoc}
  1217. */
  1218. public void renderSpace(SpaceArea space) {
  1219. // UNUSED
  1220. // String name = getInternalFontNameForArea(space.getParentArea());
  1221. // int size = ((Integer)
  1222. // space.getParentArea().getTrait(Trait.FONT_SIZE)).intValue();
  1223. // AFPFont tf = (AFPFont) fontInfo.getFonts().get(name);
  1224. //
  1225. // String s = space.getSpace();
  1226. //
  1227. // FontMetrics metrics = fontInfo.getMetricsFor(name);
  1228. super.renderSpace(space);
  1229. }
  1230. /**
  1231. * Render leader area.
  1232. * This renders a leader area which is an area with a rule.
  1233. * @param area the leader area to render
  1234. */
  1235. public void renderLeader(Leader area) {
  1236. renderInlineAreaBackAndBorders(area);
  1237. int style = area.getRuleStyle();
  1238. float startx = (currentIPPosition + area.getBorderAndPaddingWidthStart()) / 1000f;
  1239. float starty = (currentBPPosition + area.getOffset()) / 1000f;
  1240. float endx = (currentIPPosition + area.getBorderAndPaddingWidthStart()
  1241. + area.getIPD()) / 1000f;
  1242. float ruleThickness = area.getRuleThickness() / 1000f;
  1243. Color col = (Color)area.getTrait(Trait.COLOR);
  1244. switch (style) {
  1245. case EN_SOLID:
  1246. case EN_DASHED:
  1247. case EN_DOUBLE:
  1248. case EN_DOTTED:
  1249. case EN_GROOVE:
  1250. case EN_RIDGE:
  1251. drawBorderLine(startx, starty, endx, starty + ruleThickness,
  1252. true, true, style, col);
  1253. break;
  1254. default:
  1255. throw new UnsupportedOperationException("rule style not supported");
  1256. }
  1257. super.renderLeader(area);
  1258. }
  1259. /**
  1260. * Sets the AFPRenderer options
  1261. * @param options the <code>Map</code> containing the options
  1262. */
  1263. // UNUSED
  1264. // public void setOptions(Map options) {
  1265. //
  1266. // this.afpOptions = options;
  1267. //
  1268. // }
  1269. /**
  1270. * Determines the orientation from the string representation, this method
  1271. * guarantees to return a value of either 0, 90, 180 or 270.
  1272. *
  1273. * @return the orientation
  1274. */
  1275. // UNUSED
  1276. // private int getOrientation(String orientationString) {
  1277. //
  1278. // int orientation = 0;
  1279. // if (orientationString != null && orientationString.length() > 0) {
  1280. // try {
  1281. // orientation = Integer.parseInt(orientationString);
  1282. // } catch (NumberFormatException nfe) {
  1283. // log.error("Cannot use orientation of " + orientation
  1284. // + " defaulting to zero.");
  1285. // orientation = 0;
  1286. // }
  1287. // } else {
  1288. // orientation = 0;
  1289. // }
  1290. // switch (orientation) {
  1291. // case 0:
  1292. // break;
  1293. // case 90:
  1294. // break;
  1295. // case 180:
  1296. // break;
  1297. // case 270:
  1298. // break;
  1299. // default:
  1300. // log.error("Cannot use orientation of " + orientation
  1301. // + " defaulting to zero.");
  1302. // orientation = 0;
  1303. // break;
  1304. // }
  1305. //
  1306. // return orientation;
  1307. //
  1308. // }
  1309. /**
  1310. * Sets the rotation to be used for portrait pages, valid values are 0
  1311. * (default), 90, 180, 270.
  1312. *
  1313. * @param rotation
  1314. * The rotation in degrees.
  1315. */
  1316. public void setPortraitRotation(int rotation) {
  1317. if (rotation == 0
  1318. || rotation == 90
  1319. || rotation == 180
  1320. || rotation == 270) {
  1321. portraitRotation = rotation;
  1322. } else {
  1323. throw new IllegalArgumentException("The portrait rotation must be one"
  1324. + " of the values 0, 90, 180, 270");
  1325. }
  1326. }
  1327. /**
  1328. * Sets the rotation to be used for landsacpe pages, valid values are 0, 90,
  1329. * 180, 270 (default).
  1330. *
  1331. * @param rotation
  1332. * The rotation in degrees.
  1333. */
  1334. public void setLandscapeRotation(int rotation) {
  1335. if (rotation == 0
  1336. || rotation == 90
  1337. || rotation == 180
  1338. || rotation == 270) {
  1339. landscapeRotation = rotation;
  1340. } else {
  1341. throw new IllegalArgumentException("The landscape rotation must be one"
  1342. + " of the values 0, 90, 180, 270");
  1343. }
  1344. }
  1345. /**
  1346. * Get the MIME type of the renderer.
  1347. *
  1348. * @return The MIME type of the renderer
  1349. */
  1350. public String getMimeType() {
  1351. return MimeConstants.MIME_AFP;
  1352. }
  1353. /**
  1354. * Method to render the page extension.
  1355. * <p>
  1356. *
  1357. * @param pageViewport the page object
  1358. */
  1359. private void renderPageObjectExtensions(PageViewport pageViewport) {
  1360. pageSegmentsMap = null;
  1361. if (pageViewport.getExtensionAttachments() != null
  1362. && pageViewport.getExtensionAttachments().size() > 0) {
  1363. // Extract all AFPPageSetup instances from the attachment list on
  1364. // the s-p-m
  1365. Iterator i = pageViewport.getExtensionAttachments().iterator();
  1366. while (i.hasNext()) {
  1367. ExtensionAttachment attachment = (ExtensionAttachment)i.next();
  1368. if (AFPPageSetup.CATEGORY.equals(attachment.getCategory())) {
  1369. AFPPageSetup aps = (AFPPageSetup) attachment;
  1370. String element = aps.getElementName();
  1371. if (AFPElementMapping.INCLUDE_PAGE_OVERLAY.equals(element)) {
  1372. String overlay = aps.getName();
  1373. if (overlay != null) {
  1374. afpDataStream.createIncludePageOverlay(overlay);
  1375. }
  1376. } else if (AFPElementMapping.INCLUDE_PAGE_SEGMENT
  1377. .equals(element)) {
  1378. String name = aps.getName();
  1379. String source = aps.getValue();
  1380. if (pageSegmentsMap == null) {
  1381. pageSegmentsMap = new HashMap();
  1382. }
  1383. pageSegmentsMap.put(source, name);
  1384. } else if (AFPElementMapping.TAG_LOGICAL_ELEMENT
  1385. .equals(element)) {
  1386. String name = aps.getName();
  1387. String value = aps.getValue();
  1388. if (pageSegmentsMap == null) {
  1389. pageSegmentsMap = new HashMap();
  1390. }
  1391. afpDataStream.createTagLogicalElement(name, value);
  1392. } else if (AFPElementMapping.NO_OPERATION.equals(element)) {
  1393. String content = aps.getContent();
  1394. if (content != null) {
  1395. afpDataStream.createNoOperation(content);
  1396. }
  1397. }
  1398. }
  1399. }
  1400. }
  1401. }
  1402. /**
  1403. * Converts FOP mpt measurement to afp measurement units
  1404. * @param mpt the millipoints value
  1405. */
  1406. private int mpts2units(int mpt) {
  1407. return mpts2units((double) mpt);
  1408. }
  1409. /**
  1410. * Converts FOP pt measurement to afp measurement units
  1411. * @param mpt the millipoints value
  1412. */
  1413. private int pts2units(float mpt) {
  1414. return mpts2units(mpt * 1000d);
  1415. }
  1416. /**
  1417. * Converts FOP mpt measurement to afp measurement units
  1418. *
  1419. * @param mpt
  1420. * the millipoints value
  1421. * @return afp measurement unit value
  1422. */
  1423. private int mpts2units(double mpt) {
  1424. return (int)Math.round(mpt / (DPI_CONVERSION_FACTOR / getResolution()));
  1425. }
  1426. /**
  1427. * Converts a byte array containing 24 bit RGB image data to a grayscale
  1428. * image.
  1429. *
  1430. * @param io
  1431. * the target image object
  1432. * @param raw
  1433. * the buffer containing the RGB image data
  1434. * @param width
  1435. * the width of the image in pixels
  1436. * @param height
  1437. * the height of the image in pixels
  1438. * @param bitsPerPixel
  1439. * the number of bits to use per pixel
  1440. */
  1441. protected static void convertToGrayScaleImage(ImageObject io, byte[] raw, int width,
  1442. int height, int bitsPerPixel) {
  1443. int pixelsPerByte = 8 / bitsPerPixel;
  1444. int bytewidth = (width / pixelsPerByte);
  1445. if ((width % pixelsPerByte) != 0) {
  1446. bytewidth++;
  1447. }
  1448. byte[] bw = new byte[height * bytewidth];
  1449. byte ib;
  1450. for (int y = 0; y < height; y++) {
  1451. ib = 0;
  1452. int i = 3 * y * width;
  1453. for (int x = 0; x < width; x++, i += 3) {
  1454. // see http://www.jguru.com/faq/view.jsp?EID=221919
  1455. double greyVal = 0.212671d * ((int) raw[i] & 0xff) + 0.715160d
  1456. * ((int) raw[i + 1] & 0xff) + 0.072169d
  1457. * ((int) raw[i + 2] & 0xff);
  1458. switch (bitsPerPixel) {
  1459. case 1:
  1460. if (greyVal < 128) {
  1461. ib |= (byte) (1 << (7 - (x % 8)));
  1462. }
  1463. break;
  1464. case 4:
  1465. greyVal /= 16;
  1466. ib |= (byte) ((byte) greyVal << ((1 - (x % 2)) * 4));
  1467. break;
  1468. case 8:
  1469. ib = (byte) greyVal;
  1470. break;
  1471. default:
  1472. throw new UnsupportedOperationException(
  1473. "Unsupported bits per pixel: " + bitsPerPixel);
  1474. }
  1475. if ((x % pixelsPerByte) == (pixelsPerByte - 1)
  1476. || ((x + 1) == width)) {
  1477. bw[(y * bytewidth) + (x / pixelsPerByte)] = ib;
  1478. ib = 0;
  1479. }
  1480. }
  1481. }
  1482. io.setImageIDESize((byte) bitsPerPixel);
  1483. io.setImageData(bw);
  1484. }
  1485. private final class ViewPortPos {
  1486. private int x = 0;
  1487. private int y = 0;
  1488. private int rot = 0;
  1489. private ViewPortPos() {
  1490. }
  1491. private ViewPortPos(Rectangle2D view, CTM ctm) {
  1492. ViewPortPos currentVP = (ViewPortPos) viewPortPositions
  1493. .get(viewPortPositions.size() - 1);
  1494. int xOrigin;
  1495. int yOrigin;
  1496. int width;
  1497. int height;
  1498. switch (currentVP.rot) {
  1499. case 90:
  1500. width = mpts2units(view.getHeight());
  1501. height = mpts2units(view.getWidth());
  1502. xOrigin = pageWidth - width - mpts2units(view.getY())
  1503. - currentVP.y;
  1504. yOrigin = mpts2units(view.getX()) + currentVP.x;
  1505. break;
  1506. case 180:
  1507. width = mpts2units(view.getWidth());
  1508. height = mpts2units(view.getHeight());
  1509. xOrigin = pageWidth - width - mpts2units(view.getX())
  1510. - currentVP.x;
  1511. yOrigin = pageHeight - height - mpts2units(view.getY())
  1512. - currentVP.y;
  1513. break;
  1514. case 270:
  1515. width = mpts2units(view.getHeight());
  1516. height = mpts2units(view.getWidth());
  1517. xOrigin = mpts2units(view.getY()) + currentVP.y;
  1518. yOrigin = pageHeight - height - mpts2units(view.getX())
  1519. - currentVP.x;
  1520. break;
  1521. default:
  1522. xOrigin = mpts2units(view.getX()) + currentVP.x;
  1523. yOrigin = mpts2units(view.getY()) + currentVP.y;
  1524. width = mpts2units(view.getWidth());
  1525. height = mpts2units(view.getHeight());
  1526. break;
  1527. }
  1528. this.rot = currentVP.rot;
  1529. double[] ctmf = ctm.toArray();
  1530. if (ctmf[0] == 0.0d && ctmf[1] == -1.0d && ctmf[2] == 1.0d
  1531. && ctmf[3] == 0.d) {
  1532. this.rot += 270;
  1533. } else if (ctmf[0] == -1.0d && ctmf[1] == 0.0d && ctmf[2] == 0.0d
  1534. && ctmf[3] == -1.0d) {
  1535. this.rot += 180;
  1536. } else if (ctmf[0] == 0.0d && ctmf[1] == 1.0d && ctmf[2] == -1.0d
  1537. && ctmf[3] == 0.0d) {
  1538. this.rot += 90;
  1539. }
  1540. this.rot %= 360;
  1541. switch (this.rot) {
  1542. /*
  1543. * case 0: this.x = mpts2units(view.getX()) + x; this.y =
  1544. * mpts2units(view.getY()) + y; break; case 90: this.x =
  1545. * mpts2units(view.getY()) + y; this.y = _pageWidth -
  1546. * mpts2units(view.getX() + view.getWidth()) - x; break; case 180:
  1547. * this.x = _pageWidth - mpts2units(view.getX() + view.getWidth()) -
  1548. * x; this.y = _pageHeight - mpts2units(view.getY() +
  1549. * view.getHeight()) - y; break; case 270: this.x = _pageHeight -
  1550. * mpts2units(view.getY() + view.getHeight()) - y; this.y =
  1551. * mpts2units(view.getX()) + x; break;
  1552. */
  1553. case 0:
  1554. this.x = xOrigin;
  1555. this.y = yOrigin;
  1556. break;
  1557. case 90:
  1558. this.x = yOrigin;
  1559. this.y = pageWidth - width - xOrigin;
  1560. break;
  1561. case 180:
  1562. this.x = pageWidth - width - xOrigin;
  1563. this.y = pageHeight - height - yOrigin;
  1564. break;
  1565. case 270:
  1566. this.x = pageHeight - height - yOrigin;
  1567. this.y = xOrigin;
  1568. break;
  1569. default:
  1570. }
  1571. }
  1572. public String toString() {
  1573. return "x:" + x + " y:" + y + " rot:" + rot;
  1574. }
  1575. }
  1576. private List viewPortPositions = new ArrayList();
  1577. private void pushViewPortPos(ViewPortPos vpp) {
  1578. viewPortPositions.add(vpp);
  1579. afpDataStream.setOffsets(vpp.x, vpp.y, vpp.rot);
  1580. }
  1581. private void popViewPortPos() {
  1582. viewPortPositions.remove(viewPortPositions.size() - 1);
  1583. if (viewPortPositions.size() > 0) {
  1584. ViewPortPos vpp = (ViewPortPos)viewPortPositions.get(viewPortPositions.size() - 1);
  1585. afpDataStream.setOffsets(vpp.x, vpp.y, vpp.rot);
  1586. }
  1587. }
  1588. /**
  1589. * Sets the number of bits used per pixel
  1590. *
  1591. * @param bitsPerPixel
  1592. * number of bits per pixel
  1593. */
  1594. public void setBitsPerPixel(int bitsPerPixel) {
  1595. this.bitsPerPixel = bitsPerPixel;
  1596. switch (bitsPerPixel) {
  1597. case 1:
  1598. case 4:
  1599. case 8:
  1600. break;
  1601. default:
  1602. log.warn("Invalid bits_per_pixel value, must be 1, 4 or 8.");
  1603. bitsPerPixel = 8;
  1604. break;
  1605. }
  1606. }
  1607. /**
  1608. * Sets whether images are color or not
  1609. *
  1610. * @param colorImages
  1611. * color image output
  1612. */
  1613. public void setColorImages(boolean colorImages) {
  1614. this.colorImages = colorImages;
  1615. }
  1616. /**
  1617. * Sets the output/device resolution
  1618. *
  1619. * @param resolution
  1620. * the output resolution (dpi)
  1621. */
  1622. public void setResolution(int resolution) {
  1623. if (log.isDebugEnabled()) {
  1624. log.debug("renderer-resolution set to: " + resolution + "dpi");
  1625. }
  1626. this.resolution = resolution;
  1627. }
  1628. /**
  1629. * Returns the output/device resolution.
  1630. * @return the resolution in dpi
  1631. */
  1632. public int getResolution() {
  1633. return this.resolution;
  1634. }
  1635. }