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.

LineArea.java 40KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061
  1. /*-- $Id$ --
  2. ============================================================================
  3. The Apache Software License, Version 1.1
  4. ============================================================================
  5. Copyright (C) 1999 The Apache Software Foundation. All rights reserved.
  6. Redistribution and use in source and binary forms, with or without modifica-
  7. tion, are permitted provided that the following conditions are met:
  8. 1. Redistributions of source code must retain the above copyright notice,
  9. this list of conditions and the following disclaimer.
  10. 2. Redistributions in binary form must reproduce the above copyright notice,
  11. this list of conditions and the following disclaimer in the documentation
  12. and/or other materials provided with the distribution.
  13. 3. The end-user documentation included with the redistribution, if any, must
  14. include the following acknowledgment: "This product includes software
  15. developed by the Apache Software Foundation (http://www.apache.org/)."
  16. Alternately, this acknowledgment may appear in the software itself, if
  17. and wherever such third-party acknowledgments normally appear.
  18. 4. The names "FOP" and "Apache Software Foundation" must not be used to
  19. endorse or promote products derived from this software without prior
  20. written permission. For written permission, please contact
  21. apache@apache.org.
  22. 5. Products derived from this software may not be called "Apache", nor may
  23. "Apache" appear in their name, without prior written permission of the
  24. Apache Software Foundation.
  25. THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES,
  26. INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
  27. FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
  28. APACHE SOFTWARE FOUNDATION OR ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
  29. INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLU-
  30. DING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
  31. OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
  32. ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
  33. (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
  34. THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  35. This software consists of voluntary contributions made by many individuals
  36. on behalf of the Apache Software Foundation and was originally created by
  37. James Tauber <jtauber@jtauber.com>. For more information on the Apache
  38. Software Foundation, please see <http://www.apache.org/>.
  39. */
  40. package org.apache.fop.layout;
  41. //fop
  42. import org.apache.fop.render.Renderer;
  43. import org.apache.fop.messaging.MessageHandler;
  44. import org.apache.fop.layout.inline.*;
  45. import org.apache.fop.datatypes.IDNode;
  46. import org.apache.fop.fo.properties.WrapOption;
  47. import org.apache.fop.fo.properties.WhiteSpaceCollapse;
  48. import org.apache.fop.fo.properties.TextAlign;
  49. import org.apache.fop.fo.properties.TextAlignLast;
  50. import org.apache.fop.fo.properties.LeaderPattern;
  51. import org.apache.fop.fo.properties.Hyphenate;
  52. import org.apache.fop.fo.properties.CountryMaker;
  53. import org.apache.fop.fo.properties.LanguageMaker;
  54. import org.apache.fop.fo.properties.LeaderAlignment;
  55. import org.apache.fop.fo.properties.VerticalAlign;
  56. import org.apache.fop.layout.hyphenation.Hyphenation;
  57. import org.apache.fop.layout.hyphenation.Hyphenator;
  58. //java
  59. import java.util.Vector;
  60. import java.util.Enumeration;
  61. import java.awt.Rectangle;
  62. public class LineArea extends Area {
  63. protected int lineHeight;
  64. protected int halfLeading;
  65. protected int nominalFontSize;
  66. protected int nominalGlyphHeight;
  67. protected int allocationHeight;
  68. protected int startIndent;
  69. protected int endIndent;
  70. private int placementOffset;
  71. private FontState currentFontState; // not the nominal, which is
  72. // in this.fontState
  73. private float red, green, blue;
  74. private int wrapOption;
  75. private int whiteSpaceCollapse;
  76. int vAlign;
  77. /*hyphenation*/
  78. protected int hyphenate;
  79. protected char hyphenationChar;
  80. protected int hyphenationPushCharacterCount;
  81. protected int hyphenationRemainCharacterCount;
  82. protected String language;
  83. protected String country;
  84. /* the width of text that has definitely made it into the line
  85. area */
  86. protected int finalWidth = 0;
  87. /* the position to shift a link rectangle in order to compensate for links embedded within a word*/
  88. protected int embeddedLinkStart = 0;
  89. /* the width of the current word so far */
  90. // protected int wordWidth = 0;
  91. /* values that prev (below) may take */
  92. protected static final int NOTHING = 0;
  93. protected static final int WHITESPACE = 1;
  94. protected static final int TEXT = 2;
  95. /* the character type of the previous character */
  96. protected int prev = NOTHING;
  97. /* the position in data[] of the start of the current word */
  98. // protected int wordStart;
  99. /* the length (in characters) of the current word */
  100. // protected int wordLength = 0;
  101. /* width of spaces before current word */
  102. protected int spaceWidth = 0;
  103. /* the inline areas that have not yet been added to the line
  104. because subsequent characters to come (in a different addText)
  105. may be part of the same word */
  106. protected Vector pendingAreas = new Vector();
  107. /* the width of the pendingAreas */
  108. protected int pendingWidth = 0;
  109. /* text-decoration of the previous text */
  110. protected boolean prevUlState = false;
  111. protected boolean prevOlState = false;
  112. protected boolean prevLTState = false;
  113. public LineArea(FontState fontState, int lineHeight,
  114. int halfLeading, int allocationWidth, int startIndent,
  115. int endIndent, LineArea prevLineArea) {
  116. super(fontState);
  117. this.currentFontState = fontState;
  118. this.lineHeight = lineHeight;
  119. this.nominalFontSize = fontState.getFontSize();
  120. this.nominalGlyphHeight =
  121. fontState.getAscender() - fontState.getDescender();
  122. this.placementOffset = fontState.getAscender();
  123. this.contentRectangleWidth =
  124. allocationWidth - startIndent - endIndent;
  125. this.fontState = fontState;
  126. this.allocationHeight = this.nominalGlyphHeight;
  127. this.halfLeading = this.lineHeight - this.allocationHeight;
  128. this.startIndent = startIndent;
  129. this.endIndent = endIndent;
  130. if (prevLineArea != null) {
  131. Enumeration e = prevLineArea.pendingAreas.elements();
  132. while (e.hasMoreElements()) {
  133. pendingAreas.addElement(e.nextElement());
  134. }
  135. pendingWidth = prevLineArea.getPendingWidth();
  136. }
  137. }
  138. public void render(Renderer renderer) {
  139. renderer.renderLineArea(this);
  140. }
  141. public int addPageNumberCitation(String refid, LinkSet ls) {
  142. /* We should add code here to handle the case where the page number doesn't fit on the current line
  143. */
  144. //Space must be alloted to the page number, so currently we give it 3 spaces
  145. int width = currentFontState.width(currentFontState.mapChar(' '));
  146. PageNumberInlineArea pia =
  147. new PageNumberInlineArea(currentFontState, this.red,
  148. this.green, this.blue, refid, width);
  149. pia.setYOffset(placementOffset);
  150. pendingAreas.addElement(pia);
  151. pendingWidth += width;
  152. prev = TEXT;
  153. return -1;
  154. }
  155. /**
  156. * adds text to line area
  157. *
  158. * @return int character position
  159. */
  160. public int addText(char odata[], int start, int end, LinkSet ls,
  161. TextState textState) {
  162. // this prevents an array index out of bounds
  163. // which occurs when some text is laid out again.
  164. if(start == -1) return -1;
  165. boolean overrun = false;
  166. int wordStart = start;
  167. int wordLength = 0;
  168. int wordWidth = 0;
  169. // With CID fonts, space isn't neccecary currentFontState.width(32)
  170. int whitespaceWidth =
  171. currentFontState.width(currentFontState.mapChar(' '));
  172. char[] data = new char[odata.length];
  173. for (int count = 0; count < odata.length; count++) {
  174. data[count] = odata[count];
  175. }
  176. /* iterate over each character */
  177. for (int i = start; i < end; i++) {
  178. int charWidth;
  179. /* get the character */
  180. char c = data[i];
  181. if (!((c == ' ') || (c == '\n') || (c == '\r') ||
  182. (c == '\t')))
  183. c = data[i] = currentFontState.mapChar(c);
  184. charWidth = currentFontState.width(c);
  185. if ((c == ' ') || (c == '\n') || (c == '\r') ||
  186. (c == '\t')) { // whitespace
  187. if (prev == WHITESPACE) {
  188. // if current & previous are WHITESPACE
  189. if (this.whiteSpaceCollapse ==
  190. WhiteSpaceCollapse.FALSE) {
  191. if (c == ' ') {
  192. spaceWidth += whitespaceWidth;
  193. } else if (c == '\n') {
  194. // force line break
  195. return i;
  196. } else if (c == '\t') {
  197. spaceWidth += 8 * whitespaceWidth;
  198. }
  199. } // else ignore it
  200. } else if (prev == TEXT) {
  201. // if current is WHITESPACE and previous TEXT
  202. // the current word made it, so
  203. // add the space before the current word (if there
  204. // was some)
  205. if (spaceWidth > 0) {
  206. InlineSpace is = new InlineSpace(spaceWidth);
  207. if (prevUlState) {
  208. is.setUnderlined(textState.getUnderlined());
  209. }
  210. if (prevOlState) {
  211. is.setOverlined(textState.getOverlined());
  212. }
  213. if (prevLTState) {
  214. is.setLineThrough(textState.getLineThrough());
  215. }
  216. addChild(is);
  217. finalWidth += spaceWidth;
  218. spaceWidth = 0;
  219. }
  220. // add any pending areas
  221. Enumeration e = pendingAreas.elements();
  222. while (e.hasMoreElements()) {
  223. Box box = (Box) e.nextElement();
  224. if (box instanceof InlineArea) {
  225. if (ls != null) {
  226. Rectangle lr = new Rectangle(finalWidth, 0,
  227. ((InlineArea) box).
  228. getContentWidth(),
  229. fontState.getFontSize());
  230. ls.addRect(lr, this);
  231. }
  232. }
  233. addChild(box);
  234. }
  235. finalWidth += pendingWidth;
  236. // reset pending areas array
  237. pendingWidth = 0;
  238. pendingAreas = new Vector();
  239. // add the current word
  240. if (wordLength > 0) {
  241. WordArea ia = new WordArea(currentFontState,
  242. this.red, this.green, this.blue,
  243. new String(data, wordStart,
  244. wordLength), wordWidth);
  245. ia.setYOffset(placementOffset);
  246. ia.setUnderlined(textState.getUnderlined());
  247. prevUlState = textState.getUnderlined();
  248. ia.setOverlined(textState.getOverlined());
  249. prevOlState = textState.getOverlined();
  250. ia.setLineThrough(textState.getLineThrough());
  251. prevLTState = textState.getLineThrough();
  252. ia.setVerticalAlign(vAlign);
  253. addChild(ia);
  254. if (ls != null) {
  255. Rectangle lr = new Rectangle(finalWidth, 0,
  256. ia.getContentWidth(),
  257. fontState.getFontSize());
  258. ls.addRect(lr, this);
  259. }
  260. finalWidth += wordWidth;
  261. // reset word width
  262. wordWidth = 0;
  263. }
  264. // deal with this new whitespace following the
  265. // word we just added
  266. prev = WHITESPACE;
  267. embeddedLinkStart = 0; //reset embeddedLinkStart since a space was encountered
  268. spaceWidth = whitespaceWidth;
  269. /*
  270. here is the place for space-treatment value 'ignore':
  271. if (this.spaceTreatment ==
  272. SpaceTreatment.IGNORE) {
  273. // do nothing
  274. } else {
  275. spaceWidth = currentFontState.width(32);
  276. }
  277. */
  278. if (this.whiteSpaceCollapse ==
  279. WhiteSpaceCollapse.FALSE) {
  280. if (c == '\n') {
  281. // force a line break
  282. return i;
  283. } else if (c == '\t') {
  284. spaceWidth = whitespaceWidth;
  285. }
  286. }
  287. } else {
  288. // if current is WHITESPACE and no previous
  289. if (this.whiteSpaceCollapse ==
  290. WhiteSpaceCollapse.FALSE) {
  291. prev = WHITESPACE;
  292. spaceWidth = whitespaceWidth;
  293. } else {
  294. // skip over it
  295. start++;
  296. }
  297. }
  298. } else { // current is TEXT
  299. if (prev == WHITESPACE) {
  300. // if current is TEXT and previous WHITESPACE
  301. wordWidth = charWidth;
  302. if ((finalWidth + spaceWidth + wordWidth) >
  303. this.getContentWidth()) {
  304. if (overrun)
  305. MessageHandler.error(">");
  306. if (this.wrapOption == WrapOption.WRAP)
  307. return i;
  308. }
  309. prev = TEXT;
  310. wordStart = i;
  311. wordLength = 1;
  312. } else if (prev == TEXT) {
  313. wordLength++;
  314. wordWidth += charWidth;
  315. } else { // nothing previous
  316. prev = TEXT;
  317. wordStart = i;
  318. wordLength = 1;
  319. wordWidth = charWidth;
  320. }
  321. if ((finalWidth + spaceWidth + pendingWidth +
  322. wordWidth) > this.getContentWidth()) {
  323. // BREAK MID WORD
  324. if (wordStart == start) { // if couldn't even fit
  325. // first word
  326. overrun = true;
  327. // if not at start of line, return word start
  328. // to try again on a new line
  329. if (finalWidth > 0) {
  330. return wordStart;
  331. }
  332. } else if (this.wrapOption == WrapOption.WRAP) {
  333. if (this.hyphenate == Hyphenate.TRUE) {
  334. return this.doHyphenation(data,i,wordStart,this.getContentWidth() - (finalWidth + spaceWidth + pendingWidth));
  335. } else {
  336. return wordStart;
  337. }
  338. }
  339. }
  340. }
  341. } // end of iteration over text
  342. if (prev == TEXT) {
  343. if (spaceWidth > 0) {
  344. InlineSpace pis = new InlineSpace(spaceWidth);
  345. if (prevUlState) {
  346. pis.setUnderlined(textState.getUnderlined());
  347. }
  348. if (prevOlState) {
  349. pis.setOverlined(textState.getOverlined());
  350. }
  351. if (prevLTState) {
  352. pis.setLineThrough(textState.getLineThrough());
  353. }
  354. pendingAreas.addElement(pis);
  355. pendingWidth += spaceWidth;
  356. spaceWidth = 0;
  357. }
  358. WordArea pia = new WordArea(currentFontState, this.red,
  359. this.green, this.blue,
  360. new String(data, wordStart, wordLength), wordWidth);
  361. pia.setYOffset(placementOffset);
  362. pia.setUnderlined(textState.getUnderlined());
  363. prevUlState = textState.getUnderlined();
  364. pia.setOverlined(textState.getOverlined());
  365. prevOlState = textState.getOverlined();
  366. pia.setLineThrough(textState.getLineThrough());
  367. prevLTState = textState.getLineThrough();
  368. pia.setVerticalAlign(vAlign);
  369. if (ls != null) {
  370. Rectangle lr = new Rectangle(finalWidth + spaceWidth +
  371. embeddedLinkStart, spaceWidth,
  372. pia.getContentWidth(), fontState.getFontSize());
  373. ls.addRect(lr, this);
  374. }
  375. embeddedLinkStart += wordWidth;
  376. pendingAreas.addElement(pia);
  377. pendingWidth += wordWidth;
  378. wordWidth = 0;
  379. }
  380. if (overrun)
  381. MessageHandler.error(">");
  382. return -1;
  383. }
  384. /**
  385. * adds a Leader; actually the method receives the leader properties
  386. * and creates a leader area or an inline area which is appended to
  387. * the children of the containing line area. <br>
  388. * leader pattern use-content is not implemented.
  389. */
  390. public void addLeader(int leaderPattern, int leaderLengthMinimum,
  391. int leaderLengthOptimum, int leaderLengthMaximum,
  392. int ruleStyle, int ruleThickness, int leaderPatternWidth,
  393. int leaderAlignment) {
  394. WordArea leaderPatternArea;
  395. int leaderLength = 0;
  396. int remainingWidth =
  397. this.getContentWidth() - this.getCurrentXPosition();
  398. /** checks whether leaderLenghtOptimum fits into rest of line;
  399. * should never overflow, as it has been checked already in BlockArea
  400. * first check: use remaining width if it smaller than optimum oder maximum
  401. * */
  402. if ((remainingWidth <= leaderLengthOptimum) || (remainingWidth <= leaderLengthMaximum)) {
  403. leaderLength = remainingWidth;
  404. } else if ((remainingWidth > leaderLengthOptimum) && ( remainingWidth > leaderLengthMaximum)) {
  405. leaderLength = leaderLengthMaximum;
  406. } else if ((leaderLengthOptimum > leaderLengthMaximum) && (leaderLengthOptimum < remainingWidth)) {
  407. leaderLength = leaderLengthOptimum;
  408. }
  409. //stop if leader-length is too small
  410. if (leaderLength <= 0 ) {
  411. return;
  412. }
  413. switch (leaderPattern) {
  414. case LeaderPattern.SPACE:
  415. //whitespace setting must be false for this
  416. int whiteSpaceSetting = this.whiteSpaceCollapse;
  417. this.changeWhiteSpaceCollapse(WhiteSpaceCollapse.FALSE);
  418. pendingAreas.addElement(
  419. this.buildSimpleLeader(32, leaderLength));
  420. this.changeWhiteSpaceCollapse(whiteSpaceSetting);
  421. break;
  422. case LeaderPattern.RULE:
  423. LeaderArea leaderArea =
  424. new LeaderArea(fontState, red, green, blue, "",
  425. leaderLength, leaderPattern, ruleThickness,
  426. ruleStyle);
  427. leaderArea.setYOffset(placementOffset);
  428. pendingAreas.addElement(leaderArea);
  429. break;
  430. case LeaderPattern.DOTS:
  431. //if the width of a dot is larger than leader-pattern-width
  432. //ignore this property
  433. if (leaderPatternWidth < this.currentFontState.width(46)) {
  434. leaderPatternWidth = 0;
  435. }
  436. //if value of leader-pattern-width is 'use-font-metrics' (0)
  437. if (leaderPatternWidth == 0) {
  438. pendingAreas.addElement(
  439. this.buildSimpleLeader(46, leaderLength));
  440. } else {
  441. //if leader-alignment is used, calculate space to insert before leader
  442. //so that all dots will be parallel.
  443. if (leaderAlignment == LeaderAlignment.REFERENCE_AREA) {
  444. int spaceBeforeLeader = this.getLeaderAlignIndent(
  445. leaderLength, leaderPatternWidth);
  446. //appending indent space leader-alignment
  447. //setting InlineSpace to false, so it is not used in line justification
  448. if (spaceBeforeLeader != 0) {
  449. pendingAreas.addElement(
  450. new InlineSpace(spaceBeforeLeader,
  451. false));
  452. pendingWidth += spaceBeforeLeader;
  453. //shorten leaderLength, otherwise - in case of
  454. //leaderLength=remaining length - it will cut off the end of
  455. //leaderlength
  456. leaderLength -= spaceBeforeLeader;
  457. }
  458. }
  459. // calculate the space to insert between the dots and create a
  460. //inline area with this width
  461. InlineSpace spaceBetweenDots =
  462. new InlineSpace(leaderPatternWidth -
  463. this.currentFontState.width(46), false);
  464. leaderPatternArea =
  465. new WordArea(currentFontState, this.red,
  466. this.green, this.blue, new String ("."),
  467. this.currentFontState.width(46));
  468. leaderPatternArea.setYOffset(placementOffset);
  469. int dotsFactor = (int) Math.floor (
  470. ((double) leaderLength) /
  471. ((double) leaderPatternWidth));
  472. //add combination of dot + space to fill leader
  473. //is there a way to do this in a more effective way?
  474. for (int i = 0; i < dotsFactor; i++) {
  475. pendingAreas.addElement(leaderPatternArea);
  476. pendingAreas.addElement(spaceBetweenDots);
  477. }
  478. //append at the end some space to fill up to leader length
  479. pendingAreas.addElement( new InlineSpace(leaderLength -
  480. dotsFactor * leaderPatternWidth));
  481. }
  482. break;
  483. //leader pattern use-content not implemented.
  484. case LeaderPattern.USECONTENT:
  485. MessageHandler.errorln(
  486. "leader-pattern=\"use-content\" not " + "supported by this version of Fop");
  487. return;
  488. }
  489. //adds leader length to length of pending inline areas
  490. pendingWidth += leaderLength;
  491. //sets prev to TEXT and makes so sure, that also blocks only
  492. //containing leaders are processed
  493. prev = TEXT;
  494. }
  495. /**
  496. * adds pending inline areas to the line area
  497. * normally done, when the line area is filled and
  498. * added as child to the parent block area
  499. */
  500. public void addPending() {
  501. if (spaceWidth > 0) {
  502. addChild(new InlineSpace(spaceWidth));
  503. finalWidth += spaceWidth;
  504. spaceWidth = 0;
  505. }
  506. Enumeration e = pendingAreas.elements();
  507. while (e.hasMoreElements()) {
  508. Box box = (Box) e.nextElement();
  509. addChild(box);
  510. }
  511. finalWidth += pendingWidth;
  512. // reset pending areas array
  513. pendingWidth = 0;
  514. pendingAreas = new Vector();
  515. }
  516. /**
  517. * aligns line area
  518. *
  519. */
  520. public void align(int type) {
  521. int padding = 0;
  522. switch (type) {
  523. case TextAlign.START: // left
  524. padding = this.getContentWidth() - finalWidth;
  525. endIndent += padding;
  526. break;
  527. case TextAlign.END: // right
  528. padding = this.getContentWidth() - finalWidth;
  529. startIndent += padding;
  530. break;
  531. case TextAlign.CENTER: // center
  532. padding = (this.getContentWidth() - finalWidth) / 2;
  533. startIndent += padding;
  534. endIndent += padding;
  535. break;
  536. case TextAlign.JUSTIFY: // justify
  537. Vector spaceList = new Vector();
  538. int spaceCount = 0;
  539. Enumeration e = children.elements();
  540. while (e.hasMoreElements()) {
  541. Box b = (Box) e.nextElement();
  542. if (b instanceof InlineSpace) {
  543. InlineSpace space = (InlineSpace) b;
  544. if (space.getResizeable()) {
  545. spaceList.addElement(space);
  546. spaceCount++;
  547. }
  548. }
  549. }
  550. if (spaceCount > 0) {
  551. padding = (this.getContentWidth() - finalWidth) /
  552. spaceCount;
  553. } else { // no spaces
  554. padding = 0;
  555. }
  556. Enumeration f = spaceList.elements();
  557. while (f.hasMoreElements()) {
  558. InlineSpace space2 = (InlineSpace) f.nextElement();
  559. int i = space2.getSize();
  560. space2.setSize(i + padding);
  561. }
  562. }
  563. }
  564. /**
  565. * Balance (vertically) the inline areas within this line.
  566. */
  567. public void verticalAlign()
  568. {
  569. int superHeight = -this.placementOffset;
  570. int maxHeight = this.allocationHeight;
  571. Enumeration e = children.elements();
  572. while (e.hasMoreElements()) {
  573. Box b = (Box) e.nextElement();
  574. if(b instanceof InlineArea) {
  575. InlineArea ia = (InlineArea)b;
  576. if(ia instanceof WordArea) {
  577. ia.setYOffset(placementOffset);
  578. }
  579. if(ia.getHeight() > maxHeight) {
  580. maxHeight = ia.getHeight();
  581. }
  582. int vert = ia.getVerticalAlign();
  583. if(vert == VerticalAlign.SUPER) {
  584. int fh = fontState.getAscender();
  585. ia.setYOffset((int)(placementOffset - (fh / 3.0)));
  586. } else if(vert == VerticalAlign.SUB) {
  587. int fh = fontState.getAscender();
  588. ia.setYOffset((int)(placementOffset + (fh / 3.0)));
  589. }
  590. } else {
  591. }
  592. }
  593. // adjust the height of this line to the
  594. // resulting alignment height.
  595. this.allocationHeight = maxHeight;
  596. }
  597. public void changeColor(float red, float green, float blue) {
  598. this.red = red;
  599. this.green = green;
  600. this.blue = blue;
  601. }
  602. public void changeFont(FontState fontState) {
  603. this.currentFontState = fontState;
  604. }
  605. public void changeWhiteSpaceCollapse(int whiteSpaceCollapse) {
  606. this.whiteSpaceCollapse = whiteSpaceCollapse;
  607. }
  608. public void changeWrapOption(int wrapOption) {
  609. this.wrapOption = wrapOption;
  610. }
  611. public void changeVerticalAlign(int vAlign) {
  612. this.vAlign = vAlign;
  613. }
  614. public int getEndIndent() {
  615. return endIndent;
  616. }
  617. public int getHeight() {
  618. return this.allocationHeight;
  619. }
  620. public int getPlacementOffset() {
  621. return this.placementOffset;
  622. }
  623. public int getStartIndent() {
  624. return startIndent;
  625. }
  626. public boolean isEmpty() {
  627. return !(pendingAreas.size() > 0 || children.size() > 0);
  628. // return (prev == NOTHING);
  629. }
  630. public Vector getPendingAreas() {
  631. return pendingAreas;
  632. }
  633. public int getPendingWidth() {
  634. return pendingWidth;
  635. }
  636. public void setPendingAreas(Vector areas) {
  637. pendingAreas = areas;
  638. }
  639. public void setPendingWidth(int width) {
  640. pendingWidth = width;
  641. }
  642. /**
  643. * sets hyphenation related traits: language, country, hyphenate, hyphenation-character
  644. * and minimum number of character to remain one the previous line and to be on the
  645. * next line.
  646. */
  647. public void changeHyphenation(String language, String country,
  648. int hyphenate, char hyphenationChar,
  649. int hyphenationPushCharacterCount,
  650. int hyphenationRemainCharacterCount) {
  651. this.language = language;
  652. this.country = country;
  653. this.hyphenate = hyphenate;
  654. this.hyphenationChar = hyphenationChar;
  655. this.hyphenationPushCharacterCount = hyphenationPushCharacterCount;
  656. this.hyphenationRemainCharacterCount =
  657. hyphenationRemainCharacterCount;
  658. }
  659. /**
  660. * creates a leader as String out of the given char and the leader length
  661. * and wraps it in an InlineArea which is returned
  662. */
  663. private InlineArea buildSimpleLeader(int charNumber, int leaderLength) {
  664. int factor = (int) Math.floor (leaderLength /
  665. this.currentFontState.width(charNumber));
  666. char [] leaderChars = new char [factor];
  667. char fillChar = (char) charNumber;
  668. for (int i = 0; i < factor; i ++) {
  669. leaderChars[i] = fillChar;
  670. }
  671. WordArea leaderPatternArea =
  672. new WordArea(currentFontState, this.red, this.green,
  673. this.blue, new String (leaderChars), leaderLength);
  674. leaderPatternArea.setYOffset(placementOffset);
  675. return leaderPatternArea;
  676. }
  677. /**
  678. * calculates the width of space which has to be inserted before the
  679. * start of the leader, so that all leader characters are aligned.
  680. * is used if property leader-align is set. At the moment only the value
  681. * for leader-align="reference-area" is supported.
  682. *
  683. */
  684. private int getLeaderAlignIndent (int leaderLength,
  685. int leaderPatternWidth) {
  686. //calculate position of used space in line area
  687. double position = getCurrentXPosition();
  688. //calculate factor of next leader pattern cycle
  689. double nextRepeatedLeaderPatternCycle =
  690. Math.ceil(position / leaderPatternWidth);
  691. //calculate difference between start of next leader
  692. //pattern cycle and already used space
  693. double difference = (leaderPatternWidth *
  694. nextRepeatedLeaderPatternCycle) - position;
  695. return (int) difference;
  696. }
  697. /**
  698. * calculates the used space in this line area
  699. */
  700. private int getCurrentXPosition() {
  701. return finalWidth + spaceWidth + startIndent + pendingWidth;
  702. }
  703. /**
  704. * extracts a complete word from the character data
  705. */
  706. private String getHyphenationWord (char [] characters, int wordStart) {
  707. boolean wordendFound = false;
  708. int counter = 0;
  709. char [] newWord = new char [100]; //create a buffer
  710. while ((!wordendFound) && ((wordStart + counter) < characters.length)) {
  711. char tk = characters[wordStart+counter];
  712. if (Character.isLetter(tk)) {
  713. newWord[counter] = tk;
  714. counter++;
  715. } else {
  716. wordendFound = true;
  717. }
  718. }
  719. return new String (newWord,0,counter);
  720. }
  721. /** extracts word for hyphenation and calls hyphenation package,
  722. * handles cases of inword punctuation and quotation marks at the beginning
  723. * of words, but not in a internationalized way
  724. */
  725. public int doHyphenation (char [] characters, int position, int wordStart, int remainingWidth) {
  726. //check whether the language property has been set
  727. if (this.language.equalsIgnoreCase("none")) {
  728. MessageHandler.errorln("if property 'hyphenate' is used, a language must be specified");
  729. return wordStart;
  730. }
  731. /** remaining part string of hyphenation */
  732. StringBuffer remainingString = new StringBuffer();
  733. /** for words with some inword punctuation like / or - */
  734. StringBuffer preString = null;
  735. /** char before the word, probably whitespace */
  736. char startChar = ' ' ;//characters[wordStart-1];
  737. /** in word punctuation character */
  738. char inwordPunctuation;
  739. /** the complete word handed to the hyphenator */
  740. String wordToHyphenate;
  741. //width of hyphenation character
  742. int hyphCharWidth = this.currentFontState.width(this.hyphenationChar);
  743. remainingWidth -= hyphCharWidth;
  744. //handles ' or " at the beginning of the word
  745. if (characters[wordStart] == '"' || characters[wordStart] == '\'' ) {
  746. remainingString.append(characters[wordStart]);
  747. //extracts whole word from string
  748. wordToHyphenate = getHyphenationWord(characters,wordStart+1);
  749. } else {
  750. wordToHyphenate = getHyphenationWord(characters,wordStart);
  751. }
  752. //if the extracted word is smaller than the remaining width
  753. //we have a non letter character inside the word. at the moment
  754. //we will only handle hard hyphens and slashes
  755. if (getWordWidth(wordToHyphenate)< remainingWidth) {
  756. inwordPunctuation = characters[wordStart+wordToHyphenate.length()];
  757. if (inwordPunctuation == '-' ||
  758. inwordPunctuation == '/' ) {
  759. preString = new StringBuffer(wordToHyphenate);
  760. preString = preString.append(inwordPunctuation);
  761. wordToHyphenate = getHyphenationWord(characters,wordStart+wordToHyphenate.length()+1);
  762. remainingWidth -= (getWordWidth(wordToHyphenate) + this.currentFontState.width(inwordPunctuation));
  763. }
  764. }
  765. //are there any hyphenation points
  766. Hyphenation hyph = Hyphenator.hyphenate(language,country,wordToHyphenate,hyphenationRemainCharacterCount,hyphenationPushCharacterCount);
  767. //no hyphenation points and no inword non letter character
  768. if (hyph == null && preString == null) {
  769. if (remainingString.length() > 0) {
  770. return wordStart-1;
  771. } else {
  772. return wordStart;
  773. }
  774. //no hyphenation points, but a inword non-letter character
  775. } else if (hyph == null && preString != null){
  776. remainingString.append(preString);
  777. this.addWord(startChar,remainingString);
  778. return wordStart + remainingString.length();
  779. //hyphenation points and no inword non-letter character
  780. } else if (hyph != null && preString == null) {
  781. int index = getFinalHyphenationPoint(hyph,remainingWidth);
  782. if (index != -1) {
  783. remainingString.append(hyph.getPreHyphenText(index));
  784. remainingString.append(this.hyphenationChar);
  785. this.addWord(startChar,remainingString);
  786. return wordStart + remainingString.length()-1;
  787. }
  788. //hyphenation points and a inword non letter character
  789. } else if (hyph != null && preString != null) {
  790. int index = getFinalHyphenationPoint(hyph,remainingWidth);
  791. if (index != -1) {
  792. remainingString.append(preString.append(hyph.getPreHyphenText(index)));
  793. remainingString.append(this.hyphenationChar);
  794. this.addWord(startChar,remainingString);
  795. return wordStart + remainingString.length()-1;
  796. } else {
  797. remainingString.append(preString) ;
  798. this.addWord(startChar,remainingString);
  799. return wordStart + remainingString.length();
  800. }
  801. }
  802. return wordStart;
  803. }
  804. /** calculates the wordWidth using the actual fontstate*/
  805. private int getWordWidth (String word) {
  806. int wordLength = word.length();
  807. int width = 0;
  808. char [] characters = new char [wordLength];
  809. word.getChars(0,wordLength,characters,0);
  810. for (int i = 0; i < wordLength; i++) {
  811. width += this.currentFontState.width(characters[i]);
  812. }
  813. return width;
  814. }
  815. public int getRemainingWidth()
  816. {
  817. return this.getContentWidth() - this.getCurrentXPosition();
  818. }
  819. public void setLinkSet(LinkSet ls)
  820. {
  821. }
  822. public void addInlineArea(Area box)
  823. {
  824. addPending();
  825. addChild(box);
  826. prev = TEXT;
  827. finalWidth += box.getContentWidth();
  828. }
  829. public void addInlineSpace(InlineSpace is, int spaceWidth)
  830. {
  831. addChild(is);
  832. finalWidth += spaceWidth;
  833. // spaceWidth = 0;
  834. }
  835. /** adds a single character to the line area tree*/
  836. public int addCharacter (char data, LinkSet ls, boolean ul) {
  837. WordArea ia = null;
  838. int remainingWidth =
  839. this.getContentWidth() - this.getCurrentXPosition();
  840. int width = this.currentFontState.width(data);
  841. //if it doesn't fit, return
  842. if (width > remainingWidth) {
  843. return org.apache.fop.fo.flow.Character.DOESNOT_FIT;
  844. } else {
  845. //if whitespace-collapse == true, discard character
  846. if (Character.isSpaceChar(data) && whiteSpaceCollapse == WhiteSpaceCollapse.TRUE) {
  847. return org.apache.fop.fo.flow.Character.OK;
  848. }
  849. //create new InlineArea
  850. ia = new WordArea(currentFontState,
  851. this.red, this.green, this.blue,
  852. new Character(data).toString(),width);
  853. ia.setYOffset(placementOffset);
  854. ia.setUnderlined(ul);
  855. pendingAreas.addElement(ia);
  856. if (Character.isSpaceChar(data)) {
  857. this.spaceWidth =+ width;
  858. prev = LineArea.WHITESPACE;
  859. } else {
  860. pendingWidth += width;
  861. prev = LineArea.TEXT;
  862. }
  863. return org.apache.fop.fo.flow.Character.OK;
  864. }
  865. }
  866. /** adds a InlineArea containing the String startChar+wordBuf to the line area children. */
  867. private void addWord (char startChar, StringBuffer wordBuf) {
  868. String word = wordBuf.toString();
  869. WordArea hia;
  870. int startCharWidth = this.currentFontState.width(startChar);
  871. if (startChar == ' ') {
  872. this.addChild(new InlineSpace(startCharWidth));
  873. } else {
  874. hia = new WordArea(currentFontState,
  875. this.red, this.green, this.blue,
  876. new Character(startChar).toString(),1);
  877. hia.setYOffset(placementOffset);
  878. this.addChild(hia);
  879. }
  880. int wordWidth = this.getWordWidth(word);
  881. hia = new WordArea(currentFontState,
  882. this.red, this.green, this.blue,
  883. word,word.length());
  884. hia.setYOffset(placementOffset);
  885. this.addChild(hia);
  886. //calculate the space needed
  887. finalWidth += startCharWidth + wordWidth ;
  888. }
  889. /** extracts from a hyphenated word the best (most greedy) fit */
  890. private int getFinalHyphenationPoint(Hyphenation hyph, int remainingWidth) {
  891. int [] hyphenationPoints = hyph.getHyphenationPoints();
  892. int numberOfHyphenationPoints = hyphenationPoints.length;
  893. int index = -1;
  894. String wordBegin = "";
  895. int wordBeginWidth = 0;
  896. for (int i = 0;i < numberOfHyphenationPoints; i++){
  897. wordBegin = hyph.getPreHyphenText(i);
  898. if (this.getWordWidth(wordBegin) > remainingWidth){
  899. break;
  900. }
  901. index = i;
  902. }
  903. return index;
  904. }
  905. }