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.

InlineStackingLayoutManager.java 15KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435
  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.layoutmgr.inline;
  19. import java.util.LinkedList;
  20. import java.util.Iterator;
  21. import java.util.List;
  22. import java.util.ListIterator;
  23. import java.util.HashMap;
  24. import org.apache.fop.fo.FObj;
  25. import org.apache.fop.fo.properties.SpaceProperty;
  26. import org.apache.fop.layoutmgr.AbstractLayoutManager;
  27. import org.apache.fop.layoutmgr.KnuthElement;
  28. import org.apache.fop.layoutmgr.LayoutContext;
  29. import org.apache.fop.layoutmgr.LayoutManager;
  30. import org.apache.fop.layoutmgr.NonLeafPosition;
  31. import org.apache.fop.layoutmgr.Position;
  32. import org.apache.fop.layoutmgr.PositionIterator;
  33. import org.apache.fop.area.Area;
  34. import org.apache.fop.area.inline.Space;
  35. import org.apache.fop.traits.MinOptMax;
  36. /**
  37. * Class modelling the commonalities of layoutmanagers for objects
  38. * which stack children in the inline direction, such as Inline or
  39. * Line. It should not be instantiated directly.
  40. */
  41. public abstract class InlineStackingLayoutManager extends AbstractLayoutManager
  42. implements InlineLevelLayoutManager {
  43. protected static class StackingIter extends PositionIterator {
  44. StackingIter(Iterator parentIter) {
  45. super(parentIter);
  46. }
  47. protected LayoutManager getLM(Object nextObj) {
  48. return ((Position) nextObj).getLM();
  49. }
  50. protected Position getPos(Object nextObj) {
  51. return ((Position) nextObj);
  52. }
  53. }
  54. /**
  55. * Size of any start or end borders and padding.
  56. */
  57. private MinOptMax allocIPD = new MinOptMax(0);
  58. /**
  59. * Size of border and padding in BPD (ie, before and after).
  60. */
  61. protected MinOptMax extraBPD;
  62. private Area currentArea; // LineArea or InlineParent
  63. /** The child layout context */
  64. protected LayoutContext childLC;
  65. /** Used to store previous content IPD for each child LM. */
  66. private HashMap hmPrevIPD = new HashMap();
  67. /**
  68. * Create an inline stacking layout manager.
  69. * This is used for fo's that create areas that
  70. * contain inline areas.
  71. *
  72. * @param node the formatting object that creates the area
  73. */
  74. protected InlineStackingLayoutManager(FObj node) {
  75. super(node);
  76. extraBPD = new MinOptMax(0);
  77. }
  78. /**
  79. * Set the iterator.
  80. *
  81. * @param iter the iterator for this LM
  82. */
  83. public void setLMiter(ListIterator iter) {
  84. childLMiter = iter;
  85. }
  86. /**
  87. * Returns the extra IPD needed for any leading or trailing fences for the
  88. * current area.
  89. * @param bNotFirst true if not the first area for this layout manager
  90. * @param bNotLast true if not the last area for this layout manager
  91. * @return the extra IPD as a MinOptMax spec
  92. */
  93. protected MinOptMax getExtraIPD(boolean bNotFirst, boolean bNotLast) {
  94. return new MinOptMax(0);
  95. }
  96. /**
  97. * Indication if the current area has a leading fence.
  98. * @param bNotFirst true if not the first area for this layout manager
  99. * @return the leading fence flag
  100. */
  101. protected boolean hasLeadingFence(boolean bNotFirst) {
  102. return false;
  103. }
  104. /**
  105. * Indication if the current area has a trailing fence.
  106. * @param bNotLast true if not the last area for this layout manager
  107. * @return the trailing fence flag
  108. */
  109. protected boolean hasTrailingFence(boolean bNotLast) {
  110. return false;
  111. }
  112. /**
  113. * Get the space at the start of the inline area.
  114. * @return the space property describing the space
  115. */
  116. protected SpaceProperty getSpaceStart() {
  117. return null;
  118. }
  119. /**
  120. * Get the space at the end of the inline area.
  121. * @return the space property describing the space
  122. */
  123. protected SpaceProperty getSpaceEnd() {
  124. return null;
  125. }
  126. /**
  127. * TODO: Explain this method
  128. * @param lm ???
  129. * @return ???
  130. */
  131. protected MinOptMax getPrevIPD(LayoutManager lm) {
  132. return (MinOptMax) hmPrevIPD.get(lm);
  133. }
  134. /**
  135. * Clear the previous IPD calculation.
  136. */
  137. protected void clearPrevIPD() {
  138. hmPrevIPD.clear();
  139. }
  140. /**
  141. * Returns the current area.
  142. * @return the current area
  143. */
  144. protected Area getCurrentArea() {
  145. return currentArea;
  146. }
  147. /**
  148. * Set the current area.
  149. * @param area the current area
  150. */
  151. protected void setCurrentArea(Area area) {
  152. currentArea = area;
  153. }
  154. /**
  155. * Trait setter to be overridden by subclasses.
  156. * @param bNotFirst true if this is not the first child area added
  157. * @param bNotLast true if this is not the last child area added
  158. */
  159. protected void setTraits(boolean bNotFirst, boolean bNotLast) {
  160. }
  161. /**
  162. * Set the current child layout context
  163. * @param lc the child layout context
  164. */
  165. protected void setChildContext(LayoutContext lc) {
  166. childLC = lc;
  167. }
  168. /**
  169. * Current child layout context
  170. * @return the current child layout context
  171. */
  172. protected LayoutContext getContext() {
  173. return childLC;
  174. }
  175. /**
  176. * Adds a space to the area
  177. * @param parentArea the area to which to add the space
  178. * @param spaceRange the space range specifier
  179. * @param dSpaceAdjust the factor by which to stretch or shrink the space
  180. */
  181. protected void addSpace(Area parentArea, MinOptMax spaceRange,
  182. double dSpaceAdjust) {
  183. if (spaceRange != null) {
  184. int iAdjust = spaceRange.opt;
  185. if (dSpaceAdjust > 0.0) {
  186. // Stretch by factor
  187. iAdjust += (int) ((double) (spaceRange.max
  188. - spaceRange.opt) * dSpaceAdjust);
  189. } else if (dSpaceAdjust < 0.0) {
  190. // Shrink by factor
  191. iAdjust += (int) ((double) (spaceRange.opt
  192. - spaceRange.min) * dSpaceAdjust);
  193. }
  194. if (iAdjust != 0) {
  195. //getLogger().debug("Add leading space: " + iAdjust);
  196. Space ls = new Space();
  197. ls.setIPD(iAdjust);
  198. parentArea.addChildArea(ls);
  199. }
  200. }
  201. }
  202. /** {@inheritDoc} */
  203. public List addALetterSpaceTo(List oldList) {
  204. // old list contains only a box, or the sequence: box penalty glue box
  205. ListIterator oldListIterator = oldList.listIterator();
  206. KnuthElement element = null;
  207. // "unwrap" the Position stored in each element of oldList
  208. while (oldListIterator.hasNext()) {
  209. element = (KnuthElement) oldListIterator.next();
  210. element.setPosition(element.getPosition().getPosition());
  211. }
  212. // The last element may not have a layout manager (its position == null);
  213. // this may happen if it is a padding box; see bug 39571.
  214. InlineLevelLayoutManager LM =
  215. (InlineLevelLayoutManager) element.getLayoutManager();
  216. if (LM != null) {
  217. oldList = LM.addALetterSpaceTo(oldList);
  218. }
  219. // "wrap" again the Position stored in each element of oldList
  220. oldListIterator = oldList.listIterator();
  221. while (oldListIterator.hasNext()) {
  222. element = (KnuthElement) oldListIterator.next();
  223. element.setPosition(notifyPos(new NonLeafPosition(this, element.getPosition())));
  224. }
  225. return oldList;
  226. }
  227. /**
  228. * remove the AreaInfo object represented by the given elements,
  229. * so that it won't generate any element when getChangedKnuthElements
  230. * will be called
  231. *
  232. * @param oldList the elements representing the word space
  233. */
  234. public void removeWordSpace(List oldList) {
  235. ListIterator oldListIterator = oldList.listIterator();
  236. KnuthElement element = null;
  237. // "unwrap" the Position stored in each element of oldList
  238. while (oldListIterator.hasNext()) {
  239. element = (KnuthElement) oldListIterator.next();
  240. element.setPosition(element.getPosition().getPosition());
  241. }
  242. ((InlineLevelLayoutManager)
  243. element.getLayoutManager()).removeWordSpace(oldList);
  244. }
  245. /** {@inheritDoc} */
  246. public void getWordChars(StringBuffer sbChars, Position pos) {
  247. Position newPos = pos.getPosition();
  248. ((InlineLevelLayoutManager)
  249. newPos.getLM()).getWordChars(sbChars, newPos);
  250. }
  251. /** {@inheritDoc} */
  252. public void hyphenate(Position pos, HyphContext hc) {
  253. Position newPos = pos.getPosition();
  254. ((InlineLevelLayoutManager)
  255. newPos.getLM()).hyphenate(newPos, hc);
  256. }
  257. /** {@inheritDoc} */
  258. public boolean applyChanges(List oldList) {
  259. // "unwrap" the Positions stored in the elements
  260. ListIterator oldListIterator = oldList.listIterator();
  261. KnuthElement oldElement;
  262. while (oldListIterator.hasNext()) {
  263. oldElement = (KnuthElement) oldListIterator.next();
  264. oldElement.setPosition
  265. (oldElement.getPosition().getPosition());
  266. }
  267. // reset the iterator
  268. oldListIterator = oldList.listIterator();
  269. InlineLevelLayoutManager prevLM = null;
  270. InlineLevelLayoutManager currLM;
  271. int fromIndex = 0;
  272. boolean bSomethingChanged = false;
  273. while (oldListIterator.hasNext()) {
  274. oldElement = (KnuthElement) oldListIterator.next();
  275. currLM = (InlineLevelLayoutManager) oldElement.getLayoutManager();
  276. // initialize prevLM
  277. if (prevLM == null) {
  278. prevLM = currLM;
  279. }
  280. if (currLM != prevLM || !oldListIterator.hasNext()) {
  281. if (prevLM == this || currLM == this) {
  282. prevLM = currLM;
  283. } else if (oldListIterator.hasNext()) {
  284. bSomethingChanged
  285. = prevLM.applyChanges(oldList.subList(fromIndex
  286. , oldListIterator.previousIndex()))
  287. || bSomethingChanged;
  288. prevLM = currLM;
  289. fromIndex = oldListIterator.previousIndex();
  290. } else if (currLM == prevLM) {
  291. bSomethingChanged
  292. = (prevLM != null)
  293. && prevLM.applyChanges(oldList.subList(fromIndex, oldList.size()))
  294. || bSomethingChanged;
  295. } else {
  296. bSomethingChanged
  297. = prevLM.applyChanges(oldList.subList(fromIndex
  298. , oldListIterator.previousIndex()))
  299. || bSomethingChanged;
  300. if (currLM != null) {
  301. bSomethingChanged
  302. = currLM.applyChanges(oldList.subList(oldListIterator.previousIndex()
  303. , oldList.size()))
  304. || bSomethingChanged;
  305. }
  306. }
  307. }
  308. }
  309. // "wrap" again the Positions stored in the elements
  310. oldListIterator = oldList.listIterator();
  311. while (oldListIterator.hasNext()) {
  312. oldElement = (KnuthElement) oldListIterator.next();
  313. oldElement.setPosition
  314. (notifyPos(new NonLeafPosition(this, oldElement.getPosition())));
  315. }
  316. return bSomethingChanged;
  317. }
  318. /**
  319. * {@inheritDoc}
  320. */
  321. public List getChangedKnuthElements(List oldList, int alignment) {
  322. // "unwrap" the Positions stored in the elements
  323. ListIterator oldListIterator = oldList.listIterator();
  324. KnuthElement oldElement;
  325. while (oldListIterator.hasNext()) {
  326. oldElement = (KnuthElement) oldListIterator.next();
  327. oldElement.setPosition
  328. (oldElement.getPosition().getPosition());
  329. }
  330. // reset the iterator
  331. oldListIterator = oldList.listIterator();
  332. KnuthElement returnedElement;
  333. LinkedList returnedList = new LinkedList();
  334. LinkedList returnList = new LinkedList();
  335. InlineLevelLayoutManager prevLM = null;
  336. InlineLevelLayoutManager currLM;
  337. int fromIndex = 0;
  338. while (oldListIterator.hasNext()) {
  339. oldElement = (KnuthElement) oldListIterator.next();
  340. currLM = (InlineLevelLayoutManager) oldElement.getLayoutManager();
  341. if (prevLM == null) {
  342. prevLM = currLM;
  343. }
  344. if (currLM != prevLM || !oldListIterator.hasNext()) {
  345. if (oldListIterator.hasNext()) {
  346. returnedList.addAll
  347. (prevLM.getChangedKnuthElements
  348. (oldList.subList(fromIndex,
  349. oldListIterator.previousIndex()),
  350. /*flaggedPenalty,*/ alignment));
  351. prevLM = currLM;
  352. fromIndex = oldListIterator.previousIndex();
  353. } else if (currLM == prevLM) {
  354. returnedList.addAll
  355. (prevLM.getChangedKnuthElements
  356. (oldList.subList(fromIndex, oldList.size()),
  357. /*flaggedPenalty,*/ alignment));
  358. } else {
  359. returnedList.addAll
  360. (prevLM.getChangedKnuthElements
  361. (oldList.subList(fromIndex,
  362. oldListIterator.previousIndex()),
  363. /*flaggedPenalty,*/ alignment));
  364. if (currLM != null) {
  365. returnedList.addAll
  366. (currLM.getChangedKnuthElements
  367. (oldList.subList(oldListIterator.previousIndex(),
  368. oldList.size()),
  369. /*flaggedPenalty,*/ alignment));
  370. }
  371. }
  372. }
  373. }
  374. // "wrap" the Position stored in each element of returnedList
  375. ListIterator listIter = returnedList.listIterator();
  376. while (listIter.hasNext()) {
  377. returnedElement = (KnuthElement) listIter.next();
  378. returnedElement.setPosition
  379. (notifyPos(new NonLeafPosition(this, returnedElement.getPosition())));
  380. returnList.add(returnedElement);
  381. }
  382. return returnList;
  383. }
  384. }