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

/xmerge/java/org/openoffice/xmerge/converter/xml/sxw/pocketword/Paragraph.java

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