Blob Blame History Raw
/* -*- Mode: C++; c-default-style: "k&r"; indent-tabs-mode: nil; tab-width: 2; c-basic-offset: 2 -*- */

/* libmwaw
* Version: MPL 2.0 / LGPLv2+
*
* The contents of this file are subject to the Mozilla Public License Version
* 2.0 (the "License"); you may not use this file except in compliance with
* the License or as specified alternatively below. You may obtain a copy of
* the License at http://www.mozilla.org/MPL/
*
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
* for the specific language governing rights and limitations under the
* License.
*
* Major Contributor(s):
* Copyright (C) 2002 William Lachance (wrlach@gmail.com)
* Copyright (C) 2002,2004 Marc Maurer (uwog@uwog.net)
* Copyright (C) 2004-2006 Fridrich Strba (fridrich.strba@bluewin.ch)
* Copyright (C) 2006, 2007 Andrew Ziem
* Copyright (C) 2011, 2012 Alonso Laurent (alonso@loria.fr)
*
*
* All Rights Reserved.
*
* For minor contributions see the git repository.
*
* Alternatively, the contents of this file may be used under the terms of
* the GNU Lesser General Public License Version 2 or later (the "LGPLv2+"),
* in which case the provisions of the LGPLv2+ are applicable
* instead of those above.
*/

#include <cmath>
#include <iomanip>
#include <iostream>
#include <limits>
#include <map>
#include <sstream>

#include <librevenge/librevenge.h>

#include "MWAWFont.hxx"
#include "MWAWFontConverter.hxx"
#include "MWAWGraphicShape.hxx"
#include "MWAWGraphicStyle.hxx"
#include "MWAWParser.hxx"
#include "MWAWPosition.hxx"
#include "MWAWGraphicStyle.hxx"
#include "MWAWPresentationListener.hxx"
#include "MWAWSubDocument.hxx"

#include "PowerPoint7Struct.hxx"

#include "PowerPoint7Graph.hxx"
#include "PowerPoint7Parser.hxx"

/** Internal: the structures of a PowerPoint7Graph */
namespace PowerPoint7GraphInternal
{
//! Internal: a frame of a PowerPoint7Graph
struct Frame {
  //! the frame type
  enum Type { Arc, Line, Group, Placeholder, Polygon, Rect, Unknown };
  //! constructor
  explicit Frame(Type type=Unknown)
    : m_type(type)
    , m_subType(-10000)
    , m_dimension()
    , m_rotation(0)
    , m_style()
    , m_pictureId(-1)
    , m_textId(-1)
    , m_isBackground(false)
    , m_isSent(false)
  {
    for (auto &flip : m_flip) flip=false;
  }
  //! destructor
  virtual ~Frame();
  //! try to update the list of text sub zone
  virtual void getTextZoneList(std::vector<int> &textIdList) const
  {
    if (m_textId>=0)
      textIdList.push_back(m_textId);
  }
  //! the type:
  enum Type m_type;
  //! the sub type
  int m_subType;
  //! the dimension
  MWAWBox2i m_dimension;
  //! the rotation
  float m_rotation;
  //! the flip flags: horizontal and vertical
  bool m_flip[2];
  //! the style
  MWAWGraphicStyle m_style;
  //! the picture id(if positif)
  int m_pictureId;
  //! the text id(if positif)
  int m_textId;
  //! a flag to know if this is the slide's background
  bool m_isBackground;
  //! flag to know if a frame is sent
  mutable bool m_isSent;
};

Frame::~Frame()
{
}

//! Internal: a frame rect of a PowerPoint7Graph
struct FrameArc final : public Frame {
  //! constructor
  FrameArc() : Frame(Arc)
  {
    m_angles[0]=0;
    m_angles[1]=90;
  }
  //! update the shape
  bool updateShape(MWAWBox2f const &finalBox, MWAWGraphicShape &shape) const;
  //! the arc angles
  float m_angles[2];
};

bool FrameArc::updateShape(MWAWBox2f const &finalBox, MWAWGraphicShape &shape) const
{
  float angle[2] = { m_angles[0], m_angles[0]+m_angles[1] };
  if (angle[1]<angle[0])
    std::swap(angle[0],angle[1]);
  if (angle[1]>360) {
    int numLoop=int(angle[1]/360)-1;
    angle[0]-=float(numLoop*360);
    angle[1]-=float(numLoop*360);
    while (angle[1] > 360) {
      angle[0]-=360;
      angle[1]-=360;
    }
  }
  if (angle[0] < -360) {
    int numLoop=int(angle[0]/360)+1;
    angle[0]-=float(numLoop*360);
    angle[1]-=float(numLoop*360);
    while (angle[0] < -360) {
      angle[0]+=360;
      angle[1]+=360;
    }
  }
  MWAWVec2f center = finalBox.center();
  MWAWVec2f axis = 0.5f*MWAWVec2f(finalBox.size());
  // we must compute the real bd box
  float minVal[2] = { 0, 0 }, maxVal[2] = { 0, 0 };
  int limitAngle[2];
  for (int i = 0; i < 2; i++)
    limitAngle[i] = (angle[i] < 0) ? int(angle[i]/90)-1 : int(angle[i]/90);
  for (int bord = limitAngle[0]; bord <= limitAngle[1]+1; bord++) {
    float ang = (bord == limitAngle[0]) ? angle[0] :
                (bord == limitAngle[1]+1) ? angle[1] : 90 * float(bord);
    ang *= float(M_PI/180.);
    float actVal[2] = { axis[0] *std::cos(ang), -axis[1] *std::sin(ang)};
    if (actVal[0] < minVal[0]) minVal[0] = actVal[0];
    else if (actVal[0] > maxVal[0]) maxVal[0] = actVal[0];
    if (actVal[1] < minVal[1]) minVal[1] = actVal[1];
    else if (actVal[1] > maxVal[1]) maxVal[1] = actVal[1];
  }
  MWAWBox2f realBox(MWAWVec2f(center[0]+minVal[0],center[1]+minVal[1]),
                    MWAWVec2f(center[0]+maxVal[0],center[1]+maxVal[1]));
  shape = MWAWGraphicShape::pie(realBox, finalBox, MWAWVec2f(float(angle[0]),float(angle[1])));
  return true;
}

//! Internal: a group of a PowerPoint7Graph
struct FrameGroup final : public Frame {
  //! constructor
  FrameGroup()
    : Frame(Group)
    , m_child()
  {
  }
  //! try to update the list of text sub zone
  void getTextZoneList(std::vector<int> &textIdList) const final
  {
    for (auto child : m_child) {
      if (child)
        child->getTextZoneList(textIdList);
    }
  }
  //! the child
  std::vector<std::shared_ptr<Frame> > m_child;
};

//! Internal: a frame placeholder of a PowerPoint7Graph
struct FramePlaceholder final : public Frame {
  //! constructor
  FramePlaceholder()
    : Frame(Placeholder)
  {
  }
};

//! Internal: a polygon of a PowerPoint7Graph
struct FramePolygon final : public Frame {
  //! constructor
  FramePolygon()
    : Frame(Polygon)
    , m_vertices()
  {
  }
  //! update the shape
  bool updateShape(MWAWBox2f const &finalBox, MWAWGraphicShape &shape) const;
  //! the vertices
  std::vector<MWAWVec2i> m_vertices;
};

bool FramePolygon::updateShape(MWAWBox2f const &finalBox, MWAWGraphicShape &shape) const
{
  if (m_vertices.empty()) return false;
  MWAWBox2i actBox(m_vertices[0],m_vertices[0]);
  for (size_t i=1; i<m_vertices.size(); ++i) actBox=actBox.getUnion(MWAWBox2i(m_vertices[i],m_vertices[i]));
  float factor[2], decal[2];
  for (int i=0; i<2; ++i) {
    if (actBox.size()[i]<0||actBox.size()[i]>0)
      factor[i]=float(finalBox.size()[i])/float(actBox.size()[i]);
    else
      factor[i]=1.f;
    decal[i]=finalBox[0][i]-factor[i]*float(actBox[0][i]);
  }
  shape.m_type = MWAWGraphicShape::Polygon;
  for (auto const &pt : m_vertices)
    shape.m_vertices.push_back(MWAWVec2f(decal[0]+factor[0]*float(pt[0]), decal[1]+factor[1]*float(pt[1])));
  //if (m_type==1) shape.m_vertices.push_back(shape.m_vertices[0]);
  return true;
}

//! Internal: a frame rect of a PowerPoint7Graph
struct FrameRect final : public Frame {
  //! constructor
  FrameRect()
    : Frame(Rect)
  {
  }
};

//! Internal: a picture of a PowerPoint7Graph
struct Picture {
  //! constructor
  Picture()
    : m_object()
    , m_box()
    , m_name("")
  {
  }
  //! returns true if the picture is empty
  bool isEmpty() const
  {
    return m_object.isEmpty();
  }
  //! the picture data
  MWAWEmbeddedObject m_object;
  //! the picture box
  MWAWBox2i m_box;
  //! the picture name
  std::string m_name;
};

////////////////////////////////////////
//! Internal: the state of a PowerPoint7Graph
struct State {
  //! constructor
  State()
    : m_decal(-2880,-2160)
    , m_actualSlideId()
    , m_colorList()
    , m_arrowList()
    , m_actualFrame()
    , m_actualGroup()
    , m_idToFrameMap()
    , m_idToPictureMap()
  {
  }
  //! try to add a frame
  void setFrame(Frame *frame)
  {
    if (!frame) {
      MWAW_DEBUG_MSG(("PowerPoint7GraphInternal::State::setFrame: oops called with no frame\n"));
      return;
    }
    auto *group=dynamic_cast<FrameGroup *>(frame);
    std::shared_ptr<Frame> newFrame;
    bool inGroup=m_actualGroup.get()!=nullptr;
    if (group==nullptr) {
      if (m_actualFrame) {
        MWAW_DEBUG_MSG(("PowerPoint7GraphInternal::State::setFrame: oops a frame is not closed\n"));
      }
      newFrame.reset(frame);
      m_actualFrame=newFrame;
      if (inGroup)
        m_actualGroup->m_child.push_back(newFrame);
    }
    else {
      std::shared_ptr<FrameGroup> newGroup(group);
      newFrame=newGroup;
      if (inGroup)
        m_actualGroup->m_child.push_back(newFrame);
      m_actualGroup=newGroup;
    }
    if (!inGroup && !m_actualSlideId.isValid()) {
      MWAW_DEBUG_MSG(("PowerPoint7GraphInternal::State::setFrame: oops called with no parent\n"));
    }
    else if (!inGroup) {
      if (m_idToFrameMap.find(m_actualSlideId)==m_idToFrameMap.end())
        m_idToFrameMap[m_actualSlideId]=std::vector<std::shared_ptr<Frame> >();
      m_idToFrameMap.find(m_actualSlideId)->second.push_back(newFrame);
    }
  }
  //! reset the actual frame
  void resetFrame()
  {
    m_actualFrame.reset();
  }
  //! try to return a pattern
  bool getPattern(int id, MWAWGraphicStyle::Pattern &pattern) const;
  //! returns an arrow if possible
  bool getArrow(int id, MWAWGraphicStyle::Arrow &arrow)
  {
    if (m_arrowList.empty()) initArrows();
    if (id<=0 || id>int(m_arrowList.size())) {
      MWAW_DEBUG_MSG(("PowerPoint7GraphInternal::State::getArrow: can not find arrow %d\n", id));
      return false;
    }
    arrow=m_arrowList[size_t(id-1)];
    return true;
  }
  //! init the arrow list
  void initArrows();
  //! returns a custom shape corresponding to an id
  static bool getCustomShape(int id, MWAWGraphicShape &shape);
  //! the decal from file position to final position
  MWAWVec2i m_decal;
  //! the actual slide id
  PowerPoint7Struct::SlideId m_actualSlideId;
  //! the current color list
  std::vector<MWAWColor> m_colorList;
  //! the arrow list
  std::vector<MWAWGraphicStyle::Arrow> m_arrowList;
  //! the actual frame
  std::shared_ptr<Frame> m_actualFrame;
  //! the actual group
  std::shared_ptr<FrameGroup> m_actualGroup;
  //! a map slide id to the list of frame
  std::map<PowerPoint7Struct::SlideId, std::vector<std::shared_ptr<Frame> > > m_idToFrameMap;
  //! a map id to picture
  std::map<int, Picture> m_idToPictureMap;
};

bool State::getPattern(int id, MWAWGraphicStyle::Pattern &pattern) const
{
  // normally between 1 and 32 but find a pattern resource with 38 patterns
  if (id<=0 || id>=39) {
    MWAW_DEBUG_MSG(("PowerPoint7GraphInternal::State::getPattern: unknown id=%d\n", id));
    return false;
  }
  static uint16_t const values[] = {
    0xffff, 0xffff, 0xffff, 0xffff, 0x0, 0x0, 0x0, 0x0,
    0xddff, 0x77ff, 0xddff, 0x77ff, 0x8000, 0x800, 0x8000, 0x800,
    0xdd77, 0xdd77, 0xdd77, 0xdd77, 0x8800, 0x2200, 0x8800, 0x2200,
    0xaa55, 0xaa55, 0xaa55, 0xaa55, 0x8822, 0x8822, 0x8822, 0x8822,
    0x8844, 0x2211, 0x8844, 0x2211, 0x1122, 0x4488, 0x1122, 0x4488,
    0xaaaa, 0xaaaa, 0xaaaa, 0xaaaa, 0xff00, 0xff00, 0xff00, 0xff00,
    0x81c0, 0x6030, 0x180c, 0x603, 0x8103, 0x60c, 0x1830, 0x60c0,
    0x8888, 0x8888, 0x8888, 0x8888, 0xff00, 0x0, 0xff00, 0x0,
    0xb130, 0x31b, 0xd8c0, 0xc8d, 0x8010, 0x220, 0x108, 0x4004,
    0xff80, 0x8080, 0x8080, 0x8080, 0xff88, 0x8888, 0xff88, 0x8888,
    0xff80, 0x8080, 0xff08, 0x808, 0xeedd, 0xbb77, 0xeedd, 0xbb77,
    0x7fff, 0xffff, 0xf7ff, 0xffff, 0x88, 0x4422, 0x1100, 0x0,
    0x11, 0x2244, 0x8800, 0x0, 0x8080, 0x8080, 0x808, 0x808, 0xf000,
    0x0, 0xf00, 0x0, 0x8142, 0x2418, 0x8142, 0x2418,
    0x8000, 0x2200, 0x800, 0x2200, 0x1038, 0x7cfe, 0x7c38, 0x1000,
    0x102, 0x408, 0x1824, 0x4281, 0xc1e0, 0x7038, 0x1c0e, 0x783,
    0x8307, 0xe1c, 0x3870, 0xe0c1, 0xcccc, 0xcccc, 0xcccc, 0xcccc,
    0xffff, 0x0, 0xffff, 0x0, 0xf0f0, 0xf0f0, 0xf0f, 0xf0f,
    0x6699, 0x9966, 0x6699, 0x9966, 0x8142, 0x2418, 0x1824, 0x4281,
  };
  pattern.m_dim=MWAWVec2i(8,8);
  uint16_t const *ptr=&values[4*(id-1)];
  pattern.m_data.resize(8);
  for (size_t i=0; i < 4; ++i, ++ptr) {
    pattern.m_data[2*i]=static_cast<unsigned char>((*ptr)>>8);
    pattern.m_data[2*i+1]=static_cast<unsigned char>((*ptr)&0xff);
  }
  return true;
}

void State::initArrows()
{
  if (!m_arrowList.empty())
    return;
  m_arrowList.push_back(MWAWGraphicStyle::Arrow(5, MWAWBox2i(MWAWVec2i(0,0),MWAWVec2i(1131,1580)),
                        "M1013 1491l118 89-567-1580-564 1580 114-85 136-68 148-46 161-17 161 13 153 46z", false));
  m_arrowList.push_back(MWAWGraphicStyle::Arrow(5, MWAWBox2i(MWAWVec2i(0,0),MWAWVec2i(1131,1131)), "M462 1118l-102-29-102-51-93-72-72-93-51-102-29-102-13-105 13-102 29-106 51-102 72-89 93-72 102-50 102-34 106-9 101 9 106 34 98 50 93 72 72 89 51 102 29 106 13 102-13 105-29 102-51 102-72 93-93 72-98 51-106 29-101 13z", false));
  m_arrowList.push_back(MWAWGraphicStyle::Arrow(5, MWAWBox2i(MWAWVec2i(0,0),MWAWVec2i(1131,1131)), "M462 1118l-102-29-102-51-93-72-72-93-51-102-29-102-13-105 13-102 29-106 51-102 72-89 93-72 102-50 102-34 106-9 101 9 106 34 98 50 93 72 72 89 51 102 29 106 13 102-13 105-29 102-51 102-72 93-93 72-98 51-106 29-101 13z", false));
  m_arrowList.push_back(MWAWGraphicStyle::Arrow(5, MWAWBox2i(MWAWVec2i(0,0),MWAWVec2i(1131,1580)),
                        "M1013 1491l118 89-567-1580-564 1580 114-85 136-68 148-46 161-17 161 13 153 46z", false));
}

bool State::getCustomShape(int id, MWAWGraphicShape &shape)
{
  int N=4;
  double const *vertices=nullptr;
  switch (id) {
  case 0: {
    static double const v[]= {0.5,1, 1,0.5, 0.5,0, 0,0.5 };
    vertices=v;
    break;
  }
  case 1: {
    N=3;
    static double const v[]= {0,1, 1,1, 0.5,0};
    vertices=v;
    break;
  }
  case 2: {
    N=3;
    static double const v[]= {0,1, 1,1, 0,0};
    vertices=v;
    break;
  }
  case 3: {
    static double const v[]= {0,1, 0.7,1, 1,0, 0.3,0 };
    vertices=v;
    break;
  }
  case 4: {
    static double const v[]= {0,1, 0.3,0, 0.7,0, 1,1 };
    vertices=v;
    break;
  }
  case 5: {
    N=6;
    static double const v[]= {0,0.5, 0.2,1, 0.8,1, 1,0.5, 0.8,0, 0.2,0};
    vertices=v;
    break;
  }
  case 6: {
    N=8;
    static double const v[]= {0,0.3, 0,0.7, 0.3,1, 0.7,1, 1,0.7, 1,0.3, 0.7,0, 0.3,0};
    vertices=v;
    break;
  }
  case 7: {
    N=12;
    static double const v[]= {0,0.2, 0,0.8, 0.2,0.8, 0.2,1,
                              0.8,1, 0.8,0.8, 1,0.8, 1,0.2,
                              0.8,0.2, 0.8,0, 0.2,0, 0.2,0.2
                             };
    vertices=v;
    break;
  }
  case 8: {
    N=10;
    static double const v[]= {0.5,0, 0.383,0.383, 0,0.383, 0.3112,0.62,
                              0.1943,1, 0.5,0.78, 0.8056,1, 0.688,0.62,
                              1,0.3822, 0.6167,0.3822,
                             };
    vertices=v;
    break;
  }
  case 9: {
    N=7;
    static double const v[]= {0,0.333, 0,0.666, 0.7,0.666, 0.7,1,
                              1,0.5, 0.7,0, 0.7,0.333
                             };
    vertices=v;
    break;
  }
  case 10: {
    N=7;
    static double const v[]= {0,0.2, 0,0.8, 0.7,0.8, 0.7,1,
                              1,0.5, 0.7,0, 0.7,0.2
                             };
    vertices=v;
    break;
  }
  case 11: {
    N=5;
    static double const v[]= {0,0, 0,1, 0.7,1, 1,0.5, 0.7,0};
    vertices=v;
    break;
  }
  case 12: {
    N=12;
    static double const v[]= {0,1, 0.8,1, 1,0.8, 1,0,
                              0.8,0.2, 0.8,1, 0.8,0.2, 0,0.2,
                              0.2,0., 1,0, 0.2,0, 0,0.2
                             };

    vertices=v;
    break;
  }
  case 13: {
    N=11;
    static double const v[]= {0,0.1, 0,0.8, 0.1,0.9, 0.2,0.9,
                              0.1,1, 0.3,0.9, 0.9,0.9, 1,0.8,
                              1,0.1, 0.9,0, 0.1,0
                             };
    vertices=v;
    break;
  }
  case 14: {
    N=24;
    static double const v[]= { 0.5,0, 0.55,0.286, 0.7465,0.07, 0.656,0.342,
                               0.935,0.251, 0.7186,0.4465, 1,0.5, 0.7186,0.5535,
                               0.935,0.75, 0.6558,0.66558, 0.7465,0.9349, 0.558,0.7186,
                               0.495,1, 0.44,0.7186, 0.2511,0.935, 0.3418,0.6627,
                               0.063,0.7535, 0.279,0.558, 0,0.502, 0.279,0.4465,
                               0.063,0.2511, 0.3418,0.3418, 0.2511,0.069, 0.4395,0.286
                             };
    vertices=v;
    break;
  }
  default:
    break;
  }
  if (N<=0 || !vertices) {
    MWAW_DEBUG_MSG(("PowerPoint7GraphInternal::State::getCustomShape: unknown id %d\n", id));
    return false;
  }
  shape.m_type = MWAWGraphicShape::Polygon;
  shape.m_vertices.resize(size_t(N+1));
  for (int i=0; i<N; ++i)
    shape.m_vertices[size_t(i)]=MWAWVec2f(float(vertices[2*i]),float(vertices[2*i+1]));
  shape.m_vertices[size_t(N)]=MWAWVec2f(float(vertices[0]),float(vertices[1]));
  return true;
}

////////////////////////////////////////
//! Internal: the subdocument of a PowerPoint7Graph
class SubDocument final : public MWAWSubDocument
{
public:
  //! constructor for a text zone
  SubDocument(PowerPoint7Graph &parser, MWAWInputStreamPtr const &input, int tId)
    : MWAWSubDocument(nullptr, input, MWAWEntry())
    , m_parser(parser)
    , m_textId(tId)
    , m_listTextId()
  {
  }
  //! constructor for a list text zone
  SubDocument(PowerPoint7Graph &parser, MWAWInputStreamPtr const &input, std::vector<int> const &listTextId)
    : MWAWSubDocument(nullptr, input, MWAWEntry())
    , m_parser(parser)
    , m_textId(-1)
    , m_listTextId(listTextId)
  {
  }

  //! destructor
  ~SubDocument() final {}

  //! operator!=
  bool operator!=(MWAWSubDocument const &doc) const final
  {
    if (MWAWSubDocument::operator!=(doc)) return true;
    auto const *sDoc = dynamic_cast<SubDocument const *>(&doc);
    if (!sDoc) return true;
    if (&m_parser != &sDoc->m_parser) return true;
    if (m_textId != sDoc->m_textId) return true;
    if (m_listTextId != sDoc->m_listTextId) return true;
    return false;
  }

  //! the parser function
  void parse(MWAWListenerPtr &listener, libmwaw::SubDocumentType type) final;

protected:
  //! the parser
  PowerPoint7Graph &m_parser;
  //! the text id
  int m_textId;
  //! a list of text id
  std::vector<int> m_listTextId;
private:
  SubDocument(SubDocument const &) = delete;
  SubDocument &operator=(SubDocument const &) = delete;
};

void SubDocument::parse(MWAWListenerPtr &listener, libmwaw::SubDocumentType)
{
  if (!listener.get()) {
    MWAW_DEBUG_MSG(("PowerPoint7ParserInternal::SubDocument::parse: no listener\n"));
    return;
  }
  long pos = m_input->tell();
  if (m_textId>=0)
    m_parser.sendText(m_textId);
  else {
    for (size_t z=0; z<m_listTextId.size(); ++z) {
      if (z) listener->insertEOL();
      m_parser.sendText(m_listTextId[z]);
    }
  }
  m_input->seek(pos, librevenge::RVNG_SEEK_SET);
}
}

////////////////////////////////////////////////////////////
// constructor/destructor, ...
////////////////////////////////////////////////////////////
PowerPoint7Graph::PowerPoint7Graph(PowerPoint7Parser &parser)
  : m_parserState(parser.getParserState())
  , m_state(new PowerPoint7GraphInternal::State)
  , m_mainParser(&parser)
{
}

PowerPoint7Graph::~PowerPoint7Graph()
{ }

int PowerPoint7Graph::version() const
{
  return m_parserState->m_version;
}

void PowerPoint7Graph::setPageSize(MWAWVec2i &pageSize)
{
  m_state->m_decal=MWAWVec2i(pageSize[0]/2,pageSize[1]/2);
}

void PowerPoint7Graph::setSlideId(PowerPoint7Struct::SlideId const &id)
{
  m_state->m_actualSlideId=id;
}

void PowerPoint7Graph::setColorList(std::vector<MWAWColor> const &colorList)
{
  m_state->m_colorList=colorList;
}

////////////////////////////////////////////////////////////
//
// Intermediate level
//
////////////////////////////////////////////////////////////

bool PowerPoint7Graph::readGroup(int level, long lastPos)
{
  MWAWInputStreamPtr input=m_parserState->m_input;
  long pos=input->tell();

  libmwaw::DebugFile &ascFile = m_parserState->m_asciiFile;
  libmwaw::DebugStream f;
  PowerPoint7Struct::Zone header;
  if (!header.read(input,lastPos) || header.m_type!=3001) {
    MWAW_DEBUG_MSG(("PowerPoint7Graph::readGroup: can not find the zone header\n"));
    input->seek(pos, librevenge::RVNG_SEEK_SET);
    return false;
  }
  f << "Entries(Group)[" << level << "]:" << header;
  ascFile.addPos(pos);
  ascFile.addNote(f.str().c_str());

  auto actualGroup=m_state->m_actualGroup;
  m_state->setFrame(new PowerPoint7GraphInternal::FrameGroup);
  long endPos=pos+16+header.m_dataSize;
  while (input->tell()<endPos) {
    pos=input->tell();
    auto cType=int(input->readULong(2));
    input->seek(pos, librevenge::RVNG_SEEK_SET);

    bool done=false;
    switch (cType) {
    case 2000:
      done=m_mainParser->readContainerList(level+1,endPos);
      break;
    case 3000:
      done=m_mainParser->readZone3000(level+1,endPos);
      break;
    case 3002:
      done=readGroupAtom(level+1,endPos);
      break;
    default:
      done=m_mainParser->readZone(level+1,endPos);
      if (done) {
        MWAW_DEBUG_MSG(("PowerPoint7Graph::readGroup: find unexpected zone %d\n", cType));
      }
      break;
    }
    if (done) continue;
    MWAW_DEBUG_MSG(("PowerPoint7Graph::readGroup: can not read some data\n"));
    ascFile.addPos(pos);
    ascFile.addNote("Group:###extra");
    input->seek(endPos, librevenge::RVNG_SEEK_SET);
    break;
  }
  m_state->m_actualGroup=actualGroup;
  return true;
}

bool PowerPoint7Graph::readGroupAtom(int level, long lastPos)
{
  MWAWInputStreamPtr input=m_parserState->m_input;
  long pos=input->tell();

  libmwaw::DebugFile &ascFile = m_parserState->m_asciiFile;
  libmwaw::DebugStream f;
  PowerPoint7Struct::Zone header;
  if (!header.read(input,lastPos) || header.m_type!=3002) {
    MWAW_DEBUG_MSG(("PowerPoint7Graph::readGroupAtom: can not find the zone header\n"));
    input->seek(pos, librevenge::RVNG_SEEK_SET);
    return false;
  }
  f << "Entries(Group)[atom," << level << "]:" << header;
  if (header.m_dataSize!=4) {
    MWAW_DEBUG_MSG(("PowerPoint7Graph::readGroupAtom: find unexpected data size\n"));
    f << "###dataSz=" << header.m_dataSize << ",";
    if (header.m_dataSize)
      ascFile.addDelimiter(pos+16,'|');
    input->seek(header.m_dataSize, librevenge::RVNG_SEEK_CUR);
  }
  else {
    for (int i=0; i<2; ++i) {
      auto val=int(input->readULong(2));
      int const expected[]= {0x3b5b, 0x5000};
      if (val!=expected[i]) f << "f" << i << "=" << std::hex << val << std::dec << ",";
    }
  }
  ascFile.addPos(pos);
  ascFile.addNote(f.str().c_str());
  return true;
}

bool PowerPoint7Graph::readStyle(int level, long lastPos)
{
  MWAWInputStreamPtr input=m_parserState->m_input;
  long pos=input->tell();

  libmwaw::DebugFile &ascFile = m_parserState->m_asciiFile;
  libmwaw::DebugStream f;
  PowerPoint7Struct::Zone header;
  if (!header.read(input,lastPos) || header.m_type!=3005) {
    MWAW_DEBUG_MSG(("PowerPoint7Graph::readStyle: can not find the zone header\n"));
    input->seek(pos, librevenge::RVNG_SEEK_SET);
    return false;
  }
  f << "Entries(GraphStyle)[" << level << "]:" << header;
  if (header.m_dataSize!=0x38) {
    MWAW_DEBUG_MSG(("PowerPoint7Graph::readStyle: find unexpected data size\n"));
    f << "###dataSz=" << header.m_dataSize << ",";
    if (header.m_dataSize)
      ascFile.addDelimiter(pos+16,'|');
    input->seek(header.m_dataSize, librevenge::RVNG_SEEK_CUR);
    ascFile.addPos(pos);
    ascFile.addNote(f.str().c_str());
    return true;
  }

  MWAWGraphicStyle emptyStyle;
  MWAWGraphicStyle *style=m_state->m_actualFrame ? &m_state->m_actualFrame->m_style : &emptyStyle;
  int val;
  //
  // line
  //
  val=int(input->readLong(1));
  bool showLine=true;
  if (val==-1 || val==1) { // normally -1, but =1 in dual powerpoint 95 and 97 files
    showLine=false;
    f << "no[line],";
  }
  else if (val) f << "fl0=" << val << ",";
  auto dashId=int(input->readLong(1));
  switch (dashId) {
  case 0: // no dash
    break;
  case 1:
    f << "dot,";
    break;
  case 2:
    f << "dot[2x2],";
    break;
  case 3:
    f << "dot[4x2],";
    break;
  case 4:
    f << "dot[4,4,1,4],";
    break;
  default:
    f << "###dashId=" << dashId << ",";
  }
  val=int(input->readLong(1)); // 0
  if (val) f << "f0=" << val << ",";
  auto lineW=int(input->readLong(1));
  if (lineW>=1 && lineW<=9) {
    char const *wh[]= {"w=1", "w=2","w=4", "w=8", "w=16", "w=32",
                       "double", "double1x2", "double2x1", "triple1x2x1"
                      };
    f << wh[lineW] << ",";
  }
  else if (lineW) {
    MWAW_DEBUG_MSG(("PowerPoint7Graph::readStyle: find unexpected line style\n"));
    f << "##style[line]=" << lineW << ",";
  }
  unsigned char col[4];
  for (auto &c : col) c=static_cast<unsigned char>(input->readULong(1));
  MWAWColor lineColor=MWAWColor::black();
  if (col[3]==0xfe)
    lineColor=MWAWColor(col[0],col[1],col[2]);
  else if (col[3]<int(m_state->m_colorList.size()))
    lineColor=m_state->m_colorList[size_t(col[3])];
  else {
    // normally can happen one time at the beginning
    if (m_state->m_actualSlideId.isValid()) {
      MWAW_DEBUG_MSG(("PowerPoint7Graph::readStyle: can not find the line color\n"));
      f << "##";
    }
    f << "color[lineId]=" << int(col[3]) << ",";
  }
  if (!lineColor.isBlack())
    f << "color[line]=" << lineColor<< ",";
  if (!showLine)
    style->m_lineWidth=0;
  else {
    int lineWidth=1;
    MWAWBorder border;
    if (lineW>=1&&lineW<=9) {
      int const lWidth[]= {1, 2, 3, 6, 8, 10, 3, 4, 4, 6};
      lineWidth=lWidth[lineW];
      switch (lineW) {
      case 6:
        border.m_type=MWAWBorder::Double;
        break;
      case 7:
        border.m_type=MWAWBorder::Double;
        border.m_widthsList.push_back(1);
        border.m_widthsList.push_back(0);
        border.m_widthsList.push_back(2);
        break;
      case 8:
        border.m_type=MWAWBorder::Double;
        border.m_widthsList.push_back(2);
        border.m_widthsList.push_back(0);
        border.m_widthsList.push_back(1);
        break;
      case 9:
        border.m_type=MWAWBorder::Triple;
        border.m_widthsList.push_back(1);
        border.m_widthsList.push_back(0);
        border.m_widthsList.push_back(2);
        border.m_widthsList.push_back(0);
        border.m_widthsList.push_back(1);
        break;
      default:
        break;
      }
    }
    style->m_lineWidth=float(lineWidth);
    border.m_width=double(lineWidth);
    style->setBorders(0xF, border);

    style->m_lineColor=border.m_color=lineColor;
    switch (dashId) {
    case 0: // no dash
    default: // problem
      break;
    case 1:
      style->m_lineDashWidth.resize(2,float(lineWidth));
      break;
    case 2:
      style->m_lineDashWidth.resize(2,float(2*lineWidth));
      break;
    case 3:
      style->m_lineDashWidth.resize(2,float(4*lineWidth));
      break;
    case 4:
      style->m_lineDashWidth.resize(4,float(2*lineWidth));
      style->m_lineDashWidth[2]=float(lineWidth);
      break;
    }
  }

  for (int i=0; i<2; ++i) { // 0
    val=int(input->readLong(2));
    if (val) f << "f" << i+1 << "=" << val << ",";
  }

  //
  // surface
  //
  val=int(input->readLong(1));
  bool showSurf=true;
  if (val==-1 || val==1) {
    showSurf=false;
    f << "no[surf],";
  }
  else if (val) f << "fl1=" << val << ",";
  auto surfType=int(input->readLong(1));
  switch (surfType) {
  case 1: // color
    break;
  case 2:
    f << "background,";
    break;
  case 3:
    f << "transparent[semi],";
    break;
  case 4:
    f << "pattern,";
    break;
  case 5:
    f << "gradient,";
    break;
  case 6:
    f << "picture,";
    break;
  case 7:
    f << "background[picture],";
    break;
  default:
    MWAW_DEBUG_MSG(("PowerPoint7Graph::readStyle: find unexpected surface type\n"));
    f << "##surf[type]=" << surfType << ",";
    break;
  }
  int patGradId=0, gradType=0, gradColorMapId=0, subGradientId=0;
  for (int i=0; i<6; ++i) { // fl3=0|78-b7,fl4=0|1|4,then small number
    val=int(input->readULong(1));
    if (!val) continue;
    switch (i) {
    case 2:
      patGradId=val;
      f << "patGrad[id]=" << val << ",";
      break;
    case 3:
      subGradientId=val;
      f << "grad[subId]=" << val << ",";
      break;
    case 4:
      gradColorMapId=val;
      f << "grad[colorMap]=" << val << ",";
      break;
    case 5: // 0: one color, 1: two color, 2: preset
      gradType=val;
      if (val==2)
        f << "gradType=preset,";
      else if (val!=1)
        f << "###gradType=" << val << ",";
      break;
    default:
      f << "fl" << i+3 << "=" << std::hex << val << std::dec << ",";
    }
  }
  MWAWColor surfColors[2]= {MWAWColor::white(), MWAWColor::black()};
  for (int c=0; c<2; ++c) {
    for (auto &co : col) co=static_cast<unsigned char>(input->readULong(1));
    if (col[3]==0xfe)
      surfColors[c]=MWAWColor(col[0],col[1],col[2]);
    else if (col[3]<int(m_state->m_colorList.size()))
      surfColors[c]=m_state->m_colorList[size_t(col[3])];
    else {
      // normally can happen one time at the beginning
      if (m_state->m_actualSlideId.isValid()) {
        MWAW_DEBUG_MSG(("PowerPoint7Graph::readStyle: can not find the surface color\n"));
        f << "##";
      }
      f << "color" << c << "[surf]=" << int(col[3]) << ",";
    }
    if ((c==0 && !surfColors[c].isWhite()) || (c==1 && !surfColors[c].isBlack()))
      f << "color" << c << "[surf]=" << surfColors[c] << ",";
  }
  if (showSurf) {
    switch (surfType) {
    case 1:
      style->setSurfaceColor(surfColors[0]);
      break;
    case 2:
      if (!m_state->m_colorList.empty())
        style->setSurfaceColor(m_state->m_colorList[0]);
      break;
    case 3:
      style->setSurfaceColor(surfColors[0], 0.5);
      break;
    case 4: {
      MWAWGraphicStyle::Pattern pattern;
      if (m_state->getPattern(patGradId+1, pattern)) {
        pattern.m_colors[0]=surfColors[1];
        pattern.m_colors[1]=surfColors[0];
        MWAWColor color;
        if (pattern.getUniqueColor(color))
          style->setSurfaceColor(color);
        else
          style->setPattern(pattern);
      }
      break;
    }
    case 5: {
      style->m_gradientStopList.resize(0);
      MWAWColor colors[]= {surfColors[0],surfColors[1]};
      if (gradType==2 && gradColorMapId>=0 && gradColorMapId<=15) {
        uint32_t const defColors[]= {
          0xff, 0xff0000, // early sunset
          0xff, 0xffff00, // late sunset
          0, 0x80, // night fall
          0xff, 0xffffff, // daybreak
          0xfff8dc, 0xd284bc, // parchment
          0xfff8dc, 0xbf8f8f, // Mahogany
          0x80, 0x808080, // fog
          0xffffff, 0xff00, // moss
          0xff, 0x80, // ocean
          0xffff00, 0xff0000, // fire
          0xff00ff, 0xffff00, // rainbow
          0xffffff, 0xffff00, // gold
          0xffff00, 0x808000, // brass
          0xffffff, 0x808080, // chrome
          0xffffff, 0x808080, // silver
          0xff, 0x80, // sapphire
        };
        colors[0]=defColors[2*gradColorMapId];
        colors[1]=defColors[2*gradColorMapId+1];
      }
      if (patGradId>=1 && patGradId<=4) {
        if (subGradientId<2) {
          style->m_gradientType=MWAWGraphicStyle::G_Linear;
          for (int c=0; c < 2; ++c)
            style->m_gradientStopList.push_back(MWAWGraphicStyle::GradientStop(float(c), (c==subGradientId)  ? colors[0] : colors[1]));
        }
        else {
          style->m_gradientType=MWAWGraphicStyle::G_Axial;
          for (int c=0; c < 3; ++c)
            style->m_gradientStopList.push_back(MWAWGraphicStyle::GradientStop(float(c)/2.f, ((c%2)==(subGradientId%2)) ? colors[0] : colors[1]));
        }
        float angles[]= {90,0,45,315};
        style->m_gradientAngle=angles[patGradId-1];
      }
      else if (patGradId==5) {
        style->m_gradientType=MWAWGraphicStyle::G_Rectangular;
        for (int c=0; c < 2; ++c)
          style->m_gradientStopList.push_back(MWAWGraphicStyle::GradientStop(float(c), c==0 ? colors[0] : colors[1]));
        style->m_gradientPercentCenter=MWAWVec2f(float(subGradientId&1),float(subGradientId<2 ? 0 : 1));
      }
      else if (patGradId==7) {
        style->m_gradientType=MWAWGraphicStyle::G_Rectangular;
        for (int c=0; c < 2; ++c)
          style->m_gradientStopList.push_back(MWAWGraphicStyle::GradientStop(float(c), ((c%2)==(subGradientId%2))  ? colors[0] : colors[1]));
      }
      else {
        MWAW_DEBUG_MSG(("PowerPoint7Graph::readStyle: find unknown gradient\n"));
        style->setSurfaceColor(colors[0]);
      }
      break;
    }
    case 6: {
      if (m_state->m_idToPictureMap.find(patGradId)==m_state->m_idToPictureMap.end()) {
        MWAW_DEBUG_MSG(("PowerPoint7Graph::readStyle: can not find picture %d\n", patGradId));
        break;
      }
      auto const &picture=m_state->m_idToPictureMap.find(patGradId)->second;
      MWAWGraphicStyle::Pattern pattern(picture.m_box.size(), picture.m_object.m_dataList[0], surfColors[0]);
      style->setPattern(pattern);
      break;
    }
    default:
      break;
    }
  }
  ascFile.addPos(pos);
  ascFile.addNote(f.str().c_str());

  //
  // the shadow
  //
  pos=input->tell();
  f.str("");
  f << "GraphStyle-A:";
  bool hasShadow=false;
  val=int(input->readLong(1));
  if (val==0) {
    f << "has[shadow],";
    hasShadow=true;
  }
  else if (val!=-1 && val!=1) f << "#has[shadow]=" << val << ",";
  auto shadowType=int(input->readLong(1)); // 1
  switch (shadowType) {
  case 1: // basic
    break;
  case 2:
    f << "semi[transparent],";
    break;
  default:
    MWAW_DEBUG_MSG(("PowerPoint7Graph::readStyle: find unexpected shadow type\n"));
    f << "##shadow[type]=" << shadowType << ",";
  }
  for (auto &c : col) c=static_cast<unsigned char>(input->readULong(1));
  MWAWColor shadowColor=MWAWColor::black();
  if (col[3]==0xfe)
    shadowColor=MWAWColor(col[0],col[1],col[2]);
  else if (col[3]<int(m_state->m_colorList.size()))
    shadowColor=m_state->m_colorList[size_t(col[3])];
  else {
    // normally can happen one time at the beginning
    if (m_state->m_actualSlideId.isValid()) {
      MWAW_DEBUG_MSG(("PowerPoint7Graph::readStyle: can not find the shadow color\n"));
      f << "##";
    }
    f << "color[shadowId]=" << int(col[3]) << ",";
  }
  if (!shadowColor.isBlack())
    f << "color[shadow]=" << shadowColor<< ",";
  val=int(input->readLong(2)); // 0|bd
  if (val) f << "f1=" << val << ",";
  float shadowDepl[2]= {6,6};
  for (int i=0; i<2; ++i) {
    long depl=input->readLong(4);
    if (depl==48) continue;
    shadowDepl[i]=float(depl)/8.f;
    f << "depl[" << (i==0 ? "right" : "bottom") << "]=" << shadowDepl[i] << ",";
  }
  if (hasShadow) {
    style->setShadowColor(shadowColor, shadowType==2 ? 0.5f : 1.f);
    style->m_shadowOffset=MWAWVec2f(shadowDepl[0],shadowDepl[1]);
  }
  val=int(input->readULong(1)); // 0|1|4
  if (val) f << "f2=" << val << ",";
  val=int(input->readULong(2)); // 0|62XX
  if (val) f << "f3=" << std::hex << val << std::dec << ",";
  val=int(input->readULong(1)); // 0|1|50
  if (val) f << "f4=" << std::hex << val << std::dec << ",";
  val=int(input->readLong(2));
  if (val) f << "rot=" << float(val)/16.f << ",";
  val=int(input->readULong(2)); // 0
  if (val) f << "f5=" << val << ",";
  val=int(input->readULong(1));
  if (val&0x1) {
    if (m_state->m_actualFrame) m_state->m_actualFrame->m_flip[0]=true;
    f << "flipX,";
  }
  if (val&0x2) {
    if (m_state->m_actualFrame) m_state->m_actualFrame->m_flip[1]=true;
    f << "flipY,";
  }
  val&=0xfc;
  if (val) f << "fl1=" << std::hex << val << std::dec << ",";
  val=int(input->readULong(2));
  if (val) f << "f5=" << std::hex << val << std::dec << ",";
  val=int(input->readULong(1)); // 0
  if (val) f << "fl2=" << val << ",";
  input->seek(pos+28, librevenge::RVNG_SEEK_SET);
  ascFile.addPos(pos);
  ascFile.addNote(f.str().c_str());

  return true;
}

bool PowerPoint7Graph::readLineArrows(int level, long lastPos)
{
  MWAWInputStreamPtr input=m_parserState->m_input;
  long pos=input->tell();

  libmwaw::DebugFile &ascFile = m_parserState->m_asciiFile;
  libmwaw::DebugStream f;
  PowerPoint7Struct::Zone header;
  if (!header.read(input,lastPos) || header.m_type!=3007) {
    MWAW_DEBUG_MSG(("PowerPoint7Graph::readLineArrows: can not find the zone header\n"));
    input->seek(pos, librevenge::RVNG_SEEK_SET);
    return false;
  }
  MWAWGraphicStyle emptyStyle;
  MWAWGraphicStyle *style=m_state->m_actualFrame ? &m_state->m_actualFrame->m_style : &emptyStyle;
  f << "Entries(GraphLine)[arrows," << level << "]:" << header;
  if (header.m_dataSize!=2) {
    MWAW_DEBUG_MSG(("PowerPoint7Graph::readLineArrows: find unexpected data size\n"));
    f << "###dataSz=" << header.m_dataSize << ",";
    if (header.m_dataSize)
      ascFile.addDelimiter(pos+16,'|');
    input->seek(header.m_dataSize, librevenge::RVNG_SEEK_CUR);
  }
  else {
    for (int i=0; i<2; ++i) { // f0=0|1, f1=0|1
      auto val=int(input->readULong(1));
      if (!val) continue;
      MWAWGraphicStyle::Arrow arrow;
      if (m_state->getArrow(val, arrow))
        style->m_arrows[i]=arrow;
      f << "arrow[" << (i==0 ? "start" : "end") << "]=" << val << ",";
    }
  }
  ascFile.addPos(pos);
  ascFile.addNote(f.str().c_str());
  return true;
}

bool PowerPoint7Graph::readRect(int level, long lastPos)
{
  MWAWInputStreamPtr input=m_parserState->m_input;
  long pos=input->tell();

  libmwaw::DebugFile &ascFile = m_parserState->m_asciiFile;
  libmwaw::DebugStream f;
  PowerPoint7Struct::Zone header;
  if (!header.read(input,lastPos) || header.m_type!=3008) {
    MWAW_DEBUG_MSG(("PowerPoint7Graph::readRect: can not find the zone header\n"));
    input->seek(pos, librevenge::RVNG_SEEK_SET);
    return false;
  }
  f << "Entries(GraphRect)[" << level << "]:" << header;
  switch (header.m_values[3]) {
  case 16:
    f << "type=16,";
    break;
  case 19: // basic
    break;
  case 28:
    f << "background,";
    break;
  default:
    MWAW_DEBUG_MSG(("PowerPoint7Graph::readRect: find unknow type\n"));
    f << "##type=" << header.m_values[3] << ",";
    break;
  }
  ascFile.addPos(pos);
  ascFile.addNote(f.str().c_str());

  m_state->setFrame(new PowerPoint7GraphInternal::FrameRect);
  auto &frame=*m_state->m_actualFrame;
  if (header.m_values[3]==28)
    frame.m_isBackground=true;
  long endPos=pos+16+header.m_dataSize;
  while (input->tell()<endPos) {
    pos=input->tell();
    auto cType=int(input->readULong(2));
    input->seek(pos, librevenge::RVNG_SEEK_SET);

    bool done=false;
    switch (cType) {
    case 3005:
      done=readStyle(level+1,endPos);
      break;
    case 3009:
      done=readRectAtom(level+1,endPos);
      break;
    case 3036:
      done=readZoneFlags(level+1,endPos);
      break;
    case 4001:
      if (frame.m_textId!=-1) {
        MWAW_DEBUG_MSG(("PowerPoint7Graph::readRect: already find some text zone\n"));
      }
      done=m_mainParser->readStyleTextPropAtom(level+1,endPos,frame.m_textId);
      break;
    case 4014: {
      PowerPoint7Struct::SlideId sId;
      done=m_mainParser->readOutlineTextProps9Atom(level+1,endPos,frame.m_pictureId,sId);
      break;
    }
    case 4072:
      done=m_mainParser->readZone4072(level+1,endPos);
      break;
    case 5000:
      done=readZone5000(level+1,endPos);
      break;
    default:
      done=m_mainParser->readZone(level+1,endPos);
      if (done) {
        MWAW_DEBUG_MSG(("PowerPoint7Graph::readRect: find unexpected zone %d\n", cType));
      }
      break;
    }
    if (done) continue;
    MWAW_DEBUG_MSG(("PowerPoint7Graph::readRect: can not read some data\n"));
    ascFile.addPos(pos);
    ascFile.addNote("GraphRect:###extra");
    input->seek(endPos, librevenge::RVNG_SEEK_SET);
    break;
  }
  m_state->resetFrame();
  return true;
}

bool PowerPoint7Graph::readRectAtom(int level, long lastPos)
{
  MWAWInputStreamPtr input=m_parserState->m_input;
  long pos=input->tell();

  libmwaw::DebugFile &ascFile = m_parserState->m_asciiFile;
  libmwaw::DebugStream f;
  PowerPoint7Struct::Zone header;
  if (!header.read(input,lastPos) || header.m_type!=3009) {
    MWAW_DEBUG_MSG(("PowerPoint7Graph::readRectAtom: can not find the zone header\n"));
    input->seek(pos, librevenge::RVNG_SEEK_SET);
    return false;
  }
  f << "Entries(GraphRect)[atom," << level << "]:" << header;
  if (header.m_dataSize!=0x28) {
    MWAW_DEBUG_MSG(("PowerPoint7Graph::readRectAtom: find unexpected data size\n"));
    f << "###dataSz=" << header.m_dataSize << ",";
    if (header.m_dataSize)
      ascFile.addDelimiter(pos+16,'|');
    input->seek(header.m_dataSize, librevenge::RVNG_SEEK_CUR);
    ascFile.addPos(pos);
    ascFile.addNote(f.str().c_str());
    return true;
  }

  auto frame=m_state->m_actualFrame;
  auto type=int(input->readLong(1));
  if (frame) frame->m_subType=type;
  switch (type) {
  case -3:
    f << "rect,";
    break;
  case -2:
    f << "rectOval,";
    break;
  case -1:
    f << "circle,";
    break;
  default: // >=1 && <=? : the shape id
    f << "type=" << type << ",";
    break;
  }
  auto val=int(input->readULong(1));
  if (val!=0xff) { // unsure
    switch ((val>>5)&3) {
    case 0:
      break;
    case 2:
      f << "flipX,";
      break;
    case 3:
      f << "flipY,";
      break;
    default:
      f << "##flip=1,";
      break;
    }
    val&=0x9f;
    if (val) f << "##flip[other]=" << std::hex << val << std::dec << ",";
  }
  for (int i=0; i<3; ++i) {
    val=int(input->readLong(2));
    int const expected[]= {-1,0,0};
    if (val!=expected[i]) f << "f" << i << "=" << val << ",";
  }
  for (int i=0; i<4; ++i) {
    val=int(input->readULong(1));
    if (val) f << "fl" << i << "=" << std::hex << val << std::dec << ",";
  }
  int dim[4];
  for (auto &d : dim) d=int(input->readLong(4));
  MWAWBox2i dimension(MWAWVec2i(dim[0],dim[1]),MWAWVec2i(dim[2],dim[3]));
  if (frame) frame->m_dimension=dimension;
  f << "dim=" << dimension << ",";
  val=int(input->readLong(2));
  if (val) {
    if (frame) frame->m_rotation=float(val)/16.f;
    f << "rot=" << float(val)/16.f << ",";
  }
  for (int i=0; i<2; ++i) {
    val=int(input->readLong(i==0 ? 2 : 4));
    int const expected[]= {0,-3};
    if (val!=expected[i])
      f << "f" << i+6 << "=" << val << ",";
  }
  val=int(input->readULong(1));
  if (val==1)
    f << "has[anchor],";
  else if (val)
    f << "##has[anchor]=" << val << ",";
  for (int i=0; i<3; ++i) { // fl0=0-1, fl1=0|76-b8
    val=int(input->readULong(1));
    if (val) f << "fl" << i+4 << "=" << std::hex << val << std::dec << ",";
  }
  input->seek(pos+16+header.m_dataSize, librevenge::RVNG_SEEK_SET);
  ascFile.addPos(pos);
  ascFile.addNote(f.str().c_str());

  return true;
}

bool PowerPoint7Graph::readPlaceholderContainer(int level, long lastPos)
{
  MWAWInputStreamPtr input=m_parserState->m_input;
  long pos=input->tell();

  libmwaw::DebugFile &ascFile = m_parserState->m_asciiFile;
  libmwaw::DebugStream f;
  PowerPoint7Struct::Zone header;
  if (!header.read(input,lastPos) || header.m_type!=3010) {
    MWAW_DEBUG_MSG(("PowerPoint7Graph::readPlaceholderContainer: can not find the zone header\n"));
    input->seek(pos, librevenge::RVNG_SEEK_SET);
    return false;
  }
  f << "Entries(Placeholder)[container," << level << "]:" << header;
  ascFile.addPos(pos);
  ascFile.addNote(f.str().c_str());

  m_state->setFrame(new PowerPoint7GraphInternal::FramePlaceholder);
  auto &frame=*m_state->m_actualFrame;
  long endPos=pos+16+header.m_dataSize;
  while (input->tell()<endPos) {
    pos=input->tell();
    auto cType=int(input->readULong(2));
    input->seek(pos, librevenge::RVNG_SEEK_SET);

    bool done=false;
    switch (cType) {
    case 3005:
      done=readStyle(level+1,endPos);
      break;
    case 3009:
      done=readRectAtom(level+1,endPos);
      break;
    case 3011:
      done=readPlaceholderAtom(level+1,endPos);
      break;
    case 3036:
      done=readZoneFlags(level+1,endPos);
      break;
    case 4001:
      if (frame.m_textId!=-1) {
        MWAW_DEBUG_MSG(("PowerPoint7GraphPlaceholderContainer::read: already find some text zone\n"));
      }
      done=m_mainParser->readStyleTextPropAtom(level+1,endPos,frame.m_textId);
      break;
    case 4014: {
      int pId;
      PowerPoint7Struct::SlideId sId;
      done=m_mainParser->readOutlineTextProps9Atom(level+1,endPos,pId,sId);
      break;
    }
    default:
      done=m_mainParser->readZone(level+1,endPos);
      if (done) {
        MWAW_DEBUG_MSG(("PowerPoint7Graph::readPlaceholderContainer: find unexpected zone %d\n", cType));
      }
      break;
    }
    if (done) continue;
    MWAW_DEBUG_MSG(("PowerPoint7Graph::readPlaceholderContainer: can not read some data\n"));
    ascFile.addPos(pos);
    ascFile.addNote("Placeholder:###extra");
    input->seek(endPos, librevenge::RVNG_SEEK_SET);
    break;
  }
  m_state->resetFrame();
  return true;
}

bool PowerPoint7Graph::readPlaceholderAtom(int level, long lastPos)
{
  MWAWInputStreamPtr input=m_parserState->m_input;
  long pos=input->tell();

  libmwaw::DebugFile &ascFile = m_parserState->m_asciiFile;
  libmwaw::DebugStream f;
  PowerPoint7Struct::Zone header;
  if (!header.read(input,lastPos) || header.m_type!=3011) {
    MWAW_DEBUG_MSG(("PowerPoint7Graph::readPlaceholderAtom: can not find the zone header\n"));
    input->seek(pos, librevenge::RVNG_SEEK_SET);
    return false;
  }
  f << "Entries(Placeholder)[atom," << level << "]:" << header;
  if (header.m_dataSize!=8) {
    MWAW_DEBUG_MSG(("PowerPoint7Graph::readPlaceholderAtom: find unexpected data size\n"));
    f << "###dataSz=" << header.m_dataSize << ",";
    if (header.m_dataSize)
      ascFile.addDelimiter(pos+16,'|');
    input->seek(header.m_dataSize, librevenge::RVNG_SEEK_CUR);
  }
  else {
    for (int i=0; i<4; ++i) { // f0 id?, f1=62, f2=0-6
      auto val=int(input->readULong(2));
      if (!val) continue;
      f << "f" << i << "=" << val << ",";
    }
  }
  ascFile.addPos(pos);
  ascFile.addNote(f.str().c_str());
  return true;
}

bool PowerPoint7Graph::readLine(int level, long lastPos)
{
  MWAWInputStreamPtr input=m_parserState->m_input;
  long pos=input->tell();

  libmwaw::DebugFile &ascFile = m_parserState->m_asciiFile;
  libmwaw::DebugStream f;
  PowerPoint7Struct::Zone header;
  if (!header.read(input,lastPos) || header.m_type!=3014) {
    MWAW_DEBUG_MSG(("PowerPoint7Graph::readLine: can not find the zone header\n"));
    input->seek(pos, librevenge::RVNG_SEEK_SET);
    return false;
  }
  f << "Entries(GraphLine)[" << level << "]:" << header;
  ascFile.addPos(pos);
  ascFile.addNote(f.str().c_str());

  m_state->setFrame(new PowerPoint7GraphInternal::Frame(PowerPoint7GraphInternal::Frame::Line));
  long endPos=pos+16+header.m_dataSize;
  while (input->tell()<endPos) {
    pos=input->tell();
    auto cType=int(input->readULong(2));
    input->seek(pos, librevenge::RVNG_SEEK_SET);

    bool done=false;
    switch (cType) {
    case 3005:
      done=readStyle(level+1,endPos);
      break;
    case 3007:
      done=readLineArrows(level+1,endPos);
      break;
    case 3015:
      done=readLineAtom(level+1,endPos);
      break;
    default:
      done=m_mainParser->readZone(level+1,endPos);
      if (done) {
        MWAW_DEBUG_MSG(("PowerPoint7Graph::readLine: find unexpected zone %d\n", cType));
      }
      break;
    }
    if (done) continue;
    MWAW_DEBUG_MSG(("PowerPoint7Graph::readLine: can not read some data\n"));
    ascFile.addPos(pos);
    ascFile.addNote("GraphLine:###extra");
    input->seek(endPos, librevenge::RVNG_SEEK_SET);
    break;
  }
  m_state->resetFrame();
  return true;
}

bool PowerPoint7Graph::readLineAtom(int level, long lastPos)
{
  MWAWInputStreamPtr input=m_parserState->m_input;
  long pos=input->tell();

  libmwaw::DebugFile &ascFile = m_parserState->m_asciiFile;
  libmwaw::DebugStream f;
  PowerPoint7Struct::Zone header;
  if (!header.read(input,lastPos) || header.m_type!=3015) {
    MWAW_DEBUG_MSG(("PowerPoint7Graph::readLineAtom: can not find the zone header\n"));
    input->seek(pos, librevenge::RVNG_SEEK_SET);
    return false;
  }
  f << "Entries(GraphLine)[atom," << level << "]:" << header;
  if (header.m_dataSize!=0x10) {
    MWAW_DEBUG_MSG(("PowerPoint7Graph::readLineAtom: find unexpected data size\n"));
    f << "###dataSz=" << header.m_dataSize << ",";
    if (header.m_dataSize)
      ascFile.addDelimiter(pos+16,'|');
    input->seek(header.m_dataSize, librevenge::RVNG_SEEK_CUR);
    ascFile.addPos(pos);
    ascFile.addNote(f.str().c_str());
    return true;
  }

  auto frame=m_state->m_actualFrame;
  int dim[4];
  for (auto &d : dim) d=int(input->readLong(4));
  MWAWBox2i dimension(MWAWVec2i(dim[0],dim[1]),MWAWVec2i(dim[2],dim[3]));
  if (frame) frame->m_dimension=dimension;
  f << "dim=" << dimension << ",";
  input->seek(pos+16+header.m_dataSize, librevenge::RVNG_SEEK_SET);
  ascFile.addPos(pos);
  ascFile.addNote(f.str().c_str());

  return true;
}

bool PowerPoint7Graph::readPolygon(int level, long lastPos)
{
  MWAWInputStreamPtr input=m_parserState->m_input;
  long pos=input->tell();

  libmwaw::DebugFile &ascFile = m_parserState->m_asciiFile;
  libmwaw::DebugStream f;
  PowerPoint7Struct::Zone header;
  if (!header.read(input,lastPos) || header.m_type!=3016) {
    MWAW_DEBUG_MSG(("PowerPoint7Graph::readPolygon: can not find the zone header\n"));
    input->seek(pos, librevenge::RVNG_SEEK_SET);
    return false;
  }
  f << "Entries(GraphPolygon)[" << level << "]:" << header;
  ascFile.addPos(pos);
  ascFile.addNote(f.str().c_str());

  auto *poly=new PowerPoint7GraphInternal::FramePolygon;
  m_state->setFrame(poly);
  long endPos=pos+16+header.m_dataSize;
  std::vector<MWAWVec2i> points;
  while (input->tell()<endPos) {
    pos=input->tell();
    auto cType=int(input->readULong(2));
    input->seek(pos, librevenge::RVNG_SEEK_SET);

    bool done=false;
    switch (cType) {
    case 3005:
      done=readStyle(level+1,endPos);
      break;
    case 3007:
      done=readLineArrows(level+1,endPos);
      break;
    case 3017:
      done=readPolygonAtom(level+1,endPos);
      break;
    case 3035:
      done=readPointList(level+1,endPos, poly->m_vertices);
      break;
    default:
      done=m_mainParser->readZone(level+1,endPos);
      if (done) {
        MWAW_DEBUG_MSG(("PowerPoint7Graph::readPolygon: find unexpected zone %d\n", cType));
      }
      break;
    }
    if (done) continue;
    MWAW_DEBUG_MSG(("PowerPoint7Graph::readPolygon: can not read some data\n"));
    ascFile.addPos(pos);
    ascFile.addNote("GraphPolygon:###extra");
    input->seek(endPos, librevenge::RVNG_SEEK_SET);
    break;
  }
  m_state->resetFrame();
  return true;
}

bool PowerPoint7Graph::readPolygonAtom(int level, long lastPos)
{
  MWAWInputStreamPtr input=m_parserState->m_input;
  long pos=input->tell();

  libmwaw::DebugFile &ascFile = m_parserState->m_asciiFile;
  libmwaw::DebugStream f;
  PowerPoint7Struct::Zone header;
  if (!header.read(input,lastPos) || header.m_type!=3017) {
    MWAW_DEBUG_MSG(("PowerPoint7Graph::readPolygonAtom: can not find the zone header\n"));
    input->seek(pos, librevenge::RVNG_SEEK_SET);
    return false;
  }
  f << "Entries(GraphPolygon)[atom," << level << "]:" << header;
  if (header.m_dataSize!=0x28) {
    MWAW_DEBUG_MSG(("PowerPoint7Graph::readPolygonAtom: find unexpected data size\n"));
    f << "###dataSz=" << header.m_dataSize << ",";
    if (header.m_dataSize)
      ascFile.addDelimiter(pos+16,'|');
    input->seek(header.m_dataSize, librevenge::RVNG_SEEK_CUR);
    ascFile.addPos(pos);
    ascFile.addNote(f.str().c_str());
    return true;
  }

  auto frame=m_state->m_actualFrame;
  int dim[4];
  for (auto &d : dim) d=int(input->readLong(4));
  MWAWBox2i dimension(MWAWVec2i(dim[0],dim[1]),MWAWVec2i(dim[2],dim[3]));
  if (frame) frame->m_dimension=dimension;
  f << "dim=" << dimension << ",";
  for (auto &d : dim) d=int(input->readLong(4));
  f << "dim2=" << MWAWBox2i(MWAWVec2i(dim[0],dim[1]),MWAWVec2i(dim[2],dim[3])) << ",";
  for (int i=0; i<4; ++i) { // f0=4,f2=dc01,f3=12
    int val=int(input->readLong(2));
    if (val)
      f << "f" << i << "=" << val << ",";
  }
  input->seek(pos+16+header.m_dataSize, librevenge::RVNG_SEEK_SET);
  ascFile.addPos(pos);
  ascFile.addNote(f.str().c_str());

  return true;
}

bool PowerPoint7Graph::readArc(int level, long lastPos)
{
  MWAWInputStreamPtr input=m_parserState->m_input;
  long pos=input->tell();

  libmwaw::DebugFile &ascFile = m_parserState->m_asciiFile;
  libmwaw::DebugStream f;
  PowerPoint7Struct::Zone header;
  if (!header.read(input,lastPos) || header.m_type!=3018) {
    MWAW_DEBUG_MSG(("PowerPoint7Graph::readArc: can not find the zone header\n"));
    input->seek(pos, librevenge::RVNG_SEEK_SET);
    return false;
  }
  f << "Entries(GraphArc)[" << level << "]:" << header;
  ascFile.addPos(pos);
  ascFile.addNote(f.str().c_str());

  m_state->setFrame(new PowerPoint7GraphInternal::FrameArc);
  long endPos=pos+16+header.m_dataSize;
  while (input->tell()<endPos) {
    pos=input->tell();
    auto cType=int(input->readULong(2));
    input->seek(pos, librevenge::RVNG_SEEK_SET);

    bool done=false;
    switch (cType) {
    case 3005:
      done=readStyle(level+1,endPos);
      break;
    case 3007:
      done=readLineArrows(level+1,endPos);
      break;
    case 3019:
      done=readArcAtom(level+1,endPos);
      break;
    default:
      done=m_mainParser->readZone(level+1,endPos);
      if (done) {
        MWAW_DEBUG_MSG(("PowerPoint7Graph::readArc: find unexpected zone %d\n", cType));
      }
      break;
    }
    if (done) continue;
    MWAW_DEBUG_MSG(("PowerPoint7Graph::readArc: can not read some data\n"));
    ascFile.addPos(pos);
    ascFile.addNote("GraphArc:###extra");
    input->seek(endPos, librevenge::RVNG_SEEK_SET);
    break;
  }
  m_state->resetFrame();
  return true;
}

bool PowerPoint7Graph::readArcAtom(int level, long lastPos)
{
  MWAWInputStreamPtr input=m_parserState->m_input;
  long pos=input->tell();

  libmwaw::DebugFile &ascFile = m_parserState->m_asciiFile;
  libmwaw::DebugStream f;
  PowerPoint7Struct::Zone header;
  if (!header.read(input,lastPos) || header.m_type!=3019) {
    MWAW_DEBUG_MSG(("PowerPoint7Graph::readArcAtom: can not find the zone header\n"));
    input->seek(pos, librevenge::RVNG_SEEK_SET);
    return false;
  }
  f << "Entries(GraphArc)[atom," << level << "]:" << header;
  if (header.m_dataSize!=0x20) {
    MWAW_DEBUG_MSG(("PowerPoint7Graph::readArcAtom: find unexpected data size\n"));
    f << "###dataSz=" << header.m_dataSize << ",";
    if (header.m_dataSize)
      ascFile.addDelimiter(pos+16,'|');
    input->seek(header.m_dataSize, librevenge::RVNG_SEEK_CUR);
    ascFile.addPos(pos);
    ascFile.addNote(f.str().c_str());
    return true;
  }

  auto frame=m_state->m_actualFrame;
  PowerPoint7GraphInternal::FrameArc *arc=nullptr;
  if (frame) arc=dynamic_cast<PowerPoint7GraphInternal::FrameArc *>(frame.get());
  int dim[4];
  for (auto &d : dim) d=int(input->readLong(4));
  MWAWBox2i dimension(MWAWVec2i(dim[0],dim[1]),MWAWVec2i(dim[2],dim[3]));
  if (frame) frame->m_dimension=dimension;
  f << "dim=" << dimension << ",";
  f << "angles=[";
  for (int i=0; i<2; ++i) { // find 1440,-1440
    float angle=float(input->readLong(4))/16.f;
    if (arc) arc->m_angles[i]=angle;
    f << angle << ",";
  }
  f << "],";
  auto val=int(input->readLong(2));
  if (val) {
    if (frame)
      frame->m_rotation=float(val)/16.0f;
    f << "rot=" << float(val)/16.0f << ",";
  }
  for (int i=0; i<3; ++i) { // f2=74
    val=int(input->readLong(2));
    if (val)
      f << "f" << i << "=" << val << ",";
  }
  input->seek(pos+16+header.m_dataSize, librevenge::RVNG_SEEK_SET);
  ascFile.addPos(pos);
  ascFile.addNote(f.str().c_str());

  return true;
}

