<commit_before>/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/*
 * This file is part of the libkeynote project.
 *
 * This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
 */

#include <algorithm>
#include <cassert>

#include <boost/bind.hpp>

#include "libkeynote_utils.h"
#include "KNCollectorBase.h"
#include "KNDefaults.h"
#include "KNDictionary.h"
#include "KNShape.h"
#include "KNStyles.h"
#include "KNText.h"

using boost::optional;

namespace libkeynote
{

namespace
{

/** Return value, either directly or by its ID.
  *
  * If the input is not a reference, save value to @c map for future use.
  *
  * @arg[in] id the value's ID
  * @arg[in] value a possible value
  * @arg[in] ref indicator whether the input should be handled as a
  *   value or a reference
  * @arg[in] map the map to use to retrieve the value by ID or to save a
  *   new value
  * @return the value
  */
template<class T>
T getValue(const optional<ID_t> &id, const T &value, const bool ref, boost::unordered_map<ID_t, T> &map)
{
  T retval;

  if (ref)
  {
    assert(id);

    const typename boost::unordered_map<ID_t, T>::const_iterator it = map.find(get(id));
    if (map.end() != it)
    {
      retval = it->second;
    }
    else
    {
      KN_DEBUG_MSG(("item with ID %s does not exist\n", get(id).c_str()));
    }
  }
  else
  {
    retval = value;
    if (id)
      map[get(id)] = value;
  }

  return retval;
}

/** Return value by its ID.
  *
  * @arg[in] id the value's ID
  * @arg[in] map the map to use to retrieve the value
  * @return the value
  */
template<class T>
T getValue(const optional<ID_t> &id, boost::unordered_map<ID_t, T> &map)
{
  return getValue(id, T(), true, map);
}

}

KNCollectorBase::Level::Level()
  : geometry()
  , graphicStyle()
  , placeholderStyle()
{
}

KNCollectorBase::KNCollectorBase(KNDictionary &dict, const KNDefaults &defaults)
  : m_dict(dict)
  , m_defaults(defaults)
  , m_currentLayer()
  , m_levelStack()
  , m_objectsStack()
  , m_currentPath()
  , m_currentText()
  , m_currentStylesheet(new KNStylesheet())
  , m_newStyles()
  , m_collecting(false)
  , m_layerOpened(false)
  , m_groupLevel(0)
{
}

KNCollectorBase::~KNCollectorBase()
{
  assert(!m_collecting);
  assert(m_objectsStack.empty());
}

void KNCollectorBase::collectCellStyle(const boost::optional<ID_t> &id, const KNCellStylePtr_t &style, const bool ref, const bool anonymous)
{
  // TODO: implement me
  (void) id;
  (void) style;
  (void) ref;
  (void) anonymous;
}

void KNCollectorBase::collectCharacterStyle(const optional<ID_t> &id, const KNCharacterStylePtr_t &style, const bool ref, const bool anonymous)
{
  if (m_collecting)
  {
    assert(m_currentStylesheet);

    const KNCharacterStylePtr_t charStyle = getValue(id, style, ref, m_dict.characterStyles);
    if (bool(charStyle))
    {
      if (id && !anonymous)
        m_currentStylesheet->characterStyles[get(id)] = charStyle;
      if (!ref)
        m_newStyles.push_back(charStyle);
    }
  }
}

void KNCollectorBase::collectConnectionStyle(const boost::optional<ID_t> &id, const KNConnectionStylePtr_t &style, const bool ref, const bool anonymous)
{
  // TODO: implement me
  (void) id;
  (void) style;
  (void) ref;
  (void) anonymous;
}

void KNCollectorBase::collectGraphicStyle(const optional<ID_t> &id, const KNGraphicStylePtr_t &style, const bool ref, const bool anonymous)
{
  if (m_collecting)
  {
    assert(m_currentStylesheet);

    const KNGraphicStylePtr_t graphicStyle = getValue(id, style, ref, m_dict.graphicStyles);
    if (bool(graphicStyle))
    {
      if (id && !anonymous)
        m_currentStylesheet->graphicStyles[get(id)] = graphicStyle;
      if (!ref)
        m_newStyles.push_back(graphicStyle);
    }

    if (m_layerOpened)
    {
      assert(!m_levelStack.empty());

      m_levelStack.top().graphicStyle = graphicStyle;
    }
  }
}

void KNCollectorBase::collectLayoutStyle(const optional<ID_t> &id, const KNLayoutStylePtr_t &style, const bool ref, const bool anonymous)
{
  if (m_collecting)
  {
    assert(m_currentStylesheet);

    const KNLayoutStylePtr_t layoutStyle = getValue(id, style, ref, m_dict.layoutStyles);
    if (bool(layoutStyle))
    {
      if (id && !anonymous)
        m_currentStylesheet->layoutStyles[get(id)] = layoutStyle;
      if (!ref)
        m_newStyles.push_back(layoutStyle);
    }
  }
}

void KNCollectorBase::collectListStyle(const boost::optional<ID_t> &id, const KNListStylePtr_t &style, const bool ref, const bool anonymous)
{
  // TODO: implement me
  (void) id;
  (void) style;
  (void) ref;
  (void) anonymous;
}

void KNCollectorBase::collectParagraphStyle(const optional<ID_t> &id, const KNParagraphStylePtr_t &style, const bool ref, const bool anonymous)
{
  if (m_collecting)
  {
    assert(m_currentStylesheet);

    const KNParagraphStylePtr_t paraStyle = getValue(id, style, ref, m_dict.paragraphStyles);
    if (bool(paraStyle))
    {
      if (id && !anonymous)
        m_currentStylesheet->paragraphStyles[get(id)] = paraStyle;
      if (!ref)
        m_newStyles.push_back(paraStyle);
    }
  }
}

void KNCollectorBase::collectPlaceholderStyle(const boost::optional<ID_t> &id, const KNPlaceholderStylePtr_t &style, const bool ref, const bool anonymous)
{
  if (m_collecting)
  {
    assert(m_currentStylesheet);

    const KNPlaceholderStylePtr_t placeholderStyle = getValue(id, style, ref, m_dict.placeholderStyles);
    if (bool(placeholderStyle))
    {
      if (id && !anonymous)
        m_currentStylesheet->placeholderStyles[get(id)] = placeholderStyle;
      if (!ref)
        m_newStyles.push_back(placeholderStyle);
    }

    if (m_layerOpened)
    {
      assert(!m_levelStack.empty());

      m_levelStack.top().placeholderStyle = placeholderStyle;
    }
  }
}

void KNCollectorBase::collectSlideStyle(const boost::optional<ID_t> &id, const KNSlideStylePtr_t &style, const bool ref, const bool anonymous)
{
  // TODO: implement me
  (void) id;
  (void) style;
  (void) ref;
  (void) anonymous;
}

void KNCollectorBase::collectTabularStyle(const boost::optional<ID_t> &id, const KNTabularStylePtr_t &style, const bool ref, const bool anonymous)
{
  // TODO: implement me
  (void) id;
  (void) style;
  (void) ref;
  (void) anonymous;
}

void KNCollectorBase::collectVectorStyle(const boost::optional<ID_t> &id, const KNVectorStylePtr_t &style, const bool ref, const bool anonymous)
{
  // TODO: implement me
  (void) id;
  (void) style;
  (void) ref;
  (void) anonymous;
}

void KNCollectorBase::collectGeometry(boost::optional<ID_t> &,
                                      boost::optional<KNSize> &naturalSize, boost::optional<KNPosition> &position,
                                      boost::optional<double> &angle,
                                      boost::optional<double> &shearXAngle, boost::optional<double> &shearYAngle,
                                      boost::optional<bool> &horizontalFlip, boost::optional<bool> &verticalFlip,
                                      boost::optional<bool> &aspectRatioLocked, boost::optional<bool> &sizesLocked)
{
  if (m_collecting)
  {
    assert(!m_levelStack.empty());

    m_defaults.applyGeometry(naturalSize, position, angle, shearXAngle, shearYAngle, horizontalFlip, verticalFlip, aspectRatioLocked, sizesLocked);
    assert(naturalSize && position && angle && shearXAngle && shearYAngle && horizontalFlip && verticalFlip && aspectRatioLocked && sizesLocked);

    const KNGeometryPtr_t geometry(new KNGeometry);
    geometry->naturalSize = get(naturalSize);
    geometry->position = get(position);
    geometry->angle = get(angle);
    geometry->shearXAngle = get(shearXAngle);
    geometry->shearYAngle = get(shearYAngle);
    geometry->horizontalFlip = get(horizontalFlip);
    geometry->verticalFlip = get(verticalFlip);
    geometry->aspectRatioLocked = get(aspectRatioLocked);
    geometry->sizesLocked = get(sizesLocked);

    m_levelStack.top().geometry = geometry;
  }
}

void KNCollectorBase::collectBezier(const optional<ID_t> &id, const KNPathPtr_t &path, const bool ref)
{
  if (m_collecting)
    m_currentPath = getValue(id, path, ref, m_dict.beziers);
}

void KNCollectorBase::collectGroup(const optional<ID_t> &, const KNGroupPtr_t &group)
{
  if (m_collecting)
  {
    assert(!m_objectsStack.empty());

    group->objects = m_objectsStack.top();
    m_objectsStack.pop();
    assert(!m_objectsStack.empty());
    m_objectsStack.top().push_back(makeObject(group));
  }
}

void KNCollectorBase::collectImage(const optional<ID_t> &id, const KNImagePtr_t &image)
{
  if (m_collecting)
  {
    assert(!m_objectsStack.empty());
    assert(!m_levelStack.empty());

    image->geometry = m_levelStack.top().geometry;
    m_levelStack.top().geometry.reset();
    if (id)
      m_dict.images[get(id)] = image;
    m_objectsStack.top().push_back(makeObject(image));
  }
}

void KNCollectorBase::collectLine(const optional<ID_t> &, const KNLinePtr_t &line)
{
  if (m_collecting)
  {
    assert(!m_objectsStack.empty());
    assert(!m_levelStack.empty());

    line->geometry = m_levelStack.top().geometry;
    m_levelStack.top().geometry.reset();
    m_objectsStack.top().push_back(makeObject(line));
  }
}

void KNCollectorBase::collectShape(const optional<ID_t> &)
{
  if (m_collecting)
  {
    assert(!m_objectsStack.empty());
    assert(!m_levelStack.empty());

    const KNShapePtr_t shape(new KNShape());

    if (!m_currentPath)
    {
      KN_DEBUG_MSG(("the path is empty\n"));
    }
    shape->path = m_currentPath;
    m_currentPath.reset();

    shape->geometry = m_levelStack.top().geometry;
    m_levelStack.top().geometry.reset();

    shape->text = m_currentText;
    m_currentText.reset();

    // TODO: fill style

    m_objectsStack.top().push_back(makeObject(shape));
  }
}

void KNCollectorBase::collectBezierPath(const optional<ID_t> &)
{
  // nothing needed
}

void KNCollectorBase::collectPolygonPath(const optional<ID_t> &, const KNSize &size, const unsigned edges)
{
  if (m_collecting)
    m_currentPath = makePolygonPath(size, edges);
}

void KNCollectorBase::collectRoundedRectanglePath(const optional<ID_t> &, const KNSize &size, const double radius)
{
  if (m_collecting)
    m_currentPath = makeRoundedRectanglePath(size, radius);
}

void KNCollectorBase::collectArrowPath(const optional<ID_t> &, const KNSize &size, const double headWidth, const double stemRelYPos, bool const doubleSided)
{
  if (m_collecting)
  {
    if (doubleSided)
      m_currentPath = makeDoubleArrowPath(size, headWidth, stemRelYPos);
    else
      m_currentPath = makeArrowPath(size, headWidth, stemRelYPos);
  }
}

void KNCollectorBase::collectStarPath(const optional<ID_t> &, const KNSize &size, const unsigned points, const double innerRadius)
{
  if (m_collecting)
    m_currentPath = makeStarPath(size, points, innerRadius);
}

void KNCollectorBase::collectConnectionPath(const optional<ID_t> &, const KNSize &size, const double middleX, const double middleY)
{
  if (m_collecting)
    m_currentPath = makeConnectionPath(size, middleX, middleY);
}

void KNCollectorBase::collectCalloutPath(const optional<ID_t> &, const KNSize &size, const double radius, const double tailSize, const double tailX, const double tailY, bool quoteBubble)
{
  if (m_collecting)
  {
    if (quoteBubble)
      m_currentPath = makeQuoteBubblePath(size, radius, tailSize, tailX, tailY);
    else
      m_currentPath = makeCalloutPath(size, radius, tailSize, tailX, tailY);
  }
}

void KNCollectorBase::collectData(const boost::optional<ID_t> &id, const WPXInputStreamPtr_t &stream, const boost::optional<std::string> &displayName, const boost::optional<unsigned> &type, const bool ref)
{
  // TODO: implement me
  (void) id;
  (void) stream;
  (void) displayName;
  (void) type;
  (void) ref;
}

void KNCollectorBase::collectUnfiltered(const boost::optional<ID_t> &id, const boost::optional<KNSize> &size, const bool ref)
{
  // TODO: implement me
  (void) id;
  (void) size;
  (void) ref;
}

void KNCollectorBase::collectFilteredImage(const boost::optional<ID_t> &id, const bool ref)
{
  // TODO: implement me
  (void) id;
  (void) ref;
}

void KNCollectorBase::collectMedia(const optional<ID_t> &)
{
  if (m_collecting)
  {
    assert(!m_objectsStack.empty());

    // FIXME: build media from collected parts
    // media->geometry = m_currentGeometry;
    // m_currentGeometry.reset();
    // m_objectsStack.top().push_back(makeObject(media));
  }
}

void KNCollectorBase::collectLayer(const optional<ID_t> &, bool)
{
  if (m_collecting)
  {
    assert(m_layerOpened);
    assert(!m_objectsStack.empty());

    m_currentLayer.reset(new KNLayer());
    m_currentLayer->objects = m_objectsStack.top();
    m_objectsStack.pop();
  }
}

void KNCollectorBase::collectStylesheet(const boost::optional<ID_t> &id, const boost::optional<ID_t> &parent)
{
  if (m_collecting)
  {
    assert(m_currentStylesheet);

    if (parent)
      m_currentStylesheet->parent = m_dict.stylesheets[get(parent)];

    if (id)
      m_dict.stylesheets[get(id)] = m_currentStylesheet;

    for_each(m_newStyles.begin(), m_newStyles.end(), boost::bind(&KNStyle::link, _1, m_currentStylesheet));

    m_currentStylesheet.reset(new KNStylesheet());
    m_newStyles.clear();
  }
}

void KNCollectorBase::collectText(const optional<ID_t> &style, const std::string &text)
{
  if (m_collecting)
  {
    assert(bool(m_currentText));

    m_currentText->insertText(text, getValue(style, m_dict.characterStyles));
  }
}

void KNCollectorBase::collectTab()
{
  if (m_collecting)
  {
    assert(bool(m_currentText));

    m_currentText->insertTab();
  }
}

void KNCollectorBase::collectLineBreak()
{
  if (m_collecting)
  {
    assert(bool(m_currentText));

    m_currentText->insertLineBreak();
  }
}

void KNCollectorBase::collectTextPlaceholder(const optional<ID_t> &id, const bool title, const bool ref)
{
  if (m_collecting)
  {
    KNPlaceholderPtr_t placeholder;
    KNPlaceholderMap_t &placeholderMap = title ? m_dict.titlePlaceholders : m_dict.bodyPlaceholders;

    if (ref)
    {
      assert(!m_objectsStack.empty());
      if (id)
        placeholder = placeholderMap[get(id)];

      if (bool(placeholder))
      {
        m_objectsStack.top().push_back(makeObject(placeholder));
      }
      else
      {
        KN_DEBUG_MSG(("no text placeholder found\n"));
      }
    }
    else
    {
      placeholder.reset(new KNPlaceholder());
      placeholder->title = title;
      placeholder->text = m_currentText;

      m_currentText.reset();

      if (id)
        placeholderMap[get(id)] = placeholder;
    }
  }
}

void KNCollectorBase::startLayer()
{
  if (m_collecting)
  {
    assert(!m_layerOpened);
    assert(m_objectsStack.empty());
    assert(!m_currentLayer);

    m_objectsStack.push(KNObjectList_t());
    m_layerOpened = true;

    startLevel();

    assert(!m_objectsStack.empty());
  }
}
<commit_msg>attach style to text placeholder<commit_after>/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/*
 * This file is part of the libkeynote project.
 *
 * This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
 */

#include <algorithm>
#include <cassert>

#include <boost/bind.hpp>

#include "libkeynote_utils.h"
#include "KNCollectorBase.h"
#include "KNDefaults.h"
#include "KNDictionary.h"
#include "KNShape.h"
#include "KNStyles.h"
#include "KNText.h"

using boost::optional;

namespace libkeynote
{

namespace
{

/** Return value, either directly or by its ID.
  *
  * If the input is not a reference, save value to @c map for future use.
  *
  * @arg[in] id the value's ID
  * @arg[in] value a possible value
  * @arg[in] ref indicator whether the input should be handled as a
  *   value or a reference
  * @arg[in] map the map to use to retrieve the value by ID or to save a
  *   new value
  * @return the value
  */
template<class T>
T getValue(const optional<ID_t> &id, const T &value, const bool ref, boost::unordered_map<ID_t, T> &map)
{
  T retval;

  if (ref)
  {
    assert(id);

    const typename boost::unordered_map<ID_t, T>::const_iterator it = map.find(get(id));
    if (map.end() != it)
    {
      retval = it->second;
    }
    else
    {
      KN_DEBUG_MSG(("item with ID %s does not exist\n", get(id).c_str()));
    }
  }
  else
  {
    retval = value;
    if (id)
      map[get(id)] = value;
  }

  return retval;
}

/** Return value by its ID.
  *
  * @arg[in] id the value's ID
  * @arg[in] map the map to use to retrieve the value
  * @return the value
  */
template<class T>
T getValue(const optional<ID_t> &id, boost::unordered_map<ID_t, T> &map)
{
  return getValue(id, T(), true, map);
}

}

KNCollectorBase::Level::Level()
  : geometry()
  , graphicStyle()
  , placeholderStyle()
{
}

KNCollectorBase::KNCollectorBase(KNDictionary &dict, const KNDefaults &defaults)
  : m_dict(dict)
  , m_defaults(defaults)
  , m_currentLayer()
  , m_levelStack()
  , m_objectsStack()
  , m_currentPath()
  , m_currentText()
  , m_currentStylesheet(new KNStylesheet())
  , m_newStyles()
  , m_collecting(false)
  , m_layerOpened(false)
  , m_groupLevel(0)
{
}

KNCollectorBase::~KNCollectorBase()
{
  assert(!m_collecting);
  assert(m_objectsStack.empty());
}

void KNCollectorBase::collectCellStyle(const boost::optional<ID_t> &id, const KNCellStylePtr_t &style, const bool ref, const bool anonymous)
{
  // TODO: implement me
  (void) id;
  (void) style;
  (void) ref;
  (void) anonymous;
}

void KNCollectorBase::collectCharacterStyle(const optional<ID_t> &id, const KNCharacterStylePtr_t &style, const bool ref, const bool anonymous)
{
  if (m_collecting)
  {
    assert(m_currentStylesheet);

    const KNCharacterStylePtr_t charStyle = getValue(id, style, ref, m_dict.characterStyles);
    if (bool(charStyle))
    {
      if (id && !anonymous)
        m_currentStylesheet->characterStyles[get(id)] = charStyle;
      if (!ref)
        m_newStyles.push_back(charStyle);
    }
  }
}

void KNCollectorBase::collectConnectionStyle(const boost::optional<ID_t> &id, const KNConnectionStylePtr_t &style, const bool ref, const bool anonymous)
{
  // TODO: implement me
  (void) id;
  (void) style;
  (void) ref;
  (void) anonymous;
}

void KNCollectorBase::collectGraphicStyle(const optional<ID_t> &id, const KNGraphicStylePtr_t &style, const bool ref, const bool anonymous)
{
  if (m_collecting)
  {
    assert(m_currentStylesheet);

    const KNGraphicStylePtr_t graphicStyle = getValue(id, style, ref, m_dict.graphicStyles);
    if (bool(graphicStyle))
    {
      if (id && !anonymous)
        m_currentStylesheet->graphicStyles[get(id)] = graphicStyle;
      if (!ref)
        m_newStyles.push_back(graphicStyle);
    }

    if (m_layerOpened)
    {
      assert(!m_levelStack.empty());

      m_levelStack.top().graphicStyle = graphicStyle;
    }
  }
}

void KNCollectorBase::collectLayoutStyle(const optional<ID_t> &id, const KNLayoutStylePtr_t &style, const bool ref, const bool anonymous)
{
  if (m_collecting)
  {
    assert(m_currentStylesheet);

    const KNLayoutStylePtr_t layoutStyle = getValue(id, style, ref, m_dict.layoutStyles);
    if (bool(layoutStyle))
    {
      if (id && !anonymous)
        m_currentStylesheet->layoutStyles[get(id)] = layoutStyle;
      if (!ref)
        m_newStyles.push_back(layoutStyle);
    }
  }
}

void KNCollectorBase::collectListStyle(const boost::optional<ID_t> &id, const KNListStylePtr_t &style, const bool ref, const bool anonymous)
{
  // TODO: implement me
  (void) id;
  (void) style;
  (void) ref;
  (void) anonymous;
}

void KNCollectorBase::collectParagraphStyle(const optional<ID_t> &id, const KNParagraphStylePtr_t &style, const bool ref, const bool anonymous)
{
  if (m_collecting)
  {
    assert(m_currentStylesheet);

    const KNParagraphStylePtr_t paraStyle = getValue(id, style, ref, m_dict.paragraphStyles);
    if (bool(paraStyle))
    {
      if (id && !anonymous)
        m_currentStylesheet->paragraphStyles[get(id)] = paraStyle;
      if (!ref)
        m_newStyles.push_back(paraStyle);
    }
  }
}

void KNCollectorBase::collectPlaceholderStyle(const boost::optional<ID_t> &id, const KNPlaceholderStylePtr_t &style, const bool ref, const bool anonymous)
{
  if (m_collecting)
  {
    assert(m_currentStylesheet);

    const KNPlaceholderStylePtr_t placeholderStyle = getValue(id, style, ref, m_dict.placeholderStyles);
    if (bool(placeholderStyle))
    {
      if (id && !anonymous)
        m_currentStylesheet->placeholderStyles[get(id)] = placeholderStyle;
      if (!ref)
        m_newStyles.push_back(placeholderStyle);
    }

    if (m_layerOpened)
    {
      assert(!m_levelStack.empty());

      m_levelStack.top().placeholderStyle = placeholderStyle;
    }
  }
}

void KNCollectorBase::collectSlideStyle(const boost::optional<ID_t> &id, const KNSlideStylePtr_t &style, const bool ref, const bool anonymous)
{
  // TODO: implement me
  (void) id;
  (void) style;
  (void) ref;
  (void) anonymous;
}

void KNCollectorBase::collectTabularStyle(const boost::optional<ID_t> &id, const KNTabularStylePtr_t &style, const bool ref, const bool anonymous)
{
  // TODO: implement me
  (void) id;
  (void) style;
  (void) ref;
  (void) anonymous;
}

void KNCollectorBase::collectVectorStyle(const boost::optional<ID_t> &id, const KNVectorStylePtr_t &style, const bool ref, const bool anonymous)
{
  // TODO: implement me
  (void) id;
  (void) style;
  (void) ref;
  (void) anonymous;
}

void KNCollectorBase::collectGeometry(boost::optional<ID_t> &,
                                      boost::optional<KNSize> &naturalSize, boost::optional<KNPosition> &position,
                                      boost::optional<double> &angle,
                                      boost::optional<double> &shearXAngle, boost::optional<double> &shearYAngle,
                                      boost::optional<bool> &horizontalFlip, boost::optional<bool> &verticalFlip,
                                      boost::optional<bool> &aspectRatioLocked, boost::optional<bool> &sizesLocked)
{
  if (m_collecting)
  {
    assert(!m_levelStack.empty());

    m_defaults.applyGeometry(naturalSize, position, angle, shearXAngle, shearYAngle, horizontalFlip, verticalFlip, aspectRatioLocked, sizesLocked);
    assert(naturalSize && position && angle && shearXAngle && shearYAngle && horizontalFlip && verticalFlip && aspectRatioLocked && sizesLocked);

    const KNGeometryPtr_t geometry(new KNGeometry);
    geometry->naturalSize = get(naturalSize);
    geometry->position = get(position);
    geometry->angle = get(angle);
    geometry->shearXAngle = get(shearXAngle);
    geometry->shearYAngle = get(shearYAngle);
    geometry->horizontalFlip = get(horizontalFlip);
    geometry->verticalFlip = get(verticalFlip);
    geometry->aspectRatioLocked = get(aspectRatioLocked);
    geometry->sizesLocked = get(sizesLocked);

    m_levelStack.top().geometry = geometry;
  }
}

void KNCollectorBase::collectBezier(const optional<ID_t> &id, const KNPathPtr_t &path, const bool ref)
{
  if (m_collecting)
    m_currentPath = getValue(id, path, ref, m_dict.beziers);
}

void KNCollectorBase::collectGroup(const optional<ID_t> &, const KNGroupPtr_t &group)
{
  if (m_collecting)
  {
    assert(!m_objectsStack.empty());

    group->objects = m_objectsStack.top();
    m_objectsStack.pop();
    assert(!m_objectsStack.empty());
    m_objectsStack.top().push_back(makeObject(group));
  }
}

void KNCollectorBase::collectImage(const optional<ID_t> &id, const KNImagePtr_t &image)
{
  if (m_collecting)
  {
    assert(!m_objectsStack.empty());
    assert(!m_levelStack.empty());

    image->geometry = m_levelStack.top().geometry;
    m_levelStack.top().geometry.reset();
    if (id)
      m_dict.images[get(id)] = image;
    m_objectsStack.top().push_back(makeObject(image));
  }
}

void KNCollectorBase::collectLine(const optional<ID_t> &, const KNLinePtr_t &line)
{
  if (m_collecting)
  {
    assert(!m_objectsStack.empty());
    assert(!m_levelStack.empty());

    line->geometry = m_levelStack.top().geometry;
    m_levelStack.top().geometry.reset();
    m_objectsStack.top().push_back(makeObject(line));
  }
}

void KNCollectorBase::collectShape(const optional<ID_t> &)
{
  if (m_collecting)
  {
    assert(!m_objectsStack.empty());
    assert(!m_levelStack.empty());

    const KNShapePtr_t shape(new KNShape());

    if (!m_currentPath)
    {
      KN_DEBUG_MSG(("the path is empty\n"));
    }
    shape->path = m_currentPath;
    m_currentPath.reset();

    shape->geometry = m_levelStack.top().geometry;
    m_levelStack.top().geometry.reset();

    shape->text = m_currentText;
    m_currentText.reset();

    // TODO: fill style

    m_objectsStack.top().push_back(makeObject(shape));
  }
}

void KNCollectorBase::collectBezierPath(const optional<ID_t> &)
{
  // nothing needed
}

void KNCollectorBase::collectPolygonPath(const optional<ID_t> &, const KNSize &size, const unsigned edges)
{
  if (m_collecting)
    m_currentPath = makePolygonPath(size, edges);
}

void KNCollectorBase::collectRoundedRectanglePath(const optional<ID_t> &, const KNSize &size, const double radius)
{
  if (m_collecting)
    m_currentPath = makeRoundedRectanglePath(size, radius);
}

void KNCollectorBase::collectArrowPath(const optional<ID_t> &, const KNSize &size, const double headWidth, const double stemRelYPos, bool const doubleSided)
{
  if (m_collecting)
  {
    if (doubleSided)
      m_currentPath = makeDoubleArrowPath(size, headWidth, stemRelYPos);
    else
      m_currentPath = makeArrowPath(size, headWidth, stemRelYPos);
  }
}

void KNCollectorBase::collectStarPath(const optional<ID_t> &, const KNSize &size, const unsigned points, const double innerRadius)
{
  if (m_collecting)
    m_currentPath = makeStarPath(size, points, innerRadius);
}

void KNCollectorBase::collectConnectionPath(const optional<ID_t> &, const KNSize &size, const double middleX, const double middleY)
{
  if (m_collecting)
    m_currentPath = makeConnectionPath(size, middleX, middleY);
}

void KNCollectorBase::collectCalloutPath(const optional<ID_t> &, const KNSize &size, const double radius, const double tailSize, const double tailX, const double tailY, bool quoteBubble)
{
  if (m_collecting)
  {
    if (quoteBubble)
      m_currentPath = makeQuoteBubblePath(size, radius, tailSize, tailX, tailY);
    else
      m_currentPath = makeCalloutPath(size, radius, tailSize, tailX, tailY);
  }
}

void KNCollectorBase::collectData(const boost::optional<ID_t> &id, const WPXInputStreamPtr_t &stream, const boost::optional<std::string> &displayName, const boost::optional<unsigned> &type, const bool ref)
{
  // TODO: implement me
  (void) id;
  (void) stream;
  (void) displayName;
  (void) type;
  (void) ref;
}

void KNCollectorBase::collectUnfiltered(const boost::optional<ID_t> &id, const boost::optional<KNSize> &size, const bool ref)
{
  // TODO: implement me
  (void) id;
  (void) size;
  (void) ref;
}

void KNCollectorBase::collectFilteredImage(const boost::optional<ID_t> &id, const bool ref)
{
  // TODO: implement me
  (void) id;
  (void) ref;
}

void KNCollectorBase::collectMedia(const optional<ID_t> &)
{
  if (m_collecting)
  {
    assert(!m_objectsStack.empty());

    // FIXME: build media from collected parts
    // media->geometry = m_currentGeometry;
    // m_currentGeometry.reset();
    // m_objectsStack.top().push_back(makeObject(media));
  }
}

void KNCollectorBase::collectLayer(const optional<ID_t> &, bool)
{
  if (m_collecting)
  {
    assert(m_layerOpened);
    assert(!m_objectsStack.empty());

    m_currentLayer.reset(new KNLayer());
    m_currentLayer->objects = m_objectsStack.top();
    m_objectsStack.pop();
  }
}

void KNCollectorBase::collectStylesheet(const boost::optional<ID_t> &id, const boost::optional<ID_t> &parent)
{
  if (m_collecting)
  {
    assert(m_currentStylesheet);

    if (parent)
      m_currentStylesheet->parent = m_dict.stylesheets[get(parent)];

    if (id)
      m_dict.stylesheets[get(id)] = m_currentStylesheet;

    for_each(m_newStyles.begin(), m_newStyles.end(), boost::bind(&KNStyle::link, _1, m_currentStylesheet));

    m_currentStylesheet.reset(new KNStylesheet());
    m_newStyles.clear();
  }
}

void KNCollectorBase::collectText(const optional<ID_t> &style, const std::string &text)
{
  if (m_collecting)
  {
    assert(bool(m_currentText));

    m_currentText->insertText(text, getValue(style, m_dict.characterStyles));
  }
}

void KNCollectorBase::collectTab()
{
  if (m_collecting)
  {
    assert(bool(m_currentText));

    m_currentText->insertTab();
  }
}

void KNCollectorBase::collectLineBreak()
{
  if (m_collecting)
  {
    assert(bool(m_currentText));

    m_currentText->insertLineBreak();
  }
}

void KNCollectorBase::collectTextPlaceholder(const optional<ID_t> &id, const bool title, const bool ref)
{
  if (m_collecting)
  {
    KNPlaceholderPtr_t placeholder;
    KNPlaceholderMap_t &placeholderMap = title ? m_dict.titlePlaceholders : m_dict.bodyPlaceholders;

    if (ref)
    {
      assert(!m_objectsStack.empty());
      if (id)
        placeholder = placeholderMap[get(id)];

      if (bool(placeholder))
      {
        m_objectsStack.top().push_back(makeObject(placeholder));
      }
      else
      {
        KN_DEBUG_MSG(("no text placeholder found\n"));
      }
    }
    else
    {
      assert(!m_levelStack.empty());

      placeholder.reset(new KNPlaceholder());
      placeholder->title = title;
      placeholder->text = m_currentText;
      placeholder->style = m_levelStack.top().placeholderStyle;

      m_currentText.reset();
      m_levelStack.top().placeholderStyle.reset();

      if (id)
        placeholderMap[get(id)] = placeholder;
    }
  }
}

void KNCollectorBase::startLayer()
{
  if (m_collecting)
  {
    assert(!m_layerOpened);
    assert(m_objectsStack.empty());
    assert(!m_currentLayer);

    m_objectsStack.push(KNObjectList_t());
    m_layerOpened = true;

    startLevel();

    assert(!m_objectsStack.empty());
  }
}

{'fraction_non_alphanumeric': 0.12656591767747066, 'fraction_numerical': 0.0005302578378736661, 'mean_word_length': 3.992554599602912, 'pattern_counts': {'":': 0, '<': 113, '<?xml version=': 0, '>': 172, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 10, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '6172442', 'n_tokens_mistral': 10353, 'n_tokens_neox': 10291, 'n_words': 2144}