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.

LineLayoutManager.java 81KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818
  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.ArrayList;
  20. import java.util.LinkedList;
  21. import java.util.List;
  22. import java.util.ListIterator;
  23. import org.apache.commons.logging.Log;
  24. import org.apache.commons.logging.LogFactory;
  25. import org.apache.fop.area.Area;
  26. import org.apache.fop.area.LineArea;
  27. import org.apache.fop.area.Trait;
  28. import org.apache.fop.area.inline.InlineArea;
  29. import org.apache.fop.datatypes.Length;
  30. import org.apache.fop.datatypes.Numeric;
  31. import org.apache.fop.fo.Constants;
  32. import org.apache.fop.fo.flow.Block;
  33. import org.apache.fop.fo.properties.CommonHyphenation;
  34. import org.apache.fop.fonts.Font;
  35. import org.apache.fop.fonts.FontInfo;
  36. import org.apache.fop.fonts.FontTriplet;
  37. import org.apache.fop.hyphenation.Hyphenation;
  38. import org.apache.fop.hyphenation.Hyphenator;
  39. import org.apache.fop.layoutmgr.BlockLevelLayoutManager;
  40. import org.apache.fop.layoutmgr.BreakElement;
  41. import org.apache.fop.layoutmgr.BreakingAlgorithm;
  42. import org.apache.fop.layoutmgr.ElementListObserver;
  43. import org.apache.fop.layoutmgr.InlineKnuthSequence;
  44. import org.apache.fop.layoutmgr.KnuthBlockBox;
  45. import org.apache.fop.layoutmgr.KnuthBox;
  46. import org.apache.fop.layoutmgr.KnuthElement;
  47. import org.apache.fop.layoutmgr.KnuthGlue;
  48. import org.apache.fop.layoutmgr.KnuthPenalty;
  49. import org.apache.fop.layoutmgr.KnuthPossPosIter;
  50. import org.apache.fop.layoutmgr.KnuthSequence;
  51. import org.apache.fop.layoutmgr.LayoutContext;
  52. import org.apache.fop.layoutmgr.LayoutManager;
  53. import org.apache.fop.layoutmgr.LeafPosition;
  54. import org.apache.fop.layoutmgr.ListElement;
  55. import org.apache.fop.layoutmgr.NonLeafPosition;
  56. import org.apache.fop.layoutmgr.Position;
  57. import org.apache.fop.layoutmgr.PositionIterator;
  58. import org.apache.fop.layoutmgr.SpaceSpecifier;
  59. import org.apache.fop.traits.MinOptMax;
  60. /**
  61. * LayoutManager for lines. It builds one or more lines containing
  62. * inline areas generated by its sub layout managers.
  63. * A break is found for each line which may contain one of more
  64. * breaks from the child layout managers.
  65. * Once a break is found then it is return for the parent layout
  66. * manager to handle.
  67. * When the areas are being added to the page this manager
  68. * creates a line area to contain the inline areas added by the
  69. * child layout managers.
  70. */
  71. public class LineLayoutManager extends InlineStackingLayoutManager
  72. implements BlockLevelLayoutManager {
  73. /**
  74. * logging instance
  75. */
  76. private static Log log = LogFactory.getLog(LineLayoutManager.class);
  77. private Block fobj;
  78. private boolean isFirstInBlock;
  79. /** {@inheritDoc} */
  80. public void initialize() {
  81. textAlignment = fobj.getTextAlign();
  82. textAlignmentLast = fobj.getTextAlignLast();
  83. textIndent = fobj.getTextIndent();
  84. lastLineEndIndent = fobj.getLastLineEndIndent();
  85. hyphenationProperties = fobj.getCommonHyphenation();
  86. hyphenationLadderCount = fobj.getHyphenationLadderCount();
  87. wrapOption = fobj.getWrapOption();
  88. whiteSpaceTreament = fobj.getWhitespaceTreatment();
  89. //
  90. effectiveAlignment = getEffectiveAlignment(textAlignment, textAlignmentLast);
  91. isFirstInBlock = (this == getParent().getChildLMs().get(0));
  92. }
  93. private int getEffectiveAlignment(int alignment, int alignmentLast) {
  94. if (textAlignment != EN_JUSTIFY && textAlignmentLast == EN_JUSTIFY) {
  95. return 0;
  96. } else {
  97. return textAlignment;
  98. }
  99. }
  100. /**
  101. * Private class to store information about inline breaks.
  102. * Each value holds the start and end indexes into a List of
  103. * inline break positions.
  104. */
  105. private static class LineBreakPosition extends LeafPosition {
  106. private int iParIndex; // index of the Paragraph this Position refers to
  107. private int iStartIndex; //index of the first element this Position refers to
  108. private int availableShrink;
  109. private int availableStretch;
  110. private int difference;
  111. private double dAdjust; // Percentage to adjust (stretch or shrink)
  112. private double ipdAdjust; // Percentage to adjust (stretch or shrink)
  113. private int startIndent;
  114. private int lineHeight;
  115. private int lineWidth;
  116. private int spaceBefore;
  117. private int spaceAfter;
  118. private int baseline;
  119. LineBreakPosition(LayoutManager lm, int index, int iStartIndex, int iBreakIndex,
  120. int shrink, int stretch, int diff,
  121. double ipdA, double adjust, int ind,
  122. int lh, int lw, int sb, int sa, int bl) {
  123. super(lm, iBreakIndex);
  124. availableShrink = shrink;
  125. availableStretch = stretch;
  126. difference = diff;
  127. iParIndex = index;
  128. this.iStartIndex = iStartIndex;
  129. ipdAdjust = ipdA;
  130. dAdjust = adjust;
  131. startIndent = ind;
  132. lineHeight = lh;
  133. lineWidth = lw;
  134. spaceBefore = sb;
  135. spaceAfter = sa;
  136. baseline = bl;
  137. }
  138. }
  139. private int textAlignment = EN_JUSTIFY;
  140. private int textAlignmentLast;
  141. private int effectiveAlignment;
  142. private Length textIndent;
  143. private Length lastLineEndIndent;
  144. private CommonHyphenation hyphenationProperties;
  145. private Numeric hyphenationLadderCount;
  146. private int wrapOption = EN_WRAP;
  147. private int whiteSpaceTreament;
  148. //private LayoutProps layoutProps;
  149. private Length lineHeight;
  150. private int lead;
  151. private int follow;
  152. private AlignmentContext alignmentContext = null;
  153. private List knuthParagraphs = null;
  154. private int iReturnedLBP = 0;
  155. // parameters of Knuth's algorithm:
  156. // penalty value for flagged penalties
  157. private int flaggedPenalty = 50;
  158. private LineLayoutPossibilities lineLayouts;
  159. private List lineLayoutsList;
  160. private int iLineWidth = 0;
  161. /**
  162. * this constant is used to create elements when text-align is center:
  163. * every TextLM descendant of LineLM must use the same value,
  164. * otherwise the line breaking algorithm does not find the right
  165. * break point
  166. */
  167. public static final int DEFAULT_SPACE_WIDTH = 3336;
  168. /**
  169. * This class is used to remember
  170. * which was the first element in the paragraph
  171. * returned by each LM.
  172. */
  173. private class Update {
  174. private InlineLevelLayoutManager inlineLM;
  175. private int iFirstIndex;
  176. public Update(InlineLevelLayoutManager lm, int index) {
  177. inlineLM = lm;
  178. iFirstIndex = index;
  179. }
  180. }
  181. // this class represents a paragraph
  182. private class Paragraph extends InlineKnuthSequence {
  183. /** Number of elements to ignore at the beginning of the list. */
  184. private int ignoreAtStart = 0;
  185. /** Number of elements to ignore at the end of the list. */
  186. private int ignoreAtEnd = 0;
  187. // space at the end of the last line (in millipoints)
  188. private MinOptMax lineFiller;
  189. private int textAlignment;
  190. private int textAlignmentLast;
  191. private int textIndent;
  192. private int lastLineEndIndent;
  193. // the LM which created the paragraph
  194. private LineLayoutManager layoutManager;
  195. Paragraph(LineLayoutManager llm, int alignment, int alignmentLast,
  196. int indent, int endIndent) {
  197. super();
  198. layoutManager = llm;
  199. textAlignment = alignment;
  200. textAlignmentLast = alignmentLast;
  201. textIndent = indent;
  202. lastLineEndIndent = endIndent;
  203. }
  204. public void startSequence() {
  205. // set the minimum amount of empty space at the end of the
  206. // last line
  207. if (textAlignment == EN_CENTER) {
  208. lineFiller = new MinOptMax(lastLineEndIndent);
  209. } else {
  210. lineFiller = new MinOptMax(lastLineEndIndent,
  211. lastLineEndIndent,
  212. layoutManager.iLineWidth);
  213. }
  214. // add auxiliary elements at the beginning of the paragraph
  215. if (textAlignment == EN_CENTER && textAlignmentLast != EN_JUSTIFY) {
  216. this.add(new KnuthGlue(0, 3 * DEFAULT_SPACE_WIDTH, 0,
  217. null, false));
  218. ignoreAtStart++;
  219. }
  220. // add the element representing text indentation
  221. // at the beginning of the first paragraph
  222. if (isFirstInBlock && knuthParagraphs.size() == 0
  223. && textIndent != 0) {
  224. this.add(new KnuthInlineBox(textIndent, null,
  225. null, false));
  226. ignoreAtStart++;
  227. }
  228. }
  229. public void endParagraph() {
  230. KnuthSequence finishedPar = this.endSequence();
  231. if (finishedPar != null) {
  232. knuthParagraphs.add(finishedPar);
  233. }
  234. }
  235. public KnuthSequence endSequence() {
  236. if (this.size() > ignoreAtStart) {
  237. if (textAlignment == EN_CENTER
  238. && textAlignmentLast != EN_JUSTIFY) {
  239. this.add(new KnuthGlue(0, 3 * DEFAULT_SPACE_WIDTH, 0,
  240. null, false));
  241. this.add(new KnuthPenalty(lineFiller.opt, -KnuthElement.INFINITE,
  242. false, null, false));
  243. ignoreAtEnd = 2;
  244. } else if (textAlignmentLast != EN_JUSTIFY) {
  245. // add the elements representing the space
  246. // at the end of the last line
  247. // and the forced break
  248. this.add(new KnuthPenalty(0, KnuthElement.INFINITE,
  249. false, null, false));
  250. this.add(new KnuthGlue(0,
  251. lineFiller.max - lineFiller.opt,
  252. lineFiller.opt - lineFiller.min, null, false));
  253. this.add(new KnuthPenalty(lineFiller.opt, -KnuthElement.INFINITE,
  254. false, null, false));
  255. ignoreAtEnd = 3;
  256. } else {
  257. // add only the element representing the forced break
  258. this.add(new KnuthPenalty(lineFiller.opt, -KnuthElement.INFINITE,
  259. false, null, false));
  260. ignoreAtEnd = 1;
  261. }
  262. return this;
  263. } else {
  264. this.clear();
  265. return null;
  266. }
  267. }
  268. /**
  269. * @return true if the sequence contains a box
  270. */
  271. public boolean containsBox() {
  272. for (int i = 0; i < this.size(); i++) {
  273. KnuthElement el = (KnuthElement)this.get(i);
  274. if (el.isBox()) {
  275. return true;
  276. }
  277. }
  278. return false;
  279. }
  280. }
  281. private class LineBreakingAlgorithm extends BreakingAlgorithm {
  282. private LineLayoutManager thisLLM;
  283. private int pageAlignment;
  284. private int activePossibility;
  285. private int addedPositions;
  286. private int textIndent;
  287. private int fillerMinWidth;
  288. private int lineHeight;
  289. private int lead;
  290. private int follow;
  291. private int maxDiff;
  292. private static final double MAX_DEMERITS = 10e6;
  293. public LineBreakingAlgorithm (int pageAlign,
  294. int textAlign, int textAlignLast,
  295. int indent, int fillerWidth,
  296. int lh, int ld, int fl, boolean first,
  297. int maxFlagCount, LineLayoutManager llm) {
  298. super(textAlign, textAlignLast, first, false, maxFlagCount);
  299. pageAlignment = pageAlign;
  300. textIndent = indent;
  301. fillerMinWidth = fillerWidth;
  302. lineHeight = lh;
  303. lead = ld;
  304. follow = fl;
  305. thisLLM = llm;
  306. activePossibility = -1;
  307. maxDiff = fobj.getWidows() >= fobj.getOrphans()
  308. ? fobj.getWidows()
  309. : fobj.getOrphans();
  310. }
  311. public void updateData1(int lineCount, double demerits) {
  312. lineLayouts.addPossibility(lineCount, demerits);
  313. if (super.log.isTraceEnabled()) {
  314. super.log.trace(
  315. "Layout possibility in " + lineCount + " lines; break at position:");
  316. }
  317. }
  318. public void updateData2(KnuthNode bestActiveNode,
  319. KnuthSequence par,
  320. int total) {
  321. // compute indent and adjustment ratio, according to
  322. // the value of text-align and text-align-last
  323. int indent = 0;
  324. int difference = bestActiveNode.difference;
  325. int textAlign = (bestActiveNode.line < total) ? alignment : alignmentLast;
  326. indent += (textAlign == Constants.EN_CENTER)
  327. ? difference / 2 : (textAlign == Constants.EN_END) ? difference : 0;
  328. indent += (bestActiveNode.line == 1 && bFirst && isFirstInBlock) ? textIndent : 0;
  329. double ratio = (textAlign == Constants.EN_JUSTIFY
  330. || difference < 0 && -difference <= bestActiveNode.availableShrink)
  331. ? bestActiveNode.adjustRatio : 0;
  332. // add nodes at the beginning of the list, as they are found
  333. // backwards, from the last one to the first one
  334. // the first time this method is called, initialize activePossibility
  335. if (activePossibility == -1) {
  336. activePossibility = 0;
  337. addedPositions = 0;
  338. }
  339. if (addedPositions == lineLayouts.getLineCount(activePossibility)) {
  340. activePossibility++;
  341. addedPositions = 0;
  342. }
  343. if (log.isWarnEnabled()) {
  344. int lack = difference + bestActiveNode.availableShrink;
  345. if (lack < 0) {
  346. InlineLevelEventProducer eventProducer
  347. = InlineLevelEventProducer.Factory.create(
  348. getFObj().getUserAgent().getEventBroadcaster());
  349. eventProducer.lineOverflows(this, addedPositions + 1,
  350. -lack, getFObj().getLocator());
  351. String textDiff = (lack < -50000 ? "more than 50 points" : (-lack) + "mpt");
  352. }
  353. }
  354. //log.debug("LLM> (" + (lineLayouts.getLineNumber(activePossibility) - addedPositions)
  355. // + ") difference = " + difference + " ratio = " + ratio);
  356. lineLayouts.addBreakPosition(makeLineBreakPosition(par,
  357. (bestActiveNode.line > 1 ? bestActiveNode.previous.position + 1 : 0),
  358. bestActiveNode.position,
  359. bestActiveNode.availableShrink - (addedPositions > 0
  360. ? 0 : ((Paragraph)par).lineFiller.opt - ((Paragraph)par).lineFiller.min),
  361. bestActiveNode.availableStretch,
  362. difference, ratio, indent), activePossibility);
  363. addedPositions++;
  364. }
  365. /* reset activePossibility, as if breakpoints have not yet been computed
  366. */
  367. public void resetAlgorithm() {
  368. activePossibility = -1;
  369. }
  370. private LineBreakPosition makeLineBreakPosition(KnuthSequence par,
  371. int firstElementIndex,
  372. int lastElementIndex,
  373. int availableShrink,
  374. int availableStretch,
  375. int difference,
  376. double ratio,
  377. int indent) {
  378. // line height calculation - spaceBefore may differ from spaceAfter
  379. // by 1mpt due to rounding
  380. int spaceBefore = (lineHeight - lead - follow) / 2;
  381. int spaceAfter = lineHeight - lead - follow - spaceBefore;
  382. // height before the main baseline
  383. int lineLead = lead;
  384. // maximum follow
  385. int lineFollow = follow;
  386. // true if this line contains only zero-height, auxiliary boxes
  387. // and the actual line width is 0; in this case, the line "collapses"
  388. // i.e. the line area will have bpd = 0
  389. boolean bZeroHeightLine = (difference == iLineWidth);
  390. // if line-stacking-strategy is "font-height", the line height
  391. // is not affected by its content
  392. if (fobj.getLineStackingStrategy() != EN_FONT_HEIGHT) {
  393. ListIterator inlineIterator
  394. = par.listIterator(firstElementIndex);
  395. AlignmentContext lastAC = null;
  396. int maxIgnoredHeight = 0; // See spec 7.13
  397. for (int j = firstElementIndex;
  398. j <= lastElementIndex;
  399. j++) {
  400. KnuthElement element = (KnuthElement) inlineIterator.next();
  401. if (element instanceof KnuthInlineBox ) {
  402. AlignmentContext ac = ((KnuthInlineBox) element).getAlignmentContext();
  403. if (ac != null && lastAC != ac) {
  404. if (!ac.usesInitialBaselineTable()
  405. || ac.getAlignmentBaselineIdentifier() != EN_BEFORE_EDGE
  406. && ac.getAlignmentBaselineIdentifier() != EN_AFTER_EDGE) {
  407. if (fobj.getLineHeightShiftAdjustment() == EN_CONSIDER_SHIFTS
  408. || ac.getBaselineShiftValue() == 0) {
  409. int alignmentOffset = ac.getTotalAlignmentBaselineOffset();
  410. if (alignmentOffset + ac.getAltitude() > lineLead) {
  411. lineLead = alignmentOffset + ac.getAltitude();
  412. }
  413. if (ac.getDepth() - alignmentOffset > lineFollow) {
  414. lineFollow = ac.getDepth() - alignmentOffset;
  415. }
  416. }
  417. } else {
  418. if (ac.getHeight() > maxIgnoredHeight) {
  419. maxIgnoredHeight = ac.getHeight();
  420. }
  421. }
  422. lastAC = ac;
  423. }
  424. if (bZeroHeightLine
  425. && (!element.isAuxiliary() || ac != null && ac.getHeight() > 0)) {
  426. bZeroHeightLine = false;
  427. }
  428. }
  429. }
  430. if (lineFollow < maxIgnoredHeight - lineLead) {
  431. lineFollow = maxIgnoredHeight - lineLead;
  432. }
  433. }
  434. constantLineHeight = lineLead + lineFollow;
  435. if (bZeroHeightLine) {
  436. return new LineBreakPosition(thisLLM,
  437. knuthParagraphs.indexOf(par),
  438. firstElementIndex, lastElementIndex,
  439. availableShrink, availableStretch,
  440. difference, ratio, 0, indent,
  441. 0, iLineWidth, 0, 0, 0);
  442. } else {
  443. return new LineBreakPosition(thisLLM,
  444. knuthParagraphs.indexOf(par),
  445. firstElementIndex, lastElementIndex,
  446. availableShrink, availableStretch,
  447. difference, ratio, 0, indent,
  448. lineLead + lineFollow,
  449. iLineWidth, spaceBefore, spaceAfter,
  450. lineLead);
  451. }
  452. }
  453. public int findBreakingPoints(Paragraph par, /*int lineWidth,*/
  454. double threshold, boolean force,
  455. int allowedBreaks) {
  456. return super.findBreakingPoints(par, /*lineWidth,*/
  457. threshold, force, allowedBreaks);
  458. }
  459. protected int filterActiveNodes() {
  460. KnuthNode bestActiveNode = null;
  461. if (pageAlignment == EN_JUSTIFY) {
  462. // leave all active nodes and find the optimum line number
  463. //log.debug("LBA.filterActiveNodes> " + activeNodeCount + " layouts");
  464. for (int i = startLine; i < endLine; i++) {
  465. for (KnuthNode node = getNode(i); node != null; node = node.next) {
  466. //log.debug(" + lines = " + node.line + " demerits = " + node.totalDemerits);
  467. bestActiveNode = compareNodes(bestActiveNode, node);
  468. }
  469. }
  470. // scan the node set once again and remove some nodes
  471. //log.debug("LBA.filterActiveList> layout selection");
  472. for (int i = startLine; i < endLine; i++) {
  473. for (KnuthNode node = getNode(i); node != null; node = node.next) {
  474. //if (Math.abs(node.line - bestActiveNode.line) > maxDiff) {
  475. //if (false) {
  476. if (node.line != bestActiveNode.line
  477. && node.totalDemerits > MAX_DEMERITS) {
  478. //log.debug(" XXX lines = " + node.line + " demerits = " + node.totalDemerits);
  479. removeNode(i, node);
  480. } else {
  481. //log.debug(" ok lines = " + node.line + " demerits = " + node.totalDemerits);
  482. }
  483. }
  484. }
  485. } else {
  486. // leave only the active node with fewest total demerits
  487. for (int i = startLine; i < endLine; i++) {
  488. for (KnuthNode node = getNode(i); node != null; node = node.next) {
  489. bestActiveNode = compareNodes(bestActiveNode, node);
  490. if (node != bestActiveNode) {
  491. removeNode(i, node);
  492. }
  493. }
  494. }
  495. }
  496. return bestActiveNode.line;
  497. }
  498. }
  499. private int constantLineHeight = 12000;
  500. /**
  501. * Create a new Line Layout Manager.
  502. * This is used by the block layout manager to create
  503. * line managers for handling inline areas flowing into line areas.
  504. * @param block the block formatting object
  505. * @param lh the default line height
  506. * @param l the default lead, from top to baseline
  507. * @param f the default follow, from baseline to bottom
  508. */
  509. public LineLayoutManager(Block block, Length lh, int l, int f) {
  510. super(block);
  511. fobj = block;
  512. // the child FObj are owned by the parent BlockLM
  513. // this LM has all its childLMs preloaded
  514. fobjIter = null;
  515. lineHeight = lh;
  516. lead = l;
  517. follow = f;
  518. }
  519. /** {@inheritDoc} */
  520. public LinkedList getNextKnuthElements(LayoutContext context, int alignment) {
  521. FontInfo fi = fobj.getFOEventHandler().getFontInfo();
  522. FontTriplet[] fontkeys = fobj.getCommonFont().getFontState(fi);
  523. Font fs = fi.getFontInstance(fontkeys[0], fobj.getCommonFont().fontSize.getValue(this));
  524. alignmentContext
  525. = new AlignmentContext(fs, lineHeight.getValue(this), context.getWritingMode());
  526. context.setAlignmentContext(alignmentContext);
  527. // Get a break from currently active child LM
  528. // Set up constraints for inline level managers
  529. // IPD remaining in line
  530. MinOptMax availIPD = context.getStackLimit();
  531. clearPrevIPD();
  532. //PHASE 1: Create Knuth elements
  533. if (knuthParagraphs == null) {
  534. // it's the first time this method is called
  535. knuthParagraphs = new ArrayList();
  536. // here starts Knuth's algorithm
  537. collectInlineKnuthElements(context);
  538. } else {
  539. // this method has been called before
  540. // all line breaks are already calculated
  541. }
  542. // return finished when there's no content
  543. if (knuthParagraphs.size() == 0) {
  544. setFinished(true);
  545. return null;
  546. }
  547. //PHASE 2: Create line breaks
  548. return createLineBreaks(context.getBPAlignment(), context);
  549. /*
  550. LineBreakPosition lbp = null;
  551. if (breakpoints == null) {
  552. // find the optimal line breaking points for each paragraph
  553. breakpoints = new ArrayList();
  554. ListIterator paragraphsIterator
  555. = knuthParagraphs.listIterator(knuthParagraphs.size());
  556. Paragraph currPar = null;
  557. while (paragraphsIterator.hasPrevious()) {
  558. currPar = (Paragraph) paragraphsIterator.previous();
  559. findBreakingPoints(currPar, context.getStackLimit().opt);
  560. }
  561. }*/
  562. //PHASE 3: Return lines
  563. /*
  564. // get a break point from the list
  565. lbp = (LineBreakPosition) breakpoints.get(iReturnedLBP ++);
  566. if (iReturnedLBP == breakpoints.size()) {
  567. setFinished(true);
  568. }
  569. BreakPoss curLineBP = new BreakPoss(lbp);
  570. curLineBP.setFlag(BreakPoss.ISLAST, isFinished());
  571. curLineBP.setStackingSize(new MinOptMax(lbp.lineHeight));
  572. return curLineBP;
  573. */
  574. }
  575. /**
  576. * Phase 1 of Knuth algorithm: Collect all inline Knuth elements before determining line breaks.
  577. * @param context the LayoutContext
  578. */
  579. private void collectInlineKnuthElements(LayoutContext context) {
  580. LayoutContext inlineLC = new LayoutContext(context);
  581. InlineLevelLayoutManager curLM;
  582. LinkedList returnedList = null;
  583. iLineWidth = context.getStackLimit().opt;
  584. // convert all the text in a sequence of paragraphs made
  585. // of KnuthBox, KnuthGlue and KnuthPenalty objects
  586. boolean bPrevWasKnuthBox = false;
  587. StringBuffer trace = new StringBuffer("LineLM:");
  588. Paragraph lastPar = null;
  589. while ((curLM = (InlineLevelLayoutManager) getChildLM()) != null) {
  590. returnedList = curLM.getNextKnuthElements(inlineLC, effectiveAlignment);
  591. if (returnedList == null
  592. || returnedList.size() == 0) {
  593. /* curLM.getNextKnuthElements() returned null or an empty list;
  594. * this can happen if there is nothing more to layout,
  595. * so just iterate once more to see if there are other children */
  596. continue;
  597. }
  598. if (lastPar != null) {
  599. KnuthSequence firstSeq = (KnuthSequence) returnedList.getFirst();
  600. // finish last paragraph before a new block sequence
  601. if (!firstSeq.isInlineSequence()) {
  602. lastPar.endParagraph();
  603. ElementListObserver.observe(lastPar, "line", null);
  604. lastPar = null;
  605. if (log.isTraceEnabled()) {
  606. trace.append(" ]");
  607. }
  608. bPrevWasKnuthBox = false;
  609. }
  610. // does the first element of the first paragraph add to an existing word?
  611. if (lastPar != null) {
  612. KnuthElement thisElement;
  613. thisElement = (KnuthElement) firstSeq.get(0);
  614. if (thisElement.isBox() && !thisElement.isAuxiliary()
  615. && bPrevWasKnuthBox) {
  616. lastPar.addALetterSpace();
  617. }
  618. }
  619. }
  620. // loop over the KnuthSequences (and single KnuthElements) in returnedList
  621. ListIterator iter = returnedList.listIterator();
  622. while (iter.hasNext()) {
  623. KnuthSequence sequence = (KnuthSequence) iter.next();
  624. // the sequence contains inline Knuth elements
  625. if (sequence.isInlineSequence()) {
  626. // look at the last element
  627. ListElement lastElement = sequence.getLast();
  628. if (lastElement == null) {
  629. throw new NullPointerException(
  630. "Sequence was empty! lastElement is null");
  631. }
  632. bPrevWasKnuthBox = lastElement.isBox()
  633. && !((KnuthElement) lastElement).isAuxiliary()
  634. && ((KnuthElement) lastElement).getW() != 0;
  635. // if last paragraph is open, add the new elements to the paragraph
  636. // else this is the last paragraph
  637. if (lastPar == null) {
  638. lastPar = new Paragraph(this,
  639. textAlignment, textAlignmentLast,
  640. textIndent.getValue(this),
  641. lastLineEndIndent.getValue(this));
  642. lastPar.startSequence();
  643. if (log.isTraceEnabled()) {
  644. trace.append(" [");
  645. }
  646. } else {
  647. if (log.isTraceEnabled()) {
  648. trace.append(" +");
  649. }
  650. }
  651. lastPar.addAll(sequence);
  652. if (log.isTraceEnabled()) {
  653. trace.append(" I");
  654. }
  655. // finish last paragraph if it was closed with a linefeed
  656. if (lastElement.isPenalty()
  657. && ((KnuthPenalty) lastElement).getP()
  658. == -KnuthPenalty.INFINITE) {
  659. // a penalty item whose value is -inf
  660. // represents a preserved linefeed,
  661. // which forces a line break
  662. lastPar.removeLast();
  663. if (!lastPar.containsBox()) {
  664. //only a forced linefeed on this line
  665. //-> compensate with an auxiliary glue
  666. lastPar.add(new KnuthGlue(iLineWidth, 0, iLineWidth, null, true));
  667. }
  668. lastPar.endParagraph();
  669. ElementListObserver.observe(lastPar, "line", null);
  670. lastPar = null;
  671. if (log.isTraceEnabled()) {
  672. trace.append(" ]");
  673. }
  674. bPrevWasKnuthBox = false;
  675. }
  676. } else { // the sequence is a block sequence
  677. // the positions will be wrapped with this LM in postProcessLineBreaks
  678. knuthParagraphs.add(sequence);
  679. if (log.isTraceEnabled()) {
  680. trace.append(" B");
  681. }
  682. }
  683. } // end of loop over returnedList
  684. }
  685. if (lastPar != null) {
  686. lastPar.endParagraph();
  687. ElementListObserver.observe(lastPar, "line", fobj.getId());
  688. if (log.isTraceEnabled()) {
  689. trace.append(" ]");
  690. }
  691. }
  692. log.trace(trace);
  693. }
  694. /**
  695. * Find a set of breaking points.
  696. * This method is called only once by getNextBreakPoss, and it
  697. * subsequently calls the other findBreakingPoints() method with
  698. * different parameters, until a set of breaking points is found.
  699. *
  700. * @param par the list of elements that must be parted
  701. * into lines
  702. * @param lineWidth the desired length ot the lines
  703. */
  704. /*
  705. private void findBreakingPoints(Paragraph par, int lineWidth) {
  706. // maximum adjustment ratio permitted
  707. float maxAdjustment = 1;
  708. // first try
  709. if (!findBreakingPoints(par, lineWidth, maxAdjustment, false)) {
  710. // the first try failed, now try something different
  711. log.debug("No set of breaking points found with maxAdjustment = " + maxAdjustment);
  712. if (hyphenationProperties.hyphenate == Constants.EN_TRUE) {
  713. // consider every hyphenation point as a legal break
  714. findHyphenationPoints(par);
  715. } else {
  716. // try with a higher threshold
  717. maxAdjustment = 5;
  718. }
  719. if (!findBreakingPoints(par, lineWidth, maxAdjustment, false)) {
  720. // the second try failed too, try with a huge threshold;
  721. // if this fails too, use a different algorithm
  722. log.debug("No set of breaking points found with maxAdjustment = " + maxAdjustment
  723. + (hyphenationProperties.hyphenate == Constants.EN_TRUE ? " and hyphenation" : ""));
  724. maxAdjustment = 20;
  725. if (!findBreakingPoints(par, lineWidth, maxAdjustment, true)) {
  726. log.debug("No set of breaking points found, using first-fit algorithm");
  727. }
  728. }
  729. }
  730. }
  731. private boolean findBreakingPoints(Paragraph par, int lineWidth,
  732. double threshold, boolean force) {
  733. KnuthParagraph knuthPara = new KnuthParagraph(par);
  734. int lines = knuthPara.findBreakPoints(lineWidth, threshold, force);
  735. if (lines == 0) {
  736. return false;
  737. }
  738. for (int i = lines-1; i >= 0; i--) {
  739. int line = i+1;
  740. if (log.isTraceEnabled()) {
  741. log.trace("Making line from " + knuthPara.getStart(i) + " to " +
  742. knuthPara.getEnd(i));
  743. }
  744. // compute indent and adjustment ratio, according to
  745. // the value of text-align and text-align-last
  746. int difference = knuthPara.getDifference(i);
  747. if (line == lines) {
  748. difference += par.lineFillerWidth;
  749. }
  750. int textAlign = (line < lines)
  751. ? textAlignment : textAlignmentLast;
  752. int indent = (textAlign == EN_CENTER)
  753. ? difference / 2
  754. : (textAlign == EN_END) ? difference : 0;
  755. indent += (line == 1 && knuthParagraphs.indexOf(par) == 0)
  756. ? textIndent.getValue(this) : 0;
  757. double ratio = (textAlign == EN_JUSTIFY)
  758. ? knuthPara.getAdjustRatio(i) : 0;
  759. int start = knuthPara.getStart(i);
  760. int end = knuthPara.getEnd(i);
  761. makeLineBreakPosition(par, start, end, 0, ratio, indent);
  762. }
  763. return true;
  764. }
  765. private void makeLineBreakPosition(Paragraph par,
  766. int firstElementIndex, int lastElementIndex,
  767. int insertIndex, double ratio, int indent) {
  768. // line height calculation
  769. int halfLeading = (lineHeight - lead - follow) / 2;
  770. // height above the main baseline
  771. int lineLead = lead + halfLeading;
  772. // maximum size of top and bottom alignment
  773. int lineFollow = follow + halfLeading;
  774. ListIterator inlineIterator
  775. = par.listIterator(firstElementIndex);
  776. for (int j = firstElementIndex;
  777. j <= lastElementIndex;
  778. j++) {
  779. KnuthElement element = (KnuthElement) inlineIterator.next();
  780. if (element.isBox()) {
  781. KnuthInlineBox box = (KnuthInlineBox)element;
  782. if (box.getLead() > lineLead) {
  783. lineLead = box.getLead();
  784. }
  785. if (box.getTotal() > lineFollow) {
  786. lineFollow = box.getTotal();
  787. }
  788. if (box.getMiddle() > lineLead + middleShift) {
  789. lineLead += box.getMiddle()
  790. - lineLead - middleShift;
  791. }
  792. if (box.getMiddle() > middlefollow - middleShift) {
  793. middlefollow += box.getMiddle()
  794. - middlefollow + middleShift;
  795. }
  796. }
  797. }
  798. if (lineFollow - lineLead > middlefollow) {
  799. middlefollow = lineFollow - lineLead;
  800. }
  801. breakpoints.add(insertIndex,
  802. new LineBreakPosition(this,
  803. knuthParagraphs.indexOf(par),
  804. lastElementIndex ,
  805. ratio, 0, indent,
  806. lineLead + middlefollow,
  807. lineLead));
  808. }*/
  809. /**
  810. * Phase 2 of Knuth algorithm: find optimal break points.
  811. * @param alignment alignment in BP direction of the paragraph
  812. * @param context the layout context
  813. * @return a list of Knuth elements representing broken lines
  814. */
  815. private LinkedList createLineBreaks(int alignment, LayoutContext context) {
  816. // find the optimal line breaking points for each paragraph
  817. ListIterator paragraphsIterator
  818. = knuthParagraphs.listIterator(knuthParagraphs.size());
  819. lineLayoutsList = new ArrayList(knuthParagraphs.size());
  820. LineLayoutPossibilities llPoss;
  821. while (paragraphsIterator.hasPrevious()) {
  822. KnuthSequence seq = (KnuthSequence) paragraphsIterator.previous();
  823. if (!seq.isInlineSequence()) {
  824. // This set of line layout possibilities does not matter;
  825. // we only need an entry in lineLayoutsList.
  826. llPoss = new LineLayoutPossibilities();
  827. } else {
  828. llPoss = findOptimalBreakingPoints(alignment, (Paragraph) seq);
  829. }
  830. lineLayoutsList.add(0, llPoss);
  831. }
  832. setFinished(true);
  833. //Post-process the line breaks found
  834. return postProcessLineBreaks(alignment, context);
  835. }
  836. /**
  837. * Fint the optimal linebreaks for a paragraph
  838. * @param alignment alignment of the paragraph
  839. * @param currPar the Paragraph for which the linebreaks are found
  840. * @return the line layout possibilities for the paragraph
  841. */
  842. private LineLayoutPossibilities findOptimalBreakingPoints(int alignment, Paragraph currPar) {
  843. // use the member lineLayouts, which is read by LineBreakingAlgorithm.updateData1 and 2
  844. lineLayouts = new LineLayoutPossibilities();
  845. double maxAdjustment = 1;
  846. int iBPcount = 0;
  847. LineBreakingAlgorithm alg = new LineBreakingAlgorithm(alignment,
  848. textAlignment, textAlignmentLast,
  849. textIndent.getValue(this), currPar.lineFiller.opt,
  850. lineHeight.getValue(this), lead, follow,
  851. (knuthParagraphs.indexOf(currPar) == 0),
  852. hyphenationLadderCount.getEnum() == EN_NO_LIMIT
  853. ? 0 : hyphenationLadderCount.getValue(),
  854. this);
  855. if (hyphenationProperties.hyphenate.getEnum() == EN_TRUE
  856. && fobj.getWrapOption() != EN_NO_WRAP) {
  857. findHyphenationPoints(currPar);
  858. }
  859. // first try
  860. int allowedBreaks;
  861. if (wrapOption == EN_NO_WRAP) {
  862. allowedBreaks = BreakingAlgorithm.ONLY_FORCED_BREAKS;
  863. } else {
  864. allowedBreaks = BreakingAlgorithm.NO_FLAGGED_PENALTIES;
  865. }
  866. alg.setConstantLineWidth(iLineWidth);
  867. iBPcount = alg.findBreakingPoints(currPar,
  868. maxAdjustment, false, allowedBreaks);
  869. if (iBPcount == 0 || alignment == EN_JUSTIFY) {
  870. // if the first try found a set of breaking points, save them
  871. if (iBPcount > 0) {
  872. alg.resetAlgorithm();
  873. lineLayouts.savePossibilities(false);
  874. } else {
  875. // the first try failed
  876. log.debug("No set of breaking points found with maxAdjustment = " + maxAdjustment);
  877. }
  878. // now try something different
  879. log.debug("Hyphenation possible? " + (hyphenationProperties.hyphenate.getEnum() == EN_TRUE));
  880. if (hyphenationProperties.hyphenate.getEnum() == EN_TRUE
  881. && !(allowedBreaks == BreakingAlgorithm.ONLY_FORCED_BREAKS)) {
  882. // consider every hyphenation point as a legal break
  883. allowedBreaks = BreakingAlgorithm.ALL_BREAKS;
  884. } else {
  885. // try with a higher threshold
  886. maxAdjustment = 5;
  887. }
  888. if ((iBPcount
  889. = alg.findBreakingPoints(currPar,
  890. maxAdjustment, false, allowedBreaks)) == 0) {
  891. // the second try failed too, try with a huge threshold
  892. // and force the algorithm to find
  893. // a set of breaking points
  894. if (log.isDebugEnabled()) {
  895. log.debug("No set of breaking points found with maxAdjustment = "
  896. + maxAdjustment
  897. + (hyphenationProperties.hyphenate.getEnum() == EN_TRUE
  898. ? " and hyphenation" : ""));
  899. }
  900. maxAdjustment = 20;
  901. iBPcount
  902. = alg.findBreakingPoints(currPar,
  903. maxAdjustment, true, allowedBreaks);
  904. }
  905. // use non-hyphenated breaks, when possible
  906. lineLayouts.restorePossibilities();
  907. /* extension (not in the XSL FO recommendation): if vertical alignment
  908. is justify and the paragraph has only one layout, try using
  909. shorter or longer lines */
  910. //TODO This code snippet is disabled. Reenable?
  911. if (false && alignment == EN_JUSTIFY && textAlignment == EN_JUSTIFY) {
  912. //log.debug("LLM.getNextKnuthElements> layouts with more lines? " + lineLayouts.canUseMoreLines());
  913. //log.debug(" layouts with fewer lines? " + lineLayouts.canUseLessLines());
  914. if (!lineLayouts.canUseMoreLines()) {
  915. alg.resetAlgorithm();
  916. lineLayouts.savePossibilities(true);
  917. // try with shorter lines
  918. int savedLineWidth = iLineWidth;
  919. iLineWidth = (int) (iLineWidth * 0.95);
  920. iBPcount = alg.findBreakingPoints(currPar,
  921. maxAdjustment, true, allowedBreaks);
  922. // use normal lines, when possible
  923. lineLayouts.restorePossibilities();
  924. iLineWidth = savedLineWidth;
  925. }
  926. if (!lineLayouts.canUseLessLines()) {
  927. alg.resetAlgorithm();
  928. lineLayouts.savePossibilities(true);
  929. // try with longer lines
  930. int savedLineWidth = iLineWidth;
  931. iLineWidth = (int) (iLineWidth * 1.05);
  932. alg.setConstantLineWidth(iLineWidth);
  933. iBPcount = alg.findBreakingPoints(currPar,
  934. maxAdjustment, true, allowedBreaks);
  935. // use normal lines, when possible
  936. lineLayouts.restorePossibilities();
  937. iLineWidth = savedLineWidth;
  938. }
  939. //log.debug("LLM.getNextKnuthElements> now, layouts with more lines? " + lineLayouts.canUseMoreLines());
  940. //log.debug(" now, layouts with fewer lines? " + lineLayouts.canUseLessLines());
  941. }
  942. }
  943. return lineLayouts;
  944. }
  945. /**
  946. * Creates the element list in BP direction for the broken lines.
  947. * @param alignment the currently applicable vertical alignment
  948. * @param context the layout context
  949. * @return the newly built element list
  950. */
  951. private LinkedList postProcessLineBreaks(int alignment, LayoutContext context) {
  952. LinkedList returnList = new LinkedList();
  953. for (int p = 0; p < knuthParagraphs.size(); p++) {
  954. // null penalty between paragraphs
  955. if (p > 0 && !((BlockLevelLayoutManager) parentLM).mustKeepTogether()) {
  956. returnList.add(new BreakElement(
  957. new Position(this), 0, context));
  958. //returnList.add(new KnuthPenalty(0, 0, false, new Position(this), false));
  959. }
  960. LineLayoutPossibilities llPoss;
  961. llPoss = (LineLayoutPossibilities) lineLayoutsList.get(p);
  962. KnuthSequence seq = (KnuthSequence) knuthParagraphs.get(p);
  963. if (!seq.isInlineSequence()) {
  964. LinkedList targetList = new LinkedList();
  965. ListIterator listIter = seq.listIterator();
  966. while (listIter.hasNext()) {
  967. ListElement tempElement;
  968. tempElement = (ListElement) listIter.next();
  969. if (tempElement.getLayoutManager() != this) {
  970. tempElement.setPosition(notifyPos(new NonLeafPosition(this,
  971. tempElement.getPosition())));
  972. }
  973. targetList.add(tempElement);
  974. }
  975. returnList.addAll(targetList);
  976. } else if (seq.isInlineSequence() && alignment == EN_JUSTIFY) {
  977. /* justified vertical alignment (not in the XSL FO recommendation):
  978. create a multi-layout sequence whose elements will contain
  979. a conventional Position */
  980. Position returnPosition = new LeafPosition(this, p);
  981. createElements(returnList, llPoss, returnPosition);
  982. } else {
  983. /* "normal" vertical alignment: create a sequence whose boxes
  984. represent effective lines, and contain LineBreakPositions */
  985. Position returnPosition = new LeafPosition(this, p);
  986. int startIndex = 0;
  987. for (int i = 0;
  988. i < llPoss.getChosenLineCount();
  989. i++) {
  990. if (!((BlockLevelLayoutManager) parentLM).mustKeepTogether()
  991. && i >= fobj.getOrphans()
  992. && i <= llPoss.getChosenLineCount() - fobj.getWidows()
  993. && returnList.size() > 0) {
  994. // null penalty allowing a page break between lines
  995. returnList.add(new BreakElement(
  996. returnPosition, 0, context));
  997. //returnList.add(new KnuthPenalty(0, 0, false, returnPosition, false));
  998. }
  999. int endIndex
  1000. = ((LineBreakPosition) llPoss.getChosenPosition(i)).getLeafPos();
  1001. // create a list of the FootnoteBodyLM handling footnotes
  1002. // whose citations are in this line
  1003. LinkedList footnoteList = new LinkedList();
  1004. ListIterator elementIterator = seq.listIterator(startIndex);
  1005. while (elementIterator.nextIndex() <= endIndex) {
  1006. KnuthElement element = (KnuthElement) elementIterator.next();
  1007. if (element instanceof KnuthInlineBox
  1008. && ((KnuthInlineBox) element).isAnchor()) {
  1009. footnoteList.add(((KnuthInlineBox) element).getFootnoteBodyLM());
  1010. } else if (element instanceof KnuthBlockBox) {
  1011. footnoteList.addAll(((KnuthBlockBox) element).getFootnoteBodyLMs());
  1012. }
  1013. }
  1014. startIndex = endIndex + 1;
  1015. LineBreakPosition lbp
  1016. = (LineBreakPosition) llPoss.getChosenPosition(i);
  1017. returnList.add(new KnuthBlockBox
  1018. (lbp.lineHeight + lbp.spaceBefore + lbp.spaceAfter,
  1019. footnoteList, lbp, false));
  1020. /* // add stretch and shrink to the returnlist
  1021. if (!seq.isInlineSequence()
  1022. && lbp.availableStretch != 0 || lbp.availableShrink != 0) {
  1023. returnList.add(new KnuthPenalty(0, -KnuthElement.INFINITE,
  1024. false, new Position(this), false));
  1025. returnList.add(new KnuthGlue(0, lbp.availableStretch, lbp.availableShrink,
  1026. new Position(this), false));
  1027. }
  1028. */
  1029. }
  1030. }
  1031. }
  1032. return returnList;
  1033. }
  1034. private void createElements(List list, LineLayoutPossibilities llPoss,
  1035. Position elementPosition) {
  1036. /* number of normal, inner lines */
  1037. int nInnerLines = 0;
  1038. /* number of lines that can be used in order to fill more space */
  1039. int nOptionalLines = 0;
  1040. /* number of lines that can be used in order to fill more space
  1041. only if the paragraph is not parted */
  1042. int nConditionalOptionalLines = 0;
  1043. /* number of lines that can be omitted in order to fill less space */
  1044. int nEliminableLines = 0;
  1045. /* number of lines that can be omitted in order to fill less space
  1046. only if the paragraph is not parted */
  1047. int nConditionalEliminableLines = 0;
  1048. /* number of the first unbreakable lines */
  1049. int nFirstLines = fobj.getOrphans();
  1050. /* number of the last unbreakable lines */
  1051. int nLastLines = fobj.getWidows();
  1052. /* sub-sequence used to separate the elements representing different lines */
  1053. List breaker = new LinkedList();
  1054. /* comment out the next lines in order to test particular situations */
  1055. if (fobj.getOrphans() + fobj.getWidows() <= llPoss.getMinLineCount()) {
  1056. nInnerLines = llPoss.getMinLineCount()
  1057. - (fobj.getOrphans() + fobj.getWidows());
  1058. nOptionalLines = llPoss.getMaxLineCount()
  1059. - llPoss.getOptLineCount();
  1060. nEliminableLines = llPoss.getOptLineCount()
  1061. - llPoss.getMinLineCount();
  1062. } else if (fobj.getOrphans() + fobj.getWidows() <= llPoss.getOptLineCount()) {
  1063. nOptionalLines = llPoss.getMaxLineCount()
  1064. - llPoss.getOptLineCount();
  1065. nEliminableLines = llPoss.getOptLineCount()
  1066. - (fobj.getOrphans() + fobj.getWidows());
  1067. nConditionalEliminableLines = (fobj.getOrphans() + fobj.getWidows())
  1068. - llPoss.getMinLineCount();
  1069. } else if (fobj.getOrphans() + fobj.getWidows() <= llPoss.getMaxLineCount()) {
  1070. nOptionalLines = llPoss.getMaxLineCount()
  1071. - (fobj.getOrphans() + fobj.getWidows());
  1072. nConditionalOptionalLines = (fobj.getOrphans() + fobj.getWidows())
  1073. - llPoss.getOptLineCount();
  1074. nConditionalEliminableLines = llPoss.getOptLineCount()
  1075. - llPoss.getMinLineCount();
  1076. nFirstLines -= nConditionalOptionalLines;
  1077. } else {
  1078. nConditionalOptionalLines = llPoss.getMaxLineCount()
  1079. - llPoss.getOptLineCount();
  1080. nConditionalEliminableLines = llPoss.getOptLineCount()
  1081. - llPoss.getMinLineCount();
  1082. nFirstLines = llPoss.getOptLineCount();
  1083. nLastLines = 0;
  1084. }
  1085. /* comment out the previous lines in order to test particular situations */
  1086. /* use these lines to test particular situations
  1087. nInnerLines = 0;
  1088. nOptionalLines = 1;
  1089. nConditionalOptionalLines = 2;
  1090. nEliminableLines = 0;
  1091. nConditionalEliminableLines = 0;
  1092. nFirstLines = 1;
  1093. nLastLines = 3;
  1094. */
  1095. if (nLastLines != 0
  1096. && (nConditionalOptionalLines > 0 || nConditionalEliminableLines > 0)) {
  1097. breaker.add(new KnuthPenalty(0, KnuthElement.INFINITE, false, elementPosition, false));
  1098. breaker.add(new KnuthGlue(0, -nConditionalOptionalLines * constantLineHeight,
  1099. -nConditionalEliminableLines * constantLineHeight,
  1100. LINE_NUMBER_ADJUSTMENT, elementPosition, false));
  1101. breaker.add(new KnuthPenalty(nConditionalOptionalLines * constantLineHeight,
  1102. 0, false, elementPosition, false));
  1103. breaker.add(new KnuthGlue(0, nConditionalOptionalLines * constantLineHeight,
  1104. nConditionalEliminableLines * constantLineHeight,
  1105. LINE_NUMBER_ADJUSTMENT, elementPosition, false));
  1106. } else if (nLastLines != 0) {
  1107. breaker.add(new KnuthPenalty(0, 0, false, elementPosition, false));
  1108. }
  1109. //log.debug("first=" + nFirstLines + " inner=" + nInnerLines
  1110. // + " optional=" + nOptionalLines + " eliminable=" + nEliminableLines
  1111. // + " last=" + nLastLines
  1112. // + " (condOpt=" + nConditionalOptionalLines + " condEl=" + nConditionalEliminableLines + ")");
  1113. // creation of the elements:
  1114. // first group of lines
  1115. list.add(new KnuthBox(nFirstLines * constantLineHeight, elementPosition,
  1116. (nLastLines == 0
  1117. && nConditionalOptionalLines == 0
  1118. && nConditionalEliminableLines == 0 ? true : false)));
  1119. if (nConditionalOptionalLines > 0
  1120. || nConditionalEliminableLines > 0) {
  1121. list.add(new KnuthPenalty(0, KnuthElement.INFINITE, false, elementPosition, false));
  1122. list.add(new KnuthGlue(0, nConditionalOptionalLines * constantLineHeight,
  1123. nConditionalEliminableLines * constantLineHeight,
  1124. LINE_NUMBER_ADJUSTMENT, elementPosition, false));
  1125. list.add(new KnuthBox(0, elementPosition,
  1126. (nLastLines == 0 ? true : false)));
  1127. }
  1128. // optional lines
  1129. for (int i = 0; i < nOptionalLines; i++) {
  1130. list.addAll(breaker);
  1131. list.add(new KnuthBox(0, elementPosition, false));
  1132. list.add(new KnuthPenalty(0, KnuthElement.INFINITE, false, elementPosition, false));
  1133. list.add(new KnuthGlue(0, 1 * constantLineHeight, 0,
  1134. LINE_NUMBER_ADJUSTMENT, elementPosition, false));
  1135. list.add(new KnuthBox(0, elementPosition, false));
  1136. }
  1137. // eliminable lines
  1138. for (int i = 0; i < nEliminableLines; i++) {
  1139. list.addAll(breaker);
  1140. list.add(new KnuthBox(1 * constantLineHeight, elementPosition, false));
  1141. list.add(new KnuthPenalty(0, KnuthElement.INFINITE, false, elementPosition, false));
  1142. list.add(new KnuthGlue(0, 0, 1 * constantLineHeight,
  1143. LINE_NUMBER_ADJUSTMENT, elementPosition, false));
  1144. list.add(new KnuthBox(0, elementPosition, false));
  1145. }
  1146. // inner lines
  1147. for (int i = 0; i < nInnerLines; i++) {
  1148. list.addAll(breaker);
  1149. list.add(new KnuthBox(1 * constantLineHeight, elementPosition, false));
  1150. }
  1151. // last group of lines
  1152. if (nLastLines > 0) {
  1153. list.addAll(breaker);
  1154. list.add(new KnuthBox(nLastLines * constantLineHeight,
  1155. elementPosition, true));
  1156. }
  1157. }
  1158. /**
  1159. * {@inheritDoc}
  1160. */
  1161. public boolean mustKeepTogether() {
  1162. return ((BlockLevelLayoutManager) getParent()).mustKeepTogether();
  1163. }
  1164. /**
  1165. * {@inheritDoc}
  1166. */
  1167. public boolean mustKeepWithPrevious() {
  1168. return false;
  1169. }
  1170. /**
  1171. * {@inheritDoc}
  1172. */
  1173. public boolean mustKeepWithNext() {
  1174. return false;
  1175. }
  1176. /**
  1177. * {@inheritDoc}
  1178. */
  1179. public int negotiateBPDAdjustment(int adj, KnuthElement lastElement) {
  1180. LeafPosition pos = (LeafPosition)lastElement.getPosition();
  1181. int totalAdj = adj;
  1182. //if (lastElement.isPenalty()) {
  1183. // totalAdj += lastElement.getW();
  1184. //}
  1185. //int lineNumberDifference = (int)((double) totalAdj / constantLineHeight);
  1186. int lineNumberDifference = (int) Math.round((double) totalAdj / constantLineHeight
  1187. + (adj > 0 ? - 0.4 : 0.4));
  1188. //log.debug(" LLM> variazione calcolata = " + ((double) totalAdj / constantLineHeight) + " variazione applicata = " + lineNumberDifference);
  1189. LineLayoutPossibilities llPoss;
  1190. llPoss = (LineLayoutPossibilities) lineLayoutsList.get(pos.getLeafPos());
  1191. lineNumberDifference = llPoss.applyLineCountAdjustment(lineNumberDifference);
  1192. return lineNumberDifference * constantLineHeight;
  1193. }
  1194. /**
  1195. * {@inheritDoc}
  1196. */
  1197. public void discardSpace(KnuthGlue spaceGlue) {
  1198. }
  1199. /**
  1200. * {@inheritDoc}
  1201. */
  1202. public LinkedList getChangedKnuthElements(List oldList, int alignment) {
  1203. LinkedList returnList = new LinkedList();
  1204. for (int p = 0; p < knuthParagraphs.size(); p++) {
  1205. LineLayoutPossibilities llPoss;
  1206. llPoss = (LineLayoutPossibilities)lineLayoutsList.get(p);
  1207. //log.debug("demerits of the chosen layout: " + llPoss.getChosenDemerits());
  1208. for (int i = 0; i < llPoss.getChosenLineCount(); i++) {
  1209. if (!((BlockLevelLayoutManager) parentLM).mustKeepTogether()
  1210. && i >= fobj.getOrphans()
  1211. && i <= llPoss.getChosenLineCount() - fobj.getWidows()) {
  1212. // null penalty allowing a page break between lines
  1213. returnList.add(new KnuthPenalty(0, 0, false, new Position(this), false));
  1214. }
  1215. LineBreakPosition lbp = (LineBreakPosition) llPoss.getChosenPosition(i);
  1216. //log.debug("LLM.getChangedKnuthElements> lineWidth= " + lbp.lineWidth + " difference= " + lbp.difference);
  1217. //log.debug(" shrink= " + lbp.availableShrink + " stretch= " + lbp.availableStretch);
  1218. //log.debug("linewidth= " + lbp.lineWidth + " difference= " + lbp.difference + " indent= " + lbp.startIndent);
  1219. MinOptMax contentIPD;
  1220. if (alignment == EN_JUSTIFY) {
  1221. contentIPD = new MinOptMax(
  1222. lbp.lineWidth - lbp.difference - lbp.availableShrink,
  1223. lbp.lineWidth - lbp.difference,
  1224. lbp.lineWidth - lbp.difference + lbp.availableStretch);
  1225. } else if (alignment == EN_CENTER) {
  1226. contentIPD = new MinOptMax(lbp.lineWidth - 2 * lbp.startIndent);
  1227. } else if (alignment == EN_END) {
  1228. contentIPD = new MinOptMax(lbp.lineWidth - lbp.startIndent);
  1229. } else {
  1230. contentIPD = new MinOptMax(lbp.lineWidth - lbp.difference + lbp.startIndent);
  1231. }
  1232. returnList.add(new KnuthBlockBox(lbp.lineHeight,
  1233. contentIPD,
  1234. (lbp.ipdAdjust != 0
  1235. ? lbp.lineWidth - lbp.difference : 0),
  1236. lbp, false));
  1237. }
  1238. }
  1239. return returnList;
  1240. }
  1241. /**
  1242. * find hyphenation points for every word int the current paragraph
  1243. * @ param currPar the paragraph whose words will be hyphenated
  1244. */
  1245. private void findHyphenationPoints(Paragraph currPar) {
  1246. // hyphenate every word
  1247. ListIterator currParIterator
  1248. = currPar.listIterator(currPar.ignoreAtStart);
  1249. // list of TLM involved in hyphenation
  1250. LinkedList updateList = new LinkedList();
  1251. KnuthElement firstElement = null;
  1252. KnuthElement nextElement = null;
  1253. // current InlineLevelLayoutManager
  1254. InlineLevelLayoutManager currLM = null;
  1255. // number of KnuthBox elements containing word fragments
  1256. int boxCount;
  1257. // number of auxiliary KnuthElements between KnuthBoxes
  1258. int auxCount;
  1259. StringBuffer sbChars = null;
  1260. // find all hyphenation points
  1261. while (currParIterator.hasNext()) {
  1262. firstElement = (KnuthElement) currParIterator.next();
  1263. //
  1264. if (firstElement.getLayoutManager() != currLM) {
  1265. currLM = (InlineLevelLayoutManager) firstElement.getLayoutManager();
  1266. if (currLM != null) {
  1267. updateList.add(new Update(currLM, currParIterator.previousIndex()));
  1268. } else {
  1269. break;
  1270. }
  1271. } else if (currLM == null) {
  1272. break;
  1273. }
  1274. //TODO Something's not right here. See block_hyphenation_linefeed_preserve.xml
  1275. // collect word fragments, ignoring auxiliary elements;
  1276. // each word fragment was created by a different TextLM
  1277. if (firstElement.isBox() && !firstElement.isAuxiliary()) {
  1278. boxCount = 1;
  1279. auxCount = 0;
  1280. sbChars = new StringBuffer();
  1281. currLM.getWordChars(sbChars, firstElement.getPosition());
  1282. // look if next elements are boxes too
  1283. while (currParIterator.hasNext()) {
  1284. nextElement = (KnuthElement) currParIterator.next();
  1285. if (nextElement.isBox() && !nextElement.isAuxiliary()) {
  1286. // a non-auxiliary KnuthBox: append word chars
  1287. if (currLM != nextElement.getLayoutManager()) {
  1288. currLM = (InlineLevelLayoutManager) nextElement.getLayoutManager();
  1289. updateList.add(new Update(currLM, currParIterator.previousIndex()));
  1290. }
  1291. // append text to recreate the whole word
  1292. boxCount++;
  1293. currLM.getWordChars(sbChars, nextElement.getPosition());
  1294. } else if (!nextElement.isAuxiliary()) {
  1295. // a non-auxiliary non-box KnuthElement: stop
  1296. // go back to the last box or auxiliary element
  1297. currParIterator.previous();
  1298. break;
  1299. } else {
  1300. if (currLM != nextElement.getLayoutManager()) {
  1301. currLM = (InlineLevelLayoutManager) nextElement.getLayoutManager();
  1302. updateList.add(new Update(currLM, currParIterator.previousIndex()));
  1303. }
  1304. // an auxiliary KnuthElement: simply ignore it
  1305. auxCount++;
  1306. }
  1307. }
  1308. if (log.isTraceEnabled()) {
  1309. log.trace(" Word to hyphenate: " + sbChars.toString());
  1310. }
  1311. // find hyphenation points
  1312. HyphContext hc = getHyphenContext(sbChars);
  1313. // ask each LM to hyphenate its word fragment
  1314. if (hc != null) {
  1315. KnuthElement element = null;
  1316. for (int i = 0; i < (boxCount + auxCount); i++) {
  1317. currParIterator.previous();
  1318. }
  1319. for (int i = 0; i < (boxCount + auxCount); i++) {
  1320. element = (KnuthElement) currParIterator.next();
  1321. if (element.isBox() && !element.isAuxiliary()) {
  1322. ((InlineLevelLayoutManager)
  1323. element.getLayoutManager()).hyphenate(element.getPosition(), hc);
  1324. } else {
  1325. // nothing to do, element is an auxiliary KnuthElement
  1326. }
  1327. }
  1328. }
  1329. }
  1330. }
  1331. // create iterator for the updateList
  1332. ListIterator updateListIterator = updateList.listIterator();
  1333. Update currUpdate = null;
  1334. //int iPreservedElements = 0;
  1335. int iAddedElements = 0;
  1336. //int iRemovedElements = 0;
  1337. while (updateListIterator.hasNext()) {
  1338. // ask the LMs to apply the changes and return
  1339. // the new KnuthElements to replace the old ones
  1340. currUpdate = (Update) updateListIterator.next();
  1341. int fromIndex = currUpdate.iFirstIndex;
  1342. int toIndex;
  1343. if (updateListIterator.hasNext()) {
  1344. Update nextUpdate = (Update) updateListIterator.next();
  1345. toIndex = nextUpdate.iFirstIndex;
  1346. updateListIterator.previous();
  1347. } else {
  1348. // maybe this is not always correct!
  1349. toIndex = currPar.size() - currPar.ignoreAtEnd
  1350. - iAddedElements;
  1351. }
  1352. // applyChanges() returns true if the LM modifies its data,
  1353. // so it must return new KnuthElements to replace the old ones
  1354. if (((InlineLevelLayoutManager) currUpdate.inlineLM)
  1355. .applyChanges(currPar.subList(fromIndex + iAddedElements,
  1356. toIndex + iAddedElements))) {
  1357. // insert the new KnuthElements
  1358. LinkedList newElements = null;
  1359. newElements
  1360. = currUpdate.inlineLM.getChangedKnuthElements
  1361. (currPar.subList(fromIndex + iAddedElements,
  1362. toIndex + iAddedElements),
  1363. /*flaggedPenalty,*/ effectiveAlignment);
  1364. // remove the old elements
  1365. currPar.subList(fromIndex + iAddedElements,
  1366. toIndex + iAddedElements).clear();
  1367. // insert the new elements
  1368. currPar.addAll(fromIndex + iAddedElements, newElements);
  1369. iAddedElements += newElements.size() - (toIndex - fromIndex);
  1370. }
  1371. }
  1372. updateListIterator = null;
  1373. updateList.clear();
  1374. }
  1375. /**
  1376. * Line area is always considered to act as a fence.
  1377. * @param isNotFirst ignored
  1378. * @return always true
  1379. */
  1380. protected boolean hasLeadingFence(boolean isNotFirst) {
  1381. return true;
  1382. }
  1383. /**
  1384. * Line area is always considered to act as a fence.
  1385. * @param isNotLast ignored
  1386. * @return always true
  1387. */
  1388. protected boolean hasTrailingFence(boolean isNotLast) {
  1389. return true;
  1390. }
  1391. private HyphContext getHyphenContext(StringBuffer sbChars) {
  1392. // Find all hyphenation points in this word
  1393. // (get in an array of offsets)
  1394. // hyphenationProperties are from the block level?.
  1395. // Note that according to the spec,
  1396. // they also "apply to" fo:character.
  1397. // I don't know what that means, since
  1398. // if we change language in the middle of a "word",
  1399. // the effect would seem quite strange!
  1400. // Or perhaps in that case, we say that it's several words.
  1401. // We probably should bring the hyphenation props up from the actual
  1402. // TextLM which generate the hyphenation buffer,
  1403. // since these properties inherit and could be specified
  1404. // on an inline or wrapper below the block level.
  1405. Hyphenation hyph
  1406. = Hyphenator.hyphenate(hyphenationProperties.language.getString(),
  1407. hyphenationProperties.country.getString(),
  1408. getFObj().getUserAgent().getFactory().getHyphenationTreeResolver(),
  1409. sbChars.toString(),
  1410. hyphenationProperties.hyphenationRemainCharacterCount.getValue(),
  1411. hyphenationProperties.hyphenationPushCharacterCount.getValue());
  1412. // They hyph structure contains the information we need
  1413. // Now start from prev: reset to that position, ask that LM to get
  1414. // a Position for the first hyphenation offset. If the offset isn't in
  1415. // its characters, it returns null,
  1416. // but must tell how many chars it had.
  1417. // Keep looking at currentBP using next hyphenation point until the
  1418. // returned size is greater than the available size
  1419. // or no more hyphenation points remain. Choose the best break.
  1420. if (hyph != null) {
  1421. return new HyphContext(hyph.getHyphenationPoints());
  1422. } else {
  1423. return null;
  1424. }
  1425. }
  1426. /**
  1427. * Add the areas with the break points.
  1428. *
  1429. * @param parentIter the iterator of break positions
  1430. * @param context the context for adding areas
  1431. */
  1432. public void addAreas(PositionIterator parentIter,
  1433. LayoutContext context) {
  1434. while (parentIter.hasNext()) {
  1435. Position pos = (Position) parentIter.next();
  1436. boolean isLastPosition = !parentIter.hasNext();
  1437. if (pos instanceof LineBreakPosition) {
  1438. addInlineArea(context, pos, isLastPosition);
  1439. } else if ((pos instanceof NonLeafPosition) && pos.generatesAreas()) {
  1440. addBlockArea(context, pos, isLastPosition);
  1441. } else {
  1442. /*
  1443. * pos was the Position inside a penalty item, nothing to do;
  1444. * or Pos does not generate an area,
  1445. * i.e. it stand for spaces, borders and padding.
  1446. */
  1447. }
  1448. }
  1449. setCurrentArea(null); // ?? necessary
  1450. }
  1451. /**
  1452. * Add a line with inline content
  1453. * @param context the context for adding areas
  1454. * @param pos the position for which the line is generated
  1455. * @param isLastPosition true if this is the last position of this LM
  1456. */
  1457. private void addInlineArea(LayoutContext context, Position pos, boolean isLastPosition) {
  1458. ListIterator seqIterator = null;
  1459. KnuthElement tempElement = null;
  1460. // the TLM which created the last KnuthElement in this line
  1461. LayoutManager lastLM = null;
  1462. LineBreakPosition lbp = (LineBreakPosition) pos;
  1463. int iCurrParIndex;
  1464. iCurrParIndex = lbp.iParIndex;
  1465. KnuthSequence seq = (KnuthSequence) knuthParagraphs.get(iCurrParIndex);
  1466. int iStartElement = lbp.iStartIndex;
  1467. int iEndElement = lbp.getLeafPos();
  1468. LineArea lineArea
  1469. = new LineArea((lbp.getLeafPos() < seq.size() - 1
  1470. ? textAlignment : textAlignmentLast),
  1471. lbp.difference, lbp.availableStretch, lbp.availableShrink);
  1472. if (lbp.startIndent != 0) {
  1473. lineArea.addTrait(Trait.START_INDENT, new Integer(lbp.startIndent));
  1474. }
  1475. lineArea.setBPD(lbp.lineHeight);
  1476. lineArea.setIPD(lbp.lineWidth);
  1477. lineArea.addTrait(Trait.SPACE_BEFORE, new Integer(lbp.spaceBefore));
  1478. lineArea.addTrait(Trait.SPACE_AFTER, new Integer(lbp.spaceAfter));
  1479. alignmentContext.resizeLine(lbp.lineHeight, lbp.baseline);
  1480. if (seq instanceof Paragraph) {
  1481. Paragraph currPar = (Paragraph) seq;
  1482. // ignore the first elements added by the LineLayoutManager
  1483. iStartElement += (iStartElement == 0) ? currPar.ignoreAtStart : 0;
  1484. // if this is the last line area that for this paragraph,
  1485. // ignore the last elements added by the LineLayoutManager and
  1486. // subtract the last-line-end-indent from the area ipd
  1487. if (iEndElement == (currPar.size() - 1)) {
  1488. iEndElement -= currPar.ignoreAtEnd;
  1489. lineArea.setIPD(lineArea.getIPD() - lastLineEndIndent.getValue(this));
  1490. }
  1491. }
  1492. // Remove trailing spaces if allowed so
  1493. if (whiteSpaceTreament == EN_IGNORE_IF_SURROUNDING_LINEFEED
  1494. || whiteSpaceTreament == EN_IGNORE
  1495. || whiteSpaceTreament == EN_IGNORE_IF_BEFORE_LINEFEED) {
  1496. // ignore the last element in the line if it is a KnuthGlue object
  1497. seqIterator = seq.listIterator(iEndElement);
  1498. tempElement = (KnuthElement) seqIterator.next();
  1499. if (tempElement.isGlue()) {
  1500. iEndElement--;
  1501. // this returns the same KnuthElement
  1502. seqIterator.previous();
  1503. if (seqIterator.hasPrevious()) {
  1504. tempElement = (KnuthElement) seqIterator.previous();
  1505. } else {
  1506. tempElement = null;
  1507. }
  1508. }
  1509. if (tempElement != null) {
  1510. lastLM = tempElement.getLayoutManager();
  1511. }
  1512. }
  1513. // Remove leading spaces if allowed so
  1514. if (whiteSpaceTreament == EN_IGNORE_IF_SURROUNDING_LINEFEED
  1515. || whiteSpaceTreament == EN_IGNORE
  1516. || whiteSpaceTreament == EN_IGNORE_IF_AFTER_LINEFEED) {
  1517. // ignore KnuthGlue and KnuthPenalty objects
  1518. // at the beginning of the line
  1519. seqIterator = seq.listIterator(iStartElement);
  1520. tempElement = (KnuthElement) seqIterator.next();
  1521. while (!tempElement.isBox() && seqIterator.hasNext()) {
  1522. tempElement = (KnuthElement) seqIterator.next();
  1523. iStartElement++;
  1524. }
  1525. }
  1526. // Add the inline areas to lineArea
  1527. PositionIterator inlinePosIter
  1528. = new KnuthPossPosIter(seq, iStartElement, iEndElement + 1);
  1529. iStartElement = lbp.getLeafPos() + 1;
  1530. if (iStartElement == seq.size()) {
  1531. // advance to next paragraph
  1532. iStartElement = 0;
  1533. }
  1534. LayoutContext lc = new LayoutContext(0);
  1535. lc.setAlignmentContext(alignmentContext);
  1536. lc.setSpaceAdjust(lbp.dAdjust);
  1537. lc.setIPDAdjust(lbp.ipdAdjust);
  1538. lc.setLeadingSpace(new SpaceSpecifier(true));
  1539. lc.setTrailingSpace(new SpaceSpecifier(false));
  1540. lc.setFlags(LayoutContext.RESOLVE_LEADING_SPACE, true);
  1541. /*
  1542. * extension (not in the XSL FO recommendation): if the left and right margins
  1543. * have been optimized, recompute indents and / or adjust ratio, according
  1544. * to the paragraph horizontal alignment
  1545. */
  1546. if (false && textAlignment == EN_JUSTIFY) {
  1547. // re-compute space adjust ratio
  1548. int updatedDifference = context.getStackLimit().opt
  1549. - lbp.lineWidth + lbp.difference;
  1550. double updatedRatio = 0.0;
  1551. if (updatedDifference > 0) {
  1552. updatedRatio = (float) updatedDifference / lbp.availableStretch;
  1553. } else if (updatedDifference < 0) {
  1554. updatedRatio = (float) updatedDifference / lbp.availableShrink;
  1555. }
  1556. lc.setIPDAdjust(updatedRatio);
  1557. //log.debug("LLM.addAreas> old difference = " + lbp.difference + " new difference = " + updatedDifference);
  1558. //log.debug(" old ratio = " + lbp.ipdAdjust + " new ratio = " + updatedRatio);
  1559. } else if (false && textAlignment == EN_CENTER) {
  1560. // re-compute indent
  1561. int updatedIndent = lbp.startIndent
  1562. + (context.getStackLimit().opt - lbp.lineWidth) / 2;
  1563. lineArea.addTrait(Trait.START_INDENT, new Integer(updatedIndent));
  1564. } else if (false && textAlignment == EN_END) {
  1565. // re-compute indent
  1566. int updatedIndent = lbp.startIndent
  1567. + (context.getStackLimit().opt - lbp.lineWidth);
  1568. lineArea.addTrait(Trait.START_INDENT, new Integer(updatedIndent));
  1569. }
  1570. setCurrentArea(lineArea);
  1571. setChildContext(lc);
  1572. LayoutManager childLM;
  1573. while ((childLM = inlinePosIter.getNextChildLM()) != null) {
  1574. lc.setFlags(LayoutContext.LAST_AREA, (childLM == lastLM));
  1575. childLM.addAreas(inlinePosIter, lc);
  1576. lc.setLeadingSpace(lc.getTrailingSpace());
  1577. lc.setTrailingSpace(new SpaceSpecifier(false));
  1578. }
  1579. // when can this be null?
  1580. // if display-align is distribute, add space after
  1581. if (context.getSpaceAfter() > 0
  1582. && (!context.isLastArea() || !isLastPosition)) {
  1583. lineArea.setBPD(lineArea.getBPD() + context.getSpaceAfter());
  1584. }
  1585. lineArea.finalise();
  1586. parentLM.addChildArea(lineArea);
  1587. }
  1588. /**
  1589. * Add a line with block content
  1590. * @param context the context for adding areas
  1591. * @param pos the position for which the line is generated
  1592. * @param isLastPosition true if this is the last position of this LM
  1593. */
  1594. private void addBlockArea(LayoutContext context, Position pos, boolean isLastPosition) {
  1595. /* Nested block-level content;
  1596. * go down the LM stack again;
  1597. * "unwrap" the positions and put the child positions in a new list.
  1598. * The positionList must contain one area-generating position,
  1599. * which creates one line area.
  1600. */
  1601. List positionList = new ArrayList(1);
  1602. Position innerPosition;
  1603. innerPosition = ((NonLeafPosition) pos).getPosition();
  1604. positionList.add(innerPosition);
  1605. // do we have the last LM?
  1606. LayoutManager lastLM = null;
  1607. if (isLastPosition) {
  1608. lastLM = innerPosition.getLM();
  1609. }
  1610. LineArea lineArea = new LineArea();
  1611. setCurrentArea(lineArea);
  1612. LayoutContext lc = new LayoutContext(0);
  1613. lc.setAlignmentContext(alignmentContext);
  1614. setChildContext(lc);
  1615. PositionIterator childPosIter = new StackingIter(positionList.listIterator());
  1616. LayoutContext blocklc = new LayoutContext(0);
  1617. blocklc.setLeadingSpace(new SpaceSpecifier(true));
  1618. blocklc.setTrailingSpace(new SpaceSpecifier(false));
  1619. blocklc.setFlags(LayoutContext.RESOLVE_LEADING_SPACE, true);
  1620. LayoutManager childLM;
  1621. while ((childLM = childPosIter.getNextChildLM()) != null) {
  1622. // set last area flag
  1623. blocklc.setFlags(LayoutContext.LAST_AREA,
  1624. (context.isLastArea() && childLM == lastLM));
  1625. blocklc.setStackLimit(context.getStackLimit());
  1626. // Add the line areas to Area
  1627. childLM.addAreas(childPosIter, blocklc);
  1628. blocklc.setLeadingSpace(blocklc.getTrailingSpace());
  1629. blocklc.setTrailingSpace(new SpaceSpecifier(false));
  1630. }
  1631. lineArea.updateExtentsFromChildren();
  1632. parentLM.addChildArea(lineArea);
  1633. }
  1634. /**
  1635. * {@inheritDoc}
  1636. */
  1637. public void addChildArea(Area childArea) {
  1638. // Make sure childArea is inline area
  1639. if (childArea instanceof InlineArea) {
  1640. Area parent = getCurrentArea();
  1641. if (getContext().resolveLeadingSpace()) {
  1642. addSpace(parent,
  1643. getContext().getLeadingSpace().resolve(false),
  1644. getContext().getSpaceAdjust());
  1645. }
  1646. parent.addChildArea(childArea);
  1647. }
  1648. }
  1649. // --------- Property Resolution related functions --------- //
  1650. /**
  1651. * {@inheritDoc}
  1652. */
  1653. public boolean getGeneratesBlockArea() {
  1654. return true;
  1655. }
  1656. /**
  1657. * {@inheritDoc}
  1658. */
  1659. public boolean getGeneratesLineArea() {
  1660. return true;
  1661. }
  1662. }