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.

IFRenderer.java 47KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307
  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.intermediate;
  19. import java.awt.Color;
  20. import java.awt.Dimension;
  21. import java.awt.Point;
  22. import java.awt.Rectangle;
  23. import java.awt.geom.AffineTransform;
  24. import java.awt.geom.Rectangle2D;
  25. import java.io.IOException;
  26. import java.io.OutputStream;
  27. import java.util.Arrays;
  28. import java.util.Iterator;
  29. import java.util.List;
  30. import java.util.Locale;
  31. import java.util.Map;
  32. import java.util.Stack;
  33. import javax.xml.transform.stream.StreamResult;
  34. import org.w3c.dom.Document;
  35. import org.xml.sax.SAXException;
  36. import org.apache.batik.parser.AWTTransformProducer;
  37. import org.apache.commons.logging.Log;
  38. import org.apache.commons.logging.LogFactory;
  39. import org.apache.xmlgraphics.xmp.Metadata;
  40. import org.apache.xmlgraphics.xmp.schemas.DublinCoreAdapter;
  41. import org.apache.xmlgraphics.xmp.schemas.DublinCoreSchema;
  42. import org.apache.xmlgraphics.xmp.schemas.XMPBasicAdapter;
  43. import org.apache.xmlgraphics.xmp.schemas.XMPBasicSchema;
  44. import org.apache.fop.Version;
  45. import org.apache.fop.apps.FOPException;
  46. import org.apache.fop.apps.MimeConstants;
  47. import org.apache.fop.area.Area;
  48. import org.apache.fop.area.AreaTreeObject;
  49. import org.apache.fop.area.Block;
  50. import org.apache.fop.area.BlockViewport;
  51. import org.apache.fop.area.BookmarkData;
  52. import org.apache.fop.area.CTM;
  53. import org.apache.fop.area.DestinationData;
  54. import org.apache.fop.area.OffDocumentExtensionAttachment;
  55. import org.apache.fop.area.OffDocumentItem;
  56. import org.apache.fop.area.PageSequence;
  57. import org.apache.fop.area.PageViewport;
  58. import org.apache.fop.area.RegionViewport;
  59. import org.apache.fop.area.Trait;
  60. import org.apache.fop.area.inline.AbstractTextArea;
  61. import org.apache.fop.area.inline.ForeignObject;
  62. import org.apache.fop.area.inline.Image;
  63. import org.apache.fop.area.inline.InlineArea;
  64. import org.apache.fop.area.inline.InlineParent;
  65. import org.apache.fop.area.inline.Leader;
  66. import org.apache.fop.area.inline.SpaceArea;
  67. import org.apache.fop.area.inline.TextArea;
  68. import org.apache.fop.area.inline.Viewport;
  69. import org.apache.fop.area.inline.WordArea;
  70. import org.apache.fop.datatypes.URISpecification;
  71. import org.apache.fop.fo.extensions.ExtensionAttachment;
  72. import org.apache.fop.fo.extensions.xmp.XMPMetadata;
  73. import org.apache.fop.fonts.Font;
  74. import org.apache.fop.fonts.FontInfo;
  75. import org.apache.fop.fonts.FontTriplet;
  76. import org.apache.fop.fonts.LazyFont;
  77. import org.apache.fop.fonts.Typeface;
  78. import org.apache.fop.render.AbstractPathOrientedRenderer;
  79. import org.apache.fop.render.Renderer;
  80. import org.apache.fop.render.intermediate.extensions.AbstractAction;
  81. import org.apache.fop.render.intermediate.extensions.ActionSet;
  82. import org.apache.fop.render.intermediate.extensions.Bookmark;
  83. import org.apache.fop.render.intermediate.extensions.BookmarkTree;
  84. import org.apache.fop.render.intermediate.extensions.GoToXYAction;
  85. import org.apache.fop.render.intermediate.extensions.Link;
  86. import org.apache.fop.render.intermediate.extensions.NamedDestination;
  87. import org.apache.fop.render.intermediate.extensions.URIAction;
  88. import org.apache.fop.render.pdf.PDFEventProducer;
  89. import org.apache.fop.traits.BorderProps;
  90. import org.apache.fop.traits.RuleStyle;
  91. /**
  92. * This renderer implementation is an adapter to the {@link IFPainter} interface. It is used
  93. * to generate content using FOP's intermediate format.
  94. */
  95. public class IFRenderer extends AbstractPathOrientedRenderer {
  96. //TODO Many parts of the Renderer infrastructure are using floats (coordinates in points)
  97. //instead of ints (in millipoints). A lot of conversion to and from is performed.
  98. //When the new IF is established, the Renderer infrastructure should be revisited so check
  99. //if optimizations can be done to avoid int->float->int conversions.
  100. /** logging instance */
  101. protected static final Log log = LogFactory.getLog(IFRenderer.class);
  102. /** XML MIME type */
  103. public static final String IF_MIME_TYPE = MimeConstants.MIME_FOP_IF;
  104. private IFDocumentHandler documentHandler;
  105. private IFPainter painter;
  106. /** If not null, the XMLRenderer will mimic another renderer by using its font setup. */
  107. protected Renderer mimic;
  108. private boolean inPageSequence = false;
  109. private Stack graphicContextStack = new Stack();
  110. private Stack viewportDimensionStack = new Stack();
  111. private IFGraphicContext graphicContext = new IFGraphicContext();
  112. //private Stack groupStack = new Stack();
  113. private Metadata documentMetadata;
  114. /**
  115. * Maps XSL-FO element IDs to their on-page XY-positions
  116. * Must be used in conjunction with the page reference to fully specify the details
  117. * of a "go-to" action.
  118. */
  119. private Map idPositions = new java.util.HashMap();
  120. /**
  121. * The "go-to" actions in idGoTos that are not complete yet
  122. */
  123. private List unfinishedGoTos = new java.util.ArrayList();
  124. // can't use a Set because PDFGoTo.equals returns true if the target is the same,
  125. // even if the object number differs
  126. /** Maps unique PageViewport key to page indices (for link target handling) */
  127. protected Map pageIndices = new java.util.HashMap();
  128. private BookmarkTree bookmarkTree;
  129. private List deferredDestinations = new java.util.ArrayList();
  130. private List deferredLinks = new java.util.ArrayList();
  131. private ActionSet actionSet = new ActionSet();
  132. private TextUtil textUtil = new TextUtil();
  133. /**
  134. * Main constructor
  135. */
  136. public IFRenderer() {
  137. }
  138. /** {@inheritDoc} */
  139. public String getMimeType() {
  140. return IF_MIME_TYPE;
  141. }
  142. /**
  143. * Sets the {@link IFDocumentHandler} to be used by the {@link IFRenderer}.
  144. * @param documentHandler the {@link IFDocumentHandler}
  145. */
  146. public void setDocumentHandler(IFDocumentHandler documentHandler) {
  147. this.documentHandler = documentHandler;
  148. }
  149. /** {@inheritDoc} */
  150. public void setupFontInfo(FontInfo inFontInfo) throws FOPException {
  151. if (this.documentHandler == null) {
  152. this.documentHandler = createDefaultDocumentHandler();
  153. }
  154. IFUtil.setupFonts(this.documentHandler, inFontInfo);
  155. this.fontInfo = inFontInfo;
  156. }
  157. private void handleIFException(IFException ife) {
  158. if (ife.getCause() instanceof SAXException) {
  159. throw new RuntimeException(ife.getCause());
  160. } else {
  161. throw new RuntimeException(ife);
  162. }
  163. }
  164. private void handleIFExceptionWithIOException(IFException ife) throws IOException {
  165. if (ife.getCause() instanceof IOException) {
  166. throw (IOException)ife.getCause();
  167. } else {
  168. handleIFException(ife);
  169. }
  170. }
  171. /** {@inheritDoc} */
  172. public boolean supportsOutOfOrder() {
  173. return (this.documentHandler != null
  174. ? this.documentHandler.supportsPagesOutOfOrder() : false);
  175. }
  176. /**
  177. * Returns the document navigation handler if available/supported.
  178. * @return the document navigation handler or null if not supported
  179. */
  180. protected IFDocumentNavigationHandler getDocumentNavigationHandler() {
  181. return this.documentHandler.getDocumentNavigationHandler();
  182. }
  183. /**
  184. * Indicates whether document navigation features are supported by the document handler.
  185. * @return true if document navigation features are available
  186. */
  187. protected boolean hasDocumentNavigation() {
  188. return getDocumentNavigationHandler() != null;
  189. }
  190. /**
  191. * Creates a default {@link IFDocumentHandler} when none has been set.
  192. * @return the default IFDocumentHandler
  193. */
  194. protected IFDocumentHandler createDefaultDocumentHandler() {
  195. IFSerializer serializer = new IFSerializer();
  196. serializer.setContext(new IFContext(getUserAgent()));
  197. return serializer;
  198. }
  199. /** {@inheritDoc} */
  200. public void startRenderer(OutputStream outputStream)
  201. throws IOException {
  202. try {
  203. if (outputStream != null) {
  204. StreamResult result = new StreamResult(outputStream);
  205. if (getUserAgent().getOutputFile() != null) {
  206. result.setSystemId(
  207. getUserAgent().getOutputFile().toURI().toURL().toExternalForm());
  208. }
  209. if (this.documentHandler == null) {
  210. this.documentHandler = createDefaultDocumentHandler();
  211. }
  212. this.documentHandler.setResult(result);
  213. }
  214. super.startRenderer(null);
  215. if (log.isDebugEnabled()) {
  216. log.debug("Rendering areas via IF document handler ("
  217. + this.documentHandler.getClass().getName() + ")...");
  218. }
  219. documentHandler.startDocument();
  220. documentHandler.startDocumentHeader();
  221. } catch (IFException e) {
  222. handleIFExceptionWithIOException(e);
  223. }
  224. }
  225. /** {@inheritDoc} */
  226. public void stopRenderer() throws IOException {
  227. try {
  228. if (this.inPageSequence) {
  229. documentHandler.endPageSequence();
  230. this.inPageSequence = false;
  231. }
  232. documentHandler.startDocumentTrailer();
  233. //Wrap up document navigation
  234. if (hasDocumentNavigation()) {
  235. finishOpenGoTos();
  236. Iterator iter = this.deferredDestinations.iterator();
  237. while (iter.hasNext()) {
  238. NamedDestination dest = (NamedDestination)iter.next();
  239. iter.remove();
  240. getDocumentNavigationHandler().renderNamedDestination(dest);
  241. }
  242. if (this.bookmarkTree != null) {
  243. getDocumentNavigationHandler().renderBookmarkTree(this.bookmarkTree);
  244. }
  245. }
  246. documentHandler.endDocumentTrailer();
  247. documentHandler.endDocument();
  248. } catch (IFException e) {
  249. handleIFExceptionWithIOException(e);
  250. }
  251. pageIndices.clear();
  252. idPositions.clear();
  253. actionSet.clear();
  254. super.stopRenderer();
  255. log.debug("Rendering finished.");
  256. }
  257. /** {@inheritDoc} */
  258. public void processOffDocumentItem(OffDocumentItem odi) {
  259. if (odi instanceof DestinationData) {
  260. // render Destinations
  261. renderDestination((DestinationData) odi);
  262. } else if (odi instanceof BookmarkData) {
  263. // render Bookmark-Tree
  264. renderBookmarkTree((BookmarkData) odi);
  265. } else if (odi instanceof OffDocumentExtensionAttachment) {
  266. ExtensionAttachment attachment = ((OffDocumentExtensionAttachment)odi).getAttachment();
  267. if (XMPMetadata.CATEGORY.equals(attachment.getCategory())) {
  268. renderXMPMetadata((XMPMetadata)attachment);
  269. } else {
  270. try {
  271. this.documentHandler.handleExtensionObject(attachment);
  272. } catch (IFException ife) {
  273. handleIFException(ife);
  274. }
  275. }
  276. }
  277. }
  278. private void renderDestination(DestinationData dd) {
  279. if (!hasDocumentNavigation()) {
  280. return;
  281. }
  282. String targetID = dd.getIDRef();
  283. if (targetID == null || targetID.length() == 0) {
  284. throw new IllegalArgumentException("DestinationData must contain a ID reference");
  285. }
  286. PageViewport pv = dd.getPageViewport();
  287. if (pv != null) {
  288. GoToXYAction action = getGoToActionForID(targetID, pv.getPageIndex());
  289. NamedDestination namedDestination = new NamedDestination(targetID, action);
  290. this.deferredDestinations.add(namedDestination);
  291. } else {
  292. //Warning already issued by AreaTreeHandler (debug level is sufficient)
  293. log.debug("Unresolved destination item received: " + dd.getIDRef());
  294. }
  295. }
  296. /**
  297. * Renders a Bookmark-Tree object
  298. * @param bookmarks the BookmarkData object containing all the Bookmark-Items
  299. */
  300. protected void renderBookmarkTree(BookmarkData bookmarks) {
  301. assert this.bookmarkTree == null;
  302. if (!hasDocumentNavigation()) {
  303. return;
  304. }
  305. this.bookmarkTree = new BookmarkTree();
  306. for (int i = 0; i < bookmarks.getCount(); i++) {
  307. BookmarkData ext = bookmarks.getSubData(i);
  308. Bookmark b = renderBookmarkItem(ext);
  309. bookmarkTree.addBookmark(b);
  310. }
  311. }
  312. private Bookmark renderBookmarkItem(BookmarkData bookmarkItem) {
  313. String targetID = bookmarkItem.getIDRef();
  314. if (targetID == null || targetID.length() == 0) {
  315. throw new IllegalArgumentException("DestinationData must contain a ID reference");
  316. }
  317. GoToXYAction action = null;
  318. PageViewport pv = bookmarkItem.getPageViewport();
  319. if (pv != null) {
  320. action = getGoToActionForID(targetID, pv.getPageIndex());
  321. } else {
  322. //Warning already issued by AreaTreeHandler (debug level is sufficient)
  323. log.debug("Bookmark with IDRef \"" + targetID + "\" has a null PageViewport.");
  324. }
  325. Bookmark b = new Bookmark(
  326. bookmarkItem.getBookmarkTitle(),
  327. bookmarkItem.showChildItems(),
  328. action);
  329. for (int i = 0; i < bookmarkItem.getCount(); i++) {
  330. b.addChildBookmark(renderBookmarkItem(bookmarkItem.getSubData(i)));
  331. }
  332. return b;
  333. }
  334. private void renderXMPMetadata(XMPMetadata metadata) {
  335. this.documentMetadata = metadata.getMetadata();
  336. }
  337. private GoToXYAction getGoToActionForID(String targetID, int pageIndex) {
  338. // Already a GoToXY present for this target? If not, create.
  339. GoToXYAction action = (GoToXYAction)actionSet.get(targetID);
  340. //GoToXYAction action = (GoToXYAction)idGoTos.get(targetID);
  341. if (action == null) {
  342. if (pageIndex < 0) {
  343. //pageIndex = page
  344. }
  345. Point position = (Point)idPositions.get(targetID);
  346. // can the GoTo already be fully filled in?
  347. if (pageIndex >= 0 && position != null) {
  348. action = new GoToXYAction(targetID, pageIndex, position);
  349. } else {
  350. // Not complete yet, can't use getPDFGoTo:
  351. action = new GoToXYAction(targetID, pageIndex, null);
  352. unfinishedGoTos.add(action);
  353. }
  354. action = (GoToXYAction)actionSet.put(action);
  355. //idGoTos.put(targetID, action);
  356. }
  357. return action;
  358. }
  359. private void finishOpenGoTos() {
  360. int count = unfinishedGoTos.size();
  361. if (count > 0) {
  362. Point defaultPos = new Point(0, 0); // top-o-page
  363. while (!unfinishedGoTos.isEmpty()) {
  364. GoToXYAction action = (GoToXYAction)unfinishedGoTos.get(0);
  365. noteGoToPosition(action, defaultPos);
  366. }
  367. PDFEventProducer eventProducer = PDFEventProducer.Provider.get(
  368. getUserAgent().getEventBroadcaster());
  369. eventProducer.nonFullyResolvedLinkTargets(this, count);
  370. // dysfunctional if pageref is null
  371. }
  372. }
  373. private void noteGoToPosition(GoToXYAction action, Point position) {
  374. action.setTargetLocation(position);
  375. try {
  376. getDocumentNavigationHandler().addResolvedAction(action);
  377. } catch (IFException ife) {
  378. handleIFException(ife);
  379. }
  380. unfinishedGoTos.remove(action);
  381. }
  382. private void noteGoToPosition(GoToXYAction action, PageViewport pv, Point position) {
  383. action.setPageIndex(pv.getPageIndex());
  384. noteGoToPosition(action, position);
  385. }
  386. private void saveAbsolutePosition(String id, PageViewport pv,
  387. int relativeIPP, int relativeBPP, AffineTransform tf) {
  388. Point position = new Point(relativeIPP, relativeBPP);
  389. tf.transform(position, position);
  390. idPositions.put(id, position);
  391. // is there already a GoTo action waiting to be completed?
  392. GoToXYAction action = (GoToXYAction)actionSet.get(id);
  393. if (action != null) {
  394. noteGoToPosition(action, pv, position);
  395. }
  396. }
  397. private void saveAbsolutePosition(String id, int relativeIPP, int relativeBPP) {
  398. saveAbsolutePosition(id, this.currentPageViewport,
  399. relativeIPP, relativeBPP, graphicContext.getTransform());
  400. }
  401. private void saveBlockPosIfTargetable(Block block) {
  402. String id = getTargetableID(block);
  403. if (hasDocumentNavigation() && id != null) {
  404. // FIXME: Like elsewhere in the renderer code, absolute and relative
  405. // directions are happily mixed here. This makes sure that the
  406. // links point to the right location, but it is not correct.
  407. int ipp = block.getXOffset();
  408. int bpp = block.getYOffset() + block.getSpaceBefore();
  409. int positioning = block.getPositioning();
  410. if (!(positioning == Block.FIXED || positioning == Block.ABSOLUTE)) {
  411. ipp += currentIPPosition;
  412. bpp += currentBPPosition;
  413. }
  414. saveAbsolutePosition(id, currentPageViewport, ipp, bpp, graphicContext.getTransform());
  415. }
  416. }
  417. private void saveInlinePosIfTargetable(InlineArea inlineArea) {
  418. String id = getTargetableID(inlineArea);
  419. if (hasDocumentNavigation() && id != null) {
  420. int extraMarginBefore = 5000; // millipoints
  421. int ipp = currentIPPosition;
  422. int bpp = currentBPPosition + inlineArea.getOffset() - extraMarginBefore;
  423. saveAbsolutePosition(id, ipp, bpp);
  424. }
  425. }
  426. private String getTargetableID(Area area) {
  427. String id = (String) area.getTrait(Trait.PROD_ID);
  428. if (id == null || id.length() == 0
  429. || !currentPageViewport.isFirstWithID(id)
  430. || idPositions.containsKey(id)) {
  431. return null;
  432. } else {
  433. return id;
  434. }
  435. }
  436. /** {@inheritDoc} */
  437. public void startPageSequence(PageSequence pageSequence) {
  438. try {
  439. if (this.inPageSequence) {
  440. documentHandler.endPageSequence();
  441. documentHandler.getContext().setLanguage(null);
  442. } else {
  443. if (this.documentMetadata == null) {
  444. this.documentMetadata = createDefaultDocumentMetadata();
  445. }
  446. documentHandler.handleExtensionObject(this.documentMetadata);
  447. documentHandler.endDocumentHeader();
  448. this.inPageSequence = true;
  449. }
  450. establishForeignAttributes(pageSequence.getForeignAttributes());
  451. documentHandler.getContext().setLanguage(toLocale(pageSequence));
  452. documentHandler.startPageSequence(null);
  453. resetForeignAttributes();
  454. processExtensionAttachments(pageSequence);
  455. } catch (IFException e) {
  456. handleIFException(e);
  457. }
  458. }
  459. private Locale toLocale(PageSequence pageSequence) {
  460. if (pageSequence.getLanguage() != null) {
  461. if (pageSequence.getCountry() != null) {
  462. return new Locale(pageSequence.getLanguage(), pageSequence.getCountry());
  463. } else {
  464. return new Locale(pageSequence.getLanguage());
  465. }
  466. }
  467. return null;
  468. }
  469. private Metadata createDefaultDocumentMetadata() {
  470. Metadata xmp = new Metadata();
  471. DublinCoreAdapter dc = DublinCoreSchema.getAdapter(xmp);
  472. if (getUserAgent().getTitle() != null) {
  473. dc.setTitle(getUserAgent().getTitle());
  474. }
  475. if (getUserAgent().getAuthor() != null) {
  476. dc.addCreator(getUserAgent().getAuthor());
  477. }
  478. if (getUserAgent().getKeywords() != null) {
  479. dc.addSubject(getUserAgent().getKeywords());
  480. }
  481. XMPBasicAdapter xmpBasic = XMPBasicSchema.getAdapter(xmp);
  482. if (getUserAgent().getProducer() != null) {
  483. xmpBasic.setCreatorTool(getUserAgent().getProducer());
  484. } else {
  485. xmpBasic.setCreatorTool(Version.getVersion());
  486. }
  487. xmpBasic.setMetadataDate(new java.util.Date());
  488. if (getUserAgent().getCreationDate() != null) {
  489. xmpBasic.setCreateDate(getUserAgent().getCreationDate());
  490. } else {
  491. xmpBasic.setCreateDate(xmpBasic.getMetadataDate());
  492. }
  493. return xmp;
  494. }
  495. /** {@inheritDoc} */
  496. public void preparePage(PageViewport page) {
  497. super.preparePage(page);
  498. }
  499. /** {@inheritDoc} */
  500. public void renderPage(PageViewport page) throws IOException, FOPException {
  501. if (log.isTraceEnabled()) {
  502. log.trace("renderPage() " + page);
  503. }
  504. try {
  505. pageIndices.put(page.getKey(), new Integer(page.getPageIndex()));
  506. Rectangle viewArea = page.getViewArea();
  507. Dimension dim = new Dimension(viewArea.width, viewArea.height);
  508. establishForeignAttributes(page.getForeignAttributes());
  509. documentHandler.startPage(page.getPageIndex(), page.getPageNumberString(),
  510. page.getSimplePageMasterName(), dim);
  511. resetForeignAttributes();
  512. documentHandler.startPageHeader();
  513. //Add page attachments to page header
  514. processExtensionAttachments(page);
  515. documentHandler.endPageHeader();
  516. this.painter = documentHandler.startPageContent();
  517. super.renderPage(page);
  518. this.painter = null;
  519. documentHandler.endPageContent();
  520. documentHandler.startPageTrailer();
  521. if (hasDocumentNavigation()) {
  522. Iterator iter = this.deferredLinks.iterator();
  523. while (iter.hasNext()) {
  524. Link link = (Link)iter.next();
  525. iter.remove();
  526. getDocumentNavigationHandler().renderLink(link);
  527. }
  528. }
  529. documentHandler.endPageTrailer();
  530. establishForeignAttributes(page.getForeignAttributes());
  531. documentHandler.endPage();
  532. resetForeignAttributes();
  533. } catch (IFException e) {
  534. handleIFException(e);
  535. }
  536. }
  537. private void processExtensionAttachments(AreaTreeObject area) throws IFException {
  538. if (area.hasExtensionAttachments()) {
  539. for (Iterator iter = area.getExtensionAttachments().iterator();
  540. iter.hasNext();) {
  541. ExtensionAttachment attachment = (ExtensionAttachment) iter.next();
  542. this.documentHandler.handleExtensionObject(attachment);
  543. }
  544. }
  545. }
  546. private void establishForeignAttributes(Map foreignAttributes) {
  547. documentHandler.getContext().setForeignAttributes(foreignAttributes);
  548. }
  549. private void resetForeignAttributes() {
  550. documentHandler.getContext().resetForeignAttributes();
  551. }
  552. private void establishStructurePointer(String ptr) {
  553. documentHandler.getContext().setStructurePointer(ptr);
  554. }
  555. private void resetStructurePointer() {
  556. documentHandler.getContext().resetStructurePointer();
  557. }
  558. /** {@inheritDoc} */
  559. protected void saveGraphicsState() {
  560. graphicContextStack.push(graphicContext);
  561. graphicContext = (IFGraphicContext)graphicContext.clone();
  562. }
  563. /** {@inheritDoc} */
  564. protected void restoreGraphicsState() {
  565. while (graphicContext.getGroupStackSize() > 0) {
  566. IFGraphicContext.Group[] groups = graphicContext.dropGroups();
  567. for (int i = groups.length - 1; i >= 0; i--) {
  568. try {
  569. groups[i].end(painter);
  570. } catch (IFException ife) {
  571. handleIFException(ife);
  572. }
  573. }
  574. }
  575. graphicContext = (IFGraphicContext)graphicContextStack.pop();
  576. }
  577. private void pushGroup(IFGraphicContext.Group group) {
  578. graphicContext.pushGroup(group);
  579. try {
  580. group.start(painter);
  581. } catch (IFException ife) {
  582. handleIFException(ife);
  583. }
  584. }
  585. /** {@inheritDoc} */
  586. protected List breakOutOfStateStack() {
  587. log.debug("Block.FIXED --> break out");
  588. List breakOutList = new java.util.ArrayList();
  589. while (!this.graphicContextStack.empty()) {
  590. //Handle groups
  591. IFGraphicContext.Group[] groups = graphicContext.getGroups();
  592. for (int j = groups.length - 1; j >= 0; j--) {
  593. try {
  594. groups[j].end(painter);
  595. } catch (IFException ife) {
  596. handleIFException(ife);
  597. }
  598. }
  599. breakOutList.add(0, this.graphicContext);
  600. graphicContext = (IFGraphicContext)graphicContextStack.pop();
  601. }
  602. return breakOutList;
  603. }
  604. /** {@inheritDoc} */
  605. protected void restoreStateStackAfterBreakOut(List breakOutList) {
  606. log.debug("Block.FIXED --> restoring context after break-out");
  607. for (int i = 0, c = breakOutList.size(); i < c; i++) {
  608. graphicContextStack.push(graphicContext);
  609. this.graphicContext = (IFGraphicContext)breakOutList.get(i);
  610. //Handle groups
  611. IFGraphicContext.Group[] groups = graphicContext.getGroups();
  612. for (int j = 0, jc = groups.length; j < jc; j++) {
  613. try {
  614. groups[j].start(painter);
  615. } catch (IFException ife) {
  616. handleIFException(ife);
  617. }
  618. }
  619. }
  620. log.debug("restored.");
  621. }
  622. /** {@inheritDoc} */
  623. protected void concatenateTransformationMatrix(AffineTransform at) {
  624. if (!at.isIdentity()) {
  625. concatenateTransformationMatrixMpt(ptToMpt(at), false);
  626. }
  627. }
  628. private void concatenateTransformationMatrixMpt(AffineTransform at, boolean force) {
  629. if (force || !at.isIdentity()) {
  630. if (log.isTraceEnabled()) {
  631. log.trace("-----concatenateTransformationMatrix: " + at);
  632. }
  633. IFGraphicContext.Group group = new IFGraphicContext.Group(at);
  634. pushGroup(group);
  635. }
  636. }
  637. /** {@inheritDoc} */
  638. protected void beginTextObject() {
  639. //nop - Ignore, handled by painter internally
  640. }
  641. /** {@inheritDoc} */
  642. protected void endTextObject() {
  643. //nop - Ignore, handled by painter internally
  644. }
  645. /** {@inheritDoc} */
  646. protected void renderRegionViewport(RegionViewport viewport) {
  647. Dimension dim = new Dimension(viewport.getIPD(), viewport.getBPD());
  648. viewportDimensionStack.push(dim);
  649. super.renderRegionViewport(viewport);
  650. viewportDimensionStack.pop();
  651. }
  652. /** {@inheritDoc} */
  653. protected void renderBlockViewport(BlockViewport bv, List children) {
  654. //Essentially the same code as in the super class but optimized for the IF
  655. //This is the content-rect
  656. Dimension dim = new Dimension(bv.getIPD(), bv.getBPD());
  657. viewportDimensionStack.push(dim);
  658. // save positions
  659. int saveIP = currentIPPosition;
  660. int saveBP = currentBPPosition;
  661. CTM ctm = bv.getCTM();
  662. int borderPaddingStart = bv.getBorderAndPaddingWidthStart();
  663. int borderPaddingBefore = bv.getBorderAndPaddingWidthBefore();
  664. if (bv.getPositioning() == Block.ABSOLUTE
  665. || bv.getPositioning() == Block.FIXED) {
  666. //For FIXED, we need to break out of the current viewports to the
  667. //one established by the page. We save the state stack for restoration
  668. //after the block-container has been painted. See below.
  669. List breakOutList = null;
  670. if (bv.getPositioning() == Block.FIXED) {
  671. breakOutList = breakOutOfStateStack();
  672. }
  673. AffineTransform positionTransform = new AffineTransform();
  674. positionTransform.translate(bv.getXOffset(), bv.getYOffset());
  675. //"left/"top" (bv.getX/YOffset()) specify the position of the content rectangle
  676. positionTransform.translate(-borderPaddingStart, -borderPaddingBefore);
  677. //Free transformation for the block-container viewport
  678. String transf;
  679. transf = bv.getForeignAttributeValue(FOX_TRANSFORM);
  680. if (transf != null) {
  681. AffineTransform freeTransform = AWTTransformProducer.createAffineTransform(transf);
  682. positionTransform.concatenate(freeTransform);
  683. }
  684. saveGraphicsState();
  685. //Viewport position
  686. concatenateTransformationMatrixMpt(positionTransform, false);
  687. //Background and borders
  688. float bpwidth = (borderPaddingStart + bv.getBorderAndPaddingWidthEnd());
  689. float bpheight = (borderPaddingBefore + bv.getBorderAndPaddingWidthAfter());
  690. drawBackAndBorders(bv, 0, 0,
  691. (dim.width + bpwidth) / 1000f, (dim.height + bpheight) / 1000f);
  692. //Shift to content rectangle after border painting
  693. AffineTransform contentRectTransform = new AffineTransform();
  694. contentRectTransform.translate(borderPaddingStart, borderPaddingBefore);
  695. concatenateTransformationMatrixMpt(contentRectTransform, false);
  696. //Clipping
  697. Rectangle clipRect = null;
  698. if (bv.getClip()) {
  699. clipRect = new Rectangle(0, 0, dim.width, dim.height);
  700. //clipRect(0f, 0f, width, height);
  701. }
  702. //saveGraphicsState();
  703. //Set up coordinate system for content rectangle
  704. AffineTransform contentTransform = ctm.toAffineTransform();
  705. //concatenateTransformationMatrixMpt(contentTransform);
  706. startViewport(contentTransform, clipRect);
  707. currentIPPosition = 0;
  708. currentBPPosition = 0;
  709. renderBlocks(bv, children);
  710. endViewport();
  711. //restoreGraphicsState();
  712. restoreGraphicsState();
  713. if (breakOutList != null) {
  714. restoreStateStackAfterBreakOut(breakOutList);
  715. }
  716. currentIPPosition = saveIP;
  717. currentBPPosition = saveBP;
  718. } else {
  719. currentBPPosition += bv.getSpaceBefore();
  720. //borders and background in the old coordinate system
  721. handleBlockTraits(bv);
  722. //Advance to start of content area
  723. currentIPPosition += bv.getStartIndent();
  724. CTM tempctm = new CTM(containingIPPosition, currentBPPosition);
  725. ctm = tempctm.multiply(ctm);
  726. //Now adjust for border/padding
  727. currentBPPosition += borderPaddingBefore;
  728. Rectangle2D clippingRect = null;
  729. if (bv.getClip()) {
  730. clippingRect = new Rectangle(currentIPPosition, currentBPPosition,
  731. bv.getIPD(), bv.getBPD());
  732. }
  733. startVParea(ctm, clippingRect);
  734. currentIPPosition = 0;
  735. currentBPPosition = 0;
  736. renderBlocks(bv, children);
  737. endVParea();
  738. currentIPPosition = saveIP;
  739. currentBPPosition = saveBP;
  740. currentBPPosition += bv.getAllocBPD();
  741. }
  742. viewportDimensionStack.pop();
  743. }
  744. /** {@inheritDoc} */
  745. public void renderViewport(Viewport viewport) {
  746. String ptr = (String) viewport.getTrait(Trait.PTR);
  747. establishStructurePointer(ptr);
  748. Dimension dim = new Dimension(viewport.getIPD(), viewport.getBPD());
  749. viewportDimensionStack.push(dim);
  750. super.renderViewport(viewport);
  751. viewportDimensionStack.pop();
  752. resetStructurePointer();
  753. }
  754. /** {@inheritDoc} */
  755. protected void startVParea(CTM ctm, Rectangle2D clippingRect) {
  756. if (log.isTraceEnabled()) {
  757. log.trace("startVParea() ctm=" + ctm + ", clippingRect=" + clippingRect);
  758. }
  759. AffineTransform at = new AffineTransform(ctm.toArray());
  760. Rectangle clipRect = null;
  761. if (clippingRect != null) {
  762. clipRect = new Rectangle(
  763. (int)clippingRect.getMinX() - currentIPPosition,
  764. (int)clippingRect.getMinY() - currentBPPosition,
  765. (int)clippingRect.getWidth(), (int)clippingRect.getHeight());
  766. }
  767. startViewport(at, clipRect);
  768. if (log.isTraceEnabled()) {
  769. log.trace("startVPArea: " + at + " --> " + graphicContext.getTransform());
  770. }
  771. }
  772. private void startViewport(AffineTransform at, Rectangle clipRect) {
  773. saveGraphicsState();
  774. try {
  775. IFGraphicContext.Viewport viewport = new IFGraphicContext.Viewport(
  776. at, (Dimension)viewportDimensionStack.peek(), clipRect);
  777. graphicContext.pushGroup(viewport);
  778. viewport.start(painter);
  779. } catch (IFException e) {
  780. handleIFException(e);
  781. }
  782. }
  783. /** {@inheritDoc} */
  784. protected void endVParea() {
  785. log.trace("endVParea()");
  786. endViewport();
  787. if (log.isTraceEnabled()) {
  788. log.trace("endVPArea() --> " + graphicContext.getTransform());
  789. }
  790. }
  791. private void endViewport() {
  792. restoreGraphicsState();
  793. }
  794. /** {@inheritDoc} */
  795. protected void renderInlineArea(InlineArea inlineArea) {
  796. saveInlinePosIfTargetable(inlineArea);
  797. super.renderInlineArea(inlineArea);
  798. }
  799. /** {@inheritDoc} */
  800. public void renderInlineParent(InlineParent ip) {
  801. // stuff we only need if a link must be created:
  802. Rectangle ipRect = null;
  803. AbstractAction action = null;
  804. String ptr = (String) ip.getTrait(Trait.PTR); // used for accessibility
  805. // make sure the rect is determined *before* calling super!
  806. int ipp = currentIPPosition;
  807. int bpp = currentBPPosition + ip.getOffset();
  808. ipRect = new Rectangle(ipp, bpp, ip.getIPD(), ip.getBPD());
  809. AffineTransform transform = graphicContext.getTransform();
  810. ipRect = transform.createTransformedShape(ipRect).getBounds();
  811. // render contents
  812. super.renderInlineParent(ip);
  813. boolean linkTraitFound = false;
  814. // try INTERNAL_LINK first
  815. Trait.InternalLink intLink = (Trait.InternalLink) ip.getTrait(Trait.INTERNAL_LINK);
  816. if (intLink != null) {
  817. linkTraitFound = true;
  818. String pvKey = intLink.getPVKey();
  819. String idRef = intLink.getIDRef();
  820. boolean pvKeyOK = pvKey != null && pvKey.length() > 0;
  821. boolean idRefOK = idRef != null && idRef.length() > 0;
  822. if (pvKeyOK && idRefOK) {
  823. Integer pageIndex = (Integer)pageIndices.get(pvKey);
  824. action = getGoToActionForID(idRef, (pageIndex != null ? pageIndex.intValue() : -1));
  825. } else {
  826. //Warnings already issued by AreaTreeHandler
  827. }
  828. }
  829. // no INTERNAL_LINK, look for EXTERNAL_LINK
  830. if (!linkTraitFound) {
  831. Trait.ExternalLink extLink = (Trait.ExternalLink) ip.getTrait(Trait.EXTERNAL_LINK);
  832. if (extLink != null) {
  833. String extDest = extLink.getDestination();
  834. if (extDest != null && extDest.length() > 0) {
  835. linkTraitFound = true;
  836. action = new URIAction(extDest, extLink.newWindow());
  837. action = actionSet.put(action);
  838. }
  839. }
  840. }
  841. // warn if link trait found but not allowed, else create link
  842. if (linkTraitFound) {
  843. action.setStructurePointer(ptr); // used for accessibility
  844. Link link = new Link(action, ipRect);
  845. this.deferredLinks.add(link);
  846. }
  847. }
  848. /** {@inheritDoc} */
  849. protected void renderBlock(Block block) {
  850. if (log.isTraceEnabled()) {
  851. log.trace("renderBlock() " + block);
  852. }
  853. saveBlockPosIfTargetable(block);
  854. super.renderBlock(block);
  855. }
  856. private Typeface getTypeface(String fontName) {
  857. Typeface tf = (Typeface) fontInfo.getFonts().get(fontName);
  858. if (tf instanceof LazyFont) {
  859. tf = ((LazyFont)tf).getRealFont();
  860. }
  861. return tf;
  862. }
  863. /** {@inheritDoc} */
  864. protected void renderText(TextArea text) {
  865. if (log.isTraceEnabled()) {
  866. log.trace("renderText() " + text);
  867. }
  868. renderInlineAreaBackAndBorders(text);
  869. Color ct = (Color) text.getTrait(Trait.COLOR);
  870. beginTextObject();
  871. String fontName = getInternalFontNameForArea(text);
  872. int size = ((Integer) text.getTrait(Trait.FONT_SIZE)).intValue();
  873. String ptr = (String)text.getTrait(Trait.PTR); // used for accessibility
  874. establishStructurePointer(ptr);
  875. // This assumes that *all* CIDFonts use a /ToUnicode mapping
  876. Typeface tf = getTypeface(fontName);
  877. FontTriplet triplet = (FontTriplet)text.getTrait(Trait.FONT);
  878. try {
  879. painter.setFont(triplet.getName(), triplet.getStyle(), new Integer(triplet.getWeight()),
  880. "normal", new Integer(size), ct);
  881. } catch (IFException e) {
  882. handleIFException(e);
  883. }
  884. int rx = currentIPPosition + text.getBorderAndPaddingWidthStart();
  885. int bl = currentBPPosition + text.getOffset() + text.getBaselineOffset();
  886. textUtil.flush();
  887. textUtil.setStartPosition(rx, bl);
  888. textUtil.setSpacing(text.getTextLetterSpaceAdjust(), text.getTextWordSpaceAdjust());
  889. super.renderText(text);
  890. textUtil.flush();
  891. renderTextDecoration(tf, size, text, bl, rx);
  892. resetStructurePointer();
  893. }
  894. /** {@inheritDoc} */
  895. protected void renderWord(WordArea word) {
  896. Font font = getFontFromArea(word.getParentArea());
  897. String s = word.getWord();
  898. renderText(s, word.getLetterAdjustArray(),
  899. font, (AbstractTextArea)word.getParentArea());
  900. super.renderWord(word);
  901. }
  902. /** {@inheritDoc} */
  903. protected void renderSpace(SpaceArea space) {
  904. Font font = getFontFromArea(space.getParentArea());
  905. String s = space.getSpace();
  906. AbstractTextArea textArea = (AbstractTextArea)space.getParentArea();
  907. renderText(s, null, font, textArea);
  908. if (textUtil.combined && space.isAdjustable()) {
  909. //Used for justified text, for example
  910. int tws = textArea.getTextWordSpaceAdjust()
  911. + 2 * textArea.getTextLetterSpaceAdjust();
  912. if (tws != 0) {
  913. textUtil.adjust(tws);
  914. }
  915. }
  916. super.renderSpace(space);
  917. }
  918. /**
  919. * Does low-level rendering of text.
  920. * @param s text to render
  921. * @param letterAdjust an array of widths for letter adjustment (may be null)
  922. * @param font to font in use
  923. * @param parentArea the parent text area to retrieve certain traits from
  924. */
  925. protected void renderText(String s,
  926. int[] letterAdjust,
  927. Font font, AbstractTextArea parentArea) {
  928. int l = s.length();
  929. if (l == 0) {
  930. return;
  931. }
  932. if (letterAdjust != null) {
  933. textUtil.adjust(letterAdjust[0]);
  934. }
  935. for (int i = 0; i < l; i++) {
  936. char ch = s.charAt(i);
  937. textUtil.addChar(ch);
  938. int glyphAdjust = 0;
  939. if (textUtil.combined && font.hasChar(ch)) {
  940. int tls = (i < l - 1 ? parentArea.getTextLetterSpaceAdjust() : 0);
  941. glyphAdjust += tls;
  942. }
  943. if (letterAdjust != null && i < l - 1) {
  944. glyphAdjust += letterAdjust[i + 1];
  945. }
  946. textUtil.adjust(glyphAdjust);
  947. }
  948. }
  949. private class TextUtil {
  950. private static final int INITIAL_BUFFER_SIZE = 16;
  951. private int[] dx = new int[INITIAL_BUFFER_SIZE];
  952. private int lastDXPos = 0;
  953. private final StringBuffer text = new StringBuffer();
  954. private int startx, starty;
  955. private int tls, tws;
  956. private final boolean combined = false;
  957. void addChar(char ch) {
  958. text.append(ch);
  959. }
  960. void adjust(int adjust) {
  961. if (adjust != 0) {
  962. int idx = text.length();
  963. if (idx > dx.length - 1) {
  964. int newSize = Math.max(dx.length, idx + 1) + INITIAL_BUFFER_SIZE;
  965. int[] newDX = new int[newSize];
  966. System.arraycopy(dx, 0, newDX, 0, dx.length);
  967. dx = newDX;
  968. }
  969. dx[idx] += adjust;
  970. lastDXPos = idx;
  971. }
  972. }
  973. void reset() {
  974. if (text.length() > 0) {
  975. text.setLength(0);
  976. Arrays.fill(dx, 0);
  977. lastDXPos = 0;
  978. }
  979. }
  980. void setStartPosition(int x, int y) {
  981. this.startx = x;
  982. this.starty = y;
  983. }
  984. void setSpacing(int tls, int tws) {
  985. this.tls = tls;
  986. this.tws = tws;
  987. }
  988. void flush() {
  989. if (text.length() > 0) {
  990. try {
  991. int[] effDX = null;
  992. if (lastDXPos > 0) {
  993. int size = lastDXPos + 1;
  994. effDX = new int[size];
  995. System.arraycopy(dx, 0, effDX, 0, size);
  996. }
  997. if (combined) {
  998. painter.drawText(startx, starty, 0, 0, effDX, text.toString());
  999. } else {
  1000. painter.drawText(startx, starty, tls, tws, effDX, text.toString());
  1001. }
  1002. } catch (IFException e) {
  1003. handleIFException(e);
  1004. }
  1005. reset();
  1006. }
  1007. }
  1008. }
  1009. /** {@inheritDoc} */
  1010. public void renderImage(Image image, Rectangle2D pos) {
  1011. drawImage(image.getURL(), pos, image.getForeignAttributes());
  1012. }
  1013. /** {@inheritDoc} */
  1014. protected void drawImage(String uri, Rectangle2D pos, Map foreignAttributes) {
  1015. Rectangle posInt = new Rectangle(
  1016. currentIPPosition + (int)pos.getX(),
  1017. currentBPPosition + (int)pos.getY(),
  1018. (int)pos.getWidth(),
  1019. (int)pos.getHeight());
  1020. uri = URISpecification.getURL(uri);
  1021. try {
  1022. establishForeignAttributes(foreignAttributes);
  1023. painter.drawImage(uri, posInt);
  1024. resetForeignAttributes();
  1025. } catch (IFException ife) {
  1026. handleIFException(ife);
  1027. }
  1028. }
  1029. /** {@inheritDoc} */
  1030. public void renderForeignObject(ForeignObject fo, Rectangle2D pos) {
  1031. endTextObject();
  1032. Rectangle posInt = new Rectangle(
  1033. currentIPPosition + (int)pos.getX(),
  1034. currentBPPosition + (int)pos.getY(),
  1035. (int)pos.getWidth(),
  1036. (int)pos.getHeight());
  1037. Document doc = fo.getDocument();
  1038. try {
  1039. establishForeignAttributes(fo.getForeignAttributes());
  1040. painter.drawImage(doc, posInt);
  1041. resetForeignAttributes();
  1042. } catch (IFException ife) {
  1043. handleIFException(ife);
  1044. }
  1045. }
  1046. /** {@inheritDoc} */
  1047. public void renderLeader(Leader area) {
  1048. renderInlineAreaBackAndBorders(area);
  1049. int style = area.getRuleStyle();
  1050. int ruleThickness = area.getRuleThickness();
  1051. int startx = currentIPPosition + area.getBorderAndPaddingWidthStart();
  1052. int starty = currentBPPosition + area.getOffset() + (ruleThickness / 2);
  1053. int endx = currentIPPosition
  1054. + area.getBorderAndPaddingWidthStart()
  1055. + area.getIPD();
  1056. Color col = (Color)area.getTrait(Trait.COLOR);
  1057. Point start = new Point(startx, starty);
  1058. Point end = new Point(endx, starty);
  1059. try {
  1060. painter.drawLine(start, end, ruleThickness, col, RuleStyle.valueOf(style));
  1061. } catch (IFException ife) {
  1062. handleIFException(ife);
  1063. }
  1064. super.renderLeader(area);
  1065. }
  1066. /** {@inheritDoc} */
  1067. protected void clip() {
  1068. throw new IllegalStateException("Not used");
  1069. }
  1070. /** {@inheritDoc} */
  1071. protected void clipRect(float x, float y, float width, float height) {
  1072. pushGroup(new IFGraphicContext.Group());
  1073. try {
  1074. painter.clipRect(toMillipointRectangle(x, y, width, height));
  1075. } catch (IFException ife) {
  1076. handleIFException(ife);
  1077. }
  1078. }
  1079. /** {@inheritDoc} */
  1080. protected void closePath() {
  1081. throw new IllegalStateException("Not used");
  1082. }
  1083. /** {@inheritDoc} */
  1084. protected void drawBorders( // CSOK: ParameterNumber
  1085. float startx, float starty,
  1086. float width, float height,
  1087. BorderProps bpsBefore, BorderProps bpsAfter,
  1088. BorderProps bpsStart, BorderProps bpsEnd) {
  1089. Rectangle rect = toMillipointRectangle(startx, starty, width, height);
  1090. try {
  1091. painter.drawBorderRect(rect, bpsBefore, bpsAfter, bpsStart, bpsEnd);
  1092. } catch (IFException ife) {
  1093. handleIFException(ife);
  1094. }
  1095. }
  1096. /** {@inheritDoc} */
  1097. protected void drawBorderLine( // CSOK: ParameterNumber
  1098. float x1, float y1, float x2, float y2, boolean horz,
  1099. boolean startOrBefore, int style, Color col) {
  1100. //Simplified implementation that is only used by renderTextDecoration()
  1101. //drawBorders() is overridden and uses the Painter's high-level method drawBorderRect()
  1102. updateColor(col, true);
  1103. fillRect(x1, y1, x2 - x1, y2 - y1);
  1104. }
  1105. private int toMillipoints(float coordinate) {
  1106. return Math.round(coordinate * 1000);
  1107. }
  1108. private Rectangle toMillipointRectangle(float x, float y, float width, float height) {
  1109. return new Rectangle(
  1110. toMillipoints(x),
  1111. toMillipoints(y),
  1112. toMillipoints(width),
  1113. toMillipoints(height));
  1114. }
  1115. /** {@inheritDoc} */
  1116. protected void fillRect(float x, float y, float width, float height) {
  1117. try {
  1118. painter.fillRect(
  1119. toMillipointRectangle(x, y, width, height),
  1120. this.graphicContext.getPaint());
  1121. } catch (IFException e) {
  1122. handleIFException(e);
  1123. }
  1124. }
  1125. /** {@inheritDoc} */
  1126. protected void moveTo(float x, float y) {
  1127. throw new IllegalStateException("Not used");
  1128. }
  1129. /** {@inheritDoc} */
  1130. protected void lineTo(float x, float y) {
  1131. throw new IllegalStateException("Not used");
  1132. }
  1133. /** {@inheritDoc} */
  1134. protected void updateColor(Color col, boolean fill) {
  1135. if (fill) {
  1136. this.graphicContext.setPaint(col);
  1137. } else {
  1138. this.graphicContext.setColor(col);
  1139. }
  1140. }
  1141. }