| /************************************************************** |
| * |
| * Licensed to the Apache Software Foundation (ASF) under one |
| * or more contributor license agreements. See the NOTICE file |
| * distributed with this work for additional information |
| * regarding copyright ownership. The ASF licenses this file |
| * to you under the Apache License, Version 2.0 (the |
| * "License"); you may not use this file except in compliance |
| * with the License. You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, |
| * software distributed under the License is distributed on an |
| * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY |
| * KIND, either express or implied. See the License for the |
| * specific language governing permissions and limitations |
| * under the License. |
| * |
| *************************************************************/ |
| |
| |
| |
| package org.openoffice.xmerge.converter.xml.sxw.pocketword; |
| |
| import java.io.ByteArrayOutputStream; |
| import java.io.IOException; |
| |
| import java.util.Vector; |
| import java.util.Enumeration; |
| |
| import java.awt.Color; |
| |
| import org.openoffice.xmerge.util.EndianConverter; |
| import org.openoffice.xmerge.util.ColourConverter; |
| import org.openoffice.xmerge.converter.xml.ParaStyle; |
| import org.openoffice.xmerge.converter.xml.TextStyle; |
| |
| |
| /** |
| * Represents a paragraph data structure within a Pocket Word document. |
| * |
| * @author Mark Murnane |
| * @version 1.1 |
| */ |
| class Paragraph implements PocketWordConstants { |
| /* |
| * The data elements of a Paragraph. |
| * |
| * As the 'unknown' values are not calculated they are declared static. |
| * They are not declared final because they do have a calcuable value. |
| */ |
| private static short unknown1 = 0x23; |
| private short dataWords = 0; |
| private short textLength = 0; |
| private short lengthWithFormatting = 0; |
| private short lines = 0; |
| |
| private static final short marker = (short)0xFFFF; |
| private static int unknown2 = 0x22; // May be two short values |
| |
| private short specialIndentation = 0; |
| private short leftIndentation = 0; |
| private short rightIndentation = 0; |
| |
| private byte bullets = 0; |
| private byte alignment = 0; |
| |
| private static int unknown3 = 0; |
| |
| // Will always have at least these formatting settings in each paragraph |
| private short defaultFont = 2; // Courier New for the time being |
| private short defaultSize = 10; |
| |
| |
| /* |
| * Remaining elements assist in calculating correct values for the paragraph |
| * representation. |
| */ |
| |
| private Vector textSegments = null; |
| |
| private Vector lineDescriptors = null; |
| |
| private ParaStyle pStyle = null; |
| |
| private boolean isLastParagraph = false; |
| |
| |
| /* |
| * Private class constructor used by all constructors. Ensures the proper |
| * initialisation of the Vector storing the paragraph's text. |
| */ |
| private Paragraph () { |
| textSegments = new Vector(0, 1); |
| } |
| |
| |
| /** |
| * <p>Constructor for use when converting from SXW format to Pocket Word |
| * format.</p> |
| * |
| * @param style Paragraph style object describing the formatting style |
| * of this paragraph. |
| */ |
| public Paragraph (ParaStyle style) { |
| this(); |
| |
| lineDescriptors = new Vector(0, 1); |
| pStyle = style; |
| } |
| |
| |
| /** |
| * <p>Constructor for use when converting from Pocket Word format to SXW |
| * format.</p> |
| * |
| * @param data Byte array containing byte data describing this paragraph |
| * from the Pocket Word file. |
| */ |
| public Paragraph (byte[] data) { |
| this(); |
| |
| /* |
| * Read in all fixed data from the array |
| * |
| * unknown1 appears at data[0] and data[1] |
| */ |
| dataWords = EndianConverter.readShort(new byte[] { data[2], data[3] } ); |
| textLength = EndianConverter.readShort(new byte[] { data[4], data [5] } ); |
| lengthWithFormatting = EndianConverter.readShort( |
| new byte[] { data[6], data[7] } ); |
| lines = EndianConverter.readShort(new byte[] { data[8], data [9] } ); |
| |
| /* |
| * The marker appears at data[10] and data[11]. |
| * |
| * The value of unknown2 is at data[12], data[13], data[14] and data[15]. |
| */ |
| |
| specialIndentation = EndianConverter.readShort(new byte[] { data[16], data[17] } ); |
| leftIndentation = EndianConverter.readShort(new byte[] { data[18], data [19] } ); |
| rightIndentation = EndianConverter.readShort(new byte[] { data[20], data [21] } ); |
| |
| bullets = data[22]; |
| alignment = data[23]; |
| |
| // The value of unknown3 is at data[24], data[25], data[26] and data[27]. |
| |
| /* |
| * The actual paragraph data is in the remainder of the byte sequence. |
| * |
| * Only the actual text seqence with the embedded formatting tags is |
| * relevant to the conversion from Pocket Word to SXW format. |
| */ |
| ByteArrayOutputStream bos = new ByteArrayOutputStream(); |
| bos.write(data, 28, lengthWithFormatting); |
| parseText(bos.toByteArray()); |
| } |
| |
| |
| /* |
| * Processes the text portion of the raw paragraph data from the Pocket Word |
| * file. This data also includes formatting settings for the text in the |
| * paragraph. |
| * |
| * Formatting changes appear like XML/HTML tags. Formatted blocks are |
| * preceded by a sequence of bytes switching on a formatting change and |
| * followed by a sequence switching off that formatting change. |
| */ |
| private void parseText (byte[] data) { |
| |
| int totalLength = data.length; |
| |
| StringBuffer sb = new StringBuffer(""); |
| |
| // Setup text style information |
| int mask = TextStyle.BOLD | TextStyle.ITALIC | TextStyle.UNDERLINE |
| | TextStyle.STRIKETHRU; |
| |
| |
| String fontName = null; |
| int fontSize = 0; |
| Color textColour = null; |
| Color backColour = null; |
| int modifiers = 0; |
| |
| TextStyle ts = null; |
| |
| int attrsSet = 0; // If this is 0, we have no extra style |
| boolean inSequence = false; |
| boolean sawText = false; |
| |
| String s = new String(); // For debugging |
| |
| // Start from the very beginning |
| for (int i = 0; i < totalLength; i++) { |
| // Will encounter at least two codes first |
| if ((byte)(data[i] & 0xF0) == FORMATTING_TAG) { |
| if (sawText) { |
| // Style change so dump previous segment and style info |
| addTextSegment(sb.toString(), ts); |
| sb = new StringBuffer(""); |
| sawText = false; |
| } |
| |
| switch (data[i]) { |
| case FONT_TAG: |
| int index = EndianConverter.readShort( |
| new byte[] { data[i + 1], data[i + 2] } ); |
| |
| /* |
| * Standard font. |
| * |
| * Should really be one, but as the only supported font |
| * currently is Courier New, want to leave it at Courier |
| * New for round trip conversions. |
| * |
| * Also need to account for the fact that Tahoma is the |
| * correct standard font. |
| */ |
| if (fontName == null || fontName.equals("2")) { |
| if (index != 2 && index != 1) { |
| fontName = String.valueOf(index); |
| attrsSet++; |
| } |
| } |
| else { |
| // Font is set, but not the default |
| if (index == 2 || index == 1) { |
| fontName = "2"; |
| attrsSet--; |
| } |
| else { |
| fontName = String.valueOf(index); |
| } |
| } |
| i += 2; |
| break; |
| |
| |
| case FONT_SIZE_TAG: |
| int size = EndianConverter.readShort( |
| new byte[] { data[i + 1], data[i + 2] } ); |
| |
| if (size == 0) { |
| // Flags the end of the last paragraph |
| isLastParagraph = true; |
| i += 2; |
| break; |
| } |
| |
| // Standard size |
| if (fontSize == 0 || fontSize == 10) { |
| if (size != 10) { |
| fontSize = size; |
| attrsSet++; |
| } |
| } |
| else { |
| // Font size is set, but not to standard |
| if (size == 10) { |
| fontSize = 10; |
| attrsSet--; |
| } |
| else { |
| fontSize = size; |
| } |
| } |
| i += 2; |
| break; |
| |
| |
| case COLOUR_TAG: |
| if (data[i + 1] != 0) { |
| ColourConverter cc = new ColourConverter(); |
| textColour = cc.convertToRGB( |
| EndianConverter.readShort(new byte[] { data[i + 1], |
| data[i + 2] } )); |
| attrsSet++; |
| } |
| else { |
| textColour = null; |
| attrsSet--; |
| } |
| i += 2; |
| break; |
| |
| |
| case FONT_WEIGHT_TAG: |
| if (data[i + 1] == FONT_WEIGHT_BOLD |
| || data[i + 1] == FONT_WEIGHT_THICK) { |
| modifiers |= TextStyle.BOLD; |
| attrsSet++; |
| } |
| else { |
| // Its a bit field so subtracting should work okay. |
| modifiers ^= TextStyle.BOLD; |
| attrsSet--; |
| } |
| i += 2; |
| break; |
| |
| |
| case ITALIC_TAG: |
| if (data[i + 1] == (byte)0x01) { |
| modifiers |= TextStyle.ITALIC; |
| attrsSet++; |
| } |
| else { |
| modifiers ^= TextStyle.ITALIC; |
| attrsSet--; |
| } |
| i++; |
| break; |
| |
| |
| case UNDERLINE_TAG: |
| if (data[i + 1] == (byte)0x01) { |
| modifiers |= TextStyle.UNDERLINE; |
| attrsSet++; |
| } |
| else { |
| modifiers ^= TextStyle.UNDERLINE; |
| attrsSet--; |
| } |
| i++; |
| break; |
| |
| |
| case STRIKETHROUGH_TAG: |
| if (data[i + 1] == (byte)0x01) { |
| modifiers |= TextStyle.STRIKETHRU; |
| attrsSet++; |
| } |
| else { |
| modifiers ^= TextStyle.STRIKETHRU; |
| attrsSet--; |
| } |
| i++; |
| break; |
| |
| case HIGHLIGHT_TAG: |
| /* |
| * Highlighting is treated by OpenOffice as a |
| * background colour. |
| */ |
| if (data[i + 1] == (byte)0x01) { |
| backColour = Color.yellow; |
| attrsSet++; |
| } |
| else { |
| backColour = null; |
| attrsSet--; |
| } |
| i++; |
| break; |
| } |
| |
| inSequence = true; |
| continue; |
| } |
| |
| if (inSequence) { |
| // Style information has been changed. Create new style here |
| |
| inSequence = false; |
| if (attrsSet > 0) { |
| ts = new TextStyle(null, TEXT_STYLE_FAMILY, DEFAULT_STYLE, |
| mask, modifiers, fontSize, fontName, null); |
| ts.setColors(textColour, backColour); |
| } |
| else { |
| ts = null; |
| } |
| } |
| |
| /* |
| * C4 xx seems to indicate a control code. C4 00 indicates the end |
| * of a paragraph; C4 04 indicates a tab space. Only these two |
| * have been seen so far. |
| */ |
| if (data[i] == (byte)0xC4) { |
| /* |
| * Redundant nodes are sometimes added to the last paragraph |
| * because a new sequence is being processed when the flag is |
| * set. |
| * |
| * To avoid this, do nothing with the last paragraph unless no |
| * text has been added for it already. In that case, add the |
| * empty text segment being process to ensure that all |
| * paragraphs have at least one text segment. |
| */ |
| if (data[i + 1] == (byte)0x00) { |
| if (isLastParagraph && textSegments.size() > 0) { |
| return; |
| } |
| addTextSegment(sb.toString(), ts); |
| return; |
| } |
| sb.append("\t"); |
| sawText = true; |
| i++; |
| continue; |
| } |
| |
| sb.append((char)data[i]); |
| sawText = true; |
| s = sb.toString(); |
| } |
| } |
| |
| |
| /** |
| * <p>Adds details of a new text block to the <code>Paragraph</code> object. |
| * </p> |
| * |
| * @param text The text of the new block. |
| * @param style Text style object describing the formatting attached |
| * to this block of text. |
| */ |
| public void addTextSegment(String text, TextStyle style) { |
| textLength += text.length(); |
| textSegments.add(new ParagraphTextSegment(text, style)); |
| } |
| |
| |
| /** |
| * <p>This method alters the state of the <code>Paragraph</code> object to |
| * indicate whether or not it is the final paragraph in the document.</p> |
| * |
| * <p>It is used during conversion from SXW format to Pocket Word format. |
| * In Pocket Word files, the last paragraph finishes with a different byte |
| * sequence to other paragraphs.</p> |
| * |
| * @param isLast true if the Paragraph is the last in the document, |
| * false otherwise. |
| */ |
| public void setLastParagraph(boolean isLast) { |
| isLastParagraph = isLast; |
| } |
| |
| |
| /** |
| * <p>Complementary method to {@link #setLastParagraph(boolean) |
| * setLastParagraph}. Returns the terminal status of this |
| * <code>Paragraph</code> within the Pocket Word document.</p> |
| * |
| * @return true if the Paragraph is the last in the document; false otherwise. |
| */ |
| public boolean getLastParagraph () { |
| return isLastParagraph; |
| } |
| |
| |
| /** |
| * <p>This method returns the Pocket Word representation of this |
| * <code>Paragraph</code> in Little Endian byte order.</p> |
| * |
| * <p>Used when converting from SXW format to Pocket Word format.</p> |
| * |
| * @return <code>byte</code> array containing the formatted representation |
| * of this Paragraph. |
| */ |
| public byte[] getParagraphData() { |
| ByteArrayOutputStream bos = new ByteArrayOutputStream(); |
| |
| postProcessText(); |
| |
| /* |
| * Need information about the paragraph segments in two places |
| * so calculate them first. |
| * |
| * The stream contains the text wrapped in any formatting sequences that |
| * are necessary. |
| */ |
| ByteArrayOutputStream segs = new ByteArrayOutputStream(); |
| |
| try { |
| for (int i = 0; i < textSegments.size(); i++) { |
| ParagraphTextSegment pts = (ParagraphTextSegment)textSegments.elementAt(i); |
| segs.write(pts.getData()); |
| } |
| } |
| catch (IOException ioe) { |
| // Should never happen in a memory based stream |
| } |
| |
| /* |
| * Number of data words for this paragraph descriptor: |
| * |
| * 26 is the number of bytes prior to the start of the segment. |
| * 3 comes from the C4 00 00 termintating sequence. |
| */ |
| dataWords = (short)(26 + segs.size() + 3 + 4); |
| if (isLastParagraph) { |
| dataWords += 6; |
| } |
| if (dataWords % 4 != 0) { |
| dataWords += (4 - (dataWords % 4)); |
| } |
| dataWords /= 4; |
| |
| /* |
| * The 8 bytes are made up of E6 ?0 00 and E5 ?0 00 at the start of the |
| * text along with the C4 00 that terminates it. |
| * |
| * In the event that the paragraph is the last one E6 00 00 is also |
| * present at the end of the text. Also, as we currently use a font |
| * other than the first in the index (Tahoma) E5 01 00 is also present. |
| * |
| * Make sure this is accurate when font specifications change |
| */ |
| lengthWithFormatting = (short)(segs.size() + (isLastParagraph ? 14 : 8)); |
| |
| try { |
| bos.write(EndianConverter.writeShort(unknown1)); |
| bos.write(EndianConverter.writeShort(dataWords)); |
| bos.write(EndianConverter.writeShort((short)(textLength + 1))); |
| bos.write(EndianConverter.writeShort(lengthWithFormatting)); |
| bos.write(EndianConverter.writeShort(lines)); |
| |
| bos.write(EndianConverter.writeShort(marker)); |
| bos.write(EndianConverter.writeInt(unknown2)); |
| |
| bos.write(EndianConverter.writeShort(specialIndentation)); |
| bos.write(EndianConverter.writeShort(leftIndentation)); |
| bos.write(EndianConverter.writeShort(rightIndentation)); |
| |
| bos.write(bullets); |
| |
| if (pStyle != null && pStyle.isAttributeSet(ParaStyle.TEXT_ALIGN)) { |
| switch (pStyle.getAttribute(ParaStyle.TEXT_ALIGN)) { |
| |
| case ParaStyle.ALIGN_RIGHT: |
| bos.write(0x01); |
| break; |
| |
| case ParaStyle.ALIGN_CENTER: |
| bos.write(0x02); |
| break; |
| |
| default: |
| bos.write(0x00); // Left align in all other circumstances |
| break; |
| } |
| } |
| else { |
| bos.write(0x00); |
| } |
| |
| bos.write(EndianConverter.writeInt(unknown3)); |
| |
| |
| /* |
| * Write out font and size. |
| * |
| * If font support is added then this should change as the information |
| * will have to be calculated from a Font table. |
| */ |
| bos.write(FONT_TAG); |
| bos.write(EndianConverter.writeShort(defaultFont)); |
| bos.write(FONT_SIZE_TAG); |
| bos.write(EndianConverter.writeShort(defaultSize)); |
| |
| // Write out the text segments |
| bos.write(segs.toByteArray()); |
| |
| /* |
| * If this is the last paragraph in the document then we need to make |
| * sure that the paragraph text is terminated correctly with an E6 00 00 |
| * before the C4 00 00. |
| */ |
| if (isLastParagraph) { |
| if (defaultFont != 1) { |
| // Must always go back to the first font. |
| bos.write(FONT_TAG); |
| bos.write(EndianConverter.writeShort((short)0x01)); |
| } |
| bos.write(FONT_SIZE_TAG); |
| bos.write(EndianConverter.writeShort((short)0x00)); |
| } |
| |
| bos.write(new byte[] { (byte)0xC4, 0x00, 0x00 } ); |
| |
| int padding = 0; |
| if (bos.size() % 4 != 0) { |
| padding = 4 - (bos.size() % 4); |
| } |
| for (int i = 0; i < padding; i++) { |
| bos.write(0x00); |
| } |
| |
| // Third byte should match first byte after 0xFF 0xFF |
| bos.write(new byte[] { 0x42, 0x00, 0x22, 0x00} ); |
| |
| /* |
| * Meaning of last two bytes seems to be the number of words describing |
| * lines. This is calculated at 10 bytes per descriptor. |
| * |
| * May have two extra padding bytes that need to be accounted for too |
| * The division below may lose 2 bytes (integer result). |
| */ |
| int wordsRemaining = (lineDescriptors.size() * 10) / 4; |
| if ((lineDescriptors.size() * 10) % 4 != 0) { |
| wordsRemaining++; |
| } |
| bos.write(EndianConverter.writeShort((short)wordsRemaining)); |
| |
| |
| // Now write out the line descriptors |
| for (int i = 0; i < lineDescriptors.size(); i++) { |
| LineDescriptor ld = (LineDescriptor)lineDescriptors.elementAt(i); |
| |
| bos.write(ld.getDescriptorInfo()); |
| } |
| |
| |
| if (!isLastParagraph) { |
| /* |
| * There may be a need to pad this. Will be writing at |
| * either start of 4 byte block or 2 bytes into it. |
| */ |
| if (bos.size() % 4 != 2) { |
| bos.write(EndianConverter.writeShort((short)0)); |
| } |
| bos.write(EndianConverter.writeShort((short)0x41)); |
| } |
| } |
| catch (IOException ioe) { |
| // Should never occur for a memory based stream |
| } |
| |
| return bos.toByteArray(); |
| } |
| |
| |
| /* |
| * This method handles the calculation of correct values for line lengths |
| * in each individual descriptor and the number of lines in the document. |
| * |
| * TODO: Update to take account of different font metrics. |
| */ |
| private void postProcessText() { |
| /* |
| * The post-processing ... |
| * |
| * For each line, we need to add a line descriptor and increment |
| * the number of lines in the paragraph data structure. |
| * |
| * To do this, make sure that no sequence goes over the given screen |
| * width unless the last char is a whitespace character. |
| */ |
| |
| // In courier, can have no more than 29 chars per line |
| |
| int chunkStart = 0; |
| StringBuffer sb = new StringBuffer(""); |
| |
| // Line Descriptor info should be eliminated each time |
| lineDescriptors = new Vector(1, 1); |
| lines = 0; |
| |
| for (int i = 0; i < textSegments.size(); i++) { |
| ParagraphTextSegment pts = (ParagraphTextSegment)textSegments.elementAt(i); |
| sb.append(pts.getText()); |
| } |
| |
| if (sb.length() == 0) { |
| lines = 1; |
| lineDescriptors.add(new LineDescriptor((short)1, (short)0)); |
| return; |
| } |
| |
| while (chunkStart < sb.length()) { |
| String text = ""; |
| |
| try { |
| text = sb.substring(chunkStart, chunkStart + 30); |
| } |
| catch (StringIndexOutOfBoundsException sioobe) { |
| // We have less than one line left so just add it |
| text = sb.substring(chunkStart); |
| lineDescriptors.add(new LineDescriptor((short)(text.length() + 1), (short)(text.length() * 36))); |
| chunkStart += text.length(); |
| lines++; |
| continue; |
| } |
| |
| int lastWhitespace = -1; |
| |
| for (int i = 29; i >= 0; i--) { |
| if (Character.isWhitespace(text.charAt(i))) { |
| lastWhitespace = i; |
| break; |
| } |
| } |
| |
| if (lastWhitespace != -1) { |
| // The line can be split |
| lineDescriptors.add(new LineDescriptor((short)(lastWhitespace + 1), (short)(lastWhitespace * 36))); |
| chunkStart += lastWhitespace + 1; |
| lines++; |
| } |
| else { |
| // The line is completely occupied by a single word |
| lineDescriptors.add(new LineDescriptor((short)29, (short)(29 * 36))); |
| chunkStart += 29; |
| lines++; |
| } |
| } |
| } |
| |
| |
| /** |
| * <p>Returns the number of lines in the <code>Paragraph</code>.</p> |
| * |
| * @return The number of lines in the document. |
| */ |
| public short getLines() { |
| postProcessText(); |
| |
| return lines; |
| } |
| |
| |
| /** |
| * <p>Toggles the flag indicating that the <code>Paragraph</code> is a |
| * bulleted paragraph.</p> |
| * |
| * @param isBulleted true to enable bulleting for this paragraph, false |
| * otherwise. |
| */ |
| public void setBullets(boolean isBulleted) { |
| if (isBulleted) { |
| bullets = (byte)0xFF; |
| } |
| else { |
| bullets = 0; |
| } |
| } |
| |
| /** |
| * <p>Returns the bulleting status of the <code>Paragraph</code>.</p> |
| * |
| * @return true if the paragraph is bulleted, false otherwise. |
| */ |
| public boolean isBulleted() { |
| if (bullets != 0) { |
| return true; |
| } |
| return false; |
| } |
| |
| |
| /** |
| * <p>Returns the number of text characters in the <code>Paragraph</code>, |
| * excluding formatting.</p> |
| * |
| * @return The length of the paragraph. |
| */ |
| public int getTextLength () { |
| return textLength; |
| } |
| |
| |
| /** |
| * <p>Returns an <code>Enumeration</code> over the individual text segments |
| * of the <code>Paragraph</code>.</p> |
| * |
| * @return An <code>Enumeration</code> of the text segments. |
| */ |
| public Enumeration getSegmentsEnumerator () { |
| return textSegments.elements(); |
| } |
| |
| |
| /** |
| * <p>Returns a paragraph style object that describes any of the paragraph |
| * level formatting used by this <code>Paragraph</code>.</p> |
| * |
| * @return Paragraph style object describing the <code>Paragraph</code>. |
| */ |
| public ParaStyle makeStyle() { |
| int attrs[] = new int[] { ParaStyle.MARGIN_LEFT, ParaStyle.MARGIN_RIGHT, |
| ParaStyle.TEXT_ALIGN }; |
| String values[] = new String[attrs.length]; |
| |
| /* |
| * Not interested in left or right indents just yet. Don't know |
| * how to calculate them. |
| */ |
| |
| switch (alignment) { |
| case 2: |
| values[2] = "center"; |
| break; |
| |
| case 1: |
| values[2] = "right"; |
| break; |
| |
| case 0: |
| default: |
| values[2] = "left"; |
| return null; // Not interested if its the default. |
| } |
| |
| return new ParaStyle(null, PARAGRAPH_STYLE_FAMILY, null, attrs, |
| values, null); |
| } |
| |
| |
| /* |
| * Class describing the data structures which appear following the text |
| * of a Paragraph. For each line on screen that the Paragraph uses, a |
| * LineDescriptor details how many characters are on the line and how much |
| * screen space they occupy. |
| * |
| * The screen space and character breaks are calculated during post-processing |
| * of the paragraph. See postProcessText(). |
| * |
| * The unit of measurement used for screen space is currently unknown. |
| */ |
| private class LineDescriptor { |
| private short characters = 0; |
| private int filler = 0; |
| private short screen_space = 0; |
| private short marker = 0; |
| |
| private LineDescriptor(short chars, short space) { |
| characters = chars; |
| screen_space = space; |
| marker = (short)0x040C; // Not a constant. Depends on font used. |
| } |
| |
| |
| private byte[] getDescriptorInfo(){ |
| ByteArrayOutputStream bos = new ByteArrayOutputStream(); |
| |
| try { |
| bos.write(EndianConverter.writeShort(characters)); |
| bos.write(EndianConverter.writeInt(filler)); |
| bos.write(EndianConverter.writeShort(screen_space)); |
| bos.write(EndianConverter.writeShort(marker)); |
| } |
| catch (IOException ioe) { |
| // Should never happen in a memory based stream. |
| } |
| |
| return bos.toByteArray(); |
| } |
| } |
| } |