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 13KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499
  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. import org.apache.fop.render.Renderer;
  42. import org.apache.fop.messaging.MessageHandler;
  43. import java.util.Vector;
  44. import java.util.Enumeration;
  45. import java.awt.Rectangle;
  46. import org.apache.fop.fo.properties.WrapOption; // for enumerated
  47. // values
  48. import org.apache.fop.fo.properties.WhiteSpaceTreatment; // for
  49. // enumerated values
  50. import org.apache.fop.fo.properties.TextAlign; // for enumerated
  51. // values
  52. import org.apache.fop.fo.properties.TextAlignLast; // for enumerated
  53. // values
  54. public class LineArea extends Area {
  55. protected int lineHeight;
  56. protected int halfLeading;
  57. protected int nominalFontSize;
  58. protected int nominalGlyphHeight;
  59. protected int allocationHeight;
  60. protected int startIndent;
  61. protected int endIndent;
  62. private int placementOffset;
  63. private FontState currentFontState; // not the nominal, which is
  64. // in this.fontState
  65. private float red, green, blue;
  66. private int wrapOption;
  67. private int whiteSpaceTreatment;
  68. /* the width of text that has definitely made it into the line
  69. area */
  70. protected int finalWidth = 0;
  71. /* the position to shift a link rectangle in order to compensate for links embedded within a word*/
  72. protected int embeddedLinkStart=0;
  73. /* the width of the current word so far */
  74. protected int wordWidth = 0;
  75. /* values that prev (below) may take */
  76. protected static final int NOTHING = 0;
  77. protected static final int WHITESPACE = 1;
  78. protected static final int TEXT = 2;
  79. /* the character type of the previous character */
  80. protected int prev = NOTHING;
  81. /* the position in data[] of the start of the current word */
  82. protected int wordStart;
  83. /* the length (in characters) of the current word */
  84. protected int wordLength = 0;
  85. /* width of spaces before current word */
  86. protected int spaceWidth = 0;
  87. /* the inline areas that have not yet been added to the line
  88. because subsequent characters to come (in a different addText)
  89. may be part of the same word */
  90. protected Vector pendingAreas = new Vector();
  91. /* the width of the pendingAreas */
  92. protected int pendingWidth = 0;
  93. public LineArea(FontState fontState, int lineHeight, int
  94. halfLeading, int allocationWidth, int startIndent,
  95. int endIndent) {
  96. super(fontState);
  97. this.currentFontState = fontState;
  98. this.lineHeight = lineHeight;
  99. this.nominalFontSize = fontState.getFontSize();
  100. this.nominalGlyphHeight = fontState.getAscender() -
  101. fontState.getDescender();
  102. this.placementOffset = fontState.getAscender();
  103. this.contentRectangleWidth = allocationWidth - startIndent -
  104. endIndent;
  105. this.fontState = fontState;
  106. this.allocationHeight = this.nominalGlyphHeight;
  107. this.halfLeading = this.lineHeight - this.allocationHeight;
  108. this.startIndent = startIndent;
  109. this.endIndent = endIndent;
  110. }
  111. public void render(Renderer renderer) {
  112. renderer.renderLineArea(this);
  113. }
  114. public int addText(char odata[], int start, int end, LinkSet ls) {
  115. boolean overrun = false;
  116. wordStart = start;
  117. wordLength = 0;
  118. wordWidth = 0;
  119. char[] data = new char[odata.length];
  120. for(int count=0;count <odata.length; count++) {
  121. data[count] = odata[count];
  122. }
  123. /* iterate over each character */
  124. for (int i = start; i < end; i++) {
  125. int charWidth;
  126. /* get the character */
  127. char c = data[i];
  128. if (c > 127) {
  129. /* this class shouldn't be hard coded */
  130. char d =
  131. org.apache.fop.render.pdf.CodePointMapping.map[c];
  132. if (d != 0) {
  133. c = data[i] = d;
  134. } else {
  135. MessageHandler.error("ch"
  136. + (int)c + "?");
  137. c = data[i] = '#';
  138. }
  139. }
  140. charWidth = currentFontState.width(c);
  141. if ((c == ' ') ||
  142. (c == '\n') ||
  143. (c == '\r') ||
  144. (c == '\t')) { // whitespace
  145. if (prev == WHITESPACE) {
  146. // if current & previous are WHITESPACE
  147. if (this.whiteSpaceTreatment ==
  148. WhiteSpaceTreatment.PRESERVE) {
  149. if (c == ' ') {
  150. spaceWidth += currentFontState.width(32);
  151. } else if (c == '\n') {
  152. // force line break
  153. return i;
  154. } else if (c == '\t') {
  155. spaceWidth += 8 * currentFontState.width(32);
  156. }
  157. } // else ignore it
  158. } else if (prev == TEXT) {
  159. // if current is WHITESPACE and previous TEXT
  160. // the current word made it, so
  161. // add the space before the current word (if there
  162. // was some)
  163. if (spaceWidth > 0) {
  164. addChild(new InlineSpace(spaceWidth));
  165. finalWidth += spaceWidth;
  166. spaceWidth = 0;
  167. }
  168. // add any pending areas
  169. Enumeration e = pendingAreas.elements();
  170. while (e.hasMoreElements()) {
  171. InlineArea inlineArea = (InlineArea) e.nextElement();
  172. if (ls != null) {
  173. Rectangle lr =
  174. new Rectangle(startIndent +
  175. finalWidth,
  176. 0,
  177. inlineArea.getContentWidth(),
  178. fontState.getFontSize());
  179. ls.addRect(lr, this);
  180. }
  181. addChild(inlineArea);
  182. }
  183. finalWidth += pendingWidth;
  184. // reset pending areas array
  185. pendingWidth = 0;
  186. pendingAreas = new Vector();
  187. // add the current word
  188. if (wordLength > 0) {
  189. InlineArea ia = new InlineArea(currentFontState,
  190. this.red, this.green,
  191. this.blue, new
  192. String(data, wordStart,
  193. wordLength),
  194. wordWidth);
  195. addChild(ia);
  196. if (ls != null) {
  197. Rectangle lr =
  198. new Rectangle(startIndent +
  199. finalWidth,
  200. 0,
  201. ia.getContentWidth(),
  202. fontState.getFontSize());
  203. ls.addRect(lr, this);
  204. }
  205. finalWidth += wordWidth;
  206. // reset word width
  207. wordWidth = 0;
  208. }
  209. // deal with this new whitespace following the
  210. // word we just added
  211. prev = WHITESPACE;
  212. embeddedLinkStart=0; //reset embeddedLinkStart since a space was encountered
  213. if (this.whiteSpaceTreatment ==
  214. WhiteSpaceTreatment.IGNORE) {
  215. // do nothing
  216. } else {
  217. spaceWidth = currentFontState.width(32);
  218. }
  219. if (this.whiteSpaceTreatment ==
  220. WhiteSpaceTreatment.PRESERVE) {
  221. if (c == '\n') {
  222. // force a line break
  223. return i;
  224. } else if (c == '\t') {
  225. spaceWidth = currentFontState.width(32);
  226. }
  227. }
  228. } else {
  229. // if current is WHITESPACE and no previous
  230. if (this.whiteSpaceTreatment ==
  231. WhiteSpaceTreatment.PRESERVE) {
  232. prev = WHITESPACE;
  233. spaceWidth = currentFontState.width(32);
  234. } else {
  235. // skip over it
  236. start++;
  237. }
  238. }
  239. } else { // current is TEXT
  240. if (prev == WHITESPACE) {
  241. // if current is TEXT and previous WHITESPACE
  242. wordWidth = charWidth;
  243. if ((finalWidth + spaceWidth + wordWidth) >
  244. this.getContentWidth()) {
  245. if (overrun)
  246. MessageHandler.error(">");
  247. if (this.wrapOption == WrapOption.WRAP)
  248. return i;
  249. }
  250. prev = TEXT;
  251. wordStart = i;
  252. wordLength = 1;
  253. } else if (prev == TEXT) {
  254. wordLength++;
  255. wordWidth += charWidth;
  256. } else { // nothing previous
  257. prev = TEXT;
  258. wordStart = i;
  259. wordLength = 1;
  260. wordWidth = charWidth;
  261. }
  262. if ((finalWidth + spaceWidth + pendingWidth + wordWidth) >
  263. this.getContentWidth()) {
  264. // BREAK MID WORD
  265. if (wordStart == start) { // if couldn't even fit
  266. // first word
  267. overrun = true;
  268. // if not at start of line, return word start
  269. // to try again on a new line
  270. if (finalWidth > 0) {
  271. return wordStart;
  272. }
  273. } else if (this.wrapOption == WrapOption.WRAP) {
  274. return wordStart;
  275. }
  276. }
  277. }
  278. } // end of iteration over text
  279. if (prev == TEXT) {
  280. InlineArea pia = new InlineArea(currentFontState, this.red,
  281. this.green, this.blue, new
  282. String(data, wordStart,
  283. wordLength), wordWidth);
  284. if (ls != null) {
  285. Rectangle lr =
  286. new Rectangle(startIndent + finalWidth + spaceWidth + embeddedLinkStart,
  287. spaceWidth,
  288. pia.getContentWidth(),
  289. fontState.getFontSize());
  290. ls.addRect(lr, this);
  291. }
  292. embeddedLinkStart += wordWidth;
  293. pendingAreas.addElement(pia);
  294. pendingWidth += wordWidth;
  295. wordWidth = 0;
  296. }
  297. if (overrun)
  298. MessageHandler.error(">");
  299. return -1;
  300. }
  301. public void addPending() {
  302. if (spaceWidth > 0) {
  303. addChild(new InlineSpace(spaceWidth));
  304. finalWidth += spaceWidth;
  305. spaceWidth = 0;
  306. }
  307. Enumeration e = pendingAreas.elements();
  308. while (e.hasMoreElements()) {
  309. InlineArea inlineArea = (InlineArea) e.nextElement();
  310. addChild(inlineArea);
  311. }
  312. finalWidth += pendingWidth;
  313. // reset pending areas array
  314. pendingWidth = 0;
  315. pendingAreas = new Vector();
  316. }
  317. public void align(int type) {
  318. int padding = 0;
  319. switch (type) {
  320. case TextAlign.START: // left
  321. padding = this.getContentWidth() - finalWidth;
  322. endIndent += padding;
  323. break;
  324. case TextAlign.END: // right
  325. padding = this.getContentWidth() - finalWidth;
  326. startIndent += padding;
  327. break;
  328. case TextAlign.CENTERED: // center
  329. padding = (this.getContentWidth() - finalWidth)/2;
  330. startIndent += padding;
  331. endIndent += padding;
  332. break;
  333. case TextAlign.JUSTIFIED: // justify
  334. Vector spaceList = new Vector();
  335. int spaceCount = 0;
  336. Enumeration e = children.elements();
  337. while (e.hasMoreElements()) {
  338. Box b = (Box)e.nextElement();
  339. if (b instanceof InlineSpace) {
  340. InlineSpace space = (InlineSpace)b;
  341. spaceList.addElement(space);
  342. spaceCount++;
  343. }
  344. }
  345. if (spaceCount > 0) {
  346. padding = (this.getContentWidth() - finalWidth) /
  347. spaceCount;
  348. } else { // no spaces
  349. padding = 0;
  350. }
  351. Enumeration f = spaceList.elements();
  352. while (f.hasMoreElements()) {
  353. InlineSpace space2 = (InlineSpace)f.nextElement();
  354. int i = space2.getSize();
  355. space2.setSize(i + padding);
  356. }
  357. }
  358. }
  359. public void changeColor(float red, float green, float blue) {
  360. this.red = red;
  361. this.green = green;
  362. this.blue = blue;
  363. }
  364. public void changeFont(FontState fontState) {
  365. this.currentFontState = fontState;
  366. }
  367. public void changeWhiteSpaceTreatment(int whiteSpaceTreatment) {
  368. this.whiteSpaceTreatment = whiteSpaceTreatment;
  369. }
  370. public void changeWrapOption(int wrapOption) {
  371. this.wrapOption = wrapOption;
  372. }
  373. public int getEndIndent() {
  374. return endIndent;
  375. }
  376. public int getHeight() {
  377. return this.allocationHeight;
  378. }
  379. public int getPlacementOffset() {
  380. return this.placementOffset;
  381. }
  382. public int getStartIndent() {
  383. return startIndent;
  384. }
  385. public boolean isEmpty() {
  386. return (prev==0);
  387. }
  388. public Vector getPendingAreas() {
  389. return pendingAreas;
  390. }
  391. public int getPendingWidth() {
  392. return pendingWidth;
  393. }
  394. public void setPendingAreas(Vector areas) {
  395. pendingAreas = areas;
  396. }
  397. public void setPendingWidth(int width) {
  398. pendingWidth = width;
  399. }
  400. }