/*
* --------- 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.awt.Color;
import com.extentech.formats.OOXML.Fill;
import com.extentech.toolkit.ByteTools;
import com.extentech.toolkit.Logger;
import com.extentech.ExtenXLS.FormatHandle;
/** <b>XF: Extended Format (E0h)</b><br>
The XF record stores formatting properties.
If fStyle bit is true, then the XF is a style XF, otherwise
it is a BiffRec XF. Cells and Styles both contain ixfe pointers
which correspond to their associated XF record.
<pre>
BiffRec XF Record
offset Bits MASK name contents
---
0 15-0 0xFFFF ifnt Index to the FONT record.
2 15-0 0xFFFF ifmt Index to the FORMAT record.
4 0 0x0001 fLocked =1 if the cell is locked.
1 0x0002 fHidden =1 if the cell formula is hidden (value still shown)
2 0x0004 fStyle =0 for cell XF.
=1 for style XF.
~~~ additional option flags omitted ~~~
</pre>
* @see SST
* @see LABELSST
* @see EXTSST
*/
public class Xf extends com.extentech.formats.XLS.XLSRecord
{
private static final long serialVersionUID = -419388613530529316L;
int tableidx = -1;
// Shared variables.
private short ifnt;
private short ifmt;
private short fLocked;
private short fHidden;
private short fStyle;
// the records that are initialized to "0" are a 1/0 flag. Just makin my life easier...
private short f123Prefix = 0;
private short ixfParent;
private short alc;
private short fWrap = 0;
private short alcV;
// private short fJustLast; Used in Far East version of Excel only!!
private short cIndent;
private short trot;
// private short cIntednt;
private short fShrinkToFit = 0;
private short fMergeCell = 0;
private short iReadingOrder;
private short fAtrNum = 0;
private short fAtrFnt = 0;
private short fAtrAlc = 0;
private short fAtrBdr = 0;
private short fAtrPat = 0;
private short fAtrProt = 0;
private short dgLeft;
private short dgRight;
private short dgTop;
private short dgBottom;
private short icvLeft;
private short icvRight;
private short grbitDiag;
private short icvTop;
private short icvBottom;
private short icvDiag;
private short dgDiag;
private short fls;
private short icvFore;
private short icvBack;
private short fSxButton = 0;
private short icvColorFlag = 0;
int Iflag = 0;
byte mystery;
public final static int NDEFAULTXFS= 20;
private String pat = null;
/** OOXML fill, if any */
public Fill fill= null; // ugly that it's public ...
// These should only be populated for boundsheet transferral issues.
private Font myFont;
private Format myFormat;
private short useCount= 0; // KSC: added 20121003 to keep track of xf usage by biffrecs
public Xf(){
//empty constructor
}
/** create a new Xf with pointer to its font
*/
public Xf(int f){
byte[] bl = { 0, 0, 0, 0, 1, 0, 32, 0, 0, 64, 0, 0, 0, 0, 0, 0, 0, 0, -64, 32};
this.setOpcode(XF);
this.setLength((short)(bl.length));
this.setData(bl);
this.setFont(f);
this.init();
}
/** create a new Xf with pointer to its font and workbook set
*/
public Xf(int f, WorkBook wkbook){
byte[] bl = { 0, 0, 0, 0, 1, 0, 32, 0, 0, 64, 0, 0, 0, 0, 0, 0, 0, 0, -64, 32};
this.setOpcode(XF);
this.setLength((short)(bl.length));
this.setData(bl);
super.setWorkBook(wkbook); // set workbook but don't insert rec or add to xfrecs
this.setFont(f);
this.init();
}
/**
* constructor which takes a Font object + a workbook
* useful for cloning xf's from other workbooks
* @param f font
* @param wkbook
*/
public Xf(Font f, WorkBook wkbook) {
byte[] bl = { 0, 0, 0, 0, 1, 0, 32, 0, 0, 64, 0, 0, 0, 0, 0, 0, 0, 0, -64, 32};
this.setOpcode(XF);
this.setLength((short)(bl.length));
myFont= f;
System.arraycopy(ByteTools.shortToLEBytes((short) f.getIdx()), 0, bl, 0, 2);
this.setData(bl);
super.setWorkBook(wkbook); // set workbook but don't insert rec or add to xfrecs
this.init();
}
/**
* Set the workbook for this XF
*
* This can get called multiple times. This results in a disparity within
* xf counting in workbook.
*/
public void setWorkBook(WorkBook b){
super.setWorkBook(b);
}
/**
* Create a string representation of the Xf
*/
public String toString(){
String f= "unknown"; //Handle missing formats
try { f= this.getFormatPattern(); } catch (Exception e) {;}
String thisToString=
" format:" + f + " fill:" + this.getFillPattern() +
" fg:" + this.getForegroundColor() +
" bg:" + this.getBackgroundColor() +
" border:["+
this.getTopBorderLineStyle() +"-"+this.getTopBorderColor()+":"+
this.getLeftBorderLineStyle() +"-"+this.getLeftBorderColor()+":"+
this.getBottomBorderLineStyle() +"-"+this.getBottomBorderColor()+":"+
this.getRightBorderLineStyle() +"-"+this.getRightBorderColor()+"]" +
"W:" + this.getWrapText() +
"R:" + this.getRotation() +
"H:" + this.getHorizontalAlignment()+ "V:" + this.getVerticalAlignment() +
"I:" + this.getIndent() +
"L:" + this.isLocked() +
"F:" + this.isFormulaHidden() +
"D:" + this.getRightToLeftReadingOrder();
return this.getFont().toString() + thisToString;
}
/**
* inc # records using this xf
*/
public void incUseCount() { useCount++; }
/**
* dec # records using this xf
*/
public void decUseCoount() { useCount--; }
/**
* return # records using this xf
* @return
*/
public short getUseCount() { return useCount; }
/**
* Populates the myFont and myFormat variables to be held onto
* when the xf record is serialized for boundsheet transfer
*
*/
protected void populateForTransfer() {
myFont = this.getFont();
myFormat = this.getWorkBook().getFormat(ifmt);
this.getData();
}
public boolean getMerged(){
if(fMergeCell == 1)return true;
else return false;
}
// marginal!
public void setMerged(boolean mgd){
byte[] rkdata = this.getData();
rkdata[9] = (byte)0x78; // 0xf4 ?
if (DEBUGLEVEL > 1)Logger.logInfo("Xf The merge style bit is: " + fMergeCell);
}
/** The XF record can either be a style XF or a Cell XF.
*/
public void init(){
super.init();
ifnt = ByteTools.readShort(this.getByteAt(0),this.getByteAt(1));
ifnt = (short)(ifnt & 0xffff);
ifmt = ByteTools.readShort(this.getByteAt(2),this.getByteAt(3));
ifmt = (short)(ifmt & 0xffff);
short flag = ByteTools.readShort(this.getByteAt(4), this.getByteAt(5));
// is the cell locked?
if ((flag & 0x1) == 0x1){
fLocked = 0x1;
}else{
fLocked = 0;
}
// is the cell hidden?
if ((flag & 0x2) == 0x2){
fHidden = 0x1;
}else{
fHidden = 0;
}
// is it a cell rec or a style rec?
if ((flag & 0x4) == 0x4){
fStyle = 1;
}else{
fStyle = 0;
}
if ((flag & 0x8) == 0x0008)
f123Prefix = 0x1;
ixfParent = (short)((flag & 0xFFF0) >> 4);
initXF();
pat= null; // ensure reset if xf has changed
if(DEBUGLEVEL> DEBUG_LOW)Logger.logInfo("Xf.init() ifnt: " + ifnt
+ " ifmt: " + ifmt + ":" +
this.toString()
+ " border: "
+ "l:"+ this.getLeftBorderColor() + ":"
+ "b:"+ this.getBottomBorderColor() + ":"
+ "r:"+ this.getRightBorderColor() + ":"
+ "t:"+ this.getTopBorderColor() + ":");
}
/**
* read and interpret bytes 6-18)
*/
void initXF(){
short flag;
// bytes 6, 7: alignment, rotation, text break
flag = ByteTools.readShort(this.getByteAt(6),this.getByteAt(7));
alc = (short)(flag & 0x7);
if ((flag & 0x8) == 0x8) fWrap = 1;
alcV = (short)((flag & 0x70) >> 4);
trot = (short)((flag & 0xFF00) >> 8);
// byte 8: indent, reading order, shrink
flag= this.getByteAt(8);
cIndent = (short)(flag & 0xF);
if ((flag & 0x10) == 0x10) fShrinkToFit = 1;
if ((flag & 0x20) == 0x20) fMergeCell = 1;
if (DEBUGLEVEL > 5)Logger.logInfo("Xf The merge cell bit is: " + fMergeCell + " and the int is " + flag);
iReadingOrder = (short)((flag & 0xC0));// >> 6); // reading order is byte 7-6 mask 0xCO
// USED_ATTRIB: bits 7-2 of byte 9
flag= this.getByteAt(9);
/* for all these flags, a cleared bit means use Parent Style XF attribute
if set, means the attributes of THIS xf is used
bit mask meaning
0 01H Flag for number format
1 02H Flag for font
2 04H Flag for horizontal and vertical alignment, text wrap, indentation, orientation, rotation, and
text direction
3 08H Flag for border lines
4 10H Flag for background area style
5 20H Flag for cell protection (cell locked and formula hidden)
*/
if ((flag & 0x4) == 0x4) fAtrNum = 1; // number format
if ((flag & 0x8) == 0x8) fAtrFnt = 1; // font
if ((flag & 0x10) == 0x10) fAtrAlc = 1; // alignment (h + v) text wrap rotation direction indent
if ((flag & 0x20) == 0x20) fAtrBdr = 1; // border lines
if ((flag & 0x40) == 0x40) fAtrPat = 1; // background format pattern
if ((flag & 0x80) == 0x80) fAtrProt = 1; // cell protection
// BORDER Section
flag = ByteTools.readShort(this.getByteAt(10), this.getByteAt(11));
dgLeft = (short)(flag & 0xF);
dgRight = (short)((flag & 0xF0) >> 4);
dgTop = (short)((flag & 0xF00) >> 8);
dgBottom = (short)((flag & 0xF000) >> 12);
flag = ByteTools.readShort(this.getByteAt(12),this.getByteAt(13));
icvLeft = (short)(flag & 0x7f);
icvRight = (short)((flag & 0x3F80) >> 7);
grbitDiag = (short)((flag & 0xC000) >> 15);
// bytes 14-17 color and fill
Iflag = ByteTools.readInt(this.getByteAt(14),this.getByteAt(15),this.getByteAt(16),this.getByteAt(17));
icvTop = (short)(Iflag & 0x7F);
icvBottom = (short)((Iflag & 0x3F80) >> 7);
icvDiag = (short)((Iflag & 0x1FC000) >> 14);
dgDiag = (short)((Iflag & 0x1E00000) >> 21);
mystery = (byte) ((Iflag & 0x3800000) >> 25);
fls = (short)((Iflag & 0xFC000000) >> 26); // fill pattern
if (DEBUGLEVEL > 5 && icvTop>0)Logger.logInfo("Xf The cell outline is true");
// bytes 18, 19: fill pattern colors
icvColorFlag = ByteTools.readShort(this.getByteAt(18),this.getByteAt(19));
icvFore = (short)(icvColorFlag & 0x7F); // = Pattern Color
icvBack = (short)((icvColorFlag & 0x3F80) >> 7); // = Pattern Background Color
if ((icvColorFlag & 0x4000) == 0x4000) fSxButton = 1;
// Logger.logInfo(com.extentech.ExtenXLS.ExcelTools.getRecordByteDef(this));
}
/** returns the associated Font record for this XF
*/
public Font getFont(){
if (myFont!=null)return myFont;
myFont= this.getWorkBook().getFont(ifnt);
return myFont;
}
/** returns whether this Format is a Date
*
* Needs to be revisited. Currently I am only returning true for the standard "built in" dates
*/
public boolean isDatePattern(){
// Check the format ID against all known date formats. Why do we do
// this instead of letting it be caught by the string matching below?
for(int x = 0;x<FormatConstants.DATE_FORMATS.length;x++){
short sxt = (short) Integer.parseInt(FormatConstants.DATE_FORMATS[x][1], 16);
if(ifmt == sxt)
return true;
}
Format fmt = this.getWorkBook().getFormat(ifmt);
if(fmt == null)return false;
// toLowerCase is a simplistic way to implement the case insensitivity
// of the pattern tokens. It could cause issues with string literals.
String myfmt = fmt.getFormat().toLowerCase();
return isDatePattern( myfmt );
}
public static boolean isDatePattern (String myfmt) {
// Search for the format string in the list of known date formats
for(int x=0;x<FormatConstants.DATE_FORMATS.length;x++){
if(FormatConstants.DATE_FORMATS[x][0].equals(myfmt)){
return true;
}
}
// check for string patterns that only exist within date records (as far as we know, may need refining)
if(myfmt.indexOf("mm")>-1||myfmt.indexOf("yy")>-1||myfmt.indexOf("dd")>-1)return true;
return false;
}
/**
* Parses an escaped xml format pattern (from ooxml) and returns an extenxls compatible
* pattern.
*
* This method certainly has weaknesses, but my intention is that if it is not a fairly standard format and/or
* we are not sure how to parse it we should leave the existent format intact soas to not break read/write operations
*
* Oddly enough, excel seems to be able to handle biff8 patterns, in my testing so far there has been no need
* to reencode, that could obviously change...
*
* @param xmlFormatPattern
* @return compatible biff8 formatPattern
*/
public static String unescapeFormatPattern(String xmlFormatPattern) {
// strip escaping for currency pattern. Probably should explore all currency types and do an iteration
xmlFormatPattern = xmlFormatPattern.replace("\"$\"", "$");
// separator between positive/negative
xmlFormatPattern = xmlFormatPattern.replace("_);", ";");
// unescape parens
xmlFormatPattern = xmlFormatPattern.replace("\\(", "(");
xmlFormatPattern = xmlFormatPattern.replace("\\)", ")");
return xmlFormatPattern;
}
/** returns whether this Format is a Currency
*/
public boolean isCurrencyPattern(){
if(pat == null) {
setFormatPattern(getFormatPattern());
}
for(int x = 0;x<FormatConstants.CURRENCY_FORMATS.length;x++){
short cpt =(short) Integer.parseInt(FormatConstants.CURRENCY_FORMATS[x][1], 16);
if(ifmt == cpt) {
String ptx = FormatConstants.CURRENCY_FORMATS[x][0];
// what up with this? General?
if(cpt==1) {
if(pat.equals(ptx))
return true;
else
return false;
}else {
return true;
}
}
}
// probably a built-in format that is not a currency format
Format fmt = this.getWorkBook().getFormat(ifmt);
if(fmt == null)return false;
String myfmt = fmt.getFormat();
for(int x=0;x<FormatConstants.CURRENCY_FORMATS.length;x++){
if(FormatConstants.CURRENCY_FORMATS[x][0].equals(myfmt)){
return true;
}
}
return false;
}
/** get the Pattern Color index for this Cell if Solid Fill, or the Foreground color if no Solid Pattern
*/
public short getForegroundColor(){
if (fill!=null) return (short)fill.getFgColorAsInt(getWorkBook().getTheme());
return this.icvFore;
}
/** get the Pattern Color for this Cell if Solid Fill, or the Foreground color if no Solid Pattern, as a Hex Color String
* @return Hex Color String
*/
public String getForegroundColorHEX(){
if (fill!=null)
return fill.getFgColorAsRGB(getWorkBook().getTheme());
return FormatHandle.colorToHexString(FormatHandle.getColor(this.icvFore));
}
/** get the background Color for this Cell as a Hex Color String
* @return Hex Color String
*/
public String getBackgroundColorHEX(){
if (fill!=null)
return fill.getBgColorAsRGB(getWorkBook().getTheme());
if (this.icvBack==65) // default background color
return "#FFFFFF"; // return white
return FormatHandle.colorToHexString(FormatHandle.getColor(this.icvBack));
}
/** get the background Color index for this Cell
*/
public short getBackgroundColor(){
if (fill!=null) return (short)fill.getBgColorAsInt(getWorkBook().getTheme());
if (this.icvBack==65) // default background color
return 64; // return white
return this.icvBack;
}
/** get the Formatting for this BiffRec from the pattern
match.
case insensitive pattern match is performed...
*/
public String getFormatPattern(){
if(pat!=null)return pat;
String[][] fmts = FormatConstantsImpl.getBuiltinFormats();
for (int x=0; x < fmts.length; x++) {
if (this.ifmt==Integer.parseInt(fmts[x][1], 16)) {
pat= fmts[x][0];
return pat;
}
}
Format fmt = this.getWorkBook().getFormat(ifmt);
if (fmt!=null) {
pat= fmt.toString();
return fmt.getFormat();
}
return null;
}
/** Sets the number format pattern for this format.
*/
public void setFormatPattern (String pattern) {
this.pat = pattern;
if (this.getWorkBook() == null) throw new IllegalStateException(
"attempting to set format pattern but workbook is null" );
this.setFormat( addFormatPattern( getWorkBook(), pattern ) );
}
/** Ensures that the given format pattern exists on the given workbook.
* @param book the workbook to which the pattern should belong
* @param pattern the number format pattern to ensure exists
* @return the format ID of the given format pattern
*/
public static short addFormatPattern (WorkBook book, String pattern) {
short ifmt = -1;
// Look up the pattern on the workbook
ifmt = book.getFormatId( pattern );
// If the pattern is unknown, create and add a Format record
if (ifmt == -1) {
Format format = new Format( book, pattern );
ifmt = format.getIfmt();
}
return ifmt;
}
/** set the pointer to the XF's Format in the WorkBook
*/
public void setFormat(short ifm){
ifmt = (short)ifm;
byte[] nef = ByteTools.shortToLEBytes(ifmt);
this.getData()[2] = nef[0];
this.getData()[3] = nef[1];
this.pat= null; // 20080228 KSC: flag to re-input
}
/** set the pointer to the XF's Font in the WorkBook
*/
public void setFont(int ifn){
ifnt = (short)ifn;
byte[] nef = ByteTools.shortToLEBytes(ifnt);
this.getData()[0] = nef[0];
this.getData()[1] = nef[1];
// reset the pointer for xf's and font's brought from other workbooks.
if (this.getWorkBook()!=null) {
myFont = this.getWorkBook().getFont(ifn);
}
}
/**
* Set myFont in XF to the same as Workbook's
*/
public void setMyFont(Font f) {
myFont = f;
}
/** set the Fill Pattern for this Format
*/
public void setPattern(int t){
fls = (short)t;
this.updatePattern();
if (fill!=null)
fill.setFillPattern(t);
}
/**
* get the format pattern for this particular XF
* @return
*/
public int getFillPattern(){
if (fill!=null) return fill.getFillPatternInt();
return fls;
}
// BORDER SECTION
public short getBottomBorderLineStyle(){
return this.dgBottom;
}
public short getTopBorderLineStyle(){
return this.dgTop;
}
public short getLeftBorderLineStyle(){
return this.dgLeft;
}
public short getRightBorderLineStyle(){
return this.dgRight;
}
public short getDiagBorderLineStyle(){
return this.dgDiag;
}
/** set the Top Border Color for this Format
*/
public void setTopBorderColor(int t){
if (t==0) t= 64; // 20080118 KSC
icvTop = (short)t;
updateBorderColors();
}
public int getTopBorderColor(){
// 20070205 KSC: 64 is automatic border color but should be interpreted as 65
if (icvTop==64) return 65;
return (int) icvTop;
}
/** set the Bottom Border Color for this Format
*/
public void setBottomBorderColor(int t){
if (t==0) t= 64; // 20080118 KSC
icvBottom = (short)t;
updateBorderColors();
}
public int getBottomBorderColor(){
// 20070205 KSC: 64 is automatic border color but should be interpreted as 65
if (icvBottom==64) return 65;
return (int)icvBottom;
}
/** set the Left Border Color for this Format
*/
public void setLeftBorderColor(int t){
if (t==0) t= 64; // 20080118 KSC
icvLeft = (short)t;
updateBorderColors();
}
public int getLeftBorderColor(){
// 20070205 KSC: 64 is automatic border color but should be interpreted as 65
if (icvLeft==64) return 65;
return (int)icvLeft;
}
/** set the Right Border Color for this Format
*/
public void setRightBorderColor(int t){
if (t==0) t= 64; // 20080118 KSC
icvRight = (short) t;
updateBorderColors();
}
public short getRightBorderColor(){
// 20070205 KSC: 64 is automatic border color but should be interpreted as 65
if (icvRight==64) return 65;
return icvRight;
}
/** set the diagonal Border Color for this Format
*/
public void setDiagBorderColor(int t){
if (t==0) t= 64; // 20080118 KSC
icvDiag= (short) t;
updateBorderColors();
}
/**
* get the diagonal border color
* @return
*/
public short getDiagBorderColor(){
// 20070205 KSC: 64 is automatic border color but should be interpreted as 65
if (icvDiag==64) return 65;
return icvDiag;
}
/** set the Left Border Color for this Format
*/
public void setLeftBorderColor(short t){
if (t==0) t= 64; // 20080118 KSC
icvLeft = t;
updateBorderColors();
}
/** set the diagonal border for this Format
*/
public void setBorderDiag(int t){
Iflag = 0;
Iflag |= ((short) icvTop);
Iflag |= ((short) icvBottom << 7);
Iflag |= ((short) t << 14);
Iflag |= ((short) dgDiag << 21);
Iflag |= ((short) mystery << 25);
Iflag |= ((short) fls << 26);
this.updatePattern();
}
/** set the border line style for this Format
*/
public void setBorderLineStyle(short t){
dgLeft = t;
dgRight = t;
dgTop = t;
dgBottom = t;
this.updateBorders();
}
/**
* set border line styles via array of ints representing border styles
* order= top, left, bottom, right [diagonal]
* @param b int[]
*/
public void setAllBorderLineStyles(int[] b) {
try {
if (b[0]>-1) dgTop= (short) b[0];
if (b[1]>-1) dgLeft= (short) b[1];
if (b[2]>-1) dgBottom= (short) b[2];
if (b[3]>-1) dgRight= (short) b[3];
if (b[4]>-1) dgDiag= (short) b[4];
} catch (ArrayIndexOutOfBoundsException e) {}
this.updateBorders();
}
/**
* set all border colors via an array of ints representing border color ints
* order= top, left, bottom, right, [diagonal]
* @param b int[]
*/
public void setAllBorderColors(int[] b) {
try {
if (b[0]>-1) icvTop = (short) b[0];
if (b[1]>-1) icvLeft = (short) b[1];
if (b[2]>-1) icvBottom = (short) b[2];
if (b[3]>-1) icvRight = (short) b[3];
if (b[4]>-1) icvDiag= (short) b[4];
} catch (ArrayIndexOutOfBoundsException e) {}
this.updateBorderColors();
}
public void setTopBorderLineStyle(short t){
dgTop = t;
this.updateBorders();
}
public void setBottomBorderLineStyle(short t){
dgBottom = t;
this.updateBorders();
}
public void setLeftBorderLineStyle(short t){
dgLeft = t;
this.updateBorders();
}
public void setRightBorderLineStyle(short t){
dgRight = t;
this.updateBorders();
}
public void updateBorders(){
short borderflag= 0;
borderflag= (short) dgLeft;
borderflag= (short)((borderflag | (dgRight) << 4));
borderflag= (short)((borderflag | (dgTop) << 8));
borderflag= (short)((borderflag | (dgBottom) << 12));
//byte[] rkdata = this.getData();
byte[] bords = ByteTools.shortToLEBytes(borderflag);
this.getData()[10] = bords[0];
this.getData()[11] = bords[1];
setAttributeFlag();
}
/**
* removes all borders for the style
*/
public void removeBorders(){
this.dgBottom = 0;
this.dgTop = 0;
this.dgDiag = 0;
this.dgLeft = 0;
this.dgRight = 0;
this.dgBottom = 0;
this.updateBorders();
}
public void updateBorderColors(){
setAttributeFlag();
}
public void updatePattern(){
byte[] rkdata = this.getData();
short thisFlag = 0;
thisFlag |= ((short) icvLeft);
thisFlag |= ((short) icvRight << 7);
thisFlag |= ((short) grbitDiag << 14);
byte[] bytes = ByteTools.shortToLEBytes(thisFlag);
rkdata[12] = bytes[0];
rkdata[13] = bytes[1];
Iflag = 0;
Iflag |= ((short) icvTop);
Iflag |= ((short) icvBottom << 7);
Iflag |= ((short) icvDiag << 14);
Iflag |= ((short) dgDiag << 21);
Iflag |= ((short) mystery << 25);
Iflag |= ((short) fls << 26);
byte[] nef = ByteTools.cLongToLEBytes(Iflag);
rkdata[14] = nef[0];
rkdata[15] = nef[1];
rkdata[16] = nef[2];
rkdata[17] = nef[3];
// update format cache upon change
pat= null;
this.wkbook.updateFormatCache(this);
}
/** set the Foreground Color for this Format
* THIS SETS THE BACKGROUND COLOR when PATTERN (fls) = PATTERN_SOLID
* THIS SETS THE PATTERN COLOR when PATTERN (fls) > PATTERN_SOLID
* <br>"If the fill style is solid: When solid is specified, the
foreground color (fgColor) is the only color rendered,
even when a background color (bgColor) is also specified"
* icvFore==Pattern Background Color
* @param t best match index into 2003-style Color tabe
* @param clr java.awt.Color or null if use standard Excel 2003 Color Table
*/
public void setForeColor(int t, Color clr){
icvColorFlag = 0;
icvColorFlag |= ((short)t);
icvColorFlag |= ((short)icvBack << 7);
if (clr!=null) {
if (!clr.equals(FormatHandle.COLORTABLE[t])) { // no exact match for color
if (fill == null)
fill = new Fill(getFillPattern(), t, FormatHandle.colorToHexString(clr), icvBack, null, this.getWorkBook().getTheme());
else
fill.setFgColor(t, FormatHandle.colorToHexString(clr));
}
} else if (fill!=null)
fill.setFgColor(t);
this.updateColors();
}
/** set the Background Color for this Format (when PATTERN - fls != PATTERN_SOLID)
* When PATTERN is PATTERN_SOLID, == 64
* @param t best-match index into 2003-style Color table
* @param clr java.awt.Color or null if use standard Excel 2003 Color Table
*/
public void setBackColor(int t, Color clr){
icvColorFlag = 0;
icvColorFlag |= ((short)icvFore);
icvColorFlag |= ((short)t << 7);
if (clr!=null) {
if (!clr.equals(FormatHandle.COLORTABLE[t])) { // no exact match for color - store custom color
if (fill == null)
fill= new Fill(getFillPattern(), icvFore, null, t, FormatHandle.colorToHexString(clr), this.getWorkBook().getTheme());
else
fill.setBgColor(t, FormatHandle.colorToHexString(clr));
}
} else if (fill!=null)
fill.setBgColor(t);
this.updateColors();
}
void updateColors(){
byte[] rkdata = this.getData();
byte[] nef = ByteTools.shortToLEBytes(icvColorFlag);
rkdata[18] = nef[0];
rkdata[19] = nef[1];
icvFore = (short)(icvColorFlag & 0x7F);
icvBack = (short)((icvColorFlag & 0x3F80) >> 7);
// update format cache upon change
pat= null;
this.wkbook.updateFormatCache(this);
}
/**
* PATTERN_SOLID is a special case where icvFore= the background color and icvBack=64.
"If the fill style is solid: When solid is specified, the
foreground color (fgColor) is the only color rendered,
even when a background color (bgColor) is also
specified"
*/
public static final int PATTERN_SOLID= 1; // was set to 4 but tht's wrong!!
/**
* Sets the fill pattern to solid, which renders the background to 64=="the default fg color"
"If the fill style is solid: When solid is specified, the
foreground color (fgColor) is the only color rendered,
even when a background color (bgColor) is also
specified"
*/
public void setBackgroundSolid(){
setPattern(PATTERN_SOLID);
setBackColor(64, null);
if (fill!=null) fill.setFillPattern(PATTERN_SOLID);
}
public boolean isBackgroundSolid(){
if (fill!=null) return fill.isBackgroundSolid();
byte[] rkdata = this.getData();
return (rkdata[17]==(byte)PATTERN_SOLID);
}
/** Sets the attribute flags for this xf record. These flags consist of
* // bit 8= fAtrProt
// 7= fAtrPat
// 6= fAtrBdr
// 5= fAtrAlc (Alignment)
// 4= fAtrFnt
// 3= fAtrNum
*
*/
private void setAttributeFlag(){
setToCellXF();
byte[] rkdata = this.getData();
byte used_attrib = rkdata[9];
byte borderFlag= (byte) ((dgBottom>0 || dgTop>0 || dgLeft>0 || dgRight>0 || dgDiag>0)?1:0); // if border is set
if (borderFlag==1)
used_attrib= (byte) (used_attrib | 0x20); // set bit # 6
else
used_attrib= (byte) (used_attrib & 0xDF); // clear it
if (cIndent!=0 || iReadingOrder!=0 || alc!=0 || alcV!=0 || fWrap!=0 || trot!=0) // set bit # 5
used_attrib= (byte) (used_attrib | 0x10);
else
used_attrib= (byte) (used_attrib & 0xEF); // clear it
rkdata[9]= used_attrib;
fAtrNum= (short) ((used_attrib & 0x04)==0x04?1:0);
fAtrFnt= (short) ((used_attrib & 0x08)==0x08?1:0);
fAtrAlc= (short) ((used_attrib & 0x10)==0x10?1:0);
fAtrBdr= (short) ((used_attrib & 0x20)==0x20?1:0);
fAtrPat= (short) ((used_attrib & 0x40)==0x40?1:0);
fAtrProt= (short) ((used_attrib & 0x80)==0x80?1:0);
// must set color flag for borders or Excel will not like [BugTracker 2861]
if (dgTop > 0 && icvTop==0)
icvTop= 64;
if (dgBottom > 0 && icvBottom==0)
icvBottom= 64;
if (dgRight > 0 && icvRight==0)
icvRight= 64;
if (dgLeft > 0 && icvLeft==0)
icvLeft= 64;
if (dgDiag > 0 && icvDiag==0)
icvDiag= 64;
this.updatePattern();
}
/**
* Switch the record to a cell XF record
*/
public void setToCellXF() {
if (fStyle!=0) {// must set to cell xf (fStyle==0) as changes will not show [BugTracker 2861]
fStyle = 0;
byte flag= (byte) fLocked;
flag= (byte)((flag| (fHidden) << 1));
this.getData()[4]= flag;
this.getData()[5]= 0; // upper bits are style parent rec index
}
}
/**
* @return Returns the ifnt.
*/
public short getIfnt() {
return ifnt;
}
/**
* @param ifnt The ifnt to set.
*/
public void setIfnt(short ifnt) {
this.ifnt = ifnt;
}
public short getIfmt() {
return ifmt;
}
public void setHorizontalAlignment(int hAlign){
alc = (short)hAlign;
updateAlignment();
setAttributeFlag();
}
/**
* set the indent (1=3 spaces)
* @param indent
*/
public void setIndent(int indent) { // indent # = 3 spaces
cIndent= (short) indent; // mask = 0xF, 4 bits,
byte b= (byte) (this.getData()[8] & 0xF0);
b |= (cIndent); // 1st 4 bits
this.getData()[8]= b;
if (alc!=FormatConstants.ALIGN_LEFT || alc!=FormatConstants.ALIGN_RIGHT) // indent only valid for Left and Right (apparently
setHorizontalAlignment(FormatConstants.ALIGN_LEFT);
setAttributeFlag();
}
/**
* return the indent setting (1=3 spaces)
* @return
*/
public int getIndent() {
return cIndent;
}
/**
* sets the Right to Left Text Direction or reading order of this style
* @param rtl possible values:
* <br>0=Context Dependent
* <br>1=Left-to-Right
* <br>2=Right-to-Let
*
*/
public void setRightToLeftReadingOrder(int rtl) {
// iReadingOrder= bits 7-6
// 00= According to Context
// 01= Left to Right (0x40)
// 10= Right to Left (0x80)
if (rtl==2)
iReadingOrder=0x80;
else if (rtl==1)
iReadingOrder=0x40;
else
iReadingOrder= 0;
byte b= this.getData()[8];
b |= (iReadingOrder);
this.getData()[8]= b;
this.wkbook.updateFormatCache(this);
setAttributeFlag();
}
/**
* returns true if this style is set to Right-to-Left text direction (reading order)
* @return
*/
public int getRightToLeftReadingOrder() {
return (int)(iReadingOrder >> 6);
}
public int getHorizontalAlignment(){
return (int)alc;
}
public void setWrapText(boolean wraptext){
if (wraptext){
fWrap = 1;
}else{
fWrap = 0;
}
updateAlignment();
setAttributeFlag();
}
public boolean getWrapText(){
return ( fWrap == 1 ) ? true : false;
}
public void setVerticalAlignment(int vAlign){
alcV = (short)vAlign;
updateAlignment();
setAttributeFlag();
}
public int getVerticalAlignment(){
return (int)alcV;
}
public void setRotation(int rot){
trot = (short)rot;
updateAlignment();
setAttributeFlag();
}
public int getRotation(){
return trot;
}
private void updateAlignment(){
//short tempAlc = (short)(alc << 3);
short tempfWrap = (short)(fWrap << 3);
short tempAlcV = (short)(alcV << 4);
short tempTrot = (short)(trot << 8);
short res = 0x0;
res = (short)(res | alc);
res = (short)(res | tempfWrap);
res = (short)(res | tempAlcV);
res = (short)(res | tempTrot);
byte[] rkdata = this.getData();
byte[] bords = ByteTools.shortToLEBytes(res);
rkdata[6] = bords[0];
rkdata[7] = bords[1];
// update format cache upon change
this.wkbook.updateFormatCache(this);
}
/**
* Returns an XML fragment representing the XF backing the format Handle. The XF record is style information
* associated with a cell. Font information/lookup is not included in this output so it can be used as a comparitor
* style
*/
public String getXML() {
StringBuffer sb = new StringBuffer("<XF");
sb.append(">");
Font myf = this.getFont();
// font info...
sb.append("<font name=\""+myf.getFontName());
sb.append("\" size=\""+myf.getFontHeightInPoints());
sb.append("\" color=\""+FormatHandle.colorToHexString(myf.getColorAsColor()));
sb.append("\" weight=\""+myf.getFontWeight());
if (myf.getIsBold()) {
sb.append("\" bold=\"1");
}
sb.append("\" />");
// format info, should be expanded prolly
sb.append("<format id=\"" + ifmt);
sb.append("\" />");
// 20071218 KSC: Add Fill
sb.append("<fill id=\"" + fls);
sb.append("\" />");
// get the border..
sb.append("<Borders>");
if(getRightBorderLineStyle()!=0) {
sb.append("<Border");
sb.append(" Position=\"right\"");
sb.append(" LineStyle=\"" + FormatHandle.BORDER_NAMES[getRightBorderLineStyle()] + "\"");
sb.append(" Color=\"" + FormatHandle.colorToHexString(wkbook.colorTable[getRightBorderColor()]) + "\"");
sb.append("/>");
}
if(getBottomBorderLineStyle()!=0) {
sb.append("<Border");
sb.append(" Position=\"bottom\"");
sb.append(" LineStyle=\"" + FormatHandle.BORDER_NAMES[getBottomBorderLineStyle()] + "\"");
sb.append(" Color=\"" + FormatHandle.colorToHexString(wkbook.colorTable[getBottomBorderColor()]) + "\"");
sb.append("/>");
}
if(getLeftBorderLineStyle()!=0) {
sb.append("<Border");
sb.append(" Position=\"left\"");
sb.append(" LineStyle=\"" + FormatHandle.BORDER_NAMES[getLeftBorderLineStyle()] + "\"");
sb.append(" Color=\"" + FormatHandle.colorToHexString(wkbook.colorTable[getLeftBorderColor()]) + "\"");
sb.append("/>");
}
if(getTopBorderLineStyle()!=0) {
sb.append("<Border");
sb.append(" Position=\"top\"");
sb.append(" LineStyle=\"" + FormatHandle.BORDER_NAMES[getTopBorderLineStyle()] + "\"");
sb.append(" Color=\"" + FormatHandle.colorToHexString(wkbook.colorTable[getTopBorderColor()]) + "\"");
sb.append("/>");
}
sb.append("</Borders>");
// get the alignment..
sb.append("<Alignment");
sb.append(" Horizontal=\"" + FormatHandle.HORIZONTAL_ALIGNMENTS[this.getHorizontalAlignment()] + "\"");
sb.append(" />");
// get the background color
if(wkbook.colorTable[getForegroundColor()] != Color.WHITE) {
sb.append("<Interior Color=\"" +
FormatHandle.colorToHexString(wkbook.colorTable[getForegroundColor()]) +
"\"/>");
}
sb.append("</XF>");
return sb.toString();
}
/**
* @return Returns the myFormat.
*/
public Format getFormat() {
return myFormat;
}
/**
* @param myFormat The myFormat to set.
*/
public void setFormat(Format myFormat) {
this.myFormat = myFormat;
}
/** get whether this cell formula is hidden
*
* @return
*/
public boolean isFormulaHidden(){
return (this.fHidden == 0x1);
}
/** sets the cell formula as hidden
*
* @param hd
*/
public void setFormulaHidden(boolean hd){
if(hd)
this.fHidden = 0x1;
else
this.fHidden = 0x0;
updateLockedHidden();
}
/** get whether this is a locked Cell
*
* @return
*/
public boolean isLocked(){
return (this.fLocked == 0x1);
}
/**
* return whether this cell is set to "shrink to fit"
* @return
*/
public boolean isShrinkToFit() {
return (this.fShrinkToFit== 0x1);
}
public void setShrinkToFit(boolean b) {
if (b) {
this.fShrinkToFit= 0x1;
this.getData()[9]|=0x10;
}else {
this.fShrinkToFit= 0x0; // turn off bit 4
this.getData()[9]&=0xF7; // set bit 4
}
}
/** sets the cell as locked
*
* @param lk
*/
public void setLocked(boolean lk){
if(lk)
this.fLocked = 0x1;
else
this.fLocked = 0x0;
updateLockedHidden();
}
/**
*
* 2 2 XF type, cell protection, and parent style XF:
Bit Mask Contents
2-0 0007H XF_TYPE_PROT – XF type, cell protection (see above)
15-4 FFF0H Index to parent style XF (always FFFH in style XFs)
*
* Bit Mask Contents
0 01H 1 = Cell is locked
1 02H 1 = Formula is hidden
2 04H 0 = Cell XF; 1 = Style XF
*
*/
private void updateLockedHidden(){
short tempFL = (short)(fLocked << 0x0);
short tempFH = (short)(fHidden << 0x1);
short tempST = (short)(fStyle << 0x2);
short flag = 0x0;
flag = (short)(flag | tempFL);
flag = (short)(flag | tempFH);
flag = (short)(flag | tempST);
byte[] dx = this.getData();
byte[] nef = ByteTools.shortToLEBytes(flag);
dx[4] = nef[0];
dx[5] = nef[1];
// update format cache upon change
pat= null;
this.wkbook.updateFormatCache(this);
}
/**
*
* @return
*/
public boolean getStricken() {
if(myFont!=null)return myFont.getStricken();
return false;
}
public void setStricken(boolean b) {
if(myFont!=null) myFont.setStricken(b);
}
/**
*
* @return
*/
public boolean getItalic() {
if(myFont!=null)return myFont.getItalic();
return false;
}
public void setItalic(boolean b) {
if(myFont!=null) myFont.setItalic(b);
}
/**
*
* @return
*/
public boolean getUnderlined() {
if(myFont!=null)return myFont.getUnderlined();
return false;
}
public void setUnderlined(boolean b) {
if(myFont!=null) myFont.setUnderlined(b);
}
/**
*
* @return
*/
public boolean getBold() {
if(myFont!=null)return myFont.getBold();
return false;
}
public void setBold(boolean b) {
if(myFont!=null) myFont.setBold(b);
}
public int getIdx(){return tableidx;}
/**
* return truth of "this Xf rec is a style xf"
* @return
*/
public boolean isStyleXf() { return (fStyle==1); }
/**
* clone the xf and add to streamer
* @param xf
* @return
*/
private static Xf cloneXf(Xf xf, WorkBook wkbook) {
Xf clone;
if (xf.getIdx()>-1) { // it's in the wb already
clone = new Xf(xf.ifnt, wkbook);
byte[] xfbytes = xf.getBytesAt(0, xf.getLength()-4);
clone.setData(xfbytes);
clone.init();
} else { // xf hasn't been added to wb yet, no need to clone
clone= xf;
}
clone.fill= xf.fill;
clone.setToCellXF(); // changes will not be seen if fstyle bit is set TODO: is this correct in all cases???
clone.tableidx= wkbook.insertXf(clone);
return clone;
}
/**
* if xf parameter doesn't exist, create; if it does, create a new xf based on it
* @param xf original xf
* @param fontIdx font to link xf to
* @param wkbook
* @return new xf
*/
public static Xf updateXf(Xf xf, int fontIdx, WorkBook wkbook) {
if(xf== null) {
xf= new Xf(fontIdx, wkbook);
xf.tableidx= wkbook.insertXf(xf); // insert new xf into stream ...
return xf;
}
else {
xf= Xf.cloneXf(xf, wkbook);
}
return xf;
}
/**
* set the OOXML fill for this xf
* @param f
*/
public void setFill(Fill f) {
this.fill= (Fill) f.cloneElement();
fls= (short)this.fill.getFillPatternInt();
icvFore= (short)this.fill.getFgColorAsInt(getWorkBook().getTheme());
icvBack= (short)this.fill.getBgColorAsInt(getWorkBook().getTheme());
}
/**
* return the OOXML fill for this xf, if any
*/
public Fill getFill() { return this.fill; }
/**
* clear out object references in prep for closing workbook
*/
public void close() {
super.close();
this.myFont.close();
this.myFormat= null;
}
}