bool PowerPoint7Graph::readPointList(int level, long lastPos, std::vector<MWAWVec2i> &points)
{
  MWAWInputStreamPtr input=m_parserState->m_input;
  long pos=input->tell();

  libmwaw::DebugFile &ascFile = m_parserState->m_asciiFile;
  libmwaw::DebugStream f;
  PowerPoint7Struct::Zone header;
  if (!header.read(input,lastPos) || header.m_type!=3035) {
    MWAW_DEBUG_MSG(("PowerPoint7Graph::readPointList: can not find the zone header\n"));
    input->seek(pos, librevenge::RVNG_SEEK_SET);
    return false;
  }
  f << "Entries(GraphPointList)[" << level << "]:" << header;
  int N=header.m_dataSize>=2 ? int(input->readLong(2)) : 0;
  if (8*N+2!=header.m_dataSize) {
    MWAW_DEBUG_MSG(("PowerPoint7Graph::readPointList: find unexpected data size\n"));
    f << "###dataSz=" << header.m_dataSize << ",";
    if (header.m_dataSize)
      ascFile.addDelimiter(pos+16,'|');
    input->seek(header.m_dataSize, librevenge::RVNG_SEEK_CUR);
    ascFile.addPos(pos);
    ascFile.addNote(f.str().c_str());
    return true;
  }
  f << "points=[";
  points.resize(size_t(N));
  for (auto &pt : points) {
    int dim[2];
    for (auto &d : dim) d=int(input->readLong(4));
    pt=MWAWVec2i(dim[0],dim[1]);
    f << pt << ",";
  }
  f << "],";
  input->seek(pos+16+header.m_dataSize, librevenge::RVNG_SEEK_SET);
  ascFile.addPos(pos);
  ascFile.addNote(f.str().c_str());

  return true;
}

bool PowerPoint7Graph::readZoneFlags(int level, long lastPos)
{
  MWAWInputStreamPtr input=m_parserState->m_input;
  long pos=input->tell();

  libmwaw::DebugFile &ascFile = m_parserState->m_asciiFile;
  libmwaw::DebugStream f;
  PowerPoint7Struct::Zone header;
  if (!header.read(input,lastPos) || header.m_type!=3036) {
    MWAW_DEBUG_MSG(("PowerPoint7Graph::readZoneFlags: can not find the zone header\n"));
    input->seek(pos, librevenge::RVNG_SEEK_SET);
    return false;
  }
  f << "Entries(GraphZone)[flags" << level << "]:" << header;
  if (header.m_dataSize!=0x24) {
    MWAW_DEBUG_MSG(("PowerPoint7Graph::readZoneFlags: find unexpected data size\n"));
    f << "###dataSz=" << header.m_dataSize << ",";
    if (header.m_dataSize)
      ascFile.addDelimiter(pos+16,'|');
    input->seek(header.m_dataSize, librevenge::RVNG_SEEK_CUR);
    ascFile.addPos(pos);
    ascFile.addNote(f.str().c_str());
    return true;
  }

  for (int i=0; i<18; ++i) { // f0=1,f2=2,f5=700,f6=a,f8=3,f16=f7=-1
    int val=int(input->readLong(2));
    if (val) f << "f" << i << "=" << val << ",";
  }
  input->seek(pos+16+header.m_dataSize, librevenge::RVNG_SEEK_SET);
  ascFile.addPos(pos);
  ascFile.addNote(f.str().c_str());

  return true;
}

////////////////////////////////////////////////////////////
// Picture
////////////////////////////////////////////////////////////
bool PowerPoint7Graph::readPictureList(int level, long lastPos)
{
  MWAWInputStreamPtr input=m_parserState->m_input;
  long pos=input->tell();

  libmwaw::DebugFile &ascFile = m_parserState->m_asciiFile;
  libmwaw::DebugStream f;
  PowerPoint7Struct::Zone header;
  if (!header.read(input,lastPos) || header.m_type!=2006) {
    MWAW_DEBUG_MSG(("PowerPoint7Graph::readPictureList: can not find the zone header\n"));
    input->seek(pos, librevenge::RVNG_SEEK_SET);
    return false;
  }
  f << "Entries(Picture)[list," << level << "]:" << header;
  ascFile.addPos(pos);
  ascFile.addNote(f.str().c_str());

  long endPos=pos+16+header.m_dataSize;
  PowerPoint7GraphInternal::Picture picture;
  while (input->tell()<endPos) {
    pos=input->tell();
    auto cType=int(input->readULong(2));
    input->seek(pos, librevenge::RVNG_SEEK_SET);

    bool done=false;
    switch (cType) {
    case 1027:
      done=readBitmapContainer(level+1,endPos,picture);
      break;
    case 2017: {
      int id;
      done=m_mainParser->readIdentifier(level+1,endPos,id,"Picture");
      if (!done || picture.isEmpty()) break;
      if (m_state->m_idToPictureMap.find(id)!=m_state->m_idToPictureMap.end()) {
        picture=PowerPoint7GraphInternal::Picture();
        MWAW_DEBUG_MSG(("PowerPoint7Graph::readPictureList: a picture %d is already defined\n", id));
        break;
      }
      m_state->m_idToPictureMap.insert(std::map<int, PowerPoint7GraphInternal::Picture>::value_type(id,picture));
      break;
    }
    case 2018:
      done=m_mainParser->readZoneNoData(level+1,endPos,"Picture","id,end");
      break;
    case 4028:
      done=readPictureContainer(level+1,endPos,picture);
      break;
    case 4043: // check me
      done=m_mainParser->readZoneNoData(level+1,endPos,"Picture","flags");
      break;
    default:
      done=m_mainParser->readZone(level+1,endPos);
      if (done) {
        MWAW_DEBUG_MSG(("PowerPoint7Graph::readPictureList: find unexpected zone %d\n", cType));
      }
      break;
    }
    if (done) continue;
    MWAW_DEBUG_MSG(("PowerPoint7Graph::readPictureList: can not read some data\n"));
    ascFile.addPos(pos);
    ascFile.addNote("Picture:###extra");
    input->seek(endPos, librevenge::RVNG_SEEK_SET);
    break;
  }
  return true;
}

bool PowerPoint7Graph::readBitmapContainer(int level, long lastPos, PowerPoint7GraphInternal::Picture &picture)
{
  MWAWInputStreamPtr input=m_parserState->m_input;
  long pos=input->tell();

  libmwaw::DebugFile &ascFile = m_parserState->m_asciiFile;
  libmwaw::DebugStream f;
  PowerPoint7Struct::Zone header;
  if (!header.read(input,lastPos) || header.m_type!=1027) {
    MWAW_DEBUG_MSG(("PowerPoint7Graph::readBitmapContainer: can not find the zone header\n"));
    input->seek(pos, librevenge::RVNG_SEEK_SET);
    return false;
  }
  f << "Entries(Bitmap)[container," << level << "]:" << header;
  ascFile.addPos(pos);
  ascFile.addNote(f.str().c_str());

  long endPos=pos+16+header.m_dataSize;
  while (input->tell()<endPos) {
    pos=input->tell();
    auto cType=int(input->readULong(2));
    input->seek(pos, librevenge::RVNG_SEEK_SET);

    bool done=false;
    switch (cType) {
    case 2012:
      done=readBitmap(level+1, endPos, picture.m_object, picture.m_box);
      break;
    case 3038:
      done=readBitmapFlag(level+1, endPos);
      break;
    case 4026: {
      int zId; // 130: picture name
      done=m_mainParser->readString(level+1, endPos, picture.m_name, zId, "Bitmap");
      break;
    }
    default:
      done=m_mainParser->readZone(level+1,endPos);
      if (done) {
        MWAW_DEBUG_MSG(("PowerPoint7Graph::readBitmapContainer: find unexpected zone %d\n", cType));
      }
      break;
    }
    if (done) continue;
    MWAW_DEBUG_MSG(("PowerPoint7Graph::readBitmapContainer: can not read some data\n"));
    ascFile.addPos(pos);
    ascFile.addNote("Bitmap:###extra");
    input->seek(endPos, librevenge::RVNG_SEEK_SET);
    break;
  }
  return true;
}

bool PowerPoint7Graph::readBitmap(int level, long lastPos, MWAWEmbeddedObject &object, MWAWBox2i &box)
{
  object=MWAWEmbeddedObject();
  MWAWInputStreamPtr input=m_parserState->m_input;
  long pos=input->tell();

  libmwaw::DebugFile &ascFile = m_parserState->m_asciiFile;
  libmwaw::DebugStream f;
  PowerPoint7Struct::Zone header;
  if (!header.read(input,lastPos) || header.m_type!=2012) {
    MWAW_DEBUG_MSG(("PowerPoint7Graph::readBitmap: can not find the zone header\n"));
    input->seek(pos, librevenge::RVNG_SEEK_SET);
    return false;
  }
  f << "Entries(Bitmap)[" << level << "]:" << header;
  if (header.m_dataSize<40) {
    MWAW_DEBUG_MSG(("PowerPoint7Graph::readBitmap: find unexpected data size\n"));
    f << "###dataSz=" << header.m_dataSize << ",";
    if (header.m_dataSize)
      ascFile.addDelimiter(pos+16,'|');
    input->seek(pos+16+header.m_dataSize, librevenge::RVNG_SEEK_SET);
    ascFile.addPos(pos);
    ascFile.addNote(f.str().c_str());
    return true;
  }
  // BITMAPINFOHEADER: normally with size 40, but can probably be longer
  auto headerSz=int(input->readLong(4));
  if (headerSz<40 || headerSz>=header.m_dataSize-16) {
    MWAW_DEBUG_MSG(("PowerPoint7Graph::readBitmap: find unexpected header size\n"));
    f << "###headerSz=" << headerSz << ",";
    if (header.m_dataSize)
      ascFile.addDelimiter(input->tell(),'|');
    if (16+headerSz<header.m_dataSize)
      ascFile.skipZone(pos+16+headerSz, pos+16+header.m_dataSize-1);
    input->seek(pos+16+header.m_dataSize, librevenge::RVNG_SEEK_SET);
    ascFile.addPos(pos);
    ascFile.addNote(f.str().c_str());
    return true;
  }
  int dim[2];
  for (auto &d : dim) d=int(input->readULong(4));
  f << "dim=" << MWAWVec2i(dim[0],dim[1]) << ",";
  auto val=int(input->readULong(2));
  if (val!=1) f << "num[planes]=" << val << ",";
  auto nbBytes=int(input->readULong(2));
  f << "nunBytes=" << nbBytes << ",";
  ascFile.addDelimiter(input->tell(),'|');
  input->seek(pos+16+32, librevenge::RVNG_SEEK_SET);
  auto nColors=int(input->readULong(4));
  if (nColors)
    f << "numColors=" << nColors << ",";
  else if (nbBytes<=8) {
    nColors=1;
    for (int b=0; b<=nbBytes; ++b)
      nColors<<=1;
  }
  if ((header.m_dataSize-16-headerSz)/4<=nColors) {
    MWAW_DEBUG_MSG(("PowerPoint7Graph::readBitmap: can not find the pixel data zone\n"));
    f << "###nColors,";
    ascFile.skipZone(pos+16+headerSz, pos+16+header.m_dataSize-1);
    input->seek(pos+16+header.m_dataSize, librevenge::RVNG_SEEK_SET);
    ascFile.addPos(pos);
    ascFile.addNote(f.str().c_str());
    return true;
  }

  // ok, let create a bmp file
  box=MWAWBox2i(MWAWVec2i(0,0),MWAWVec2i(dim[0],dim[1]));
  unsigned char bmHeader[14];
  bmHeader[0]='B';
  bmHeader[1]='M';
  auto fileSize=uint32_t(14+header.m_dataSize);
  for (int i=0; i<4; ++i, fileSize>>=8)
    bmHeader[i+2]=static_cast<unsigned char>(fileSize&0xFF);
  for (int i=0; i<4; ++i)
    bmHeader[i+6]=0;
  auto dataOffs=uint32_t(14+headerSz+4*nColors);
  for (int i=0; i<4; ++i, dataOffs>>=8)
    bmHeader[i+10]=static_cast<unsigned char>(dataOffs&0xFF);
  librevenge::RVNGBinaryData file(bmHeader,14);
  input->seek(pos+16, librevenge::RVNG_SEEK_SET);

  const unsigned char *readData;
  unsigned long sizeRead;
  if ((readData=input->read(static_cast<unsigned long>(header.m_dataSize), sizeRead)) != nullptr || long(sizeRead)==header.m_dataSize) {
    file.append(readData, sizeRead);
    object.add(file, "image/bmp");
#ifdef DEBUG_WITH_FILES
    static int volatile pictName = 0;
    libmwaw::DebugStream f2;
    f2 << "PICT-" << ++pictName << ".bmp";
    libmwaw::Debug::dumpFile(file, f2.str().c_str());
#endif
  }
  else {
    MWAW_DEBUG_MSG(("PowerPoint7Graph::readBitmap: can not reconstruct the final bmp file\n"));
    f << "###";
  }
  ascFile.skipZone(pos+16+headerSz, pos+16+header.m_dataSize-1);
  ascFile.addPos(pos);
  ascFile.addNote(f.str().c_str());
  input->seek(pos+16+header.m_dataSize, librevenge::RVNG_SEEK_SET);
  return true;
}

bool PowerPoint7Graph::readBitmapFlag(int level, long lastPos)
{
  MWAWInputStreamPtr input=m_parserState->m_input;
  long pos=input->tell();

  libmwaw::DebugFile &ascFile = m_parserState->m_asciiFile;
  libmwaw::DebugStream f;
  PowerPoint7Struct::Zone header;
  if (!header.read(input,lastPos) || header.m_type!=3038) {
    MWAW_DEBUG_MSG(("PowerPoint7Graph::readBitmapFlag: can not find the zone header\n"));
    input->seek(pos, librevenge::RVNG_SEEK_SET);
    return false;
  }
  f << "Entries(Bitmap)[flag," << level << "]:" << header;
  if (header.m_dataSize!=1) {
    MWAW_DEBUG_MSG(("PowerPoint7Graph::readBitmapFlag: find unexpected data size\n"));
    f << "###dataSz=" << header.m_dataSize << ",";
    if (header.m_dataSize)
      ascFile.addDelimiter(pos+16,'|');
    input->seek(header.m_dataSize, librevenge::RVNG_SEEK_CUR);
  }
  else {
    auto val=int(input->readULong(1));
    if (val==1)
      f << "on[disk],";
    else if (val)
      f << "bitmap[type]=" << val << ",";
  }
  ascFile.addPos(pos);
  ascFile.addNote(f.str().c_str());
  return true;
}

bool PowerPoint7Graph::readMetaFileContainer(int level, long lastPos, PowerPoint7GraphInternal::Picture &picture)
{
  MWAWInputStreamPtr input=m_parserState->m_input;
  long pos=input->tell();

  libmwaw::DebugFile &ascFile = m_parserState->m_asciiFile;
  libmwaw::DebugStream f;
  PowerPoint7Struct::Zone header;
  if (!header.read(input,lastPos) || header.m_type!=4037) {
    MWAW_DEBUG_MSG(("PowerPoint7Graph::readMetaFileContainer: can not find the zone header\n"));
    input->seek(pos, librevenge::RVNG_SEEK_SET);
    return false;
  }
  f << "Entries(MetaFile)[container," << level << "]:" << header;
  ascFile.addPos(pos);
  ascFile.addNote(f.str().c_str());

  long endPos=pos+16+header.m_dataSize;
  while (input->tell()<endPos) {
    pos=input->tell();
    auto cType=int(input->readULong(2));
    input->seek(pos, librevenge::RVNG_SEEK_SET);

    bool done=false;
    switch (cType) {
    case 4033:
      done=readMetaFile(level+1,endPos,picture.m_object);
      break;
    case 4038:
      done=readMetaFileBox(level+1,endPos,picture.m_box);
      break;
    default:
      done=m_mainParser->readZone(level+1,endPos);
      if (done) {
        MWAW_DEBUG_MSG(("PowerPoint7Graph::readMetaFileContainer: find unexpected zone %d\n", cType));
      }
      break;
    }
    if (done) continue;
    MWAW_DEBUG_MSG(("PowerPoint7Graph::readMetaFileContainer: can not read some data\n"));
    ascFile.addPos(pos);
    ascFile.addNote("MetaFile:###extra");
    input->seek(endPos, librevenge::RVNG_SEEK_SET);
    break;
  }
  return true;
}

bool PowerPoint7Graph::readMetaFile(int level, long lastPos, MWAWEmbeddedObject &object)
{
  object=MWAWEmbeddedObject();
  MWAWInputStreamPtr input=m_parserState->m_input;
  long pos=input->tell();

  libmwaw::DebugFile &ascFile = m_parserState->m_asciiFile;
  libmwaw::DebugStream f;
  PowerPoint7Struct::Zone header;
  if (!header.read(input,lastPos) || header.m_type!=4033) {
    MWAW_DEBUG_MSG(("PowerPoint7Graph::readMetaFile: can not find the zone header\n"));
    input->seek(pos, librevenge::RVNG_SEEK_SET);
    return false;
  }
  f << "Entries(MetaFile)[" << level << "]:" << header;
  if (header.m_dataSize<10) {
    MWAW_DEBUG_MSG(("PowerPoint7Graph::readMetaFile: find unexpected data size\n"));
    f << "###dataSz=" << header.m_dataSize << ",";
    if (header.m_dataSize)
      ascFile.addDelimiter(pos+16,'|');
    input->seek(header.m_dataSize, librevenge::RVNG_SEEK_CUR);
    ascFile.addPos(pos);
    ascFile.addNote(f.str().c_str());
    return true;
  }
  ascFile.skipZone(input->tell(), pos+16+header.m_dataSize-1);
  ascFile.addPos(pos);
  ascFile.addNote(f.str().c_str());
  librevenge::RVNGBinaryData file;
  input->readDataBlock(header.m_dataSize, file);
  object.add(file, "image/wmf");
#ifdef DEBUG_WITH_FILES
  static int volatile pictName = 0;
  f.str("");
  f << "PICT-" << ++pictName << ".wmf";
  libmwaw::Debug::dumpFile(file, f.str().c_str());
#endif
  input->seek(pos+16+header.m_dataSize, librevenge::RVNG_SEEK_SET);
  return true;
}

bool PowerPoint7Graph::readMetaFileBox(int level, long lastPos, MWAWBox2i &box)
{
  MWAWInputStreamPtr input=m_parserState->m_input;
  long pos=input->tell();

  libmwaw::DebugFile &ascFile = m_parserState->m_asciiFile;
  libmwaw::DebugStream f;
  PowerPoint7Struct::Zone header;
  if (!header.read(input,lastPos) || header.m_type!=4038) {
    MWAW_DEBUG_MSG(("PowerPoint7Graph::readMetaFileBox: can not find the zone header\n"));
    input->seek(pos, librevenge::RVNG_SEEK_SET);
    return false;
  }
  f << "Entries(MetaFile)[box," << level << "]:" << header;
  if (header.m_dataSize!=0x14) {
    MWAW_DEBUG_MSG(("PowerPoint7Graph::readMetaFileBox: find unexpected data size\n"));
    f << "###dataSz=" << header.m_dataSize << ",";
    if (header.m_dataSize)
      ascFile.addDelimiter(pos+16,'|');
    input->seek(header.m_dataSize, librevenge::RVNG_SEEK_CUR);
    ascFile.addPos(pos);
    ascFile.addNote(f.str().c_str());
    return true;
  }

  for (int i=0; i<2; ++i) { // f0=f00
    int val=int(input->readLong(2));
    if (val) f << "f" << i << "=" << val << ",";
  }
  int dim[4];
  for (auto &d : dim) d=int(input->readLong(4));
  box=MWAWBox2i(MWAWVec2i(dim[0],dim[1]),MWAWVec2i(dim[2],dim[3]));
  f << "box=" << box << ",";
  input->seek(pos+16+header.m_dataSize, librevenge::RVNG_SEEK_SET);
  ascFile.addPos(pos);
  ascFile.addNote(f.str().c_str());

  return true;
}

bool PowerPoint7Graph::readExternalOleObjectAtom(int level, long lastPos)
{
  MWAWInputStreamPtr input=m_parserState->m_input;
  long pos=input->tell();

  libmwaw::DebugFile &ascFile = m_parserState->m_asciiFile;
  libmwaw::DebugStream f;
  PowerPoint7Struct::Zone header;
  if (!header.read(input,lastPos) || header.m_type!=4035) {
    MWAW_DEBUG_MSG(("PowerPoint7Graph::readExternalOleObjectAtom: can not find the zone header\n"));
    input->seek(pos, librevenge::RVNG_SEEK_SET);
    return false;
  }
  f << "Entries(ExternalOleEmbed)[object," << level << "]:" << header;
  if (header.m_dataSize!=0x14) {
    MWAW_DEBUG_MSG(("PowerPoint7Graph::readExternalOleObjectAtom: find unexpected data size\n"));
    f << "###dataSz=" << header.m_dataSize << ",";
    if (header.m_dataSize)
      ascFile.addDelimiter(pos+16,'|');
    input->seek(header.m_dataSize, librevenge::RVNG_SEEK_CUR);
  }
  else {
    for (int i=0; i<10; ++i) { // f0=1, f6=0|1, f8=1|5
      auto val=int(input->readLong(2));
      if (!val) continue;
      f << "f"<< i << "=" << val << ",";
    }
  }
  ascFile.addPos(pos);
  ascFile.addNote(f.str().c_str());
  return true;
}

bool PowerPoint7Graph::readExternalOleEmbed(int level, long lastPos, int &id)
{
  id=-1;
  MWAWInputStreamPtr input=m_parserState->m_input;
  long pos=input->tell();

  libmwaw::DebugFile &ascFile = m_parserState->m_asciiFile;
  libmwaw::DebugStream f;
  PowerPoint7Struct::Zone header;
  if (!header.read(input,lastPos) || header.m_type!=4044) {
    MWAW_DEBUG_MSG(("PowerPoint7Graph::readExternalOleEmbed: can not find the zone header\n"));
    input->seek(pos, librevenge::RVNG_SEEK_SET);
    return false;
  }
  f << "Entries(ExternalOleEmbed)[list," << level << "]:" << header;
  ascFile.addPos(pos);
  ascFile.addNote(f.str().c_str());

  long endPos=pos+16+header.m_dataSize;
  while (input->tell()<endPos) {
    pos=input->tell();
    auto cType=int(input->readULong(2));
    input->seek(pos, librevenge::RVNG_SEEK_SET);

    bool done=false;
    switch (cType) {
    case 4026: {
      std::string string;
      int zId; // 0: Ole name, 2:prog, 3:type, 45: program
      done=m_mainParser->readString(level+1,endPos,string,zId,"ExternalOleEmbed");
      break;
    }
    case 4035:
      done=readExternalOleObjectAtom(level+1,endPos);
      break;
    case 4036:
      done=readPictureId(level+1,endPos,id);
      break;
    case 4045:
      done=readExternalOleEmbedAtom(level+1,endPos);
      break;
    default:
      done=m_mainParser->readZone(level+1,endPos);
      if (done) {
        MWAW_DEBUG_MSG(("PowerPoint7Graph::readExternalOleEmbed: find unexpected zone %d\n", cType));
      }
      break;
    }
    if (done) continue;
    MWAW_DEBUG_MSG(("PowerPoint7Graph::readExternalOleEmbed: can not read some data\n"));
    ascFile.addPos(pos);
    ascFile.addNote("ExternalOleEmbed:###extra");
    input->seek(endPos, librevenge::RVNG_SEEK_SET);
    break;
  }
  return true;
}

bool PowerPoint7Graph::readExternalOleEmbedAtom(int level, long lastPos)
{
  MWAWInputStreamPtr input=m_parserState->m_input;
  long pos=input->tell();

  libmwaw::DebugFile &ascFile = m_parserState->m_asciiFile;
  libmwaw::DebugStream f;
  PowerPoint7Struct::Zone header;
  if (!header.read(input,lastPos) || header.m_type!=4045) {
    MWAW_DEBUG_MSG(("PowerPoint7Graph::readExternalOleEmbedAtom: can not find the zone header\n"));
    input->seek(pos, librevenge::RVNG_SEEK_SET);
    return false;
  }
  f << "Entries(ExternalOleEmbed)[atom," << level << "]:" << header;
  if (header.m_dataSize!=0x8) {
    MWAW_DEBUG_MSG(("PowerPoint7Graph::readExternalOleEmbedAtom: find unexpected data size\n"));
    f << "###dataSz=" << header.m_dataSize << ",";
    if (header.m_dataSize)
      ascFile.addDelimiter(pos+16,'|');
    input->seek(header.m_dataSize, librevenge::RVNG_SEEK_CUR);
  }
  else {
    for (int i=0; i<4; ++i) { // f0=0|1, f2=1
      auto val=int(input->readLong(2));
      if (!val) continue;
      f << "f"<< i << "=" << val << ",";
    }
  }
  ascFile.addPos(pos);
  ascFile.addNote(f.str().c_str());
  return true;
}

bool PowerPoint7Graph::readPictureContainer(int level, long lastPos, PowerPoint7GraphInternal::Picture &picture)
{
  MWAWInputStreamPtr input=m_parserState->m_input;
  long pos=input->tell();

  libmwaw::DebugFile &ascFile = m_parserState->m_asciiFile;
  libmwaw::DebugStream f;
  PowerPoint7Struct::Zone header;
  if (!header.read(input,lastPos) || header.m_type!=4028) {
    MWAW_DEBUG_MSG(("PowerPoint7Graph::readPictureContainer: can not find the zone header\n"));
    input->seek(pos, librevenge::RVNG_SEEK_SET);
    return false;
  }
  f << "Entries(Picture)[container," << level << "]:" << header;
  ascFile.addPos(pos);
  ascFile.addNote(f.str().c_str());

  long endPos=pos+16+header.m_dataSize;
  while (input->tell()<endPos) {
    pos=input->tell();
    auto cType=int(input->readULong(2));
    input->seek(pos, librevenge::RVNG_SEEK_SET);

    bool done=false;
    switch (cType) {
    case 4037:
      done=readMetaFileContainer(level+1,endPos, picture);
      break;
    default:
      done=m_mainParser->readZone(level+1,endPos);
      if (done) {
        MWAW_DEBUG_MSG(("PowerPoint7Graph::readPictureContainer: find unexpected zone %d\n", cType));
      }
      break;
    }
    if (done) continue;
    MWAW_DEBUG_MSG(("PowerPoint7Graph::readPictureContainer: can not read some data\n"));
    ascFile.addPos(pos);
    ascFile.addNote("Picture:###extra");
    input->seek(endPos, librevenge::RVNG_SEEK_SET);
    break;
  }
  return true;
}

bool PowerPoint7Graph::readPictureId(int level, long lastPos, int &id)
{
  id=-1;
  MWAWInputStreamPtr input=m_parserState->m_input;
  long pos=input->tell();

  libmwaw::DebugFile &ascFile = m_parserState->m_asciiFile;
  libmwaw::DebugStream f;
  PowerPoint7Struct::Zone header;
  if (!header.read(input,lastPos) || header.m_type!=4036) {
    MWAW_DEBUG_MSG(("PowerPoint7Graph::readPictureId: can not find the zone header\n"));
    input->seek(pos, librevenge::RVNG_SEEK_SET);
    return false;
  }
  f << "Entries(Picture)[id," << level << "]:" << header;
  if (header.m_dataSize!=4) {
    MWAW_DEBUG_MSG(("PowerPoint7Graph::readPictureId: find unexpected data size\n"));
    f << "###dataSz=" << header.m_dataSize << ",";
    if (header.m_dataSize)
      ascFile.addDelimiter(pos+16,'|');
    input->seek(header.m_dataSize, librevenge::RVNG_SEEK_CUR);
    ascFile.addPos(pos);
    ascFile.addNote(f.str().c_str());
    return true;
  }
  id=int(input->readLong(4));
  if (id) f << "id=" << id << ",";
  ascFile.addPos(pos);
  ascFile.addNote(f.str().c_str());

  return true;
}

bool PowerPoint7Graph::readPictureIdContainer(int level, long lastPos, int &id)
{
  id=-1;
  MWAWInputStreamPtr input=m_parserState->m_input;
  long pos=input->tell();

  libmwaw::DebugFile &ascFile = m_parserState->m_asciiFile;
  libmwaw::DebugStream f;
  PowerPoint7Struct::Zone header;
  if (!header.read(input,lastPos) || header.m_type!=4053) {
    MWAW_DEBUG_MSG(("PowerPoint7Graph::readPictureIdContainer: can not find the zone header\n"));
    input->seek(pos, librevenge::RVNG_SEEK_SET);
    return false;
  }
  f << "Entries(Picture)[id,container," << level << "]:" << header;
  ascFile.addPos(pos);
  ascFile.addNote(f.str().c_str());

  long endPos=pos+16+header.m_dataSize;
  while (input->tell()<endPos) {
    pos=input->tell();
    auto cType=int(input->readULong(2));
    input->seek(pos, librevenge::RVNG_SEEK_SET);

    bool done=false;
    switch (cType) {
    case 4036:
      done=readPictureId(level+1,endPos,id);
      break;
    default:
      done=m_mainParser->readZone(level+1,endPos);
      if (done) {
        MWAW_DEBUG_MSG(("PowerPoint7Graph::readPictureIdContainer: find unexpected zone %d\n", cType));
      }
      break;
    }
    if (done) continue;
    MWAW_DEBUG_MSG(("PowerPoint7Graph::readPictureIdContainer: can not read some data\n"));
    ascFile.addPos(pos);
    ascFile.addNote("Picture:###extra");
    input->seek(endPos, librevenge::RVNG_SEEK_SET);
    break;
  }
  return true;
}

bool PowerPoint7Graph::readZone5000(int level, long lastPos)
{
  MWAWInputStreamPtr input=m_parserState->m_input;
  long pos=input->tell();

  libmwaw::DebugFile &ascFile = m_parserState->m_asciiFile;
  libmwaw::DebugStream f;
  PowerPoint7Struct::Zone header;
  if (!header.read(input,lastPos) || header.m_type!=5000) {
    MWAW_DEBUG_MSG(("PowerPoint7Graph::readZone5000: can not find the zone header\n"));
    input->seek(pos, librevenge::RVNG_SEEK_SET);
    return false;
  }
  f << "Entries(Zone5000B)[" << level << "]:" << header;
  ascFile.addPos(pos);
  ascFile.addNote(f.str().c_str());

  long endPos=pos+16+header.m_dataSize;
  while (input->tell()<endPos) {
    pos=input->tell();
    auto cType=int(input->readULong(2));
    input->seek(pos, librevenge::RVNG_SEEK_SET);

    bool done=false;
    switch (cType) {
    case 5001:
      done=readZone5000Header(level+1,endPos);
      break;
    case 5002:
      done=readZone5000Data(level+1,endPos);
      break;
    default:
      done=m_mainParser->readZone(level+1,endPos);
      if (done) {
        MWAW_DEBUG_MSG(("PowerPoint7Graph::readZone5000: find unexpected zone %d\n", cType));
      }
      break;
    }
    if (done) continue;
    MWAW_DEBUG_MSG(("PowerPoint7Graph::readZone5000: can not read some data\n"));
    ascFile.addPos(pos);
    ascFile.addNote("Zone5000B:###extra");
    input->seek(endPos, librevenge::RVNG_SEEK_SET);
    break;
  }
  return true;
}

bool PowerPoint7Graph::readZone5000Header(int level, long lastPos)
{
  MWAWInputStreamPtr input=m_parserState->m_input;
  long pos=input->tell();

  libmwaw::DebugFile &ascFile = m_parserState->m_asciiFile;
  libmwaw::DebugStream f;
  PowerPoint7Struct::Zone header;
  if (!header.read(input,lastPos) || header.m_type!=5001) {
    MWAW_DEBUG_MSG(("PowerPoint7Graph::readZone5000Header: can not find the zone header\n"));
    input->seek(pos, librevenge::RVNG_SEEK_SET);
    return false;
  }
  f << "Entries(Zone5000B)[header," << level << "]:" << header;
  if (header.m_dataSize!=4) {
    MWAW_DEBUG_MSG(("PowerPoint7Graph::readZone5000Header: find unexpected data size\n"));
    f << "###dataSz=" << header.m_dataSize << ",";
    if (header.m_dataSize)
      ascFile.addDelimiter(pos+16,'|');
    input->seek(header.m_dataSize, librevenge::RVNG_SEEK_CUR);
    ascFile.addPos(pos);
    ascFile.addNote(f.str().c_str());
    return true;
  }
  auto val=int(input->readLong(4));
  if (val!=4) f << "num[data]=" << val << ",";
  ascFile.addPos(pos);
  ascFile.addNote(f.str().c_str());

  return true;
}

bool PowerPoint7Graph::readZone5000Data(int level, long lastPos)
{
  MWAWInputStreamPtr input=m_parserState->m_input;
  long pos=input->tell();

  libmwaw::DebugFile &ascFile = m_parserState->m_asciiFile;
  libmwaw::DebugStream f;
  PowerPoint7Struct::Zone header;
  if (!header.read(input,lastPos) || header.m_type!=5002) {
    MWAW_DEBUG_MSG(("PowerPoint7Graph::readZone5000Data: can not find the zone header\n"));
    input->seek(pos, librevenge::RVNG_SEEK_SET);
    return false;
  }
  f << "Entries(Zone5000B)[data," << level << "]:" << header;
  ascFile.addPos(pos);
  ascFile.addNote(f.str().c_str());

  long endPos=pos+16+header.m_dataSize;
  while (input->tell()<endPos) {
    pos=input->tell();
    auto cType=int(input->readULong(2));
    input->seek(pos, librevenge::RVNG_SEEK_SET);

    bool done=false;
    switch (cType) {
    case 4026: {
      std::string string;
      int zId;
      done=m_mainParser->readString(level+1,endPos,string,zId,"Zone5000B");
      break;
    }
    default:
      done=m_mainParser->readZone(level+1,endPos);
      if (done) {
        MWAW_DEBUG_MSG(("PowerPoint7Graph::readZone5000: find unexpected zone %d\n", cType));
      }
      break;
    }
    if (done) continue;
    MWAW_DEBUG_MSG(("PowerPoint7Graph::readZone5000: can not read some data\n"));
    ascFile.addPos(pos);
    ascFile.addNote("Zone5000B:###extra");
    input->seek(endPos, librevenge::RVNG_SEEK_SET);
    break;
  }
  return true;
}

////////////////////////////////////////////////////////////
// send data
////////////////////////////////////////////////////////////

bool PowerPoint7Graph::sendSlide(PowerPoint7Struct::SlideId const &id, bool sendBackground)
{
  MWAWPresentationListenerPtr listener=m_parserState->m_presentationListener;
  if (!listener) {
    MWAW_DEBUG_MSG(("PowerPoint7Graph::sendSlide: can not find the listener\n"));
    return false;
  }
  std::vector<int> textIdList;
  if (m_state->m_idToFrameMap.find(id)!=m_state->m_idToFrameMap.end()) {
    std::vector<std::shared_ptr<PowerPoint7GraphInternal::Frame> > const &frames=m_state->m_idToFrameMap.find(id)->second;
    for (auto fram : frames) {
      if (!fram) continue;
      if (!sendBackground && fram->m_isBackground) continue;
      sendFrame(*fram, id.m_isMaster);
    }
  }
  // check if the slide has some note
  if (id.m_isMaster) return true;
  PowerPoint7Struct::SlideId noteId=id;
  noteId.m_inNotes=true;
  if (m_state->m_idToFrameMap.find(noteId)==m_state->m_idToFrameMap.end())
    return true;
  auto const &frames=m_state->m_idToFrameMap.find(noteId)->second;
  for (auto fram : frames) {
    if (!fram) continue;
    if (!sendBackground && fram->m_isBackground) continue;
    fram->getTextZoneList(textIdList);
  }
  if (textIdList.empty())
    return true;

  MWAWPosition pos(MWAWVec2f(0,0), MWAWVec2f(200,200), librevenge::RVNG_POINT);
  pos.m_anchorTo = MWAWPosition::Page;
  MWAWSubDocumentPtr subdoc(new PowerPoint7GraphInternal::SubDocument(*this, m_parserState->m_input,textIdList));
  listener->insertSlideNote(pos, subdoc);
  return true;
}

bool PowerPoint7Graph::sendFrame(PowerPoint7GraphInternal::Frame const &frame, bool master)
{
  frame.m_isSent=true;
  if (master && frame.m_type==PowerPoint7GraphInternal::Frame::Placeholder)
    return true;
  MWAWListenerPtr listener=m_parserState->m_presentationListener;
  if (!listener) {
    MWAW_DEBUG_MSG(("PowerPoint7Graph::sendFrame: can not find the listener\n"));
    return false;
  }
  MWAWBox2f fBox(1.f/8.f*MWAWVec2f(frame.m_dimension[0]+m_state->m_decal),
                 1.f/8.f*MWAWVec2f(frame.m_dimension[1]+m_state->m_decal));
  if (frame.m_textId>=0) {
    MWAWPosition pos(fBox[0], fBox.size(), librevenge::RVNG_POINT);
    pos.m_anchorTo = MWAWPosition::Page;
    MWAWSubDocumentPtr subdoc(new PowerPoint7GraphInternal::SubDocument(*this, m_parserState->m_input, frame.m_textId));
    listener->insertTextBox(pos, subdoc, frame.m_style);
    return true;
  }
  if (frame.m_pictureId>=0) {
    if (m_state->m_idToPictureMap.find(frame.m_pictureId)==m_state->m_idToPictureMap.end()) {
      MWAW_DEBUG_MSG(("PowerPoint7Graph::sendFrame: can not find the picture %d\n", frame.m_pictureId));
      return false;
    }
    MWAWPosition pos(fBox[0], fBox.size(), librevenge::RVNG_POINT);
    pos.m_anchorTo = MWAWPosition::Page;
    if (frame.m_isBackground) pos.m_wrapping = MWAWPosition::WBackground;
    listener->insertPicture(pos, m_state->m_idToPictureMap.find(frame.m_pictureId)->second.m_object);
    return true;
  }
  MWAWGraphicShape shape;
  switch (frame.m_type) {
  case PowerPoint7GraphInternal::Frame::Arc: {
    auto const *arc= dynamic_cast<PowerPoint7GraphInternal::FrameArc const *>(&frame);
    if (!arc) {
      MWAW_DEBUG_MSG(("PowerPoint7Graph::sendFrame: can not find the arc\n"));
      return false;
    }
    if (!arc->updateShape(fBox, shape)) return false;
    if (frame.m_rotation<0||frame.m_rotation>0)
      shape=shape.rotate(-frame.m_rotation, fBox.center());
    break;
  }
  case PowerPoint7GraphInternal::Frame::Line:
    shape=MWAWGraphicShape::line(fBox[0], fBox[1]);
    break;
  case PowerPoint7GraphInternal::Frame::Group: {
    auto const *group=dynamic_cast<PowerPoint7GraphInternal::FrameGroup const *>(&frame);
    if (!group) {
      MWAW_DEBUG_MSG(("PowerPoint7Graph::sendFrame: can not find the group\n"));
      return false;
    }
    if (group->m_child.empty())
      return true;
    MWAWPosition pos(fBox[0], fBox.size(), librevenge::RVNG_POINT);
    pos.m_anchorTo = MWAWPosition::Page;
    listener->openGroup(pos);
    for (auto child : group->m_child) {
      if (!child) continue;
      sendFrame(*child, master);
    }
    listener->closeGroup();
    return true;
  }
  case PowerPoint7GraphInternal::Frame::Polygon: {
    auto const *poly=dynamic_cast<PowerPoint7GraphInternal::FramePolygon const *>(&frame);
    if (!poly) {
      MWAW_DEBUG_MSG(("PowerPoint7Graph::sendFrame: can not find the polygon\n"));
      return false;
    }
    if (!poly->updateShape(fBox, shape)) return false;
    break;
  }
  case PowerPoint7GraphInternal::Frame::Rect:
    if (frame.m_subType>=0) {
      if (!m_state->getCustomShape(frame.m_subType, shape))
        return false;
      if (frame.m_flip[0]||frame.m_flip[1]) {
        shape.translate(MWAWVec2f(-0.5f,-0.5f));
        if (frame.m_flip[0]) shape.scale(MWAWVec2f(-1,1));
        if (frame.m_flip[1]) shape.scale(MWAWVec2f(1,-1));
        shape.translate(MWAWVec2f(0.5f,0.5f));
      }
      shape.scale(fBox.size());
      shape.translate(fBox[0]);
      if (frame.m_rotation<0||frame.m_rotation>0)
        shape=shape.rotate(-frame.m_rotation, fBox.center());
      break;
    }
    switch (frame.m_subType) {
    case -1:
      shape=MWAWGraphicShape::circle(fBox);
      break;
    case -2:
      shape=MWAWGraphicShape::rectangle(fBox, MWAWVec2f(3,3));
      break;
    case -3:
      shape=MWAWGraphicShape::rectangle(fBox);
      break;
    default:
      return false;
    }
    if (frame.m_rotation<0||frame.m_rotation>0)
      shape=shape.rotate(-frame.m_rotation, fBox.center());
    break;
  case PowerPoint7GraphInternal::Frame::Placeholder:
  case PowerPoint7GraphInternal::Frame::Unknown:
  default:
    return false;
  }

  MWAWBox2f box=shape.getBdBox();
  MWAWPosition pos(box[0], box.size(), librevenge::RVNG_POINT);
  pos.m_anchorTo = MWAWPosition::Page;
  if (frame.m_isBackground) pos.m_wrapping = MWAWPosition::WBackground;
  listener->insertShape(pos, shape, frame.m_style);
  return true;
}

bool PowerPoint7Graph::sendText(int textId)
{
  return m_mainParser->sendText(textId);
}
// vim: set filetype=cpp tabstop=2 shiftwidth=2 cindent autoindent smartindent noexpandtab: