/* -*- 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 <algorithm>
#include <cmath>
#include <iomanip>
#include <iostream>
#include <limits>
#include <sstream>
#include <utility>
#include <librevenge/librevenge.h>
#include "MWAWFont.hxx"
#include "MWAWFontConverter.hxx"
#include "MWAWGraphicListener.hxx"
#include "MWAWGraphicShape.hxx"
#include "MWAWGraphicStyle.hxx"
#include "MWAWHeader.hxx"
#include "MWAWParagraph.hxx"
#include "MWAWPictBitmap.hxx"
#include "MWAWPictData.hxx"
#include "MWAWPrinter.hxx"
#include "MWAWPosition.hxx"
#include "MWAWSubDocument.hxx"
#include "ApplePictParser.hxx"
/** Internal: the structures of a ApplePictParser */
namespace ApplePictParserInternal
{
//! internal: low level class to store a region
struct Region {
//! constructor
Region()
: m_bdBox()
, m_points()
, m_extra("")
{
}
//! operator<<
friend std::ostream &operator<<(std::ostream &o, Region const &rgn)
{
o << rgn.m_bdBox << ",";
if (!rgn.m_points.empty()) {
o << "points=[";
for (auto const &pt : rgn.m_points)
o << pt << ",";
o << "],";
}
o << rgn.m_extra;
return o;
}
//! the bdbox
MWAWBox2i m_bdBox;
//! the set of points which defines the mask
std::vector<MWAWVec2i> m_points;
//! extra data
std::string m_extra;
};
//! Internal and low level: a class used to read pack/unpack black-white bitmap
struct Bitmap {
Bitmap()
: m_rowBytes()
, m_rect()
, m_src()
, m_dst()
, m_region()
, m_bitmap()
, m_mode(0) {}
//! operator<< for Bitmap
friend std::ostream &operator<< (std::ostream &o, Bitmap const &f)
{
o << "rDim=" << f.m_rowBytes << ", " << f.m_rect << ", " << f.m_src << ", " << f.m_dst;
if (f.m_region.get()) o << ", " << *f.m_region;
return o;
}
//! creates the bitmap from the packdata
bool unpackedData(unsigned char const *pData, int sz)
{
int rPos = 0;
size_t wPos = m_bitmap.size(), wNPos = wPos+size_t(m_rowBytes);
m_bitmap.resize(size_t(wNPos));
while (rPos < sz) {
if (rPos+2 > sz) return false;
auto n = static_cast<signed char>(pData[rPos++]);
if (n < 0) {
int nCount = 1-n;
if (wPos+size_t(nCount) > wNPos) return false;
unsigned char val = pData[rPos++];
for (int i = 0; i < nCount; i++)
m_bitmap[wPos++] = val;
continue;
}
int nCount = 1+n;
if (rPos+nCount > sz || wPos+size_t(nCount) > wNPos) return false;
for (int i = 0; i < nCount; i++)
m_bitmap[wPos++] = pData[rPos++];
}
return (wPos == wNPos);
}
//! parses the bitmap data zone
bool readBitmapData(MWAWInputStream &input, bool packed)
{
int numRows = m_rect.size().y(), szRowSize=1;
if (packed) {
// CHECKME: the limit(1/2 bytes) is probably 251: the value for a Pict2.0
// from collected data files, we have 246 < limit < 254
if (m_rowBytes > 250) szRowSize = 2;
}
else
m_bitmap.resize(size_t(numRows*m_rowBytes));
size_t pos=0;
for (int i = 0; i < numRows; i++) {
if (input.isEnd()) break;
if (!packed) {
unsigned long numR = 0;
unsigned char const *data = input.read(size_t(m_rowBytes), numR);
if (!data || int(numR) != m_rowBytes) {
MWAW_DEBUG_MSG(("ApplePictParserInternal::Bitmap::readBitmapData: can not read line %d/%d (%d chars)\n", i, numRows, m_rowBytes));
return false;
}
for (int j = 0; j < m_rowBytes; j++)
m_bitmap[pos++]=data[j];
}
else {
auto numB = static_cast<int>(input.readULong(szRowSize));
if (numB < 0 || numB > 2*m_rowBytes) {
MWAW_DEBUG_MSG(("ApplePictParserInternal::Bitmap::readBitmapData: odd numB:%d in row: %d/%d\n", numB, i, numRows));
return false;
}
unsigned long numR = 0;
unsigned char const *data = input.read(size_t(numB), numR);
if (!data || int(numR) != numB) {
MWAW_DEBUG_MSG(("ApplePictParserInternal::Bitmap::readBitmapData: can not read line %d/%d (%d chars)\n", i, numRows, numB));
return false;
}
if (!unpackedData(data,numB)) {
MWAW_DEBUG_MSG(("ApplePictParserInternal::Bitmap::readBitmapData: can not unpacked line:%d\n", i));
return false;
}
}
}
return true;
}
//! returns the bitmap and the type
bool get(MWAWEmbeddedObject &picture) const
{
if (m_rowBytes <= 0) return false;
int nRows = int(m_bitmap.size())/m_rowBytes;
MWAWPictBitmapBW bitmap(MWAWVec2i(m_rect.size().x(),nRows));
if (!bitmap.valid()) return false;
for (int i = 0; i < nRows; i++)
bitmap.setRowPacked(i, &m_bitmap[size_t(i*m_rowBytes)], &m_bitmap[0] + m_bitmap.size());
return bitmap.getBinary(picture);
}
//! the num of bytes used to store a row
int m_rowBytes;
MWAWBox2i m_rect /** the bitmap rectangle */, m_src/** the initial dimension */, /** the final dimension */ m_dst ;
//! the region
std::shared_ptr<Region> m_region;
//! the bitmap
std::vector<unsigned char> m_bitmap;
//! the encoding mode ?
int m_mode;
};
//! Internal and low level: a class used to read a color map in a Apple Pict
struct ColorTable {
//! constructor
ColorTable()
: m_flags(0)
, m_colors() {}
//! tries to read a colortable
bool read(MWAWInputStream &input)
{
long actPos = input.tell();
input.seek(4, librevenge::RVNG_SEEK_CUR); // ignore seed
m_flags = static_cast<int>(input.readULong(2));
int n = static_cast<int>(input.readLong(2))+1;
if (n < 0 || !input.checkPosition(actPos+8+8*n)) return false;
m_colors.resize(size_t(n));
for (size_t i = 0; i < size_t(n); i++) {
input.readULong(2); // indexId: ignored
unsigned char col[3];
for (unsigned char &c : col) {
c = static_cast<unsigned char>(input.readULong(1));
input.readULong(1);
}
m_colors[i] = MWAWColor(col[0], col[1], col[2]);
}
return long(input.tell()) == actPos + 8+ 8*n;
}
//! operator<< for ColorTable
friend std::ostream &operator<< (std::ostream &o, ColorTable const &f)
{
size_t numColor = f.m_colors.size();
o << "color";
if (f.m_flags) o << "(" << std::hex << f.m_flags << ")";
o << "={" << std::dec;
for (size_t i = 0; i < numColor; i++)
o << "col" << i << "=" << f.m_colors[i] << ",";
o << "}";
return o;
}
//! the color table flags
int m_flags;
//! the list of colors
std::vector<MWAWColor> m_colors;
};
//! Internal and low level: a class used to read pack/unpack color pixmap (version 2)
struct Pixmap {
Pixmap()
: m_rowBytes(0)
, m_rect()
, m_version(-1)
, m_packType(0)
, m_packSize(0)
, m_pixelType(0)
, m_pixelSize(0)
, m_compCount(0)
, m_compSize(0)
, m_planeBytes(0)
, m_colorTable()
, m_src()
, m_dst()
, m_region()
, m_indices()
, m_colors()
, m_mode(0)
{
m_resolution[0] = m_resolution[1] = 0;
}
//! operator<< for Pixmap
friend std::ostream &operator<< (std::ostream &o, Pixmap const &f)
{
o << "rDim=" << f.m_rowBytes << ", " << f.m_rect << ", " << f.m_src << ", " << f.m_dst;
o << ", resol=" << f.m_resolution[0] << "x" << f.m_resolution[1];
if (f.m_colorTable.get()) o << ", " << *f.m_colorTable;
if (f.m_region.get()) o << ", " << *f.m_region;
return o;
}
//! creates the pixmap from the packdata
bool unpackedData(unsigned char const *pData, int sz, int byteSz, int nSize, std::vector<unsigned char> &res) const
{
if (byteSz<1||byteSz>4) {
MWAW_DEBUG_MSG(("ApplePictParserInternal::Pixmap::unpackedData: unknown byteSz\n"));
return false;
}
int rPos = 0, wPos = 0, maxW = m_rowBytes+24;
while (rPos < sz) {
if (rPos+2 > sz) return false;
auto n = static_cast<signed char>(pData[rPos++]);
if (n < 0) {
int nCount = 1-n;
if (rPos+byteSz > sz || wPos+byteSz *nCount >= maxW) return false;
unsigned char val[4];
for (int b = 0; b < byteSz; b++) val[b] = pData[rPos++];
for (int i = 0; i < nCount; i++) {
if (wPos+byteSz >= maxW) break;
for (int b = 0; b < byteSz; b++)res[size_t(wPos++)] = val[b];
}
continue;
}
int nCount = 1+n;
if (rPos+byteSz *nCount > sz || wPos+byteSz *nCount >= maxW) return false;
for (int i = 0; i < nCount; i++) {
if (wPos+byteSz >= maxW) break;
for (int b = 0; b < byteSz; b++) res[size_t(wPos++)] = pData[rPos++];
}
}
return wPos+8 >= nSize;
}
MWAWColor extractColor(const std::vector<unsigned char> &data, size_t rIdx, size_t gIdx, size_t bIdx)
{
unsigned char r = rIdx < data.size() ? data[rIdx] : 0;
unsigned char g = gIdx < data.size() ? data[gIdx] : 0;
unsigned char b = bIdx < data.size() ? data[bIdx] : 0;
return MWAWColor(r, g, b);
}
//! computes the height based on available data
int computeHeight(MWAWInputStream &input, const int height, const int width, const bool packed, const int szRowSize) const
{
if (packed) {
const long pos = input.tell();
int h = 0;
for (; h < height && !input.isEnd(); ++h) {
auto len = long(input.readULong(szRowSize));
input.seek(len, librevenge::RVNG_SEEK_CUR);
}
input.seek(pos, librevenge::RVNG_SEEK_SET);
return h;
}
else {
const long remaining = input.size() - input.tell();
const long maxHeight = remaining / width + int(remaining % width > 1);
return std::min(height, int(maxHeight));
}
}
//! parses the pixmap data zone
bool readPixmapData(MWAWInputStream &input)
{
int W = m_rect.size().x();
int szRowSize=1;
if (m_rowBytes > 250) szRowSize = 2;
bool packed = !(m_rowBytes < 8 || m_packType == 1);
int H = computeHeight(input, m_rect.size().y(), W, packed, szRowSize);
int nPlanes = 1, nBytes = 3, rowBytes = m_rowBytes;
int numValuesByInt = 1;
int numColors = m_colorTable.get() ? int(m_colorTable->m_colors.size()) : 0;
int maxColorsIndex = -1;
switch (m_pixelSize) {
case 1:
case 2:
case 4:
case 8: { // indices (associated to a color map)
nBytes = 1;
numValuesByInt = 8/m_pixelSize;
int numValues = (W+numValuesByInt-1)/numValuesByInt;
if (m_rowBytes < numValues || m_rowBytes > numValues+10) {
MWAW_DEBUG_MSG(("ApplePictParserInternal::Pixmap::readPixmapData invalid number of rowsize : %d, pixelSize=%d, W=%d\n", m_rowBytes, m_pixelSize, W));
return false;
}
if (numColors == 0) {
MWAW_DEBUG_MSG(("ApplePictParserInternal::Pixmap::readPixmapData: readPixmapData no color table \n"));
return false;
}
break;
}
case 16:
nBytes = 2;
break;
case 32:
if (!packed) {
nBytes=4;
break;
}
if (m_packType == 2) {
packed = false;
break;
}
if (m_compCount != 3 && m_compCount != 4) {
MWAW_DEBUG_MSG(("ApplePictParserInternal::Pixmap::readPixmapData: do not known how to read cmpCount=%d\n", m_compCount));
return false;
}
nPlanes=m_compCount;
nBytes=1;
if (nPlanes == 3) rowBytes = (3*rowBytes)/4;
break;
default:
MWAW_DEBUG_MSG(("ApplePictParserInternal::Pixmap::readPixmapData: do not known how to read pixelsize=%d \n", m_pixelSize));
return false;
}
const size_t dataSize = size_t(H) * size_t(W);
if (m_pixelSize <= 8)
m_indices.resize(dataSize);
else {
if (rowBytes != W * nBytes * nPlanes) {
MWAW_DEBUG_MSG(("ApplePictParserInternal::Pixmap::readPixmapData: find W=%d pixelsize=%d, rowSize=%d\n", W, m_pixelSize, m_rowBytes));
}
m_colors.resize(dataSize);
}
std::vector<unsigned char> values;
values.resize(size_t(m_rowBytes+24), static_cast<unsigned char>(0));
for (int y = 0; y < H; y++) {
if (!packed) {
unsigned long numR = 0;
unsigned char const *data = input.read(size_t(m_rowBytes), numR);
if (!data || int(numR) != m_rowBytes) {
MWAW_DEBUG_MSG(("ApplePictParserInternal::Pixmap::readPixmapData: readColors can not read line %d/%d (%d chars)\n", y, H, m_rowBytes));
return false;
}
for (size_t j = 0; j < size_t(m_rowBytes); j++)
values[j]=data[j];
}
else { // ok, packed
auto numB = static_cast<int>(input.readULong(szRowSize));
if (numB < 0 || numB > 2*m_rowBytes) {
MWAW_DEBUG_MSG(("ApplePictParserInternal::Pixmap::readPixmapData: odd numB:%d in row: %d/%d\n", numB, y, H));
return false;
}
unsigned long numR = 0;
unsigned char const *data = input.read(size_t(numB), numR);
if (!data || int(numR) != numB) {
MWAW_DEBUG_MSG(("ApplePictParserInternal::Pixmap::readPixmapData: can not read line %d/%d (%d chars)\n", y, H, numB));
return false;
}
if (!unpackedData(data,numB, nBytes, rowBytes, values)) {
MWAW_DEBUG_MSG(("ApplePictParserInternal::Pixmap::readPixmapData: can not unpacked line:%d\n", y));
return false;
}
}
//
// ok, we can add it in the pictures
//
int wPos = y*W;
if (m_pixelSize <= 8) { // indexed
int maxValues = (1 << m_pixelSize)-1;
for (int x = 0, rPos = 0; x < W;) {
unsigned char val = values[size_t(rPos++)];
for (int v = numValuesByInt-1; v >=0; v--) {
int index = (val>>(v*m_pixelSize))&maxValues;
if (index > maxColorsIndex) maxColorsIndex = index;
m_indices[size_t(wPos++)] = index;
if (++x >= W) break;
}
}
}
else if (m_pixelSize == 16) {
for (int x = 0, rPos = 0; x < W; x++) {
unsigned int c1 = size_t(rPos) < values.size() ? values[size_t(rPos)] : 0;
unsigned int c2 = size_t(rPos+1) < values.size() ? values[size_t(rPos+1)] : 0;
unsigned int val = 256*c1 + c2;
rPos+=2;
m_colors[size_t(wPos++)]=MWAWColor((val>>7)& 0xF8, (val>>2) & 0xF8, static_cast<unsigned char>(val << 3));
}
}
else if (nPlanes==1) {
for (int x = 0, rPos = 0; x < W; x++) {
if (nBytes==4) rPos++;
m_colors[size_t(wPos++)]=extractColor(values, size_t(rPos), size_t(rPos+1), size_t(rPos+2));
rPos+=3;
}
}
else {
for (int x = 0, rPos = (nPlanes==4) ? W:0; x < W; x++) {
m_colors[size_t(wPos++)]=extractColor(values, size_t(rPos), size_t(rPos+W), size_t(rPos+2*W));
rPos+=1;
}
}
}
if (maxColorsIndex >= numColors) {
if (!m_colorTable) m_colorTable.reset(new ColorTable);
std::vector<MWAWColor> &cols = m_colorTable->m_colors;
// can be ok for a pixpat ; in this case:
// maxColorsIndex -> foregroundColor, numColors -> backGroundColor
// and intermediate index fills with intermediate colors
int numUnset = maxColorsIndex-numColors+1;
int decGray = (numUnset==1) ? 0 : 255/(numUnset-1);
for (int i = 0; i < numUnset; i++)
cols.push_back(MWAWColor(static_cast<unsigned char>(255-i*decGray), static_cast<unsigned char>(255-i*decGray), static_cast<unsigned char>(255-i*decGray)));
MWAW_DEBUG_MSG(("ApplePictParserInternal::Pixmap::readPixmapData: find index=%d >= numColors=%d\n", maxColorsIndex, numColors));
return true;
}
return true;
}
//! returns the pixmap
bool get(MWAWEmbeddedObject &picture) const
{
int W = m_rect.size().x();
if (W <= 0) return false;
if (m_colorTable.get() && m_indices.size()) {
int nRows = int(m_indices.size())/W;
MWAWPictBitmapIndexed pixmap(MWAWVec2i(W,nRows));
if (!pixmap.valid()) return false;
pixmap.setColors(m_colorTable->m_colors);
size_t rPos = 0;
for (int i = 0; i < nRows; i++) {
for (int x = 0; x < W; x++)
pixmap.set(x, i, m_indices[rPos++]);
}
return pixmap.getBinary(picture);
}
if (m_colors.size()) {
int nRows = int(m_colors.size())/W;
MWAWPictBitmapColor pixmap(MWAWVec2i(W,nRows));
if (!pixmap.valid()) return false;
size_t rPos = 0;
for (int i = 0; i < nRows; i++) {
for (int x = 0; x < W; x++)
pixmap.set(x, i, m_colors[rPos++]);
}
return pixmap.getBinary(picture);
}
MWAW_DEBUG_MSG(("ApplePictParserInternal::Pixmap::get: can not find any indices or colors \n"));
return false;
}
//! the num of bytes used to store a row
int m_rowBytes;
MWAWBox2i m_rect /** the pixmap rectangle */;
int m_version /** the pixmap version */;
int m_packType /** the packing format */;
long m_packSize /** size of data in the packed state */;
int m_resolution[2] /** horizontal/vertical definition */;
int m_pixelType /** format of pixel image */;
int m_pixelSize /** physical bit by image */;
int m_compCount /** logical components per pixels */;
int m_compSize /** logical bits by components */;
long m_planeBytes /** offset to the next plane */;
std::shared_ptr<ColorTable> m_colorTable /** the color table */;
MWAWBox2i m_src/** the initial dimension */, /** another final dimension */ m_dst ;
//! the region
std::shared_ptr<Region> m_region;
//! the pixmap indices
std::vector<int> m_indices;
//! the colors
std::vector<MWAWColor> m_colors;
//! the encoding mode ?
int m_mode;
};
////////////////////////////////////////
//! Internal: the state of a ApplePictParser
struct State {
//! constructor
State()
: m_version(0)
, m_bdBox()
, m_origin(0,0)
, m_penPosition(0,0)
, m_textPosition(0,0)
, m_penSize(1,1)
, m_ovalSize(1,1)
, m_penMode(0)
, m_textMode(0)
, m_isHiliteMode(false)
, m_font(3,12)
, m_foreColor(MWAWColor::black())
, m_backColor(MWAWColor::white())
, m_hiliteColor(MWAWColor::black())
, m_opColor(MWAWColor::black())
, m_penPattern()
, m_backgroundPattern()
, m_fillPattern()
, m_rectangle()
, m_roundRectangle()
, m_circle()
, m_pie()
, m_points()
, m_afterQuicktime(false)
{
}
//! init the patterns list
void initPatterns();
//! returns true if the shape is invisible
bool isInvisible(ApplePictParser::DrawingMethod method) const
{
if (method==ApplePictParser::D_INVERT ||
(method==ApplePictParser::D_TEXT && m_textMode==23) ||
(method!=ApplePictParser::D_TEXT && m_penMode==23)) return true;
return (method==ApplePictParser::D_FRAME && (m_penSize[0] == 0 || m_penSize[1] == 0));
}
//! update the actual style
void updateStyle(ApplePictParser::DrawingMethod method, MWAWGraphicStyle &style)
{
style=MWAWGraphicStyle();
if (method!=ApplePictParser::D_FRAME)
style.m_lineWidth=0;
else
style.m_lineWidth=0.5f*float(m_penSize[0]+m_penSize[1]);
MWAWColor color;
switch (method) {
case ApplePictParser::D_FRAME:
case ApplePictParser::D_TEXT: // set foreColor, it is use for defining the font color
color=m_foreColor;
if (!m_penPattern.empty())
m_penPattern.getAverageColor(color);
style.m_lineColor=color;
break;
case ApplePictParser::D_PAINT:
if (m_penPattern.empty())
style.setSurfaceColor(m_foreColor);
else if (m_penPattern.getUniqueColor(color))
style.setSurfaceColor(color);
else
style.setPattern(m_penPattern);
break;
case ApplePictParser::D_FILL:
if (m_fillPattern.empty())
style.setSurfaceColor(m_foreColor);
else if (m_fillPattern.getUniqueColor(color))
style.setSurfaceColor(color);
else
style.setPattern(m_fillPattern);
break;
case ApplePictParser::D_ERASE:
if (m_backgroundPattern.empty())
style.setSurfaceColor(MWAWColor(255,255,255));
else if (m_backgroundPattern.getUniqueColor(color))
style.setSurfaceColor(color);
else
style.setPattern(m_backgroundPattern);
break;
case ApplePictParser::D_INVERT:
break;
case ApplePictParser::D_UNDEFINED:
#if !defined(__clang__)
default:
#endif
break;
}
}
//! update the position
void updatePosition(MWAWVec2f const &orig, MWAWPosition &pos)
{
pos=MWAWPosition(orig-m_bdBox[0]+m_origin, MWAWVec2f(-1,-1), librevenge::RVNG_POINT);
pos.m_anchorTo=MWAWPosition::Page;
}
//! update the position
void updatePosition(MWAWBox2f const &bdBox, MWAWPosition &pos)
{
pos=MWAWPosition(bdBox[0]-m_bdBox[0]+m_origin, bdBox.size(), librevenge::RVNG_POINT);
pos.m_anchorTo=MWAWPosition::Page;
}
//! the file version
int m_version;
//! the bounding rectangle
MWAWBox2f m_bdBox;
//! the origin
MWAWVec2f m_origin;
//! the actual pen position
MWAWVec2i m_penPosition;
//! the actual text position
MWAWVec2i m_textPosition;
//! the actual pensize
MWAWVec2i m_penSize;
//! the actual ovalsize
MWAWVec2i m_ovalSize;
//! the pen mode
int m_penMode;
//! the text mode
int m_textMode;
//! true if we must use the hilite mode
bool m_isHiliteMode;
//! the actual font
MWAWFont m_font;
//! the foreground color
MWAWColor m_foreColor;
//! the background color
MWAWColor m_backColor;
//! the hilite color
MWAWColor m_hiliteColor;
//! the op color
MWAWColor m_opColor;
//! the pen pattern
MWAWGraphicStyle::Pattern m_penPattern;
//! the background pattern
MWAWGraphicStyle::Pattern m_backgroundPattern;
//! the fill pattern
MWAWGraphicStyle::Pattern m_fillPattern;
//! the last rectangle
MWAWBox2i m_rectangle;
//! the last round rectangle
MWAWBox2i m_roundRectangle;
//! the last circle
MWAWBox2i m_circle;
//! the last pie
MWAWBox2i m_pie;
//! the last polygon points
std::vector<MWAWVec2i> m_points;
//! a flag to know if we have found a quicktime picture/movie
bool m_afterQuicktime;
};
////////////////////////////////////////
//! Internal: the subdocument of a ApplePictParser
class SubDocument final : public MWAWSubDocument
{
public:
SubDocument(ApplePictParser &pars, MWAWInputStreamPtr const &input, MWAWEntry const &entry)
: MWAWSubDocument(&pars, input, entry) {}
//! 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;
return false;
}
//! the parser function
void parse(MWAWListenerPtr &listener, libmwaw::SubDocumentType type) final;
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(("ApplePictParserInternal::SubDocument::parse: no listener\n"));
return;
}
auto *parser=dynamic_cast<ApplePictParser *>(m_parser);
if (!parser) {
MWAW_DEBUG_MSG(("ApplePictParserInternal::SubDocument::parse: no parser\n"));
return;
}
long pos = m_input->tell();
parser->drawText(m_zone);
m_input->seek(pos, librevenge::RVNG_SEEK_SET);
}
}
////////////////////////////////////////////////////////////
// constructor/destructor, ...
////////////////////////////////////////////////////////////
ApplePictParser::ApplePictParser(MWAWInputStreamPtr const &input, MWAWRSRCParserPtr const &rsrcParser, MWAWHeader *header)
: MWAWGraphicParser(input, rsrcParser, header)
, m_state()
{
init();
}
ApplePictParser::~ApplePictParser()
{
}
void ApplePictParser::init()
{
resetGraphicListener();
setAsciiName("main-1");
m_state.reset(new ApplePictParserInternal::State);
getPageSpan().setMargins(0.001);
}
////////////////////////////////////////////////////////////
// the parser
////////////////////////////////////////////////////////////
void ApplePictParser::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);
createDocument(docInterface);
ok = createZones();
ascii().reset();
}
catch (...) {
MWAW_DEBUG_MSG(("ApplePictParser::parse: exception catched when parsing\n"));
ok = false;
}
resetGraphicListener();
if (!ok) throw(libmwaw::ParseException());
}
////////////////////////////////////////////////////////////
// create the document
////////////////////////////////////////////////////////////
void ApplePictParser::createDocument(librevenge::RVNGDrawingInterface *documentInterface)
{
if (!documentInterface) return;
if (getGraphicListener()) {
MWAW_DEBUG_MSG(("ApplePictParser::createDocument: listener already exist\n"));
return;
}
// create the page list
MWAWPageSpan ps(getPageSpan());
ps.setPageSpan(1);
ps.setFormWidth(double(m_state->m_bdBox.size()[0])/72.);
ps.setFormLength(double(m_state->m_bdBox.size()[1])/72.);
std::vector<MWAWPageSpan> pageList(1,ps);
MWAWGraphicListenerPtr listen(new MWAWGraphicListener(*getParserState(), pageList, documentInterface));
setGraphicListener(listen);
listen->startDocument();
}
////////////////////////////////////////////////////////////
//
// Intermediate level
//
////////////////////////////////////////////////////////////
bool ApplePictParser::createZones()
{
//m_state->m_penPosition=m_state->m_bdBox[0];
MWAWInputStreamPtr input = getInput();
long pos, debPos=input->tell();
while (!input->isEnd()) {
pos=input->tell();
if (!readZone()) {
input->seek(pos, librevenge::RVNG_SEEK_SET);
break;
}
}
bool ok=true;
if (!input->isEnd()) {
pos=input->tell();
MWAW_DEBUG_MSG(("ApplePictParser::createZones: find extra data\n"));
ascii().addPos(input->tell());
ascii().addNote("Entries(Data):##");
ok=(input->size()-debPos)<=2*(pos-debPos);
}
return ok;
}
bool ApplePictParser::readZone()
{
MWAWInputStreamPtr input = getInput();
long pos=input->tell();
int const vers=version();
int const opLength=version()>=2 ? 2 : 1;
if (!input->checkPosition(pos+opLength))
return false;
auto opCode=static_cast<int>(input->readULong(opLength));
DrawingMethod drawingMethod = D_UNDEFINED;
/* in general a basic bitmap follow a Quicktime picture/movie,
so let store the state to ignore some uneeded bitmap/pixmap
*/
bool afterQuicktime=m_state->m_afterQuicktime;
m_state->m_afterQuicktime=false;
switch (opCode & 7) {
case 0:
drawingMethod = D_FRAME;
break;
case 1:
drawingMethod = D_PAINT;
break;
case 2:
drawingMethod = D_ERASE;
break;
case 3:
drawingMethod = D_INVERT;
break;
case 4:
drawingMethod = D_FILL;
break;
default:
break;
}
long dSz;
libmwaw::DebugStream f;
int val;
long actPos=input->tell();
switch (opCode) {
case 0: // NOP
f << "_";
break;
case 1: {
ApplePictParserInternal::Region region;
if (!readRegion(region))
return false;
f << "Entries(Region)[clip]:" << region;
break;
}
case 2:
if (!readBWPattern(m_state->m_backgroundPattern))
return false;
f << "Entries(Pattern)[back]:" << m_state->m_backgroundPattern << ",";
break;
case 3:
if (!input->checkPosition(actPos+2))
return false;
m_state->m_font.setId(static_cast<int>(input->readULong(2)));
f << "Entries(FontId):" << m_state->m_font.id() << ",";
break;
case 4: {
if (!input->checkPosition(actPos+1))
return false;
f << "Entries(TextFace):";
auto flag=static_cast<int>(input->readULong(1));
uint32_t flags=0;
if (flag&0x1) {
flags |= MWAWFont::boldBit;
f << "b:";
}
if (flag&0x2) {
flags |= MWAWFont::italicBit;
f << "it:";
}
if (flag&0x4) {
m_state->m_font.setUnderlineStyle(MWAWFont::Line::Simple);
f << "under:";
}
else
m_state->m_font.setUnderlineStyle(MWAWFont::Line::None);
if (flag&0x8) {
flags |= MWAWFont::embossBit;
f << "emboss:";
}
if (flag&0x10) {
flags |= MWAWFont::shadowBit;
f << "shadow:";
}
m_state->m_font.setDeltaLetterSpacing(0);
if (flag&0x20) {
m_state->m_font.setDeltaLetterSpacing(-1);
f << "condensed:";
}
if (flag&0x40) {
m_state->m_font.setDeltaLetterSpacing(1);
f << "extend:";
}
if (flag&0x80) f << "#flag0[0x80],";
m_state->m_font.setFlags(flags);
break;
}
case 5:
case 8: {
if (!input->checkPosition(actPos+2))
return false;
auto mode=static_cast<int>(input->readULong(2));
if (opCode==5) {
f << "Entries(TextMode):";
m_state->m_textMode=mode;
}
else {
f << "Entries(PenMode):";
m_state->m_penMode=mode;
}
f << getModeName(mode);
break;
}
case 6:
if (!input->checkPosition(actPos+4))
return false;
f << "Entries(SpaceExtra):" << float(input->readLong(4))/65536.f;
break;
case 7:
case 0xb:
case 0xc: {
if (!input->checkPosition(actPos+4))
return false;
MWAWVec2i size;
for (int i=0; i<2; ++i)
size[1-i]=static_cast<int>(input->readULong(2));
if (opCode==7) {
m_state->m_penSize=size;
f << "Entries(PenSize):" << size << ",";
}
else if (opCode==0xb) {
m_state->m_ovalSize=size;
f << "Entries(OvalSize):" << size << ",";
}
else {
m_state->m_origin+=MWAWVec2f(static_cast<float>(size[1]),static_cast<float>(size[0]));
f << "Entries(Orign):delta=" << MWAWVec2i(size[1],size[0]) << ",";
}
break;
}
case 9:
if (!readBWPattern(m_state->m_penPattern))
return false;
f << "Entries(Pattern)[pen]:" << m_state->m_penPattern << ",";
break;
case 0xa:
if (!readBWPattern(m_state->m_fillPattern))
return false;
f << "Entries(Pattern)[fill]:" << m_state->m_fillPattern << ",";
break;
case 0xd:
if (!input->checkPosition(actPos+2))
return false;
val=static_cast<int>(input->readULong(2));
f << "Entries(FontSz):" << val;
m_state->m_font.setSize(float(val));
break;
case 0xe:
case 0xf: {
if (!input->checkPosition(actPos+2))
return false;
val=static_cast<int>(input->readULong(4));
MWAWColor color;
if (opCode==0xe)
f << "Entries(Color)[fore]:";
else
f << "Entries(Color)[back]:";
switch (val) {
case 30:
color=MWAWColor::white();
break;
case 33:
color=MWAWColor::black();
break;
case 69:
color=MWAWColor(255,255,0);
break;
case 137:
color=MWAWColor(255,0,255);
break;
case 205:
color=MWAWColor(255,0,0);
break;
case 273:
color=MWAWColor(0,255,255);
break;
case 341:
color=MWAWColor(0,0,255);
break;
case 409:
color=MWAWColor(0,255,0);
break;
default:
MWAW_DEBUG_MSG(("ApplePictParser::readZone: find unknown color\n"));
break;
}
f << color;
if (opCode==0xe)
m_state->m_foreColor=color;
else
m_state->m_backColor=color;
break;
}
case 0x10:
if (!input->checkPosition(actPos+8))
return false;
f << "Entries(TextRatio):";
for (int i=0; i<2; ++i) {
if (i==0)
f << "num=";
else
f << "denom=";
for (int j=0; j<2; ++j) {
f << input->readULong(2);
if (j==0)
f << "x";
else
f << ",";
}
}
break;
case 0x11:
if (!input->checkPosition(actPos+1))
return false;
f << "Entries(Version):" << input->readLong(1);
break;
case 0x12:
if (!readColorPattern(m_state->m_backgroundPattern))
return false;
f << "Entries(CPat)[back]:" << m_state->m_backgroundPattern << ",";
break;
case 0x13:
if (!readColorPattern(m_state->m_penPattern))
return false;
f << "Entries(CPat)[pen]:" << m_state->m_penPattern << ",";
break;
case 0x14:
if (!readColorPattern(m_state->m_fillPattern))
return false;
f << "Entries(CPat)[fill]:" << m_state->m_fillPattern << ",";
break;
case 0x15:
if (!input->checkPosition(actPos+2))
return false;
f << "Entries(PnLocHFrac):" << float(input->readLong(2))/256.f;
break;
case 0x16:
if (!input->checkPosition(actPos+2))
return false;
val=static_cast<int>(input->readLong(2));
f << "Entries(ChExtra):" << val;
m_state->m_font.setDeltaLetterSpacing(static_cast<float>(val));
break;
case 0x1c:
f << "Entries(HiliteMode):";
m_state->m_isHiliteMode=true;
break;
case 0x1e:
f << "Entries(HiliteDef):";
m_state->m_hiliteColor=MWAWColor::black();
break;
case 0x1a:
case 0x1b:
case 0x1d:
case 0x1f: {
MWAWColor col;
if (!readRGBColor(col))
return false;
f << "Entries(Color)";
if (opCode==0x1a) {
f << "[fore]";
m_state->m_foreColor=col;
}
else if (opCode==0x1b) {
f << "[back]";
m_state->m_backColor=col;
}
else if (opCode==0x1d) {
f << "[hilite]";
m_state->m_hiliteColor=col;
}
else {
f << "[op]";
m_state->m_opColor=col;
}
f << ":" << col;
break;
}
case 0x20: {
if (!input->checkPosition(8+actPos))
return false;
f << "Entries(Line):";
for (int i=0; i<2; ++i)
m_state->m_penPosition[1-i]=static_cast<int>(input->readLong(2));
MWAWVec2i point;
for (int i=0; i<2; ++i)
point[1-i]=static_cast<int>(input->readLong(2));
f << m_state->m_penPosition << "->" << point << ",";
drawLine(point);
break;
}
case 0x21: {
if (!input->checkPosition(4+actPos))
return false;
f << "Entries(Line):";
MWAWVec2i point;
for (int i=0; i<2; ++i)
point[1-i]=static_cast<int>(input->readLong(2));
f << m_state->m_penPosition << "->" << point << ",";
drawLine(point);
break;
}
case 0x22: {
if (!input->checkPosition(6+actPos))
return false;
f << "Entries(Line):";
for (int i=0; i<2; ++i)
m_state->m_penPosition[1-i]=static_cast<int>(input->readLong(2));
MWAWVec2i point;
for (int i=0; i<2; ++i)
point[i]=m_state->m_penPosition[i]+static_cast<int>(input->readLong(1));
f << m_state->m_penPosition << "->" << point << ",";
drawLine(point);
break;
}
case 0x23: {
if (!input->checkPosition(2+actPos))
return false;
f << "Entries(Line):";
MWAWVec2i point;
for (int i=0; i<2; ++i)
point[i]=m_state->m_penPosition[i]+static_cast<int>(input->readLong(1));
f << m_state->m_penPosition << "->" << point << ",";
drawLine(point);
break;
}
case 0x28: {
if (!input->checkPosition(5+actPos))
return false;
f << "Entries(TextData):";
for (int i=0; i<2; ++i)
m_state->m_textPosition[1-i]=static_cast<int>(input->readLong(2));
f << m_state->m_textPosition << ",";
std::string text("");
if (!readAndDrawText(text))
return false;
f << text;
break;
}
case 0x29:
case 0x2a: {
if (!input->checkPosition(2+actPos))
return false;
f << "Entries(TextData):";
m_state->m_textPosition[opCode-0x29]=
m_state->m_textPosition[opCode-0x29]+static_cast<int>(input->readULong(1));
f << m_state->m_textPosition << ",";
std::string text("");
if (!readAndDrawText(text))
return false;
f << text;
break;
}
case 0x2b: {
if (!input->checkPosition(3+actPos))
return false;
f << "Entries(TextData):";
for (int i=0; i<2; ++i)
m_state->m_textPosition[i]=m_state->m_textPosition[i]+
static_cast<int>(input->readULong(1));
f << m_state->m_textPosition << ",";
std::string text("");
if (!readAndDrawText(text))
return false;
f << text;
break;
}
case 0x2c: {
if (!input->checkPosition(5+actPos))
return false;
f << "Entries(FontName):";
dSz=static_cast<long>(input->readULong(2));
f << "dSz=" << dSz << ",";
if (!input->checkPosition(2+dSz+actPos))
return false;
auto id=static_cast<int>(input->readULong(2));
f << "id=" << id << ",";
auto sSz=static_cast<int>(input->readULong(1));
if (sSz>dSz+3) {
MWAW_DEBUG_MSG(("ApplePictParser::readZone: font name size seems bad\n"));
sSz=int(dSz-3);
}
if (!input->checkPosition(5+sSz+actPos))
return false;
std::string name("");
for (int i=0; i < sSz; ++i) name+=static_cast<char>(input->readULong(1));
f << name;
if (!name.empty())
getParserState()->m_fontConverter->setCorrespondance(id, name);
m_state->m_font.setId(id);
input->seek(2+dSz+actPos, librevenge::RVNG_SEEK_SET);
break;
}
case 0x2d:
if (!input->checkPosition(actPos+2))
return false;
dSz=static_cast<long>(input->readULong(2));
if (!input->checkPosition(actPos+2+dSz))
return false;
f << "Entries(LineSpacing):";
if (dSz!=8) {
MWAW_DEBUG_MSG(("ApplePictParser::readZone: the data length seems bad\n"));
f << "###";
input->seek(actPos+2+dSz, librevenge::RVNG_SEEK_SET);
break;
}
val=static_cast<int>(input->readLong(4));
m_state->m_font.setDeltaLetterSpacing(float(val)/65536.f);
f << "char[spacing]=" << float(val)/65536.f << ",";
f << "space[spacing]=" << float(input->readLong(4))/65536.f << ",";
break;
case 0x2e:
if (!input->checkPosition(actPos+2))
return false;
dSz=static_cast<long>(input->readULong(2));
if (!input->checkPosition(actPos+2+dSz))
return false;
f << "Entries(Glyph):";
if (dSz!=4) {
MWAW_DEBUG_MSG(("ApplePictParser::readZone: the data length seems bad\n"));
f << "###";
input->seek(actPos+2+dSz, librevenge::RVNG_SEEK_SET);
break;
}
for (int i=0; i<4; ++i) {
val=static_cast<int>(input->readLong(1));
if (!val) continue;
f << "f" << i << "=" << val << ",";
}
break;
case 0x30:
case 0x31:
case 0x32:
case 0x33:
case 0x34:
if (!readAndDrawRectangle(drawingMethod))
return false;
break;
case 0x38:
case 0x39:
case 0x3a:
case 0x3b:
case 0x3c:
f << "Entries(Rectangle)[" << getDrawingName(drawingMethod) << "]:same";
drawRectangle(drawingMethod);
break;
case 0x40:
case 0x41:
case 0x42:
case 0x43:
case 0x44:
if (!readAndDrawRoundRectangle(drawingMethod))
return false;
break;
case 0x48:
case 0x49:
case 0x4a:
case 0x4b:
case 0x4c:
f << "Entries(RoundRect)[" << getDrawingName(drawingMethod) << "]:same";
drawRoundRectangle(drawingMethod);
break;
case 0x50:
case 0x51:
case 0x52:
case 0x53:
case 0x54:
if (!readAndDrawCircle(drawingMethod))
return false;
break;
case 0x58:
case 0x59:
case 0x5a:
case 0x5b:
case 0x5c:
f << "Entries(Circle)[" << getDrawingName(drawingMethod) << "]:same";
drawCircle(drawingMethod);
break;
case 0x60:
case 0x61:
case 0x62:
case 0x63:
case 0x64:
if (!readAndDrawPie(drawingMethod))
return false;
break;
case 0x68:
case 0x69:
case 0x6a:
case 0x6b:
case 0x6c: {
if (!input->checkPosition(actPos+4))
return false;
f << "Entries(Pie)[" << getDrawingName(drawingMethod) << "]:same";
int angles[2];
for (int &angle : angles) angle=static_cast<int>(input->readLong(2));
drawPie(drawingMethod, angles[0], angles[1]);
break;
}
case 0x70:
case 0x71:
case 0x72:
case 0x73:
case 0x74:
if (!readAndDrawPolygon(drawingMethod))
return false;
break;
case 0x78:
case 0x79:
case 0x7a:
case 0x7b:
case 0x7c:
f << "Entries(Polygon)[" << getDrawingName(drawingMethod) << "]:same";
drawPolygon(drawingMethod);
break;
case 0x80:
case 0x81:
case 0x82:
case 0x83:
case 0x84: {
ApplePictParserInternal::Region region;
if (!readRegion(region))
return false;
f << "Entries(Region)[" << getDrawingName(drawingMethod) << "]:" << region;
break;
}
case 0x88:
case 0x89:
case 0x8a:
case 0x8b:
case 0x8c:
f << "Entries(Region)[" << getDrawingName(drawingMethod) << "]:same";
break;
case 0x90:
case 0x91:
case 0x98:
case 0x99: {
// first check if it is a bitmap or a pixmap
bool pixmap = input->readULong(2) & 0x8000;
input->seek(-2, librevenge::RVNG_SEEK_CUR);
bool packed = (opCode&8);
bool hasRgn = (opCode&1);
if (pixmap) {
ApplePictParserInternal::Pixmap bitmap;
if (!readPixmap(bitmap, packed, true, true, hasRgn))
return false;
if (!afterQuicktime)
drawPixmap(bitmap);
f << "Entries(Pixmap):";
}
else {
ApplePictParserInternal::Bitmap bitmap;
if (!readBitmap(bitmap, packed, hasRgn))
return false;
if (!afterQuicktime)
drawBitmap(bitmap);
f << "Entries(Bitmap):";
}
break;
}
case 0x9a:
case 0x9b: {
ApplePictParserInternal::Pixmap bitmap;
if (!readPixmap(bitmap, false, false, true, (opCode&1) ? true : false))
return false;
drawPixmap(bitmap);
f << "Entries(Pixmap):";
break;
}
case 0xa0:
if (!input->checkPosition(actPos+2))
return false;
f << "Entries(Comment)[short]:kind="<<input->readLong(2) << ",";
break;
case 0xa1:
case 0xa5: // not really a Pict1 code, but it can appear in some pict
if (!input->checkPosition(actPos+4))
return false;
f << "Entries(Comment)[long]:kind="<<input->readLong(2) << ",";
if (opCode==0xa5) f << "#unusual,";
dSz=static_cast<long>(input->readULong(2));
if (!input->checkPosition(actPos+4+dSz))
return false;
input->seek(dSz, librevenge::RVNG_SEEK_CUR);
break;
case 0xff:
f << "Entries(EOP):";
if (vers>=2) input->seek(2, librevenge::RVNG_SEEK_CUR);
break;
case 0x8200: {
MWAWEmbeddedObject picture;
MWAWBox2f bdBox;
if (!readQuicktime(picture,bdBox))
return false;
m_state->m_afterQuicktime=true;
if (!picture.isEmpty() && getGraphicListener()) {
MWAWGraphicStyle style;
MWAWPosition position;
m_state->updatePosition(bdBox, position);
getGraphicListener()->insertPicture(position,picture);
}
break;
}
case 0x8201: {
dSz=4+static_cast<long>(input->readULong(4));
if (!input->checkPosition(actPos+dSz))
return false;
MWAW_DEBUG_MSG(("ApplePictParser::readZone: reading compressed Quicktime is not implemented\n"));
f << "Entries(QuickTComp):";
input->seek(actPos+dSz, librevenge::RVNG_SEEK_SET);
break;
}
//
// Reserved
//
case 0x17: // reserved + 0 byes
case 0x18:
case 0x19:
case 0x3d:
case 0x3e:
case 0x3f:
case 0x4d:
case 0x4e:
case 0x4f:
case 0x5d:
case 0x5e:
case 0x5f:
case 0x6d:
case 0x6e:
case 0x6f:
case 0x7d:
case 0x7e:
case 0x7f:
case 0x8d:
case 0x8e:
case 0x8f:
case 0xcf: // checkme
f << "Entries(Reserved"<<std::hex << opCode << std::dec << "):";
break;
case 0x35: // reserved + 8 bytes
case 0x36:
case 0x37:
case 0x45:
case 0x46:
case 0x47:
case 0x55:
case 0x56:
case 0x57:
if (!input->checkPosition(actPos+8))
return false;
f << "Entries(Reserved"<<std::hex << opCode << std::dec << "):";
input->seek(8, librevenge::RVNG_SEEK_CUR);
break;
case 0x65: // reserved + 12 bytes
case 0x66:
case 0x67:
if (!input->checkPosition(actPos+12))
return false;
f << "Entries(Reserved"<<std::hex << opCode << std::dec << "):";
input->seek(8, librevenge::RVNG_SEEK_CUR);
break;
case 0x24: // reserved + N bytes
case 0x25:
case 0x26:
case 0x27:
case 0x2f:
case 0x75:
case 0x76:
case 0x77:
case 0x85:
case 0x86:
case 0x87:
case 0x92:
case 0x93:
case 0x94:
case 0x95:
case 0x96:
case 0x97:
case 0x9c:
case 0x9d:
case 0x9e:
case 0x9f:
case 0xa2: // checkme
if (!input->checkPosition(actPos+2))
return false;
f << "Entries(Reserved"<<std::hex << opCode << std::dec << "):";
dSz=static_cast<long>(input->readULong(2));
if (!input->checkPosition(actPos+2+dSz))
return false;
input->seek(dSz, librevenge::RVNG_SEEK_CUR);
break;
default: {
if (opCode<=0xaf) dSz=2+static_cast<int>(input->readULong(2));
else if (opCode<=0xcf) dSz=0;
else if (opCode<=0x100) dSz=4+static_cast<long>(input->readULong(4));
else if (opCode<=0x01ff) dSz=2;
else if (opCode<=0x0bfe) dSz=4;
else if (opCode<=0x0bff) dSz=22;
else if (opCode==0x0c00) dSz=24; // HeaderOp
else if (opCode<=0x7eff) dSz=24;
else if (opCode<=0x7fff) dSz=254;
else if (opCode<=0x80ff) dSz=0;
else dSz=4+static_cast<long>(input->readULong(4));
if (!input->checkPosition(actPos+dSz))
return false;
f << "Entries(Reserved"<<std::hex << opCode << std::dec << "):";
input->seek(actPos+dSz, librevenge::RVNG_SEEK_SET);
break;
}
}
if (vers>=2 && ((input->tell()-pos)%2)!=0)
input->seek(1, librevenge::RVNG_SEEK_CUR);
ascii().addPos(pos);
ascii().addNote(f.str().c_str());
return true;
}
bool ApplePictParser::readAndDrawRectangle(ApplePictParser::DrawingMethod method)
{
MWAWInputStreamPtr input = getInput();
long pos=input->tell();
if (!input->checkPosition(pos+8))
return false;
libmwaw::DebugStream f;
f << "Entries(Rectangle)[" << getDrawingName(method) << "]:";
int dim[4];
for (int &i : dim) i=static_cast<int>(input->readLong(2));
m_state->m_rectangle=MWAWBox2i(MWAWVec2i(dim[1],dim[0]),MWAWVec2i(dim[3],dim[2]));
f << m_state->m_rectangle;
drawRectangle(method);
ascii().addPos(pos-(version()==1 ? 1 : 2));
ascii().addNote(f.str().c_str());
return true;
}
bool ApplePictParser::readAndDrawRoundRectangle(ApplePictParser::DrawingMethod method)
{
MWAWInputStreamPtr input = getInput();
long pos=input->tell();
if (!input->checkPosition(pos+8))
return false;
libmwaw::DebugStream f;
f << "Entries(RoundRect)[" << getDrawingName(method) << "]:";
int dim[4];
for (int &i : dim) i=static_cast<int>(input->readLong(2));
m_state->m_roundRectangle=MWAWBox2i(MWAWVec2i(dim[1],dim[0]),MWAWVec2i(dim[3],dim[2]));
f << m_state->m_roundRectangle;
drawRoundRectangle(method);
ascii().addPos(pos-(version()==1 ? 1 : 2));
ascii().addNote(f.str().c_str());
return true;
}
bool ApplePictParser::readAndDrawCircle(ApplePictParser::DrawingMethod method)
{
MWAWInputStreamPtr input = getInput();
long pos=input->tell();
if (!input->checkPosition(pos+8))
return false;
libmwaw::DebugStream f;
f << "Entries(Circle)[" << getDrawingName(method) << "]:";
int dim[4];
for (int &i : dim) i=static_cast<int>(input->readLong(2));
m_state->m_circle=MWAWBox2i(MWAWVec2i(dim[1],dim[0]),MWAWVec2i(dim[3],dim[2]));
f << m_state->m_circle;
drawCircle(method);
ascii().addPos(pos-(version()==1 ? 1 : 2));
ascii().addNote(f.str().c_str());
return true;
}
bool ApplePictParser::readAndDrawPie(ApplePictParser::DrawingMethod method)
{
MWAWInputStreamPtr input = getInput();
long pos=input->tell();
if (!input->checkPosition(pos+12))
return false;
libmwaw::DebugStream f;
f << "Entries(Pie)[" << getDrawingName(method) << "]:";
int dim[4];
for (int &i : dim) i=static_cast<int>(input->readLong(2));
m_state->m_pie=MWAWBox2i(MWAWVec2i(dim[1],dim[0]),MWAWVec2i(dim[3],dim[2]));
f << m_state->m_pie << ",";
int angles[2];
for (int &angle : angles) angle=static_cast<int>(input->readLong(2));
f << "angl=" << angles[0] << "x" << angles[0]+angles[1] << ",";
drawPie(method, angles[0], angles[1]);
ascii().addPos(pos-(version()==1 ? 1 : 2));
ascii().addNote(f.str().c_str());
return true;
}
bool ApplePictParser::readAndDrawPolygon(ApplePictParser::DrawingMethod method)
{
MWAWInputStreamPtr input = getInput();
long pos=input->tell();
auto dSz=static_cast<int>(input->readULong(2));
if (dSz<10 || (dSz%4)!=2 || !input->checkPosition(pos+dSz))
return false;
libmwaw::DebugStream f;
f << "Entries(Polygon)[" << getDrawingName(method) << "]:";
int dim[4];
for (int &i : dim) i=static_cast<int>(input->readLong(2));
f << MWAWBox2i(MWAWVec2i(dim[1],dim[0]),MWAWVec2i(dim[3],dim[2])) << ",";
int N=(dSz-10)/4;
f << "pts=[";
m_state->m_points.clear();
for (int i=0; i<N; ++i) {
int coord[2];
for (int &j : coord) j=int(input->readLong(2));
m_state->m_points.push_back(MWAWVec2i(coord[1],coord[0]));
f << MWAWVec2i(coord[1],coord[0]) << ",";
}
f << "],";
drawPolygon(method);
ascii().addPos(pos-(version()==1 ? 1 : 2));
ascii().addNote(f.str().c_str());
return true;
}
bool ApplePictParser::readRGBColor(MWAWColor &color)
{
MWAWInputStreamPtr input = getInput();
long pos=input->tell();
if (!input->checkPosition(pos+6))
return false;
uint8_t col[3];
for (uint8_t &i : col) i=uint8_t(input->readULong(2)>>8);
color=MWAWColor(col[0],col[1],col[2]);
return true;
}
bool ApplePictParser::readBWPattern(MWAWGraphicStyle::Pattern &pat)
{
MWAWInputStreamPtr input = getInput();
long pos=input->tell();
if (!input->checkPosition(pos+8))
return false;
pat.m_dim=MWAWVec2i(8,8);
pat.m_data.resize(8);
pat.m_colors[0]=MWAWColor::white();
pat.m_colors[1]=MWAWColor::black();
for (size_t i=0; i<8; ++i)
pat.m_data[i]=uint8_t(input->readULong(1));
return true;
}
bool ApplePictParser::readColorPattern(MWAWGraphicStyle::Pattern &pat)
{
MWAWInputStreamPtr input = getInput();
long pos=input->tell();
if (!input->checkPosition(pos+10))
return false;
auto type = static_cast<int>(input->readULong(2));
if (type !=1 && type != 2) {
MWAW_DEBUG_MSG(("ApplePictParser::readColorPattern: unknown type=%d... \n", type));
return false;
}
pat.m_dim=MWAWVec2i(8,8);
pat.m_data.resize(8);
pat.m_colors[0]=MWAWColor::white();
pat.m_colors[1]=MWAWColor::black();
for (size_t i=0; i<8; ++i)
pat.m_data[i]=uint8_t(input->readULong(1));
if (type==2) {
// a color pattern -> create a uniform color pattern
if (!readRGBColor(pat.m_colors[0]))
return false;
for (size_t i=0; i<8; ++i)
pat.m_data[i]=0;
return true;
}
ApplePictParserInternal::Pixmap pixmap;
return readPixmap(pixmap, false, true, false, false);
}
bool ApplePictParser::readAndDrawText(std::string &text)
{
MWAWInputStreamPtr input = getInput();
long pos=input->tell();
if (!input->checkPosition(pos+1))
return false;
auto dSz=static_cast<int>(input->readULong(1));
if (!input->checkPosition(pos+1+dSz))
return false;
text="";
MWAWEntry entry;
entry.setBegin(input->tell());
entry.setLength(dSz);
for (int i=0; i<dSz; ++i) text+=static_cast<char>(input->readULong(1));
if (m_state->isInvisible(D_TEXT)) return true;
MWAWListenerPtr listener=getGraphicListener();
if (!listener || listener->canWriteText()) {
MWAW_DEBUG_MSG(("ApplePictParser::readAndDrawText: can not find the listener\n"));
return true;
}
std::shared_ptr<MWAWSubDocument> doc(new ApplePictParserInternal::SubDocument(*this, getInput(), entry));
MWAWGraphicStyle style;
m_state->updateStyle(D_TEXT, style);
MWAWVec2f orig(m_state->m_textPosition);
orig[1]=orig[1]-m_state->m_font.size();
MWAWPosition position;
m_state->updatePosition(orig, position);
listener->insertTextBox(position, doc, style);
input->seek(pos+1+dSz, librevenge::RVNG_SEEK_SET);
return true;
}
bool ApplePictParser::readBitmap(ApplePictParserInternal::Bitmap &bitmap, bool packed, bool hasRegion)
{
MWAWInputStreamPtr input = getInput();
long pos=input->tell();
if (!input->checkPosition(pos+28))
return false;
libmwaw::DebugStream f;
f << "Bitmap:";
bitmap.m_rowBytes = static_cast<int>(input->readULong(2));
bitmap.m_rowBytes &= 0x3FFF;
if (bitmap.m_rowBytes < 0 || (!packed && bitmap.m_rowBytes > 8)) {
MWAW_DEBUG_MSG(("ApplePictParser::readBitmap: find odd rowBytes %d... \n", bitmap.m_rowBytes));
return false;
}
// read the rectangle: bound
// ------ end of bitmap ----------
// and the two general rectangle src, dst
for (int c = 0; c < 3; c++) {
int val[4];
for (int &d : val) d = static_cast<int>(input->readLong(2));
MWAWBox2i box(MWAWVec2i(val[1],val[0]), MWAWVec2i(val[3],val[2]));
if (box.size().x() <= 0 || box.size().y() <= 0) {
MWAW_DEBUG_MSG(("ApplePictParser::readBitmap: find odd rectangle %d... \n", c));
return false;
}
if (c == 0) bitmap.m_rect=box;
else if (c==1) bitmap.m_src = box;
else bitmap.m_dst = box;
}
if (!packed && bitmap.m_rowBytes*8 < bitmap.m_rect.size().x()) {
MWAW_DEBUG_MSG(("ApplePictParser::readBitmap: row bytes seems to short: %d/%d... \n", bitmap.m_rowBytes*8, bitmap.m_rect.size().y()));
return false;
}
bitmap.m_mode = static_cast<int>(input->readLong(2)); // mode: I find 0,1 and 3
if (bitmap.m_mode < 0 || bitmap.m_mode > 64) {
MWAW_DEBUG_MSG(("ApplePictParser::readBitmap: unknown mode: %d \n", bitmap.m_mode));
return false;
}
if (hasRegion) { // CHECKME...
std::shared_ptr<ApplePictParserInternal::Region>rgn(new ApplePictParserInternal::Region);
if (!readRegion(*rgn)) return false;
bitmap.m_region = rgn;
}
long actPos=input->tell();
if (!bitmap.readBitmapData(*input, packed)) return false;
ascii().skipZone(actPos,input->tell()-1);
f << bitmap;
f << getModeName(bitmap.m_mode) << ",";
ascii().addPos(pos);
ascii().addNote(f.str().c_str());
return true;
}
bool ApplePictParser::readPixmap(ApplePictParserInternal::Pixmap &pixmap, bool packed, bool colorTable, bool hasRectsMode, bool hasRegion)
{
MWAWInputStreamPtr input = getInput();
long pos=input->tell();
if (!input->checkPosition(pos+46))
return false;
libmwaw::DebugStream f;
f << "Pixmap:";
if (!colorTable) input->seek(4, librevenge::RVNG_SEEK_CUR); // skip the base address
pixmap.m_rowBytes = static_cast<int>(input->readULong(2));
pixmap.m_rowBytes &= 0x3FFF;
// read the rectangle: bound
int val[4];
for (int &d : val) d = static_cast<int>(input->readLong(2));
pixmap.m_rect = MWAWBox2i(MWAWVec2i(val[1],val[0]), MWAWVec2i(val[3],val[2]));
if (pixmap.m_rect.size().x() <= 0 || pixmap.m_rect.size().y() <= 0) {
MWAW_DEBUG_MSG(("ApplePictParser::readPixmap: find odd bound rectangle ... \n"));
return false;
}
pixmap.m_version = static_cast<int>(input->readLong(2));
pixmap.m_packType = static_cast<int>(input->readLong(2));
pixmap.m_packSize = static_cast<int>(input->readLong(4));
for (int &c : pixmap.m_resolution) {
c = static_cast<int>(input->readLong(2));
input->readLong(2);
}
pixmap.m_pixelType = static_cast<int>(input->readLong(2));
pixmap.m_pixelSize = static_cast<int>(input->readLong(2));
pixmap.m_compCount = static_cast<int>(input->readLong(2));
pixmap.m_compSize = static_cast<int>(input->readLong(2));
pixmap.m_planeBytes = static_cast<int>(input->readLong(4));
// ignored: colorHandle+reserved
input->seek(8, librevenge::RVNG_SEEK_CUR);
// the color table
if (colorTable) {
pixmap.m_colorTable.reset(new ApplePictParserInternal::ColorTable);
if (!pixmap.m_colorTable->read(*input)) return false;
}
if (!packed && pixmap.m_rowBytes*8 < pixmap.m_rect.size().y()) {
MWAW_DEBUG_MSG(("ApplePictParser::readPixmap: row bytes seems to short: %d/%d... \n", pixmap.m_rowBytes*8, pixmap.m_rect.size().y()));
return false;
}
// read the two general rectangle src, dst
if (hasRectsMode) {
for (int c = 0; c < 2; c++) {
int dim[4];
for (int &d : dim) d = static_cast<int>(input->readLong(2));
MWAWBox2i box(MWAWVec2i(dim[1],dim[0]), MWAWVec2i(dim[3],dim[2]));
if (box.size().x() <= 0 || box.size().y() <= 0) {
MWAW_DEBUG_MSG(("ApplePictParser::readPixmap: find odd rectangle %d... \n", c));
return false;
}
else if (c==0) pixmap.m_src = box;
else pixmap.m_dst = box;
}
pixmap.m_mode = static_cast<int>(input->readLong(2)); // mode: I find 0,1 and 3
f << "mode=" << getModeName(pixmap.m_mode) << ",";
}
if (hasRegion) { // CHECKME...
std::shared_ptr<ApplePictParserInternal::Region> rgn(new ApplePictParserInternal::Region);
if (!readRegion(*rgn)) return false;
pixmap.m_region = rgn;
}
long actPos=input->tell();
if (!pixmap.readPixmapData(*input)) return false;
ascii().skipZone(actPos,input->tell()-1);
f << pixmap << ",";
ascii().addPos(pos);
ascii().addNote(f.str().c_str());
return true;
}
bool ApplePictParser::readQuicktime(MWAWEmbeddedObject &object, MWAWBox2f &bdBox)
{
MWAWInputStreamPtr input = getInput();
long pos=input->tell();
if (!input->checkPosition(pos+4))
return false;
auto dSz=static_cast<long>(input->readULong(4));
long endPos=pos+4+dSz;
if (dSz<68 || !input->checkPosition(endPos))
return false;
libmwaw::DebugStream f;
f << "Entries(Quicktime):";
auto val=static_cast<int>(input->readULong(2));
if (val) f << "vers=" << val << ",";
float matrix[9];
f << "mat=[";
for (int i=0; i<9; ++i) {
float value=float(input->readLong(4))/65536.f;
// the last one is a fract number
if (i==8) value/=16384.f;
matrix[i]=value;
if (value<0 || value>0)
f << value << ",";
else
f << "_,";
}
f << "],";
if (matrix[8]<=0) {
MWAW_DEBUG_MSG(("ApplePictParser::readQuicktime: find odd w coefficient in matrix\n"));
f << "###w,";
matrix[8]=1;
}
auto matteSize=static_cast<long>(input->readULong(4));
if (matteSize)
f << "matteSize=" << std::hex << matteSize << std::dec << ",";
int dim[4];
for (int &i : dim) i=static_cast<int>(input->readLong(2));
if (dim[2]!=dim[0] || dim[3]!=dim[2])
f << "matteRect=" << MWAWBox2i(MWAWVec2i(dim[1],dim[0]),MWAWVec2i(dim[3],dim[2])) << ",";
auto mode=static_cast<int>(input->readULong(2));
if (mode) f << "mode=" << mode << ",";
for (int &i : dim) i=static_cast<int>(input->readLong(2));
f << "srcRec=" << MWAWBox2i(MWAWVec2i(dim[1],dim[0]),MWAWVec2i(dim[3],dim[2])) << ",";
// assume that the matrix contains only some scaling and/or translation
if (matrix[1]<0 || matrix[1]>0 || matrix[2]<0 || matrix[2]>0 ||
matrix[3]<0 || matrix[3]>0 || matrix[5]<0 || matrix[5]>0) {
MWAW_DEBUG_MSG(("ApplePictParser::readQuicktime: oops the matrix is not a scaling matrix\n"));
matrix[1]=matrix[3]=1;
matrix[2]=matrix[5]=0;
}
float dimF[4];
for (int i=0; i<2; ++i) {
dimF[2*i]=(static_cast<float>(dim[2*i])*matrix[0]+matrix[7])/matrix[8];
dimF[2*i+1]=(static_cast<float>(dim[2*i+1])*matrix[4]+matrix[6])/matrix[8];
}
if (dimF[0]>dimF[2]) std::swap(dimF[0],dimF[2]);
if (dimF[1]>dimF[3]) std::swap(dimF[1],dimF[3]);
bdBox=MWAWBox2f(MWAWVec2f(dimF[1],dimF[0]),MWAWVec2f(dimF[3],dimF[2]));
val=static_cast<int>(input->readULong(4));
if (val) f << "accuracy=" << val << ",";
auto maskSize=static_cast<long>(input->readULong(4));
if (maskSize)
f << "maskSize=" << std::hex << maskSize << std::dec << ",";
ascii().addPos(pos-2);
ascii().addNote(f.str().c_str());
if (matteSize) {
pos=input->tell();
f.str("");
f << "Quicktime:matteDesc,";
dSz=static_cast<long>(input->readULong(4));
if (pos+4+dSz+matteSize>endPos) {
MWAW_DEBUG_MSG(("ApplePictParser::readQuicktime: find odd mat size\n"));
f << "###";
ascii().addPos(pos);
ascii().addNote(f.str().c_str());
input->seek(endPos, librevenge::RVNG_SEEK_SET);
return true;
}
input->seek(pos+4+dSz, librevenge::RVNG_SEEK_SET);
ascii().addPos(pos);
ascii().addNote(f.str().c_str());
pos=input->tell();
f.str("");
f << "Quicktime:matteData,";
input->seek(pos+matteSize, librevenge::RVNG_SEEK_SET);
ascii().addPos(pos);
ascii().addNote(f.str().c_str());
}
if (maskSize) {
pos=input->tell();
f.str("");
f << "Quicktime:mask,";
if (pos+maskSize>endPos) {
MWAW_DEBUG_MSG(("ApplePictParser::readQuicktime: can not read the mask section\n"));
f << "###";
ascii().addPos(pos);
ascii().addNote(f.str().c_str());
input->seek(endPos, librevenge::RVNG_SEEK_SET);
return true;
}
// CHECKME: normally a region
input->seek(pos+maskSize, librevenge::RVNG_SEEK_SET);
ascii().addPos(pos);
ascii().addNote(f.str().c_str());
}
pos=input->tell();
f.str("");
f << "Quicktime:imageDesc,";
dSz=static_cast<long>(input->readULong(4));
if (dSz<86 || pos+dSz>endPos) {
MWAW_DEBUG_MSG(("ApplePictParser::readQuicktime: can not read the image description\n"));
f << "###";
ascii().addPos(pos);
ascii().addNote(f.str().c_str());
input->seek(endPos, librevenge::RVNG_SEEK_SET);
return true;
}
std::string creator;
for (int i=0; i<4; ++i) creator+=char(input->readULong(1));
f << "creator=" << creator << ",";
input->seek(6, librevenge::RVNG_SEEK_CUR); // reserved1 and reserved2
val=static_cast<int>(input->readLong(2));
if (val) f << "dataRefId=" << val << ","; // must be 0
val=static_cast<int>(input->readLong(2));
if (val) f << "vers=" << val << ",";
val=static_cast<int>(input->readLong(2));
if (val) f << "revision[level]=" << val << ",";
std::string vendor;
for (int i=0; i<4; ++i) vendor+=char(input->readULong(1));
f << "vendor=" << vendor << ",";
val=static_cast<int>(input->readULong(4)); // CodecQ: min=0, max=3FF, loseless=400
if (val) f << "quality[temporal]=" << std::hex << val << std::dec << ",";
val=static_cast<int>(input->readULong(4)); // CodecQ: min=0, max=3FF, loseless=400
if (val) f << "quality[spacial]=" << std::hex << val << std::dec << ",";
for (int i=0; i<2; ++i) dim[i]=static_cast<int>(input->readLong(2));
f << "src[sz]=" << MWAWVec2i(dim[0],dim[1]) << ",";
f << "res=" << double(input->readLong(4))/65536. << "x" << double(input->readLong(4))/65536. << ",";
auto dataSize=static_cast<long>(input->readULong(4));
f << "dataSize=" << std::hex << dataSize << std::dec << ",";
f << "frame[count]=" << input->readULong(2) << ",";
auto sSz=static_cast<int>(input->readULong(1));
if (sSz>31 || input->tell()+sSz>pos+dSz) {
MWAW_DEBUG_MSG(("ApplePictParser::readQuicktime: can not read the compression name\n"));
f << "###";
ascii().addPos(pos);
ascii().addNote(f.str().c_str());
input->seek(endPos, librevenge::RVNG_SEEK_SET);
return true;
}
long lastSPos=input->tell()+31;
std::string name;
for (int i=0; i<sSz; ++i) name+=char(input->readULong(1));
f << name << ",";
input->seek(lastSPos, librevenge::RVNG_SEEK_SET);
val=static_cast<int>(input->readLong(2));
if (val) f << "depth=" << val << ",";
val=static_cast<int>(input->readLong(2));
if (val!=-1) f << "clutId=" << val << ",";
if (input->tell()!=pos+dSz)
ascii().addDelimiter(input->tell(),'|');
ascii().addPos(pos);
ascii().addNote(f.str().c_str());
input->seek(pos+dSz, librevenge::RVNG_SEEK_SET);
pos=input->tell();
f.str("");
f << "Quicktime:data,";
librevenge::RVNGBinaryData data;
if (dataSize<=0 || pos+dataSize>endPos || !input->readDataBlock(dataSize, data)) {
MWAW_DEBUG_MSG(("ApplePictParser::readQuicktime: can not read the data zone\n"));
f << "###";
ascii().addPos(pos);
ascii().addNote(f.str().c_str());
input->seek(endPos, librevenge::RVNG_SEEK_SET);
return true;
}
object.add(data);
ascii().skipZone(pos,pos+dataSize-1);
input->seek(pos+dataSize, librevenge::RVNG_SEEK_SET);
pos=input->tell();
if (pos+3<endPos) {
MWAW_DEBUG_MSG(("ApplePictParser::readQuicktime: find some extra data\n"));
f << "#extra,";
ascii().addPos(pos);
ascii().addNote(f.str().c_str());
}
else if (pos!=endPos) {
ascii().addPos(pos);
ascii().addNote("_");
}
input->seek(endPos, librevenge::RVNG_SEEK_SET);
return true;
}
bool ApplePictParser::readRegion(ApplePictParserInternal::Region ®ion)
{
MWAWInputStreamPtr input = getInput();
long pos=input->tell();
if (!input->checkPosition(pos+10))
return false;
auto dSz=static_cast<int>(input->readULong(2));
if (dSz<10||!input->checkPosition(pos+dSz))
return false;
int dim[4];
for (int &i : dim) i=static_cast<int>(input->readLong(2));
region.m_bdBox=MWAWBox2i(MWAWVec2i(dim[1],dim[0]),MWAWVec2i(dim[3],dim[2]));
int remain=(dSz-10)/2;
// une liste de point dans la box: x1, y1, .. yn 0x7fff, x2, ... 0x7fff
while (remain > 0) {
auto y = static_cast<int>(input->readLong(2));
--remain;
if (y == 0x7fff) break;
if (y < region.m_bdBox[0].y() || y > region.m_bdBox[1].y()) {
MWAW_DEBUG_MSG(("ApplePictParser::readRegion: found eroneous y value: %d\n", y));
break;
}
bool endF = false;
while (remain > 0) {
auto x = static_cast<int>(input->readLong(2));
--remain;
if (x == 0x7fff) {
endF = true;
break;
}
if (x < region.m_bdBox[0].x() || x > region.m_bdBox[1].x()) {
MWAW_DEBUG_MSG(("ApplePictParser::readRegion: found eroneous x value\n"));
break;
}
region.m_points.push_back(MWAWVec2i(x,y));
}
if (!endF) {
MWAW_DEBUG_MSG(("ApplePictParser::readRegion: does not find end of file...\n"));
break;
}
}
if (remain) {
MWAW_DEBUG_MSG(("ApplePictParser::readRegion: find some remaining data ...\n"));
region.m_extra="###,";
}
input->seek(pos+dSz, librevenge::RVNG_SEEK_SET);
return true;
}
//
// helper function
//
std::string ApplePictParser::getModeName(int mode)
{
switch (mode) {
case 0:
return "srcCopy";
case 1:
return "srcOr";
case 2:
return "srcXOr";
case 3:
return "srcBic";
case 4:
return "notSrcCopy";
case 5:
return "notSrcOr";
case 6:
return "notSrcXOr";
case 7:
return "notSrcBic";
case 8:
return "patCopy";
case 9:
return "patOr";
case 10:
return "patXOr";
case 11:
return "patBic";
case 12:
return "notPatCopy";
case 13:
return "notPatOr";
case 14:
return "notPatXOr";
case 15:
return "notPatBic";
case 23:
return "postscript";
case 32:
return "blend";
case 33:
return "addPin";
case 34:
return "addOver";
case 35:
return "subPin";
case 36:
return "transparent";
case 37:
return "addMax";
case 38:
return "subOver";
case 39:
return "addMin";
case 49:
return "grayishTextOr";
case 50:
return "hilite";
case 64:
return "mask";
default:
break;
}
MWAW_DEBUG_MSG(("ApplePictParser::getModeName: find unknown mode\n"));
std::stringstream s;
s << "##mode=" << mode;
return s.str();
}
////////////////////////////////////////////////////////////
// read the header
////////////////////////////////////////////////////////////
bool ApplePictParser::checkHeader(MWAWHeader *header, bool strict)
{
*m_state = ApplePictParserInternal::State();
MWAWInputStreamPtr input = getInput();
if (!input || !input->hasDataFork() || !input->checkPosition(13))
return false;
int vers=0;
for (int st=0; st<2; ++st) {
if (!input->checkPosition(512*st+13))
return false;
long pos=st*512;
input->seek(pos, librevenge::RVNG_SEEK_SET);
libmwaw::DebugStream f;
f << "FileHeader:";
auto dSz=static_cast<int>(input->readULong(2));
if (dSz)
f << "dSz=" << dSz << ",";
int dim[4];
for (int &i : dim) i=static_cast<int>(input->readLong(2));
if (dim[0]>dim[2]||dim[1]>dim[3])
continue;
m_state->m_bdBox=MWAWBox2f(MWAWVec2f(float(dim[1]),float(dim[0])),MWAWVec2f(float(dim[3]),float(dim[2])));
f << "dim=" << m_state->m_bdBox << ",";
long lastFlag = input->readLong(2);
switch (lastFlag) {
case 0x1101:
if (!input->checkPosition(pos+dSz))
break;
f << "pict1,";
vers = 1;
break;
case 0x11: {
if (!input->checkPosition(pos+40))
break;
if (input->readULong(2) != 0x2ff || input->readULong(2) != 0xC00) break;
int fileVersion = -int(input->readLong(2));
int subvers = -int(input->readLong(2));
float dim2[4];
switch (fileVersion) {
case 1:
f << "pict2[1:" << subvers << "],";
for (float &i : dim2) i=float(input->readLong(4))/65536.f;
if (strict && (dim2[0]>dim2[2]||dim2[1]>dim2[3]))
break;
m_state->m_bdBox=MWAWBox2f(MWAWVec2f(static_cast<float>(dim2[0]),static_cast<float>(dim2[1])),MWAWVec2f(static_cast<float>(dim2[2]),static_cast<float>(dim2[3])));
f << "dim[fixed]=" << m_state->m_bdBox << ",";
vers=2;
break;
case 2: {
f << "pict2[2:" << subvers << "],";
for (int i=0; i<2; ++i) dim2[i]=float(input->readLong(4))/65536.f;
if (strict && (dim2[0]<0 || dim2[1]<=0)) break;
f << "res=" << MWAWVec2f(dim2[1],dim2[0]) << ",";
for (int &i : dim) i=static_cast<int>(input->readLong(2));
if (dim[0]>dim[2]||dim[1]>dim[3])
break;
MWAWBox2f bdbox(MWAWVec2f(static_cast<float>(dim[1]),static_cast<float>(dim[0])),MWAWVec2f(static_cast<float>(dim[3]),static_cast<float>(dim[2])));
if (bdbox.size()[0]>0 && bdbox.size()[1]>0)
m_state->m_bdBox=bdbox;
else
f << "##";
f << "dim[optimal]=" << bdbox << ",";
vers=2;
break;
}
default:
break;
}
if (vers==0 || !input->checkPosition(input->tell()+4)) {
vers=0;
break;
}
input->seek(4, librevenge::RVNG_SEEK_CUR); // reserved
break;
}
default:
break;
}
if (vers) {
ascii().addPos(pos);
ascii().addNote(f.str().c_str());
break;
}
if (st==0) {
ascii().addPos(0);
ascii().addNote("Entries(Pref):");
}
}
if (vers==0) return false;
setVersion(vers);
m_state->m_version=vers;
if (header)
header->reset(MWAWDocument::MWAW_T_APPLEPICT, vers, MWAWDocument::MWAW_K_DRAW);
return true;
}
////////////////////////////////////////////////////////////
//
// send data
//
////////////////////////////////////////////////////////////
void ApplePictParser::drawLine(MWAWVec2i const &pt)
{
MWAWVec2f orig(m_state->m_penPosition);
m_state->m_penPosition=pt;
MWAWListenerPtr listener=getGraphicListener();
if (m_state->isInvisible(D_FRAME) || !listener)
return;
MWAWGraphicStyle style;
m_state->updateStyle(D_FRAME, style);
auto shape=MWAWGraphicShape::line(orig, MWAWVec2f(pt));
MWAWPosition pos;
m_state->updatePosition(shape.getBdBox(), pos);
listener->insertShape(pos,shape, style);
}
void ApplePictParser::drawRectangle(ApplePictParser::DrawingMethod method)
{
MWAWListenerPtr listener=getGraphicListener();
if (m_state->isInvisible(method) || !listener)
return;
MWAWGraphicStyle style;
m_state->updateStyle(method, style);
MWAWBox2f rect(m_state->m_rectangle);
if (method==D_ERASE) // the rectangle can be very large
rect=rect.getIntersection(m_state->m_bdBox);
auto shape=MWAWGraphicShape::rectangle(rect);
MWAWPosition pos;
m_state->updatePosition(shape.getBdBox(), pos);
listener->insertShape(pos,shape, style);
}
void ApplePictParser::drawRoundRectangle(ApplePictParser::DrawingMethod method)
{
MWAWListenerPtr listener=getGraphicListener();
if (m_state->isInvisible(method) || !listener)
return;
MWAWGraphicStyle style;
m_state->updateStyle(D_FRAME, style);
auto shape=MWAWGraphicShape::rectangle(MWAWBox2f(m_state->m_roundRectangle), MWAWVec2f(m_state->m_ovalSize));
MWAWPosition pos;
m_state->updatePosition(shape.getBdBox(), pos);
listener->insertShape(pos,shape, style);
}
void ApplePictParser::drawCircle(ApplePictParser::DrawingMethod method)
{
MWAWListenerPtr listener=getGraphicListener();
if (m_state->isInvisible(method) || !listener)
return;
MWAWGraphicStyle style;
m_state->updateStyle(method, style);
auto shape=MWAWGraphicShape::circle(MWAWBox2f(m_state->m_circle));
MWAWPosition pos;
m_state->updatePosition(shape.getBdBox(), pos);
listener->insertShape(pos,shape, style);
}
void ApplePictParser::drawPie(DrawingMethod method, int startAngle, int dAngle)
{
MWAWListenerPtr listener=getGraphicListener();
if (m_state->isInvisible(method) || !listener)
return;
MWAWGraphicStyle style;
m_state->updateStyle(method, style);
//
int angle[2] = { 90-startAngle-dAngle, 90-startAngle };
if (dAngle<0) {
angle[0]=90-startAngle;
angle[1]=90-startAngle-dAngle;
}
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;
}
}
MWAWVec2f axis = 0.5f*MWAWVec2f(m_state->m_pie.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]) ? 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];
}
MWAWVec2f center(m_state->m_pie.center());
MWAWBox2f realBox(MWAWVec2f(center[0]+minVal[0],center[1]+minVal[1]),
MWAWVec2f(center[0]+maxVal[0],center[1]+maxVal[1]));
auto shape = method==D_FRAME ?
MWAWGraphicShape::arc(realBox, MWAWBox2f(m_state->m_pie), MWAWVec2f(float(angle[0]),float(angle[1]))) :
MWAWGraphicShape::pie(realBox, MWAWBox2f(m_state->m_pie), MWAWVec2f(float(angle[0]),float(angle[1])));
MWAWPosition pos;
m_state->updatePosition(shape.getBdBox(), pos);
listener->insertShape(pos,shape, style);
}
void ApplePictParser::drawPolygon(ApplePictParser::DrawingMethod method)
{
if (m_state->m_points.empty()) {
MWAW_DEBUG_MSG(("ApplePictParser::drawPolygon: can not find the main polygon\n"));
return;
}
MWAWListenerPtr listener=getGraphicListener();
if (m_state->isInvisible(method) || !listener)
return;
// first compute the bdbox
MWAWGraphicShape shape;
shape.m_type=MWAWGraphicShape::Polygon;
MWAWBox2f box(MWAWVec2f(m_state->m_points[0]),MWAWVec2f(m_state->m_points[0]));
shape.m_vertices.push_back(MWAWVec2f(m_state->m_points[0]));
for (size_t i=1; i<m_state->m_points.size(); ++i) {
box=box.getUnion(MWAWBox2f(MWAWVec2f(m_state->m_points[i]),MWAWVec2f(m_state->m_points[i])));
shape.m_vertices.push_back(MWAWVec2f(m_state->m_points[i]));
}
shape.m_bdBox=box;
MWAWGraphicStyle style;
m_state->updateStyle(method, style);
MWAWPosition pos;
m_state->updatePosition(shape.getBdBox(), pos);
listener->insertShape(pos,shape, style);
}
void ApplePictParser::drawText(MWAWEntry const &entry)
{
MWAWListenerPtr listener=getGraphicListener();
if (!listener || !listener->canWriteText()) {
MWAW_DEBUG_MSG(("ApplePictParser::drawText: can not find the listener\n"));
return;
}
MWAWGraphicStyle style;
m_state->updateStyle(D_TEXT, style);
MWAWFont font(m_state->m_font);
font.setColor(style.m_lineColor);
listener->setFont(font);
if (!entry.valid())
return;
MWAWInputStreamPtr input = getInput();
long actPos=input->tell();
input->seek(entry.begin(), librevenge::RVNG_SEEK_SET);
long endPos=entry.end();
while (!input->isEnd() && input->tell()<endPos) {
auto c = static_cast<char>(input->readULong(1));
if (c==0) {
MWAW_DEBUG_MSG(("ApplePictParser::drawText: find char 0\n"));
continue;
}
switch (c) {
case 9:
listener->insertTab();
break;
case 0xd:
listener->insertEOL();
break;
default:
listener->insertCharacter(static_cast<unsigned char>(c), input, entry.end());
break;
}
}
input->seek(actPos, librevenge::RVNG_SEEK_SET);
}
void ApplePictParser::drawBitmap(ApplePictParserInternal::Bitmap const &bitmap)
{
MWAWListenerPtr listener=getGraphicListener();
if (!listener)
return;
MWAWEmbeddedObject picture;
if (!bitmap.get(picture))
return;
MWAWGraphicStyle style;
MWAWPosition pos;
if (bitmap.m_dst.size()[0]>0 && bitmap.m_dst.size()[1]>0)
m_state->updatePosition(MWAWBox2f(bitmap.m_dst), pos);
else
m_state->updatePosition(MWAWBox2f(bitmap.m_rect), pos);
listener->insertPicture(pos,picture);
}
void ApplePictParser::drawPixmap(ApplePictParserInternal::Pixmap const &pixmap)
{
MWAWListenerPtr listener=getGraphicListener();
if (!listener)
return;
MWAWEmbeddedObject picture;
if (!pixmap.get(picture))
return;
MWAWGraphicStyle style;
MWAWPosition pos;
m_state->updatePosition(MWAWBox2f(pixmap.m_dst), pos);
listener->insertPicture(pos,picture);
}
// vim: set filetype=cpp tabstop=2 shiftwidth=2 cindent autoindent smartindent noexpandtab: