File indexing completed on 2024-04-14 14:12:15

0001 /*
0002  * OriginAnyParser.cpp
0003  *
0004  * Copyright 2017 Miquel Garriga <gbmiquel@gmail.com>
0005  *
0006  * This program is free software: you can redistribute it and/or modify
0007  * it under the terms of the GNU General Public License as published by
0008  * the Free Software Foundation, either version 3 of the License, or
0009  * (at your option) any later version.
0010  *
0011  * This program is distributed in the hope that it will be useful,
0012  * but WITHOUT ANY WARRANTY; without even the implied warranty of
0013  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
0014  * GNU General Public License for more details.
0015  *
0016  * You should have received a copy of the GNU General Public License
0017  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
0018  *
0019  * Parser for all versions. Based mainly on Origin750Parser.cpp
0020  */
0021 
0022 #include "OriginAnyParser.h"
0023 #include <sstream>
0024 #include <cinttypes>
0025 
0026 /* define a macro to get an int (or uint) from a istringstream in binary mode */
0027 #if __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__
0028 #define GET_SHORT(iss, ovalue)  {iss.read(reinterpret_cast<char *>(&ovalue), 2);};
0029 #define GET_INT(iss, ovalue)    {iss.read(reinterpret_cast<char *>(&ovalue), 4);};
0030 #define GET_FLOAT(iss, ovalue)  {iss.read(reinterpret_cast<char *>(&ovalue), 4);};
0031 #define GET_DOUBLE(iss, ovalue) {iss.read(reinterpret_cast<char *>(&ovalue), 8);};
0032 #else
0033 void inline swap_bytes(unsigned char* data, int size) {int i = 0, j = size - 1; while(i < j) {std::swap(data[i], data[j]); ++i, --j;}}
0034 #define GET_SHORT(iss, ovalue)  {iss.read(reinterpret_cast<char *>(&ovalue), 2); swap_bytes(reinterpret_cast<unsigned char *>(&ovalue), 2);};
0035 #define GET_INT(iss, ovalue)    {iss.read(reinterpret_cast<char *>(&ovalue), 4); swap_bytes(reinterpret_cast<unsigned char *>(&ovalue), 4);};
0036 #define GET_FLOAT(iss, ovalue)  {iss.read(reinterpret_cast<char *>(&ovalue), 4); swap_bytes(reinterpret_cast<unsigned char *>(&ovalue), 4);};
0037 #define GET_DOUBLE(iss, ovalue) {iss.read(reinterpret_cast<char *>(&ovalue), 8); swap_bytes(reinterpret_cast<unsigned char *>(&ovalue), 8);};
0038 #endif
0039 
0040 OriginAnyParser::OriginAnyParser(const string& fileName)
0041 :   file(fileName.c_str(),ios::binary),
0042     logfile(nullptr),
0043     d_file_size(0),
0044     curpos(0),
0045     objectIndex(0),
0046     parseError(0),
0047     ispread(-1),
0048     imatrix(-1),
0049     iexcel(-1),
0050     igraph(-1),
0051     ilayer(-1) {
0052 }
0053 
0054 bool OriginAnyParser::parse() {
0055 #ifdef GENERATE_CODE_FOR_LOG
0056     // append progress in log file
0057     logfile = fopen("opjfile.log","a");
0058 #endif // GENERATE_CODE_FOR_LOG
0059 
0060     // get length of file:
0061     file.seekg (0, ios_base::end);
0062     d_file_size = file.tellg();
0063     file.seekg(0, ios_base::beg);
0064 
0065     LOG_PRINT(logfile, "File size: %" PRId64 "\n", d_file_size)
0066 
0067     // get file and program version, check it is a valid file
0068     readFileVersion();
0069     if (parseError > 1) return false;
0070     curpos = file.tellg();
0071     LOG_PRINT(logfile, "Now at %" PRId64 " [0x%" PRIx64 "]\n", curpos, curpos)
0072 
0073     // get global header
0074     readGlobalHeader();
0075     if (parseError > 1) return false;
0076     curpos = file.tellg();
0077     LOG_PRINT(logfile, "Now at %"  PRId64 " [0x%" PRIx64 "]\n", curpos, curpos)
0078 
0079     // get dataset list
0080     unsigned int dataset_list_size = 0;
0081     objectIndex = 0; // use it to count DataSets
0082 
0083     LOG_PRINT(logfile, "Reading Data sets ...\n")
0084     while (true) {
0085         if (!readDataSetElement()) break;
0086         dataset_list_size++;
0087     }
0088     if (parseError > 1) return false;
0089     LOG_PRINT(logfile, " ... done. Data sets: %d\n", dataset_list_size)
0090     curpos = file.tellg();
0091     LOG_PRINT(logfile, "Now at %" PRId64 " [0x%" PRIx64 "], file size %" PRId64 "\n", curpos, curpos, d_file_size)
0092 
0093     for(unsigned int i = 0; i < spreadSheets.size(); ++i){
0094         if(spreadSheets[i].sheets > 1){
0095             LOG_PRINT(logfile, "        CONVERT SPREADSHEET \"%s\" to EXCEL\n", spreadSheets[i].name.c_str());
0096             convertSpreadToExcel(i);
0097             --i;
0098         }
0099     }
0100 
0101     // get window list
0102     unsigned int window_list_size = 0;
0103     objectIndex = 0; // reset it to count Windows (except Notes)
0104 
0105     LOG_PRINT(logfile, "Reading Windows ...\n")
0106     while (true) {
0107         if (!readWindowElement()) break;
0108         window_list_size++;
0109     }
0110     LOG_PRINT(logfile, " ... done. Windows: %d\n", window_list_size)
0111     curpos = file.tellg();
0112     LOG_PRINT(logfile, "Now at %" PRId64 " [0x%" PRIx64 "], file size %" PRId64 "\n", curpos, curpos, d_file_size)
0113 
0114     // get parameter list
0115     unsigned int parameter_list_size = 0;
0116 
0117     LOG_PRINT(logfile, "Reading Parameters ...\n")
0118     while (true) {
0119         if (!readParameterElement()) break;
0120         parameter_list_size++;
0121     }
0122     LOG_PRINT(logfile, " ... done. Parameters: %d\n", parameter_list_size)
0123     curpos = file.tellg();
0124     LOG_PRINT(logfile, "Now at %" PRId64 " [0x%" PRIx64 "], file size %" PRId64 "\n", curpos, curpos, d_file_size)
0125 
0126     // Note windows were added between version >4.141 and 4.210,
0127     // i.e., with Release 5.0
0128     if (curpos < d_file_size) {
0129         // get note windows list
0130         unsigned int note_list_size = 0;
0131 
0132         LOG_PRINT(logfile, "Reading Note windows ...\n")
0133         objectIndex = 0; // reset it to count Notes
0134         while (true) {
0135             if (!readNoteElement()) break;
0136             note_list_size++;
0137         }
0138         LOG_PRINT(logfile, " ... done. Note windows: %d\n", note_list_size)
0139         curpos = file.tellg();
0140         LOG_PRINT(logfile, "Now at %" PRId64 " [0x%" PRIx64 "], file size %" PRId64 "\n", curpos, curpos, d_file_size)
0141     }
0142 
0143     // Project Tree was added between version >4.210 and 4.2616,
0144     // i.e., with Release 6.0
0145     if (curpos < d_file_size) {
0146         // get project tree
0147         readProjectTree();
0148         curpos = file.tellg();
0149         LOG_PRINT(logfile, "Now at %" PRId64 " [0x%" PRIx64 "], file size %" PRId64 "\n", curpos, curpos, d_file_size)
0150     }
0151 
0152     // Attachments were added between version >4.2673_558 and 4.2764_623,
0153     // i.e., with Release 7.0
0154     if (curpos < d_file_size) {
0155         readAttachmentList();
0156         curpos = file.tellg();
0157         LOG_PRINT(logfile, "Now at %" PRId64 " [0x%" PRIx64 "], file size %" PRId64 "\n", curpos, curpos, d_file_size)
0158     }
0159 
0160     if (curpos >= d_file_size) LOG_PRINT(logfile, "Now at end of file\n")
0161 
0162 #ifdef GENERATE_CODE_FOR_LOG
0163     fclose(logfile);
0164 #endif // GENERATE_CODE_FOR_LOG
0165 
0166     return true;
0167 }
0168 
0169 string toLowerCase(string str){
0170     for (unsigned int i = 0; i < str.length(); i++)
0171         if (str[i] >= 0x41 && str[i] <= 0x5A)
0172             str[i] = str[i] + 0x20;
0173     return str;
0174 }
0175 
0176 OriginParser* createOriginAnyParser(const string& fileName)
0177 {
0178     return new OriginAnyParser(fileName);
0179 }
0180 
0181 unsigned int OriginAnyParser::readObjectSize() {
0182     unsigned int obj_size = 0;
0183 
0184     char c = 0;
0185     file >> obj_size;
0186     file >> c;
0187     if (c != '\n') {
0188         curpos = file.tellg();
0189         LOG_PRINT(logfile, "Wrong delimiter %c at %" PRId64 " [0x%" PRIx64 "]\n", c, curpos, curpos)
0190         parseError = 3;
0191         return 0;
0192     }
0193     return obj_size;
0194 }
0195 
0196 string OriginAnyParser::readObjectAsString(unsigned int size) {
0197     char c;
0198     // read a size-byte blob of data followed by '\n'
0199     if (size > 0) {
0200         // get a string large enough to hold the result, initialize it to all 0's
0201         string blob = string(size, '\0');
0202         // read data into that string
0203         // cannot use '>>' operator because iendianfstream truncates it at first '\0'
0204         file.read(&blob[0], size);
0205         // read the '\n'
0206         file >> c;
0207         if (c != '\n') {
0208             curpos = file.tellg();
0209             LOG_PRINT(logfile, "Wrong delimiter %c at %" PRId64 " [0x%" PRIx64 "]\n", c, curpos, curpos)
0210             parseError = 4;
0211             return string();
0212         }
0213         return blob;
0214     }
0215     return string();
0216 }
0217 
0218 void OriginAnyParser::readFileVersion() {
0219     // get file and program version, check it is a valid file
0220     string sFileVersion;
0221     getline(file, sFileVersion);
0222 
0223     if ((sFileVersion.substr(0,4) != "CPYA")) {
0224         LOG_PRINT(logfile, "File, is not a valid OPJ file\n")
0225         if ((sFileVersion.substr(0,5) != "CPYUA")) {
0226             LOG_PRINT(logfile, "File, is not a valid OPJU file\n")
0227             parseError = 2;
0228             return;
0229         }
0230     }
0231 
0232     if (*sFileVersion.rbegin() != '#') parseError = 1;
0233     LOG_PRINT(logfile, "File version string: %s\n", sFileVersion.c_str())
0234 }
0235 
0236 void OriginAnyParser::readGlobalHeader() {
0237     // get global header size
0238     unsigned int gh_size = 0, gh_endmark = 0;
0239     gh_size = readObjectSize();
0240     curpos = file.tellg();
0241     LOG_PRINT(logfile, "Global header size: %d [0x%X], starts at %" PRId64 " [0x%" PRIx64 "],", gh_size, gh_size, curpos, curpos)
0242 
0243     // get global header data
0244     string gh_data;
0245     gh_data = readObjectAsString(gh_size);
0246 
0247     curpos = file.tellg();
0248     LOG_PRINT(logfile, " ends at %" PRId64 " [0x%" PRIx64 "]\n", curpos, curpos)
0249 
0250     // when gh_size > 0x1B, a double with fileVersion/100 can be read at gh_data[0x1B:0x23]
0251     if (gh_size > 0x1B) {
0252         istringstream stmp;
0253         stmp.str(gh_data.substr(0x1B));
0254         double dFileVersion;
0255         GET_DOUBLE(stmp, dFileVersion)
0256         if (dFileVersion > 8.5) {
0257             fileVersion = (unsigned int)trunc(dFileVersion*100.);
0258         } else {
0259             fileVersion = 10*(unsigned int)trunc(dFileVersion*10.);
0260         }
0261         LOG_PRINT(logfile, "Project version as read from header: %.2f (%.6f)\n", fileVersion/100.0, dFileVersion)
0262     }
0263 
0264     // now read a zero size end mark
0265     gh_endmark = readObjectSize();
0266     if (gh_endmark != 0) {
0267         curpos = file.tellg();
0268         LOG_PRINT(logfile, "Wrong end of list mark %d at %" PRId64 " [0x%" PRIx64 "]\n", gh_endmark, curpos, curpos)
0269         parseError = 5;
0270         return;
0271     }
0272 }
0273 
0274 bool OriginAnyParser::readDataSetElement() {
0275     /* get info and values of a DataSet (worksheet column, matrix sheet, ...)
0276      * return true if a DataSet is found, otherwise return false */
0277     unsigned int dse_header_size = 0, dse_data_size = 0, dse_mask_size = 0;
0278     std::streamoff dsh_start = 0, dsd_start = 0, dsm_start = 0;
0279     string dse_header;
0280 
0281     // get dataset header size
0282     dse_header_size = readObjectSize();
0283     if (dse_header_size == 0) return false;
0284 
0285     curpos = file.tellg();
0286     dsh_start = curpos;
0287     LOG_PRINT(logfile, "Column: header size %d [0x%X], starts at %" PRId64 " [0x%" PRIx64 "], ", dse_header_size, dse_header_size, curpos, curpos)
0288     dse_header = readObjectAsString(dse_header_size);
0289 
0290     // get known info
0291     string name(25,0);
0292     name = dse_header.substr(0x58,25);
0293 
0294     // go to end of dataset header, get data size
0295     file.seekg(dsh_start+dse_header_size+1, ios_base::beg);
0296     dse_data_size = readObjectSize();
0297     dsd_start = file.tellg();
0298     string dse_data = readObjectAsString(dse_data_size);
0299     curpos = file.tellg();
0300     LOG_PRINT(logfile, "data size %d [0x%X], from %" PRId64 " [0x%" PRIx64 "] to %" PRId64 " [0x%" PRIx64 "],", dse_data_size, dse_data_size, dsd_start, dsd_start, curpos, curpos)
0301 
0302     // get data values
0303     getColumnInfoAndData(dse_header, dse_header_size, dse_data, dse_data_size);
0304 
0305     // go to end of data values, get mask size (often zero)
0306     file.seekg(dsd_start+dse_data_size, ios_base::beg); // dse_data_size can be zero
0307     if (dse_data_size > 0) file.seekg(1, ios_base::cur);
0308     dse_mask_size = readObjectSize();
0309     dsm_start = file.tellg();
0310     if (dse_mask_size > 0) LOG_PRINT(logfile, "\nmask size %d [0x%X], starts at %" PRId64 " [0x%" PRIx64 "]", dse_mask_size, dse_mask_size, dsm_start, dsm_start)
0311     string dse_mask = readObjectAsString(dse_mask_size);
0312 
0313     // get mask values
0314     if (dse_mask_size > 0) {
0315         curpos = file.tellg();
0316         LOG_PRINT(logfile, ", ends at %" PRId64 " [0x%" PRIx64 "]\n", curpos, curpos)
0317         // TODO: extract mask values from dse_mask
0318         // go to end of dataset mask
0319         file.seekg(dsm_start+dse_mask_size+1, ios_base::beg);
0320     }
0321     curpos = file.tellg();
0322     LOG_PRINT(logfile, " ends at %" PRId64 " [0x%" PRIx64 "]: ", curpos, curpos)
0323     LOG_PRINT(logfile, "%s\n", name.c_str())
0324 
0325     return true;
0326 }
0327 
0328 bool OriginAnyParser::readWindowElement() {
0329     /* get general info and details of a window
0330      * return true if a Window is found, otherwise return false */
0331     unsigned int wde_header_size = 0;
0332     std::streamoff wdh_start = 0;
0333 
0334     // get window header size
0335     wde_header_size = readObjectSize();
0336     if (wde_header_size == 0) return false;
0337 
0338     curpos = file.tellg();
0339     wdh_start = curpos;
0340     LOG_PRINT(logfile, "Window found: header size %d [0x%X], starts at %" PRId64 " [0x%" PRIx64 "]: ", wde_header_size, wde_header_size, curpos, curpos)
0341     string wde_header = readObjectAsString(wde_header_size);
0342 
0343     // get known info
0344     string name(25,0);
0345     name = wde_header.substr(0x02,25).c_str();
0346     LOG_PRINT(logfile, "%s\n", name.c_str())
0347 
0348     // classify type of window
0349     ispread = findSpreadByName(name);
0350     imatrix = findMatrixByName(name);
0351     iexcel  = findExcelByName(name);
0352     igraph = -1;
0353 
0354     if (ispread != -1) {
0355         LOG_PRINT(logfile, "\n  Window is a Worksheet book\n")
0356         getWindowProperties(spreadSheets[ispread], wde_header, wde_header_size);
0357     } else if (imatrix != -1) {
0358         LOG_PRINT(logfile, "\n  Window is a Matrix book\n")
0359         getWindowProperties(matrixes[imatrix], wde_header, wde_header_size);
0360     } else if (iexcel != -1) {
0361         LOG_PRINT(logfile, "\n  Window is an Excel book\n")
0362         getWindowProperties(excels[iexcel], wde_header, wde_header_size);
0363     } else {
0364         LOG_PRINT(logfile, "\n  Window is a Graph\n")
0365         graphs.push_back(Graph(name));
0366         igraph = (int)graphs.size()-1;
0367         getWindowProperties(graphs[igraph], wde_header, wde_header_size);
0368     }
0369 
0370     // go to end of window header
0371     file.seekg(wdh_start+wde_header_size+1, ios_base::beg);
0372 
0373     // get layer list
0374     unsigned int layer_list_size = 0;
0375 
0376     LOG_PRINT(logfile, " Reading Layers ...\n")
0377     while (true) {
0378         ilayer = layer_list_size;
0379         if (!readLayerElement()) break;
0380         layer_list_size++;
0381     }
0382     LOG_PRINT(logfile, " ... done. Layers: %d\n", layer_list_size)
0383     curpos = file.tellg();
0384     LOG_PRINT(logfile, "window ends at %" PRId64 " [0x%" PRIx64 "]\n", curpos, curpos)
0385 
0386     return true;
0387 }
0388 
0389 bool OriginAnyParser::readLayerElement() {
0390     /* get general info and details of a layer
0391      * return true if a Layer is found, otherwise return false */
0392     unsigned int lye_header_size = 0;
0393     std::streamoff lyh_start = 0;
0394 
0395     // get layer header size
0396     lye_header_size = readObjectSize();
0397     if (lye_header_size == 0) return false;
0398 
0399     curpos = file.tellg();
0400     lyh_start = curpos;
0401     LOG_PRINT(logfile, "  Layer found: header size %d [0x%X], starts at %" PRId64 " [0x%" PRIx64 "]\n", lye_header_size, lye_header_size, curpos, curpos)
0402     string lye_header = readObjectAsString(lye_header_size);
0403 
0404     // get known info
0405     getLayerProperties(lye_header, lye_header_size);
0406 
0407     // go to end of layer header
0408     file.seekg(lyh_start+lye_header_size+1, ios_base::beg);
0409 
0410     // get annotation list
0411     unsigned int annotation_list_size = 0;
0412 
0413     LOG_PRINT(logfile, "   Reading Annotations ...\n")
0414     /* Some annotations can be groups of annotations. We need a recursive function for those cases */
0415     annotation_list_size = readAnnotationList();
0416     if (annotation_list_size > 0) {
0417         LOG_PRINT(logfile, "   ... done. Annotations: %d\n", annotation_list_size)
0418     }
0419 
0420     // get curve list
0421     unsigned int curve_list_size = 0;
0422 
0423     LOG_PRINT(logfile, "   Reading Curves ...\n")
0424     while (true) {
0425         if (!readCurveElement()) break;
0426         curve_list_size++;
0427     }
0428     LOG_PRINT(logfile, "   ... done. Curves: %d\n", curve_list_size)
0429 
0430     // get axisbreak list
0431     unsigned int axisbreak_list_size = 0;
0432 
0433     LOG_PRINT(logfile, "   Reading Axis breaks ...\n")
0434     while (true) {
0435         if (!readAxisBreakElement()) break;
0436         axisbreak_list_size++;
0437     }
0438     LOG_PRINT(logfile, "   ... done. Axis breaks: %d\n", axisbreak_list_size)
0439 
0440     // get x axisparameter list
0441     unsigned int axispar_x_list_size = 0;
0442 
0443     LOG_PRINT(logfile, "   Reading x-Axis parameters ...\n")
0444     while (true) {
0445         if (!readAxisParameterElement(1)) break;
0446         axispar_x_list_size++;
0447     }
0448     LOG_PRINT(logfile, "   ... done. x-Axis parameters: %d\n", axispar_x_list_size)
0449 
0450     // get y axisparameter list
0451     unsigned int axispar_y_list_size = 0;
0452 
0453     LOG_PRINT(logfile, "   Reading y-Axis parameters ...\n")
0454     while (true) {
0455         if (!readAxisParameterElement(2)) break;
0456         axispar_y_list_size++;
0457     }
0458     LOG_PRINT(logfile, "   ... done. y-Axis parameters: %d\n", axispar_y_list_size)
0459 
0460     // get z axisparameter list
0461     unsigned int axispar_z_list_size = 0;
0462 
0463     LOG_PRINT(logfile, "   Reading z-Axis parameters ...\n")
0464     while (true) {
0465         if (!readAxisParameterElement(3)) break;
0466         axispar_z_list_size++;
0467     }
0468     LOG_PRINT(logfile, "   ... done. z-Axis parameters: %d\n", axispar_z_list_size)
0469 
0470     curpos = file.tellg();
0471     LOG_PRINT(logfile, "  layer ends at %" PRId64 " [0x%" PRIx64 "]\n", curpos, curpos)
0472 
0473     return true;
0474 }
0475 
0476 unsigned int OriginAnyParser::readAnnotationList() {
0477     /* Purpose of this function is to allow recursive call for groups of annotation elements. */
0478     unsigned int annotation_list_size = 0;
0479 
0480     while (true) {
0481         if (!readAnnotationElement()) break;
0482         annotation_list_size++;
0483     }
0484     return annotation_list_size;
0485 }
0486 
0487 bool OriginAnyParser::readAnnotationElement() {
0488     /* get general info and details of an Annotation
0489      * return true if an Annotation is found, otherwise return false */
0490     unsigned int ane_header_size = 0;
0491     std::streamoff anh_start = 0;
0492 
0493     // get annotation header size
0494     ane_header_size = readObjectSize();
0495     if (ane_header_size == 0) return false;
0496 
0497     curpos = file.tellg();
0498     anh_start = curpos;
0499     LOG_PRINT(logfile, "    Annotation found: header size %d [0x%X], starts at %" PRId64 " [0x%" PRIx64 "]: ", ane_header_size, ane_header_size, curpos, curpos)
0500     string ane_header = readObjectAsString(ane_header_size);
0501 
0502     // get known info
0503     string name(41,0);
0504     name = ane_header.substr(0x46,41);
0505     LOG_PRINT(logfile, "%s\n", name.c_str())
0506 
0507     // go to end of annotation header
0508     file.seekg(anh_start+ane_header_size+1, ios_base::beg);
0509 
0510     // data of an annotation element is divided in three blocks
0511     // first block
0512     unsigned int ane_data_1_size = 0;
0513     std::streamoff andt1_start = 0;
0514     ane_data_1_size = readObjectSize();
0515 
0516     andt1_start = file.tellg();
0517     LOG_PRINT(logfile, "     block 1 size %d [0x%X] at %" PRId64 " [0x%" PRIx64 "]\n", ane_data_1_size, ane_data_1_size, andt1_start, andt1_start)
0518     string andt1_data = readObjectAsString(ane_data_1_size);
0519 
0520     // TODO: get known info
0521 
0522     // go to end of first data block
0523     file.seekg(andt1_start+ane_data_1_size+1, ios_base::beg);
0524 
0525     // second block
0526     unsigned int ane_data_2_size = 0;
0527     std::streamoff andt2_start = 0;
0528     ane_data_2_size = readObjectSize();
0529     andt2_start = file.tellg();
0530     LOG_PRINT(logfile, "     block 2 size %d [0x%X] at %" PRId64 " [0x%" PRIx64 "]\n", ane_data_2_size, ane_data_2_size, andt2_start, andt2_start)
0531     string andt2_data;
0532 
0533     // check for group of annotations
0534     if (((ane_data_1_size == 0x5e) || (ane_data_1_size == 0x0A)) && (ane_data_2_size == 0x04)) {
0535         curpos = file.tellg();
0536         LOG_PRINT(logfile, "  Annotation group found at %" PRId64 " [0x%" PRIx64 "] ...\n", curpos, curpos)
0537         unsigned int angroup_size = readAnnotationList();
0538         curpos = file.tellg();
0539         if (angroup_size > 0) {
0540             LOG_PRINT(logfile, "  ... group end at %" PRId64 " [0x%" PRIx64 "]. Annotations: %d\n", curpos, curpos, angroup_size)
0541         }
0542         andt2_data = string();
0543     } else {
0544         andt2_data = readObjectAsString(ane_data_2_size);
0545         // TODO: get known info
0546         // go to end of second data block
0547         file.seekg(andt2_start+ane_data_2_size, ios_base::beg);
0548         if (ane_data_2_size > 0) file.seekg(1, ios_base::cur);
0549     }
0550 
0551     // third block
0552     unsigned int ane_data_3_size = 0;
0553     ane_data_3_size = readObjectSize();
0554 
0555     std::streamoff andt3_start = file.tellg();
0556     if (andt3_start > 0) {
0557         LOG_PRINT(logfile, "     block 3 size %d [0x%X] at %" PRId64 " [0x%" PRIx64 "]\n", ane_data_3_size, ane_data_3_size, andt3_start, andt3_start)
0558     }
0559     string andt3_data = readObjectAsString(ane_data_3_size);
0560 
0561     curpos = file.tellg();
0562     LOG_PRINT(logfile, "    annotation ends at %" PRId64 " [0x%" PRIx64 "]\n", curpos, curpos)
0563 
0564     // get annotation info
0565     getAnnotationProperties(ane_header, ane_header_size, andt1_data, ane_data_1_size, andt2_data, ane_data_2_size, andt3_data, ane_data_3_size);
0566 
0567     return true;
0568 }
0569 
0570 bool OriginAnyParser::readCurveElement() {
0571     /* get general info and details of a Curve
0572      * return true if a Curve is found, otherwise return false */
0573     unsigned int cve_header_size = 0, cve_data_size = 0;
0574     std::streamoff cvh_start = 0, cvd_start = 0;
0575 
0576     // get curve header size
0577     cve_header_size = readObjectSize();
0578     if (cve_header_size == 0) return false;
0579 
0580     curpos = file.tellg();
0581     cvh_start = curpos;
0582     LOG_PRINT(logfile, "    Curve: header size %d [0x%X], starts at %" PRId64 " [0x%" PRIx64 "], ", cve_header_size, cve_header_size, curpos, curpos)
0583     string cve_header = readObjectAsString(cve_header_size);
0584 
0585     // TODO: get known info from curve header
0586     string name = cve_header.substr(0x12,12);
0587 
0588     // go to end of header, get curve data size
0589     file.seekg(cvh_start+cve_header_size+1, ios_base::beg);
0590     cve_data_size = readObjectSize();
0591     cvd_start = file.tellg();
0592     LOG_PRINT(logfile, "data size %d [0x%X], from %" PRId64 " [0x%" PRIx64 "]", cve_data_size, cve_data_size, cvd_start, cvd_start)
0593     string cve_data = readObjectAsString(cve_data_size);
0594 
0595     // TODO: get known info from curve data
0596 
0597     // go to end of data
0598     file.seekg(cvd_start+cve_data_size, ios_base::beg);
0599     if (cve_data_size > 0) file.seekg(1, ios_base::cur);
0600 
0601     curpos = file.tellg();
0602     LOG_PRINT(logfile, "to %" PRId64 " [0x%" PRIx64 "]: %s\n", curpos, curpos, name.c_str())
0603 
0604     // get curve (or column) info
0605     getCurveProperties(cve_header, cve_header_size, cve_data, cve_data_size);
0606 
0607     return true;
0608 }
0609 
0610 bool OriginAnyParser::readAxisBreakElement() {
0611     /* get info of Axis breaks
0612      * return true if an Axis break, otherwise return false */
0613     unsigned int abe_data_size = 0;
0614     std::streamoff abd_start = 0;
0615 
0616     // get axis break data size
0617     abe_data_size = readObjectSize();
0618     if (abe_data_size == 0) return false;
0619 
0620     curpos = file.tellg();
0621     abd_start = curpos;
0622     string abd_data = readObjectAsString(abe_data_size);
0623 
0624     // get known info
0625 
0626     // go to end of axis break data
0627     file.seekg(abd_start+abe_data_size+1, ios_base::beg);
0628 
0629     // get axis break info
0630     getAxisBreakProperties(abd_data, abe_data_size);
0631 
0632     return true;
0633 }
0634 
0635 bool OriginAnyParser::readAxisParameterElement(unsigned int naxis) {
0636     /* get info of Axis parameters for naxis-axis (x,y,z) = (1,2,3)
0637      * return true if an Axis break is found, otherwise return false */
0638     unsigned int ape_data_size = 0;
0639     std::streamoff apd_start = 0;
0640 
0641     // get axis break data size
0642     ape_data_size = readObjectSize();
0643     if (ape_data_size == 0) return false;
0644 
0645     curpos = file.tellg();
0646     apd_start = curpos;
0647     string apd_data = readObjectAsString(ape_data_size);
0648 
0649     // get known info
0650 
0651     // go to end of axis break data
0652     file.seekg(apd_start+ape_data_size+1, ios_base::beg);
0653 
0654     // get axis parameter info
0655     getAxisParameterProperties(apd_data, ape_data_size, naxis);
0656 
0657     return true;
0658 }
0659 
0660 bool OriginAnyParser::readParameterElement() {
0661     // get parameter name
0662     string par_name;
0663     char c;
0664 
0665     getline(file, par_name);
0666     if (par_name[0] == '\0') {
0667         unsigned int eof_parameters_mark = readObjectSize();
0668         if (eof_parameters_mark != 0) {
0669             LOG_PRINT(logfile, "Wrong end of parameters mark\n")
0670         }
0671         return false;
0672     }
0673     LOG_PRINT(logfile, " %s:", par_name.c_str())
0674     // get value
0675     double value;
0676     file >> value;
0677     LOG_PRINT(logfile, " %g\n", value)
0678     // read the '\n'
0679     file >> c;
0680     if (c != '\n') {
0681         curpos = file.tellg();
0682         LOG_PRINT(logfile, "Wrong delimiter %c at %" PRId64 " [0x%" PRIx64 "]\n", c, curpos, curpos)
0683         parseError = 6;
0684         return false;
0685     }
0686 
0687     return true;
0688 }
0689 
0690 bool OriginAnyParser::readNoteElement() {
0691     /* get info of Note windows, including "Results Log"
0692      * return true if a Note window is found, otherwise return false */
0693     unsigned int nwe_header_size = 0, nwe_label_size = 0, nwe_contents_size = 0;
0694     std::streamoff nwh_start = 0, nwl_start = 0, nwc_start = 0;
0695 
0696     // get note header size
0697     nwe_header_size = readObjectSize();
0698     if (nwe_header_size == 0) return false;
0699 
0700     curpos = file.tellg();
0701     nwh_start = curpos;
0702     LOG_PRINT(logfile, "  Note window found: header size %d [0x%X], starts at %" PRId64 " [0x%" PRIx64 "]\n", nwe_header_size, nwe_header_size, curpos, curpos)
0703     string nwe_header = readObjectAsString(nwe_header_size);
0704 
0705     // TODO: get known info from header
0706 
0707     // go to end of header
0708     file.seekg(nwh_start+nwe_header_size+1, ios_base::beg);
0709 
0710     // get label size
0711     nwe_label_size = readObjectSize();
0712     nwl_start = file.tellg();
0713     string nwe_label = readObjectAsString(nwe_label_size);
0714     LOG_PRINT(logfile, "  label at %" PRId64 " [0x%" PRIx64 "]: %s\n", nwl_start, nwl_start, nwe_label.c_str())
0715 
0716     // go to end of label
0717     file.seekg(nwl_start+nwe_label_size, ios_base::beg);
0718     if (nwe_label_size > 0) file.seekg(1, ios_base::cur);
0719 
0720     // get contents size
0721     nwe_contents_size = readObjectSize();
0722     nwc_start = file.tellg();
0723     string nwe_contents = readObjectAsString(nwe_contents_size);
0724     if (nwc_start > 0) {
0725         LOG_PRINT(logfile, "  contents at %" PRId64 " [0x%" PRIx64 "]: \n%s\n", nwc_start, nwc_start, nwe_contents.c_str())
0726     }
0727 
0728     // get note window info
0729     getNoteProperties(nwe_header, nwe_header_size, nwe_label, nwe_label_size, nwe_contents, nwe_contents_size);
0730 
0731     return true;
0732 }
0733 
0734 void OriginAnyParser::readProjectTree() {
0735     unsigned int pte_depth = 0;
0736 
0737     // first preamble size and data (usually 4)
0738     unsigned int pte_pre1_size = readObjectSize();
0739     string pte_pre1 = readObjectAsString(pte_pre1_size);
0740 
0741     // second preamble size and data (usually 16)
0742     unsigned int pte_pre2_size = readObjectSize();
0743     string pte_pre2 = readObjectAsString(pte_pre2_size);
0744 
0745     // root element and children
0746     unsigned int rootfolder = readFolderTree(
0747         projectTree.insert(projectTree.begin(), ProjectNode("", ProjectNode::Folder)),
0748         pte_depth
0749     );
0750     if (rootfolder > 0) {
0751         LOG_PRINT(logfile, "Number of files at root: %d\n", rootfolder)
0752     }
0753 
0754     // epilogue (should be zero)
0755     unsigned int pte_post_size = readObjectSize();
0756     if (pte_post_size != 0) {
0757         LOG_PRINT(logfile, "Wrong end of project tree mark\n")
0758     }
0759 
0760     // log info on project tree
0761 #ifdef GENERATE_CODE_FOR_LOG
0762     outputProjectTree(cout);
0763 #endif // GENERATE_CODE_FOR_LOG
0764 
0765     return;
0766 }
0767 
0768 unsigned int OriginAnyParser::readFolderTree(tree<ProjectNode>::iterator parent, unsigned int depth) {
0769     unsigned int fle_header_size = 0, fle_eofh_size = 0, fle_name_size = 0, fle_prop_size = 0;
0770 
0771     // folder header size, data, end mark
0772     fle_header_size = readObjectSize();
0773     string fle_header = readObjectAsString(fle_header_size);
0774     fle_eofh_size = readObjectSize(); // (usually 0)
0775     if (fle_eofh_size != 0) {
0776         LOG_PRINT(logfile, "Wrong end of folder header mark")
0777     }
0778 
0779     // folder name size
0780     fle_name_size = readObjectSize();
0781     curpos = file.tellg();
0782     string fle_name = readObjectAsString(fle_name_size);
0783     LOG_PRINT(logfile, "Folder name at %" PRId64 " [0x%" PRIx64 "]: %s\n", curpos, curpos, fle_name.c_str());
0784 
0785     // additional properties
0786     fle_prop_size = readObjectSize();
0787     for (unsigned int i = 0; i < fle_prop_size; i++) {
0788         unsigned int obj_size = readObjectSize();
0789         string obj_data = readObjectAsString(obj_size);
0790     }
0791 
0792     // get project folder properties
0793     tree<ProjectNode>::iterator current_folder = projectTree.append_child(parent, ProjectNode(fle_name, ProjectNode::Folder));
0794     getProjectFolderProperties(current_folder, fle_header, fle_header_size);
0795 
0796     // file entries
0797     unsigned int number_of_files_size = 0;
0798 
0799     number_of_files_size = readObjectSize(); // should be 4 as number_of_files is an integer
0800     curpos = file.tellg();
0801     LOG_PRINT(logfile, "Number of files at %" PRId64 " [0x%" PRIx64 "] ", curpos, curpos)
0802     string fle_nfiles = readObjectAsString(number_of_files_size);
0803 
0804     istringstream stmp(ios_base::binary);
0805     stmp.str(fle_nfiles);
0806     unsigned int number_of_files = 0;
0807     GET_INT(stmp, number_of_files)
0808     LOG_PRINT(logfile, "%d\n", number_of_files)
0809 
0810     for (unsigned int i=0; i < number_of_files; i++) {
0811         readProjectLeaf(current_folder);
0812     }
0813 
0814     // subfolder entries
0815     unsigned int number_of_folders_size = 0;
0816 
0817     number_of_folders_size = readObjectSize(); // should be 4 as number_of_subfolders is an integer
0818     curpos = file.tellg();
0819     LOG_PRINT(logfile, "Number of subfolders at %" PRId64 " [0x%" PRIx64 "] ", curpos, curpos)
0820     string fle_nfolders = readObjectAsString(number_of_folders_size);
0821 
0822     stmp.str(fle_nfolders);
0823     unsigned int number_of_folders = 0;
0824     GET_INT(stmp, number_of_folders)
0825     LOG_PRINT(logfile, "%d\n", number_of_folders)
0826 
0827     for (unsigned int i=0; i < number_of_folders; i++) {
0828         depth++;
0829         unsigned int files_in_subfolder = readFolderTree(current_folder, depth);
0830         if (files_in_subfolder > 0) {
0831             LOG_PRINT(logfile, "Number of files in subfolder: %d\n", files_in_subfolder)
0832         }
0833         depth--;
0834     }
0835 
0836     return number_of_files;
0837 }
0838 
0839 void OriginAnyParser::readProjectLeaf(tree<ProjectNode>::iterator current_folder) {
0840     // preamble size (usually 0) and data
0841     unsigned int ptl_pre_size = readObjectSize();
0842     string ptl_pre = readObjectAsString(ptl_pre_size);
0843 
0844     // file data size (usually 8) and data
0845     unsigned int ptl_data_size = readObjectSize();
0846     curpos = file.tellg();
0847     string ptl_data = readObjectAsString(ptl_data_size);
0848     LOG_PRINT(logfile, "File at %" PRId64 " [0x%" PRIx64 "]\n", curpos, curpos)
0849 
0850     // epilogue (should be zero)
0851     unsigned int ptl_post_size = readObjectSize();
0852     if (ptl_post_size != 0) {
0853         LOG_PRINT(logfile, "Wrong end of project leaf mark\n")
0854     }
0855 
0856     // get project node properties
0857     getProjectLeafProperties(current_folder, ptl_data, ptl_data_size);
0858 
0859     return;
0860 }
0861 
0862 void OriginAnyParser::readAttachmentList() {
0863     /* Attachments are divided in two groups (which can be empty)
0864      first group is preceded by two integers: 4096 (0x1000) and number_of_attachments followed as usual by a '\n' mark
0865      second group is a series of (header, name, data) triplets without the '\n' mark.
0866     */
0867 
0868     // figure out if first group is not empty. In this case we will read integer=8 at current file position
0869     unsigned int att_1st_empty = 0;
0870     file >> att_1st_empty;
0871     file.seekg(-4, ios_base::cur);
0872 
0873     istringstream stmp(ios_base::binary);
0874     string att_header;
0875     if (att_1st_empty == 8) {
0876         // first group
0877         unsigned int att_list1_size = 0;
0878 
0879         // get two integers
0880         // next line fails if first attachment group is empty: readObjectSize exits as there is no '\n' after 4 bytes for uint
0881         att_list1_size = readObjectSize(); // should be 8 as we expect two integer values
0882         curpos = file.tellg();
0883         string att_list1 = readObjectAsString(att_list1_size);
0884         LOG_PRINT(logfile, "First attachment group at %" PRId64 " [0x%" PRIx64 "]", curpos, curpos)
0885 
0886         stmp.str(att_list1);
0887 
0888         unsigned int att_mark = 0, number_of_atts = 0, iattno = 0, att_data_size = 0;
0889         GET_INT(stmp, att_mark) // should be 4096
0890         GET_INT(stmp, number_of_atts)
0891         LOG_PRINT(logfile, " with %d attachments.\n", number_of_atts)
0892 
0893         for (unsigned int i=0; i < number_of_atts; i++) {
0894             /* Header is a group of 7 integers followed by \n
0895             1st  attachment mark (4096: 0x00 0x10 0x00 0x00)
0896             2nd  attachment number ( <num_of_att)
0897             3rd  attachment size
0898             4th .. 7th ???
0899             */
0900             // get header
0901             att_header = readObjectAsString(7*4);
0902             stmp.str(att_header);
0903             GET_INT(stmp, att_mark) // should be 4096
0904             GET_INT(stmp, iattno)
0905             GET_INT(stmp, att_data_size)
0906             curpos = file.tellg();
0907             LOG_PRINT(logfile, "Attachment no %d (%d) at %" PRId64 " [0x%" PRIx64 "], size %d\n", i, iattno, curpos, curpos, att_data_size)
0908 
0909             // get data
0910             string att_data = readObjectAsString(att_data_size);
0911             // even if att_data_size is zero, we get a '\n' mark
0912             if (att_data_size == 0) file.seekg(1, ios_base::cur);
0913         }
0914     } else {
0915         LOG_PRINT(logfile, "First attachment group is empty\n")
0916     }
0917 
0918     /* Second group is a series of (header, name, data) triplets
0919        There is no number of attachments. It ends when we reach EOF. */
0920     curpos = file.tellg();
0921     LOG_PRINT(logfile, "Second attachment group starts at %" PRId64 " [0x%" PRIx64 "], file size %" PRId64 "\n", curpos, curpos, d_file_size)
0922     /* Header is a group of 3 integers, with no '\n' at end
0923         1st attachment header+name size including itself
0924         2nd attachment type (0x59 0x04 0xCA 0x7F for excel workbooks)
0925         3rd size of data */
0926 
0927     // get header
0928     att_header = string(12,0);
0929     while (true) {
0930         // check for eof
0931         if ((d_file_size == file.tellg()) || (file.eof())) break;
0932         // cannot use readObjectAsString: there is no '\n' at end
0933         file.read(reinterpret_cast<char*>(&att_header[0]), 12);
0934 
0935         if (file.gcount() != 12) break;
0936         // get header size, type and data size
0937         unsigned int att_header_size=0, att_type=0, att_size=0;
0938         stmp.str(att_header);
0939         GET_INT(stmp, att_header_size)
0940         GET_INT(stmp, att_type)
0941         GET_INT(stmp, att_size)
0942 
0943         // get name and data
0944         unsigned int name_size = att_header_size - 3*4;
0945         string att_name = string(name_size, 0);
0946         file.read(&att_name[0], name_size);
0947         curpos = file.tellg();
0948         string att_data = string(att_size, 0);
0949         file.read(&att_data[0], att_size);
0950         LOG_PRINT(logfile, "attachment at %" PRId64 " [0x%" PRIx64 "], type 0x%X, size %d [0x%X]: %s\n", curpos, curpos, att_type, att_size, att_size, att_name.c_str())
0951     }
0952 
0953     return;
0954 }
0955 
0956 bool OriginAnyParser::getColumnInfoAndData(const string& col_header, unsigned int col_header_size, const string& col_data, unsigned int col_data_size) {
0957     istringstream stmp(ios_base::binary);
0958     short data_type;
0959     char data_type_u;
0960     unsigned char valuesize;
0961     string name(25,0), column_name;
0962 
0963     stmp.str(col_header.substr(0x16));
0964     GET_SHORT(stmp, data_type);
0965 
0966     data_type_u = col_header[0x3F];
0967     if (fileVersion == 350) {
0968         valuesize = col_header[0x36];
0969     } else {
0970         valuesize = col_header[0x3D];
0971     }
0972     if(valuesize == 0) {
0973         LOG_PRINT(logfile, "    WARNING : found strange valuesize of %d\n", (int)valuesize);
0974         valuesize = 8;
0975     }
0976 
0977     if (fileVersion == 350) {
0978         name = col_header.substr(0x57,25).c_str();
0979     } else {
0980         name = col_header.substr(0x58,25).c_str();
0981     }
0982     string dataset_name = name;
0983     string::size_type colpos = name.find_last_of("_");
0984 
0985     if(colpos != string::npos){
0986         column_name = name.substr(colpos + 1);
0987         name.resize(colpos);
0988     }
0989 
0990     LOG_PRINT(logfile, "\n  data_type 0x%.4X, data_type_u 0x%.2X, valuesize %d [0x%X], %s [%s]\n", data_type, data_type_u, valuesize, valuesize, name.c_str(), column_name.c_str());
0991 
0992     int total_rows, first_row, last_row;
0993     stmp.str(col_header.substr(0x19));
0994     GET_INT(stmp, total_rows);
0995     GET_INT(stmp, first_row);
0996     GET_INT(stmp, last_row);
0997     LOG_PRINT(logfile, "  total %d, first %d, last %d rows\n", total_rows, first_row, last_row)
0998 
0999     unsigned short signature;
1000     if (col_header_size > 0x72) {
1001         stmp.str(col_header.substr(0x71));
1002         GET_SHORT(stmp, signature);
1003     } else {
1004         LOG_PRINT(logfile, "  NOTE: alternative signature determination\n")
1005         signature = col_header[0x18];
1006     }
1007     LOG_PRINT(logfile, "  signature %d [0x%X], valuesize %d size %d ", signature, signature, valuesize, col_data_size)
1008 
1009 
1010     size_t current_col = 1;//, nr = 0, nbytes = 0;
1011     static unsigned int col_index = 0;
1012     unsigned int current_sheet = 0;
1013     vector<Origin::SpreadSheet>::difference_type spread = 0;
1014 
1015     if (column_name.empty()) { // Matrix or function
1016         if (data_type == 0x6081) { // Function
1017             functions.push_back(Function(name, objectIndex));
1018             Origin::Function &f = functions.back();
1019             f.formula = toLowerCase(col_data.c_str());
1020 
1021             stmp.str(col_header.substr(0x0A));
1022             short t;
1023             GET_SHORT(stmp, t)
1024             if (t == 0x1194)
1025                 f.type = Function::Polar;
1026 
1027             stmp.str(col_header.substr(0x21));
1028             GET_INT(stmp, f.totalPoints)
1029             GET_DOUBLE(stmp, f.begin)
1030             double d;
1031             GET_DOUBLE(stmp, d)
1032             f.end = f.begin + d*(f.totalPoints - 1);
1033 
1034             LOG_PRINT(logfile, "\n NEW FUNCTION: %s = %s", f.name.c_str(), f.formula.c_str());
1035             LOG_PRINT(logfile, ". Range [%g : %g], number of points: %d\n", f.begin, f.end, f.totalPoints);
1036 
1037         } else { // Matrix
1038             vector<Origin::Matrix>::difference_type mIndex = -1;
1039             string::size_type pos = name.find_first_of("@");
1040             if (pos != string::npos){
1041                 string sheetName = name;
1042                 name.resize(pos);
1043                 mIndex = findMatrixByName(name);
1044                 if (mIndex != -1){
1045                     LOG_PRINT(logfile, "\n  NEW MATRIX SHEET\n");
1046                     matrixes[mIndex].sheets.push_back(MatrixSheet(sheetName, objectIndex));
1047                 }
1048             } else {
1049                 LOG_PRINT(logfile, "\n  NEW MATRIX\n");
1050                 matrixes.push_back(Matrix(name));
1051                 matrixes.back().sheets.push_back(MatrixSheet(name, objectIndex));
1052             }
1053             // add an empty data set to keep objectIndex synchronized with datasets.size()
1054             datasets.push_back(SpreadColumn(name,objectIndex));
1055             getMatrixValues(col_data, col_data_size, data_type, data_type_u, valuesize, mIndex);
1056         }
1057     } else {
1058         if(spreadSheets.size() == 0 || findSpreadByName(name) == -1) {
1059             LOG_PRINT(logfile, "\n  NEW SPREADSHEET\n");
1060             current_col = 1;
1061             spreadSheets.push_back(SpreadSheet(name));
1062             spread = spreadSheets.size() - 1;
1063             spreadSheets.back().maxRows = 0;
1064             current_sheet = 0;
1065         } else {
1066             spread = findSpreadByName(name);
1067             current_col = spreadSheets[spread].columns.size();
1068             if(!current_col)
1069                 current_col = 1;
1070             ++current_col;
1071         }
1072         spreadSheets[spread].columns.push_back(SpreadColumn(column_name, objectIndex));
1073         spreadSheets[spread].columns.back().colIndex = ++col_index;
1074         spreadSheets[spread].columns.back().dataset_name = dataset_name;
1075         spreadSheets[spread].columns.back().numRows = total_rows;
1076         spreadSheets[spread].columns.back().beginRow = first_row;
1077         spreadSheets[spread].columns.back().endRow = last_row;
1078 
1079         string::size_type sheetpos = spreadSheets[spread].columns.back().name.find_last_of("@");
1080         if(sheetpos != string::npos){
1081             unsigned int sheet = strtol(column_name.substr(sheetpos + 1).c_str(), nullptr, 10);
1082             if( sheet > 1){
1083                 spreadSheets[spread].columns.back().name = column_name;
1084 
1085                 if (current_sheet != (sheet - 1))
1086                     current_sheet = sheet - 1;
1087 
1088                 spreadSheets[spread].columns.back().sheet = current_sheet;
1089                 if (spreadSheets[spread].sheets < sheet)
1090                     spreadSheets[spread].sheets = sheet;
1091             }
1092         }
1093         LOG_PRINT(logfile, "  data index %d, valuesize %d, ", objectIndex, valuesize)
1094 
1095         unsigned int nr = col_data_size / valuesize;
1096         LOG_PRINT(logfile, "n. of rows = %d\n\n", nr)
1097 
1098         spreadSheets[spread].maxRows<nr ? spreadSheets[spread].maxRows=nr : 0;
1099         stmp.str(col_data);
1100         for(unsigned int i = 0; i < nr; ++i)
1101         {
1102             double value;
1103             if(valuesize <= 8)  // Numeric, Time, Date, Month, Day
1104             {
1105                 GET_DOUBLE(stmp, value)
1106                 if ((i < 5) || (i > (nr-5))) {
1107                     LOG_PRINT(logfile, "%g ", value)
1108                 } else if (i == 5) {
1109                     LOG_PRINT(logfile, "... ")
1110                 }
1111                 spreadSheets[spread].columns[(current_col-1)].data.push_back(value);
1112             }
1113             else if((data_type & 0x100) == 0x100) // Text&Numeric
1114             {
1115                 unsigned char c = col_data[i*valuesize];
1116                 stmp.seekg(i*valuesize+2, ios_base::beg);
1117                 if(c != 1) //value
1118                 {
1119                     GET_DOUBLE(stmp, value);
1120                     if ((i < 5) || (i > (nr-5))) {
1121                         LOG_PRINT(logfile, "%g ", value)
1122                     } else if (i == 5) {
1123                         LOG_PRINT(logfile, "... ")
1124                     }
1125                     spreadSheets[spread].columns[(current_col-1)].data.push_back(value);
1126                 }
1127                 else //text
1128                 {
1129                     string svaltmp = col_data.substr(i*valuesize+2, valuesize-2);
1130                     // TODO: check if this test is still needed
1131                     if(svaltmp.find(0x0E) != string::npos) { // try find non-printable symbol - garbage test
1132                         svaltmp = string();
1133                         LOG_PRINT(logfile, "Non printable symbol found, place 1 for i=%d\n", i)
1134                     }
1135                     if ((i < 5) || (i > (nr-5))) {
1136                         LOG_PRINT(logfile, "\"%s\" ", svaltmp.c_str())
1137                     } else if (i == 5) {
1138                         LOG_PRINT(logfile, "... ")
1139                     }
1140                     spreadSheets[spread].columns[(current_col-1)].data.push_back(svaltmp);
1141                 }
1142             }
1143             else //text
1144             {
1145                 string svaltmp = col_data.substr(i*valuesize, valuesize).c_str();
1146                 // TODO: check if this test is still needed
1147                 if(svaltmp.find(0x0E) != string::npos) { // try find non-printable symbol - garbage test
1148                     svaltmp = string();
1149                     LOG_PRINT(logfile, "Non printable symbol found, place 2 for i=%d\n", i)
1150                 }
1151                 if ((i < 5) || (i > (nr-5))) {
1152                     LOG_PRINT(logfile, "\"%s\" ", svaltmp.c_str())
1153                 } else if (i == 5) {
1154                     LOG_PRINT(logfile, "... ")
1155                 }
1156                 spreadSheets[spread].columns[(current_col-1)].data.push_back(svaltmp);
1157             }
1158         }
1159         LOG_PRINT(logfile, "\n\n")
1160         datasets.push_back(spreadSheets[spread].columns.back());
1161     }
1162     ++objectIndex;
1163 
1164     return true;
1165 }
1166 
1167 void OriginAnyParser::getMatrixValues(const string& col_data, unsigned int col_data_size, short data_type, char data_type_u, char valuesize, vector<Origin::Matrix>::difference_type mIndex) {
1168     if (matrixes.empty())
1169         return;
1170 
1171     istringstream stmp;
1172     stmp.str(col_data);
1173 
1174     if (mIndex < 0)
1175         mIndex = (vector<Origin::Matrix>::difference_type)matrixes.size() - 1;
1176 
1177     unsigned int size = col_data_size/valuesize;
1178     bool logValues = true;
1179     switch(data_type){
1180         case 0x6001://double
1181             for(unsigned int i = 0; i < size; ++i){
1182                 double value;
1183                 GET_DOUBLE(stmp, value)
1184                 matrixes[mIndex].sheets.back().data.push_back(value);
1185             }
1186             break;
1187         case 0x6003://float
1188             for(unsigned int i = 0; i < size; ++i){
1189                 float value;
1190                 GET_FLOAT(stmp, value)
1191                 matrixes[mIndex].sheets.back().data.push_back((double)value);
1192             }
1193             break;
1194         case 0x6801://int
1195             if (data_type_u == 8){//unsigned
1196                 for(unsigned int i = 0; i < size; ++i){
1197                     unsigned int value;
1198                     GET_INT(stmp, value)
1199                     matrixes[mIndex].sheets.back().data.push_back((double)value);
1200                 }
1201             } else {
1202                 for(unsigned int i = 0; i < size; ++i){
1203                     int value;
1204                     GET_INT(stmp, value)
1205                     matrixes[mIndex].sheets.back().data.push_back((double)value);
1206                 }
1207             }
1208             break;
1209         case 0x6803://short
1210             if (data_type_u == 8){//unsigned
1211                 for(unsigned int i = 0; i < size; ++i){
1212                     unsigned short value;
1213                     GET_SHORT(stmp, value)
1214                     matrixes[mIndex].sheets.back().data.push_back((double)value);
1215                 }
1216             } else {
1217                 for(unsigned int i = 0; i < size; ++i){
1218                     short value;
1219                     GET_SHORT(stmp, value)
1220                     matrixes[mIndex].sheets.back().data.push_back((double)value);
1221                 }
1222             }
1223             break;
1224         case 0x6821://char
1225             if (data_type_u == 8){//unsigned
1226                 for(unsigned int i = 0; i < size; ++i){
1227                     unsigned char value;
1228                     value = col_data[i];
1229                     matrixes[mIndex].sheets.back().data.push_back((double)value);
1230                 }
1231             } else {
1232                 for(unsigned int i = 0; i < size; ++i){
1233                     char value;
1234                     value = col_data[i];
1235                     matrixes[mIndex].sheets.back().data.push_back((double)value);
1236                 }
1237             }
1238             break;
1239         default:
1240             LOG_PRINT(logfile, "    UNKNOWN MATRIX DATATYPE: %02X SKIP DATA\n", data_type);
1241             matrixes.pop_back();
1242             logValues = false;
1243     }
1244 
1245     if (logValues){
1246         LOG_PRINT(logfile, "    FIRST 10 CELL VALUES: ");
1247         for(unsigned int i = 0; i < 10 && i < matrixes[mIndex].sheets.back().data.size(); ++i)
1248             LOG_PRINT(logfile, "%g\t", matrixes[mIndex].sheets.back().data[i]);
1249     }
1250 }
1251 
1252 void OriginAnyParser::getWindowProperties(Origin::Window& window, const string& wde_header, unsigned int wde_header_size) {
1253     window.objectID = objectIndex;
1254     ++objectIndex;
1255 
1256     istringstream stmp;
1257 
1258     stmp.str(wde_header.substr(0x1B));
1259     GET_SHORT(stmp, window.frameRect.left)
1260     GET_SHORT(stmp, window.frameRect.top)
1261     GET_SHORT(stmp, window.frameRect.right)
1262     GET_SHORT(stmp, window.frameRect.bottom)
1263 
1264     char c = wde_header[0x32];
1265 
1266     if(c & 0x01)
1267         window.state = Window::Minimized;
1268     else if(c & 0x02)
1269         window.state = Window::Maximized;
1270 
1271     if (wde_header[0x42] != 0) {
1272         window.windowBackgroundColorGradient = (ColorGradientDirection)(wde_header[0x42] >> 2);
1273         window.windowBackgroundColorBase.type = Color::Regular;
1274         window.windowBackgroundColorBase.regular = wde_header[0x43];
1275         window.windowBackgroundColorEnd.type =  Color::Regular;
1276         window.windowBackgroundColorEnd.regular = wde_header[0x44];
1277     } else {
1278         window.windowBackgroundColorGradient = ColorGradientDirection::NoGradient;
1279         window.windowBackgroundColorBase.type = Color::Regular;
1280         window.windowBackgroundColorBase.regular = Color::White;
1281         window.windowBackgroundColorEnd.type =  Color::Regular;
1282         window.windowBackgroundColorEnd.regular = Color::White;
1283     }
1284     LOG_PRINT(logfile, "ColorGradient %d, base %d, end %d\n",window.windowBackgroundColorGradient,
1285         window.windowBackgroundColorBase.regular, window.windowBackgroundColorEnd.regular);
1286 
1287     c = wde_header[0x69];
1288 
1289     if(c & 0x01)
1290         window.title = Window::Label;
1291     else if(c & 0x02)
1292         window.title = Window::Name;
1293     else
1294         window.title = Window::Both;
1295 
1296     window.hidden = ((c & 0x08) != 0);
1297     if (window.hidden) {
1298         LOG_PRINT(logfile, "            WINDOW %d NAME : %s is hidden\n", objectIndex, window.name.c_str());
1299     } else {
1300         LOG_PRINT(logfile, "            WINDOW %d NAME : %s is not hidden\n", objectIndex, window.name.c_str());
1301     }
1302 
1303     if (wde_header_size > 0x82) {
1304         // only projects of version 6.0 and higher have these
1305         double creationDate, modificationDate;
1306         stmp.str(wde_header.substr(0x73));
1307         GET_DOUBLE(stmp, creationDate);
1308         window.creationDate = doubleToPosixTime(creationDate);
1309         GET_DOUBLE(stmp, modificationDate)
1310         window.modificationDate = doubleToPosixTime(modificationDate);
1311     }
1312 
1313     if ((wde_header_size > 0x9E) && (wde_header[0x42] != 0)) {
1314         // get window background colors for version > 5.0
1315         window.windowBackgroundColorBase = getColor(wde_header.substr(0x97,4));
1316         window.windowBackgroundColorEnd = getColor(wde_header.substr(0x9B,4));
1317     }
1318 
1319     if(wde_header_size > 0xC3){
1320         window.label = wde_header.substr(0xC3).c_str();
1321         window.label = window.label.substr(0,window.label.find("@${"));
1322         LOG_PRINT(logfile, "            WINDOW %d LABEL: %s\n", objectIndex, window.label.c_str());
1323     }
1324 
1325     if (imatrix != -1) { // additional properties for matrix windows
1326         unsigned char h = wde_header[0x29];
1327         matrixes[imatrix].activeSheet = h;
1328         if (wde_header_size > 0x86) {
1329             h = wde_header[0x87];
1330             matrixes[imatrix].header = (h == 194) ? Matrix::XY : Matrix::ColumnRow;
1331         }
1332     }
1333     if (igraph != -1) { // additional properties for graph/layout windows
1334         stmp.str(wde_header.substr(0x23));
1335         GET_SHORT(stmp, graphs[igraph].width)
1336         GET_SHORT(stmp, graphs[igraph].height)
1337 
1338         unsigned char c = wde_header[0x38];
1339         graphs[igraph].connectMissingData = ((c & 0x40) != 0);
1340 
1341         string templateName = wde_header.substr(0x45,20).c_str();
1342         graphs[igraph].templateName = templateName;
1343         if (templateName == "LAYOUT") graphs[igraph].isLayout = true;
1344     }
1345 }
1346 
1347 void OriginAnyParser::getLayerProperties(const string& lye_header, unsigned int lye_header_size) {
1348     istringstream stmp;
1349 
1350     if (ispread != -1) { // spreadsheet
1351 
1352         spreadSheets[ispread].loose = false;
1353 
1354     } else if (imatrix != -1) { // matrix
1355 
1356         MatrixSheet& sheet = matrixes[imatrix].sheets[ilayer];
1357 
1358         unsigned short width = 8;
1359         stmp.str(lye_header.substr(0x27));
1360         GET_SHORT(stmp, width)
1361         if (width == 0) width = 8;
1362         sheet.width = width;
1363 
1364         stmp.str(lye_header.substr(0x2B));
1365         GET_SHORT(stmp, sheet.columnCount)
1366 
1367         stmp.str(lye_header.substr(0x52));
1368         GET_SHORT(stmp, sheet.rowCount)
1369 
1370         unsigned char view = lye_header[0x71];
1371         if (view != 0x32 && view != 0x28){
1372             sheet.view = MatrixSheet::ImageView;
1373         } else {
1374             sheet.view = MatrixSheet::DataView;
1375         }
1376 
1377         if (lye_header_size > 0xD2) {
1378             sheet.name = lye_header.substr(0xD2,32).c_str();
1379         }
1380 
1381     } else if (iexcel != -1) { // excel
1382 
1383         excels[iexcel].loose = false;
1384         if (lye_header_size > 0xD2) {
1385             excels[iexcel].sheets[ilayer].name = lye_header.substr(0xD2,32).c_str();
1386         }
1387 
1388     } else { // graph
1389         graphs[igraph].layers.push_back(GraphLayer());
1390         GraphLayer& glayer = graphs[igraph].layers[ilayer];
1391 
1392         stmp.str(lye_header.substr(0x0F));
1393         GET_DOUBLE(stmp, glayer.xAxis.min);
1394         GET_DOUBLE(stmp, glayer.xAxis.max);
1395         GET_DOUBLE(stmp, glayer.xAxis.step);
1396 
1397         glayer.xAxis.majorTicks = lye_header[0x2B];
1398 
1399         unsigned char g = lye_header[0x2D];
1400         glayer.xAxis.zeroLine = ((g & 0x80) != 0);
1401         glayer.xAxis.oppositeLine = ((g & 0x40) != 0);
1402 
1403         glayer.xAxis.minorTicks = lye_header[0x37];
1404         glayer.xAxis.scale = lye_header[0x38];
1405 
1406         stmp.str(lye_header.substr(0x3A));
1407         GET_DOUBLE(stmp, glayer.yAxis.min);
1408         GET_DOUBLE(stmp, glayer.yAxis.max);
1409         GET_DOUBLE(stmp, glayer.yAxis.step);
1410 
1411         glayer.yAxis.majorTicks = lye_header[0x56];
1412 
1413         g = lye_header[0x58];
1414         glayer.yAxis.zeroLine = ((g & 0x80) != 0);
1415         glayer.yAxis.oppositeLine = ((g & 0x40) != 0);
1416 
1417         glayer.yAxis.minorTicks = lye_header[0x62];
1418         glayer.yAxis.scale = lye_header[0x63];
1419 
1420         g = lye_header[0x68];
1421         glayer.gridOnTop = ((g & 0x04) != 0);
1422         glayer.exchangedAxes = ((g & 0x40) != 0);
1423 
1424         stmp.str(lye_header.substr(0x71));
1425         GET_SHORT(stmp, glayer.clientRect.left)
1426         GET_SHORT(stmp, glayer.clientRect.top)
1427         GET_SHORT(stmp, glayer.clientRect.right)
1428         GET_SHORT(stmp, glayer.clientRect.bottom)
1429 
1430         unsigned char border = lye_header[0x89];
1431         glayer.borderType = (BorderType)(border >= 0x80 ? border-0x80 : None);
1432 
1433         if (lye_header_size > 0x107)
1434             glayer.backgroundColor = getColor(lye_header.substr(0x105,4));
1435 
1436     }
1437 }
1438 
1439 Origin::Color OriginAnyParser::getColor(const string& strbincolor) {
1440     /* decode a color value from a 4 byte binary string */
1441     Origin::Color result;
1442     unsigned char sbincolor[4];
1443     for (int i=0; i < 4; i++) {
1444         sbincolor[i] = strbincolor[i];
1445     }
1446     switch(sbincolor[3]) {
1447         case 0:
1448             if(sbincolor[0] < 0x64) {
1449                 result.type = Origin::Color::Regular;
1450                 result.regular = sbincolor[0];
1451             } else {
1452                 switch(sbincolor[2]) {
1453                     case 0:
1454                         result.type = Origin::Color::Indexing;
1455                         break;
1456                     case 0x40:
1457                         result.type = Origin::Color::Mapping;
1458                         break;
1459                     case 0x80:
1460                         result.type = Origin::Color::RGB;
1461                         break;
1462                 }
1463                 result.column = sbincolor[0] - 0x64;
1464             }
1465             break;
1466         case 1:
1467             result.type = Origin::Color::Custom;
1468             for(int i = 0; i < 3; ++i)
1469                 result.custom[i] = sbincolor[i];
1470             break;
1471         case 0x20:
1472             result.type = Origin::Color::Increment;
1473             result.starting = sbincolor[1];
1474             break;
1475         case 0xFF:
1476             if(sbincolor[0] == 0xFC)
1477                 result.type = Origin::Color::None;
1478             else if(sbincolor[0] == 0xF7)
1479                 result.type = Origin::Color::Automatic;
1480             else {
1481                 result.type = Origin::Color::Regular;
1482                 result.regular = sbincolor[0];
1483             }
1484             break;
1485         default:
1486             result.type = Origin::Color::Regular;
1487             result.regular = sbincolor[0];
1488             break;
1489     }
1490     return result;
1491 }
1492 
1493 void OriginAnyParser::getAnnotationProperties(const string& anhd, unsigned int anhdsz, const string& andt1, unsigned int andt1sz, const string& andt2, unsigned int andt2sz, const string& andt3, unsigned int andt3sz) {
1494     istringstream stmp;
1495     (void) anhdsz; (void) andt3; (void) andt3sz;
1496 
1497     if (ispread != -1) {
1498 
1499         string sec_name = anhd.substr(0x46,41).c_str();
1500         int col_index = findColumnByName((int)ispread, sec_name);
1501         if (col_index != -1){ //check if it is a formula
1502             spreadSheets[ispread].columns[col_index].command = andt1.c_str();
1503             LOG_PRINT(logfile, "                Column: %s has formula: %s\n", sec_name.c_str(), spreadSheets[ispread].columns[col_index].command.c_str())
1504         }
1505 
1506     } else if (imatrix != -1) {
1507 
1508         MatrixSheet& sheet = matrixes[imatrix].sheets[ilayer];
1509         string sec_name = anhd.substr(0x46,41).c_str();
1510 
1511         stmp.str(andt1.c_str());
1512         if (sec_name == "MV") {
1513             sheet.command = andt1.c_str();
1514         } else if (sec_name == "Y2") {
1515             stmp >> sheet.coordinates[0];
1516         } else if (sec_name == "X2") {
1517             stmp >> sheet.coordinates[1];
1518         } else if (sec_name == "Y1") {
1519             stmp >> sheet.coordinates[2];
1520         } else if (sec_name == "X1") {
1521             stmp >> sheet.coordinates[3];
1522         } else if (sec_name == "COLORMAP") {
1523             // Color maps for matrix annotations are similar to color maps for graph curves (3D).
1524             // They differ only in the start offset to the data string.
1525             getColorMap(sheet.colorMap, andt2, andt2sz);
1526         }
1527 
1528     } else if (iexcel != -1) {
1529 
1530         string sec_name = anhd.substr(0x46,41).c_str();
1531         vector<Origin::SpreadColumn>::difference_type col_index = findExcelColumnByName(iexcel, ilayer, sec_name);
1532         if (col_index != -1){ //check if it is a formula
1533             excels[iexcel].sheets[ilayer].columns[col_index].command = andt1.c_str();
1534         }
1535 
1536     } else {
1537 
1538         GraphLayer& glayer = graphs[igraph].layers[ilayer];
1539         string sec_name = anhd.substr(0x46,41).c_str();
1540 
1541         Rect r;
1542         stmp.str(anhd.substr(0x03));
1543         GET_SHORT(stmp, r.left)
1544         GET_SHORT(stmp, r.top)
1545         GET_SHORT(stmp, r.right)
1546         GET_SHORT(stmp, r.bottom)
1547 
1548         unsigned char attach = anhd[0x28];
1549         if (attach >= (unsigned char)Attach::End_) attach = Attach::Frame;
1550         unsigned char border = anhd[0x29];
1551 
1552         Color color = getColor(anhd.substr(0x33,4));
1553 
1554         if (sec_name == "PL") glayer.yAxis.formatAxis[0].prefix = andt1.c_str();
1555         if (sec_name == "PR") glayer.yAxis.formatAxis[1].prefix = andt1.c_str();
1556         if (sec_name == "PB") glayer.xAxis.formatAxis[0].prefix = andt1.c_str();
1557         if (sec_name == "PT") glayer.xAxis.formatAxis[1].prefix = andt1.c_str();
1558         if (sec_name == "SL") glayer.yAxis.formatAxis[0].suffix = andt1.c_str();
1559         if (sec_name == "SR") glayer.yAxis.formatAxis[1].suffix = andt1.c_str();
1560         if (sec_name == "SB") glayer.xAxis.formatAxis[0].suffix = andt1.c_str();
1561         if (sec_name == "ST") glayer.xAxis.formatAxis[1].suffix = andt1.c_str();
1562         if (sec_name == "OL") glayer.yAxis.formatAxis[0].factor = andt1.c_str();
1563         if (sec_name == "OR") glayer.yAxis.formatAxis[1].factor = andt1.c_str();
1564         if (sec_name == "OB") glayer.xAxis.formatAxis[0].factor = andt1.c_str();
1565         if (sec_name == "OT") glayer.xAxis.formatAxis[1].factor = andt1.c_str();
1566 
1567         unsigned char type = andt1[0x00];
1568         LineVertex begin, end;
1569         /* OriginNNNParser identify line/arrow annotation by checking size of andt1
1570            Origin410: 21||24; Origin 500: 24; Origin 610: 24||96; Origin700 and higher: 120;
1571            An alternative is to look at anhd[0x02]:
1572              (0x21 for Circle/Rect, 0x22 for Line/Arrow, 0x23 for Polygon/Polyline)
1573          */
1574         unsigned char ankind = anhd[0x02];
1575         if (ankind == 0x22) {//Line/Arrow
1576             if ((attach == Origin::Scale) && (andt1sz > 0x5F)) {
1577                 if (type == 2) {
1578                     stmp.str(andt1.substr(0x20));
1579                     GET_DOUBLE(stmp, begin.x)
1580                     GET_DOUBLE(stmp, end.x)
1581                     stmp.str(andt1.substr(0x40));
1582                     GET_DOUBLE(stmp, begin.y)
1583                     GET_DOUBLE(stmp, end.y)
1584                 } else if (type == 4) {//curved arrow: start point, 2 middle points and end point
1585                     stmp.str(andt1.substr(0x20));
1586                     GET_DOUBLE(stmp, begin.x)
1587                     GET_DOUBLE(stmp, end.x)
1588                     GET_DOUBLE(stmp, end.x)
1589                     GET_DOUBLE(stmp, end.x)
1590                     GET_DOUBLE(stmp, begin.y)
1591                     GET_DOUBLE(stmp, end.y)
1592                     GET_DOUBLE(stmp, end.y)
1593                     GET_DOUBLE(stmp, end.y)
1594                 }
1595             } else {
1596                 short x1=0, x2=0, y1=0, y2=0;
1597                 if (type == 2) {//straight line/arrow
1598                     stmp.str(andt1.substr(0x01));
1599                     GET_SHORT(stmp, x1)
1600                     GET_SHORT(stmp, x2)
1601                     stmp.seekg(4, ios_base::cur);
1602                     GET_SHORT(stmp, y1)
1603                     GET_SHORT(stmp, y2)
1604                 } else if (type == 4) {//curved line/arrow has 4 points
1605                     stmp.str(andt1.substr(0x01));
1606                     GET_SHORT(stmp, x1)
1607                     stmp.seekg(4, ios_base::cur);
1608                     GET_SHORT(stmp, x2)
1609                     GET_SHORT(stmp, y1)
1610                     stmp.seekg(4, ios_base::cur);
1611                     GET_SHORT(stmp, y2)
1612                 }
1613 
1614                 double dx = fabs(x2 - x1);
1615                 double dy = fabs(y2 - y1);
1616                 double minx = (x1 <= x2) ? x1 : x2;
1617                 double miny = (y1 <= y2) ? y1 : y2;
1618 
1619                 begin.x = (x1 == x2) ? r.left + 0.5*r.width() : r.left + (x1 - minx)/dx*r.width();
1620                 end.x   = (x1 == x2) ? r.left + 0.5*r.width() : r.left + (x2 - minx)/dx*r.width();
1621                 begin.y = (y1 == y2) ? r.top  + 0.5*r.height(): r.top + (y1 - miny)/dy*r.height();
1622                 end.y   = (y1 == y2) ? r.top  + 0.5*r.height(): r.top + (y2 - miny)/dy*r.height();
1623             }
1624             unsigned char arrows = andt1[0x11];
1625             switch (arrows) {
1626                 case 0:
1627                     begin.shapeType = 0;
1628                     end.shapeType = 0;
1629                     break;
1630                 case 1:
1631                     begin.shapeType = 1;
1632                     end.shapeType = 0;
1633                     break;
1634                 case 2:
1635                     begin.shapeType = 0;
1636                     end.shapeType = 1;
1637                     break;
1638                 case 3:
1639                     begin.shapeType = 1;
1640                     end.shapeType = 1;
1641                     break;
1642             }
1643             if (andt1sz > 0x77) {
1644                 begin.shapeType = andt1[0x60];
1645                 unsigned int w = 0;
1646                 stmp.str(andt1.substr(0x64));
1647                 GET_INT(stmp, w)
1648                 begin.shapeWidth = (double)w/500.0;
1649                 GET_INT(stmp, w)
1650                 begin.shapeLength = (double)w/500.0;
1651 
1652                 end.shapeType = andt1[0x6C];
1653                 stmp.str(andt1.substr(0x70));
1654                 GET_INT(stmp, w)
1655                 end.shapeWidth = (double)w/500.0;
1656                 GET_INT(stmp, w)
1657                 end.shapeLength = (double)w/500.0;
1658             }
1659         }
1660         //text properties
1661         short rotation;
1662         stmp.str(andt1.substr(0x02));
1663         GET_SHORT(stmp, rotation)
1664         unsigned char fontSize = andt1[0x4];
1665         unsigned char tab = andt1[0x0A];
1666 
1667         //line properties
1668         unsigned char lineStyle = andt1[0x12];
1669         unsigned short w1 = 0;
1670         if (andt1sz > 0x14) {
1671             stmp.str(andt1.substr(0x13));
1672             GET_SHORT(stmp, w1)
1673         }
1674         double width = (double)w1/500.0;
1675 
1676         Figure figure;
1677         stmp.str(andt1.substr(0x05));
1678         GET_SHORT(stmp, w1)
1679         figure.width = (double)w1/500.0;
1680         figure.style = andt1[0x08];
1681 
1682         if (andt1sz > 0x4D) {
1683             figure.fillAreaColor = getColor(andt1.substr(0x42,4));
1684             stmp.str(andt1.substr(0x46));
1685             GET_SHORT(stmp, w1)
1686             figure.fillAreaPatternWidth = (double)w1/500.0;
1687             figure.fillAreaPatternColor = getColor(andt1.substr(0x4A,4));
1688             figure.fillAreaPattern = andt1[0x4E];
1689         }
1690         if (andt1sz > 0x56) {
1691             unsigned char h = andt1[0x57];
1692             figure.useBorderColor = (h == 0x10);
1693         }
1694 
1695         if (sec_name == "XB") {
1696             string text = andt2.c_str();
1697             glayer.xAxis.position = GraphAxis::Bottom;
1698             glayer.xAxis.formatAxis[0].label = TextBox(text, r, color, fontSize, rotation/10, tab, (BorderType)(border >= 0x80 ? border-0x80 : None), (Attach)attach);
1699         }
1700         else if (sec_name == "XT") {
1701             string text = andt2.c_str();
1702             glayer.xAxis.position = GraphAxis::Top;
1703             glayer.xAxis.formatAxis[1].label = TextBox(text, r, color, fontSize, rotation/10, tab, (BorderType)(border >= 0x80 ? border-0x80 : None), (Attach)attach);
1704         }
1705         else if (sec_name == "YL") {
1706             string text = andt2.c_str();
1707             glayer.yAxis.position = GraphAxis::Left;
1708             glayer.yAxis.formatAxis[0].label = TextBox(text, r, color, fontSize, rotation/10, tab, (BorderType)(border >= 0x80 ? border-0x80 : None), (Attach)attach);
1709         }
1710         else if (sec_name == "YR") {
1711             string text = andt2.c_str();
1712             glayer.yAxis.position = GraphAxis::Right;
1713             glayer.yAxis.formatAxis[1].label = TextBox(text, r, color, fontSize, rotation/10, tab, (BorderType)(border >= 0x80 ? border-0x80 : None), (Attach)attach);
1714         }
1715         else if (sec_name == "ZF") {
1716             string text = andt2.c_str();
1717             glayer.zAxis.position = GraphAxis::Front;
1718             glayer.zAxis.formatAxis[0].label = TextBox(text, r, color, fontSize, rotation/10, tab, (BorderType)(border >= 0x80 ? border-0x80 : None), (Attach)attach);
1719         }
1720         else if (sec_name == "ZB") {
1721             string text = andt2.c_str();
1722             glayer.zAxis.position = GraphAxis::Back;
1723             glayer.zAxis.formatAxis[1].label = TextBox(text, r, color, fontSize, rotation/10, tab, (BorderType)(border >= 0x80 ? border-0x80 : None), (Attach)attach);
1724         }
1725         else if (sec_name == "3D") {
1726             stmp.str(andt2);
1727             GET_DOUBLE(stmp, glayer.zAxis.min)
1728             GET_DOUBLE(stmp, glayer.zAxis.max)
1729             GET_DOUBLE(stmp, glayer.zAxis.step)
1730             glayer.zAxis.majorTicks = andt2[0x1C];
1731             glayer.zAxis.minorTicks = andt2[0x28];
1732             glayer.zAxis.scale = andt2[0x29];
1733 
1734             stmp.str(andt2.substr(0x5A));
1735             GET_FLOAT(stmp, glayer.xAngle)
1736             GET_FLOAT(stmp, glayer.yAngle)
1737             GET_FLOAT(stmp, glayer.zAngle)
1738 
1739             stmp.str(andt2.substr(0x218));
1740             GET_FLOAT(stmp, glayer.xLength)
1741             GET_FLOAT(stmp, glayer.yLength)
1742             GET_FLOAT(stmp, glayer.zLength)
1743             glayer.xLength /= 23.0;
1744             glayer.yLength /= 23.0;
1745             glayer.zLength /= 23.0;
1746 
1747             glayer.orthographic3D = (andt2[0x240] != 0);
1748         }
1749         else if (sec_name == "Legend") {
1750             string text = andt2.c_str();
1751             glayer.legend = TextBox(text, r, color, fontSize, rotation/10, tab, (BorderType)(border >= 0x80 ? border-0x80 : None), (Attach)attach);
1752         }
1753         else if (sec_name == "__BCO2") { // histogram
1754             stmp.str(andt2.substr(0x10));
1755             GET_DOUBLE(stmp, glayer.histogramBin)
1756             stmp.str(andt2.substr(0x20));
1757             GET_DOUBLE(stmp, glayer.histogramEnd)
1758             GET_DOUBLE(stmp, glayer.histogramBegin)
1759 
1760             // TODO: check if 0x5E is right (obtained from anhdsz-0x46+93-andt1sz = 111-70+93-40 = 94)
1761             glayer.percentile.p1SymbolType = andt2[0x5E];
1762             glayer.percentile.p99SymbolType = andt2[0x5F];
1763             glayer.percentile.meanSymbolType = andt2[0x60];
1764             glayer.percentile.maxSymbolType = andt2[0x61];
1765             glayer.percentile.minSymbolType = andt2[0x62];
1766 
1767             // 0x9F = 0x5E+65
1768             glayer.percentile.labels = andt2[0x9F];
1769             // 0x6B = 0x5E+106-93 = 107
1770             glayer.percentile.whiskersRange = andt2[0x6B];
1771             glayer.percentile.boxRange = andt2[0x6C];
1772             // 0x8e = 0x5E+141-93 = 142
1773             glayer.percentile.whiskersCoeff = andt2[0x8e];
1774             glayer.percentile.boxCoeff = andt2[0x8f];
1775             unsigned char h = andt2[0x90];
1776             glayer.percentile.diamondBox = (h == 0x82) ? true : false;
1777             // 0xCB = 0x5E+109 = 203
1778             stmp.str(andt2.substr(0xCB));
1779             GET_SHORT(stmp, glayer.percentile.symbolSize)
1780             glayer.percentile.symbolSize = glayer.percentile.symbolSize/2 + 1;
1781             // 0x101 = 0x5E+163
1782             glayer.percentile.symbolColor = getColor(andt2.substr(0x101,4));
1783             glayer.percentile.symbolFillColor = getColor(andt2.substr(0x105,4));
1784         }
1785         else if (sec_name == "_206") { // box plot labels
1786         }
1787         else if (sec_name == "VLine") {
1788             stmp.str(andt1.substr(0x0A));
1789             double start;
1790             GET_DOUBLE(stmp, start)
1791             stmp.str(andt1.substr(0x1A));
1792             double width;
1793             GET_DOUBLE(stmp, width)
1794             glayer.vLine = start + 0.5*width;
1795             glayer.imageProfileTool = 2;
1796         }
1797         else if (sec_name == "HLine") {
1798             stmp.str(andt1.substr(0x12));
1799             double start;
1800             GET_DOUBLE(stmp, start)
1801             stmp.str(andt1.substr(0x22));
1802             double width;
1803             GET_DOUBLE(stmp, width)
1804             glayer.hLine = start + 0.5*width;
1805             glayer.imageProfileTool = 2;
1806         }
1807         else if (sec_name == "vline") {
1808             stmp.str(andt1.substr(0x20));
1809             GET_DOUBLE(stmp, glayer.vLine)
1810             glayer.imageProfileTool = 1;
1811         }
1812         else if (sec_name == "hline") {
1813             stmp.str(andt1.substr(0x40));
1814             GET_DOUBLE(stmp, glayer.hLine)
1815             glayer.imageProfileTool = 1;
1816         }
1817         else if (sec_name == "ZCOLORS") {
1818             glayer.isXYY3D = true;
1819             if (fileVersion < 600) {
1820                 ColorMap& colorMap = glayer.colorMap;
1821                 getZcolorsMap(colorMap, andt2, andt2sz);
1822             }
1823         }
1824         else if (sec_name == "SPECTRUM1") {
1825             glayer.isXYY3D = false;
1826             glayer.colorScale.visible = true;
1827             glayer.colorScale.reverseOrder = (andt2[0x18] != 0);
1828             stmp.str(andt2.substr(0x20));
1829             GET_SHORT(stmp, glayer.colorScale.colorBarThickness)
1830             GET_SHORT(stmp, glayer.colorScale.labelGap)
1831             glayer.colorScale.labelsColor = getColor(andt2.substr(0x5C,4));
1832         }
1833         else if (sec_name == "&0") {
1834             glayer.isWaterfall = true;
1835             string text = andt1.c_str();
1836             string::size_type commaPos = text.find_first_of(",");
1837             stmp.str(text.substr(0,commaPos));
1838             stmp >> glayer.xOffset;
1839             stmp.str(text.substr(commaPos+1));
1840             stmp >> glayer.yOffset;
1841         }
1842         /* OriginNNNParser identify text, circle, rectangle and bitmap annotation by checking size of andt1:
1843                      text/pie text          rectangle/circle       line            bitmap
1844            Origin410: 22                    0xA(10)                21/24           38
1845            Origin500: 22                    0xA(10)                24              40
1846            Origin610: 22                    0xA(10)                24/96           40
1847            Origin700:                       0x5E(94)               120             0x28(40)
1848            Origin750: 0x3E(62)/78           0x5E(94)              0x78(120)        0x28(40)
1849            Origin850: 0x3E(62)/78           0x5E(94)              0x78(120)        0x28(40)
1850            An alternative is to look at anhd[0x02]:
1851              (0x00 for Text, 0x21 for Circle/Rect, 0x22 for Line/Arrow, 0x23 for Polygon/Polyline)
1852         */
1853         else if ((ankind == 0x0) && (sec_name != "DelData")) { // text
1854             string text = andt2.c_str();
1855             if (sec_name.substr(0,3) == "PIE")
1856                 glayer.pieTexts.push_back(TextBox(text, r, color, fontSize, rotation/10, tab, (BorderType)(border >= 0x80 ? border-0x80 : None), (Attach)attach));
1857             else
1858                 glayer.texts.push_back(TextBox(text, r, color, fontSize, rotation/10, tab, (BorderType)(border >= 0x80 ? border-0x80 : None), (Attach)attach));
1859         }
1860         else if (ankind == 0x21) { // rectangle & circle
1861             switch (type) { // type = andt1[0x00]
1862                 case 0:
1863                 case 1:
1864                     figure.type = Figure::Rectangle;
1865                     break;
1866                 case 2:
1867                 case 3:
1868                     figure.type = Figure::Circle;
1869                     break;
1870             }
1871             figure.clientRect = r;
1872             figure.attach = (Attach)attach;
1873             figure.color = color;
1874 
1875             glayer.figures.push_back(figure);
1876         }
1877         else if ((ankind == 0x22) && (sec_name != "sLine") && (sec_name != "sline")) { // line/arrow
1878             glayer.lines.push_back(Line());
1879             Line& line(glayer.lines.back());
1880             line.color = color;
1881             line.clientRect = r;
1882             line.attach = (Attach)attach;
1883             line.width = width;
1884             line.style = lineStyle;
1885             line.begin = begin;
1886             line.end = end;
1887         }
1888         else if (andt1sz == 40) { // bitmap
1889             if (type == 4) { // type = andt1[0x00]
1890                 unsigned long filesize = andt2sz + 14;
1891                 glayer.bitmaps.push_back(Bitmap());
1892                 Bitmap& bitmap(glayer.bitmaps.back());
1893                 bitmap.clientRect = r;
1894                 bitmap.attach = (Attach)attach;
1895                 bitmap.size = filesize;
1896                 bitmap.borderType = (BorderType)(border >= 0x80 ? border-0x80 : None);
1897                 bitmap.data = new unsigned char[filesize];
1898                 unsigned char* data = bitmap.data;
1899                 //add Bitmap header
1900                 memcpy(data, "BM", 2);
1901                 data += 2;
1902                 memcpy(data, &filesize, 4);
1903                 data += 4;
1904                 unsigned int d = 0;
1905                 memcpy(data, &d, 4);
1906                 data += 4;
1907                 d = 0x36;
1908                 memcpy(data, &d, 4);
1909                 data += 4;
1910                 memcpy(data, andt2.c_str(), andt2sz);
1911             } else if (type == 6) {
1912                 // TODO check if 0x5E is right (obtained from anhdsz-0x46+93-andt1sz = 111-70+93-40 = 94)
1913                 string gname = andt2.substr(0x5E).c_str();
1914                 glayer.bitmaps.push_back(Bitmap(gname));
1915                 Bitmap& bitmap(glayer.bitmaps.back());
1916                 bitmap.clientRect = r;
1917                 bitmap.attach = (Attach)attach;
1918                 bitmap.size = 0;
1919                 bitmap.borderType = (BorderType)(border >= 0x80 ? border-0x80 : None);
1920             }
1921         }
1922 
1923     }
1924     return;
1925 }
1926 
1927 void OriginAnyParser::getCurveProperties(const string& cvehd, unsigned int cvehdsz, const string& cvedt, unsigned int cvedtsz) {
1928     istringstream stmp;
1929 
1930     if (ispread != -1) { // spreadsheet: curves are columns
1931 
1932         // TODO: check that spreadsheet columns are stored in proper order
1933         // vector<SpreadColumn> header;
1934         unsigned char c = cvehd[0x11];
1935         string name = cvehd.substr(0x12).c_str();
1936         unsigned short width = 0;
1937         if (cvehdsz > 0x4B) {
1938             stmp.str(cvehd.substr(0x4A));
1939             GET_SHORT(stmp, width)
1940         }
1941         int col_index = findColumnByName((int)ispread, name);
1942         if (col_index != -1) {
1943             if (spreadSheets[ispread].columns[col_index].name != name)
1944                 spreadSheets[ispread].columns[col_index].name = name;
1945 
1946             SpreadColumn::ColumnType type;
1947             switch(c){
1948                 case 3:
1949                     type = SpreadColumn::X;
1950                     break;
1951                 case 0:
1952                     type = SpreadColumn::Y;
1953                     break;
1954                 case 5:
1955                     type = SpreadColumn::Z;
1956                     break;
1957                 case 6:
1958                     type = SpreadColumn::XErr;
1959                     break;
1960                 case 2:
1961                     type = SpreadColumn::YErr;
1962                     break;
1963                 case 4:
1964                     type = SpreadColumn::Label;
1965                     break;
1966                 default:
1967                     type = SpreadColumn::NONE;
1968                     break;
1969             }
1970             spreadSheets[ispread].columns[col_index].type = type;
1971 
1972             width /= 0xA;
1973             if(width == 0) width = 8;
1974             spreadSheets[ispread].columns[col_index].width = width;
1975             unsigned char c1 = cvehd[0x1E];
1976             unsigned char c2 = cvehd[0x1F];
1977             switch (c1) {
1978                 case 0x00: // Numeric      - Dec1000
1979                 case 0x09: // Text&Numeric - Dec1000
1980                 case 0x10: // Numeric      - Scientific
1981                 case 0x19: // Text&Numeric - Scientific
1982                 case 0x20: // Numeric      - Engineering
1983                 case 0x29: // Text&Numeric - Engineering
1984                 case 0x30: // Numeric      - Dec1,000
1985                 case 0x39: // Text&Numeric - Dec1,000
1986                     spreadSheets[ispread].columns[col_index].valueType = (c1%0x10 == 0x9) ? TextNumeric : Numeric;
1987                     spreadSheets[ispread].columns[col_index].valueTypeSpecification = c1 / 0x10;
1988                     if (c2 >= 0x80) {
1989                         spreadSheets[ispread].columns[col_index].significantDigits = c2 - 0x80;
1990                         spreadSheets[ispread].columns[col_index].numericDisplayType = SignificantDigits;
1991                     } else if (c2 > 0) {
1992                         spreadSheets[ispread].columns[col_index].decimalPlaces = c2 - 0x03;
1993                         spreadSheets[ispread].columns[col_index].numericDisplayType = DecimalPlaces;
1994                     }
1995                     break;
1996                 case 0x02: // Time
1997                     spreadSheets[ispread].columns[col_index].valueType = Time;
1998                     spreadSheets[ispread].columns[col_index].valueTypeSpecification = c2 - 0x80;
1999                     break;
2000                 case 0x03: // Date
2001                 case 0x33:
2002                     spreadSheets[ispread].columns[col_index].valueType = Date;
2003                     spreadSheets[ispread].columns[col_index].valueTypeSpecification= c2 - 0x80;
2004                     break;
2005                 case 0x31: // Text
2006                     spreadSheets[ispread].columns[col_index].valueType = Text;
2007                     break;
2008                 case 0x4: // Month
2009                 case 0x34:
2010                     spreadSheets[ispread].columns[col_index].valueType = Month;
2011                     spreadSheets[ispread].columns[col_index].valueTypeSpecification = c2;
2012                     break;
2013                 case 0x5: // Day
2014                 case 0x35:
2015                     spreadSheets[ispread].columns[col_index].valueType = Day;
2016                     spreadSheets[ispread].columns[col_index].valueTypeSpecification = c2;
2017                     break;
2018                 default: // Text
2019                     spreadSheets[ispread].columns[col_index].valueType = Text;
2020                     break;
2021             }
2022             if (cvedtsz > 0) {
2023                 spreadSheets[ispread].columns[col_index].comment = cvedt.c_str();
2024             }
2025             // TODO: check that spreadsheet columns are stored in proper order
2026             // header.push_back(spreadSheets[ispread].columns[col_index]);
2027         }
2028         // TODO: check that spreadsheet columns are stored in proper order
2029         // for (unsigned int i = 0; i < header.size(); i++)
2030         //  spreadSheets[spread].columns[i] = header[i];
2031 
2032     } else if (imatrix != -1) {
2033 
2034         MatrixSheet sheet = matrixes[imatrix].sheets[ilayer];
2035         unsigned char c1 = cvehd[0x1E];
2036         unsigned char c2 = cvehd[0x1F];
2037 
2038         sheet.valueTypeSpecification = c1/0x10;
2039         if (c2 >= 0x80) {
2040             sheet.significantDigits = c2-0x80;
2041             sheet.numericDisplayType = SignificantDigits;
2042         } else if (c2 > 0) {
2043             sheet.decimalPlaces = c2-0x03;
2044             sheet.numericDisplayType = DecimalPlaces;
2045         }
2046 
2047         matrixes[imatrix].sheets[ilayer] = sheet;
2048 
2049     } else if (iexcel != -1) {
2050 
2051         unsigned char c = cvehd[0x11];
2052         string name = cvehd.substr(0x12).c_str();
2053         unsigned short width = 0;
2054         stmp.str(cvehd.substr(0x4A));
2055         GET_SHORT(stmp, width)
2056         unsigned short dataID = 0;
2057         stmp.str(cvehd.substr(0x04));
2058         GET_SHORT(stmp, dataID)
2059 
2060         unsigned int isheet = datasets[dataID-1].sheet;
2061         vector<Origin::SpreadColumn>::difference_type col_index = findExcelColumnByName(iexcel, isheet, name);
2062         if (col_index != -1) {
2063             SpreadColumn::ColumnType type;
2064             switch(c){
2065                 case 3:
2066                     type = SpreadColumn::X;
2067                     break;
2068                 case 0:
2069                     type = SpreadColumn::Y;
2070                     break;
2071                 case 5:
2072                     type = SpreadColumn::Z;
2073                     break;
2074                 case 6:
2075                     type = SpreadColumn::XErr;
2076                     break;
2077                 case 2:
2078                     type = SpreadColumn::YErr;
2079                     break;
2080                 case 4:
2081                     type = SpreadColumn::Label;
2082                     break;
2083                 default:
2084                     type = SpreadColumn::NONE;
2085                     break;
2086             }
2087             excels[iexcel].sheets[isheet].columns[col_index].type = type;
2088             width /= 0xA;
2089             if (width == 0) width = 8;
2090             excels[iexcel].sheets[isheet].columns[col_index].width = width;
2091 
2092             unsigned char c1 = cvehd[0x1E];
2093             unsigned char c2 = cvehd[0x1F];
2094             switch (c1) {
2095                 case 0x00: // Numeric      - Dec1000
2096                 case 0x09: // Text&Numeric - Dec1000
2097                 case 0x10: // Numeric      - Scientific
2098                 case 0x19: // Text&Numeric - Scientific
2099                 case 0x20: // Numeric      - Engineering
2100                 case 0x29: // Text&Numeric - Engineering
2101                 case 0x30: // Numeric      - Dec1,000
2102                 case 0x39: // Text&Numeric - Dec1,000
2103                     excels[iexcel].sheets[isheet].columns[col_index].valueType = (c1%0x10 == 0x9) ? TextNumeric : Numeric;
2104                     excels[iexcel].sheets[isheet].columns[col_index].valueTypeSpecification = c1 / 0x10;
2105                     if (c2 >= 0x80) {
2106                         excels[iexcel].sheets[isheet].columns[col_index].significantDigits = c2 - 0x80;
2107                         excels[iexcel].sheets[isheet].columns[col_index].numericDisplayType = SignificantDigits;
2108                     } else if (c2 > 0) {
2109                         excels[iexcel].sheets[isheet].columns[col_index].decimalPlaces = c2 - 0x03;
2110                         excels[iexcel].sheets[isheet].columns[col_index].numericDisplayType = DecimalPlaces;
2111                     }
2112                     break;
2113                 case 0x02: // Time
2114                     excels[iexcel].sheets[isheet].columns[col_index].valueType = Time;
2115                     excels[iexcel].sheets[isheet].columns[col_index].valueTypeSpecification = c2 - 0x80;
2116                     break;
2117                 case 0x03: // Date
2118                     excels[iexcel].sheets[isheet].columns[col_index].valueType = Date;
2119                     excels[iexcel].sheets[isheet].columns[col_index].valueTypeSpecification = c2 - 0x80;
2120                     break;
2121                 case 0x31: // Text
2122                     excels[iexcel].sheets[isheet].columns[col_index].valueType = Text;
2123                     break;
2124                 case 0x04: // Month
2125                 case 0x34:
2126                     excels[iexcel].sheets[isheet].columns[col_index].valueType = Month;
2127                     excels[iexcel].sheets[isheet].columns[col_index].valueTypeSpecification = c2;
2128                     break;
2129                 case 0x05: // Day
2130                 case 0x35:
2131                     excels[iexcel].sheets[isheet].columns[col_index].valueType = Day;
2132                     excels[iexcel].sheets[isheet].columns[col_index].valueTypeSpecification = c2;
2133                     break;
2134                 default: // Text
2135                     excels[iexcel].sheets[isheet].columns[col_index].valueType = Text;
2136                     break;
2137             }
2138             if (cvedtsz > 0) {
2139                 excels[iexcel].sheets[isheet].columns[col_index].comment = cvedt.c_str();
2140             }
2141         }
2142 
2143     } else {
2144 
2145         GraphLayer& glayer = graphs[igraph].layers[ilayer];
2146         glayer.curves.push_back(GraphCurve());
2147         GraphCurve& curve(glayer.curves.back());
2148 
2149         unsigned char h = cvehd[0x26];
2150         curve.hidden = (h == 33);
2151         curve.type = cvehd[0x4C];
2152         if (curve.type == GraphCurve::XYZContour || curve.type == GraphCurve::Contour)
2153             glayer.isXYY3D = false;
2154 
2155         unsigned short w;
2156         stmp.str(cvehd.substr(0x04));
2157         GET_SHORT(stmp, w)
2158         pair<string, string> column = findDataByIndex(w-1);
2159         short nColY = w;
2160         if (column.first.size() > 0) {
2161             curve.dataName = column.first;
2162             if (glayer.is3D() || (curve.type == GraphCurve::XYZContour)) {
2163                 curve.zColumnName = column.second;
2164             } else {
2165                 curve.yColumnName = column.second;
2166             }
2167         }
2168 
2169         stmp.str(cvehd.substr(0x23));
2170         GET_SHORT(stmp, w)
2171         column = findDataByIndex(w-1);
2172         if (column.first.size() > 0) {
2173             curve.xDataName = (curve.dataName != column.first) ? column.first : string();
2174             if (glayer.is3D() || (curve.type == GraphCurve::XYZContour)) {
2175                 curve.yColumnName = column.second;
2176             } else if (glayer.isXYY3D){
2177                 curve.xColumnName = column.second;
2178             } else {
2179                 curve.xColumnName = column.second;
2180             }
2181         }
2182 
2183         if (cvehdsz > 0x4E) {
2184             stmp.str(cvehd.substr(0x4D));
2185             GET_SHORT(stmp, w)
2186             column = findDataByIndex(w-1);
2187             if (column.first.size() > 0 && (glayer.is3D() || (curve.type == GraphCurve::XYZContour))) {
2188                 curve.xColumnName = column.second;
2189                 if (curve.dataName != column.first) {
2190                     // graph X and Y from different tables
2191                 }
2192             }
2193         }
2194 
2195         if (glayer.is3D() || glayer.isXYY3D) graphs[igraph].is3D = true;
2196 
2197         curve.lineConnect = cvehd[0x11];
2198         curve.lineStyle = cvehd[0x12];
2199         curve.boxWidth = cvehd[0x14];
2200 
2201         stmp.str(cvehd.substr(0x15));
2202         GET_SHORT(stmp, w)
2203         curve.lineWidth = (double)w/500.0;
2204 
2205         curve.symbolShape = cvehd[0x17];
2206         curve.symbolInterior = cvehd[0x18];
2207 
2208         stmp.str(cvehd.substr(0x19));
2209         GET_SHORT(stmp, w)
2210         curve.symbolSize = (double)w/500.0;
2211 
2212         h = cvehd[0x1C];
2213         curve.fillArea = (h==2);
2214         curve.fillAreaType = cvehd[0x1E];
2215 
2216         //text
2217         if (curve.type == GraphCurve::TextPlot) {
2218             stmp.str(cvehd.substr(0x13));
2219             GET_SHORT(stmp, curve.text.rotation)
2220             curve.text.rotation /= 10;
2221             GET_SHORT(stmp, curve.text.fontSize)
2222 
2223             h = cvehd[0x19];
2224             switch (h) {
2225                 case 26:
2226                     curve.text.justify = TextProperties::Center;
2227                     break;
2228                 case 2:
2229                     curve.text.justify = TextProperties::Right;
2230                     break;
2231                 default:
2232                     curve.text.justify = TextProperties::Left;
2233                     break;
2234             }
2235 
2236             h = cvehd[0x20];
2237             curve.text.fontUnderline = ((h & 0x1) != 0);
2238             curve.text.fontItalic = ((h & 0x2) != 0);
2239             curve.text.fontBold = ((h & 0x8) != 0);
2240             curve.text.whiteOut = ((h & 0x20) != 0);
2241 
2242             char offset = cvehd[0x37];
2243             curve.text.xOffset = offset * 5;
2244             offset = cvehd[0x38];
2245             curve.text.yOffset = offset * 5;
2246         }
2247 
2248         //vector
2249         if (curve.type == GraphCurve::FlowVector || curve.type == GraphCurve::Vector) {
2250             stmp.str(cvehd.substr(0x56));
2251             GET_FLOAT(stmp, curve.vector.multiplier)
2252 
2253             h = cvehd[0x5E];
2254             column = findDataByIndex(nColY - 1 + h - 0x64);
2255             if (column.first.size() > 0)
2256                 curve.vector.endXColumnName = column.second;
2257 
2258             h = cvehd[0x62];
2259             column = findDataByIndex(nColY - 1 + h - 0x64);
2260             if (column.first.size() > 0)
2261                 curve.vector.endYColumnName = column.second;
2262 
2263             h = cvehd[0x18];
2264             if (h >= 0x64) {
2265                 column = findDataByIndex(nColY - 1 + h - 0x64);
2266                 if (column.first.size() > 0)
2267                     curve.vector.angleColumnName = column.second;
2268             } else if (h <= 0x08)
2269                 curve.vector.constAngle = 45*h;
2270 
2271             h = cvehd[0x19];
2272             if (h >= 0x64){
2273                 column = findDataByIndex(nColY - 1 + h - 0x64);
2274                 if (column.first.size() > 0)
2275                     curve.vector.magnitudeColumnName = column.second;
2276             } else
2277                 curve.vector.constMagnitude = (int)curve.symbolSize;
2278 
2279             stmp.str(cvehd.substr(0x66));
2280             GET_SHORT(stmp, curve.vector.arrowLength)
2281             curve.vector.arrowAngle = cvehd[0x68];
2282 
2283             h = cvehd[0x69];
2284             curve.vector.arrowClosed = !(h & 0x1);
2285 
2286             stmp.str(cvehd.substr(0x70));
2287             GET_SHORT(stmp, w)
2288             curve.vector.width = (double)w/500.0;
2289 
2290             h = cvehd[0x142];
2291             switch (h) {
2292                 case 2:
2293                     curve.vector.position = VectorProperties::Midpoint;
2294                     break;
2295                 case 4:
2296                     curve.vector.position = VectorProperties::Head;
2297                     break;
2298                 default:
2299                     curve.vector.position = VectorProperties::Tail;
2300                     break;
2301             }
2302         }
2303         //pie
2304         if (curve.type == GraphCurve::Pie) {
2305             // code from Origin410/500Parser
2306             h = cvehd[0x14];
2307             curve.pie.formatPercentages = ((h & 0x08) != 0);
2308             curve.pie.formatValues = !curve.pie.formatPercentages;
2309             curve.pie.positionAssociate = ((h & 0x80) != 0);
2310             curve.pie.formatCategories  = ((h & 0x20) != 0);
2311 
2312             h = cvehd[0x19];
2313             curve.pie.radius = 100 - h;
2314 
2315             h = cvehd[0x1A];
2316             curve.pie.distance = h;
2317             curve.pie.formatAutomatic = true;
2318             curve.pie.viewAngle = 90;
2319             curve.pie.thickness = 33;
2320             curve.pie.rotation = 0;
2321             curve.pie.horizontalOffset = 0;
2322 
2323             if (cvehdsz > 0xA9) { // code from Origin750Parser.cpp
2324 
2325                 h = cvehd[0x92];
2326                 curve.pie.formatPercentages = ((h & 0x01) != 0);
2327                 curve.pie.formatValues = ((h & 0x02) != 0);
2328                 curve.pie.positionAssociate = ((h & 0x08) != 0);
2329                 curve.pie.clockwiseRotation = ((h & 0x20) != 0);
2330                 curve.pie.formatCategories = ((h & 0x80) != 0);
2331 
2332                 curve.pie.formatAutomatic = (cvehd[0x93] != 0);
2333                 stmp.str(cvehd.substr(0x94));
2334                 GET_SHORT(stmp, curve.pie.distance)
2335                 curve.pie.viewAngle = cvehd[0x96];
2336                 curve.pie.thickness = cvehd[0x98];
2337 
2338                 stmp.str(cvehd.substr(0x9A));
2339                 GET_SHORT(stmp, curve.pie.rotation)
2340 
2341                 stmp.str(cvehd.substr(0x9E));
2342                 GET_SHORT(stmp, curve.pie.displacement)
2343 
2344                 stmp.str(cvehd.substr(0xA0));
2345                 GET_SHORT(stmp, curve.pie.radius)
2346                 GET_SHORT(stmp, curve.pie.horizontalOffset)
2347 
2348                 stmp.str(cvehd.substr(0xA6));
2349                 GET_INT(stmp, curve.pie.displacedSectionCount)
2350             }
2351         }
2352         //surface
2353         if (glayer.isXYY3D || curve.type == GraphCurve::Mesh3D) {
2354             curve.surface.type = cvehd[0x17];
2355             h = cvehd[0x1C];
2356             if ((h & 0x60) == 0x60)
2357                 curve.surface.grids = SurfaceProperties::X;
2358             else if (h & 0x20)
2359                 curve.surface.grids = SurfaceProperties::Y;
2360             else if (h & 0x40)
2361                 curve.surface.grids = SurfaceProperties::None;
2362             else
2363                 curve.surface.grids = SurfaceProperties::XY;
2364 
2365             curve.surface.sideWallEnabled = ((h & 0x10) != 0);
2366             curve.surface.frontColor = getColor(cvehd.substr(0x1D,4));
2367 
2368             h = cvehd[0x13];
2369             curve.surface.backColorEnabled = ((h & 0x08) != 0);
2370             curve.surface.surface.fill = ((h & 0x10) != 0);
2371             curve.surface.surface.contour = ((h & 0x40) != 0);
2372             curve.surface.topContour.fill = ((h & 0x02) != 0);
2373             curve.surface.topContour.contour = ((h & 0x04) != 0);
2374             curve.surface.bottomContour.fill = ((h & 0x80) != 0);
2375             curve.surface.bottomContour.contour = ((h & 0x01) != 0);
2376 
2377             if (cvehdsz > 0x165) {
2378                 stmp.str(cvehd.substr(0x14C));
2379                 GET_SHORT(stmp, w)
2380                 curve.surface.gridLineWidth = (double)w/500.0;
2381                 curve.surface.gridColor = getColor(cvehd.substr(0x14E,4));
2382                 curve.surface.backColor = getColor(cvehd.substr(0x15A,4));
2383                 curve.surface.xSideWallColor = getColor(cvehd.substr(0x15E,4));
2384                 curve.surface.ySideWallColor = getColor(cvehd.substr(0x162,4));
2385             }
2386             if (cvehdsz > 0xA9) {
2387                 stmp.str(cvehd.substr(0x94));
2388                 GET_SHORT(stmp, w)
2389                 curve.surface.surface.lineWidth = (double)w/500.0;
2390                 curve.surface.surface.lineColor = getColor(cvehd.substr(0x96,4));
2391 
2392                 stmp.str(cvehd.substr(0xB4));
2393                 GET_SHORT(stmp, w)
2394                 curve.surface.topContour.lineWidth = (double)w/500.0;
2395                 curve.surface.topContour.lineColor = getColor(cvehd.substr(0xB6,4));
2396 
2397                 stmp.str(cvehd.substr(0xA4));
2398                 GET_SHORT(stmp, w)
2399                 curve.surface.bottomContour.lineWidth = (double)w/500.0;
2400                 curve.surface.bottomContour.lineColor = getColor(cvehd.substr(0xA6,4));
2401             }
2402 
2403         }
2404         if (curve.type == GraphCurve::Mesh3D || curve.type == GraphCurve::Contour || curve.type == GraphCurve::XYZContour) {
2405             if (curve.type == GraphCurve::Contour || curve.type == GraphCurve::XYZContour) glayer.isXYY3D = false;
2406             ColorMap& colorMap = (curve.type == GraphCurve::Mesh3D ? curve.surface.colorMap : curve.colorMap);
2407             h = cvehd[0x13];
2408             colorMap.fillEnabled = ((h & 0x82) != 0);
2409 
2410             if ((curve.type == GraphCurve::Contour) && (cvehdsz > 0x89)) {
2411                 stmp.str(cvehd.substr(0x7A));
2412                 GET_SHORT(stmp, curve.text.fontSize)
2413 
2414                 h = cvehd[0x83];
2415                 curve.text.fontUnderline = ((h & 0x1) != 0);
2416                 curve.text.fontItalic = ((h & 0x2) != 0);
2417                 curve.text.fontBold = ((h & 0x8) != 0);
2418                 curve.text.whiteOut = ((h & 0x20) != 0);
2419 
2420                 curve.text.color = getColor(cvehd.substr(0x86,4));
2421             }
2422             if (cvedtsz > 0x6C) {
2423                 getColorMap(colorMap, cvedt, cvedtsz);
2424             } else {
2425                 colorMap = glayer.colorMap;
2426             }
2427         }
2428 
2429         if (fileVersion >= 850) {
2430             curve.lineTransparency = cvehd[0x9C];
2431             h = cvehd[0x9D];
2432             curve.fillAreaWithLineTransparency = !h;
2433             curve.fillAreaTransparency = cvehd[0x11E];
2434         } else {
2435             // use sensible default values
2436             curve.fillAreaWithLineTransparency = false;
2437             curve.fillAreaTransparency = 255;
2438         }
2439 
2440         if (cvehdsz > 0x143) {
2441             curve.fillAreaColor = getColor(cvehd.substr(0xC2,4));
2442             stmp.str(cvehd.substr(0xC6));
2443             GET_SHORT(stmp, w)
2444             curve.fillAreaPatternWidth = (double)w/500.0;
2445 
2446             curve.fillAreaPatternColor = getColor(cvehd.substr(0xCA,4));
2447             curve.fillAreaPattern = cvehd[0xCE];
2448             curve.fillAreaPatternBorderStyle = cvehd[0xCF];
2449             stmp.str(cvehd.substr(0xD0));
2450             GET_SHORT(stmp, w)
2451             curve.fillAreaPatternBorderWidth = (double)w/500.0;
2452             curve.fillAreaPatternBorderColor = getColor(cvehd.substr(0xD2,4));
2453 
2454             curve.fillAreaTransparency = cvehd[0x11E];
2455 
2456             curve.lineColor = getColor(cvehd.substr(0x16A,4));
2457 
2458             if (curve.type != GraphCurve::Contour) curve.text.color = curve.lineColor;
2459 
2460             curve.symbolFillColor = getColor(cvehd.substr(0x12E,4));
2461             curve.symbolColor = getColor(cvehd.substr(0x132,4));
2462             curve.vector.color = curve.symbolColor;
2463 
2464             h = cvehd[0x136];
2465             curve.symbolThickness = (h == 255 ? 1 : h);
2466             curve.pointOffset = cvehd[0x137];
2467             h = cvehd[0x138];
2468             curve.symbolFillTransparency = cvehd[0x139];
2469 
2470             h = cvehd[0x143];
2471             curve.connectSymbols = ((h & 0x8) != 0);
2472         }
2473     }
2474 }
2475 
2476 void OriginAnyParser::getAxisBreakProperties(const string& abdata, unsigned int abdatasz) {
2477     istringstream stmp;
2478     (void) abdatasz;
2479 
2480     if (ispread != -1) { // spreadsheet
2481 
2482     } else if (imatrix != -1) { // matrix
2483 
2484 
2485     } else if (iexcel != -1) { // excel
2486 
2487 
2488     } else { // graph
2489 
2490         GraphLayer& glayer = graphs[igraph].layers[ilayer];
2491         unsigned char h = abdata[0x02];
2492         if (h == 2) {
2493             glayer.xAxisBreak.minorTicksBefore = glayer.xAxis.minorTicks;
2494             glayer.xAxisBreak.scaleIncrementBefore = glayer.xAxis.step;
2495             glayer.xAxisBreak.show = true;
2496             stmp.str(abdata.substr(0x0B));
2497             GET_DOUBLE(stmp, glayer.xAxisBreak.from)
2498             GET_DOUBLE(stmp, glayer.xAxisBreak.to)
2499             GET_DOUBLE(stmp, glayer.xAxisBreak.scaleIncrementAfter)
2500             GET_DOUBLE(stmp, glayer.xAxisBreak.position)
2501             h = abdata[0x2B];
2502             glayer.xAxisBreak.log10 = (h == 1);
2503             glayer.xAxisBreak.minorTicksAfter = abdata[0x2C];
2504         } else if (h == 4) {
2505             glayer.yAxisBreak.minorTicksBefore = glayer.yAxis.minorTicks;
2506             glayer.yAxisBreak.scaleIncrementBefore = glayer.yAxis.step;
2507             glayer.yAxisBreak.show = true;
2508             stmp.str(abdata.substr(0x0B));
2509             GET_DOUBLE(stmp, glayer.yAxisBreak.from)
2510             GET_DOUBLE(stmp, glayer.yAxisBreak.to)
2511             GET_DOUBLE(stmp, glayer.yAxisBreak.scaleIncrementAfter)
2512             GET_DOUBLE(stmp, glayer.yAxisBreak.position)
2513             h = abdata[0x2B];
2514             glayer.yAxisBreak.log10 = (h == 1);
2515             glayer.yAxisBreak.minorTicksAfter = abdata[0x2C];
2516         }
2517 
2518     }
2519 }
2520 
2521 void OriginAnyParser::getAxisParameterProperties(const string& apdata, unsigned int apdatasz, int naxis) {
2522     istringstream stmp;
2523     static int iaxispar = 0;
2524 
2525     if (igraph != -1) {
2526         unsigned char h = 0;
2527         unsigned short w = 0;
2528 
2529         GraphLayer& glayer = graphs[igraph].layers[ilayer];
2530         GraphAxis axis = glayer.xAxis;
2531         if (naxis == 1) {
2532             axis = glayer.xAxis;
2533         } else if (naxis == 2) {
2534             axis = glayer.yAxis;
2535         } else if (naxis == 3) {
2536             axis = glayer.zAxis;
2537         }
2538         if (iaxispar == 0) { // minor Grid
2539             h = apdata[0x26];
2540             axis.minorGrid.hidden = (h==0);
2541             axis.minorGrid.color = apdata[0x0F];
2542             axis.minorGrid.style = apdata[0x12];
2543             stmp.str(apdata.substr(0x15));
2544             GET_SHORT(stmp, w)
2545             axis.minorGrid.width = (double)w/500.0;
2546         } else if (iaxispar == 1) { // major Grid
2547             h = apdata[0x26];
2548             axis.majorGrid.hidden = (h==0);
2549             axis.majorGrid.color = apdata[0x0F];
2550             axis.majorGrid.style = apdata[0x12];
2551             stmp.str(apdata.substr(0x15));
2552             GET_SHORT(stmp, w)
2553             axis.majorGrid.width = (double)w/500.0;
2554         } else if (iaxispar == 2) { // tickaxis 0
2555             h = apdata[0x26];
2556             axis.tickAxis[0].showMajorLabels = ((h & 0x40) != 0);
2557             axis.tickAxis[0].color = apdata[0x0F];
2558             stmp.str(apdata.substr(0x13));
2559             GET_SHORT(stmp, w)
2560             axis.tickAxis[0].rotation = w/10;
2561             GET_SHORT(stmp, w)
2562             axis.tickAxis[0].fontSize = w;
2563             h = apdata[0x1A];
2564             axis.tickAxis[0].fontBold = ((h & 0x08) != 0);
2565             stmp.str(apdata.substr(0x23));
2566             GET_SHORT(stmp, w)
2567             h = apdata[0x25];
2568             unsigned char h1 = apdata[0x26];
2569             axis.tickAxis[0].valueType = (ValueType)(h & 0x0F);
2570             pair<string, string> column;
2571             switch (axis.tickAxis[0].valueType) {
2572                 case Numeric:
2573                     /*switch ((h>>4)) {
2574                         case 0x9:
2575                             axis.tickAxis[0].valueTypeSpecification=1;
2576                             break;
2577                         case 0xA:
2578                             axis.tickAxis[0].valueTypeSpecification=2;
2579                             break;
2580                         case 0xB:
2581                             axis.tickAxis[0].valueTypeSpecification=3;
2582                             break;
2583                         default:
2584                             axis.tickAxis[0].valueTypeSpecification=0;
2585                     }*/
2586                     if ((h>>4) > 7) {
2587                         axis.tickAxis[0].valueTypeSpecification = (h>>4) - 8;
2588                         axis.tickAxis[0].decimalPlaces = h1 - 0x40;
2589                     } else {
2590                         axis.tickAxis[0].valueTypeSpecification = (h>>4);
2591                         axis.tickAxis[0].decimalPlaces = -1;
2592                     }
2593                     break;
2594                 case Time:
2595                 case Date:
2596                 case Month:
2597                 case Day:
2598                 case ColumnHeading:
2599                     axis.tickAxis[0].valueTypeSpecification = h1 - 0x40;
2600                     break;
2601                 case Text:
2602                 case TickIndexedDataset:
2603                 case Categorical:
2604                     column = findDataByIndex(w-1);
2605                     if (column.first.size() > 0) {
2606                         axis.tickAxis[0].dataName = column.first;
2607                         axis.tickAxis[0].columnName = column.second;
2608                     }
2609                     break;
2610                 case TextNumeric: // Numeric Decimal 1.000
2611                     axis.tickAxis[0].valueType = Numeric;
2612                     axis.tickAxis[0].valueTypeSpecification = 0;
2613                     break;
2614             }
2615         } else if (iaxispar == 3) { // formataxis 0
2616             h = apdata[0x26];
2617             axis.formatAxis[0].hidden = (h == 0);
2618             axis.formatAxis[0].color = apdata[0x0F];
2619             if (apdatasz > 0x4B) {
2620                 stmp.str(apdata.substr(0x4A));
2621                 GET_SHORT(stmp, w)
2622                 axis.formatAxis[0].majorTickLength = (double)w/10.0;
2623             }
2624             stmp.str(apdata.substr(0x15));
2625             GET_SHORT(stmp, w)
2626             axis.formatAxis[0].thickness = (double)w/500.0;
2627             h = apdata[0x25];
2628             axis.formatAxis[0].minorTicksType = (h>>6);
2629             axis.formatAxis[0].majorTicksType = ((h>>4) & 3);
2630             axis.formatAxis[0].axisPosition = (h & 0x0F);
2631             short w1 = 0;
2632             switch (axis.formatAxis[0].axisPosition) { // TODO: check if correct
2633                 case 1:
2634                     stmp.str(apdata.substr(0x37));
2635                     GET_SHORT(stmp, w1)
2636                     axis.formatAxis[0].axisPositionValue = (double)w1;
2637                     break;
2638                 case 2:
2639                     stmp.str(apdata.substr(0x2F));
2640                     GET_DOUBLE(stmp, axis.formatAxis[0].axisPositionValue)
2641                     break;
2642             }
2643         } else if (iaxispar == 4) { // tickaxis 1
2644             h = apdata[0x26];
2645             axis.tickAxis[1].showMajorLabels = ((h & 0x40) != 0);
2646             axis.tickAxis[1].color = apdata[0x0F];
2647             stmp.str(apdata.substr(0x13));
2648             GET_SHORT(stmp, w)
2649             axis.tickAxis[1].rotation = w/10;
2650             GET_SHORT(stmp, w)
2651             axis.tickAxis[1].fontSize = w;
2652             h = apdata[0x1A];
2653             axis.tickAxis[1].fontBold = ((h & 0x08) != 0);
2654             stmp.str(apdata.substr(0x23));
2655             GET_SHORT(stmp, w)
2656             h = apdata[0x25];
2657             unsigned char h1 = apdata[0x26];
2658             axis.tickAxis[1].valueType = (ValueType)(h & 0x0F);
2659             pair<string, string> column;
2660             switch (axis.tickAxis[1].valueType) {
2661                 case Numeric:
2662                     /*switch ((h>>4)) {
2663                         case 0x9:
2664                             axis.tickAxis[1].valueTypeSpecification=1;
2665                             break;
2666                         case 0xA:
2667                             axis.tickAxis[1].valueTypeSpecification=2;
2668                             break;
2669                         case 0xB:
2670                             axis.tickAxis[1].valueTypeSpecification=3;
2671                             break;
2672                         default:
2673                             axis.tickAxis[1].valueTypeSpecification=0;
2674                     }*/
2675                     if ((h>>4) > 7) {
2676                         axis.tickAxis[1].valueTypeSpecification = (h>>4) - 8;
2677                         axis.tickAxis[1].decimalPlaces = h1 - 0x40;
2678                     } else {
2679                         axis.tickAxis[1].valueTypeSpecification = (h>>4);
2680                         axis.tickAxis[1].decimalPlaces = -1;
2681                     }
2682                     break;
2683                 case Time:
2684                 case Date:
2685                 case Month:
2686                 case Day:
2687                 case ColumnHeading:
2688                     axis.tickAxis[1].valueTypeSpecification = h1 - 0x40;
2689                     break;
2690                 case Text:
2691                 case TickIndexedDataset:
2692                 case Categorical:
2693                     column = findDataByIndex(w-1);
2694                     if (column.first.size() > 0) {
2695                         axis.tickAxis[1].dataName = column.first;
2696                         axis.tickAxis[1].columnName = column.second;
2697                     }
2698                     break;
2699                 case TextNumeric: // Numeric Decimal 1.000
2700                     axis.tickAxis[1].valueType = Numeric;
2701                     axis.tickAxis[1].valueTypeSpecification = 0;
2702                 break;
2703             }
2704         } else if (iaxispar == 5) { // formataxis 1
2705             h = apdata[0x26];
2706             axis.formatAxis[1].hidden = (h == 0);
2707             axis.formatAxis[1].color = apdata[0x0F];
2708             if (apdatasz > 0x4B) {
2709                 stmp.str(apdata.substr(0x4A));
2710                 GET_SHORT(stmp, w)
2711                 axis.formatAxis[1].majorTickLength = (double)w/10.0;
2712             }
2713             stmp.str(apdata.substr(0x15));
2714             GET_SHORT(stmp, w)
2715             axis.formatAxis[1].thickness = (double)w/500.0;
2716             h = apdata[0x25];
2717             axis.formatAxis[1].minorTicksType = (h>>6);
2718             axis.formatAxis[1].majorTicksType = ((h>>4) & 3);
2719             axis.formatAxis[1].axisPosition = (h & 0x0F);
2720             short w1 = 0;
2721             switch (axis.formatAxis[1].axisPosition) { // TODO: check if correct
2722                 case 1:
2723                     stmp.str(apdata.substr(0x37));
2724                     GET_SHORT(stmp, w1)
2725                     axis.formatAxis[1].axisPositionValue = (double)w1;
2726                     break;
2727                 case 2:
2728                     stmp.str(apdata.substr(0x2F));
2729                     GET_DOUBLE(stmp, axis.formatAxis[1].axisPositionValue)
2730                     break;
2731             }
2732         }
2733 
2734         if (naxis == 1) {
2735             glayer.xAxis = axis;
2736         } else if (naxis == 2) {
2737             glayer.yAxis = axis;
2738         } else if (naxis == 3) {
2739             glayer.zAxis = axis;
2740         }
2741 
2742         iaxispar++;
2743         iaxispar %= 6;
2744 
2745     }
2746 }
2747 
2748 void OriginAnyParser::getNoteProperties(const string& nwehd, unsigned int nwehdsz, const string& nwelb, unsigned int nwelbsz, const string& nwect, unsigned int nwectsz) {
2749     LOG_PRINT(logfile, "OriginAnyParser::getNoteProperties()");
2750     istringstream stmp;
2751     (void) nwehdsz; (void) nwelbsz; (void) nwectsz;
2752 
2753     // note window position and size
2754     Rect rect;
2755     unsigned int coord;
2756     stmp.str(nwehd);
2757     GET_INT(stmp, coord)
2758     rect.left = coord;
2759     GET_INT(stmp, coord)
2760     rect.top = coord;
2761     GET_INT(stmp, coord)
2762     rect.right = coord;
2763     GET_INT(stmp, coord)
2764     rect.bottom = coord;
2765 
2766     string name = nwelb.c_str();
2767 
2768     // ResultsLog note window has left, top, right, bottom all zero.
2769     // All other parameters are also zero, except "name" and "text".
2770     if (!rect.bottom || !rect.right) {
2771         resultsLog = nwect;
2772         return;
2773     }
2774     unsigned char state = nwehd[0x18];
2775 
2776     // files from version < 6.0 have nwehdsz < 1D
2777     if (nwehdsz < 0x2F) return;
2778 
2779     double creationDate, modificationDate;
2780     stmp.str(nwehd.substr(0x20));
2781     GET_DOUBLE(stmp, creationDate)
2782     GET_DOUBLE(stmp, modificationDate)
2783 
2784     if (nwehdsz < 0x38) return;
2785     unsigned char c = nwehd[0x38];
2786 
2787     if (nwehdsz < 0x3F) return;
2788     unsigned int labellen = 0;
2789     stmp.str(nwehd.substr(0x3C));
2790     GET_INT(stmp, labellen)
2791 
2792     notes.push_back(Note(name));
2793     LOG_PRINT(logfile,"notes: %d\n", (int)notes.size());
2794     notes.back().objectID = objectIndex;
2795     ++objectIndex;
2796 
2797     notes.back().frameRect = rect;
2798     notes.back().creationDate = doubleToPosixTime(creationDate);
2799     notes.back().modificationDate = doubleToPosixTime(modificationDate);
2800 
2801     if (c == 0x01)
2802         notes.back().title = Window::Label;
2803     else if (c == 0x02)
2804         notes.back().title = Window::Name;
2805     else
2806         notes.back().title = Window::Both;
2807 
2808     if (state == 0x07)
2809         notes.back().state = Window::Minimized;
2810     else if (state == 0x0b)
2811         notes.back().state = Window::Maximized;
2812 
2813     notes.back().hidden = ((state & 0x40) != 0);
2814 
2815     if (labellen > 1) {
2816         notes.back().label = nwect.substr(0,labellen);
2817         notes.back().text = nwect.substr(labellen).c_str();
2818     } else {
2819         notes.back().text = nwect.c_str();
2820     }
2821 }
2822 
2823 void OriginAnyParser::getColorMap(ColorMap& cmap, const string& cmapdata, unsigned int cmapdatasz) {
2824     istringstream stmp;
2825     unsigned int cmoffset = 0;
2826     // color maps for matrix annotations have a different offset than graph curve's colormaps
2827     if (imatrix != -1) {
2828         cmoffset = 0x14;
2829     } else if (igraph != -1) {
2830         cmoffset = 0x6C;
2831     } else {
2832         return;
2833     }
2834 
2835     stmp.str(cmapdata.substr(cmoffset));
2836     unsigned int colorMapSize = 0;
2837     GET_INT(stmp, colorMapSize)
2838 
2839     // check we have enough data to fill the map
2840     unsigned int minDataSize = cmoffset + 0x114 + (colorMapSize+2)*0x38;
2841     if (minDataSize > cmapdatasz) {
2842         LOG_PRINT(logfile, "WARNING: Too few data while getting ColorMap. Needed: at least %d bytes. Have: %d bytes.\n", minDataSize, cmapdatasz)
2843         return;
2844     }
2845 
2846     unsigned int lvl_offset = 0;
2847     for (unsigned int i = 0; i < colorMapSize + 3; ++i) {
2848         lvl_offset = cmoffset + 0x114 + i*0x38;
2849         ColorMapLevel level;
2850 
2851         level.fillPattern = cmapdata[lvl_offset];
2852         level.fillPatternColor = getColor(cmapdata.substr(lvl_offset+0x04, 4));
2853 
2854         stmp.str(cmapdata.substr(lvl_offset+0x08));
2855         unsigned short w;
2856         GET_SHORT(stmp, w)
2857         level.fillPatternLineWidth = (double)w/500.0;
2858 
2859         level.lineStyle = cmapdata[lvl_offset+0x10];
2860 
2861         stmp.str(cmapdata.substr(lvl_offset+0x12));
2862         GET_SHORT(stmp, w)
2863         level.lineWidth = (double)w/500.0;
2864 
2865         level.lineColor = getColor(cmapdata.substr(lvl_offset+0x14, 4));
2866 
2867         unsigned char h = cmapdata[lvl_offset+0x1A];
2868         level.labelVisible = (h & 0x1);
2869         level.lineVisible = !(h & 0x2);
2870 
2871         level.fillColor = getColor(cmapdata.substr(lvl_offset+0x28, 4));
2872 
2873         double value = 0.0;
2874         stmp.str(cmapdata.substr(lvl_offset+0x30));
2875         GET_DOUBLE(stmp, value)
2876 
2877         cmap.levels.push_back(make_pair(value, level));
2878     }
2879 
2880 }
2881 
2882 void OriginAnyParser::getZcolorsMap(ColorMap& colorMap, const string& cmapdata, unsigned int cmapdatasz) {
2883     istringstream stmp;
2884     (void) cmapdatasz;
2885 
2886     Color lowColor;//color below
2887     lowColor.type = Origin::Color::Custom;
2888     lowColor.custom[0] = cmapdata[0x0E];
2889     lowColor.custom[1] = cmapdata[0x0F];
2890     lowColor.custom[2] = cmapdata[0x10];
2891     // skip an unsigned char at 0x11
2892 
2893     Color highColor;//color above
2894     highColor.type = Origin::Color::Custom;
2895     highColor.custom[0] = cmapdata[0x12];
2896     highColor.custom[1] = cmapdata[0x13];
2897     highColor.custom[2] = cmapdata[0x14];
2898     // skip an unsigned char at 0x15
2899 
2900     unsigned short colorMapSize;
2901     stmp.str(cmapdata.substr(0x16));
2902     GET_SHORT(stmp, colorMapSize)
2903     // skip a short at 0x18-0x19
2904 
2905     for (int i = 0; i < 4; ++i) {//low, high, middle and missing data colors
2906         Color color; (void) color;
2907         color.type = Origin::Color::Custom;
2908         color.custom[0] = cmapdata[0x1A+4*i];
2909         color.custom[1] = cmapdata[0x1B+4*i];
2910         color.custom[2] = cmapdata[0x1C+4*i];
2911     }
2912 
2913     double zmin, zmax, zmissing;
2914     stmp.str(cmapdata.substr(0x2A));
2915     GET_DOUBLE(stmp, zmin);
2916     GET_DOUBLE(stmp, zmax);
2917     GET_DOUBLE(stmp, zmissing);
2918 
2919     short val;
2920     for (int i = 0; i < 2; ++i) {
2921         Color color; (void) color;
2922         color.type = Origin::Color::Custom;
2923         color.custom[0] = cmapdata[0x66+10*i];
2924         color.custom[1] = cmapdata[0x67+10*i];
2925         color.custom[2] = cmapdata[0x68+10*i];
2926         // skip an unsigned char at 0x69+10*i
2927         stmp.str(cmapdata.substr(0x6A+10*i));
2928         GET_SHORT(stmp, val)
2929     }
2930 
2931     ColorMapLevel level;
2932     level.fillColor = lowColor;
2933     colorMap.levels.push_back(make_pair(zmin, level));
2934 
2935     for (int i = 0; i < (colorMapSize + 1); ++i) {
2936         Color color; (void) color;
2937         color.type = Origin::Color::Custom;
2938         color.custom[0] = cmapdata[0x7A+10*i];
2939         color.custom[1] = cmapdata[0x7B+10*i];
2940         color.custom[2] = cmapdata[0x7C+10*i];
2941         // skip an unsigned char at 0x7D+10*i
2942         stmp.str(cmapdata.substr((0x7E)+10*i));
2943         GET_SHORT(stmp, val)
2944 
2945         level.fillColor = color;
2946         colorMap.levels.push_back(make_pair(val, level));
2947     }
2948 
2949     level.fillColor = highColor;
2950     colorMap.levels.push_back(make_pair(zmax, level));
2951 }
2952 
2953 void OriginAnyParser::getProjectLeafProperties(tree<ProjectNode>::iterator current_folder, const string& ptldt, unsigned int ptldtsz) {
2954     LOG_PRINT(logfile,"OriginAnyParser::getProjectLeafProperties()\n");
2955     istringstream stmp;
2956     (void) ptldtsz;
2957 
2958     stmp.str(ptldt);
2959     unsigned int file_type = 0, file_object_id = 0;
2960     GET_INT(stmp, file_type);
2961     GET_INT(stmp, file_object_id);
2962 
2963     LOG_PRINT(logfile,"file_type=%d file_object_id=%d\n",file_type,file_object_id);
2964     if (file_type == 0x100000) { // Note window
2965         LOG_PRINT(logfile,"notes.size()=%d\n",(int)notes.size());
2966         if ((file_object_id <= notes.size()) && (notes.size()>0)) {
2967             projectTree.append_child(current_folder, ProjectNode(notes[file_object_id].name, ProjectNode::Note));
2968         }
2969     } else { // other windows
2970         tree<Origin::ProjectNode>::iterator childnode;
2971         pair<ProjectNode::NodeType, Origin::Window> object = findWindowObjectByIndex(file_object_id);
2972         childnode=projectTree.append_child(current_folder, ProjectNode(object.second.name, object.first));
2973         (*childnode).creationDate = object.second.creationDate;
2974         (*childnode).modificationDate = object.second.modificationDate;
2975     }
2976 }
2977 
2978 void OriginAnyParser::getProjectFolderProperties(tree<ProjectNode>::iterator current_folder, const string& flehd, unsigned int flehdsz) {
2979     istringstream stmp;
2980     (void) flehdsz;
2981 
2982     unsigned char a = flehd[0x02];
2983     (*current_folder).active = (a == 1);
2984 
2985     double creationDate, modificationDate;
2986     stmp.str(flehd.substr(0x10));
2987     GET_DOUBLE(stmp, creationDate);
2988     GET_DOUBLE(stmp, modificationDate);
2989 
2990     (*current_folder).creationDate = doubleToPosixTime(creationDate);
2991     (*current_folder).modificationDate = doubleToPosixTime(modificationDate);
2992 }
2993 
2994 void OriginAnyParser::outputProjectTree(std::ostream & out) {
2995     size_t windowsCount = spreadSheets.size()+matrixes.size()+excels.size()+graphs.size()+notes.size();
2996 
2997     out << "Project has " << windowsCount << " windows." << endl;
2998     out << "Origin project Tree" << endl;
2999 
3000     char cdsz[21];
3001     for (tree<ProjectNode>::iterator it = projectTree.begin(projectTree.begin()); it != projectTree.end(projectTree.begin()); ++it) {
3002         strftime(cdsz, sizeof(cdsz), "%F %T", gmtime(&(*it).creationDate));
3003         out <<  string(projectTree.depth(it) - 1, ' ') <<  (*it).name.c_str() << "\t" << cdsz << endl;
3004     }
3005 }