Package com.extentech.formats.XLS

Source Code of com.extentech.formats.XLS.Note

/*
* --------- BEGIN COPYRIGHT NOTICE ---------
* Copyright 2002-2012 Extentech Inc.
* Copyright 2013 Infoteria America Corp.
*
* This file is part of OpenXLS.
*
* OpenXLS is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as
* published by the Free Software Foundation, either version 3 of
* the License, or (at your option) any later version.
*
* OpenXLS is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with OpenXLS.  If not, see
* <http://www.gnu.org/licenses/>.
* ---------- END COPYRIGHT NOTICE ----------
*/
package com.extentech.formats.XLS;

import java.util.ArrayList;

import com.extentech.ExtenXLS.ExcelTools;
import com.extentech.formats.OOXML.Text;
import com.extentech.toolkit.ByteTools;
import com.extentech.toolkit.Logger;


/** <b>Note: A cell annotation (1CDh)</b><br>

    Note records contain the row/col position of the annotation, plus an ordinal id that links it to the
    set of records that define it:  Mso/obj/mso/Txo/Continue/Continue.  These record sets appear one after
    another after the DIMENSIONS record and before WINDOW2.  After the associated records, all Note records
    appear, in order.
   
   
    * Kaia's notes on notes:
*
* The Note record is the last of a set of records necessary to define each note:
*  Mso - shapeType= msosptTextBox.  See Msodrawing.createTextBoxStyle for a list of the necessary sub-records
*  Obj containing an ftNts sub-record
*  Mso - odd Mso cotnaining only 8 bytes - see Msodrawing.getAttachedTextPrototype
*  Txo - contains the size of the text + options such as text rotation + size of the formatting runs
*  Continue - 0 + text
*  Continue - formatting runs for text
*
*  For each note, the above records repeat, then each Note record appears in order after all of the above recs
*  Notes appear to have an ordinal id that is necessary for proper display
*  It's hidden/shown state appears to be defined by byte 5 but there is also other necessary info that I don't know yet
*
*  The tricky part of Notes is the Mso msosptTextBox plus the Mso msosptClientTextBox; they don't follow the
*  normal rules we are used to - the SPCONTAINERLENGTH of the 1st Mso MUST BE inc by 8, the length of the
*  second Mso ... but when multiple Notes are on the same sheet, that logic doesn't seem to be followed 100%
*  plus the number of shapes of the mso header is NOT incremented by the secondary Mso ...
*     
*  Also, the Mso's don't appear to follow the normal rules for the drawing Id - seem to be repeated ...
*  Lastly, the 1st Mso contains an OPT sub-record msofbtlTxid; this contains the host-defined id for the note;
*  I'm not sure what to set for this so I put a random int.
*   More info:
*  
*   Some templates define notes differently; instead of the above set of records, it goes
*   Obj (ftNts)
*   Continue [00, 00, (byte)0x0D, (byte)0xF0, 00, 00, 00, 00]  --> masks the 2nd mso
*   Txo
*   Continue
*   Continue
*   Continue --> masks the 1st mso
*
*   Also:
*   formatting runs: This is almost finished but - of course - doc on formatting runs format doesn't appear to match reality ((:)
*   Also:
*   There needs to be a good, easy interface for setting formatting runs for a text string
*  
    
    offset  name        size    contents
    ---       
  0     2 Index to row
  2     2 Index to column
    4    2 grbit controls hidden state - 0=hidden, 2=shown
    6    2 ordinal id MUST MATCH Obj record id
    8    1 lenght of author string or /2 if encoding=1
  9       1 encoding 0= non-unicode? 1= unicode?   
    10    (var) 0 + author bytes
           (author encoding)
    </p></pre>
    * @see Note
*/
public final class Note extends com.extentech.formats.XLS.XLSRecord {

 
    /**
   *
   *
   */
  private short id;
  boolean hidden= true// default
  private String author;
  private byte auth_encoding;
  private MSODrawing mso= null;
  private static final long serialVersionUID = -1571461658267879478L;
  // pointer to associated Txo, stores actual text + formatting runs
  Txo txo= null;

  public void init(){
        super.init();       
        rw= ByteTools.readShort(this.getData()[0],this.getData()[1]);
        col= ByteTools.readShort(this.getData()[2],this.getData()[3]);
        hidden= (this.getData()[4]!=(byte)2)// not entirely sure of this
        // bytes 5 unknnown
        id= ByteTools.readShort(this.getData()[6],this.getData()[7]);
        // rest are fairy known :)
        short authorlen= this.getData()[8];
        auth_encoding= this.getData()[9];
        byte[] authorbytes= new byte[authorlen];
        System.arraycopy(this.getData(), 11, authorbytes, 0, authorlen);
        if (auth_encoding==0)
          author= new String(authorbytes);
        else
          try {
            author= new String(authorbytes, WorkBookFactory.UNICODEENCODING);
          } catch (Exception e) { ; }
    }
 
  public void setSheet(Sheet bs) {
    super.setSheet(bs);
        txo= getAssociatedTxo();   
  }
 
  /**
   * Is this a useful to string?  possibly just the note itself, at a minimum include it
   * @see com.extentech.formats.XLS.XLSRecord#toString()
   */
  public String toString() {
    String s= "";
    if (txo!=null)
      s= txo.getStringVal();
    if (author!=null)
      s+= " author:" + author;
    return "NOTE at [" + this.getCellAddressWithSheet() + "]: " + s;
  }
  
    public static XLSRecord getPrototype(String author) {
      Note n = new Note();
        n.setOpcode(NOTE);     
        /*
         *  $row, $col, $visible, $obj_id,
                                 $num_chars, $author_enc
        $length     = length($data) + length($author);
        my $header  = pack("vv", $record, $length);
         */
        byte[] authbytes= author.getBytes();
        byte[] data= new byte[n.PROTOTYPE_BYTES.length + authbytes.length + 1];
        data[8]= (byte) author.length();
        // encoding= 0
        System.arraycopy(authbytes, 0, data, 11, authbytes.length);
        n.setData(data);       
        n.init()
        return n;
    }
    private byte[] PROTOTYPE_BYTES = new byte[] {0, 0,   /* row */
                           0, 0,   /* col */
                           0,    /* hidden state */
                           0,    /* unknown */
                           1, 0/* ordinal id */
                           4,      /* length of author */
                           0,     /* string encoding*/
                           //    /* 0-padded author string*/
                          };

    /**
     * set the row and column that this note is attached to
     * @param row
     * @param col
     */
    public void setRowCol(int row, int col) {
      this.rw= (short)row;
      this.col= (short)col;
      byte[] b= ByteTools.shortToLEBytes((short) this.rw);
      this.getData()[0]= b[0];
      this.getData()[1]= b[1];
      b= ByteTools.shortToLEBytes(this.col);     
      this.getData()[2]= b[0];
      this.getData()[3]= b[1];
    }
   
    /**
     * return the ordinal id of this note
     * @param id
     */
    public void setId(int id) {
      this.id= (short)id;
      byte[] b= ByteTools.shortToLEBytes(this.id);
      this.getData()[6]= b[0];
      this.getData()[7]= b[1];
    }
   
    /**
     * retrieve the ordinal id of this note
     * @return
     */
    public int getId() {
      return this.id;
    }
   
    /**
     * returns true if this note is hidden (default state)
     * @return
     */
    public boolean getHidden() {
      return this.hidden;
    }
   
    /**
     * show or hide this note
     * <br>NOTE: this is still experimental
     * @param hidden
     */
    public void setHidden(boolean hidden) {
      this.hidden= hidden;
    if (mso==null)
      mso=this.getAssociatedMso();
      if (this.hidden) {  // hide
        this.getData()[4]= 0;
        // ALSO MUST SET the associated Mso opt subrec to actually hide the note textbox ...
          mso.setOPTSubRecord(MSODrawingConstants.msooptGroupShapeProperties, false, false, 131074, null);     
      } else {      //show
        this.getData()[4]= 2;
        // ALSO MUST SET the associated Mso opt subrec to actually show the note textbox ...
          mso.setOPTSubRecord(MSODrawingConstants.msooptGroupShapeProperties, false, false, 131072, null);     
      }       
    }

