/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * This file is part of xlslib -- A multiplatform, C/C++ library * for dynamic generation of Excel(TM) files. * * xlslib 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. * * xlslib 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 xlslib. If not, see . * * Copyright 2004 Yeico S. A. de C. V. * Copyright 2008 David Hoerl * * $Source: /cvsroot/xlslib/xlslib/src/xlslib/sheetrec.cpp,v $ * $Revision: 1.3 $ * $Author: dhoerl $ * $Date: 2008/10/25 18:39:54 $ * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * File description: * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */ #include using namespace std; using namespace xlslib_core; /* * LOCAL MODULE MACROS */ #if STORAGE_CELL==LIST_CONTAINER #define MARK_CELLS_UNSORTED() { \ m_CellsSorted = false; \ m_SizesCalculated = false; \ m_RBSizes.clear(); \ } #elif STORAGE_CELL==SET_CONTAINER #define MARK_CELLS_UNSORTED() { \ m_CellsSorted = false; \ m_SizesCalculated = false; \ m_RBSizes.clear(); \ } #endif #define MAX_ROWBLOCK_SIZE (32) #define RB_DBCELL_MINSIZE (unsigned8_t(8)) #define RB_DBCELL_CELLSIZEOFFSET (unsigned8_t(2)) using namespace std; /* ********************************************************************** worksheet class implementation ********************************************************************** */ worksheet::worksheet(CGlobalRecords* pglobalrec) : m_DumpState(SHEET_INIT), m_pCurrentData(NULL), m_Size(0), m_CellsSorted(false), m_SizesCalculated(false), m_DumpRBState(RB_INIT), m_DBCellOffset(0), m_CurrentRowBlock(0) { m_pGlobalRecords = pglobalrec; minRow = minCol = 0xFFFF; // UINT16_MAX maxRow = maxCol = 0; } #ifdef HAVE_ICONV worksheet::worksheet(CGlobalRecords* pglobalrec, string& code) : m_DumpState(SHEET_INIT), m_pCurrentData(NULL), m_Size(0), m_CellsSorted(false), m_SizesCalculated(false), m_DumpRBState(RB_INIT), m_DBCellOffset(0), m_CurrentRowBlock(0) { m_pGlobalRecords = pglobalrec; minRow = minCol = 0xFFFF; // UINT16_MAX maxRow = maxCol = 0; iconv_code = code; } #endif worksheet::~worksheet() { if(!m_RBSizes.empty()) for(RBSize_List_Itor_t rbs = m_RBSizes.begin(); rbs != m_RBSizes.end(); rbs++) delete *rbs; // Delete the dinamically created cell objects (pointers) if(!m_Cells.empty()) { // cout<<"worksheet::~worksheet(), this = "<AddDBCellOffset(dbcelloffset); // Update the offset for the next DBCELL's offset rb_size_acc += dbcell_size; } m_DumpState = SHEET_DIMENSION; // Change to the next state break; } case SHEET_DIMENSION: XTRACE("\tDIMENSION"); repeat = false; //Delete_Pointer(m_pCurrentData); m_pCurrentData = (CUnit*)(new CDimension(minRow, maxRow, minCol, maxCol)); m_DumpState = SHEET_ROWBLOCKS; break; case SHEET_ROWBLOCKS: XTRACE("\tROWBLOCKS"); if(GetNumRowBlocks()) {// First check if the list of RBs is not empty... m_pCurrentData = RowBlocksDump(); if(m_pCurrentData == NULL) { repeat = true; m_DumpState = SHEET_MERGED; } } else {// if the list is empty, change the dump state. repeat = true; m_DumpState = SHEET_MERGED; } break; case SHEET_MERGED: repeat = false; XTRACE("\tMERGED"); if(!m_MergedRanges.empty()) { m_pCurrentData = (CUnit*)(new CMergedCells()); ((CMergedCells*)m_pCurrentData)->SetNumRanges(m_MergedRanges.size()); for(Range_List_Itor_t mr = m_MergedRanges.begin(); mr != m_MergedRanges.end(); mr++) { ((CMergedCells*)m_pCurrentData)->AddRange(*mr); } } else { repeat = true; } m_DumpState = SHEET_COLINFO; break; case SHEET_COLINFO: repeat = false; XTRACE("\tCOLINFO"); if(!m_Colinfos.empty()) {// First check if the list of fonts is not empty... //Delete_Pointer(m_pCurrentData); m_pCurrentData = (CUnit*)(new CColInfo(*m_Current_Colinfo)); if(m_Current_Colinfo != (--m_Colinfos.end())) {// if it was'nt the last font from the list, increment to get the next one m_Current_Colinfo++; } else {// if it was the last from the list, change the DumpState m_DumpState = SHEET_WINDOW2; m_Current_Colinfo = m_Colinfos.begin(); } } else {// if the list is empty, change the dump state. m_DumpState = SHEET_WINDOW2; //font = m_Fonts.begin(); repeat = true; } break; case SHEET_WINDOW2: XTRACE("\tWINDOW2"); repeat = false; //Delete_Pointer(m_pCurrentData); m_pCurrentData = (CUnit*)(new CWindow2()); m_DumpState = SHEET_EOF; break; case SHEET_EOF: XTRACE("\tEOF"); //Delete_Pointer(m_pCurrentData); m_pCurrentData = (CUnit*)(new CEof()); m_DumpState = SHEET_FINISH; break; case SHEET_FINISH: XTRACE("\tFINISH"); //Delete_Pointer(m_pCurrentData); m_pCurrentData = NULL; m_DumpState = SHEET_INIT; break; } }while(repeat); return m_pCurrentData; } CUnit* worksheet::RowBlocksDump() { bool repeat = false; CUnit* rb_record = NULL; do { switch(m_DumpRBState) { case RB_INIT: m_DumpRBState = RB_ROWS; m_CurrentRowBlock = 0; m_RowCounter = 0; m_CurrentCell = m_Cells.begin(); // Initialize the row widths m_Current_RowHeight = m_RowHeights.begin(); m_DumpRBState = RB_FIRST_ROW; repeat = true; break; case RB_FIRST_ROW: repeat = true; if( m_Cells.empty() || m_CurrentCell != m_Cells.end()) { m_Starting_RBCell = m_CurrentCell; m_CellCounter = 0; m_DBCellOffset = 0; m_CellOffsets.clear(); m_DumpRBState = RB_ROWS; } else { m_DumpRBState = RB_FINISH; } break; case RB_ROWS: { repeat = false; // Initialize first/last cols to impossible values // that are appropriate for the following detection algorithm unsigned16_t first_col = (unsigned16_t)(-1); unsigned16_t last_col = 0; unsigned16_t row_num = 0; // Get the row number for the current row row_num = (*m_CurrentCell)->GetRow(); Cell_List_Itor_t this_cell, next_cell; do { // Determine the first/last column of the current row if((*m_CurrentCell)->GetCol() > last_col) last_col = (*m_CurrentCell)->GetCol(); if((*m_CurrentCell)->GetCol() < first_col) first_col = (*m_CurrentCell)->GetCol(); // To avoid dereference an empty iterator check if this is the only one cell // in m_Cells list. if(m_Cells.size() > 1) { m_CellCounter++; this_cell = m_CurrentCell; next_cell = ++m_CurrentCell; // Break the while if there are no more cells if(next_cell == m_Cells.end()) break; } else { // Break the loop if this was the only cell. if(!m_Cells.empty()) { m_CellCounter++; ++m_CurrentCell; } break; } // The order in the following and-statement is important }while( m_CurrentCell != (m_Cells.end()) && *(*this_cell) == *(*next_cell )); // Check if the current row is in the list of height-set // rows. if(m_Current_RowHeight != m_RowHeights.end()) { if((*m_Current_RowHeight)->GetRowNum() == row_num) { rb_record = (CUnit*) (new CRow(row_num, first_col, last_col, (*m_Current_RowHeight)->GetRowHeight()) ); m_Current_RowHeight++; } else { rb_record = (CUnit*) (new CRow(row_num, first_col, last_col) ); } } else { rb_record = (CUnit*) (new CRow(row_num, first_col, last_col) ); } m_DBCellOffset += ROW_RECORD_SIZE; // If the current row-block is full OR there are no more cells if(++m_RowCounter >= MAX_ROWBLOCK_SIZE || m_CurrentCell == m_Cells.end()) { if (m_CurrentCell == (--m_Cells.end())) m_CellCounter++; m_RowCounter = 0; m_DumpRBState = RB_FIRSTCELL; } break; } case RB_FIRSTCELL: rb_record = (*m_Starting_RBCell)->GetData(); // Update the offset to be used in the DBCell Record m_DBCellOffset += rb_record->GetDataSize(); // The first cell of the rowblock has an offset that includes (among others) // the rows size (without counting the first row) m_CellOffsets.push_back(m_DBCellOffset -ROW_RECORD_SIZE); // Update the pointer (iterator) to the next cell if(--m_CellCounter == 0) {// The RowBlock's cells are done m_DumpRBState = RB_DBCELL; } else { m_Starting_RBCell++; m_DumpRBState = RB_CELLS; } break; case RB_CELLS: repeat = false; if(m_CellCounter == 0) {// The RowBlock's cells are done m_DumpRBState = RB_DBCELL; repeat = true; } else { rb_record = (*m_Starting_RBCell)->GetData(); m_DBCellOffset += rb_record->GetDataSize(); m_CellOffsets.push_back(rb_record->GetDataSize()); m_CellCounter--; m_Starting_RBCell++; } break; case RB_DBCELL: { repeat = false; rb_record = (CUnit*)(new CDBCell(m_DBCellOffset)); CellOffsets_List_Itor_t celloffset; for(celloffset = m_CellOffsets.begin(); celloffset != m_CellOffsets.end(); celloffset++) ((CDBCell*)rb_record)->AddRowOffset(*celloffset); if(m_CurrentCell == (--m_Cells.end()) ) m_DumpRBState = RB_FINISH; else m_DumpRBState = RB_FIRST_ROW; break; } case RB_FINISH: repeat = false; rb_record = NULL; m_DumpRBState = RB_INIT; break; default: break; } }while(repeat); return rb_record; } /* *********************************** *********************************** */ cell_t* worksheet::label(unsigned16_t row, unsigned16_t col, string strlabel, xf_t* pxformat) { label_t* label = new label_t(row, col, strlabel, pxformat); AddCell((cell_t*)label); return (cell_t*)label; } #if VERSION_BIFF == VERSION_BIFF8 cell_t* worksheet::label(unsigned16_t row, unsigned16_t col, ustring strlabel, xf_t* pxformat) { label_t* label = new label_t(row, col, strlabel #ifdef HAVE_ICONV , iconv_code #endif , pxformat); AddCell((cell_t*)label); return (cell_t*)label; } #endif cell_t* worksheet::number(unsigned16_t row, unsigned16_t col, double numval, format_number_t fmtval, xf_t* pxformat) { number_t* number = new number_t(row, col, numval, pxformat); AddCell((cell_t*)number); number->format(fmtval); return (cell_t*)number; } /* *********************************** *********************************** */ cell_t* worksheet::blank(unsigned16_t row, unsigned16_t col, xf_t* pxformat) { blank_t* blank = new blank_t(row, col, pxformat); AddCell((cell_t*)blank); return (cell_t*)blank; } /* *********************************** *********************************** */ void worksheet::AddCell(cell_t* pcell) { Cell_List_Itor_t existing_cell; unsigned32_t row, col; row = pcell->GetRow(); col = pcell->GetCol(); if(row < minRow) minRow = row; if(row > maxRow) maxRow = row; if(col < minCol) minCol = col; if(col > maxCol) maxCol = col; // Pass a pointer to m_GlobalRecords, so the global records // can be modified from the cell as needed pcell->SetGlobalRecs(m_pGlobalRecords); SortCells(); // lookup the cell existing_cell = m_Cells.find(pcell); if(existing_cell != m_Cells.end()) { //Always overwrite delete (*existing_cell); m_Cells.erase(existing_cell); m_Cells.insert(pcell); MARK_CELLS_UNSORTED(); } else { m_Cells.insert(pcell); MARK_CELLS_UNSORTED(); } } cell_t* worksheet::FindCell(unsigned16_t row, unsigned16_t col) { Cell_List_Itor_t existing_cell; cell_t* cell = new blank_t(row, col); existing_cell = m_Cells.find(cell); delete cell; // The find operation returns the end() itor // if the cell wasn't found if(existing_cell != m_Cells.end()) return *existing_cell; else { return blank(row,col); } } /* void worksheet::AddCell(cell_t* pcell, bool overwrite) { #if STORAGE_CELL == LIST_CONTAINER m_Cells.push_back(pcell); #elif STORAGE_CELL == SET_CONTAINER m_Cells.insert(pcell); #endif MARK_CELLS_UNSORTED(); } */ /* *********************************** *********************************** */ unsigned32_t worksheet::GetSize() { m_CurrentSizeCell = m_Cells.begin(); unsigned32_t numrb = GetNumRowBlocks(); unsigned16_t merged_size; unsigned16_t colinfo_size; // The size of the merged cells record (if any) has to be taken in count if(!m_MergedRanges.empty()) { // [HEADER] + [NUMRANGESFIELD] + [RANGES] merged_size = 4 + 2 + m_MergedRanges.size()*8; } else { merged_size = 0; } // The size of the Colinfo records (if any) has to be taken in count if(!m_Colinfos.empty()) { colinfo_size = m_Colinfos.size()*14; } else { colinfo_size = 0; } unsigned32_t size = BOF_SIZE + RB_INDEX_MINSIZE + 4*numrb + merged_size + colinfo_size + WINDOW2_SIZE + EOF_SIZE; for(unsigned32_t rb = 0; rb < numrb; rb++) { // Get sizes of next RowBlock unsigned32_t rowandcell_size, dbcell_size; GetRowBlockSizes( &rowandcell_size, &dbcell_size); // Update the offset accumulator and cerate the next DBCELL's offset size += rowandcell_size; size += dbcell_size; } m_CurrentSizeCell = m_Cells.begin(); return size; } /* *********************************** *********************************** */ bool worksheet::GetRowBlockSizes(unsigned32_t* rowandcell_size, unsigned32_t* dbcell_size, unsigned32_t* num_rows) { SortCells(); unsigned32_t row_counter = 0; unsigned32_t cell_counter = 0; Cell_List_Itor_t beginning_cell = m_CurrentSizeCell; // Initialize the size values (since they work as accumulators) *rowandcell_size = 0; *dbcell_size = 0; if(!m_SizesCalculated) { // Check if there are no cells if(!m_Cells.empty()) { // The first cell is inside a row that has to be counted // row_counter = 1; do { cell_counter++; // There's at least one cell on each loop.. that's for sure! // Since the list of cells is sorted by rows, continuouslly equal cells (compared by row) // conform one row... if the next one is different, increment the row counter Cell_List_Itor_t this_cell = m_CurrentSizeCell; Cell_List_Itor_t next_cell = ++ m_CurrentSizeCell; // To avoid dereferencing an empty iterator check if this is the only // one cell in m_Cells list. if( m_Cells.size()>1) { if( *(*this_cell) != *(*(next_cell)) ) row_counter++; } else { // Break the loop if this was the only one cell in the list // .. also set the only one row cell_counter--; m_CurrentSizeCell = (--m_Cells.end()); break; } }while(row_counter < MAX_ROWBLOCK_SIZE && m_CurrentSizeCell != (--m_Cells.end())); // Check also if the currentcell isn't the last one // If the cells run out before the row counter changes, the last row (and last cell) isn't counted // in the previous control structre. if(m_CurrentSizeCell == (--m_Cells.end())) { row_counter++; cell_counter++; } if(num_rows != NULL) *num_rows += row_counter; // Get the size of the rows *rowandcell_size += ROW_RECORD_SIZE*row_counter; // Get the size of the cells using the saved iterator pointing to the beginning of this block for(unsigned32_t count_blockcells = 0; count_blockcells GetSize(); beginning_cell++; } // Now get the size of the DBCELL *dbcell_size += RB_DBCELL_MINSIZE; *dbcell_size += RB_DBCELL_CELLSIZEOFFSET*cell_counter; // Check the size of the data int the DBCELL record (without the header) // to take in count the overhead of the CONTINUE record (4bytes/CONTrec) if((*dbcell_size-4) > MAX_RECORD_SIZE) { unsigned32_t cont_overhead = (*dbcell_size / MAX_RECORD_SIZE); if(*dbcell_size % MAX_RECORD_SIZE) cont_overhead++; *dbcell_size += (cont_overhead-1)*4; } rowblocksize_t* rbsize = new rowblocksize_t; rbsize->rowandcell_size = *rowandcell_size; rbsize->dbcell_size = *dbcell_size; rbsize->rows_sofar = row_counter; m_RBSizes.push_back(rbsize); // If it was the last block, reset the current-label pointer if(m_CurrentSizeCell == (--m_Cells.end())) { m_CurrentSizeCell = m_Cells.begin(); m_Current_RBSize = m_RBSizes.begin(); m_SizesCalculated = true; return false; } } // If there are no cells in the sheet, return sizes = 0. if(m_Cells.empty()) return false; else return true; } else { *rowandcell_size = (*m_Current_RBSize)->rowandcell_size; *dbcell_size = (*m_Current_RBSize)->dbcell_size; if(num_rows != NULL) *num_rows += (*m_Current_RBSize)->rows_sofar; m_Current_RBSize++; // Resett the current RBSize if(m_Current_RBSize == m_RBSizes.end()) { m_Current_RBSize = m_RBSizes.begin(); return false; } } return true; } /* *********************************** *********************************** */ void worksheet::GetFirstLastRows(unsigned32_t* first_row, unsigned32_t* last_row) { // First check that the m_Cells list is not empty, so we won't dereference // empty anr iterator. if(!m_Cells.empty()) { SortCells(); cell_t* pcell; pcell = *(m_Cells.begin()); *first_row = pcell->GetRow(); pcell = *(--m_Cells.end()); *last_row = pcell->GetRow(); } else { // If there is no cells in the list the first/last rows // are defaulted to zero. *first_row = 0; *last_row = 0; } } /* *********************************** *********************************** */ unsigned32_t worksheet::GetNumRowBlocks() { unsigned32_t numrb; // First check that the m_Cells list is not empty, so we won't dereference // empty anr iterator. bool cont = false; unsigned32_t num_rows = 0; do { unsigned32_t dummy1, dummy2; cont = GetRowBlockSizes(&dummy1, &dummy2, &num_rows); }while(cont); /* Cell_List_t temp_cell_list = m_Cells; temp_cell_list.sort(); temp_cell_list.unique(); */ if(!m_Cells.empty()) { numrb = num_rows/MAX_ROWBLOCK_SIZE; if(num_rows%MAX_ROWBLOCK_SIZE) numrb++; } else { // If the m_Cell list is empty, there are no rowblocks in the sheet. numrb = 0; } return numrb; } /* *********************************** *********************************** */ void worksheet::merge(unsigned16_t first_row, unsigned16_t first_col, unsigned16_t last_row, unsigned16_t last_col) { range_t* newrange = new range_t; newrange->first_row = first_row; newrange->last_row = last_row; newrange->first_col = first_col; newrange->last_col = last_col; m_MergedRanges.push_back(newrange); } /* *********************************** *********************************** */ void worksheet::colwidth(unsigned16_t col, unsigned16_t width) { colinfo_t* newci = new colinfo_t; Colinfo_List_Itor_t existing_ci; newci->colfirst = col; newci->collast = col; newci->flags = 0x00; newci->xformat = NULL; newci->width = width*256; //sets column widths to 1/256 x width of "0" // m_Colinfos.push_back(newci); existing_ci = m_Colinfos.find(newci); if(existing_ci != m_Colinfos.end()) { //Always overwrite delete (*existing_ci); m_Colinfos.erase(existing_ci); m_Colinfos.insert(newci); } else { m_Colinfos.insert(newci); } } /* *********************************** *********************************** */ void worksheet::rowheight(unsigned16_t row, unsigned16_t height) { rowheight_t* newrh = new rowheight_t(row,height*20); RowHeight_List_Itor_t existing_rh; //m_RowHeights.insert(newrh); existing_rh = m_RowHeights.find(newrh); if(existing_rh != m_RowHeights.end()) { //Always overwrite delete (*existing_rh); m_RowHeights.erase(existing_rh); m_RowHeights.insert(newrh); } else { m_RowHeights.insert(newrh); } } range* worksheet::rangegroup(unsigned16_t row1, unsigned16_t col1, unsigned16_t row2, unsigned16_t col2) { range* newrange = new range(row1, col1, row2, col2, this); m_Ranges.push_back(newrange); return newrange; } /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * $Log: sheetrec.cpp,v $ * Revision 1.3 2008/10/25 18:39:54 dhoerl * 2008 * * Revision 1.2 2004/09/01 00:47:04 darioglz * + Modified to gain independence of target * * Revision 1.1.1.1 2004/08/27 16:31:51 darioglz * Initial Import. * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */