Coverage Report

Created: 2026-09-14 06:37

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/libwps/src/lib/QuattroDosChart.cpp
Line
Count
Source
1
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
2
/* libwps
3
 * Version: MPL 2.0 / LGPLv2.1+
4
 *
5
 * This Source Code Form is subject to the terms of the Mozilla Public
6
 * License, v. 2.0. If a copy of the MPL was not distributed with this
7
 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
8
 *
9
 * Major Contributor(s):
10
 * Copyright (C) 2006, 2007 Andrew Ziem
11
 * Copyright (C) 2004 Marc Maurer (uwog@uwog.net)
12
 * Copyright (C) 2004-2006 Fridrich Strba (fridrich.strba@bluewin.ch)
13
 *
14
 * For minor contributions see the git repository.
15
 *
16
 * Alternatively, the contents of this file may be used under the terms
17
 * of the GNU Lesser General Public License Version 2.1 or later
18
 * (LGPLv2.1+), in which case the provisions of the LGPLv2.1+ are
19
 * applicable instead of those above.
20
 */
21
22
#include <stdlib.h>
23
#include <string.h>
24
25
#include <cmath>
26
#include <sstream>
27
#include <limits>
28
#include <map>
29
#include <stack>
30
31
#include <librevenge-stream/librevenge-stream.h>
32
33
#include "libwps_internal.h"
34
#include "libwps_tools_win.h"
35
36
#include "WKSChart.h"
37
#include "WKSContentListener.h"
38
#include "WPSEntry.h"
39
#include "WPSFont.h"
40
#include "WPSPosition.h"
41
42
#include "QuattroDos.h"
43
44
#include "QuattroDosChart.h"
45
46
namespace QuattroDosChartInternal
47
{
48
49
///////////////////////////////////////////////////////////////////
50
//! the chart of a Quattro Pro Dos
51
52
class Chart final : public WKSChart
53
{
54
public:
55
  //! constructor
56
  explicit Chart(QuattroDosChart &parser, RVNGInputStreamPtr const &input)
57
60.3k
    : WKSChart()
58
60.3k
    , m_spreadsheetId(-1)
59
60.3k
    , m_parser(parser)
60
60.3k
    , m_input(input)
61
60.3k
  {
62
60.3k
  }
63
  //! send the zone content (called when the zone is of text type)
64
  void sendContent(TextZone const &zone, WPSListenerPtr &listener) const final;
65
66
  //! the chart range
67
  Position m_ranges[2];
68
  //! the sheet id
69
  int m_spreadsheetId;
70
protected:
71
  //! the parser
72
  QuattroDosChart &m_parser;
73
  //! the input
74
  RVNGInputStreamPtr m_input;
75
};
76
77
void Chart::sendContent(TextZone const &zone, WPSListenerPtr &listener) const
78
750
{
79
750
  if (!listener.get())
80
0
  {
81
0
    WPS_DEBUG_MSG(("QuattroDosChartInternal::Chart::sendContent: no listener\n"));
82
0
    return;
83
0
  }
84
750
  long pos = m_input->tell();
85
750
  listener->setFont(zone.m_font);
86
750
  bool sendText=false;
87
750
  for (auto const &e : zone.m_textEntryList)
88
750
  {
89
750
    if (!e.valid()) continue;
90
750
    if (sendText) listener->insertEOL(true);
91
750
    m_parser.sendText(e);
92
750
    sendText=true;
93
750
  }
94
750
  m_input->seek(pos, librevenge::RVNG_SEEK_SET);
95
750
}
96
97
//! the state of QuattroDosChart
98
struct State
99
{
100
  //! constructor
101
  State()
102
15.4k
    : m_eof(-1)
103
15.4k
    , m_version(-1)
104
15.4k
    , m_chartType(-1)
105
15.4k
    , m_idToChartMap()
106
15.4k
  {
107
15.4k
  }
108
  //! returns the pattern corresponding to a pattern id between 0 and 15
109
  static bool getPattern(int id, WPSGraphicStyle::Pattern &pattern);
110
111
  //! the last file position
112
  long m_eof;
113
  //! the file version
114
  int m_version;
115
  //! the chart type
116
  int m_chartType;
117
  //! map sheet id to chart
118
  std::multimap<int, std::shared_ptr<Chart> > m_idToChartMap;
119
120
};
121
122
bool State::getPattern(int id, WPSGraphicStyle::Pattern &pat)
123
263k
{
124
263k
  if (id<0 || id>15)
125
51.2k
  {
126
51.2k
    WPS_DEBUG_MSG(("QuattroDosChartInternal::State::getPattern(): unknown pattern id: %d\n",id));
127
51.2k
    return false;
128
51.2k
  }
129
212k
  static const uint16_t  patterns[]=
130
212k
  {
131
212k
    0xffff, 0xffff, 0xffff, 0xffff, 0x0000, 0x0000, 0x0000, 0x0000, 0x00ff, 0xff00, 0x00ff, 0xff00, 0xeedd, 0xbb77, 0xeedd, 0xbb77, // 0-3
132
212k
    0x3366, 0xcc99, 0x3366, 0xcc99, 0x8844, 0x2211, 0x8844, 0x2211, 0x99cc, 0x6633, 0x99cc, 0x6633, 0xff10, 0x1010, 0xff10, 0xff10, // 4-7
133
212k
    0xbf7f, 0xfefc, 0x7bb7, 0xcfdf, 0xaa55, 0xaa55, 0xaa55, 0xaa55, 0x7fff, 0xffff, 0xf7ff, 0xffff, 0x77ff, 0xddff, 0x77ff, 0xddff, // 8-9
134
212k
    0x990f, 0x050f, 0x99f0, 0x50f0, 0x0101, 0x01ff, 0x1010, 0x10ff, 0xbf7f, 0xfefc, 0x7bb7, 0xcfdf, 0xf77f, 0xbfdf, 0xeffe, 0xfdfb // 12-15
135
212k
  };
136
212k
  pat.m_dim=Vec2i(8,8);
137
212k
  uint16_t const *ptr=&patterns[4*id];
138
212k
  pat.m_data.resize(8);
139
1.06M
  for (size_t i=0; i < 8; i+=2)
140
851k
  {
141
851k
    uint16_t val=*(ptr++);
142
851k
    pat.m_data[i]=static_cast<unsigned char>((val>>8) & 0xFF);
143
851k
    pat.m_data[i+1]=static_cast<unsigned char>(val & 0xFF);
144
851k
  }
145
212k
  return true;
146
263k
}
147
}
148
149
// constructor, destructor
150
QuattroDosChart::QuattroDosChart(QuattroDosParser &parser)
151
15.4k
  : m_input(parser.getInput())
