/* -*- 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 #include #include #include #include #include #include #include "MWAWGraphicShape.hxx" #include "MWAWGraphicStyle.hxx" #include "MWAWHeader.hxx" #include "MWAWParagraph.hxx" #include "MWAWPictData.hxx" #include "MWAWPosition.hxx" #include "MWAWPrinter.hxx" #include "MWAWRSRCParser.hxx" #include "MWAWGraphicListener.hxx" #include "MWAWSubDocument.hxx" #include "BeagleWksStructManager.hxx" #include "BeagleWksDRParser.hxx" /** Internal: the structures of a BeagleWksDRParser */ namespace BeagleWksDRParserInternal { //! Internal: the shape of BeagleWksDRParser struct Shape { //! constructor Shape() : m_type(-1) , m_box() , m_shape() , m_entry() , m_dataSize(0) , m_style() , m_font() , m_justify(MWAWParagraph::JustificationLeft) , m_interline(1) , m_extra("") { } //! operator<< friend std::ostream &operator<<(std::ostream &o, Shape const &shape); //! the shape type int m_type; //! the shape bdbox MWAWBox2f m_box; //! the graphic shape ( for line, rect, ...) MWAWGraphicShape m_shape; //! the textbox or picture entry MWAWEntry m_entry; //! the data size long m_dataSize; // style part //! the style MWAWGraphicStyle m_style; //! the textbox font MWAWFont m_font; //! the textbox justification MWAWParagraph::Justification m_justify; //! the interline in percent double m_interline; //! extra data std::string m_extra; }; std::ostream &operator<<(std::ostream &o, Shape const &shape) { switch (shape.m_type) { case -1: break; case 1: o << "rect,"; break; case 2: o << "circle,"; break; case 3: o << "line,"; break; case 4: o << "rectOval,"; break; case 5: o << "arc,"; break; case 6: o << "poly,"; break; case 7: o << "textbox,"; break; case 8: o << "group,"; break; case 0xa: o << "poly[hand],"; break; case 0xb: o << "picture,"; break; default: o << "#type=" << shape.m_type << ","; break; } if (shape.m_box.size()[0]>0 || shape.m_box.size()[1]>0) o << "box=" << shape.m_box << ","; o << shape.m_style << ","; if (shape.m_dataSize) o << "size[data]=" << shape.m_dataSize << ","; o << shape.m_extra; return o; } //////////////////////////////////////// //! Internal: the state of a BeagleWksDRParser struct State { //! constructor State() : m_graphicBegin(-1) , m_typeEntryMap() , m_colorList() , m_patternList() , m_shapeList() , m_actPage(0) , m_numPages(0) , m_headerHeight(0) , m_footerHeight(0) { m_numShapes[0]=m_numShapes[1]=0; m_shapesBegin[0]=m_shapesBegin[1]=0; } /** the graphic begin position */ long m_graphicBegin; /** the shape definitions and the unknown */ long m_shapesBegin[2]; /** the type entry map */ std::multimap m_typeEntryMap; /** the colors list */ std::vector m_colorList; /** the pattern list */ std::vector m_patternList; /** the number of shapes: positions and definitions */ int m_numShapes[2]; /** the list of shapes */ std::vector m_shapeList; int m_actPage /** the actual page */, m_numPages /** the number of page of the final document */; int m_headerHeight /** the header height if known */, m_footerHeight /** the footer height if known */; }; //////////////////////////////////////// //! Internal: the subdocument of a BeagleWksDRParser class SubDocument final : public MWAWSubDocument { public: SubDocument(BeagleWksDRParser &pars, MWAWInputStreamPtr &input, int zoneId) : MWAWSubDocument(&pars, input, MWAWEntry()) , m_id(zoneId) {} //! destructor ~SubDocument() final {} //! operator!= bool operator!=(MWAWSubDocument const &doc) const final { if (MWAWSubDocument::operator!=(doc)) return true; auto const *sDoc = dynamic_cast(&doc); if (!sDoc) return true; if (m_id != sDoc->m_id) return true; return false; } //! the parser function void parse(MWAWListenerPtr &listener, libmwaw::SubDocumentType type) final; protected: //! the subdocument id int m_id; private: SubDocument(SubDocument const &orig) = delete; SubDocument &operator=(SubDocument const &orig) = delete; }; void SubDocument::parse(MWAWListenerPtr &listener, libmwaw::SubDocumentType) { if (!listener || !listener->canWriteText()) { MWAW_DEBUG_MSG(("BeagleWksDRParserInternal::SubDocument::parse: no listener\n")); return; } if (!m_parser) { MWAW_DEBUG_MSG(("BeagleWksDRParserInternal::SubDocument::parse: no parser\n")); return; } long pos = m_input->tell(); static_cast(m_parser)->sendText(m_id); m_input->seek(pos, librevenge::RVNG_SEEK_SET); } } //////////////////////////////////////////////////////////// // constructor/destructor, ... //////////////////////////////////////////////////////////// BeagleWksDRParser::BeagleWksDRParser(MWAWInputStreamPtr const &input, MWAWRSRCParserPtr const &rsrcParser, MWAWHeader *header) : MWAWGraphicParser(input, rsrcParser, header) , m_state() , m_structureManager() { init(); } BeagleWksDRParser::~BeagleWksDRParser() { } void BeagleWksDRParser::init() { resetGraphicListener(); setAsciiName("main-1"); m_state.reset(new BeagleWksDRParserInternal::State); m_structureManager.reset(new BeagleWksStructManager(getParserState())); // reduce the margin (in case, the page is not defined) getPageSpan().setMargins(0.1); } MWAWInputStreamPtr BeagleWksDRParser::rsrcInput() { return getRSRCParser()->getInput(); } libmwaw::DebugFile &BeagleWksDRParser::rsrcAscii() { return getRSRCParser()->ascii(); } //////////////////////////////////////////////////////////// // position and height //////////////////////////////////////////////////////////// MWAWVec2f BeagleWksDRParser::getPageLeftTop() const { return MWAWVec2f(float(getPageSpan().getMarginLeft()), float(getPageSpan().getMarginTop()+m_state->m_headerHeight/72.0)); } //////////////////////////////////////////////////////////// // new page //////////////////////////////////////////////////////////// void BeagleWksDRParser::newPage(int number) { if (number <= m_state->m_actPage || number > m_state->m_numPages) return; while (m_state->m_actPage < number) { m_state->m_actPage++; if (!getGraphicListener() || m_state->m_actPage == 1) continue; getGraphicListener()->insertBreak(MWAWGraphicListener::PageBreak); } } //////////////////////////////////////////////////////////// // the parser //////////////////////////////////////////////////////////// void BeagleWksDRParser::parse(librevenge::RVNGDrawingInterface *docInterface) { if (!getInput().get() || !checkHeader(nullptr)) throw(libmwaw::ParseException()); bool ok = false; try { // create the asciiFile ascii().setStream(getInput()); ascii().open(asciiName()); checkHeader(nullptr); ok = createZones(); if (ok) { createDocument(docInterface); sendPageFrames(); sendPictures(); } ascii().reset(); } catch (...) { MWAW_DEBUG_MSG(("BeagleWksDRParser::parse: exception catched when parsing\n")); ok = false; } resetGraphicListener(); if (!ok) throw(libmwaw::ParseException()); } //////////////////////////////////////////////////////////// // create the document //////////////////////////////////////////////////////////// void BeagleWksDRParser::createDocument(librevenge::RVNGDrawingInterface *documentInterface) { if (!documentInterface) return; if (getGraphicListener()) { MWAW_DEBUG_MSG(("BeagleWksDRParser::createDocument: listener already exist\n")); return; } // update the page m_state->m_actPage = 0; // create the page list int numPages = 1; m_state->m_numPages = numPages; std::vector pageList; MWAWPageSpan ps(getPageSpan()); ps.setPageSpan(numPages); pageList.push_back(ps); MWAWGraphicListenerPtr listen(new MWAWGraphicListener(*getParserState(), pageList, documentInterface)); setGraphicListener(listen); listen->startDocument(); } //////////////////////////////////////////////////////////// // // Intermediate level // //////////////////////////////////////////////////////////// bool BeagleWksDRParser::createZones() { readRSRCZones(); MWAWInputStreamPtr input = getInput(); if (input->seek(66, librevenge::RVNG_SEEK_SET) || !readPrintInfo()) return false; long pos = input->tell(); if (!input->checkPosition(pos+70)) { MWAW_DEBUG_MSG(("BeagleWksDRParser::createZones: the file can not contains zones\n")); return false; } // now read the list of zones libmwaw::DebugStream f; input->seek(pos, librevenge::RVNG_SEEK_SET); f << "Entries(Zones):"; for (int i=0; i<7; ++i) { // checkme: at least 2 zones, i=2 is not a zone, maybe 7 MWAWEntry entry; entry.setBegin(input->readLong(4)); entry.setLength(input->readLong(4)); entry.setId(static_cast(input->readLong(2))); if (entry.length()==0) continue; entry.setType(i==1?"Frame":"Unknown"); f << entry.type() << "[" << entry.id() << "]=" << std::hex << entry.begin() << "<->" << entry.end() << ","; if (!entry.valid() || !input->checkPosition(entry.end())) { f << "###"; if (i<2) { MWAW_DEBUG_MSG(("BeagleWksDRParser::createZones: can not read the header zone, stop\n")); ascii().addPos(pos); ascii().addNote(f.str().c_str()); return false; } if (i!=2) { MWAW_DEBUG_MSG(("BeagleWksDRParser::createZones: can not zones entry %d\n",i)); } continue; } m_state->m_typeEntryMap.insert (std::multimap::value_type(entry.type(),entry)); } ascii().addPos(pos); ascii().addNote(f.str().c_str()); // now parse the different zones auto it=m_state->m_typeEntryMap.find("FontNames"); if (it!=m_state->m_typeEntryMap.end()) m_structureManager->readFontNames(it->second); it=m_state->m_typeEntryMap.find("Frame"); if (it!=m_state->m_typeEntryMap.end()) m_structureManager->readFrame(it->second); // now parse the different zones for (auto eIt : m_state->m_typeEntryMap) { MWAWEntry const &entry=eIt.second; if (entry.isParsed()) continue; f.str(""); f << "Entries(" << entry.type() << ")[" << entry.id() << "]:"; ascii().addPos(entry.begin()); ascii().addNote(f.str().c_str()); ascii().addPos(entry.end()); ascii().addNote("_"); } input->seek(m_state->m_graphicBegin, librevenge::RVNG_SEEK_SET); pos=input->tell(); if (!readGraphicHeader()) { MWAW_DEBUG_MSG(("BeagleWksDRParser::createZones: can not read the graphic header\n")); ascii().addPos(pos); ascii().addNote("Entries(DrHeader):###"); return false; } pos=input->tell(); if (m_state->m_shapesBegin[0]m_shapesBegin[0]) { ascii().addPos(pos); ascii().addNote("_"); } pos=input->tell(); if (m_state->m_shapesBegin[1]m_shapesBegin[1]) { ascii().addPos(pos); ascii().addNote("_"); } if (!input->isEnd()) { MWAW_DEBUG_MSG(("BeagleWksDRParser::createZones: find some extra data\n")); ascii().addPos(input->tell()); ascii().addNote("Entries(ZoneEnd)"); } return true; } bool BeagleWksDRParser::readRSRCZones() { auto rsrcParser = getRSRCParser(); if (!rsrcParser) return true; auto &entryMap = rsrcParser->getEntriesMap(); // the 1 zone char const *zNames[] = {"wPos", "DMPF" }; for (int z = 0; z < 2; ++z) { auto it = entryMap.lower_bound(zNames[z]); while (it != entryMap.end()) { if (it->first != zNames[z]) break; MWAWEntry const &entry = it++->second; switch (z) { case 0: // 1001 m_structureManager->readwPos(entry); break; case 1: // find in one file with id=4661 6a1f 4057 m_structureManager->readFontStyle(entry); break; /* find also - edpt: see sendPicture - DMPP: the paragraph style - sect and alis: position?, alis=filesystem alias(dir, filename, path...) */ default: break; } } } return true; } //////////////////////////////////////////////////////////// // read the graphic data //////////////////////////////////////////////////////////// bool BeagleWksDRParser::readGraphicHeader() { MWAWInputStreamPtr input = getInput(); long pos = input->tell(); if (!input->checkPosition(pos+112)) return false; libmwaw::DebugStream f; f << "Entries(DrHeader):"; auto val=static_cast(input->readLong(2)); if (val) f << "f0=" << val << ","; val=static_cast(input->readLong(2)); if (val!=4) f << "f1=" << val << ","; m_state->m_numShapes[0]=static_cast(input->readULong(2)); f << "num[shapesPos]=" << m_state->m_numShapes[0] << ","; for (int i=0; i<8; ++i) { static int const expected[]= {0, 0x1144, 0, 7, 0, 0x5a8, 0, 0xfcc }; val=static_cast(input->readLong(2)); if (val != expected[i]) f << "f" << i+2 << "=" << val << ","; } m_state->m_numShapes[1]=static_cast(input->readULong(2)); f << "num[shapesDef]=" << m_state->m_numShapes[1] << ","; for (int i=0; i<2; ++i) { m_state->m_shapesBegin[i]= pos+input->readLong(4); f << "ptr" << i << "=" << std::hex << m_state->m_shapesBegin[i] << std::dec << ","; if (!input->checkPosition(m_state->m_shapesBegin[i])) { MWAW_DEBUG_MSG(("BeagleWksDRParser::readGraphicHeader: the shapes pointers seems bad\n")); f << "###"; m_state->m_shapesBegin[i]=0; } } for (int i=0; i<2; ++i) { val=static_cast(input->readLong(2)); if (val!=2*i) f << "g" << i << "=" << val << ","; } int dim[4]; for (auto &i : dim) i=static_cast(input->readULong(2)); f << "dim=" << dim[1] << "x" << dim[0] << "<->" << dim[3] << "x" << dim[2] << ","; // checkme: followed by some flag? ascii().addDelimiter(input->tell(),'|'); ascii().addPos(pos); ascii().addNote(f.str().c_str()); input->seek(pos+62, librevenge::RVNG_SEEK_SET); // DOME: the actual format, must be read with the same code than readShapeDefinitions pos=input->tell(); f.str(""); f << "DrHeader-style:"; BeagleWksDRParserInternal::Shape shape; if (!readStyle(shape)) f << "###"; f << shape; ascii().addPos(pos); ascii().addNote(f.str().c_str()); input->seek(pos+50, librevenge::RVNG_SEEK_SET); if (!readPatterns() || !readColors() || !readArrows() || !readShapePositions()) return false; return true; } // read the colors bool BeagleWksDRParser::readColors() { MWAWInputStreamPtr input = getInput(); long pos = input->tell(); libmwaw::DebugStream f; f << "Entries(Color):"; if (!input->checkPosition(pos+16)) { f << "###"; MWAW_DEBUG_MSG(("BeagleWksDRParser::readColors: the header size seems too short\n")); ascii().addPos(pos); ascii().addNote(f.str().c_str()); return false; } /* checkme: {N0,N1,N2} is probably {num defined, max defined, first free} but in which order? */ auto maxN=static_cast(input->readULong(2)); f << "N0=" << maxN << ","; auto val=static_cast(input->readULong(2)); if (val!=maxN) f << "N1=" << val << ","; if (val>maxN) maxN=val; auto N=static_cast(input->readULong(2)); f << "N=" << N << ","; val=static_cast(input->readULong(2)); if (val!=6) f << "f0=" << val << ","; val=static_cast(input->readULong(2)); if (val!=maxN) f << "N2=" << val << ","; if (val>maxN) maxN=val; auto fSz=static_cast(input->readULong(2)); f << "fSz=" << fSz << ","; auto dSz=long(input->readULong(4)); if (!input->checkPosition(pos+16+dSz) || fSz<10 || !(dSz/fSz==N && dSz%fSz==0)) { f << "###"; MWAW_DEBUG_MSG(("BeagleWksDRParser::readColors: the color size seems bad\n")); ascii().addPos(pos); ascii().addNote(f.str().c_str()); return false; } ascii().addPos(pos); ascii().addNote(f.str().c_str()); m_state->m_colorList.resize(size_t(maxN)); for (int i=0; itell(); if (i>=maxN) { ascii().addPos(pos); ascii().addNote("_"); input->seek(pos+fSz, librevenge::RVNG_SEEK_SET); continue; } f.str(""); f << "Color-" << i << ":"; for (int j=0; j<2; ++j) { // f0=0, f1=small number: rsrcId ? val=static_cast(input->readLong(2)); if (val) f<<"f" << j << "=" << val << ","; } unsigned char color[3]; for (auto &c : color) c = static_cast(input->readULong(2)/256); m_state->m_colorList[size_t(i)]=MWAWColor(color[0], color[1],color[2]); f << "col=" << m_state->m_colorList[size_t(i)] << ","; ascii().addPos(pos); ascii().addNote(f.str().c_str()); input->seek(pos+fSz, librevenge::RVNG_SEEK_SET); } return true; } // read the patterns bool BeagleWksDRParser::readPatterns() { MWAWInputStreamPtr input = getInput(); long pos = input->tell(); libmwaw::DebugStream f; f << "Entries(Pattern):"; if (!input->checkPosition(pos+16)) { f << "###"; MWAW_DEBUG_MSG(("BeagleWksDRParser::readPatterns: the header size seems too short\n")); ascii().addPos(pos); ascii().addNote(f.str().c_str()); return false; } auto maxN=static_cast(input->readULong(2)); f << "N0=" << maxN << ","; auto val=static_cast(input->readULong(2)); if (val!=maxN) f << "N1=" << val << ","; if (val>maxN) maxN=val; auto N=static_cast(input->readULong(2)); f << "N=" << N << ","; val=static_cast(input->readULong(2)); if (val!=6) f << "f0=" << val << ","; val=static_cast(input->readULong(2)); if (val!=maxN) f << "N2=" << val << ","; if (val>maxN) maxN=val; auto fSz=static_cast(input->readULong(2)); f << "fSz=" << fSz << ","; auto dSz=long(input->readULong(4)); if (!input->checkPosition(pos+16+dSz) || fSz<10 || N>dSz/fSz) { f << "###"; MWAW_DEBUG_MSG(("BeagleWksDRParser::readPatterns: the pattern size seems bad\n")); ascii().addPos(pos); ascii().addNote(f.str().c_str()); return false; } ascii().addPos(pos); ascii().addNote(f.str().c_str()); m_state->m_patternList.resize(size_t(maxN)); for (int i=0; itell(); if (i>=maxN) { ascii().addPos(pos); ascii().addNote("_"); input->seek(pos+fSz, librevenge::RVNG_SEEK_SET); continue; } f.str(""); f << "Pattern-" << i << ":"; val=static_cast(input->readLong(2)); // always 0? if (val) f << "f0=" << val << ","; MWAWGraphicStyle::Pattern pat; pat.m_dim=MWAWVec2i(8,8); pat.m_data.resize(8); for (auto &data : pat.m_data) data=static_cast(input->readULong(1)); m_state->m_patternList[size_t(i)]=pat; f << "pat=[" << pat << "],"; ascii().addPos(pos); ascii().addNote(f.str().c_str()); input->seek(pos+fSz, librevenge::RVNG_SEEK_SET); } return true; } // read a unknown zone: maybe the arrow definition bool BeagleWksDRParser::readArrows() { MWAWInputStreamPtr input = getInput(); long pos = input->tell(); libmwaw::DebugStream f; f << "Entries(Arrows):"; if (!input->checkPosition(pos+16)) { f << "###"; MWAW_DEBUG_MSG(("BeagleWksDRParser::readArrows: the header size seems too short\n")); ascii().addPos(pos); ascii().addNote(f.str().c_str()); return false; } auto maxN=static_cast(input->readULong(2)); f << "N0=" << maxN << ","; auto val=static_cast(input->readULong(2)); if (val!=maxN) f << "N1=" << val << ","; if (val>maxN) maxN=val; auto N=static_cast(input->readULong(2)); f << "N=" << N << ","; val=static_cast(input->readULong(2)); if (val!=6) f << "f0=" << val << ","; val=static_cast(input->readULong(2)); if (val!=maxN) f << "N2=" << val << ","; if (val>maxN) maxN=val; auto fSz=static_cast(input->readULong(2)); f << "fSz=" << fSz << ","; auto dSz=long(input->readULong(4)); if (!input->checkPosition(pos+16+dSz) || fSz<0x3c || N>dSz/fSz) { f << "###"; MWAW_DEBUG_MSG(("BeagleWksDRParser::readArrows: the pattern size seems bad\n")); ascii().addPos(pos); ascii().addNote(f.str().c_str()); return false; } ascii().addPos(pos); ascii().addNote(f.str().c_str()); for (int i=0; itell(); if (i>=maxN) { ascii().addPos(pos); ascii().addNote("_"); input->seek(pos+fSz, librevenge::RVNG_SEEK_SET); continue; } f.str(""); f << "Arrows-" << i << ":"; ascii().addPos(pos); ascii().addNote(f.str().c_str()); input->seek(pos+fSz, librevenge::RVNG_SEEK_SET); } return true; } // read the shape positions bool BeagleWksDRParser::readShapePositions() { MWAWInputStreamPtr input = getInput(); long pos = input->tell(); libmwaw::DebugStream f; if (m_state->m_numShapes[0]<0 || !input->checkPosition(pos+m_state->m_numShapes[0]*20)) { MWAW_DEBUG_MSG(("BeagleWksDRParser::readShapePositions: can not read the shape positions\n")); ascii().addPos(pos); ascii().addNote("Entries(ShapePos):###"); return false; } m_state->m_shapeList.resize(size_t(m_state->m_numShapes[0])); for (int i=0; im_numShapes[0]; ++i) { BeagleWksDRParserInternal::Shape &shape= m_state->m_shapeList[size_t(i)]; pos=input->tell(); f.str(""); f << "Entries(ShapePos)[" << i << "]:"; auto val=static_cast(input->readULong(2)); if (val!=i) f << "#id=" << val << ","; val=static_cast(input->readULong(1)); if (val&0x8) { f << "locked,"; val &= 0xF7; } if (val!=0x10) f << "flags=" << std::hex << val << std::dec << ","; val=static_cast(input->readULong(1)); // a small number 3|7 f << "f0=" << val << ","; float dim[4]; for (auto &j : dim) j=float(input->readLong(4))/65536.f; shape.m_box=MWAWBox2f(MWAWVec2f(dim[1],dim[0]),MWAWVec2f(dim[3],dim[2])); f << "pos=" << shape.m_box << ","; ascii().addPos(pos); ascii().addNote(f.str().c_str()); input->seek(pos+20, librevenge::RVNG_SEEK_SET); } return true; } // read the shape definitions bool BeagleWksDRParser::readShapeDefinitions() { MWAWInputStreamPtr input = getInput(); if (!input->checkPosition(m_state->m_shapesBegin[0]+m_state->m_numShapes[1]*56)) return false; input->seek(m_state->m_shapesBegin[0], librevenge::RVNG_SEEK_SET); libmwaw::DebugStream f; if (m_state->m_numShapes[1]>int(m_state->m_shapeList.size())) { MWAW_DEBUG_MSG(("BeagleWksDRParser::readShapeDefinitions: the number of shapes definitions seems too big\n")); m_state->m_shapeList.resize(size_t(m_state->m_numShapes[1])); } for (int i=0; im_numShapes[1]; ++i) { BeagleWksDRParserInternal::Shape &shape=m_state->m_shapeList[size_t(i)]; long pos=input->tell(); f.str(""); shape.m_type=static_cast(input->readULong(2)); auto val=static_cast(input->readLong(2)); // always 0 ? if (val) f << "f0=" << val << ","; int lineFlags=0; val=static_cast(input->readULong(2)); if (shape.m_type==3) { lineFlags=val; if (lineFlags&1) { shape.m_style.m_arrows[1]=MWAWGraphicStyle::Arrow::plain(); f << "arrow[end],"; } if (lineFlags&2) { shape.m_style.m_arrows[0]=MWAWGraphicStyle::Arrow::plain(); f << "arrow[start],"; } val &= 0xFFCC; } if (val & 0x10) { shape.m_style.m_flip[0] = true; f << "flip[hori],"; } if (val & 0x20) { shape.m_style.m_flip[1] = true; f << "flip[verti],"; } val &= 0xFFCF; if (val) f << "fl=" << std::hex << val << std::dec << ","; switch (shape.m_type) { case 1: shape.m_shape = MWAWGraphicShape::rectangle(shape.m_box); break; case 2: shape.m_shape = MWAWGraphicShape::circle(shape.m_box); break; case 3: switch ((lineFlags>>4)&3) { case 1: shape.m_shape = MWAWGraphicShape::line(shape.m_box[0], shape.m_box[1]); break; case 2: shape.m_shape = MWAWGraphicShape::line(shape.m_box[1], shape.m_box[0]); break; default: case 0: shape.m_shape = MWAWGraphicShape::line(MWAWVec2f(shape.m_box[1][0],shape.m_box[0][1]), MWAWVec2f(shape.m_box[0][0],shape.m_box[1][1])); break; case 3: shape.m_shape = MWAWGraphicShape::line(MWAWVec2f(shape.m_box[0][0],shape.m_box[1][1]), MWAWVec2f(shape.m_box[1][0],shape.m_box[0][1])); break; } break; case 4: shape.m_shape = MWAWGraphicShape::rectangle(shape.m_box, MWAWVec2f(25,25)); break; case 6: case 10: shape.m_shape = MWAWGraphicShape::polygon(shape.m_box); break; default: break; } // now the style if (!readStyle(shape)) f << "##"; shape.m_extra+=f.str(); f.str(""); f << "Entries(ShapeDef)[" << i << "]:" << shape; ascii().addPos(pos); ascii().addNote(f.str().c_str()); input->seek(pos+56, librevenge::RVNG_SEEK_SET); } return true; } bool BeagleWksDRParser::readShapeDatas() { MWAWInputStreamPtr input = getInput(); libmwaw::DebugStream f; int n=0; for (auto &shape : m_state->m_shapeList) { long dataSize=shape.m_dataSize; if (!dataSize) continue; long pos = input->tell(); f.str(""); f << "Entries(ShapeData)[" << n++ << "]:type=" << shape.m_type << ","; bool ok=false; switch (shape.m_type) { case 6: case 10: { if (dataSize<2) break; auto N=static_cast(input->readULong(2)); if (dataSize!=2+N*8) break; f << "pts=["; ok=true; auto &vertices=shape.m_shape.m_vertices; vertices.resize(size_t(N)); for (auto &pt : vertices) { float position[2]; for (auto &j : position) j=float(input->readLong(4))/65536.f; pt= MWAWVec2f(position[1],position[0]); f << pt << ","; } f << "],"; break; } case 7: { if (dataSize<10) break; auto N=int(input->readULong(2)); if (10+N*2!=dataSize) break; f << "begPos=["; for (int line=0; linereadULong(2) << ","; f << "],"; f << "height=" << input->readLong(2) << ","; // checkme f << "unkn=" << std::hex << input->readULong(2) << std::dec << ","; auto val = static_cast(input->readULong(2)); if (val) f<< "f0=" << val << ","; // checkme: dataSize can also be a 32int auto len=static_cast(input->readULong(2)); dataSize+=len; if (!input->checkPosition(pos+dataSize)) break; ok=true; shape.m_entry.setBegin(input->tell()); shape.m_entry.setLength(len); for (int c=0; creadULong(1)); f << ch; } break; } case 11: if (dataSize<2) break; ok=long(input->readULong(2))==dataSize; shape.m_entry.setBegin(pos); shape.m_entry.setLength(dataSize); ascii().skipZone(pos,pos+dataSize-1); break; default: MWAW_DEBUG_MSG(("BeagleWksDRParser::readShapeDatas: find unexpected type\n")); } if (!ok || !input->checkPosition(pos+dataSize)) { MWAW_DEBUG_MSG(("BeagleWksDRParser::readShapeDatas: the data seems bad\n")); f << "###"; ascii().addPos(pos); ascii().addNote(f.str().c_str()); return false; } input->seek(pos+dataSize, librevenge::RVNG_SEEK_SET); ascii().addPos(pos); ascii().addNote(f.str().c_str()); } return true; } bool BeagleWksDRParser::readStyle(BeagleWksDRParserInternal::Shape &shape) { MWAWInputStreamPtr input = getInput(); long pos = input->tell(); if (!input->checkPosition(pos+50)) { MWAW_DEBUG_MSG(("BeagleWksDRParser::readStyle: the style zone seems too short\n")); return false; } libmwaw::DebugStream f; auto val=static_cast(input->readLong(2)); // always 8 ? if (val!=8) f << "f0=" << val << ","; int penSize[2]; for (int p=0; p<2; ++p) { penSize[p]=static_cast(input->readLong(2)); val=static_cast(input->readLong(2)); // always 0 ? if (val) f << "f" << p+1 << "=" << val << ","; } if (penSize[0]!=penSize[1]) f << "penSize=" << penSize[1] << "x" << penSize[0] << ","; shape.m_style.m_lineWidth=float(penSize[0]+penSize[1])/2.f; int patternIds[2], colorIds[4]; for (auto &pattern : patternIds) pattern=static_cast(input->readLong(2)); for (auto &color : colorIds) color=static_cast(input->readLong(2)); // set some default value if (patternIds[1]) shape.m_style.setSurfaceColor(MWAWColor::white()); for (int i=0; i<2; ++i) { if (shape.m_type==-1) { // data are not yet initialised, so... if (i==0) f << "line="; else f << "surf="; f << "[pat=" << patternIds[i] << ",col0=" << colorIds[2*i] << ",col0=" << colorIds[2*i] << "],"; continue; } if (patternIds[i]<0||patternIds[i]>=int(m_state->m_patternList.size())) { MWAW_DEBUG_MSG(("BeagleWksDRParser::readStyle: can not find a pattern\n")); f << "#pattern[" << i << "]=" << patternIds[i] << ","; continue; } if (patternIds[i]==0) { // no pattern if (i==0) shape.m_style.m_lineWidth=0; else shape.m_style.setSurfaceColor(MWAWColor::white(), 0); continue; } auto pattern=m_state->m_patternList[size_t(patternIds[i])]; for (int j=0; j<2; ++j) { if (colorIds[2*i+j]<0||colorIds[2*i+j]>=int(m_state->m_colorList.size())) { MWAW_DEBUG_MSG(("BeagleWksDRParser::readStyle: can not find a pattern\n")); f << "#color[" << i << "," << j << "]=" << colorIds[2*i+j] << ","; continue; } pattern.m_colors[1-j]=m_state->m_colorList[size_t(colorIds[2*i+j])]; } MWAWColor color; if (i==0) { if (pattern.getAverageColor(color)) shape.m_style.m_lineColor=color; } else if (pattern.getUniqueColor(color)) shape.m_style.setSurfaceColor(color); else shape.m_style.setPattern(pattern); } shape.m_style.m_rotate=-float(input->readLong(2)); int dim[2]; for (auto &p : dim) p=static_cast(input->readLong(2)); bool defaultDim=dim[0]==25 && dim[1]==25; switch (shape.m_type) { case 4: shape.m_shape.m_cornerWidth=MWAWVec2f(float(dim[1]), float(dim[0])); if (!defaultDim) f << "corner=" << dim[1] << "x" << dim[0] << ","; break; case 5: { f << "angle=" << dim[0] << "x" << dim[0]+dim[1] << ","; int angle[2] = { 90-dim[0]-dim[1], 90-dim[0] }; if (angle[1]>360) { int numLoop=int(angle[1]/360)-1; angle[0]-=numLoop*360; angle[1]-=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]-=numLoop*360; angle[1]-=numLoop*360; while (angle[0] < -360) { angle[0]+=360; angle[1]+=360; } } MWAWBox2f box=shape.m_box; MWAWVec2f center = box.center(); MWAWVec2f axis = 0.5f*MWAWVec2f(box.size()); // we must compute the real bd box float minVal[2] = { 0, 0 }, maxVal[2] = { 0, 0 }; int limitAngle[2]; for (int j = 0; j < 2; j++) limitAngle[j] = (angle[j] < 0) ? int(angle[j]/90)-1 : int(angle[j]/90); for (int bord = limitAngle[0]; bord <= limitAngle[1]+1; bord++) { float ang = (bord == limitAngle[0]) ? float(angle[0]) : (bord == limitAngle[1]+1) ? float(angle[1]) : float(90 * 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.m_box=realBox; shape.m_shape = MWAWGraphicShape::pie(realBox, box, MWAWVec2f(float(angle[0]),float(angle[1]))); break; } default: if (!defaultDim) f << "#dim=" << dim[1] << "x" << dim[0] << ","; break; } if (shape.m_type==3 || shape.m_type==5) { // g0:find 0-1 for arc for (int j=0; j<5; ++j) { val=static_cast(input->readLong(2)); if (!val) continue; if (j==1 && shape.m_type==3) f << "arrowId=" << val << ","; else f << "g" << j << "=" << val << ","; } } else { MWAWFont &font=shape.m_font; if (shape.m_type==-1) // not initialized f << "fId=" << input->readULong(2) << ","; else font.setId(m_structureManager->getFontId(static_cast(input->readULong(2)))); font.setSize(float(input->readULong(2))); auto flag=static_cast(input->readULong(2)); uint32_t flags=0; if (flag&0x1) flags |= MWAWFont::boldBit; if (flag&0x2) flags |= MWAWFont::italicBit; if (flag&0x4) font.setUnderlineStyle(MWAWFont::Line::Simple); if (flag&0x8) flags |= MWAWFont::embossBit; if (flag&0x10) flags |= MWAWFont::shadowBit; font.setFlags(flags); f << "font=[" << font.getDebugString(getParserState()->m_fontConverter) << "],"; if (flag&0xFFE0) f << "fFlags=" << std::hex << (flag&0xFFE0) << std::dec << ","; val=static_cast(input->readULong(2)); // 0 if (val) f << "g0=" << val << ","; val=static_cast(input->readULong(2)); // 0 if (shape.m_type==7) { switch (val) { // what is the logic ? case 0x1e: font.setColor(MWAWColor(255,255,255)); break; case 0x21: break; case 0x199: font.setColor(MWAWColor(0,0,255)); break; case 0x155: font.setColor(MWAWColor(0,255,0)); break; case 0xcd: font.setColor(MWAWColor(255,0,0)); break; case 0x111: font.setColor(MWAWColor(0,255,255)); break; case 0x89: font.setColor(MWAWColor(255,0,255)); break; case 0x45: font.setColor(MWAWColor(255,255,0)); break; default: MWAW_DEBUG_MSG(("BeagleWksDRParser::readStyle: find unknown font color\n")); f << "#fColor=" << std::hex << val << std::dec << ","; } } else if (val) f << "g1=" << val << ","; } val=static_cast(input->readLong(2)); switch (val) { case 0: break; case 1: shape.m_justify=MWAWParagraph::JustificationCenter; f << "center,"; break; case -1: shape.m_justify=MWAWParagraph::JustificationRight; f << "right,"; break; default: f << "#align=" << val << ","; } val=static_cast(input->readLong(2)); switch (val) { case -1: break; case -2: shape.m_interline=2; f << "spacing=200%,"; break; case -3: shape.m_interline=1.5; f << "spacing=150%,"; break; default: f << "#spacing=" << val << ","; } val=static_cast(input->readLong(2)); if (val) f << "h0=" << val << ","; shape.m_dataSize=long(input->readULong(2)); shape.m_extra=f.str(); ascii().addDelimiter(input->tell(),'|'); input->seek(pos+50, librevenge::RVNG_SEEK_SET); return true; } //////////////////////////////////////////////////////////// // read the print info //////////////////////////////////////////////////////////// bool BeagleWksDRParser::readPrintInfo() { MWAWInputStreamPtr input = getInput(); long pos = input->tell(); if (!input->checkPosition(pos+0x70)) return false; libmwaw::DebugStream f; // print info libmwaw::PrinterInfo info; if (!info.read(input)) return false; f << "Entries(PrintInfo):"<< info; MWAWVec2i paperSize = info.paper().size(); MWAWVec2i pageSize = info.page().size(); if (pageSize.x() <= 0 || pageSize.y() <= 0 || paperSize.x() <= 0 || paperSize.y() <= 0) return false; // define margin from print info MWAWVec2i lTopMargin= -1 * info.paper().pos(0); MWAWVec2i rBotMargin=info.paper().pos(1) - info.page().pos(1); // move margin left | top int decalX = lTopMargin.x() > 14 ? lTopMargin.x()-14 : 0; int decalY = lTopMargin.y() > 14 ? lTopMargin.y()-14 : 0; lTopMargin -= MWAWVec2i(decalX, decalY); rBotMargin += MWAWVec2i(decalX, decalY); // decrease right | bottom int rightMarg = rBotMargin.x() -10; if (rightMarg < 0) rightMarg=0; int botMarg = rBotMargin.y() -50; if (botMarg < 0) botMarg=0; getPageSpan().setMarginTop(lTopMargin.y()/72.0); getPageSpan().setMarginBottom(botMarg/72.0); getPageSpan().setMarginLeft(lTopMargin.x()/72.0); getPageSpan().setMarginRight(rightMarg/72.0); getPageSpan().setFormLength(paperSize.y()/72.); getPageSpan().setFormWidth(paperSize.x()/72.); ascii().addPos(pos); ascii().addNote(f.str().c_str()); input->seek(pos+0x78, librevenge::RVNG_SEEK_SET); if (long(input->tell()) != pos+0x78) { MWAW_DEBUG_MSG(("BeagleWksDRParser::readPrintInfo: file is too short\n")); return false; } ascii().addPos(input->tell()); return true; } //////////////////////////////////////////////////////////// // send shapes: vector graphic document //////////////////////////////////////////////////////////// bool BeagleWksDRParser::sendPictures() { MWAWGraphicListenerPtr listener=getGraphicListener(); if (!listener) { MWAW_DEBUG_MSG(("BeagleWksDRParser::sendPictures: can not find the listener\n")); return false; } MWAWInputStreamPtr input = getInput(); int n=0; for (auto const &shape : m_state->m_shapeList) { n++; MWAWBox2f box=shape.m_box; /* if m_rotate!=0, the box is the rotate box, so we need to rotate it back Normally, ok because the angle is a multiple of 90, but, in pratical, the new center is not good... */ if (shape.m_style.m_rotate<0 || shape.m_style.m_rotate>0) box=libmwaw::rotateBoxFromCenter(box, -shape.m_style.m_rotate); MWAWPosition pos(box[0], box.size(), librevenge::RVNG_POINT); pos.setPage(1); pos.m_anchorTo=MWAWPosition::Page; switch (shape.m_type) { case 8: // group break; case 7: { std::shared_ptr doc(new BeagleWksDRParserInternal::SubDocument(*this, input, static_cast(n-1))); MWAWGraphicStyle style=shape.m_style; style.m_lineWidth=0; listener->insertTextBox(pos, doc, style); break; } case 0xb: if (!shape.m_entry.valid()) { MWAW_DEBUG_MSG(("BeagleWksDRParser::sendPictures: the picture entry seems bad\n")); break; } else { input->seek(shape.m_entry.begin(), librevenge::RVNG_SEEK_SET); std::shared_ptr thePict(MWAWPictData::get(input, static_cast(shape.m_entry.length()))); MWAWEmbeddedObject picture; if (thePict && thePict->getBinary(picture)) { MWAWGraphicStyle style; style.m_lineWidth=0; style.setSurfaceColor(MWAWColor::white()); listener->insertPicture(pos, picture, style); } else { MWAW_DEBUG_MSG(("BeagleWksDRParser::sendPictures: can not check the picture data\n")); break; } } break; default: if (shape.m_type >= 1 && shape.m_type<=10 && shape.m_type != 9) listener->insertShape(pos, shape.m_shape, shape.m_style); else { MWAW_DEBUG_MSG(("BeagleWksDRParser::sendPictures: find unknown shape type\n")); } break; } } return true; } bool BeagleWksDRParser::sendText(int id) { MWAWGraphicListenerPtr listener=getGraphicListener(); if (!listener || !listener->canWriteText()) { MWAW_DEBUG_MSG(("BeagleWksDRParser::sendText: can not find the listener\n")); return false; } if (id<0 || id>=static_cast(m_state->m_shapeList.size())) { MWAW_DEBUG_MSG(("BeagleWksDRParser::sendText: can not find the textbox\n")); return false; } auto const &shape=m_state->m_shapeList[size_t(id)]; if (!shape.m_entry.valid() || shape.m_type != 7) { MWAW_DEBUG_MSG(("BeagleWksDRParser::sendText: the textbox seems invalid\n")); return false; } MWAWParagraph para; para.setInterline(shape.m_interline, librevenge::RVNG_PERCENT); para.m_justify=shape.m_justify; listener->setParagraph(para); MWAWFont font=shape.m_font; listener->setFont(font); MWAWInputStreamPtr input = getInput(); input->seek(shape.m_entry.begin(), librevenge::RVNG_SEEK_SET); for (long i = 0; i < shape.m_entry.length(); i++) { auto c = static_cast(input->readULong(1)); switch (c) { case 0x9: listener->insertTab(); break; case 0xd: if (i+1==shape.m_entry.length()) break; // this is marks the end of a paragraph listener->insertEOL(); break; default: listener->insertCharacter(c); break; } } return true; } //////////////////////////////////////////////////////////// // send other data //////////////////////////////////////////////////////////// bool BeagleWksDRParser::sendPageFrames() { auto const &frameMap = m_structureManager->getIdFrameMap(); for (auto it : frameMap) sendFrame(it.second); return true; } bool BeagleWksDRParser::sendFrame(BeagleWksStructManager::Frame const &frame) { MWAWPosition fPos(MWAWVec2f(0,0), frame.m_dim, librevenge::RVNG_POINT); fPos.setPagePos(frame.m_page > 0 ? frame.m_page : 1, frame.m_origin); fPos.setRelativePosition(MWAWPosition::Page); fPos.m_wrapping = frame.m_wrap==0 ? MWAWPosition::WNone : MWAWPosition::WDynamic; MWAWGraphicStyle style=MWAWGraphicStyle::emptyStyle(); style.setBorders(frame.m_bordersSet, frame.m_border); return sendPicture(frame.m_pictId, fPos, style); } // read/send picture (edtp resource) bool BeagleWksDRParser::sendPicture (int pId, MWAWPosition const &pictPos, MWAWGraphicStyle const &style) { MWAWGraphicListenerPtr listener=getGraphicListener(); if (!listener) { MWAW_DEBUG_MSG(("BeagleWksDRParser::sendPicture: can not find the listener\n")); return false; } auto rsrcParser = getRSRCParser(); if (!rsrcParser) { static bool first=true; if (first) { MWAW_DEBUG_MSG(("BeagleWksDRParser::sendPicture: need access to resource fork to retrieve picture content\n")); first=false; } return true; } librevenge::RVNGBinaryData data; if (!m_structureManager->readPicture(pId, data)) return false; listener->insertPicture(pictPos, MWAWEmbeddedObject(data, "image/pict"), style); return true; } //////////////////////////////////////////////////////////// // // Low level // //////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////// // read the header //////////////////////////////////////////////////////////// bool BeagleWksDRParser::checkHeader(MWAWHeader *header, bool strict) { *m_state = BeagleWksDRParserInternal::State(); MWAWInputStreamPtr input = getInput(); if (!input || !input->hasDataFork() || !input->checkPosition(66)) return false; libmwaw::DebugStream f; f << "FileHeader:"; input->seek(0, librevenge::RVNG_SEEK_SET); if (input->readLong(2)!=0x4257 || input->readLong(2)!=0x6b73 || input->readLong(2)!=0x4257 || input->readLong(2)!=0x6472 || input->readLong(2)!=0x4257 || input->readLong(2)!=0x6472) return false; for (int i=0; i < 9; ++i) { // f2=f6=1 other 0 auto val=static_cast(input->readLong(2)); if (val) f << "f" << i << "=" << val << ","; } setVersion(1); if (header) header->reset(MWAWDocument::MWAW_T_BEAGLEWORKS, 1, MWAWDocument::MWAW_K_DRAW); ascii().addPos(0); ascii().addNote(f.str().c_str()); long pos=input->tell(); f.str(""); f << "FileHeader-II:"; m_state->m_graphicBegin=input->readLong(4); if (!input->checkPosition(m_state->m_graphicBegin)) { MWAW_DEBUG_MSG(("BeagleWksDRParser::checkHeader: can not read the graphic position\n")); return false; } f << "graphic[ptr]=" << std::hex << m_state->m_graphicBegin << std::dec << ","; for (int i=0; i < 11; ++i) { // f2=0x50c|58c|5ac f3=f5=9 long val=input->readLong(2); if (val) f << "f" << i << "=" << std::hex << val << std::dec << ","; } MWAWEntry entry; entry.setBegin(input->readLong(4)); entry.setLength(input->readLong(4)); entry.setId(static_cast(input->readLong(2))); // in fact nFonts entry.setType("FontNames"); f << "fontNames[ptr]=" << std::hex << entry.begin() << "<->" << entry.end() << std::dec << ",nFonts=" << entry.id() << ","; if (entry.length() && (!entry.valid() || !input->checkPosition(entry.end()))) { MWAW_DEBUG_MSG(("BeagleWksDRParser::checkHeader: can not read the font names position\n")); f << "###"; ascii().addPos(pos); ascii().addNote(f.str().c_str()); return false; } m_state->m_typeEntryMap.insert (std::multimap::value_type(entry.type(),entry)); ascii().addPos(pos); ascii().addNote(f.str().c_str()); if (strict && !readPrintInfo()) return false; ascii().addPos(66); ascii().addNote("_"); return true; } // vim: set filetype=cpp tabstop=2 shiftwidth=2 cindent autoindent smartindent noexpandtab: