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 53KB

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