PageRenderTime 54ms CodeModel.GetById 24ms RepoModel.GetById 0ms app.codeStats 0ms

/libreoffice-3.6.0.2/xmerge/source/pocketword/java/org/openoffice/xmerge/converter/xml/sxw/pocketword/Paragraph.java

#
Java | 858 lines | 439 code | 136 blank | 283 comment | 94 complexity | 215ecb2c1d6b9ea01653be937336c846 MD5 | raw file
Possible License(s): MPL-2.0-no-copyleft-exception, LGPL-2.1, AGPL-1.0, BSD-3-Clause-No-Nuclear-License-2014, GPL-3.0, LGPL-3.0
  1. /*************************************************************************
  2. *
  3. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
  4. *
  5. * Copyright 2000, 2010 Oracle and/or its affiliates.
  6. *
  7. * OpenOffice.org - a multi-platform office productivity suite
  8. *
  9. * This file is part of OpenOffice.org.
  10. *
  11. * OpenOffice.org is free software: you can redistribute it and/or modify
  12. * it under the terms of the GNU Lesser General Public License version 3
  13. * only, as published by the Free Software Foundation.
  14. *
  15. * OpenOffice.org is distributed in the hope that it will be useful,
  16. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  17. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  18. * GNU Lesser General Public License version 3 for more details
  19. * (a copy is included in the LICENSE file that accompanied this code).
  20. *
  21. * You should have received a copy of the GNU Lesser General Public License
  22. * version 3 along with OpenOffice.org. If not, see
  23. * <http://www.openoffice.org/license.html>
  24. * for a copy of the LGPLv3 License.
  25. *
  26. ************************************************************************/
  27. package org.openoffice.xmerge.converter.xml.sxw.pocketword;
  28. import java.io.ByteArrayOutputStream;
  29. import java.io.IOException;
  30. import java.util.Vector;
  31. import java.util.Enumeration;
  32. import java.awt.Color;
  33. import org.openoffice.xmerge.util.EndianConverter;
  34. import org.openoffice.xmerge.util.ColourConverter;
  35. import org.openoffice.xmerge.converter.xml.ParaStyle;
  36. import org.openoffice.xmerge.converter.xml.TextStyle;
  37. /**
  38. * Represents a paragraph data structure within a Pocket Word document.
  39. *
  40. * @author Mark Murnane
  41. * @version 1.1
  42. */
  43. class Paragraph implements PocketWordConstants {
  44. /*
  45. * The data elements of a Paragraph.
  46. *
  47. * As the 'unknown' values are not calculated they are declared static.
  48. * They are not declared final because they do have a calcuable value.
  49. */
  50. private static short unknown1 = 0x23;
  51. private short dataWords = 0;
  52. private short textLength = 0;
  53. private short lengthWithFormatting = 0;
  54. private short lines = 0;
  55. private static final short marker = (short)0xFFFF;
  56. private static int unknown2 = 0x22; // May be two short values
  57. private short specialIndentation = 0;
  58. private short leftIndentation = 0;
  59. private short rightIndentation = 0;
  60. private byte bullets = 0;
  61. private byte alignment = 0;
  62. private static int unknown3 = 0;
  63. // Will always have at least these formatting settings in each paragraph
  64. private short defaultFont = 2; // Courier New for the time being
  65. private short defaultSize = 10;
  66. /*
  67. * Remaining elements assist in calculating correct values for the paragraph
  68. * representation.
  69. */
  70. private Vector textSegments = null;
  71. private Vector lineDescriptors = null;
  72. private ParaStyle pStyle = null;
  73. private boolean isLastParagraph = false;
  74. /*
  75. * Private class constructor used by all constructors. Ensures the proper
  76. * initialisation of the Vector storing the paragraph's text.
  77. */
  78. private Paragraph () {
  79. textSegments = new Vector(0, 1);
  80. }
  81. /**
  82. * <p>Constructor for use when converting from SXW format to Pocket Word
  83. * format.</p>
  84. *
  85. * @param style Paragraph style object describing the formatting style
  86. * of this paragraph.
  87. */
  88. public Paragraph (ParaStyle style) {
  89. this();
  90. lineDescriptors = new Vector(0, 1);
  91. pStyle = style;
  92. }
  93. /**
  94. * <p>Constructor for use when converting from Pocket Word format to SXW
  95. * format.</p>
  96. *
  97. * @param data Byte array containing byte data describing this paragraph
  98. * from the Pocket Word file.
  99. */
  100. public Paragraph (byte[] data) {
  101. this();
  102. /*
  103. * Read in all fixed data from the array
  104. *
  105. * unknown1 appears at data[0] and data[1]
  106. */
  107. dataWords = EndianConverter.readShort(new byte[] { data[2], data[3] } );
  108. textLength = EndianConverter.readShort(new byte[] { data[4], data [5] } );
  109. lengthWithFormatting = EndianConverter.readShort(
  110. new byte[] { data[6], data[7] } );
  111. lines = EndianConverter.readShort(new byte[] { data[8], data [9] } );
  112. /*
  113. * The marker appears at data[10] and data[11].
  114. *
  115. * The value of unknown2 is at data[12], data[13], data[14] and data[15].
  116. */
  117. specialIndentation = EndianConverter.readShort(new byte[] { data[16], data[17] } );
  118. leftIndentation = EndianConverter.readShort(new byte[] { data[18], data [19] } );
  119. rightIndentation = EndianConverter.readShort(new byte[] { data[20], data [21] } );
  120. bullets = data[22];
  121. alignment = data[23];
  122. // The value of unknown3 is at data[24], data[25], data[26] and data[27].
  123. /*
  124. * The actual paragraph data is in the remainder of the byte sequence.
  125. *
  126. * Only the actual text seqence with the embedded formatting tags is
  127. * relevant to the conversion from Pocket Word to SXW format.
  128. */
  129. ByteArrayOutputStream bos = new ByteArrayOutputStream();
  130. bos.write(data, 28, lengthWithFormatting);
  131. parseText(bos.toByteArray());
  132. }
  133. /*
  134. * Processes the text portion of the raw paragraph data from the Pocket Word
  135. * file. This data also includes formatting settings for the text in the
  136. * paragraph.
  137. *
  138. * Formatting changes appear like XML/HTML tags. Formatted blocks are
  139. * preceded by a sequence of bytes switching on a formatting change and
  140. * followed by a sequence switching off that formatting change.
  141. */
  142. private void parseText (byte[] data) {
  143. int totalLength = data.length;
  144. StringBuffer sb = new StringBuffer("");
  145. // Setup text style information
  146. int mask = TextStyle.BOLD | TextStyle.ITALIC | TextStyle.UNDERLINE
  147. | TextStyle.STRIKETHRU;
  148. String fontName = null;
  149. int fontSize = 0;
  150. Color textColour = null;
  151. Color backColour = null;
  152. int modifiers = 0;
  153. TextStyle ts = null;
  154. int attrsSet = 0; // If this is 0, we have no extra style
  155. boolean inSequence = false;
  156. boolean sawText = false;
  157. String s = new String(); // For debugging
  158. // Start from the very beginning
  159. for (int i = 0; i < totalLength; i++) {
  160. // Will encounter at least two codes first
  161. if ((byte)(data[i] & 0xF0) == FORMATTING_TAG) {
  162. if (sawText) {
  163. // Style change so dump previous segment and style info
  164. addTextSegment(sb.toString(), ts);
  165. sb = new StringBuffer("");
  166. sawText = false;
  167. }
  168. switch (data[i]) {
  169. case FONT_TAG:
  170. int index = EndianConverter.readShort(
  171. new byte[] { data[i + 1], data[i + 2] } );
  172. /*
  173. * Standard font.
  174. *
  175. * Should really be one, but as the only supported font
  176. * currently is Courier New, want to leave it at Courier
  177. * New for round trip conversions.
  178. *
  179. * Also need to account for the fact that Tahoma is the
  180. * correct standard font.
  181. */
  182. if (fontName == null || fontName.equals("2")) {
  183. if (index != 2 && index != 1) {
  184. fontName = String.valueOf(index);
  185. attrsSet++;
  186. }
  187. }
  188. else {
  189. // Font is set, but not the default
  190. if (index == 2 || index == 1) {
  191. fontName = "2";
  192. attrsSet--;
  193. }
  194. else {
  195. fontName = String.valueOf(index);
  196. }
  197. }
  198. i += 2;
  199. break;
  200. case FONT_SIZE_TAG:
  201. int size = EndianConverter.readShort(
  202. new byte[] { data[i + 1], data[i + 2] } );
  203. if (size == 0) {
  204. // Flags the end of the last paragraph
  205. isLastParagraph = true;
  206. i += 2;
  207. break;
  208. }
  209. // Standard size
  210. if (fontSize == 0 || fontSize == 10) {
  211. if (size != 10) {
  212. fontSize = size;
  213. attrsSet++;
  214. }
  215. }
  216. else {
  217. // Font size is set, but not to standard
  218. if (size == 10) {
  219. fontSize = 10;
  220. attrsSet--;
  221. }
  222. else {
  223. fontSize = size;
  224. }
  225. }
  226. i += 2;
  227. break;
  228. case COLOUR_TAG:
  229. if (data[i + 1] != 0) {
  230. ColourConverter cc = new ColourConverter();
  231. textColour = cc.convertToRGB(
  232. EndianConverter.readShort(new byte[] { data[i + 1],
  233. data[i + 2] } ));
  234. attrsSet++;
  235. }
  236. else {
  237. textColour = null;
  238. attrsSet--;
  239. }
  240. i += 2;
  241. break;
  242. case FONT_WEIGHT_TAG:
  243. if (data[i + 1] == FONT_WEIGHT_BOLD
  244. || data[i + 1] == FONT_WEIGHT_THICK) {
  245. modifiers |= TextStyle.BOLD;
  246. attrsSet++;
  247. }
  248. else {
  249. // Its a bit field so subtracting should work okay.
  250. modifiers ^= TextStyle.BOLD;
  251. attrsSet--;
  252. }
  253. i += 2;
  254. break;
  255. case ITALIC_TAG:
  256. if (data[i + 1] == (byte)0x01) {
  257. modifiers |= TextStyle.ITALIC;
  258. attrsSet++;
  259. }
  260. else {
  261. modifiers ^= TextStyle.ITALIC;
  262. attrsSet--;
  263. }
  264. i++;
  265. break;
  266. case UNDERLINE_TAG:
  267. if (data[i + 1] == (byte)0x01) {
  268. modifiers |= TextStyle.UNDERLINE;
  269. attrsSet++;
  270. }
  271. else {
  272. modifiers ^= TextStyle.UNDERLINE;
  273. attrsSet--;
  274. }
  275. i++;
  276. break;
  277. case STRIKETHROUGH_TAG:
  278. if (data[i + 1] == (byte)0x01) {
  279. modifiers |= TextStyle.STRIKETHRU;
  280. attrsSet++;
  281. }
  282. else {
  283. modifiers ^= TextStyle.STRIKETHRU;
  284. attrsSet--;
  285. }
  286. i++;
  287. break;
  288. case HIGHLIGHT_TAG:
  289. /*
  290. * Highlighting is treated by OpenOffice as a
  291. * background colour.
  292. */
  293. if (data[i + 1] == (byte)0x01) {
  294. backColour = Color.yellow;
  295. attrsSet++;
  296. }
  297. else {
  298. backColour = null;
  299. attrsSet--;
  300. }
  301. i++;
  302. break;
  303. }
  304. inSequence = true;
  305. continue;
  306. }
  307. if (inSequence) {
  308. // Style information has been changed. Create new style here
  309. inSequence = false;
  310. if (attrsSet > 0) {
  311. ts = new TextStyle(null, TEXT_STYLE_FAMILY, DEFAULT_STYLE,
  312. mask, modifiers, fontSize, fontName, null);
  313. ts.setColors(textColour, backColour);
  314. }
  315. else {
  316. ts = null;
  317. }
  318. }
  319. /*
  320. * C4 xx seems to indicate a control code. C4 00 indicates the end
  321. * of a paragraph; C4 04 indicates a tab space. Only these two
  322. * have been seen so far.
  323. */
  324. if (data[i] == (byte)0xC4) {
  325. /*
  326. * Redundant nodes are sometimes added to the last paragraph
  327. * because a new sequence is being processed when the flag is
  328. * set.
  329. *
  330. * To avoid this, do nothing with the last paragraph unless no
  331. * text has been added for it already. In that case, add the
  332. * empty text segment being process to ensure that all
  333. * paragraphs have at least one text segment.
  334. */
  335. if (data[i + 1] == (byte)0x00) {
  336. if (isLastParagraph && textSegments.size() > 0) {
  337. return;
  338. }
  339. addTextSegment(sb.toString(), ts);
  340. return;
  341. }
  342. sb.append("\t");
  343. sawText = true;
  344. i++;
  345. continue;
  346. }
  347. sb.append((char)data[i]);
  348. sawText = true;
  349. s = sb.toString();
  350. }
  351. }
  352. /**
  353. * <p>Adds details of a new text block to the <code>Paragraph</code> object.
  354. * </p>
  355. *
  356. * @param text The text of the new block.
  357. * @param style Text style object describing the formatting attached
  358. * to this block of text.
  359. */
  360. public void addTextSegment(String text, TextStyle style) {
  361. textLength += text.length();
  362. textSegments.add(new ParagraphTextSegment(text, style));
  363. }
  364. /**
  365. * <p>This method alters the state of the <code>Paragraph</code> object to
  366. * indicate whether or not it is the final paragraph in the document.</p>
  367. *
  368. * <p>It is used during conversion from SXW format to Pocket Word format.
  369. * In Pocket Word files, the last paragraph finishes with a different byte
  370. * sequence to other paragraphs.</p>
  371. *
  372. * @param isLast true if the Paragraph is the last in the document,
  373. * false otherwise.
  374. */
  375. public void setLastParagraph(boolean isLast) {
  376. isLastParagraph = isLast;
  377. }
  378. /**
  379. * <p>Complementary method to {@link #setLastParagraph(boolean)
  380. * setLastParagraph}. Returns the terminal status of this
  381. * <code>Paragraph</code> within the Pocket Word document.</p>
  382. *
  383. * @return true if the Paragraph is the last in the document; false otherwise.
  384. */
  385. public boolean getLastParagraph () {
  386. return isLastParagraph;
  387. }
  388. /**
  389. * <p>This method returns the Pocket Word representation of this
  390. * <code>Paragraph</code> in Little Endian byte order.</p>
  391. *
  392. * <p>Used when converting from SXW format to Pocket Word format.</p>
  393. *
  394. * @return <code>byte</code> array containing the formatted representation
  395. * of this Paragraph.
  396. */
  397. public byte[] getParagraphData() {
  398. ByteArrayOutputStream bos = new ByteArrayOutputStream();
  399. postProcessText();
  400. /*
  401. * Need information about the paragraph segments in two places
  402. * so calculate them first.
  403. *
  404. * The stream contains the text wrapped in any formatting sequences that
  405. * are necessary.
  406. */
  407. ByteArrayOutputStream segs = new ByteArrayOutputStream();
  408. try {
  409. for (int i = 0; i < textSegments.size(); i++) {
  410. ParagraphTextSegment pts = (ParagraphTextSegment)textSegments.elementAt(i);
  411. segs.write(pts.getData());
  412. }
  413. }
  414. catch (IOException ioe) {
  415. // Should never happen in a memory based stream
  416. }
  417. /*
  418. * Number of data words for this paragraph descriptor:
  419. *
  420. * 26 is the number of bytes prior to the start of the segment.
  421. * 3 comes from the C4 00 00 termintating sequence.
  422. */
  423. dataWords = (short)(26 + segs.size() + 3 + 4);
  424. if (isLastParagraph) {
  425. dataWords += 6;
  426. }
  427. if (dataWords % 4 != 0) {
  428. dataWords += (4 - (dataWords % 4));
  429. }
  430. dataWords /= 4;
  431. /*
  432. * The 8 bytes are made up of E6 ?0 00 and E5 ?0 00 at the start of the
  433. * text along with the C4 00 that terminates it.
  434. *
  435. * In the event that the paragraph is the last one E6 00 00 is also
  436. * present at the end of the text. Also, as we currently use a font
  437. * other than the first in the index (Tahoma) E5 01 00 is also present.
  438. *
  439. * Make sure this is accurate when font specifications change
  440. */
  441. lengthWithFormatting = (short)(segs.size() + (isLastParagraph ? 14 : 8));
  442. try {
  443. bos.write(EndianConverter.writeShort(unknown1));
  444. bos.write(EndianConverter.writeShort(dataWords));
  445. bos.write(EndianConverter.writeShort((short)(textLength + 1)));
  446. bos.write(EndianConverter.writeShort(lengthWithFormatting));
  447. bos.write(EndianConverter.writeShort(lines));
  448. bos.write(EndianConverter.writeShort(marker));
  449. bos.write(EndianConverter.writeInt(unknown2));
  450. bos.write(EndianConverter.writeShort(specialIndentation));
  451. bos.write(EndianConverter.writeShort(leftIndentation));
  452. bos.write(EndianConverter.writeShort(rightIndentation));
  453. bos.write(bullets);
  454. if (pStyle != null && pStyle.isAttributeSet(ParaStyle.TEXT_ALIGN)) {
  455. switch (pStyle.getAttribute(ParaStyle.TEXT_ALIGN)) {
  456. case ParaStyle.ALIGN_RIGHT:
  457. bos.write(0x01);
  458. break;
  459. case ParaStyle.ALIGN_CENTER:
  460. bos.write(0x02);
  461. break;
  462. default:
  463. bos.write(0x00); // Left align in all other circumstances
  464. break;
  465. }
  466. }
  467. else {
  468. bos.write(0x00);
  469. }
  470. bos.write(EndianConverter.writeInt(unknown3));
  471. /*
  472. * Write out font and size.
  473. *
  474. * If font support is added then this should change as the information
  475. * will have to be calculated from a Font table.
  476. */
  477. bos.write(FONT_TAG);
  478. bos.write(EndianConverter.writeShort(defaultFont));
  479. bos.write(FONT_SIZE_TAG);
  480. bos.write(EndianConverter.writeShort(defaultSize));
  481. // Write out the text segments
  482. bos.write(segs.toByteArray());
  483. /*
  484. * If this is the last paragraph in the document then we need to make
  485. * sure that the paragraph text is terminated correctly with an E6 00 00
  486. * before the C4 00 00.
  487. */
  488. if (isLastParagraph) {
  489. if (defaultFont != 1) {
  490. // Must always go back to the first font.
  491. bos.write(FONT_TAG);
  492. bos.write(EndianConverter.writeShort((short)0x01));
  493. }
  494. bos.write(FONT_SIZE_TAG);
  495. bos.write(EndianConverter.writeShort((short)0x00));
  496. }
  497. bos.write(new byte[] { (byte)0xC4, 0x00, 0x00 } );
  498. int padding = 0;
  499. if (bos.size() % 4 != 0) {
  500. padding = 4 - (bos.size() % 4);
  501. }
  502. for (int i = 0; i < padding; i++) {
  503. bos.write(0x00);
  504. }
  505. // Third byte should match first byte after 0xFF 0xFF
  506. bos.write(new byte[] { 0x42, 0x00, 0x22, 0x00} );
  507. /*
  508. * Meaning of last two bytes seems to be the number of words describing
  509. * lines. This is calculated at 10 bytes per descriptor.
  510. *
  511. * May have two extra padding bytes that need to be accounted for too
  512. * The division below may lose 2 bytes (integer result).
  513. */
  514. int wordsRemaining = (lineDescriptors.size() * 10) / 4;
  515. if ((lineDescriptors.size() * 10) % 4 != 0) {
  516. wordsRemaining++;
  517. }
  518. bos.write(EndianConverter.writeShort((short)wordsRemaining));
  519. // Now write out the line descriptors
  520. for (int i = 0; i < lineDescriptors.size(); i++) {
  521. LineDescriptor ld = (LineDescriptor)lineDescriptors.elementAt(i);
  522. bos.write(ld.getDescriptorInfo());
  523. }
  524. if (!isLastParagraph) {
  525. /*
  526. * There may be a need to pad this. Will be writing at
  527. * either start of 4 byte block or 2 bytes into it.
  528. */
  529. if (bos.size() % 4 != 2) {
  530. bos.write(EndianConverter.writeShort((short)0));
  531. }
  532. bos.write(EndianConverter.writeShort((short)0x41));
  533. }
  534. }
  535. catch (IOException ioe) {
  536. // Should never occur for a memory based stream
  537. }
  538. return bos.toByteArray();
  539. }
  540. /*
  541. * This method handles the calculation of correct values for line lengths
  542. * in each individual descriptor and the number of lines in the document.
  543. *
  544. * TODO: Update to take account of different font metrics.
  545. */
  546. private void postProcessText() {
  547. /*
  548. * The post-processing ...
  549. *
  550. * For each line, we need to add a line descriptor and increment
  551. * the number of lines in the paragraph data structure.
  552. *
  553. * To do this, make sure that no sequence goes over the given screen
  554. * width unless the last char is a whitespace character.
  555. */
  556. // In courier, can have no more than 29 chars per line
  557. int chunkStart = 0;
  558. StringBuffer sb = new StringBuffer("");
  559. // Line Descriptor info should be eliminated each time
  560. lineDescriptors = new Vector(1, 1);
  561. lines = 0;
  562. for (int i = 0; i < textSegments.size(); i++) {
  563. ParagraphTextSegment pts = (ParagraphTextSegment)textSegments.elementAt(i);
  564. sb.append(pts.getText());
  565. }
  566. if (sb.length() == 0) {
  567. lines = 1;
  568. lineDescriptors.add(new LineDescriptor((short)1, (short)0));
  569. return;
  570. }
  571. while (chunkStart < sb.length()) {
  572. String text = "";
  573. try {
  574. text = sb.substring(chunkStart, chunkStart + 30);
  575. }
  576. catch (StringIndexOutOfBoundsException sioobe) {
  577. // We have less than one line left so just add it
  578. text = sb.substring(chunkStart);
  579. lineDescriptors.add(new LineDescriptor((short)(text.length() + 1), (short)(text.length() * 36)));
  580. chunkStart += text.length();
  581. lines++;
  582. continue;
  583. }
  584. int lastWhitespace = -1;
  585. for (int i = 29; i >= 0; i--) {
  586. if (Character.isWhitespace(text.charAt(i))) {
  587. lastWhitespace = i;
  588. break;
  589. }
  590. }
  591. if (lastWhitespace != -1) {
  592. // The line can be split
  593. lineDescriptors.add(new LineDescriptor((short)(lastWhitespace + 1), (short)(lastWhitespace * 36)));
  594. chunkStart += lastWhitespace + 1;
  595. lines++;
  596. }
  597. else {
  598. // The line is completely occupied by a single word
  599. lineDescriptors.add(new LineDescriptor((short)29, (short)(29 * 36)));
  600. chunkStart += 29;
  601. lines++;
  602. }
  603. }
  604. }
  605. /**
  606. * <p>Returns the number of lines in the <code>Paragraph</code>.</p>
  607. *
  608. * @return The number of lines in the document.
  609. */
  610. public short getLines() {
  611. postProcessText();
  612. return lines;
  613. }
  614. /**
  615. * <p>Toggles the flag indicating that the <code>Paragraph</code> is a
  616. * bulleted paragraph.</p>
  617. *
  618. * @param isBulleted true to enable bulleting for this paragraph, false
  619. * otherwise.
  620. */
  621. public void setBullets(boolean isBulleted) {
  622. if (isBulleted) {
  623. bullets = (byte)0xFF;
  624. }
  625. else {
  626. bullets = 0;
  627. }
  628. }
  629. /**
  630. * <p>Returns the bulleting status of the <code>Paragraph</code>.</p>
  631. *
  632. * @return true if the paragraph is bulleted, false otherwise.
  633. */
  634. public boolean isBulleted() {
  635. if (bullets != 0) {
  636. return true;
  637. }
  638. return false;
  639. }
  640. /**
  641. * <p>Returns the number of text characters in the <code>Paragraph</code>,
  642. * excluding formatting.</p>
  643. *
  644. * @return The length of the paragraph.
  645. */
  646. public int getTextLength () {
  647. return textLength;
  648. }
  649. /**
  650. * <p>Returns an <code>Enumeration</code> over the individual text segments
  651. * of the <code>Paragraph</code>.</p>
  652. *
  653. * @return An <code>Enumeration</code> of the text segments.
  654. */
  655. public Enumeration getSegmentsEnumerator () {
  656. return textSegments.elements();
  657. }
  658. /**
  659. * <p>Returns a paragraph style object that describes any of the paragraph
  660. * level formatting used by this <code>Paragraph</code>.</p>
  661. *
  662. * @return Paragraph style object describing the <code>Paragraph</code>.
  663. */
  664. public ParaStyle makeStyle() {
  665. int attrs[] = new int[] { ParaStyle.MARGIN_LEFT, ParaStyle.MARGIN_RIGHT,
  666. ParaStyle.TEXT_ALIGN };
  667. String values[] = new String[attrs.length];
  668. /*
  669. * Not interested in left or right indents just yet. Don't know
  670. * how to calculate them.
  671. */
  672. switch (alignment) {
  673. case 2:
  674. values[2] = "center";
  675. break;
  676. case 1:
  677. values[2] = "right";
  678. break;
  679. case 0:
  680. default:
  681. values[2] = "left";
  682. return null; // Not interested if its the default.
  683. }
  684. return new ParaStyle(null, PARAGRAPH_STYLE_FAMILY, null, attrs,
  685. values, null);
  686. }
  687. /*
  688. * Class describing the data structures which appear following the text
  689. * of a Paragraph. For each line on screen that the Paragraph uses, a
  690. * LineDescriptor details how many characters are on the line and how much
  691. * screen space they occupy.
  692. *
  693. * The screen space and character breaks are calculated during post-processing
  694. * of the paragraph. See postProcessText().
  695. *
  696. * The unit of measurement used for screen space is currently unknown.
  697. */
  698. private class LineDescriptor {
  699. private short characters = 0;
  700. private int filler = 0;
  701. private short screen_space = 0;
  702. private short marker = 0;
  703. private LineDescriptor(short chars, short space) {
  704. characters = chars;
  705. screen_space = space;
  706. marker = (short)0x040C; // Not a constant. Depends on font used.
  707. }
  708. private byte[] getDescriptorInfo(){
  709. ByteArrayOutputStream bos = new ByteArrayOutputStream();
  710. try {
  711. bos.write(EndianConverter.writeShort(characters));
  712. bos.write(EndianConverter.writeInt(filler));
  713. bos.write(EndianConverter.writeShort(screen_space));
  714. bos.write(EndianConverter.writeShort(marker));
  715. }
  716. catch (IOException ioe) {
  717. // Should never happen in a memory based stream.
  718. }
  719. return bos.toByteArray();
  720. }
  721. }
  722. }