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.

AbstractRenderer.java 44KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346
  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;
  19. // Java
  20. import java.awt.Rectangle;
  21. import java.awt.geom.AffineTransform;
  22. import java.awt.geom.Rectangle2D;
  23. import java.io.IOException;
  24. import java.io.OutputStream;
  25. import java.util.List;
  26. import java.util.Locale;
  27. import java.util.Set;
  28. import java.util.Stack;
  29. import org.w3c.dom.Document;
  30. import org.apache.commons.logging.Log;
  31. import org.apache.commons.logging.LogFactory;
  32. import org.apache.fop.ResourceEventProducer;
  33. import org.apache.fop.apps.FOPException;
  34. import org.apache.fop.apps.FOUserAgent;
  35. import org.apache.fop.area.Area;
  36. import org.apache.fop.area.BeforeFloat;
  37. import org.apache.fop.area.Block;
  38. import org.apache.fop.area.BlockParent;
  39. import org.apache.fop.area.BlockViewport;
  40. import org.apache.fop.area.BodyRegion;
  41. import org.apache.fop.area.CTM;
  42. import org.apache.fop.area.Footnote;
  43. import org.apache.fop.area.LineArea;
  44. import org.apache.fop.area.MainReference;
  45. import org.apache.fop.area.NormalFlow;
  46. import org.apache.fop.area.OffDocumentItem;
  47. import org.apache.fop.area.Page;
  48. import org.apache.fop.area.PageSequence;
  49. import org.apache.fop.area.PageViewport;
  50. import org.apache.fop.area.RegionReference;
  51. import org.apache.fop.area.RegionViewport;
  52. import org.apache.fop.area.Span;
  53. import org.apache.fop.area.Trait;
  54. import org.apache.fop.area.inline.Container;
  55. import org.apache.fop.area.inline.FilledArea;
  56. import org.apache.fop.area.inline.ForeignObject;
  57. import org.apache.fop.area.inline.Image;
  58. import org.apache.fop.area.inline.InlineArea;
  59. import org.apache.fop.area.inline.InlineBlock;
  60. import org.apache.fop.area.inline.InlineBlockParent;
  61. import org.apache.fop.area.inline.InlineParent;
  62. import org.apache.fop.area.inline.InlineViewport;
  63. import org.apache.fop.area.inline.Leader;
  64. import org.apache.fop.area.inline.Space;
  65. import org.apache.fop.area.inline.SpaceArea;
  66. import org.apache.fop.area.inline.TextArea;
  67. import org.apache.fop.area.inline.WordArea;
  68. import org.apache.fop.fo.Constants;
  69. import org.apache.fop.fo.flow.ChangeBar;
  70. import org.apache.fop.fo.properties.Property;
  71. import org.apache.fop.fonts.FontInfo;
  72. import org.apache.fop.traits.BorderProps;
  73. import org.apache.fop.traits.Direction;
  74. import org.apache.fop.traits.Visibility;
  75. /**
  76. * Abstract base class for all renderers. The Abstract renderer does all the
  77. * top level processing of the area tree and adds some abstract methods to
  78. * handle viewports. This keeps track of the current block and inline position.
  79. */
  80. public abstract class AbstractRenderer
  81. implements Renderer, Constants {
  82. /** logging instance */
  83. protected static final Log log = LogFactory.getLog("org.apache.fop.render");
  84. /**
  85. * user agent
  86. */
  87. protected FOUserAgent userAgent;
  88. /**
  89. * block progression position
  90. */
  91. protected int currentBPPosition;
  92. /**
  93. * inline progression position
  94. */
  95. protected int currentIPPosition;
  96. /**
  97. * the block progression position of the containing block used for
  98. * absolutely positioned blocks
  99. */
  100. protected int containingBPPosition;
  101. /**
  102. * the inline progression position of the containing block used for
  103. * absolutely positioned blocks
  104. */
  105. protected int containingIPPosition;
  106. /**
  107. * The "start edge" IP Position of the current column (for change bars)
  108. */
  109. protected int columnStartIPPosition;
  110. /**
  111. * The "end edge" IP Position of the current column (for change bars)
  112. */
  113. protected int columnEndIPPosition;
  114. /**
  115. * The "left" position of the current column (for change bars)
  116. */
  117. protected int columnLeftIPPosition;
  118. /**
  119. * The "right" position of the current column (for change bars)
  120. */
  121. protected int columnRightIPPosition;
  122. /**
  123. * The number of columns in the span (for change bars)
  124. */
  125. protected int columnCount;
  126. /**
  127. * The index number of the current column (for change bars)
  128. */
  129. protected int columnIndex;
  130. /**
  131. * The column width (for change bars)
  132. */
  133. protected int columnWidth;
  134. /**
  135. * The size of column gap (for change bars)
  136. */
  137. protected int columnGap;
  138. /**
  139. * The block progression direction (for change bars)
  140. */
  141. protected Direction blockProgressionDirection;
  142. /**
  143. * The inline progression direction (for change bars)
  144. */
  145. protected Direction inlineProgressionDirection;
  146. /**
  147. * Is binding on start edge of column?
  148. */
  149. protected boolean bindingOnStartEdge;
  150. /**
  151. * Is binding on end edge of column?
  152. */
  153. protected boolean bindingOnEndEdge;
  154. /**
  155. * The IP begin offset of coordinate 0
  156. */
  157. private int beginOffset;
  158. /**
  159. * the currently active PageViewport
  160. */
  161. protected PageViewport currentPageViewport;
  162. /* warned XML handlers */
  163. private Set warnedXMLHandlers;
  164. /* layers stack */
  165. private Stack<String> layers;
  166. /** {@inheritDoc} */
  167. public abstract void setupFontInfo(FontInfo fontInfo) throws FOPException;
  168. /**
  169. *
  170. * @param userAgent the user agent that contains configuration details. This cannot be null.
  171. */
  172. public AbstractRenderer(FOUserAgent userAgent) {
  173. this.userAgent = userAgent;
  174. }
  175. /** {@inheritDoc} */
  176. public FOUserAgent getUserAgent() {
  177. return userAgent;
  178. }
  179. /** {@inheritDoc} */
  180. public void startRenderer(OutputStream outputStream)
  181. throws IOException {
  182. if (userAgent == null) {
  183. throw new IllegalStateException("FOUserAgent has not been set on Renderer");
  184. }
  185. }
  186. /** {@inheritDoc} */
  187. public void stopRenderer()
  188. throws IOException { }
  189. /**
  190. * Check if this renderer supports out of order rendering. If this renderer
  191. * supports out of order rendering then it means that the pages that are
  192. * not ready will be prepared and a future page will be rendered.
  193. *
  194. * @return True if the renderer supports out of order rendering
  195. */
  196. public boolean supportsOutOfOrder() {
  197. return false;
  198. }
  199. /** {@inheritDoc} */
  200. public void setDocumentLocale(Locale locale) {
  201. }
  202. /**
  203. * {@inheritDoc}
  204. */
  205. public void processOffDocumentItem(OffDocumentItem odi) { }
  206. /** {@inheritDoc} */
  207. public Graphics2DAdapter getGraphics2DAdapter() {
  208. return null;
  209. }
  210. /** {@inheritDoc} */
  211. public ImageAdapter getImageAdapter() {
  212. return null;
  213. }
  214. /** @return the current PageViewport or null, if none is active */
  215. protected PageViewport getCurrentPageViewport() {
  216. return this.currentPageViewport;
  217. }
  218. /** {@inheritDoc} */
  219. public void preparePage(PageViewport page) { }
  220. /**
  221. * Utility method to convert a page sequence title to a string. Some
  222. * renderers may only be able to use a string title. A title is a sequence
  223. * of inline areas that this method attempts to convert to an equivalent
  224. * string.
  225. *
  226. * @param title The Title to convert
  227. * @return An expanded string representing the title
  228. */
  229. protected String convertTitleToString(LineArea title) {
  230. List children = title.getInlineAreas();
  231. String str = convertToString(children);
  232. return str.trim();
  233. }
  234. private String convertToString(List children) {
  235. StringBuffer sb = new StringBuffer();
  236. for (Object aChildren : children) {
  237. InlineArea inline = (InlineArea) aChildren;
  238. //if (inline instanceof Character) {
  239. // sb.append(((Character) inline).getChar());
  240. /*} else*/
  241. if (inline instanceof TextArea) {
  242. sb.append(((TextArea) inline).getText());
  243. } else if (inline instanceof InlineParent) {
  244. sb.append(convertToString(
  245. ((InlineParent) inline).getChildAreas()));
  246. } else {
  247. sb.append(" ");
  248. }
  249. }
  250. return sb.toString();
  251. }
  252. /**
  253. * {@inheritDoc}
  254. * @deprecated
  255. */
  256. public void startPageSequence(LineArea seqTitle) {
  257. //do nothing
  258. }
  259. /** {@inheritDoc} */
  260. public void startPageSequence(PageSequence pageSequence) {
  261. // do nothing
  262. }
  263. // normally this would be overriden to create a page in the
  264. // output
  265. /** {@inheritDoc} */
  266. public void renderPage(PageViewport page)
  267. throws IOException, FOPException {
  268. this.currentPageViewport = page;
  269. try {
  270. Page p = page.getPage();
  271. renderPageAreas(p);
  272. } finally {
  273. this.currentPageViewport = null;
  274. }
  275. }
  276. /**
  277. * Renders page areas.
  278. *
  279. * @param page The page whos page areas are to be rendered
  280. */
  281. protected void renderPageAreas(Page page) {
  282. /* Spec does not appear to specify whether fo:region-body should
  283. appear above or below side regions in cases of overlap. FOP
  284. decision is to have fo:region-body on top, hence it is rendered
  285. last here. */
  286. RegionViewport viewport;
  287. viewport = page.getRegionViewport(FO_REGION_BEFORE);
  288. if (viewport != null) {
  289. renderRegionViewport(viewport);
  290. }
  291. viewport = page.getRegionViewport(FO_REGION_START);
  292. if (viewport != null) {
  293. renderRegionViewport(viewport);
  294. }
  295. viewport = page.getRegionViewport(FO_REGION_BODY);
  296. if (viewport != null) {
  297. renderRegionViewport(viewport);
  298. }
  299. viewport = page.getRegionViewport(FO_REGION_END);
  300. if (viewport != null) {
  301. renderRegionViewport(viewport);
  302. }
  303. viewport = page.getRegionViewport(FO_REGION_AFTER);
  304. if (viewport != null) {
  305. renderRegionViewport(viewport);
  306. }
  307. }
  308. /**
  309. * Renders a region viewport. <p>
  310. *
  311. * The region may clip the area and it establishes a position from where
  312. * the region is placed.</p>
  313. *
  314. * @param port The region viewport to be rendered
  315. */
  316. protected void renderRegionViewport(RegionViewport port) {
  317. // The CTM will transform coordinates relative to
  318. // this region-reference area into page coords, so
  319. // set origin for the region to 0,0.
  320. currentBPPosition = 0;
  321. currentIPPosition = 0;
  322. RegionReference regionReference = port.getRegionReference();
  323. handleRegionTraits(port);
  324. // shouldn't the viewport have the CTM
  325. startVParea(regionReference.getCTM(), port.getClipRectangle());
  326. // do after starting viewport area
  327. if (regionReference.getRegionClass() == FO_REGION_BODY) {
  328. assert (regionReference instanceof BodyRegion);
  329. renderBodyRegion((BodyRegion) regionReference);
  330. } else {
  331. renderRegion(regionReference);
  332. }
  333. endVParea();
  334. }
  335. /**
  336. * Establishes a new viewport area.
  337. *
  338. * @param ctm the coordinate transformation matrix to use
  339. * @param clippingRect the clipping rectangle if the viewport should be clipping,
  340. * null if no clipping is performed.
  341. */
  342. protected abstract void startVParea(CTM ctm, Rectangle clippingRect);
  343. /**
  344. * Signals exit from a viewport area. Subclasses can restore transformation matrices
  345. * valid before the viewport area was started.
  346. */
  347. protected abstract void endVParea();
  348. /**
  349. * Handle the traits for a region
  350. * This is used to draw the traits for the given page region.
  351. * (See Sect. 6.4.1.2 of XSL-FO spec.)
  352. * @param rv the RegionViewport whose region is to be drawn
  353. */
  354. protected void handleRegionTraits(RegionViewport rv) {
  355. // draw border and background
  356. }
  357. /**
  358. * Renders a region reference area.
  359. *
  360. * @param region The region reference area
  361. */
  362. protected void renderRegion(RegionReference region) {
  363. renderBlocks(null, region.getBlocks());
  364. }
  365. /**
  366. * Renders a body region area.
  367. *
  368. * @param region The body region
  369. */
  370. protected void renderBodyRegion(BodyRegion region) {
  371. BeforeFloat bf = region.getBeforeFloat();
  372. if (bf != null) {
  373. renderBeforeFloat(bf);
  374. }
  375. MainReference mr = region.getMainReference();
  376. if (mr != null) {
  377. renderMainReference(mr);
  378. }
  379. Footnote foot = region.getFootnote();
  380. if (foot != null) {
  381. renderFootnote(foot);
  382. }
  383. }
  384. /**
  385. * Renders a before float area.
  386. *
  387. * @param bf The before float area
  388. */
  389. protected void renderBeforeFloat(BeforeFloat bf) {
  390. List blocks = bf.getChildAreas();
  391. if (blocks != null) {
  392. renderBlocks(null, blocks);
  393. Block sep = bf.getSeparator();
  394. if (sep != null) {
  395. renderBlock(sep);
  396. }
  397. }
  398. }
  399. /**
  400. * Renders a footnote
  401. *
  402. * @param footnote The footnote
  403. */
  404. protected void renderFootnote(Footnote footnote) {
  405. currentBPPosition += footnote.getTop();
  406. List blocks = footnote.getChildAreas();
  407. if (blocks != null) {
  408. Block sep = footnote.getSeparator();
  409. if (sep != null) {
  410. renderBlock(sep);
  411. }
  412. renderBlocks(null, blocks);
  413. }
  414. }
  415. /**
  416. * Renders the main reference area.
  417. * <p>
  418. * The main reference area contains a list of spans that are
  419. * stacked on the page.
  420. * The spans contain a list of normal flow reference areas
  421. * that are positioned into columns.
  422. * </p>
  423. *
  424. * @param mainReference The main reference area
  425. */
  426. protected void renderMainReference(MainReference mainReference) {
  427. Span span = null;
  428. List spans = mainReference.getSpans();
  429. int saveBPPos = currentBPPosition;
  430. int saveIPPos = currentIPPosition;
  431. int saveSpanBPPos = saveBPPos;
  432. for (Object span1 : spans) {
  433. span = (Span) span1;
  434. columnCount = span.getColumnCount();
  435. columnGap = span.getColumnGap();
  436. columnWidth = span.getColumnWidth();
  437. blockProgressionDirection = (Direction) span.getTrait(Trait.BLOCK_PROGRESSION_DIRECTION);
  438. inlineProgressionDirection = (Direction) span.getTrait(Trait.INLINE_PROGRESSION_DIRECTION);
  439. int level = span.getBidiLevel();
  440. if (level < 0) {
  441. level = 0;
  442. }
  443. if ((level & 1) == 1) {
  444. currentIPPosition += span.getIPD();
  445. currentIPPosition += columnGap;
  446. }
  447. for (columnIndex = 0; columnIndex < columnCount; columnIndex++) {
  448. NormalFlow flow = span.getNormalFlow(columnIndex);
  449. boolean isLeftToRight = (inlineProgressionDirection == null)
  450. || (inlineProgressionDirection.getEnumValue() == Constants.EN_LR);
  451. if (flow != null) {
  452. // if direction is right to left, then end is left edge,
  453. // else end is right edge (for top-bottom/bottom-top block
  454. // progression directions)
  455. // binding edge is on left edge for odd pages and
  456. // on right edge for even pages
  457. int pageIndex = currentPageViewport.getPageIndex();
  458. bindingOnStartEdge = false;
  459. bindingOnEndEdge = false;
  460. if (isLeftToRight) {
  461. columnStartIPPosition = 0;
  462. columnEndIPPosition = columnWidth;
  463. columnLeftIPPosition = 0;
  464. columnRightIPPosition = columnWidth;
  465. if (blockProgressionDirection == null || blockProgressionDirection.isVertical()) {
  466. if (pageIndex % 2 == 0) {
  467. bindingOnStartEdge = true;
  468. } else {
  469. bindingOnEndEdge = true;
  470. }
  471. }
  472. } else {
  473. columnStartIPPosition = columnWidth;
  474. columnEndIPPosition = 0;
  475. columnLeftIPPosition = 0;
  476. columnRightIPPosition = columnWidth;
  477. if (blockProgressionDirection == null || blockProgressionDirection.isVertical()) {
  478. if (pageIndex % 2 == 0) {
  479. bindingOnEndEdge = true;
  480. } else {
  481. bindingOnStartEdge = true;
  482. }
  483. }
  484. }
  485. currentBPPosition = saveSpanBPPos;
  486. if ((level & 1) == 1) {
  487. currentIPPosition -= flow.getIPD();
  488. currentIPPosition -= columnGap;
  489. }
  490. renderFlow(flow);
  491. if ((level & 1) == 0) {
  492. currentIPPosition += flow.getIPD();
  493. currentIPPosition += columnGap;
  494. }
  495. }
  496. }
  497. currentIPPosition = saveIPPos;
  498. currentBPPosition = saveSpanBPPos + span.getHeight();
  499. saveSpanBPPos = currentBPPosition;
  500. }
  501. currentBPPosition = saveBPPos;
  502. }
  503. /**
  504. * Renders a flow reference area.
  505. *
  506. * @param flow The flow reference area
  507. */
  508. protected void renderFlow(NormalFlow flow) {
  509. // the normal flow reference area contains stacked blocks
  510. List blocks = flow.getChildAreas();
  511. if (blocks != null) {
  512. renderBlocks(null, blocks);
  513. }
  514. }
  515. /**
  516. * Handle block traits.
  517. * This method is called when the correct ip and bp posiiton is
  518. * set. This should be overridden to draw border and background
  519. * traits for the block area.
  520. *
  521. * @param block the block area
  522. */
  523. protected void handleBlockTraits(Block block) {
  524. // draw border and background
  525. }
  526. /**
  527. * Renders a block viewport.
  528. *
  529. * @param bv The block viewport
  530. * @param children The children to render within the block viewport
  531. */
  532. protected void renderBlockViewport(BlockViewport bv, List children) {
  533. boolean inNewLayer = false;
  534. if (maybeStartLayer(bv)) {
  535. inNewLayer = true;
  536. }
  537. // clip and position viewport if necessary
  538. if (bv.getPositioning() == Block.ABSOLUTE) {
  539. // save positions
  540. int saveIP = currentIPPosition;
  541. int saveBP = currentBPPosition;
  542. Rectangle clippingRect = null;
  543. if (bv.hasClip()) {
  544. clippingRect = new Rectangle(saveIP, saveBP, bv.getIPD(), bv.getBPD());
  545. }
  546. CTM ctm = bv.getCTM();
  547. currentIPPosition = 0;
  548. currentBPPosition = 0;
  549. startVParea(ctm, clippingRect);
  550. handleBlockTraits(bv);
  551. renderBlocks(bv, children);
  552. endVParea();
  553. // clip if necessary
  554. currentIPPosition = saveIP;
  555. currentBPPosition = saveBP;
  556. } else {
  557. // save position and offset
  558. int saveIP = currentIPPosition;
  559. int saveBP = currentBPPosition;
  560. handleBlockTraits(bv);
  561. renderBlocks(bv, children);
  562. currentIPPosition = saveIP;
  563. currentBPPosition = saveBP + bv.getAllocBPD();
  564. }
  565. maybeEndLayer(bv, inNewLayer);
  566. }
  567. /**
  568. * Renders a block area that represents a reference area. The reference area establishes
  569. * a new coordinate system.
  570. * @param block the block area
  571. */
  572. protected abstract void renderReferenceArea(Block block);
  573. /**
  574. * Renders a list of block areas.
  575. *
  576. * @param parent the parent block if the parent is a block, otherwise
  577. * a null value.
  578. * @param blocks The block areas
  579. */
  580. protected void renderBlocks(Block parent, List blocks) {
  581. int saveIP = currentIPPosition;
  582. // Calculate the position of the content rectangle.
  583. if (parent != null && !parent.getTraitAsBoolean(Trait.IS_VIEWPORT_AREA)) {
  584. currentBPPosition += parent.getBorderAndPaddingWidthBefore();
  585. }
  586. // the position of the containing block is used for
  587. // absolutely positioned areas
  588. int contBP = currentBPPosition;
  589. int contIP = currentIPPosition;
  590. containingBPPosition = currentBPPosition;
  591. containingIPPosition = currentIPPosition;
  592. for (Object obj : blocks) {
  593. if (obj instanceof Block) {
  594. currentIPPosition = contIP;
  595. containingBPPosition = contBP;
  596. containingIPPosition = contIP;
  597. renderBlock((Block) obj);
  598. containingBPPosition = contBP;
  599. containingIPPosition = contIP;
  600. } else if (obj instanceof LineArea) {
  601. // a line area is rendered from the top left position
  602. // of the line, each inline object is offset from there
  603. LineArea line = (LineArea) obj;
  604. if (parent != null) {
  605. int level = parent.getBidiLevel();
  606. if ((level == -1) || ((level & 1) == 0)) {
  607. currentIPPosition += parent.getStartIndent();
  608. } else {
  609. currentIPPosition += parent.getEndIndent();
  610. }
  611. }
  612. renderLineArea(line);
  613. currentBPPosition += line.getAllocBPD();
  614. }
  615. currentIPPosition = saveIP;
  616. }
  617. }
  618. /**
  619. * Renders a block area.
  620. *
  621. * @param block The block area
  622. */
  623. protected void renderBlock(Block block) {
  624. assert block != null;
  625. List<ChangeBar> changeBarList = block.getChangeBarList();
  626. if (changeBarList != null && !changeBarList.isEmpty()) {
  627. int saveIP = currentIPPosition;
  628. int saveBP = currentBPPosition;
  629. drawChangeBars(block, changeBarList);
  630. currentIPPosition = saveIP;
  631. currentBPPosition = saveBP;
  632. }
  633. List children = block.getChildAreas();
  634. boolean inNewLayer = false;
  635. if (maybeStartLayer(block)) {
  636. inNewLayer = true;
  637. }
  638. if (block instanceof BlockViewport) {
  639. if (children != null) {
  640. renderBlockViewport((BlockViewport) block, children);
  641. } else {
  642. handleBlockTraits(block);
  643. // simply move position
  644. currentBPPosition += block.getAllocBPD();
  645. }
  646. } else if (block.getTraitAsBoolean(Trait.IS_REFERENCE_AREA)) {
  647. renderReferenceArea(block);
  648. } else {
  649. // save position and offset
  650. int saveIP = currentIPPosition;
  651. int saveBP = currentBPPosition;
  652. currentIPPosition += block.getXOffset();
  653. currentBPPosition += block.getYOffset();
  654. currentBPPosition += block.getSpaceBefore();
  655. handleBlockTraits(block);
  656. if (children != null && block.getTrait(Trait.VISIBILITY) != Visibility.HIDDEN)
  657. {
  658. renderBlocks(block, children);
  659. }
  660. if (block.getPositioning() == Block.ABSOLUTE) {
  661. // absolute blocks do not effect the layout
  662. currentBPPosition = saveBP;
  663. } else {
  664. // stacked and relative blocks effect stacking
  665. currentIPPosition = saveIP;
  666. currentBPPosition = saveBP + block.getAllocBPD();
  667. }
  668. }
  669. maybeEndLayer(block, inNewLayer);
  670. }
  671. /**
  672. * Renders an inline block area.
  673. *
  674. * @param inlineBlock The inline block area
  675. */
  676. protected void renderInlineBlock(InlineBlock inlineBlock) {
  677. renderBlock(inlineBlock.getBlock());
  678. }
  679. /**
  680. * Establish new optional content group layer.
  681. *
  682. * @param layer name of layer
  683. */
  684. protected abstract void startLayer(String layer);
  685. /**
  686. * Finish current optional content group layer.
  687. */
  688. protected abstract void endLayer();
  689. protected boolean maybeStartLayer(Area area) {
  690. String layer = (String) area.getTrait(Trait.LAYER);
  691. if (layer != null) {
  692. if (layers == null) {
  693. layers = new Stack<String>();
  694. }
  695. if (layers.empty() || !layers.peek().equals(layer)) {
  696. layers.push(layer);
  697. startLayer(layer);
  698. return true;
  699. }
  700. }
  701. return false;
  702. }
  703. protected void maybeEndLayer(Area area, boolean inNewLayer) {
  704. if (inNewLayer) {
  705. assert layers != null;
  706. assert !layers.empty();
  707. String layer = (String) area.getTrait(Trait.LAYER);
  708. assert layer != null;
  709. assert layers.peek().equals(layer);
  710. endLayer();
  711. layers.pop();
  712. }
  713. }
  714. /**
  715. * Renders a line area. <p>
  716. *
  717. * A line area may have grouped styling for its children such as underline,
  718. * background.</p>
  719. *
  720. * @param line The line area
  721. */
  722. protected void renderLineArea(LineArea line) {
  723. List children = line.getInlineAreas();
  724. int saveBP = currentBPPosition;
  725. currentBPPosition += line.getSpaceBefore();
  726. int bl = line.getBidiLevel();
  727. if (bl >= 0) {
  728. if ((bl & 1) == 0) {
  729. currentIPPosition += line.getStartIndent();
  730. } else {
  731. currentIPPosition += line.getEndIndent();
  732. }
  733. } else {
  734. currentIPPosition += line.getStartIndent();
  735. }
  736. for (Object aChildren : children) {
  737. InlineArea inline = (InlineArea) aChildren;
  738. renderInlineArea(inline);
  739. }
  740. currentBPPosition = saveBP;
  741. }
  742. /**
  743. * Render the given InlineArea.
  744. * @param inlineArea inline area text to render
  745. */
  746. protected void renderInlineArea(InlineArea inlineArea) {
  747. List<ChangeBar> changeBarList = inlineArea.getChangeBarList();
  748. if (changeBarList != null && !changeBarList.isEmpty()) {
  749. drawChangeBars(inlineArea, changeBarList);
  750. }
  751. if (inlineArea instanceof TextArea) {
  752. renderText((TextArea) inlineArea);
  753. //} else if (inlineArea instanceof Character) {
  754. //renderCharacter((Character) inlineArea);
  755. } else if (inlineArea instanceof WordArea) {
  756. renderWord((WordArea) inlineArea);
  757. } else if (inlineArea instanceof SpaceArea) {
  758. renderSpace((SpaceArea) inlineArea);
  759. } else if (inlineArea instanceof InlineBlock) {
  760. renderInlineBlock((InlineBlock) inlineArea);
  761. } else if (inlineArea instanceof InlineParent) {
  762. renderInlineParent((InlineParent) inlineArea);
  763. } else if (inlineArea instanceof InlineBlockParent) {
  764. renderInlineBlockParent((InlineBlockParent) inlineArea);
  765. } else if (inlineArea instanceof Space) {
  766. renderInlineSpace((Space) inlineArea);
  767. } else if (inlineArea instanceof InlineViewport) {
  768. renderInlineViewport((InlineViewport) inlineArea);
  769. } else if (inlineArea instanceof Leader) {
  770. renderLeader((Leader) inlineArea);
  771. }
  772. }
  773. /**
  774. * Common method to render the background and borders for any inline area.
  775. * The all borders and padding are drawn outside the specified area.
  776. * @param area the inline area for which the background, border and padding is to be
  777. * rendered
  778. */
  779. protected abstract void renderInlineAreaBackAndBorders(InlineArea area);
  780. /**
  781. * Render the given Space.
  782. * @param space the space to render
  783. */
  784. protected void renderInlineSpace(Space space) {
  785. renderInlineAreaBackAndBorders(space);
  786. // an inline space moves the inline progression position
  787. // for the current block by the width or height of the space
  788. // it may also have styling (only on this object) that needs
  789. // handling
  790. currentIPPosition += space.getAllocIPD();
  791. }
  792. /**
  793. * Render the given Leader.
  794. * @param area the leader to render
  795. */
  796. protected void renderLeader(Leader area) {
  797. currentIPPosition += area.getAllocIPD();
  798. }
  799. /**
  800. * Render the given TextArea.
  801. * @param text the text to render
  802. */
  803. protected void renderText(TextArea text) {
  804. List children = text.getChildAreas();
  805. int saveIP = currentIPPosition;
  806. int saveBP = currentBPPosition;
  807. List<ChangeBar> changeBarList = text.getChangeBarList();
  808. if (changeBarList != null && !changeBarList.isEmpty()) {
  809. drawChangeBars(text, changeBarList);
  810. currentIPPosition = saveIP;
  811. currentBPPosition = saveBP;
  812. }
  813. for (Object aChildren : children) {
  814. InlineArea inline = (InlineArea) aChildren;
  815. renderInlineArea(inline);
  816. }
  817. currentIPPosition = saveIP + text.getAllocIPD();
  818. }
  819. /**
  820. * Render the given WordArea.
  821. * @param word the word to render
  822. */
  823. protected void renderWord(WordArea word) {
  824. currentIPPosition += word.getAllocIPD();
  825. }
  826. /**
  827. * Render the given SpaceArea.
  828. * @param space the space to render
  829. */
  830. protected void renderSpace(SpaceArea space) {
  831. currentIPPosition += space.getAllocIPD();
  832. }
  833. /**
  834. * Render the given InlineParent.
  835. * @param ip the inline parent to render
  836. */
  837. protected void renderInlineParent(InlineParent ip) {
  838. boolean inNewLayer = false;
  839. if (maybeStartLayer(ip)) {
  840. inNewLayer = true;
  841. }
  842. int level = ip.getBidiLevel();
  843. List children = ip.getChildAreas();
  844. renderInlineAreaBackAndBorders(ip);
  845. int saveIP = currentIPPosition;
  846. int saveBP = currentBPPosition;
  847. // if inline parent is a filled area (generated by Leader), and if
  848. // it is right-to-left, then adjust starting ip position in order to
  849. // align children to starting (right) edge of filled area
  850. int ipAdjust;
  851. if ((ip instanceof FilledArea) && ((level & 1) != 0)) {
  852. int ipdChildren = 0;
  853. for (Object aChildren : children) {
  854. InlineArea inline = (InlineArea) aChildren;
  855. ipdChildren += inline.getAllocIPD();
  856. }
  857. ipAdjust = ip.getAllocIPD() - ipdChildren;
  858. } else {
  859. ipAdjust = 0;
  860. }
  861. // perform inline position adjustments
  862. if ((level == -1) || ((level & 1) == 0)) {
  863. currentIPPosition += ip.getBorderAndPaddingWidthStart();
  864. } else {
  865. currentIPPosition += ip.getBorderAndPaddingWidthEnd();
  866. if (ipAdjust > 0) {
  867. currentIPPosition += ipAdjust;
  868. }
  869. }
  870. currentBPPosition += ip.getBlockProgressionOffset();
  871. // render children inlines
  872. for (Object aChildren : children) {
  873. InlineArea inline = (InlineArea) aChildren;
  874. renderInlineArea(inline);
  875. }
  876. currentIPPosition = saveIP + ip.getAllocIPD();
  877. currentBPPosition = saveBP;
  878. maybeEndLayer(ip, inNewLayer);
  879. }
  880. /**
  881. * Render the given InlineBlockParent.
  882. * @param ibp the inline block parent to render
  883. */
  884. protected void renderInlineBlockParent(InlineBlockParent ibp) {
  885. int level = ibp.getBidiLevel();
  886. renderInlineAreaBackAndBorders(ibp);
  887. if ((level == -1) || ((level & 1) == 0)) {
  888. currentIPPosition += ibp.getBorderAndPaddingWidthStart();
  889. } else {
  890. currentIPPosition += ibp.getBorderAndPaddingWidthEnd();
  891. }
  892. // For inline content the BP position is updated by the enclosing line area
  893. int saveBP = currentBPPosition;
  894. currentBPPosition += ibp.getBlockProgressionOffset();
  895. renderBlock(ibp.getChildArea());
  896. currentBPPosition = saveBP;
  897. }
  898. /**
  899. * Render the given Viewport.
  900. * @param viewport the viewport to render
  901. */
  902. protected void renderInlineViewport(InlineViewport viewport) {
  903. Area content = viewport.getContent();
  904. int saveBP = currentBPPosition;
  905. currentBPPosition += viewport.getBlockProgressionOffset();
  906. Rectangle2D contpos = viewport.getContentPosition();
  907. if (content instanceof Image) {
  908. renderImage((Image) content, contpos);
  909. } else if (content instanceof Container) {
  910. renderContainer((Container) content);
  911. } else if (content instanceof ForeignObject) {
  912. renderForeignObject((ForeignObject) content, contpos);
  913. } else if (content instanceof InlineBlockParent) {
  914. renderInlineBlockParent((InlineBlockParent) content);
  915. }
  916. currentIPPosition += viewport.getAllocIPD();
  917. currentBPPosition = saveBP;
  918. }
  919. /**
  920. * Renders an image area.
  921. *
  922. * @param image The image
  923. * @param pos The target position of the image
  924. * (todo) Make renderImage() protected
  925. */
  926. public void renderImage(Image image, Rectangle2D pos) {
  927. List<ChangeBar> changeBarList = image.getChangeBarList();
  928. if (changeBarList != null && !changeBarList.isEmpty()) {
  929. drawChangeBars(image, changeBarList);
  930. }
  931. // Default: do nothing.
  932. // Some renderers (ex. Text) don't support images.
  933. }
  934. /**
  935. * Tells the renderer to render an inline container.
  936. * @param cont The inline container area
  937. */
  938. protected void renderContainer(Container cont) {
  939. int saveIP = currentIPPosition;
  940. int saveBP = currentBPPosition;
  941. List blocks = cont.getBlocks();
  942. renderBlocks(null, blocks);
  943. currentIPPosition = saveIP;
  944. currentBPPosition = saveBP;
  945. }
  946. /**
  947. * Renders a foreign object area.
  948. *
  949. * @param fo The foreign object area
  950. * @param pos The target position of the foreign object
  951. * (todo) Make renderForeignObject() protected
  952. */
  953. protected void renderForeignObject(ForeignObject fo, Rectangle2D pos) {
  954. List<ChangeBar> changeBarList = fo.getChangeBarList();
  955. if (changeBarList != null && !changeBarList.isEmpty()) {
  956. drawChangeBars(fo, changeBarList);
  957. }
  958. // Default: do nothing.
  959. // Some renderers (ex. Text) don't support foreign objects.
  960. }
  961. /**
  962. * Render the xml document with the given xml namespace.
  963. * The Render Context is by the handle to render into the current
  964. * rendering target.
  965. * @param ctx rendering context
  966. * @param doc DOM Document containing the source document
  967. * @param namespace Namespace URI of the document
  968. */
  969. public void renderXML(RendererContext ctx, Document doc,
  970. String namespace) {
  971. XMLHandler handler = userAgent.getXMLHandlerRegistry().getXMLHandler(
  972. this, namespace);
  973. if (handler != null) {
  974. try {
  975. XMLHandlerConfigurator configurator
  976. = new XMLHandlerConfigurator(userAgent);
  977. configurator.configure(ctx, namespace);
  978. handler.handleXML(ctx, doc, namespace);
  979. } catch (Exception e) {
  980. // could not handle document
  981. ResourceEventProducer eventProducer
  982. = ResourceEventProducer.Provider.get(
  983. ctx.getUserAgent().getEventBroadcaster());
  984. eventProducer.foreignXMLProcessingError(this, doc, namespace, e);
  985. }
  986. } else {
  987. if (warnedXMLHandlers == null) {
  988. warnedXMLHandlers = new java.util.HashSet();
  989. }
  990. if (!warnedXMLHandlers.contains(namespace)) {
  991. // no handler found for document
  992. warnedXMLHandlers.add(namespace);
  993. ResourceEventProducer eventProducer = ResourceEventProducer.Provider.get(
  994. ctx.getUserAgent().getEventBroadcaster());
  995. eventProducer.foreignXMLNoHandler(this, doc, namespace);
  996. }
  997. }
  998. }
  999. /**
  1000. * Converts a millipoint-based transformation matrix to points.
  1001. * @param at a millipoint-based transformation matrix
  1002. * @return a point-based transformation matrix
  1003. */
  1004. protected AffineTransform mptToPt(AffineTransform at) {
  1005. double[] matrix = new double[6];
  1006. at.getMatrix(matrix);
  1007. //Convert to points
  1008. matrix[4] = matrix[4] / 1000;
  1009. matrix[5] = matrix[5] / 1000;
  1010. return new AffineTransform(matrix);
  1011. }
  1012. /**
  1013. * Converts a point-based transformation matrix to millipoints.
  1014. * @param at a point-based transformation matrix
  1015. * @return a millipoint-based transformation matrix
  1016. */
  1017. protected AffineTransform ptToMpt(AffineTransform at) {
  1018. double[] matrix = new double[6];
  1019. at.getMatrix(matrix);
  1020. //Convert to millipoints
  1021. //Math.round() because things like this can happen: 65.6 * 1000 = 65.599999999999999
  1022. //which is bad for testing
  1023. matrix[4] = Math.round(matrix[4] * 1000);
  1024. matrix[5] = Math.round(matrix[5] * 1000);
  1025. return new AffineTransform(matrix);
  1026. }
  1027. /**
  1028. * Draws all change bars associated with an area.
  1029. *
  1030. * @param area The area to draw change bars for
  1031. * @param changeBarList The list of change bars affecting the area
  1032. */
  1033. protected void drawChangeBars(Area area, List<ChangeBar> changeBarList) {
  1034. if (area.getTraitAsBoolean(Trait.IS_REFERENCE_AREA)) {
  1035. return;
  1036. }
  1037. Block changeBarArea;
  1038. int saveIP = currentIPPosition;
  1039. int saveBP = currentBPPosition;
  1040. int currentColumnStartIP = columnStartIPPosition;
  1041. int currentColumnEndIP = columnEndIPPosition;
  1042. int currentColumnLeftIP = columnLeftIPPosition;
  1043. int currentColumnRightIP = columnRightIPPosition;
  1044. for (ChangeBar changeBar : changeBarList) {
  1045. boolean isLeftToRight = (inlineProgressionDirection == null)
  1046. || (inlineProgressionDirection.getEnumValue() == Constants.EN_LR);
  1047. changeBarArea = new Block();
  1048. // currentIPPosition is reset to zero so from now on all multicolumn
  1049. // dimensions has to be calculated relatively to the given column
  1050. currentIPPosition = 0;
  1051. currentBPPosition = saveBP;
  1052. int changeBarWidth = changeBar.getWidth().getValue();
  1053. int changeBarOffset = changeBar.getOffset().getValue();
  1054. if (isLeftToRight) {
  1055. currentColumnStartIP = columnStartIPPosition - changeBarWidth;
  1056. currentColumnLeftIP = columnLeftIPPosition - changeBarWidth;
  1057. } else {
  1058. currentColumnEndIP = columnEndIPPosition - changeBarWidth;
  1059. currentColumnLeftIP = columnLeftIPPosition - changeBarWidth;
  1060. }
  1061. // xOffset by default is negative width for change bars placed on the
  1062. // start edge (overriden if placement is at the end edge)
  1063. int xOffset = currentColumnStartIP;
  1064. // xScale is for adding or subtracting the offset of the change bar
  1065. // depending on placing the bar towards or away from the edge it is
  1066. // bound to
  1067. int xScale = -1;
  1068. // determines currentIPPosition based on placement
  1069. switch (changeBar.getPlacement()) {
  1070. case EN_START:
  1071. xOffset = currentColumnStartIP;
  1072. xScale = -1;
  1073. break;
  1074. case EN_END:
  1075. xOffset = currentColumnEndIP;
  1076. xScale = 1;
  1077. break;
  1078. case EN_LEFT:
  1079. xOffset = currentColumnLeftIP;
  1080. xScale = (isLeftToRight) ? -1 : 1;
  1081. break;
  1082. case EN_RIGHT:
  1083. xOffset = currentColumnRightIP;
  1084. xScale = (isLeftToRight) ? 1 : -1;
  1085. break;
  1086. case EN_INSIDE:
  1087. if (bindingOnStartEdge) {
  1088. xOffset = currentColumnStartIP;
  1089. xScale = -1;
  1090. } else if (bindingOnEndEdge) {
  1091. xOffset = currentColumnEndIP;
  1092. xScale = 1;
  1093. } else {
  1094. xOffset = currentColumnStartIP;
  1095. xScale = -1;
  1096. }
  1097. break;
  1098. case EN_OUTSIDE:
  1099. if (bindingOnStartEdge) {
  1100. xOffset = columnEndIPPosition;
  1101. xScale = 1;
  1102. } else if (bindingOnEndEdge) {
  1103. xOffset = columnStartIPPosition;
  1104. xScale = -1;
  1105. } else {
  1106. xOffset = columnStartIPPosition;
  1107. xScale = -1;
  1108. }
  1109. break;
  1110. case EN_ALTERNATE:
  1111. if (columnCount == 2) {
  1112. if (columnIndex == 0) {
  1113. xOffset = columnStartIPPosition;
  1114. xScale = -1;
  1115. } else {
  1116. xOffset = columnEndIPPosition;
  1117. xScale = 1;
  1118. }
  1119. } else {
  1120. if (bindingOnStartEdge) {
  1121. xOffset = columnEndIPPosition;
  1122. xScale = 1;
  1123. } else if (bindingOnEndEdge) {
  1124. xOffset = columnStartIPPosition;
  1125. xScale = -1;
  1126. } else {
  1127. xOffset = columnStartIPPosition;
  1128. xScale = -1;
  1129. }
  1130. }
  1131. break;
  1132. default:
  1133. break;
  1134. }
  1135. if (isLeftToRight) {
  1136. xOffset += xScale * changeBarOffset;
  1137. } else {
  1138. xOffset -= xScale * changeBarOffset;
  1139. }
  1140. xOffset += getBeginOffset();
  1141. // Change bar area has 0 ipd, class xsl-absolute, no margin or padding
  1142. changeBarArea.setAreaClass(Area.CLASS_ABSOLUTE);
  1143. changeBarArea.setIPD(0);
  1144. BorderProps props = BorderProps.makeRectangular(
  1145. changeBar.getStyle(), changeBarWidth, changeBar.getColor(),
  1146. BorderProps.Mode.SEPARATE);
  1147. changeBarArea.addTrait(Trait.BORDER_START, props);
  1148. changeBarArea.addTrait(Trait.BORDER_END, props);
  1149. changeBarArea.setXOffset(xOffset);
  1150. int areaHeight = area.getAllocBPD();
  1151. if (area instanceof BlockParent) {
  1152. changeBarArea.setBPD(areaHeight);
  1153. changeBarArea.setYOffset(((BlockParent) area).getYOffset());
  1154. renderBlock(changeBarArea);
  1155. } else {
  1156. if (areaHeight > 0) {
  1157. Property p = changeBar.getLineHeight().getOptimum(DummyPercentBaseContext.getInstance());
  1158. int lineHeight = p.getLength().getValue();
  1159. changeBarArea.setBPD(lineHeight);
  1160. changeBarArea.setYOffset(areaHeight - lineHeight);
  1161. }
  1162. renderInlineBlock(new InlineBlock(changeBarArea));
  1163. }
  1164. // restore position on page
  1165. currentIPPosition = saveIP;
  1166. currentBPPosition = saveBP;
  1167. }
  1168. }
  1169. /**
  1170. * Returns the begin offset of the inline begin (changes by reference area
  1171. * transforms).
  1172. *
  1173. * @return the offset from current coordinate system 0 that the IP begin is
  1174. * at
  1175. */
  1176. protected int getBeginOffset() {
  1177. return beginOffset;
  1178. }
  1179. /**
  1180. * Sets the begin offset for inline progression begin (changes by reference
  1181. * area tranforms).
  1182. *
  1183. * @param offset the new offset from IPP 0 that true IP start is at
  1184. */
  1185. protected void setBeginOffset(int offset) {
  1186. beginOffset = offset;
  1187. }
  1188. }