    /**
     * returns the Text of this Note or Comment
     * @return String
     */
    public String getText() {
      if (txo!=null)
        return txo.getStringVal();
      return null;
    }
   
    /**
     * sets the Text of this Note or Comment
     * @param txt
     */
    public void setText(String txt) {
      if (txo!=null) {
        try {
          txo.setStringVal(txt);
        } catch (IllegalArgumentException e) {
        Logger.logErr(e.toString());
      }
      }     
    }

    /**
  /** set the text of this Note (Comment) as a unicode string,
   * with formatting information
     * @param txt
     */
    public void setText(Unicodestring txt) {
      if (txo!=null) {
        try {
          txo.setStringVal(txt);
        } catch (IllegalArgumentException e) {
        Logger.logErr(e.toString());
      }
      }
     
    }
   
    /**
     * return the author of this note, if set
     * @return
     */
    public String getAuthor() {
      return author;
    }
   
    /** sets the author of this note
     * @param author
     */
    public void setAuthor(String author) {
      this.author= author;
        byte[] authbytes= this.author.getBytes();
        byte[] oldData = this.getData();
        byte[] newData= new byte[authbytes.length + 11];
        System.arraycopy(oldData, 0, newData, 0, 8);
        newData[8]= (byte) author.length();
        // encoding= 0
        System.arraycopy(authbytes, 0, newData, 11, authbytes.length);
        this.setData(newData);       
        this.init()
    }
    /**
     * returns the Txo associated with this Note
     * @return
     */
    private Txo getAssociatedTxo() {
      Boundsheet bs= this.getSheet();
      int idx= -1;
    if (bs!=null) {// shouldn't!     
      idx= bs.getIndexOf(OBJ);
      if (idx==-1) return null// should't!
      while (idx < bs.getSheetRecs().size()) {
        if (((BiffRec)bs.getSheetRecs().get(idx)).getOpcode()==OBJ) {
          Obj o= ((Obj)bs.getSheetRecs().get(idx));
          // if it's of type Note + has the same id, this is it
          if (o.getObjType()==0x19 &&  o.getObjId()==this.id) { // got it!
            // now find the next TXO
            idx++;
            while (idx < bs.getSheetRecs().size() &&
                (((BiffRec)bs.getSheetRecs().get(idx))).getOpcode()!=TXO)
              idx++;
            break;
          }
        }
        idx++;
      }       
    }
    if (idx < bs.getSheetRecs().size())
      return (Txo) bs.getSheetRecs().get(idx);
    return null;
    }
       
    /**
     * returns the bounds (size and position) of the Text Box for this Note
   * <br>bounds are relative and based upon rows, columns and offsets within
   * <br>bounds are as follows:
    <br>bounds[0]= column # of top left position (0-based) of the shape
    <br>bounds[1]= x offset within the top-left column (0-1023)
    <br>bounds[2]= row # for top left corner
    <br>bounds[3]= y offset within the top-left corner  (0-1023)
    <br>bounds[4]= column # of the bottom right corner of the shape
    <br>bounds[5]= x offset within the cell  for the bottom-right corner (0-1023)
    <br>bounds[6]= row # for bottom-right corner of the shape
    <br>bounds[7]= y offset within the cell for the bottom-right corner (0-1023)
     * @return
     */
    public short[] getTextBoxBounds() {
    if (mso==null)
      mso=this.getAssociatedMso();
      return mso.getBounds();
    }
   
    /**
     * sets the bounds (size and position) of the Text Box for this Note
   * <br>bounds are relative and based upon rows, columns and offsets within
   * <br>bounds are as follows:
    <br>bounds[0]= column # of top left position (0-based) of the shape
    <br>bounds[1]= x offset within the top-left column (0-1023)
    <br>bounds[2]= row # for top left corner
    <br>bounds[3]= y offset within the top-left corner  (0-1023)
    <br>bounds[4]= column # of the bottom right corner of the shape
    <br>bounds[5]= x offset within the cell  for the bottom-right corner (0-1023)
    <br>bounds[6]= row # for bottom-right corner of the shape
    <br>bounds[7]= y offset within the cell for the bottom-right corner (0-1023)
     * @param bounds
     */
    public void setTextBoxBounds(short[] bounds) {
    if (mso==null)
      mso=this.getAssociatedMso();
      mso.setBounds(bounds);
    }
   
    /**
     * sets the text box width for the note in pixels
     * @param width
     * @return short[] end column and end column offset
     */
    public void setTextBoxWidth(short width) {
      if (mso==null)
        mso=this.getAssociatedMso();
      mso.setWidth((short) Math.round(width/6.4))// convert pixels to points
    }
   
   
    /**
     * sets the text box width for the note in pixels
     * @param width
     * @return short[] end column and end column offset
     */
    public void setTextBoxHeight(short height) {
      if (mso==null)
        mso=this.getAssociatedMso();
      mso.setHeight((short) Math.round(height))
    }
   
   
    /**
     * Store formatting runs for this Note (Comment)
     * <br>Formatting Runs are an Internal Structure and are not relavent to the end user
     * @param formattingRuns
     */
    public void setFormattingRuns(ArrayList formattingRuns) {
      if (txo!=null)
        txo.setFormattingRuns(formattingRuns);
    }
   
    /**
     * Method to retrieve formatting runs for this Note (Comment)
     * <br>Formatting Runs are an Internal Structure and are not relavent to the end user
     * @param formattingRuns
     */
    public ArrayList getFormattingRuns() {
      if (txo!=null)
        txo.getFormattingRuns();
      return null;
    }
    /**
     * get the id of drawing object which defines the text box for this Note
     * <br>For Internal Use Only
     * @return
     */
    public int getSPID() {
    if (mso==null)
      mso=this.getAssociatedMso();
      return mso.getSPID();
    }
   
    private MSODrawing getAssociatedMso() {
      Boundsheet bs= this.getSheet();
      int idx= -1;
    if (bs!=null) {// shouldn't!     
      idx= bs.getIndexOf(OBJ);
      if (idx==-1) return null// should't!
      while (idx < bs.getSheetRecs().size()) {
        if (((BiffRec)bs.getSheetRecs().get(idx)).getOpcode()==OBJ) {
          Obj o= ((Obj)bs.getSheetRecs().get(idx));
          // if it's of type Note + has the same id, this is it
          if (o.getObjType()==0x19 &&  o.getObjId()==this.id) { // got it!
            // first check if this is one of the odd configurations
            int opcodeprev= ((BiffRec)bs.getSheetRecs().get(idx-1)).getOpcode();
            int opcodenext= ((BiffRec)bs.getSheetRecs().get(idx+1)).getOpcode();
            if (opcodeprev==MSODRAWING// normal case: mso, obj (note), mso, txo, continue, continue
              return (MSODrawing) bs.getSheetRecs().get(idx-1);
            else if (opcodenext==CONTINUE)  {// continue masking mso - AFTER object record
              if (idx+5 < bs.getSheetRecs().size())
                return (((Continue)bs.getSheetRecs().get(idx+5))).maskedMso;  // the 1st mso is actually at position 5: Obj (note)/Continue/Txo/Continue/Continue/Continue
            }
          }
        }
        idx++;
      }       
    }
    return null;
    }
   
    /**
     * returns the OOXML representation of this Note
     * @return
     */
    public String getOOXML() {
      if (txo==null) return "";
        Text t= new Text(Sst.createUnicodeString(this.getText(), txo.getFormattingRuns(), Sst.STRING_ENCODING_UNICODE));
        return t.getOOXML(this.getWorkBook());
    }
}
TOP

Related Classes of com.extentech.formats.XLS.Note

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.