Coverage Report

Created: 2026-09-06 07:18

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/libetonyek/src/lib/KEYCollector.cpp
Line
Count
Source
1
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2
/*
3
 * This file is part of the libetonyek project.
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
10
#include "KEYCollector.h"
11
12
#include <algorithm>
13
#include <functional>
14
#include <memory>
15
#include <set>
16
17
#include <glm/glm.hpp>
18
19
#include "libetonyek_utils.h"
20
#include "IWORKDocumentInterface.h"
21
#include "IWORKOutputElements.h"
22
#include "IWORKPath.h"
23
#include "IWORKProperties.h"
24
#include "IWORKRecorder.h"
25
#include "IWORKTable.h"
26
#include "IWORKText.h"
27
28
namespace libetonyek
29
{
30
31
using librevenge::RVNGPropertyList;
32
33
using namespace std::placeholders;
34
35
KEYCollector::KEYCollector(IWORKDocumentInterface *const document)
36
2.43k
  : IWORKCollector(document)
37
2.43k
  , m_inSlides(false)
38
2.43k
  , m_size()
39
2.43k
  , m_currentSlide()
40
2.43k
  , m_notes()
41
2.43k
  , m_stickyNotes()
42
2.43k
  , m_pageOpened(false)
43
2.43k
  , m_layerOpened(false)
44
2.43k
  , m_layerCount(0)
45
2.43k
{
46
2.43k
  assert(!m_inSlides);
47
2.43k
}
48
49
KEYCollector::~KEYCollector()
50
2.43k
{
51
2.43k
  assert(!m_inSlides);
52
2.43k
}
53
54
void KEYCollector::collectPresentationSize(const IWORKSize &size)
55
2.15k
{
56
2.15k
  m_size = size;
57
2.15k
}
58
59
KEYLayerPtr_t KEYCollector::collectLayer()
60
5.94k
{
61
5.94k
  assert(m_layerOpened);
62
63
5.94k
  const std::shared_ptr<IWORKRecorder> recorder(m_recorder);
64
5.94k
  m_recorder.reset();
65
5.94k
  if (recorder)
66
0
    recorder->replay(*this);
67
68
5.94k
  KEYLayerPtr_t layer(new KEYLayer());
69
70
5.94k
  layer->m_outputId = getOutputManager().save();
71
72
5.94k
  return layer;
73
5.94k
}
74
75
void KEYCollector::insertLayer(const KEYLayerPtr_t &layer)
76
5.94k
{
77
5.94k
  assert(!m_layerOpened);
78
79
5.94k
  if (bool(layer))
80
5.94k
  {
81
5.94k
    if (m_currentSlide)
82
5.94k
    {
83
84
5.94k
      ++m_layerCount;
85
86
5.94k
      librevenge::RVNGPropertyList props;
87
5.94k
      props.insert("svg:id", m_layerCount);
88
89
5.94k
      m_currentSlide->m_content.addStartLayer(props);
90
5.94k
      if (layer->m_outputId)
91
5.94k
        m_currentSlide->m_content.append(getOutputManager().get(get(layer->m_outputId)));
92
5.94k
      m_currentSlide->m_content.addEndLayer();
93
5.94k
    }
94
5.94k
  }
95
0
  else
96
0
  {
97
0
    ETONYEK_DEBUG_MSG(("no layer\n"));
98
0
  }
99
5.94k
}
100
101
KEYSlidePtr_t KEYCollector::collectSlide()
102
5.94k
{
103
5.94k
  assert(m_pageOpened);
104
105
5.94k
  if (m_currentSlide)
106
5.94k
  {
107
5.94k
    if (!m_notes.empty())
108
49
    {
109
49
      m_currentSlide->m_content.addStartNotes(librevenge::RVNGPropertyList());
110
49
      m_currentSlide->m_content.append(m_notes);
111
49
      m_currentSlide->m_content.addEndNotes();
112
49
    }
113
5.94k
    m_currentSlide->m_content.append(m_stickyNotes);
114
5.94k
    return m_currentSlide;
115
5.94k
  }
116
117
0
  return KEYSlidePtr_t();
118
5.94k
}
119
120
void KEYCollector::insertSlide(const KEYSlidePtr_t &slide, bool isMaster, const boost::optional<std::string> &pageName)
121
4.75k
{
122
4.75k
  if (!slide)
123
0
  {
124
0
    ETONYEK_DEBUG_MSG(("KEYCollector::insertSlide: called with no slide\n"));
125
0
    return;
126
0
  }
127
4.75k
  librevenge::RVNGPropertyList props;
128
4.75k
  props.insert("svg:width", pt2in(m_size.m_width));
129
4.75k
  props.insert("svg:height", pt2in(m_size.m_height));
130
4.75k
  if (pageName) props.insert("librevenge:master-page-name", get(pageName).c_str());
131
4.75k
  if (bool(slide->m_style) && slide->m_style->has<property::Fill>())
132
1.32k
    writeFill(slide->m_style->get<property::Fill>(), props);
133
134
4.75k
  if (isMaster)
135
743
    m_document->startMasterSlide(props);
136
4.01k
  else
137
4.01k
    m_document->startSlide(props);
138
4.75k
  slide->m_content.write(m_document);
139
4.75k
  if (isMaster)
140
743
    m_document->endMasterSlide();
141
4.01k
  else
142
4.01k
    m_document->endSlide();
143
4.75k
}
144
145
KEYPlaceholderPtr_t KEYCollector::collectTextPlaceholder(const IWORKStylePtr_t &style, const bool title, const boost::optional<unsigned> &resizeFlags)
146
8.65k
{
147
8.65k
  KEYPlaceholderPtr_t placeholder(new KEYPlaceholder());
148
8.65k
  placeholder->m_title = title;
149
8.65k
  placeholder->m_resizeFlags = resizeFlags;
150
8.65k
  placeholder->m_style = style;
151
8.65k
  if (bool(placeholder->m_style))
152
8.65k
  {
153
8.65k
    m_styleStack.push();
154
8.65k
    m_styleStack.set(placeholder->m_style);
155
156
8.65k
    if (m_styleStack.has<property::Geometry>())
157
7.89k
      placeholder->m_geometry = m_styleStack.get<property::Geometry>();
158
159
8.65k
    m_styleStack.pop();
160
8.65k
  }
161
8.65k
  placeholder->m_text = m_currentText;
162
8.65k
  m_currentText.reset();
163
164
8.65k
  return placeholder;
165
8.65k
}
166
167
void KEYCollector::insertTextPlaceholder(const KEYPlaceholderPtr_t &placeholder)
168
8.65k
{
169
8.65k
  if (bool(placeholder))
170
8.65k
  {
171
8.65k
    glm::dmat3 trafo(1);
172
8.65k
    if (bool(placeholder->m_geometry))
173
7.89k
      trafo = makeTransformation(*placeholder->m_geometry);
174
8.65k
    trafo *= m_levelStack.top().m_trafo;
175
176
8.65k
    if (bool(placeholder) && bool(placeholder->m_style) && bool(placeholder->m_text))
177
2.48k
    {
178
2.48k
      librevenge::RVNGPropertyList props;
179
2.48k
      fillLayoutProps(placeholder->m_style, props);
180
2.48k
      fillTextAutoSizeProps(placeholder->m_resizeFlags,placeholder->m_geometry,props);
181
2.48k
      drawTextBox(placeholder->m_text, trafo, placeholder->m_geometry, props);
182
2.48k
    }
183
8.65k
  }
184
0
  else
185
0
  {
186
0
    ETONYEK_DEBUG_MSG(("KEYCollector::insertTextPlaceholder: no text placeholder found\n"));
187
0
  }
188
8.65k
}
189
190
void KEYCollector::collectNote()
191
2.97k
{
192
2.97k
  if (bool(m_currentText))
193
2.97k
  {
194
2.97k
    m_currentText->draw(m_notes);
195
2.97k
    m_currentText.reset();
196
2.97k
  }
197
2.97k
}
198
199
void KEYCollector::collectStickyNote()
200
2.39k
{
201
2.39k
  assert(!m_levelStack.empty());
202
203
2.39k
  librevenge::RVNGPropertyList props;
204
205
2.39k
  if (bool(m_levelStack.top().m_geometry))
206
0
  {
207
0
    props.insert("svg:x", pt2in(m_levelStack.top().m_geometry->m_position.m_x));
208
0
    props.insert("svg:y", pt2in(m_levelStack.top().m_geometry->m_position.m_y));
209
0
    props.insert("svg:width", pt2in(m_levelStack.top().m_geometry->m_naturalSize.m_width));
210
0
    props.insert("svg:height", pt2in(m_levelStack.top().m_geometry->m_naturalSize.m_height));
211
0
  }
212
213
2.39k
  if (bool(m_currentText))
214
2.28k
  {
215
2.28k
    m_stickyNotes.addOpenComment(props);
216
2.28k
    m_currentText->draw(m_stickyNotes);
217
2.28k
    m_currentText.reset();
218
2.28k
    m_stickyNotes.addCloseComment();
219
2.28k
  }
220
221
2.39k
  m_levelStack.top().m_geometry.reset();
222
2.39k
  m_currentPath.reset();
223
2.39k
}
224
225
void KEYCollector::setSlideStyle(const IWORKStylePtr_t &style)
226
6.35k
{
227
6.35k
  if (m_pageOpened)
228
6.35k
  {
229
6.35k
    assert(m_currentSlide);
230
6.35k
    m_currentSlide->m_style = style;
231
6.35k
  }
232
6.35k
}
233
234
void KEYCollector::startDocument()
235
2.19k
{
236
2.19k
  IWORKCollector::startDocument(librevenge::RVNGPropertyList());
237
2.19k
}
238
239
void KEYCollector::sendSlides(const std::deque<KEYSlidePtr_t> &slides)
240
1.57k
{
241
1.57k
  RVNGPropertyList metadata;
242
1.57k
  fillMetadata(metadata);
243
1.57k
  m_document->setDocumentMetaData(metadata);
244
245
1.57k
  std::map<KEYSlide const *, std::string> masterToNameMap;
246
1.57k
  std::set<std::string> nameSet;
247
1.57k
  unsigned nameId=0;
248
1.57k
  for (auto slide : slides)
249
4.01k
  {
250
4.01k
    if (!slide) continue;
251
4.01k
    boost::optional<std::string> name;
252
4.01k
    if (slide->m_masterSlide)
253
1.71k
    {
254
1.71k
      if (masterToNameMap.find(slide->m_masterSlide.get())==masterToNameMap.end())
255
743
      {
256
743
        if (slide->m_masterSlide->m_name && nameSet.find(get(slide->m_masterSlide->m_name))==nameSet.end())
257
0
          name=get(slide->m_masterSlide->m_name);
258
743
        else
259
743
        {
260
          // ok try to find an unused name
261
743
          do
262
743
          {
263
743
            std::stringstream s;
264
743
            if (slide->m_masterSlide->m_name)
265
0
              s << get(slide->m_masterSlide->m_name) << nameId++;
266
743
            else
267
743
              s << "MasterSlide" << nameId++;
268
743
            name=s.str();
269
743
          }
270
743
          while (nameSet.find(get(name))!=nameSet.end());
271
743
        }
272
743
        nameSet.insert(get(name));
273
743
        masterToNameMap[slide->m_masterSlide.get()]=get(name);
274
743
        insertSlide(slide->m_masterSlide, true, name);
275
743
      }
276
968
      else
277
968
        name=masterToNameMap.find(slide->m_masterSlide.get())->second;
278
1.71k
    }
279
4.01k
    insertSlide(slide, false, name);
280
4.01k
  }
281
282
1.57k
}
283
284
void KEYCollector::endDocument()
285
1.57k
{
286
1.57k
  IWORKCollector::endDocument();
287
1.57k
}
288
289
void KEYCollector::startSlides()
290
2.19k
{
291
2.19k
  m_inSlides = true;
292
2.19k
}
293
294
void KEYCollector::endSlides()
295
1.57k
{
296
1.57k
  m_inSlides = false;
297
1.57k
}
298
299
void KEYCollector::startThemes()
300
0
{
301
0
}
302
303
void KEYCollector::endThemes()
304
0
{
305
0
}
306
307
void KEYCollector::startPage()
308
6.39k
{
309
6.39k
  assert(!m_pageOpened);
310
6.39k
  assert(!m_layerOpened);
311
6.39k
  assert(m_notes.empty());
312
6.39k
  assert(m_stickyNotes.empty());
313
314
6.39k
  startLevel();
315
316
6.39k
  assert(m_inSlides && !m_currentSlide);
317
6.39k
  m_currentSlide = std::make_shared<KEYSlide>();
318
6.39k
  m_pageOpened = true;
319
6.39k
}
320
321
void KEYCollector::endPage()
322
5.94k
{
323
5.94k
  assert(m_pageOpened);
324
325
5.94k
  endLevel();
326
327
5.94k
  m_notes.clear();
328
5.94k
  m_stickyNotes.clear();
329
330
5.94k
  m_currentSlide.reset();
331
5.94k
  m_pageOpened = false;
332
5.94k
}
333
334
void KEYCollector::startLayer()
335
6.39k
{
336
6.39k
  assert(m_pageOpened);
337
6.39k
  assert(!m_layerOpened);
338
339
6.39k
  getOutputManager().push();
340
6.39k
  m_layerOpened = true;
341
342
6.39k
  startLevel();
343
6.39k
}
344
345
void KEYCollector::endLayer()
346
5.94k
{
347
5.94k
  assert(m_pageOpened);
348
5.94k
  assert(m_layerOpened);
349
350
5.94k
  endLevel();
351
5.94k
  getOutputManager().pop();
352
353
5.94k
  m_layerOpened = false;
354
5.94k
}
355
356
void KEYCollector::drawTable()
357
142
{
358
142
  assert(bool(m_currentTable));
359
142
  assert(!m_levelStack.empty());
360
361
142
  librevenge::RVNGPropertyList tableProps;
362
363
142
  tableProps.insert("table:align", "center");
364
365
142
  glm::dvec3 vec = m_levelStack.top().m_trafo * glm::dvec3(0, 0, 1);
366
367
142
  tableProps.insert("svg:x", pt2in(vec[0]));
368
142
  tableProps.insert("svg:y", pt2in(vec[1]));
369
370
142
  const IWORKGeometryPtr_t geometry(m_levelStack.top().m_geometry);
371
142
  if (geometry)
372
141
  {
373
141
    double w = geometry->m_naturalSize.m_width;
374
141
    double h = geometry->m_naturalSize.m_height;
375
376
141
    vec = m_levelStack.top().m_trafo * glm::dvec3(w, h, 0);
377
378
141
    tableProps.insert("svg:width", pt2in(vec[0]));
379
141
    tableProps.insert("svg:height", pt2in(vec[1]));
380
141
  }
381
382
142
  if (bool(m_currentTable->getStyle()))
383
39
    fillWrapProps(m_currentTable->getStyle(), tableProps, m_currentTable->getOrder());
384
385
142
  m_currentTable->draw(tableProps, m_outputManager.getCurrent(), true);
386
142
}
387
388
void KEYCollector::drawMedia(
389
  const double x, const double y,
390
  const librevenge::RVNGPropertyList &data)
391
61
{
392
61
  librevenge::RVNGPropertyList style(data);
393
61
  if (style["office:binary-data"])
394
61
    style.remove("office:binary-data");
395
61
  getOutputManager().getCurrent().addSetStyle(style);
396
397
61
  librevenge::RVNGPropertyList props(data);
398
61
  props.insert("svg:x", pt2in(x));
399
61
  props.insert("svg:y", pt2in(y));
400
61
  getOutputManager().getCurrent().addDrawGraphicObject(props);
401
61
}
402
403
void KEYCollector::fillShapeProperties(librevenge::RVNGPropertyList &)
404
3.80k
{
405
3.80k
}
406
407
void KEYCollector::drawTextBox(const IWORKTextPtr_t &text, const glm::dmat3 &trafo, const IWORKGeometryPtr_t &boundingBox, const librevenge::RVNGPropertyList &style)
408
3.27k
{
409
3.27k
  if (!bool(text) || text->empty())
410
0
    return;
411
412
3.27k
  librevenge::RVNGPropertyList props(style);
413
3.27k
  if (!style["draw:fill"]) props.insert("draw:fill", "none");
414
3.27k
  if (!style["draw:stroke"]) props.insert("draw:stroke", "none");
415
416
3.27k
  glm::dvec3 vec = trafo * glm::dvec3(0, 0, 1);
417
418
3.27k
  props.insert("svg:x", pt2in(vec[0]));
419
3.27k
  props.insert("svg:y", pt2in(vec[1]));
420
421
3.27k
  if (bool(boundingBox))
422
2.54k
  {
423
2.54k
    double w = boundingBox->m_naturalSize.m_width;
424
2.54k
    double h = boundingBox->m_naturalSize.m_height;
425
2.54k
    vec = trafo * glm::dvec3(w, h, 0);
426
2.54k
    if (vec[0]>0)
427
2.16k
      props.insert("svg:width", pt2in(vec[0]));
428
2.54k
    if (vec[1]>0)
429
2.21k
      props.insert("svg:height", pt2in(vec[1]));
430
2.54k
  }
431
432
3.27k
  IWORKOutputElements &elements = m_outputManager.getCurrent();
433
3.27k
  elements.addStartTextObject(props);
434
3.27k
  text->draw(elements);
435
3.27k
  elements.addEndTextObject();
436
3.27k
}
437
438
}
439
440
/* vim:set shiftwidth=2 softtabstop=2 expandtab: */