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.

TXTRenderer.java 17KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568
  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.render.txt;
  19. import java.awt.Color;
  20. import java.awt.Point;
  21. import java.awt.geom.Rectangle2D;
  22. import java.io.IOException;
  23. import java.io.OutputStream;
  24. import java.util.List;
  25. import java.util.Map;
  26. import org.apache.fop.apps.FOPException;
  27. import org.apache.fop.area.Area;
  28. import org.apache.fop.area.CTM;
  29. import org.apache.fop.area.PageViewport;
  30. import org.apache.fop.area.inline.Image;
  31. import org.apache.fop.area.inline.TextArea;
  32. import org.apache.fop.render.AbstractPathOrientedRenderer;
  33. import org.apache.fop.render.txt.border.AbstractBorderElement;
  34. import org.apache.fop.render.txt.border.BorderManager;
  35. /**
  36. * Renderer that renders areas to plain text.
  37. *
  38. * @author Art Welch
  39. * @author <a href="mailto:mark-fop@inomial.com">Mark Lillywhite</a> (to use
  40. * the new Renderer interface)
  41. */
  42. public class TXTRenderer extends AbstractPathOrientedRenderer {
  43. private static final char LIGHT_SHADE = '\u2591';
  44. private static final char MEDIUM_SHADE = '\u2592';
  45. private static final char DARK_SHADE = '\u2593';
  46. private static final char FULL_BLOCK = '\u2588';
  47. private static final char IMAGE_CHAR = '#';
  48. /**The stream for output */
  49. private OutputStream outputStream;
  50. /** The current stream to add Text commands to. */
  51. private TXTStream currentStream;
  52. /** Buffer for text. */
  53. private StringBuffer[] charData;
  54. /** Buffer for background and images. */
  55. private StringBuffer[] decoData;
  56. /** Height of one symbol in Courier font size of 10pt. */
  57. public static final int CHAR_HEIGHT = 7860;
  58. /** Width of one symbol in Courier font size of 10pt. */
  59. public static final int CHAR_WIDTH = 6000;
  60. /** Current processing page width. */
  61. private int pageWidth;
  62. /** Current processing page height. */
  63. private int pageHeight;
  64. /**
  65. * Every line except the last line on a page (which will end with
  66. * pageEnding) will be terminated with this string.
  67. */
  68. private String lineEnding = "\r\n";
  69. /** Every page except the last one will end with this string. */
  70. private String pageEnding = "\f";
  71. /** Equals true, if current page is first. */
  72. private boolean firstPage = false;
  73. /** Manager for storing border's information. */
  74. private BorderManager bm;
  75. /** Char for current filling. */
  76. private char fillChar;
  77. /** Saves current coordinate transformation. */
  78. private TXTState currentState = new TXTState();
  79. private String encoding;
  80. /**
  81. * Constructs a newly allocated <code>TXTRenderer</code> object.
  82. */
  83. public TXTRenderer() {
  84. }
  85. /** @see org.apache.fop.render.AbstractRenderer#getMimeType() */
  86. public String getMimeType() {
  87. return "text/plain";
  88. }
  89. /**
  90. * Sets the encoding of the target file.
  91. * @param encoding the encoding, null to select the default encoding (UTF-8)
  92. */
  93. public void setEncoding(String encoding) {
  94. this.encoding = encoding;
  95. }
  96. /**
  97. * Indicates if point (x, y) lay inside currentPage.
  98. *
  99. * @param x x coordinate
  100. * @param y y coordinate
  101. * @return <b>true</b> if point lay inside page
  102. */
  103. public boolean isLayInside(int x, int y) {
  104. return (x >= 0) && (x < pageWidth) && (y >= 0) && (y < pageHeight);
  105. }
  106. /**
  107. * Add char to text buffer.
  108. *
  109. * @param x x coordinate
  110. * @param y y coordinate
  111. * @param ch char to add
  112. * @param ischar boolean, repersenting is character adding to text buffer
  113. */
  114. protected void addChar(int x, int y, char ch, boolean ischar) {
  115. Point point = currentState.transformPoint(x, y);
  116. putChar(point.x, point.y, ch, ischar);
  117. }
  118. /**
  119. * Add char to text or background buffer.
  120. *
  121. * @param x x coordinate
  122. * @param y x coordinate
  123. * @param ch char to add
  124. * @param ischar indicates if it char or background
  125. */
  126. protected void putChar(int x, int y, char ch, boolean ischar) {
  127. if (isLayInside(x, y)) {
  128. StringBuffer sb = ischar ? charData[y] : decoData[y];
  129. while (sb.length() <= x) {
  130. sb.append(' ');
  131. }
  132. sb.setCharAt(x, ch);
  133. }
  134. }
  135. /**
  136. * Adds string to text buffer (<code>charData</code>). <p>
  137. * Chars of string map in turn.
  138. *
  139. * @param row x coordinate
  140. * @param col y coordinate
  141. * @param s string to add
  142. */
  143. protected void addString(int row, int col, String s) {
  144. for (int l = 0; l < s.length(); l++) {
  145. addChar(col + l, row, s.charAt(l), true);
  146. }
  147. }
  148. /**
  149. * Render TextArea to Text.
  150. *
  151. * @param area inline area to render
  152. */
  153. protected void renderText(TextArea area) {
  154. int col = Helper.ceilPosition(this.currentIPPosition, CHAR_WIDTH);
  155. int row = Helper.ceilPosition(this.currentBPPosition, CHAR_HEIGHT);
  156. String s = area.getText();
  157. addString(row, col, s);
  158. super.renderText(area);
  159. }
  160. /**
  161. * @see org.apache.fop.render.Renderer#renderPage(PageViewport)
  162. */
  163. public void renderPage(PageViewport page) throws IOException, FOPException {
  164. if (firstPage) {
  165. firstPage = false;
  166. } else {
  167. currentStream.add(pageEnding);
  168. }
  169. Rectangle2D bounds = page.getViewArea();
  170. double width = bounds.getWidth();
  171. double height = bounds.getHeight();
  172. pageWidth = Helper.ceilPosition((int) width, CHAR_WIDTH);
  173. pageHeight = Helper.ceilPosition((int) height, CHAR_HEIGHT);
  174. // init buffers
  175. charData = new StringBuffer[pageHeight];
  176. decoData = new StringBuffer[pageHeight];
  177. for (int i = 0; i < pageHeight; i++) {
  178. charData[i] = new StringBuffer();
  179. decoData[i] = new StringBuffer();
  180. }
  181. bm = new BorderManager(pageWidth, pageHeight, currentState);
  182. super.renderPage(page);
  183. flushBorderToBuffer();
  184. flushBuffer();
  185. }
  186. /**
  187. * Projects current page borders (i.e.<code>bm</code>) to buffer for
  188. * background and images (i.e.<code>decoData</code>).
  189. */
  190. private void flushBorderToBuffer() {
  191. for (int x = 0; x < pageWidth; x++) {
  192. for (int y = 0; y < pageHeight; y++) {
  193. Character c = bm.getCharacter(x, y);
  194. if (c != null) {
  195. putChar(x, y, c.charValue(), false);
  196. }
  197. }
  198. }
  199. }
  200. /**
  201. * Write out the buffer to output stream.
  202. */
  203. private void flushBuffer() {
  204. for (int row = 0; row < pageHeight; row++) {
  205. StringBuffer cr = charData[row];
  206. StringBuffer dr = decoData[row];
  207. StringBuffer outr = null;
  208. if (cr != null && dr == null) {
  209. outr = cr;
  210. } else if (dr != null && cr == null) {
  211. outr = dr;
  212. } else if (cr != null && dr != null) {
  213. int len = dr.length();
  214. if (cr.length() > len) {
  215. len = cr.length();
  216. }
  217. outr = new StringBuffer();
  218. for (int countr = 0; countr < len; countr++) {
  219. if (countr < cr.length() && cr.charAt(countr) != ' ') {
  220. outr.append(cr.charAt(countr));
  221. } else if (countr < dr.length()) {
  222. outr.append(dr.charAt(countr));
  223. } else {
  224. outr.append(' ');
  225. }
  226. }
  227. }
  228. if (outr != null) {
  229. currentStream.add(outr.toString());
  230. }
  231. if (row < pageHeight) {
  232. currentStream.add(lineEnding);
  233. }
  234. }
  235. }
  236. /**
  237. * @see org.apache.fop.render.Renderer#startRenderer(java.io.OutputStream)
  238. */
  239. public void startRenderer(OutputStream os) throws IOException {
  240. log.info("Rendering areas to TEXT.");
  241. this.outputStream = os;
  242. currentStream = new TXTStream(os);
  243. currentStream.setEncoding(this.encoding);
  244. firstPage = true;
  245. }
  246. /**
  247. * @see org.apache.fop.render.Renderer#stopRenderer()
  248. */
  249. public void stopRenderer() throws IOException {
  250. log.info("writing out TEXT");
  251. outputStream.flush();
  252. super.stopRenderer();
  253. }
  254. /**
  255. * Does nothing.
  256. * @see org.apache.fop.render.AbstractPathOrientedRenderer
  257. */
  258. protected void restoreStateStackAfterBreakOut(List breakOutList) {
  259. }
  260. /**
  261. * Does nothing.
  262. * @return null
  263. * @see org.apache.fop.render.AbstractPathOrientedRenderer
  264. */
  265. protected List breakOutOfStateStack() {
  266. return null;
  267. }
  268. /**
  269. * Does nothing.
  270. * @see org.apache.fop.render.AbstractPathOrientedRenderer
  271. */
  272. protected void saveGraphicsState() {
  273. }
  274. /**
  275. * Does nothing.
  276. * @see org.apache.fop.render.AbstractPathOrientedRenderer
  277. */
  278. protected void restoreGraphicsState() {
  279. }
  280. /**
  281. * Does nothing.
  282. * @see org.apache.fop.render.AbstractPathOrientedRenderer
  283. */
  284. protected void beginTextObject() {
  285. }
  286. /**
  287. * Does nothing.
  288. * @see org.apache.fop.render.AbstractPathOrientedRenderer
  289. */
  290. protected void endTextObject() {
  291. }
  292. /**
  293. * Does nothing.
  294. * @see org.apache.fop.render.AbstractPathOrientedRenderer
  295. */
  296. protected void clip() {
  297. }
  298. /**
  299. * Does nothing.
  300. * @see org.apache.fop.render.AbstractPathOrientedRenderer
  301. */
  302. protected void clipRect(float x, float y, float width, float height) {
  303. }
  304. /**
  305. * Does nothing.
  306. * @see org.apache.fop.render.AbstractPathOrientedRenderer
  307. */
  308. protected void moveTo(float x, float y) {
  309. }
  310. /**
  311. * Does nothing.
  312. * @see org.apache.fop.render.AbstractPathOrientedRenderer
  313. */
  314. protected void lineTo(float x, float y) {
  315. }
  316. /**
  317. * Does nothing.
  318. * @see org.apache.fop.render.AbstractPathOrientedRenderer
  319. */
  320. protected void closePath() {
  321. }
  322. /**
  323. * Fills rectangle startX, startY, width, height with char
  324. * <code>charToFill</code>.
  325. *
  326. * @param startX x-coordinate of upper left point
  327. * @param startY y-coordinate of upper left point
  328. * @param width width of rectangle
  329. * @param height height of rectangle
  330. * @param charToFill filling char
  331. */
  332. private void fillRect(int startX, int startY, int width, int height,
  333. char charToFill) {
  334. for (int x = startX; x < startX + width; x++) {
  335. for (int y = startY; y < startY + height; y++) {
  336. addChar(x, y, charToFill, false);
  337. }
  338. }
  339. }
  340. /**
  341. * Fills a rectangular area with the current filling char.
  342. * @see org.apache.fop.render.AbstractPathOrientedRenderer
  343. */
  344. protected void fillRect(float x, float y, float width, float height) {
  345. fillRect(bm.getStartX(), bm.getStartY(), bm.getWidth(), bm.getHeight(),
  346. fillChar);
  347. }
  348. /**
  349. * Changes current filling char.
  350. * @see org.apache.fop.render.AbstractPathOrientedRenderer
  351. */
  352. protected void updateColor(Color col, boolean fill) {
  353. if (col == null) {
  354. return;
  355. }
  356. // fillShade evaluation was taken from fop-0.20.5
  357. // TODO: This fillShase is catually the luminance component of the color
  358. // transformed to the YUV (YPrBb) Colorspace. It should use standard
  359. // Java methods for its conversion instead of the formula given here.
  360. double fillShade = 0.30f / 255f * col.getRed()
  361. + 0.59f / 255f * col.getGreen()
  362. + 0.11f / 255f * col.getBlue();
  363. fillShade = 1 - fillShade;
  364. if (fillShade > 0.8f) {
  365. fillChar = FULL_BLOCK;
  366. } else if (fillShade > 0.6f) {
  367. fillChar = DARK_SHADE;
  368. } else if (fillShade > 0.4f) {
  369. fillChar = MEDIUM_SHADE;
  370. } else if (fillShade > 0.2f) {
  371. fillChar = LIGHT_SHADE;
  372. } else {
  373. fillChar = ' ';
  374. }
  375. }
  376. /**
  377. * @see org.apache.fop.render.AbstractPathOrientedRenderer#drawImage(
  378. * java.lang.String, java.awt.geom.Rectangle2D, java.util.Map)
  379. */
  380. protected void drawImage(String url, Rectangle2D pos, Map foreignAttributes) {
  381. //No images are painted here
  382. }
  383. /**
  384. * Fills image rectangle with a <code>IMAGE_CHAR</code>.
  385. * @see org.apache.fop.render.AbstractPathOrientedRenderer
  386. */
  387. public void renderImage(Image image, Rectangle2D pos) {
  388. int x1 = Helper.ceilPosition(currentIPPosition, CHAR_WIDTH);
  389. int y1 = Helper.ceilPosition(currentBPPosition, CHAR_HEIGHT);
  390. int width = Helper.ceilPosition((int) pos.getWidth(), CHAR_WIDTH);
  391. int height = Helper.ceilPosition((int) pos.getHeight(), CHAR_HEIGHT);
  392. fillRect(x1, y1, width, height, IMAGE_CHAR);
  393. }
  394. /**
  395. * Returns the closest integer to the multiplication of a number and 1000.
  396. *
  397. * @param x the value of the argument, multiplied by
  398. * 1000 and rounded
  399. * @return the value of the argument multiplied by
  400. * 1000 and rounded to the nearest integer
  401. */
  402. protected int toMilli(float x) {
  403. return Math.round(x * 1000f);
  404. }
  405. /**
  406. * Adds one element of border.
  407. *
  408. * @param x x coordinate
  409. * @param y y coordinate
  410. * @param style integer, representing border style
  411. * @param type integer, representing border element type
  412. */
  413. private void addBitOfBorder(int x, int y, int style, int type) {
  414. Point point = currentState.transformPoint(x, y);
  415. if (isLayInside(point.x, point.y)) {
  416. bm.addBorderElement(point.x, point.y, style, type);
  417. }
  418. }
  419. /**
  420. * @see org.apache.fop.render.AbstractPathOrientedRenderer
  421. */
  422. protected void drawBorderLine(float x1, float y1, float x2, float y2,
  423. boolean horz, boolean startOrBefore, int style, Color col) {
  424. int borderHeight = bm.getHeight();
  425. int borderWidth = bm.getWidth();
  426. int borderStartX = bm.getStartX();
  427. int borderStartY = bm.getStartY();
  428. int x, y;
  429. if (horz && startOrBefore) { // BEFORE
  430. x = borderStartX;
  431. y = borderStartY;
  432. } else if (horz && !startOrBefore) { // AFTER
  433. x = borderStartX;
  434. y = borderStartY + borderHeight - 1;
  435. } else if (!horz && startOrBefore) { // START
  436. x = borderStartX;
  437. y = borderStartY;
  438. } else { // END
  439. x = borderStartX + borderWidth - 1;
  440. y = borderStartY;
  441. }
  442. int dx, dy, length, startType, endType;
  443. if (horz) {
  444. length = borderWidth;
  445. dx = 1;
  446. dy = 0;
  447. startType = 1 << AbstractBorderElement.RIGHT;
  448. endType = 1 << AbstractBorderElement.LEFT;
  449. } else {
  450. length = borderHeight;
  451. dx = 0;
  452. dy = 1;
  453. startType = 1 << AbstractBorderElement.DOWN;
  454. endType = 1 << AbstractBorderElement.UP;
  455. }
  456. addBitOfBorder(x, y, style, startType);
  457. for (int i = 0; i < length - 2; i++) {
  458. x += dx;
  459. y += dy;
  460. addBitOfBorder(x, y, style, startType + endType);
  461. }
  462. x += dx;
  463. y += dy;
  464. addBitOfBorder(x, y, style, endType);
  465. }
  466. /**
  467. * @see org.apache.fop.render.AbstractPathOrientedRenderer
  468. */
  469. protected void drawBackAndBorders(Area area, float startx, float starty,
  470. float width, float height) {
  471. bm.setWidth(Helper.ceilPosition(toMilli(width), CHAR_WIDTH));
  472. bm.setHeight(Helper.ceilPosition(toMilli(height), CHAR_HEIGHT));
  473. bm.setStartX(Helper.ceilPosition(toMilli(startx), CHAR_WIDTH));
  474. bm.setStartY(Helper.ceilPosition(toMilli(starty), CHAR_HEIGHT));
  475. super.drawBackAndBorders(area, startx, starty, width, height);
  476. }
  477. /**
  478. * @see org.apache.fop.render.AbstractRenderer#startVParea(CTM, Rectangle2D)
  479. */
  480. protected void startVParea(CTM ctm, Rectangle2D clippingRect) {
  481. currentState.push(ctm);
  482. }
  483. /**
  484. * @see org.apache.fop.render.AbstractRenderer#endVParea()
  485. */
  486. protected void endVParea() {
  487. currentState.pop();
  488. }
  489. }