152
15.4k
  , m_listener()
153
15.4k
  , m_mainParser(parser)
154
15.4k
  , m_state(new QuattroDosChartInternal::State)
155
15.4k
  , m_asciiFile(parser.ascii())
156
15.4k
{
157
15.4k
}
158
159
QuattroDosChart::~QuattroDosChart()
160
15.4k
{
161
15.4k
}
162
163
int QuattroDosChart::version() const
164
108k
{
165
108k
  if (m_state->m_version<0)
166
4.10k
    m_state->m_version=m_mainParser.version();
167
108k
  return m_state->m_version;
168
108k
}
169
170
bool QuattroDosChart::checkFilePosition(long pos)
171
0
{
172
0
  if (m_state->m_eof < 0)
173
0
  {
174
0
    long actPos = m_input->tell();
175
0
    m_input->seek(0, librevenge::RVNG_SEEK_END);
176
0
    m_state->m_eof=m_input->tell();
177
0
    m_input->seek(actPos, librevenge::RVNG_SEEK_SET);
178
0
  }
179
0
  return pos <= m_state->m_eof;
180
0
}
181
182
int QuattroDosChart::getNumSpreadsheets() const
183
7.11k
{
184
7.11k
  auto it = m_state->m_idToChartMap.end();
185
7.11k
  if (it==m_state->m_idToChartMap.begin())
186
5.29k
    return 0;
187
1.82k
  --it;
188
1.82k
  if (it->first>255)
189
12
  {
190
12
    WPS_DEBUG_MSG(("QuattroDosChart::getNumSpreadsheets: the number of spreadsheets seems to big: %d\n", it->first));
191
12
    return 256;
192
12
  }
193
1.80k
  return it->first+1;
194
1.82k
}
195
196
void QuattroDosChart::getChartPositionMap(int sheetId, std::map<Vec2i, Vec2i> &cellList) const
197
83.7k
{
198
83.7k
  cellList.clear();
199
83.7k
  auto it = m_state->m_idToChartMap.lower_bound(sheetId);
200
116k
  while (it!=m_state->m_idToChartMap.end() && it->first==sheetId)
201
33.1k
  {
202
33.1k
    if (it->second)
203
33.1k
      cellList[it->second->m_ranges[0].m_pos]=it->second->m_ranges[1].m_pos;
204
33.1k
    ++it;
205
33.1k
  }
206
83.7k
}
207
208
bool  QuattroDosChart::updateSpreadsheetNames(QuattroDosChartInternal::Chart &chart)
209
3.83k
{
210
3.83k
  for (auto &range : chart.m_ranges)
211
7.66k
  {
212
7.66k
    if (range.m_sheetId>=0)
213
7.66k
      range.m_sheetName = m_mainParser.getSheetName(range.m_sheetId);
214
7.66k
  }
215
3.83k
  for (auto &it : chart.getIdSerieMap())
216
13.8k
  {
217
13.8k
    auto &serie=const_cast<WKSChart::Serie &>(it.second);
218
13.8k
    for (auto &range : serie.m_ranges)
219
27.6k
    {
220
27.6k
      if (range.m_sheetId>=0)
221
27.6k
        range.m_sheetName = m_mainParser.getSheetName(range.m_sheetId);
222
27.6k
    }
223
13.8k
    for (auto &range : serie.m_labelRanges)
224
27.6k
    {
225
27.6k
      if (range.m_sheetId>=0)
226
15.5k
        range.m_sheetName = m_mainParser.getSheetName(range.m_sheetId);
227
27.6k
    }
228
13.8k
  }
229
3.83k
  for (auto &range : chart.getAxis(0).m_labelRanges)
230
7.66k
  {
231
7.66k
    if (range.m_sheetId>=0)
232
4.42k
      range.m_sheetName = m_mainParser.getSheetName(range.m_sheetId);
233
7.66k
  }
234
3.83k
  return true;
235
3.83k
}
236
237
bool QuattroDosChart::sendChart(int sheetId, Vec2i const &cell, Vec2f const &chartSize)
238
3.83k
{
239
3.83k
  if (m_listener.get() == nullptr)
240
0
  {
241
0
    WPS_DEBUG_MSG(("QuattroDosChart::sendChart: I can not find the listener\n"));
242
0
    return false;
243
0
  }
244
3.83k
  auto it = m_state->m_idToChartMap.lower_bound(sheetId);
245
24.5k
  while (it!=m_state->m_idToChartMap.end() && it->first==sheetId)
246
24.5k
  {
247
24.5k
    if (it->second && it->second->m_ranges[0].m_pos==cell)
248
3.83k
    {
249
3.83k
      updateSpreadsheetNames(*it->second);
250
3.83k
      Vec2f size(chartSize[0]>0 ? chartSize[0] : 100, chartSize[1]>0 ? chartSize[1] : 100);
251
3.83k
      WPSPosition pos(Vec2f(0,0), size, librevenge::RVNG_POINT);
252
3.83k
      pos.m_anchorTo = WPSPosition::Cell;
253
3.83k
      auto endRange=it->second->m_ranges[1];
254
3.83k
      endRange.m_pos += Vec2i(1,1);
255
3.83k
      pos.m_anchorCellName = endRange.getCellName();
256
      // the chart is in fact a 1024*7?? windows scaled back on the chosen cells
257
3.83k
      it->second->m_dimension=Vec2f(1024,700);
258
3.83k
      m_listener->insertChart(pos, *it->second);
259
3.83k
      return true;
260
3.83k
    }
261
20.7k
    ++it;
262
20.7k
  }
263
0
  WPS_DEBUG_MSG(("QuattroDosChart::sendChart: can not find chart %dx%d[%d]\n", cell[0], cell[1], sheetId));
264
0
  return false;
265
3.83k
}
266
267
bool QuattroDosChart::sendText(WPSEntry const &entry)
268
750
{
269
750
  if (m_listener.get() == nullptr)
270
0
  {
271
0
    WPS_DEBUG_MSG(("QuattroDosChart::sendText: I can not find the listener\n"));
272
0
    return false;
273
0
  }
274
750
  if (!entry.valid())
275
0
    return true;
276
750
  m_input->seek(entry.begin(), librevenge::RVNG_SEEK_SET);
277
750
  std::string text;
278
12.3k
  for (long i=0; i<entry.length(); ++i)
279
11.6k
  {
280
11.6k
    auto c = char(libwps::readU8(m_input));
281
11.6k
    if (c == '\0') continue;
282
7.58k
    text.push_back(c);
283
7.58k
  }
284
750
  if (!text.empty())
285
750
    m_listener->insertUnicodeString(libwps_tools_win::Font::unicodeString(text, m_mainParser.getDefaultFontType()));
286
750
  return true;
287
750
}
288
////////////////////////////////////////////////////////////
289
// low level
290
291
////////////////////////////////////////////////////////////
292
// general
293
////////////////////////////////////////////////////////////
294
bool QuattroDosChart::readChartSetType()
295
1.45k
{
296
297
1.45k
  long pos = m_input->tell();
298
1.45k
  auto type = long(libwps::readU16(m_input));
299
1.45k
  if (type != 0xb8 && type != 0xca)
300
0
  {
301
0
    WPS_DEBUG_MSG(("QuattroDosChart::readChartSetType: not a chart definition\n"));
302
0
    return false;
303
0
  }
304
1.45k
  libwps::DebugStream f;
305
1.45k
  f << "Entries(" << (type==0xb8 ? "Chart3d" : "ChartBubble") << "):";
306
1.45k
  auto sz = long(libwps::readU16(m_input));
307
1.45k
  if (sz!=1)
308
849
  {
309
849
    WPS_DEBUG_MSG(("QuattroDosChart::readChartSetType: find unexpected size\n"));
310
849
    f << "###";
311
849
    ascii().addPos(pos);
312
849
    ascii().addNote(f.str().c_str());
313
849
    m_input->seek(sz, librevenge::RVNG_SEEK_CUR);
314
849
    return true;
315
849
  }
316
608
  m_state->m_chartType=int(libwps::readU8(m_input));
317
608
  f << "type=" << m_state->m_chartType << ",";
318
608
  ascii().addPos(pos);
319
608
  ascii().addNote(f.str().c_str());
320
608
  return true;
321
1.45k
}
322
323
bool QuattroDosChart::readChartName()
324
597
{
325
326
597
  long pos = m_input->tell();
327
597
  auto type = long(libwps::readU16(m_input));
328
597
  if (type != 0xb9)
329
0
  {
330
0
    WPS_DEBUG_MSG(("QuattroDosChart::readChartName: not a chart definition\n"));
331
0
    return false;
332
0
  }
333
597
  libwps::DebugStream f;
334
597
  f << "Entries(ChartName):";
335
597
  auto sz = long(libwps::readU16(m_input));
336
597
  if (sz!=0x10)
337
310
  {
338
310
    WPS_DEBUG_MSG(("QuattroDosChart::readChartName: find unexpected size\n"));
339
310
    f << "###";
340
310
    ascii().addPos(pos);
341
310
    ascii().addNote(f.str().c_str());
342
310
    m_input->seek(sz, librevenge::RVNG_SEEK_CUR);
343
310
    return true;
344
310
  }
345
287
  librevenge::RVNGString name("");
346
287
  if (!m_mainParser.readPString(name,15))
347
136
    f << "##sSz,";
348
151
  else if (!name.empty())
349
60
    f << name.cstr() << ',';
350
287
  ascii().addPos(pos);
351
287
  ascii().addNote(f.str().c_str());
352
287
  return true;
353
597
}
354
355
bool QuattroDosChart::readChart()
356
108k
{
357
108k
  int vers=version();
358
108k
  libwps::DebugStream f;
359
360
108k
  long pos = m_input->tell();
361
108k
  auto type = long(libwps::readU16(m_input));
362
108k
  if (type != 0x2D && type != 0x2e)
363
0
  {
364
0
    WPS_DEBUG_MSG(("QuattroDosChart::readChart: not a chart definition\n"));
365
0
    return false;
366
0
  }
367
108k
  auto sz = long(libwps::readU16(m_input));
368
108k
  int normalSz = 0x2ca;
369
108k
  if (type == 0x2e) normalSz += 0x10;
370
108k
  if (vers>=2) normalSz+=2*26+4;
371
108k
  if (sz < normalSz)
372
48.6k
  {
373
48.6k
    WPS_DEBUG_MSG(("QuattroDosChart::readChart: chart definition too short\n"));
374
48.6k
    ascii().addPos(pos);
375
48.6k
    ascii().addNote("Entries(ChartDef):###");
376
48.6k
    return true;
377
48.6k
  }
378
379
60.3k
  f << "Entries(ChartDef):";
380
60.3k
  std::shared_ptr<QuattroDosChartInternal::Chart> chart(new QuattroDosChartInternal::Chart(*this, m_input));
381
60.3k
  if (type == 0x2e)
382
441
  {
383
441
    long actPos = m_input->tell();
384
441
    librevenge::RVNGString name("");
385
441
    if (!m_mainParser.readPString(name,15))
386
169
      f << "##sSz,";
387
272
    else if (!name.empty())
388
73
    {
389
73
      chart->m_name=name;
390
73
      f << "title=" << name.cstr() << ',';
391
73
    }
392
441
    m_input->seek(actPos+16, librevenge::RVNG_SEEK_SET);
393
441
  }
394
60.3k
  bool hasLegend=false;
395
844k
  for (int i = 0; i < 13; i++) // 0: X-serie, 1-6: data[series%d], 7-12:label serie%d
396
783k
  {
397
783k
    WKSChart::Position ranges[2];
398
783k
    for (auto &range : ranges)
399
1.56M
    {
400
1.56M
      int dim[3];
401
5.14M
      for (int d=0; d<(vers>=2 ? 3 : 2); ++d) dim[d] = int(libwps::read16(m_input));
402
1.56M
      if (dim[0]==-1)
403
27.3k
        continue;
404
1.54M
      range = WKSChart::Position(Vec2i(dim[0],dim[1]));
405
1.54M
      range.m_sheetId=vers<2 ? 0 : dim[2];
406
1.54M
    }
407
783k
    if (ranges[0].valid(ranges[1]))
408
550k
    {
409
550k
      f << "z" << i << "=" << ranges[0] << ":" << ranges[1] << ",";
410
550k
      if (i==0)
411
47.0k
      {
412
47.0k
        auto &axis=chart->getAxis(0);
413
47.0k
        axis.m_labelRanges[0]=ranges[0];
414
47.0k
        axis.m_labelRanges[1]=ranges[1];
415
47.0k
      }
416
503k
      else if (i<=6)
417
263k
      {
418
263k
        auto *serie=chart->getSerie(i-1, true);
419
263k
        serie->m_ranges[0]=ranges[0];
420
263k
        serie->m_ranges[1]=ranges[1];
421
263k
      }
422
239k
      else
423
239k
      {
424
239k
        auto *serie=chart->getSerie(i-7, false);
425
239k
        if (serie)
426
189k
        {
427
189k
          serie->m_labelRanges[0]=ranges[0];
428
189k
          serie->m_labelRanges[1]=ranges[1];
429
189k
        }
430
239k
        if (ranges[0]!=ranges[1])
431
62.5k
        {
432
62.5k
          WPS_DEBUG_MSG(("QuattroDosChart::readChart: unexpected label ranges for %d\n", i));
433
62.5k
        }
434
239k
      }
435
550k
    }
436
783k
  }
437
60.3k
  ascii().addPos(pos);
438
60.3k
  ascii().addNote(f.str().c_str());
439
60.3k
  char const *axisNames[]= {"X", "Y", "Y2"};
440
60.3k
  pos=m_input->tell();
441
60.3k
  f.str("");
442
60.3k
  f << "ChartDef-A:"; // GraphDef0
443
60.3k
  auto chartType=int(libwps::readU8(m_input));
444
60.3k
  if (m_state->m_chartType>=0)
445
37
  {
446
37
    chartType=m_state->m_chartType;
447
37
    m_state->m_chartType=-1;
448
37
  }
449
60.3k
  WKSChart::Serie::Type serieType=WKSChart::Serie::S_Bar;
450
60.3k
  switch (chartType)
451
60.3k
  {
452
48.6k
  case 0: // XY
453
48.6k
    serieType=WKSChart::Serie::S_Scatter;
454
48.6k
    break;
455
218
  case 1: // basic column
456
218
    break;
457
2.41k
  case 2: // pie
458
2.55k
  case 6: // column (like circle)
459
2.55k
    serieType=WKSChart::Serie::S_Circle;
460
2.55k
    break;
461
397
  case 3: // area stacked
462
397
    serieType=WKSChart::Serie::S_Area;
463
397
    chart->m_dataStacked=true;
464
397
    break;
465
219
  case 4: // line
466
219
    serieType=WKSChart::Serie::S_Line;
467
219
    break;
468
132
  case 5: // basic column
469
132
    chart->m_dataStacked=true;
470
132
    break;
471
92
  case 7: // min-max
472
92
    serieType=WKSChart::Serie::S_Stock;
473
92
    break;
474
123
  case 8: // basic bar
475
123
    chart->m_dataVertical=true;
476
123
    break;
477
151
  case 9: // bubble
478
151
    serieType=WKSChart::Serie::S_Bubble;
479
151
    break;
480
127
  case 10: // 3D-bar
481
366
  case 12: // 3D-bar tie together
482
366
    chart->m_is3D=true;
483
366
    break;
484
245
  case 11: // 3D-ribbon
485
245
    chart->m_is3D=true;
486
245
    serieType=WKSChart::Serie::S_Line;
487
245
    break;
488
277
  case 13:
489
277
    chart->m_is3D=true;
490
277
    serieType=WKSChart::Serie::S_Area;
491
277
    chart->m_dataStacked=true;
492
277
    break;
493
6.86k
  default:
494
6.86k
    WPS_DEBUG_MSG(("QuattroDosChart::readChart: unknown chart type\n"));
495
6.86k
    f << "###";
496
60.3k
  }
497
60.3k
  if (chartType) f << "type=" << chartType << ",";
498
499
60.3k
  chart->m_type=serieType;
500
60.3k
  if (chartType==3 || chartType==5) chart->m_dataStacked=true;
501
60.3k
  if (chartType==8) chart->m_dataVertical=true;
502
60.3k
  auto pointType = serieType==WKSChart::Serie::S_Scatter ? WKSChart::Serie::P_Automatic : WKSChart::Serie::P_None;
503
422k
  for (int i=0; i<6; ++i)
504
361k
  {
505
361k
    auto *serie=chart->getSerie(i, false);
506
361k
    if (!serie) continue;
507
263k
    serie->m_type=serieType;
508
263k
    serie->m_pointType=pointType;
509
263k
    serie->m_style.m_lineWidth=1;
510
263k
  }
511
60.3k
  auto val=int(libwps::readU8(m_input));
512
60.3k
  f << "grid=";
513
60.3k
  if (val&1)
514
6.98k
    f << "X";
515
53.3k
  else
516
53.3k
    chart->getAxis(0).m_showGrid=false;
517
60.3k
  if (val&2)
518
6.55k
    f << "Y";
519
53.7k
  else
520
53.7k
    chart->getAxis(1).m_showGrid=false;
521
60.3k
  if (val&0xFC)
522
7.32k
    f << "[##" << std::hex << (val&0xFC) << std::dec;
523
60.3k
  f << ",";
524
60.3k
  val=int(libwps::readU8(m_input));
525
  // bool useColor=val!=0; USEME
526
60.3k
  if (val==0)
527
49.0k
    f << "use[color]=no,";
528
11.2k
  else if (val!=0xFF)
529
10.3k
    f << "use[color]=###"<< val << ",";
530
60.3k
  f << "unkn=[";
531
422k
  for (int i=0; i<6; ++i)
532
361k
  {
533
361k
    val=int(libwps::readU8(m_input));
534
361k
    if (val)
535
63.1k
      f << val << ",";
536
298k
    else
537
298k
      f << "_,";
538
361k
  }
539
60.3k
  f << "],";
540
60.3k
  f << "align[serie]=[";
541
422k
  for (int i=0; i<6; ++i) // serie, label position, safe to ignore ?
542
361k
  {
543
361k
    val=int(libwps::readU8(m_input));
544
361k
    if (val<6)
545
316k
    {
546
316k
      char const *wh[]= {"center", "left", "above", "right", "below", "none"};
547
316k
      f << wh[val] << ",";
548
316k
    }
549
45.5k
    else
550
45.5k
      f << "##" << val << ",";
551
361k
  }
552
60.3k
  f << "],";
553
180k
  for (int i=0; i<2; ++i)
554
120k
  {
555
120k
    auto &axis=chart->getAxis(i);
556
120k
    f << "axis" << axisNames[i] << "=[";
557
120k
    val=int(libwps::readU8(m_input));
558
120k
    if (val==1)
559
1.29k
    {
560
1.29k
      f << "scale[manual],";
561
1.29k
      axis.m_automaticScaling=false;
562
1.29k
    }
563
119k
    else if (val)
564
19.3k
      f << "scale=##" << val << ",";
565
361k
    for (int j=0; j<2; ++j)
566
241k
    {
567
241k
      long actPos=m_input->tell();
568
241k
      long double value;
569
241k
      bool isNaN;
570
241k
      if (!libwps::readDouble8(m_input, value, isNaN))
571
32.8k
      {
572
32.8k
        m_input->seek(actPos+8, librevenge::RVNG_SEEK_SET);
573
32.8k
        f << "##value,";
574
32.8k
      }
575
208k
      else
576
208k
      {
577
208k
        if (value<0 || value>0)
578
57.0k
          f << (j==0 ? "low" : "high") << "=" << value << ",";
579
208k
        axis.m_scaling[j]=float(value);
580
208k
      }
581
241k
    }
582
120k
    f << "],";
583
120k
  }
584
60.3k
  m_input->seek(pos+49, librevenge::RVNG_SEEK_SET);
585
60.3k
  ascii().addPos(pos);
586
60.3k
  ascii().addNote(f.str().c_str());
587
60.3k
  pos=m_input->tell();
588
60.3k
  f.str("");
589
60.3k
  f << "ChartDef-names:";
590
663k
  for (int i = 0; i < 10; i++)
591
603k
  {
592
    // ok to find string before i < 6
593
    // checkme after
594
603k
    long actPos = m_input->tell();
595
603k
    int dataSz = i < 4 ? 40 : 20;
596
603k
    librevenge::RVNGString name("");
597
603k
    if (!m_mainParser.readPString(name,dataSz-1))
598
50.0k
      f << "##sSz,";
599
603k
    if (name.empty())
600
588k
    {
601
588k
      m_input->seek(actPos+dataSz, librevenge::RVNG_SEEK_SET);
602
588k
      continue;
603
588k
    }
604
14.6k
    if (i<2)
605
4.20k
    {
606
4.20k
      WPSEntry entry;
607
4.20k
      entry.setBegin(actPos+1);
608
4.20k
      entry.setEnd(m_input->tell());
609
4.20k
      auto *textZone=chart->getTextZone(i==0 ? WKSChart::TextZone::T_Title : WKSChart::TextZone::T_SubTitle, true);
610
4.20k
      textZone->m_contentType=WKSChart::TextZone::C_Text;
611
4.20k
      textZone->m_textEntryList.push_back(entry);
612
4.20k
      f << (i==0 ? "title" : "subTitle") << "=" << name.cstr() << ",";
613
4.20k
    }
614
10.4k
    else if (i<4)
615
3.46k
    {
616
3.46k
      chart->getAxis(i-2).m_title=name;
617
3.46k
      f << (i==2 ? "x" : "y") << "Title=" << name.cstr() << ",";
618
3.46k
    }
619
6.98k
    else
620
6.98k
    {
621
6.98k
      auto *serie=chart->getSerie(i-3, false);
622
6.98k
      if (serie)
623
3.56k
      {
624
3.56k
        serie->m_legendText=name;
625
3.56k
        hasLegend=true;
626
3.56k
      }
627
6.98k
      f << "serie" << i-3 <<  "=" << name.cstr() << ",";
628
6.98k
    }
629
14.6k
    m_input->seek(actPos+dataSz, librevenge::RVNG_SEEK_SET);
630
14.6k
  }
631
60.3k
  ascii().addPos(pos);
632
60.3k
  ascii().addNote(f.str().c_str());
633
634
60.3k
  pos=m_input->tell();
635
60.3k
  f.str("");
636
60.3k
  f << "ChartDef-B:";
637
180k
  for (int i=0; i<2; ++i) // axisX, axisY: format: USEME
638
120k
  {
639
120k
    val=int(libwps::readU8(m_input));
640
120k
    if (val) f << "fmt" << axisNames[i] << "=" << val << ",";
641
120k
  }
642
180k
  for (int i=0; i<2; ++i) // axisX, axisY, number of tick: USEME
643
120k
  {
644
120k
    val=int(libwps::readU8(m_input));
645
120k
    if (val) f << "num[tick" << axisNames[i] << "]=" << val << ",";
646
120k
  }
647
180k
  for (int i=0; i<2; ++i)
648
120k
  {
649
120k
    val=int(libwps::readU8(m_input));
650
120k
    if (val==1 || val==255)
651
2.33k
      f << "display[scale" << axisNames[i] << "]=no,";
652
118k
    else if (val)
653
17.4k
      f << "display[scale" << axisNames[i] << "]=##" << val << ",";
654
120k
  }
655
60.3k
  f << "pattern[series]=[";
656
60.3k
  int patternSeriesId[6];
657
60.3k
  for (auto &pId : patternSeriesId)
658
361k
  {
659
361k
    pId=int(libwps::readU16(m_input));
660
361k
    if (pId==0)
661
256k
      f << "_,";
662
105k
    else
663
105k
      f << pId << ",";
664
361k
  }
665
60.3k
  f << "],";
666
60.3k
  f << "colors2=[";
667
241k
  for (int i=0; i<3; ++i)
668
180k
    f << int(libwps::readU16(m_input)) << ",";
669
60.3k
  f << "],";
670
60.3k
  f << "color[series]=[";
671
60.3k
  WPSColor colorSeriesId[6];
672
60.3k
  for (auto &color : colorSeriesId)
673
361k
  {
674
361k
    auto cId=int(libwps::readU16(m_input));
675
361k
    if (m_mainParser.getColor(cId, color))
676
274k
      f << color << ",";
677
87.3k
    else
678
87.3k
    {
679
87.3k
      color=WPSColor(128,128,128);
680
87.3k
      f << "###" << cId << ",";
681
87.3k
    }
682
361k
  }
683
60.3k
  f << "],";
684
60.3k
  f << "unkn=[";
685
301k
  for (int i=0; i<4; ++i) // 3 colors + big number
686
241k
    f << int(libwps::readU16(m_input)) << ",";
687
60.3k
  f << "],";
688
60.3k
  auto cId=int(libwps::readU8(m_input));
689
60.3k
  WPSColor backColor(WPSColor::white());
690
60.3k
  if (m_mainParser.getColor(cId, backColor))
691
54.6k
  {
692
54.6k
    chart->m_style.setSurfaceColor(backColor);
693
54.6k
    f << "col[background]=" << backColor << ",";
694
54.6k
  }
695
5.62k
  else
696
5.62k
    f << "col[background]=###" << val << ",";
697
60.3k
  val=int(libwps::readU8(m_input));
698
60.3k
  if (val) f << "f0=" << val << ",";
699
60.3k
  m_input->seek(pos+46, librevenge::RVNG_SEEK_SET);
700
60.3k
  ascii().addPos(pos);
701
60.3k
  ascii().addNote(f.str().c_str());
702
703
60.3k
  pos=m_input->tell();
704
60.3k
  f.str("");
705
60.3k
  f << "ChartDef-fonts:";
706
422k
  for (int i = 0; i < 6; ++i)   // the series font?
707
361k
  {
708
361k
    WPSFont font;
709
361k
    libwps_tools_win::Font::Type fFontType;
710
361k
    if (!m_mainParser.readFont(font, fFontType))
711
0
    {
712
0
      f << "###";
713
0
      m_input->seek(pos+(i+1)*8, librevenge::RVNG_SEEK_SET);
714
0
    }
715
361k
    else
716
361k
    {
717
361k
      auto *serie=chart->getSerie(i, false);
718
361k
      if (serie)
719
263k
        serie->m_font=font;
720
361k
    }
721
361k
    f << "[" << font << "],";
722
361k
  }
723
60.3k
  ascii().addPos(pos);
724
60.3k
  ascii().addNote(f.str().c_str());
725
726
60.3k
  pos=m_input->tell();
727
60.3k
  f.str("");
728
60.3k
  f << "ChartDef-C:";
729
60.3k
  val=int(libwps::readU16(m_input));
730
60.3k
  if (val) f << "f0=" << std::hex << val << std::dec << ",";
731
422k
  for (int i=0; i<6; ++i)
732
361k
  {
733
361k
    f << "serie" << i << "=[";
734
361k
    auto *serie = chart->getSerie(i, false);
735
361k
    val=int(libwps::readU8(m_input));
736
361k
    if (val==1 || val==255)
737
11.6k
    {
738
11.6k
      if (serie) serie->m_useSecondaryY=true;
739
11.6k
      f << "secondary,";
740
11.6k
    }
741
350k
    else if (val)
742
48.2k
      f << "coordinate=###" << val << ",";
743
361k
    val=int(libwps::readU8(m_input));
744
361k
    if (val==1)
745
2.36k
    {
746
2.36k
      if (serie && serie->m_type==WKSChart::Serie::S_Line)
747
197
        serie->m_type=WKSChart::Serie::S_Bar;
748
2.36k
      f << "bar,";
749
2.36k
    }
750
359k
    else if (val==2)
751
22.6k
    {
752
22.6k
      if (serie && serie->m_type==WKSChart::Serie::S_Bar)
753
1.80k
        serie->m_type=WKSChart::Serie::S_Line;
754
22.6k
      f << "line,";
755
22.6k
    }
756
336k
    else if (val)
757
53.0k
      f << "type=##" << val << ",";
758
361k
    f << "],";
759
361k
  }
760
60.3k
  val=int(libwps::readU8(m_input));
761
60.3k
  auto &axisYSecond=chart->getAxis(2);
762
60.3k
  if (val==1)
763
643
  {
764
643
    f << "scaleY2[manual],";
765
643
    axisYSecond.m_automaticScaling=false;
766
643
  }
767
59.6k
  else if (val)
768
10.4k
    f << "scaleY2=##" << val << ",";
769
180k
  for (int i=0; i<2; ++i)
770
120k
  {
771
120k
    long actPos=m_input->tell();
772
120k
    long double value;
773
120k
    bool isNaN;
774
120k
    if (!libwps::readDouble8(m_input, value, isNaN))
775
19.4k
    {
776
19.4k
      m_input->seek(actPos+8, librevenge::RVNG_SEEK_SET);
777
19.4k
      f << "##value,";
778
19.4k
    }
779
101k
    else
780
101k
    {
781
101k
      if (value<0 || value>0)
782
25.2k
        f << (i==0 ? "lowY2" : "highY2") << "=" << value << ",";
783
101k
      axisYSecond.m_scaling[i]=float(value);
784
101k
    }
785
120k
  }
786
60.3k
  val=int(libwps::readU8(m_input));
787
60.3k
  if (val)
788
13.8k
    f << "fmtY2=" << std::hex << val << std::dec << ",";
789
60.3k
  val=int(libwps::readU8(m_input));
790
60.3k
  if (val)
791
8.34k
    f << "f1=" << val << ",";
792
60.3k
  m_input->seek(pos+33, librevenge::RVNG_SEEK_SET);
793
60.3k
  ascii().addPos(pos);
794
60.3k
  ascii().addNote(f.str().c_str());
795
796
60.3k
  pos=m_input->tell();
797
60.3k
  f.str("");
798
60.3k
  f << "ChartDef-name2:";
799
60.3k
  librevenge::RVNGString name("");
800
60.3k
  if (!m_mainParser.readPString(name,39))
801
7.06k
    f << "##sSz,";
802
53.2k
  else if (!name.empty()) f << name.cstr() << ",";
803
60.3k
  m_input->seek(pos+40, librevenge::RVNG_SEEK_SET);
804
60.3k
  ascii().addPos(pos);
805
60.3k
  ascii().addNote(f.str().c_str());
806
807
60.3k
  pos=m_input->tell();
808
60.3k
  f.str("");
809
60.3k
  f << "ChartDef-D:";
810
60.3k
  val=int(libwps::readU8(m_input));
811
60.3k
  if (val==0)
812
46.9k
    f << "text[pos]=bottom,";
813
13.3k
  else if (val==1)
814
265
    f << "text[pos]=right,";
815
13.1k
  else if (val==2)
816
4.37k
    f << "text[pos]=none,";
817
8.72k
  else
818
8.72k
    f << "text[pos]=##" << val << ",";
819
241k
  for (int i=0; i<3; ++i) // USEME
820
180k
  {
821
180k
    val=int(libwps::readU8(m_input));
822
180k
    if (val==6) continue;
823
180k
    f << (i==0 ? "title" : i==1 ? "legend" : "graph") << "[ouline]=";
824
180k
    if (val>=0 && val<=7)
825
158k
    {
826
158k
      char const *wh[]= {"box", "doubleLine", "thickLine", "shadow", "3d", "rndRect", "none","sculpted"};
827
158k
      f << wh[val] << ",";
828
158k
    }
829
22.2k
    else
830
22.2k
      f << "###" << val << ",";
831
180k
  }
832
241k
  for (int i=0; i<3; ++i)
833
180k
  {
834
180k
    auto &axis = chart->getAxis(i);
835
180k
    f << "axis" << (i==0 ? "X" : i==1 ? "Y" : "Y2") << "=[";
836
180k
    val=int(libwps::readU16(m_input));
837
180k
    if (val) f << "f0=" << std::hex << val << std::dec << ",";
838
180k
    val=int(libwps::readU8(m_input));
839
180k
    if (val==0)
840
150k
      axis.m_type= WKSChart::Axis::A_Numeric;
841
30.6k
    else if (val==1 || val==255)
842
5.49k
    {
843
5.49k
      f << "log,"; // 0:normal
844
5.49k
      axis.m_type= WKSChart::Axis::A_Logarithmic;
845
5.49k
    }
846
25.1k
    else f << "scale=###" << val << ",";
847
180k
    long actPos=m_input->tell();
848
180k
    long double value;
849
180k
    bool isNaN;
850
180k
    if (!libwps::readDouble8(m_input, value, isNaN))
851
29.9k
    {
852
29.9k
      m_input->seek(actPos+8, librevenge::RVNG_SEEK_SET);
853
29.9k
      f << "##value,";
854
29.9k
    }
855
150k
    else if (value<0 || value>0) // USEME
856
32.9k
      f << "increment=" << value << ",";
857
180k
    f << "],";
858
180k
  }
859
60.3k
  val=int(libwps::readU8(m_input));
860
60.3k
  if (val && val<=7)
861
2.22k
  {
862
    // USEME
863
2.22k
    char const *wh[]= {"solid", "dotted", "center-line", "dashed", "heavy solid",
864
2.22k
                       "heavy dotted", "heavy centered", "heavy dashed"
865
2.22k
                      };
866
2.22k
    f << "style[gridline]=" << wh[val] << ",";
867
2.22k
  }
868
58.0k
  else if (val)
869
6.26k
    f << "##style[gridline]=" << val << ",";
870
60.3k
  cId=int(libwps::readU8(m_input));
871
60.3k
  WPSColor gridColor(WPSColor::black());
872
60.3k
  if (m_mainParser.getColor(cId, gridColor))
873
53.7k
  {
874
53.7k
    chart->setGridColor(gridColor);
875
53.7k
    f << "color[line/grid]=" << gridColor << ",";
876
53.7k
  }
877
6.54k
  else
878
6.54k
    f << "##color[line/grid]=" << cId << ",";
879
60.3k
  ascii().addDelimiter(m_input->tell(),'|');
880
60.3k
  m_input->seek(pos+49, librevenge::RVNG_SEEK_SET);
881
60.3k
  ascii().addPos(pos);
882
60.3k
  ascii().addNote(f.str().c_str());
883
884
  // time to update the serie pattern
885
60.3k
  if (hasLegend)
886
3.03k
  {
887
3.03k
    chart->getLegend().m_show=true;
888
3.03k
    chart->getLegend().m_autoPosition=true;
889
3.03k
    chart->getLegend().m_relativePosition=WPSBorder::RightBit;
890
3.03k
  }
891
422k
  for (int i=0; i<6; ++i)
892
361k
  {
893
361k
    auto *serie=chart->getSerie(i, false);
894
361k
    if (!serie) continue;
895
263k
    WPSGraphicStyle::Pattern pattern;
896
263k
    if (!m_state->getPattern(patternSeriesId[i],pattern))
897
51.2k
    {
898
51.2k
      WPS_DEBUG_MSG(("QuattroDosChart::readChart: oops, can not find pattern %d\n", i));
899
51.2k
      serie->setPrimaryColor(colorSeriesId[i]);
900
51.2k
      serie->setSecondaryColor(gridColor);
901
51.2k
      continue;
902
51.2k
    }
903
212k
    pattern.m_colors[0]=colorSeriesId[i];
904
212k
    pattern.m_colors[1]=backColor;
905
212k
    serie->setPrimaryPattern(pattern);
906
212k
    serie->setSecondaryColor(gridColor);
907
212k
  }
908
909
60.3k
  pos=m_input->tell();
910
60.3k
  f.str("");
911
60.3k
  f << "ChartDef-E:";
912
60.3k
  val=int(libwps::readU8(m_input));
913
60.3k
  if (val) f << "tickX[alternate],";
914
60.3k
  val=int(libwps::readU16(m_input));
915
60.3k
  if (val) f << "f0=" << std::hex << val << std::dec << ",";
916
60.3k
  val=int(libwps::readU8(m_input));
917
60.3k
  if (val) f << "use[depth],";
918
60.3k
  ascii().addDelimiter(m_input->tell(),'|');
919
60.3k
  m_input->seek(24, librevenge::RVNG_SEEK_CUR);
920
60.3k
  ascii().addDelimiter(m_input->tell(),'|');
921
60.3k
  val=int(libwps::read16(m_input));
922
60.3k
  if (val) f << "bar[width]=" << val << "%,";
923
180k
  for (int i=0; i<2; ++i)   // 0: local graph position, 1: title/sub title frames
924
120k
  {
925
120k
    int dim[4];
926
482k
    for (auto &d : dim) d = int(libwps::read16(m_input));
927
120k
    if (dim[0]==-1)
928
1.30k
      continue;
929
119k
    WPSBox2i box(Vec2i(dim[0],dim[1]), Vec2i(dim[2],dim[3]));
930
119k
    if (box==WPSBox2i()) continue;
931
66.3k
    f << (i==0 ? "grid" : "title") << "[pos]=" << box << ",";
932
66.3k
    if (i==0) // the size seems to be the windows size 1024*???
933
32.9k
      chart->m_plotAreaPosition=
934
32.9k
          WPSBox2f(Vec2f(float(dim[0])/1024.f,float(dim[1])/700.f),Vec2f(float(dim[2])/1024.f,float(dim[3])/700.f));
935
66.3k
  }
936
60.3k
  f << "unk=[";
937
603k
  for (int i=0; i<9; ++i)
938
542k
  {
939
542k
    val=int(libwps::readU8(m_input));
940
542k
    if (val==255)
941
11.5k
      f << "*,";
942
531k
    else if (val)
943
75.4k
      f << val << ",";
944
455k
    else
945
455k
      f << "_,";
946
542k
  }
947
60.3k
  f << "],";
948
60.3k
  WPSColor color;
949
60.3k
  cId=int(libwps::readU8(m_input));
950
60.3k
  if (m_mainParser.getColor(cId, color))
951
53.1k
  {
952
53.1k
    chart->m_wallStyle.setSurfaceColor(color);
953
53.1k
    f << "col[fill]=" << color << ",";
954
53.1k
  }
955
7.18k
  else
956
7.18k
    f << "col[fill]=###" << color << ",";
957
60.3k
  val=int(libwps::readU8(m_input));
958
60.3k
  if (val) f << "f1=" << std::hex << val << std::dec << ",";
959
60.3k
  for (auto &range : chart->m_ranges)
960
120k
  {
961
120k
    int dim[3];
962
395k
    for (int d=0; d<(vers>=2 ? 3 : 2); ++d) dim[d] = int(libwps::read16(m_input));
963
120k
    if (dim[0]==-1)
964
664
      continue;
965
119k
    chart->m_spreadsheetId=vers<2 ? 0 : dim[2];
966
119k
    range = WKSChart::Position(Vec2i(dim[0],dim[1]));
967
119k
    range.m_sheetId=chart->m_spreadsheetId;
968
119k
  }
969
60.3k
  if (chart->m_ranges[0].valid(chart->m_ranges[1]))
970
48.5k
  {
971
48.5k
    f << "position=" << chart->m_ranges[0] << ":" << chart->m_ranges[1] << ",";
972
48.5k
    if (chart->m_ranges[1].m_pos[0]>255 || chart->m_ranges[1].m_pos[1]>65535)
973
1.85k
    {
974
1.85k
      f << "###";
975
1.85k
      WPS_DEBUG_MSG(("QuattroDosChart::readChart: oops, the maximum position seems bad\n"));
976
1.85k
    }
977
46.6k
    else if (chart->m_ranges[0].m_sheetId!=chart->m_ranges[1].m_sheetId)
978
2.71k
    {
979
2.71k
      f << "###";
980
2.71k
      WPS_DEBUG_MSG(("QuattroDosChart::readChart: oops, the position is on different sheet\n"));
981
2.71k
    }
982
43.9k
    else
983
43.9k
    {
984
43.9k
      m_state->m_idToChartMap.insert(std::multimap<int, std::shared_ptr<QuattroDosChartInternal::Chart> >::value_type(chart->m_spreadsheetId, chart));
985
43.9k
    }
986
48.5k
  }
987
60.3k
  if (sz != normalSz) ascii().addDelimiter(m_input->tell(),'#');
988
60.3k
  ascii().addPos(pos);
989
60.3k
  ascii().addNote(f.str().c_str());
990
991
60.3k
  return true;
992
60.3k
}
993
994
/* vim:set shiftwidth=4 softtabstop=4 noexpandtab: */