File indexing completed on 2024-05-19 11:26:46

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