Du kan inte välja fler än 25 ämnen Ämnen måste starta med en bokstav eller siffra, kan innehålla bindestreck ('-') och vara max 35 tecken långa.

AbstractBreaker.java 33KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776
  1. /*
  2. * Copyright 2004-2005 The Apache Software Foundation.
  3. *
  4. * Licensed under the Apache License, Version 2.0 (the "License");
  5. * you may not use this file except in compliance with the License.
  6. * You may obtain a copy of the License at
  7. *
  8. * http://www.apache.org/licenses/LICENSE-2.0
  9. *
  10. * Unless required by applicable law or agreed to in writing, software
  11. * distributed under the License is distributed on an "AS IS" BASIS,
  12. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13. * See the License for the specific language governing permissions and
  14. * limitations under the License.
  15. */
  16. /* $Id$ */
  17. package org.apache.fop.layoutmgr;
  18. import java.util.LinkedList;
  19. import java.util.List;
  20. import java.util.ListIterator;
  21. import org.apache.commons.logging.Log;
  22. import org.apache.commons.logging.LogFactory;
  23. import org.apache.fop.fo.Constants;
  24. import org.apache.fop.traits.MinOptMax;
  25. /**
  26. * Abstract base class for breakers (page breakers, static region handlers etc.).
  27. */
  28. public abstract class AbstractBreaker {
  29. /** logging instance */
  30. protected static Log log = LogFactory.getLog(AbstractBreaker.class);
  31. public static class PageBreakPosition extends LeafPosition {
  32. double bpdAdjust; // Percentage to adjust (stretch or shrink)
  33. int difference;
  34. int footnoteFirstListIndex;
  35. int footnoteFirstElementIndex;
  36. int footnoteLastListIndex;
  37. int footnoteLastElementIndex;
  38. PageBreakPosition(LayoutManager lm, int iBreakIndex,
  39. int ffli, int ffei, int flli, int flei,
  40. double bpdA, int diff) {
  41. super(lm, iBreakIndex);
  42. bpdAdjust = bpdA;
  43. difference = diff;
  44. footnoteFirstListIndex = ffli;
  45. footnoteFirstElementIndex = ffei;
  46. footnoteLastListIndex = flli;
  47. footnoteLastElementIndex = flei;
  48. }
  49. }
  50. public class BlockSequence extends KnuthSequence {
  51. /**
  52. * startOn represents where on the page/which page layout
  53. * should start for this BlockSequence. Acceptable values:
  54. * Constants.EN_ANY (can continue from finished location
  55. * of previous BlockSequence?), EN_COLUMN, EN_ODD_PAGE,
  56. * EN_EVEN_PAGE.
  57. */
  58. private int startOn;
  59. public BlockSequence(int iStartOn) {
  60. super();
  61. startOn = iStartOn;
  62. }
  63. public int getStartOn() {
  64. return this.startOn;
  65. }
  66. public BlockSequence endBlockSequence(Position breakPosition) {
  67. KnuthSequence temp = super.endSequence(breakPosition);
  68. if (temp != null) {
  69. BlockSequence returnSequence = new BlockSequence(startOn);
  70. returnSequence.addAll(temp);
  71. returnSequence.ignoreAtEnd = this.ignoreAtEnd;
  72. return returnSequence;
  73. } else {
  74. return null;
  75. }
  76. }
  77. }
  78. /** blockListIndex of the current BlockSequence in blockLists */
  79. private int blockListIndex = 0;
  80. private List blockLists = null;
  81. protected int alignment;
  82. private int alignmentLast;
  83. protected MinOptMax footnoteSeparatorLength = new MinOptMax(0);
  84. protected abstract int getCurrentDisplayAlign();
  85. protected abstract boolean hasMoreContent();
  86. protected abstract void addAreas(PositionIterator posIter, LayoutContext context);
  87. protected abstract LayoutManager getTopLevelLM();
  88. protected abstract LayoutManager getCurrentChildLM();
  89. /**
  90. * Controls the behaviour of the algorithm in cases where the first element of a part
  91. * overflows a line/page.
  92. * @return true if the algorithm should try to send the element to the next line/page.
  93. */
  94. protected boolean isPartOverflowRecoveryActivated() {
  95. return true;
  96. }
  97. /**
  98. * Returns the PageViewportProvider if any. PageBreaker overrides this method because each
  99. * page may have a different available BPD which needs to be accessible to the breaking
  100. * algorithm.
  101. * @return the applicable PageViewportProvider, or null if not applicable
  102. */
  103. protected PageSequenceLayoutManager.PageViewportProvider getPageViewportProvider() {
  104. return null;
  105. }
  106. /*
  107. * This method is to contain the logic to determine the LM's
  108. * getNextKnuthElements() implementation(s) that are to be called.
  109. * @return LinkedList of Knuth elements.
  110. */
  111. protected abstract LinkedList getNextKnuthElements(LayoutContext context, int alignment);
  112. /** @return true if there's no content that could be handled. */
  113. public boolean isEmpty() {
  114. return (blockLists.size() == 0);
  115. }
  116. protected void startPart(BlockSequence list, int breakClass) {
  117. //nop
  118. }
  119. /**
  120. * This method is called when no content is available for a part. Used to force empty pages.
  121. */
  122. protected void handleEmptyContent() {
  123. //nop
  124. }
  125. protected abstract void finishPart(PageBreakingAlgorithm alg, PageBreakPosition pbp);
  126. /**
  127. * Creates the top-level LayoutContext for the breaker operation.
  128. * @return the top-level LayoutContext
  129. */
  130. protected LayoutContext createLayoutContext() {
  131. return new LayoutContext(0);
  132. }
  133. /**
  134. * Used to update the LayoutContext in subclasses prior to starting a new element list.
  135. * @param context the LayoutContext to update
  136. */
  137. protected void updateLayoutContext(LayoutContext context) {
  138. //nop
  139. }
  140. /**
  141. * Used for debugging purposes. Notifies all registered observers about the element list.
  142. * Override to set different parameters.
  143. * @param elementList the Knuth element list
  144. */
  145. protected void observeElementList(List elementList) {
  146. ElementListObserver.observe(elementList, "breaker", null);
  147. }
  148. /**
  149. * Starts the page breaking process.
  150. * @param flowBPD the constant available block-progression-dimension (used for every part)
  151. */
  152. public void doLayout(int flowBPD) {
  153. LayoutContext childLC = createLayoutContext();
  154. childLC.setStackLimit(new MinOptMax(flowBPD));
  155. if (getCurrentDisplayAlign() == Constants.EN_X_FILL) {
  156. //EN_FILL is non-standard (by LF)
  157. alignment = Constants.EN_JUSTIFY;
  158. } else {
  159. alignment = Constants.EN_START;
  160. }
  161. alignmentLast = Constants.EN_START;
  162. childLC.setBPAlignment(alignment);
  163. BlockSequence blockList;
  164. blockLists = new java.util.ArrayList();
  165. log.debug("PLM> flow BPD =" + flowBPD);
  166. //*** Phase 1: Get Knuth elements ***
  167. int nextSequenceStartsOn = Constants.EN_ANY;
  168. while (hasMoreContent()) {
  169. blockLists.clear();
  170. nextSequenceStartsOn = getNextBlockList(childLC, nextSequenceStartsOn, blockLists);
  171. //*** Phase 2: Alignment and breaking ***
  172. log.debug("PLM> blockLists.size() = " + blockLists.size());
  173. for (blockListIndex = 0; blockListIndex < blockLists.size(); blockListIndex++) {
  174. blockList = (BlockSequence) blockLists.get(blockListIndex);
  175. //debug code start
  176. if (log.isDebugEnabled()) {
  177. log.debug(" blockListIndex = " + blockListIndex);
  178. String pagina = (blockList.startOn == Constants.EN_ANY) ? "any page"
  179. : (blockList.startOn == Constants.EN_ODD_PAGE) ? "odd page"
  180. : "even page";
  181. log.debug(" sequence starts on " + pagina);
  182. }
  183. observeElementList(blockList);
  184. //debug code end
  185. log.debug("PLM> start of algorithm (" + this.getClass().getName()
  186. + "), flow BPD =" + flowBPD);
  187. PageBreakingAlgorithm alg = new PageBreakingAlgorithm(getTopLevelLM(),
  188. getPageViewportProvider(),
  189. alignment, alignmentLast, footnoteSeparatorLength,
  190. isPartOverflowRecoveryActivated());
  191. int iOptPageCount;
  192. BlockSequence effectiveList;
  193. if (alignment == Constants.EN_JUSTIFY) {
  194. /* justification */
  195. effectiveList = justifyBoxes(blockList, alg, flowBPD);
  196. } else {
  197. /* no justification */
  198. effectiveList = blockList;
  199. }
  200. //iOptPageCount = alg.firstFit(effectiveList, flowBPD, 1, true);
  201. alg.setConstantLineWidth(flowBPD);
  202. iOptPageCount = alg.findBreakingPoints(effectiveList, /*flowBPD,*/
  203. 1, true, BreakingAlgorithm.ALL_BREAKS);
  204. log.debug("PLM> iOptPageCount= " + iOptPageCount
  205. + " pageBreaks.size()= " + alg.getPageBreaks().size());
  206. //*** Phase 3: Add areas ***
  207. doPhase3(alg, iOptPageCount, blockList, effectiveList);
  208. }
  209. }
  210. }
  211. /**
  212. * Phase 3 of Knuth algorithm: Adds the areas
  213. * @param alg PageBreakingAlgorithm instance which determined the breaks
  214. * @param partCount number of parts (pages) to be rendered
  215. * @param originalList original Knuth element list
  216. * @param effectiveList effective Knuth element list (after adjustments)
  217. */
  218. protected abstract void doPhase3(PageBreakingAlgorithm alg, int partCount,
  219. BlockSequence originalList, BlockSequence effectiveList);
  220. /**
  221. * Phase 3 of Knuth algorithm: Adds the areas
  222. * @param alg PageBreakingAlgorithm instance which determined the breaks
  223. * @param partCount number of parts (pages) to be rendered
  224. * @param originalList original Knuth element list
  225. * @param effectiveList effective Knuth element list (after adjustments)
  226. */
  227. protected void addAreas(PageBreakingAlgorithm alg, int partCount,
  228. BlockSequence originalList, BlockSequence effectiveList) {
  229. LayoutContext childLC;
  230. // add areas
  231. ListIterator effectiveListIterator = effectiveList.listIterator();
  232. int startElementIndex = 0;
  233. int endElementIndex = 0;
  234. int lastBreak = -1;
  235. for (int p = 0; p < partCount; p++) {
  236. PageBreakPosition pbp = (PageBreakPosition) alg.getPageBreaks().get(p);
  237. //Check the last break position for forced breaks
  238. int lastBreakClass;
  239. if (p == 0) {
  240. lastBreakClass = effectiveList.getStartOn();
  241. } else {
  242. KnuthElement lastBreakElement = effectiveList.getElement(endElementIndex);
  243. if (lastBreakElement.isPenalty()) {
  244. KnuthPenalty pen = (KnuthPenalty)lastBreakElement;
  245. lastBreakClass = pen.getBreakClass();
  246. } else {
  247. lastBreakClass = Constants.EN_COLUMN;
  248. }
  249. }
  250. //the end of the new part
  251. endElementIndex = pbp.getLeafPos();
  252. // ignore the first elements added by the
  253. // PageSequenceLayoutManager
  254. startElementIndex += (startElementIndex == 0)
  255. ? effectiveList.ignoreAtStart
  256. : 0;
  257. log.debug("PLM> part: " + (p + 1)
  258. + ", start at pos " + startElementIndex
  259. + ", break at pos " + endElementIndex);
  260. startPart(effectiveList, lastBreakClass);
  261. int displayAlign = getCurrentDisplayAlign();
  262. //The following is needed by SpaceResolver.performConditionalsNotification()
  263. //further down as there may be important Position elements in the element list trailer
  264. int notificationEndElementIndex = endElementIndex;
  265. // ignore the last elements added by the
  266. // PageSequenceLayoutManager
  267. endElementIndex -= (endElementIndex == (originalList.size() - 1))
  268. ? effectiveList.ignoreAtEnd
  269. : 0;
  270. // ignore the last element in the page if it is a KnuthGlue
  271. // object
  272. if (((KnuthElement) effectiveList.get(endElementIndex))
  273. .isGlue()) {
  274. endElementIndex--;
  275. }
  276. // ignore KnuthGlue and KnuthPenalty objects
  277. // at the beginning of the line
  278. effectiveListIterator = effectiveList
  279. .listIterator(startElementIndex);
  280. KnuthElement firstElement;
  281. while (effectiveListIterator.hasNext()
  282. && !(firstElement = (KnuthElement) effectiveListIterator.next())
  283. .isBox()) {
  284. /*
  285. if (firstElement.isGlue() && firstElement.getLayoutManager() != null) {
  286. // discard the space representd by the glue element
  287. ((BlockLevelLayoutManager) firstElement
  288. .getLayoutManager())
  289. .discardSpace((KnuthGlue) firstElement);
  290. }*/
  291. startElementIndex++;
  292. }
  293. if (startElementIndex <= endElementIndex) {
  294. log.debug(" addAreas from " + startElementIndex
  295. + " to " + endElementIndex);
  296. childLC = new LayoutContext(0);
  297. // set the space adjustment ratio
  298. childLC.setSpaceAdjust(pbp.bpdAdjust);
  299. // add space before if display-align is center or bottom
  300. // add space after if display-align is distribute and
  301. // this is not the last page
  302. if (pbp.difference != 0 && displayAlign == Constants.EN_CENTER) {
  303. childLC.setSpaceBefore(pbp.difference / 2);
  304. } else if (pbp.difference != 0 && displayAlign == Constants.EN_AFTER) {
  305. childLC.setSpaceBefore(pbp.difference);
  306. } else if (pbp.difference != 0 && displayAlign == Constants.EN_X_DISTRIBUTE
  307. && p < (partCount - 1)) {
  308. // count the boxes whose width is not 0
  309. int boxCount = 0;
  310. effectiveListIterator = effectiveList
  311. .listIterator(startElementIndex);
  312. while (effectiveListIterator.nextIndex() <= endElementIndex) {
  313. KnuthElement tempEl = (KnuthElement)effectiveListIterator.next();
  314. if (tempEl.isBox() && tempEl.getW() > 0) {
  315. boxCount++;
  316. }
  317. }
  318. // split the difference
  319. if (boxCount >= 2) {
  320. childLC.setSpaceAfter(pbp.difference / (boxCount - 1));
  321. }
  322. }
  323. /* *** *** non-standard extension *** *** */
  324. if (displayAlign == Constants.EN_X_FILL) {
  325. int averageLineLength = optimizeLineLength(effectiveList, startElementIndex, endElementIndex);
  326. if (averageLineLength != 0) {
  327. childLC.setStackLimit(new MinOptMax(averageLineLength));
  328. }
  329. }
  330. /* *** *** non-standard extension *** *** */
  331. // Handle SpaceHandling(Break)Positions, see SpaceResolver!
  332. SpaceResolver.performConditionalsNotification(effectiveList,
  333. startElementIndex, notificationEndElementIndex, lastBreak);
  334. // Add areas now!
  335. addAreas(new KnuthPossPosIter(effectiveList,
  336. startElementIndex, endElementIndex + 1), childLC);
  337. } else {
  338. //no content for this part
  339. handleEmptyContent();
  340. }
  341. finishPart(alg, pbp);
  342. lastBreak = endElementIndex;
  343. startElementIndex = pbp.getLeafPos() + 1;
  344. }
  345. }
  346. /**
  347. * Notifies the layout managers about the space and conditional length situation based on
  348. * the break decisions.
  349. * @param effectiveList Element list to be painted
  350. * @param startElementIndex start index of the part
  351. * @param endElementIndex end index of the part
  352. * @param lastBreak index of the last break element
  353. */
  354. /**
  355. * Handles span changes reported through the <code>LayoutContext</code>.
  356. * Only used by the PSLM and called by <code>getNextBlockList()</code>.
  357. * @param childLC the LayoutContext
  358. * @param nextSequenceStartsOn previous value for break handling
  359. * @return effective value for break handling
  360. */
  361. protected int handleSpanChange(LayoutContext childLC, int nextSequenceStartsOn) {
  362. return nextSequenceStartsOn;
  363. }
  364. /**
  365. * Gets the next block list (sequence) and adds it to a list of block lists if it's not empty.
  366. * @param childLC LayoutContext to use
  367. * @param nextSequenceStartsOn indicates on what page the next sequence should start
  368. * @param blockLists list of block lists (sequences)
  369. * @return the page on which the next content should appear after a hard break
  370. */
  371. protected int getNextBlockList(LayoutContext childLC,
  372. int nextSequenceStartsOn,
  373. List blockLists) {
  374. updateLayoutContext(childLC);
  375. //Make sure the span change signal is reset
  376. childLC.signalSpanChange(Constants.NOT_SET);
  377. LinkedList returnedList;
  378. BlockSequence blockList;
  379. if ((returnedList = getNextKnuthElements(childLC, alignment)) != null) {
  380. if (returnedList.size() == 0) {
  381. return nextSequenceStartsOn;
  382. }
  383. blockList = new BlockSequence(nextSequenceStartsOn);
  384. //Only implemented by the PSLM
  385. nextSequenceStartsOn = handleSpanChange(childLC, nextSequenceStartsOn);
  386. Position breakPosition = null;
  387. if (((KnuthElement) returnedList.getLast()).isPenalty()
  388. && ((KnuthPenalty) returnedList.getLast()).getP() == -KnuthElement.INFINITE) {
  389. KnuthPenalty breakPenalty = (KnuthPenalty) returnedList
  390. .removeLast();
  391. breakPosition = breakPenalty.getPosition();
  392. switch (breakPenalty.getBreakClass()) {
  393. case Constants.EN_PAGE:
  394. log.debug("PLM> break - PAGE");
  395. nextSequenceStartsOn = Constants.EN_ANY;
  396. break;
  397. case Constants.EN_COLUMN:
  398. log.debug("PLM> break - COLUMN");
  399. //TODO Fix this when implementing multi-column layout
  400. nextSequenceStartsOn = Constants.EN_COLUMN;
  401. break;
  402. case Constants.EN_ODD_PAGE:
  403. log.debug("PLM> break - ODD PAGE");
  404. nextSequenceStartsOn = Constants.EN_ODD_PAGE;
  405. break;
  406. case Constants.EN_EVEN_PAGE:
  407. log.debug("PLM> break - EVEN PAGE");
  408. nextSequenceStartsOn = Constants.EN_EVEN_PAGE;
  409. break;
  410. default:
  411. throw new IllegalStateException("Invalid break class: "
  412. + breakPenalty.getBreakClass());
  413. }
  414. }
  415. blockList.addAll(returnedList);
  416. BlockSequence seq = null;
  417. seq = blockList.endBlockSequence(breakPosition);
  418. if (seq != null) {
  419. blockLists.add(seq);
  420. }
  421. }
  422. return nextSequenceStartsOn;
  423. }
  424. /**
  425. * @param effectiveList effective block list to work on
  426. * @param startElementIndex
  427. * @param endElementIndex
  428. * @return the average line length, 0 if there's no content
  429. */
  430. private int optimizeLineLength(KnuthSequence effectiveList, int startElementIndex, int endElementIndex) {
  431. ListIterator effectiveListIterator;
  432. // optimize line length
  433. int boxCount = 0;
  434. int accumulatedLineLength = 0;
  435. int greatestMinimumLength = 0;
  436. effectiveListIterator = effectiveList
  437. .listIterator(startElementIndex);
  438. while (effectiveListIterator.nextIndex() <= endElementIndex) {
  439. KnuthElement tempEl = (KnuthElement) effectiveListIterator
  440. .next();
  441. if (tempEl instanceof KnuthBlockBox) {
  442. KnuthBlockBox blockBox = (KnuthBlockBox) tempEl;
  443. if (blockBox.getBPD() > 0) {
  444. log.debug("PSLM> nominal length of line = " + blockBox.getBPD());
  445. log.debug(" range = "
  446. + blockBox.getIPDRange());
  447. boxCount++;
  448. accumulatedLineLength += ((KnuthBlockBox) tempEl)
  449. .getBPD();
  450. }
  451. if (blockBox.getIPDRange().min > greatestMinimumLength) {
  452. greatestMinimumLength = blockBox
  453. .getIPDRange().min;
  454. }
  455. }
  456. }
  457. int averageLineLength = 0;
  458. if (accumulatedLineLength > 0 && boxCount > 0) {
  459. averageLineLength = (int) (accumulatedLineLength / boxCount);
  460. log.debug("Average line length = " + averageLineLength);
  461. if (averageLineLength < greatestMinimumLength) {
  462. averageLineLength = greatestMinimumLength;
  463. log.debug(" Correction to: " + averageLineLength);
  464. }
  465. }
  466. return averageLineLength;
  467. }
  468. /**
  469. * Justifies the boxes and returns them as a new KnuthSequence.
  470. * @param blockList block list to justify
  471. * @param alg reference to the algorithm instance
  472. * @param availableBPD the available BPD
  473. * @return the effective list
  474. */
  475. private BlockSequence justifyBoxes(BlockSequence blockList, PageBreakingAlgorithm alg, int availableBPD) {
  476. int iOptPageNumber;
  477. alg.setConstantLineWidth(availableBPD);
  478. iOptPageNumber = alg.findBreakingPoints(blockList, /*availableBPD,*/
  479. 1, true, BreakingAlgorithm.ALL_BREAKS);
  480. log.debug("PLM> iOptPageNumber= " + iOptPageNumber);
  481. //
  482. ListIterator sequenceIterator = blockList.listIterator();
  483. ListIterator breakIterator = alg.getPageBreaks().listIterator();
  484. KnuthElement thisElement = null;
  485. PageBreakPosition thisBreak;
  486. int accumulatedS; // accumulated stretch or shrink
  487. int adjustedDiff; // difference already adjusted
  488. int firstElementIndex;
  489. while (breakIterator.hasNext()) {
  490. thisBreak = (PageBreakPosition) breakIterator.next();
  491. if (log.isDebugEnabled()) {
  492. log.debug("| first page: break= "
  493. + thisBreak.getLeafPos() + " difference= "
  494. + thisBreak.difference + " ratio= "
  495. + thisBreak.bpdAdjust);
  496. }
  497. accumulatedS = 0;
  498. adjustedDiff = 0;
  499. // glue and penalty items at the beginning of the page must
  500. // be ignored:
  501. // the first element returned by sequenceIterator.next()
  502. // inside the
  503. // while loop must be a box
  504. KnuthElement firstElement;
  505. while (!(firstElement = (KnuthElement) sequenceIterator
  506. .next()).isBox()) {
  507. //
  508. log.debug("PLM> ignoring glue or penalty element "
  509. + "at the beginning of the sequence");
  510. if (firstElement.isGlue()) {
  511. ((BlockLevelLayoutManager) firstElement
  512. .getLayoutManager())
  513. .discardSpace((KnuthGlue) firstElement);
  514. }
  515. }
  516. firstElementIndex = sequenceIterator.previousIndex();
  517. sequenceIterator.previous();
  518. // scan the sub-sequence representing a page,
  519. // collecting information about potential adjustments
  520. MinOptMax lineNumberMaxAdjustment = new MinOptMax(0);
  521. MinOptMax spaceMaxAdjustment = new MinOptMax(0);
  522. double spaceAdjustmentRatio = 0.0;
  523. LinkedList blockSpacesList = new LinkedList();
  524. LinkedList unconfirmedList = new LinkedList();
  525. LinkedList adjustableLinesList = new LinkedList();
  526. boolean bBoxSeen = false;
  527. while (sequenceIterator.hasNext()
  528. && sequenceIterator.nextIndex() <= thisBreak
  529. .getLeafPos()) {
  530. thisElement = (KnuthElement) sequenceIterator.next();
  531. if (thisElement.isGlue()) {
  532. // glue elements are used to represent adjustable
  533. // lines
  534. // and adjustable spaces between blocks
  535. switch (((KnuthGlue) thisElement)
  536. .getAdjustmentClass()) {
  537. case BlockLevelLayoutManager.SPACE_BEFORE_ADJUSTMENT:
  538. // fall through
  539. case BlockLevelLayoutManager.SPACE_AFTER_ADJUSTMENT:
  540. // potential space adjustment
  541. // glue items before the first box or after the
  542. // last one
  543. // must be ignored
  544. unconfirmedList.add(thisElement);
  545. break;
  546. case BlockLevelLayoutManager.LINE_NUMBER_ADJUSTMENT:
  547. // potential line number adjustment
  548. lineNumberMaxAdjustment.max += ((KnuthGlue) thisElement)
  549. .getY();
  550. lineNumberMaxAdjustment.min -= ((KnuthGlue) thisElement)
  551. .getZ();
  552. adjustableLinesList.add(thisElement);
  553. break;
  554. case BlockLevelLayoutManager.LINE_HEIGHT_ADJUSTMENT:
  555. // potential line height adjustment
  556. break;
  557. default:
  558. // nothing
  559. }
  560. } else if (thisElement.isBox()) {
  561. if (!bBoxSeen) {
  562. // this is the first box met in this page
  563. bBoxSeen = true;
  564. } else if (unconfirmedList.size() > 0) {
  565. // glue items in unconfirmedList were not after
  566. // the last box
  567. // in this page; they must be added to
  568. // blockSpaceList
  569. while (unconfirmedList.size() > 0) {
  570. KnuthGlue blockSpace = (KnuthGlue) unconfirmedList
  571. .removeFirst();
  572. spaceMaxAdjustment.max += ((KnuthGlue) blockSpace)
  573. .getY();
  574. spaceMaxAdjustment.min -= ((KnuthGlue) blockSpace)
  575. .getZ();
  576. blockSpacesList.add(blockSpace);
  577. }
  578. }
  579. }
  580. }
  581. log.debug("| line number adj= "
  582. + lineNumberMaxAdjustment);
  583. log.debug("| space adj = "
  584. + spaceMaxAdjustment);
  585. if (thisElement.isPenalty() && thisElement.getW() > 0) {
  586. log.debug(" mandatory variation to the number of lines!");
  587. ((BlockLevelLayoutManager) thisElement
  588. .getLayoutManager()).negotiateBPDAdjustment(
  589. thisElement.getW(), thisElement);
  590. }
  591. if (thisBreak.bpdAdjust != 0
  592. && (thisBreak.difference > 0 && thisBreak.difference <= spaceMaxAdjustment.max)
  593. || (thisBreak.difference < 0 && thisBreak.difference >= spaceMaxAdjustment.min)) {
  594. // modify only the spaces between blocks
  595. spaceAdjustmentRatio = ((double) thisBreak.difference / (thisBreak.difference > 0 ? spaceMaxAdjustment.max
  596. : spaceMaxAdjustment.min));
  597. adjustedDiff += adjustBlockSpaces(
  598. blockSpacesList,
  599. thisBreak.difference,
  600. (thisBreak.difference > 0 ? spaceMaxAdjustment.max
  601. : -spaceMaxAdjustment.min));
  602. log.debug("single space: "
  603. + (adjustedDiff == thisBreak.difference
  604. || thisBreak.bpdAdjust == 0 ? "ok"
  605. : "ERROR"));
  606. } else if (thisBreak.bpdAdjust != 0) {
  607. adjustedDiff += adjustLineNumbers(
  608. adjustableLinesList,
  609. thisBreak.difference,
  610. (thisBreak.difference > 0 ? lineNumberMaxAdjustment.max
  611. : -lineNumberMaxAdjustment.min));
  612. adjustedDiff += adjustBlockSpaces(
  613. blockSpacesList,
  614. thisBreak.difference - adjustedDiff,
  615. ((thisBreak.difference - adjustedDiff) > 0 ? spaceMaxAdjustment.max
  616. : -spaceMaxAdjustment.min));
  617. log.debug("lines and space: "
  618. + (adjustedDiff == thisBreak.difference
  619. || thisBreak.bpdAdjust == 0 ? "ok"
  620. : "ERROR"));
  621. }
  622. }
  623. // create a new sequence: the new elements will contain the
  624. // Positions
  625. // which will be used in the addAreas() phase
  626. BlockSequence effectiveList = new BlockSequence(blockList.getStartOn());
  627. effectiveList.addAll(getCurrentChildLM().getChangedKnuthElements(
  628. blockList.subList(0, blockList.size() - blockList.ignoreAtEnd),
  629. /* 0, */0));
  630. //effectiveList.add(new KnuthPenalty(0, -KnuthElement.INFINITE,
  631. // false, new Position(this), false));
  632. effectiveList.endSequence();
  633. ElementListObserver.observe(effectiveList, "breaker-effective", null);
  634. alg.getPageBreaks().clear(); //Why this?
  635. return effectiveList;
  636. }
  637. private int adjustBlockSpaces(LinkedList spaceList, int difference, int total) {
  638. if (log.isDebugEnabled()) {
  639. log.debug("AdjustBlockSpaces: difference " + difference + " / " + total
  640. + " on " + spaceList.size() + " spaces in block");
  641. }
  642. ListIterator spaceListIterator = spaceList.listIterator();
  643. int adjustedDiff = 0;
  644. int partial = 0;
  645. while (spaceListIterator.hasNext()) {
  646. KnuthGlue blockSpace = (KnuthGlue)spaceListIterator.next();
  647. partial += (difference > 0 ? blockSpace.getY() : blockSpace.getZ());
  648. if (log.isDebugEnabled()) {
  649. log.debug("available = " + partial + " / " + total);
  650. log.debug("competenza = "
  651. + (((int)((float) partial * difference / total)) - adjustedDiff)
  652. + " / " + difference);
  653. }
  654. int newAdjust = ((BlockLevelLayoutManager) blockSpace.getLayoutManager()).negotiateBPDAdjustment(((int) ((float) partial * difference / total)) - adjustedDiff, blockSpace);
  655. adjustedDiff += newAdjust;
  656. }
  657. return adjustedDiff;
  658. }
  659. private int adjustLineNumbers(LinkedList lineList, int difference, int total) {
  660. if (log.isDebugEnabled()) {
  661. log.debug("AdjustLineNumbers: difference " + difference + " / " + total + " on " + lineList.size() + " elements");
  662. }
  663. // int adjustedDiff = 0;
  664. // int partial = 0;
  665. // KnuthGlue prevLine = null;
  666. // KnuthGlue currLine = null;
  667. // ListIterator lineListIterator = lineList.listIterator();
  668. // while (lineListIterator.hasNext()) {
  669. // currLine = (KnuthGlue)lineListIterator.next();
  670. // if (prevLine != null
  671. // && prevLine.getLayoutManager() != currLine.getLayoutManager()) {
  672. // int newAdjust = ((BlockLevelLayoutManager) prevLine.getLayoutManager())
  673. // .negotiateBPDAdjustment(((int) ((float) partial * difference / total)) - adjustedDiff, prevLine);
  674. // adjustedDiff += newAdjust;
  675. // }
  676. // partial += (difference > 0 ? currLine.getY() : currLine.getZ());
  677. // prevLine = currLine;
  678. // }
  679. // if (currLine != null) {
  680. // int newAdjust = ((BlockLevelLayoutManager) currLine.getLayoutManager())
  681. // .negotiateBPDAdjustment(((int) ((float) partial * difference / total)) - adjustedDiff, currLine);
  682. // adjustedDiff += newAdjust;
  683. // }
  684. // return adjustedDiff;
  685. ListIterator lineListIterator = lineList.listIterator();
  686. int adjustedDiff = 0;
  687. int partial = 0;
  688. while (lineListIterator.hasNext()) {
  689. KnuthGlue line = (KnuthGlue)lineListIterator.next();
  690. partial += (difference > 0 ? line.getY() : line.getZ());
  691. int newAdjust = ((BlockLevelLayoutManager) line.getLayoutManager()).negotiateBPDAdjustment(((int) ((float) partial * difference / total)) - adjustedDiff, line);
  692. adjustedDiff += newAdjust;
  693. }
  694. return adjustedDiff;
  695. }
  696. }