/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
/*
* This file is part of the LibreOffice project.
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*
* This file incorporates work covered by the following license notice:
*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed
* with this work for additional information regarding copyright
* ownership. The ASF licenses this file to you under the Apache
* License, Version 2.0 (the "License"); you may not use this file
* except in compliance with the License. You may obtain a copy of
* the License at http://www.apache.org/licenses/LICENSE-2.0 .
*/
#include <osl/thread.h>
#include <o3tl/safeint.hxx>
#include <tools/poly.hxx>
#include <tools/fract.hxx>
#include <tools/stream.hxx>
#include <sal/log.hxx>
#include <vcl/graph.hxx>
#include <vcl/dibtools.hxx>
#include <vcl/virdev.hxx>
#include <vcl/lineinfo.hxx>
#include <vcl/gdimtf.hxx>
#include <filter/MetReader.hxx>
#include <basegfx/numeric/ftools.hxx>
#include <cmath>
#include <memory>
class FilterConfigItem;
namespace {
enum PenStyle { PEN_NULL, PEN_SOLID, PEN_DOT, PEN_DASH, PEN_DASHDOT };
}
// -----------------------------Field Types-------------------------------
#define BegDocumnMagic 0xA8A8 /* Begin Document */
#define EndDocumnMagic 0xA8A9 /* End Document */
#define BegResGrpMagic 0xC6A8 /* Begin Resource Group */
#define EndResGrpMagic 0xC6A9 /* End Resource Group */
#define BegColAtrMagic 0x77A8 /* Begin Color Attribute Table */
#define EndColAtrMagic 0x77A9 /* End Color Attribute Table */
#define BlkColAtrMagic 0x77B0 /* Color Attribute Table */
#define MapColAtrMagic 0x77AB /* Map Color Attribute Table */
#define BegImgObjMagic 0xFBA8 /* Begin Image Object */
#define EndImgObjMagic 0xFBA9 /* End Image Object */
#define DscImgObjMagic 0xFBA6 /* Image Data Descriptor */
#define DatImgObjMagic 0xFBEE /* Image Picture Data */
#define BegObEnv1Magic 0xC7A8 /* Begin Object Environment Group */
#define EndObEnv1Magic 0xC7A9 /* End Object Environment Group */
#define BegGrfObjMagic 0xBBA8 /* Begin Graphics Object */
#define EndGrfObjMagic 0xBBA9 /* End Graphics Object */
#define DscGrfObjMagic 0xBBA6 /* Graphics Data Descriptor */
#define DatGrfObjMagic 0xBBEE /* Graphics Data */
#define MapCodFntMagic 0x8AAB /* Map Coded Font */
#define MapDatResMagic 0xC3AB /* Map Data Resource */
// -----------------------------Order Types-------------------------------
#define GOrdGivArc 0xC6 /* 1 Arc at given position */
#define GOrdCurArc 0x86 /* 1 Arc at current position */
#define GOrdGivBzr 0xE5 /* 1 Beziercurve at given position */
#define GOrdCurBzr 0xA5 /* 1 Beziercurve at current position */
#define GOrdGivBox 0xC0 /* 1 Box at given position */
#define GOrdCurBox 0x80 /* 1 Box at current position */
#define GOrdGivFil 0xC5 /* 1 Fillet at given position */
#define GOrdCurFil 0x85 /* 1 Fillet at current position */
#define GOrdGivCrc 0xC7 /* 1 Full arc (circle) at given position */
#define GOrdCurCrc 0x87 /* 1 Full arc (circle) at current position */
#define GOrdGivLin 0xC1 /* 1 Line at given position */
#define GOrdCurLin 0x81 /* 1 Line at current position */
#define GOrdGivMrk 0xC2 /* 1 Marker at given position */
#define GOrdCurMrk 0x82 /* 1 Marker at current position */
#define GOrdGivArP 0xE3 /* 1 Partial arc at given position */
#define GOrdCurArP 0xA3 /* 1 Partial arc at current position */
#define GOrdGivRLn 0xE1 /* 1 Relative line at given position */
#define GOrdCurRLn 0xA1 /* 1 Relative line at current position */
#define GOrdGivSFl 0xE4 /* 1 Sharp fillet at given position */
#define GOrdCurSFl 0xA4 /* 1 Sharp fillet at current position */
#define GOrdGivStM 0xF1 /* 1 Character string move at given position */
#define GOrdCurStM 0xB1 /* 1 Character string move at current position */
#define GOrdGivStr 0xC3 /* 1 Character string at given position */
#define GOrdCurStr 0x83 /* 1 Character string at current position */
#define GOrdGivStx 0xFEF0 /* 2 Character string extended at given position */
#define GOrdCurStx 0xFEB0 /* 2 Character string extended at current position */
#define GOrdGivImg 0xD1 /* 1 Begin Image at given position */
#define GOrdCurImg 0x91 /* 1 Begin Image at current position */
#define GOrdImgDat 0x92 /* 1 Image data */
#define GOrdEndImg 0x93 /* 1 End Image */
#define GOrdBegAra 0x68 /* 0 1 Begin area */
#define GOrdEndAra 0x60 /* 1 End area */
#define GOrdBegElm 0xD2 /* 1 Begin element */
#define GOrdEndElm 0x49 /* 0 1 End element */
#define GOrdBegPth 0xD0 /* 1 Begin path */
#define GOrdEndPth 0x7F /* 0 1 End path */
#define GOrdFilPth 0xD7 /* 1 Fill path */
#define GOrdModPth 0xD8 /* 1 Modify path */
#define GOrdOutPth 0xD4 /* 1 Outline path */
#define GOrdSClPth 0xB4 /* 1 Set clip path */
#define GOrdNopNop 0x00 /* 0 0 No operation */
#define GOrdRemark 0x01 /* 1 Comment */
#define GOrdSegLab 0xD3 /* 1 Label */
#define GOrdBitBlt 0xD6 /* 1 Bitblt */
#define GOrdCalSeg 0x07 /* 1 Call Segment */
#define GOrdSSgBnd 0x32 /* 1 Set segment boundary */
#define GOrdSegChr 0x04 /* 1 Segment characteristics */
#define GOrdCloFig 0x7D /* 0 1 Close Figure */
#define GOrdEndSym 0xFF /* 0 0 End of symbol definition */
#define GOrdEndPlg 0x3E /* 0 1 End prolog */
#define GOrdEscape 0xD5 /* 1 Escape */
#define GOrdExtEsc 0xFED5 /* 2 Extended Escape */
#define GOrdPolygn 0xF3 /* 2 Polygons */
#define GOrdStkPop 0x3F /* 0 1 Pop */
#define GOrdSIvAtr 0x14 /* 1 Set individual attribute */
#define GOrdPIvAtr 0x54 /* 1 Push and set individual attribute */
#define GOrdSColor 0x0A /* 0 1 Set color */
#define GOrdPColor 0x4A /* 0 1 Push and set color */
#define GOrdSIxCol 0xA6 /* 1 Set indexed color */
#define GOrdPIxCol 0xE6 /* 1 Push and set indexed color */
#define GOrdSXtCol 0x26 /* 1 Set extended color */
#define GOrdPXtCol 0x66 /* 1 Push and set extended color */
#define GOrdSBgCol 0x25 /* 1 Set background color */
#define GOrdPBgCol 0x65 /* 1 Push and set background color */
#define GOrdSBxCol 0xA7 /* 1 Set background indexed color */
#define GOrdPBxCol 0xE7 /* 1 Push and set background indexed color */
#define GOrdSMixMd 0x0C /* 0 1 Set mix */
#define GOrdPMixMd 0x4C /* 0 1 Push and set mix */
#define GOrdSBgMix 0x0D /* 0 1 Set background mix */
#define GOrdPBgMix 0x4D /* 0 1 Push and set background mix */
#define GOrdSPtSet 0x08 /* 0 1 Set pattern set */
#define GOrdPPtSet 0x48 /* 0 1 Push and set pattern set */
#define GOrdSPtSym 0x28 /* 0 1 Set pattern symbol */
#define GOrdPPtSym 0x09 /* 0 1 Push and set pattern symbol */
#define GOrdSPtRef 0xA0 /* 1 Set model pattern reference */
#define GOrdPPtRef 0xE0 /* 1 Push and set pattern reference point */
#define GOrdSLnEnd 0x1A /* 0 1 Set line end */
#define GOrdPLnEnd 0x5A /* 0 1 Push and set line end */
#define GOrdSLnJoi 0x1B /* 0 1 Set line join */
#define GOrdPLnJoi 0x5B /* 0 1 Push and set line join */
#define GOrdSLnTyp 0x18 /* 0 1 Set line type */
#define GOrdPLnTyp 0x58 /* 0 1 Push and set line type */
#define GOrdSLnWdt 0x19 /* 0 1 Set line width */
#define GOrdPLnWdt 0x59 /* 0 1 Push and set line width */
#define GOrdSFrLWd 0x11 /* 1 Set fractional line width */
#define GOrdPFrLWd 0x51 /* 1 Push and set fractional line width */
#define GOrdSStLWd 0x15 /* 1 Set stroke line width */
#define GOrdPStLWd 0x55 /* 1 Push and set stroke line width */
#define GOrdSChDir 0x3A /* 0 1 Set character direction */
#define GOrdPChDir 0x7A /* 0 1 Push and set character direction */
#define GOrdSChPrc 0x39 /* 0 1 Set character precision */
#define GOrdPChPrc 0x79 /* 0 1 Push and set character precision */
#define GOrdSChSet 0x38 /* 0 1 Set character set */
#define GOrdPChSet 0x78 /* 0 1 Push and set character set */
#define GOrdSChAng 0x34 /* 1 Set character angle */
#define GOrdPChAng 0x74 /* 1 Push and set character angle */
#define GOrdSChBrx 0x05 /* 1 Set character break extra */
#define GOrdPChBrx 0x45 /* 1 Push and set character break extra */
#define GOrdSChCel 0x33 /* 1 Set character cell */
#define GOrdPChCel 0x03 /* 1 Push and set character cell */
#define GOrdSChXtr 0x17 /* 1 Set character extra */
#define GOrdPChXtr 0x57 /* 1 Push and set character extra */
#define GOrdSChShr 0x35 /* 1 Set character shear */
#define GOrdPChShr 0x75 /* 1 Push and set character shear */
#define GOrdSTxAlg 0x36 /* 0 2 Set text allingment */
#define GOrdPTxAlg 0x76 /* 0 2 Push and set text allingment */
#define GOrdSMkPrc 0x3B /* 0 1 Set marker precision */
#define GOrdPMkPrc 0x7B /* 0 1 Push and set marker precision */
#define GOrdSMkSet 0x3C /* 0 1 Set marker set */
#define GOrdPMkSet 0x7C /* 0 1 Push and set marker set */
#define GOrdSMkSym 0x29 /* 0 1 Set marker symbol */
#define GOrdPMkSym 0x69 /* 0 1 Push and set marker symbol */
#define GOrdSMkCel 0x37 /* 1 Set marker cell */
#define GOrdPMkCel 0x77 /* 1 Push and set marker cell */
#define GOrdSArcPa 0x22 /* 1 Set arc parameters */
#define GOrdPArcPa 0x62 /* 1 Push and set arc parameters */
#define GOrdSCrPos 0x21 /* 1 Set current position */
#define GOrdPCrPos 0x61 /* 1 Push and set current position */
#define GOrdSMdTrn 0x24 /* 1 Set model transform */
#define GOrdPMdTrn 0x64 /* 1 Push and set model transform */
#define GOrdSPkIdn 0x43 /* 1 Set pick identifier */
#define GOrdPPkIdn 0x23 /* 1 Push and set pick identifier */
#define GOrdSVwTrn 0x31 /* 1 Set viewing transform */
#define GOrdSVwWin 0x27 /* 1 Set viewing window */
#define GOrdPVwWin 0x67 /* 1 Push and set viewing window */
//============================ OS2METReader ==================================
namespace {
struct OSPalette {
OSPalette * pSucc;
sal_uInt32 * p0RGB; // May be NULL!
size_t nSize;
};
struct OSArea {
OSArea * pSucc;
sal_uInt8 nFlags;
tools::PolyPolygon aPPoly;
bool bClosed;
Color aCol;
Color aBgCol;
RasterOp eMix;
RasterOp eBgMix;
bool bFill;
OSArea()
: pSucc(nullptr)
, nFlags(0)
, bClosed(false)
, eMix(RasterOp::OverPaint)
, eBgMix(RasterOp::OverPaint)
, bFill(false)
{
}
};
struct OSPath
{
OSPath* pSucc;
sal_uInt32 nID;
tools::PolyPolygon aPPoly;
bool bClosed;
bool bStroke;
OSPath()
: pSucc(nullptr)
, nID(0)
, bClosed(false)
, bStroke(false)
{
}
};
struct OSFont {
OSFont * pSucc;
sal_uInt32 nID;
vcl::Font aFont;
OSFont()
: pSucc(nullptr)
, nID(0)
{
}
};
struct OSBitmap {
OSBitmap * pSucc;
sal_uInt32 nID;
BitmapEx aBitmapEx;
// required during reading of the bitmap:
SvStream * pBMP; // pointer to temporary Windows-BMP file or NULL
sal_uInt32 nWidth, nHeight;
sal_uInt16 nBitsPerPixel;
sal_uInt32 nMapPos;
};
struct OSAttr
{
OSAttr * pSucc;
sal_uInt16 nPushOrder;
sal_uInt8 nIvAttrA, nIvAttrP; // special variables for the Order "GOrdPIvAtr"
Color aLinCol;
Color aLinBgCol;
RasterOp eLinMix;
RasterOp eLinBgMix;
Color aChrCol;
Color aChrBgCol;
RasterOp eChrMix;
RasterOp eChrBgMix;
Color aMrkCol;
Color aMrkBgCol;
RasterOp eMrkMix;
RasterOp eMrkBgMix;
Color aPatCol;
Color aPatBgCol;
RasterOp ePatMix;
RasterOp ePatBgMix;
Color aImgCol;
Color aImgBgCol;
RasterOp eImgMix;
RasterOp eImgBgMix;
sal_Int32 nArcP, nArcQ, nArcR, nArcS;
Degree10 nChrAng;
sal_Int32 nChrCellHeight;
sal_uInt32 nChrSet;
Point aCurPos;
PenStyle eLinStyle;
sal_uInt16 nLinWidth;
Size aMrkCellSize;
sal_uInt8 nMrkPrec;
sal_uInt8 nMrkSet;
sal_uInt8 nMrkSymbol;
bool bFill;
sal_uInt16 nStrLinWidth;
OSAttr()
: pSucc(nullptr)
, nPushOrder(0)
, nIvAttrA(0)
, nIvAttrP(0)
, eLinMix(RasterOp::OverPaint)
, eLinBgMix(RasterOp::OverPaint)
, eChrMix(RasterOp::OverPaint)
, eChrBgMix(RasterOp::OverPaint)
, eMrkMix(RasterOp::OverPaint)
, eMrkBgMix(RasterOp::OverPaint)
, ePatMix(RasterOp::OverPaint)
, ePatBgMix(RasterOp::OverPaint)
, eImgMix(RasterOp::OverPaint)
, eImgBgMix(RasterOp::OverPaint)
, nArcP(0)
, nArcQ(0)
, nArcR(0)
, nArcS(0)
, nChrAng(0)
, nChrCellHeight(0)
, nChrSet(0)
, eLinStyle(PEN_NULL)
, nLinWidth(0)
, nMrkPrec(0)
, nMrkSet(0)
, nMrkSymbol(0)
, bFill(false)
, nStrLinWidth(0)
{
}
};
class OS2METReader {
private:
int ErrorCode;
SvStream * pOS2MET; // the OS2MET file to be read
VclPtr<VirtualDevice> pVirDev; // here the drawing methods are being called
// While doing this a recording in the GDIMetaFile
// will take place.
tools::Rectangle aBoundingRect; // bounding rectangle as stored in the file
tools::Rectangle aCalcBndRect; // bounding rectangle calculated on our own
MapMode aGlobMapMode; // resolution of the picture
bool bCoord32;
OSPalette * pPaletteStack;
LineInfo aLineInfo;
OSArea * pAreaStack; // Areas that are being worked on
OSPath * pPathStack; // Paths that are being worked on
OSPath * pPathList; // finished Paths
OSFont * pFontList;
OSBitmap * pBitmapList;
OSAttr aDefAttr;
OSAttr aAttr;
OSAttr * pAttrStack;
std::unique_ptr<SvMemoryStream> xOrdFile;
void AddPointsToPath(const tools::Polygon & rPoly);
void AddPointsToArea(const tools::Polygon & rPoly);
void CloseFigure();
void PushAttr(sal_uInt16 nPushOrder);
void PopAttr();
void ChangeBrush( const Color& rPatColor, bool bFill );
void SetPen( const Color& rColor, sal_uInt16 nStrLinWidth = 0, PenStyle ePenStyle = PEN_SOLID );
void SetRasterOp(RasterOp eROP);
void SetPalette0RGB(sal_uInt16 nIndex, sal_uInt32 nCol);
sal_uInt32 GetPalette0RGB(sal_uInt32 nIndex) const;
// gets color from palette, or, if it doesn't exist,
// interprets nIndex as immediate RGB value.
Color GetPaletteColor(sal_uInt32 nIndex) const;
bool IsLineInfo() const;
void DrawPolyLine( const tools::Polygon& rPolygon );
void DrawPolygon( const tools::Polygon& rPolygon );
void DrawPolyPolygon( const tools::PolyPolygon& rPolygon );
sal_uInt16 ReadBigEndianWord();
sal_uInt32 ReadBigEndian3BytesLong();
sal_uInt32 ReadLittleEndian3BytesLong();
sal_Int32 ReadCoord(bool b32);
Point ReadPoint( const bool bAdjustBoundRect = true );
static RasterOp OS2MixToRasterOp(sal_uInt8 nMix);
void ReadLine(bool bGivenPos, sal_uInt16 nOrderLen);
void ReadRelLine(bool bGivenPos, sal_uInt16 nOrderLen);
void ReadBox(bool bGivenPos);
void ReadBitBlt();
void ReadChrStr(bool bGivenPos, bool bMove, bool bExtra, sal_uInt16 nOrderLen);
void ReadArc(bool bGivenPos);
void ReadFullArc(bool bGivenPos, sal_uInt16 nOrderSize);
void ReadPartialArc(bool bGivenPos, sal_uInt16 nOrderSize);
void ReadPolygons();
void ReadBezier(bool bGivenPos, sal_uInt16 nOrderLen);
void ReadFillet(bool bGivenPos, sal_uInt16 nOrderLen);
void ReadFilletSharp(bool bGivenPos, sal_uInt16 nOrderLen);
void ReadMarker(bool bGivenPos, sal_uInt16 nOrderLen);
void ReadOrder(sal_uInt16 nOrderID, sal_uInt16 nOrderLen);
void ReadDsc(sal_uInt16 nDscID);
void ReadImageData(sal_uInt16 nDataID, sal_uInt16 nDataLen);
void ReadFont(sal_uInt16 nFieldSize);
void ReadField(sal_uInt16 nFieldType, sal_uInt16 nFieldSize);
public:
OS2METReader();
~OS2METReader();
void ReadOS2MET( SvStream & rStreamOS2MET, GDIMetaFile & rGDIMetaFile );
// Reads from the stream an OS2MET file and fills up the GDIMetaFile
};
}
//=================== Methods of OS2METReader ==============================
OS2METReader::OS2METReader()
: ErrorCode(0)
, pOS2MET(nullptr)
, pVirDev(VclPtr<VirtualDevice>::Create())
, bCoord32(false)
, pPaletteStack(nullptr)
, pAreaStack(nullptr)
, pPathStack(nullptr)
, pPathList(nullptr)
, pFontList(nullptr)
, pBitmapList(nullptr)
, pAttrStack(nullptr)
{
pVirDev->EnableOutput(false);
}
OS2METReader::~OS2METReader()
{
pVirDev.disposeAndClear();
while (pAreaStack!=nullptr) {
OSArea * p=pAreaStack;
pAreaStack=p->pSucc;
delete p;
}
while (pPathStack!=nullptr) {
OSPath * p=pPathStack;
pPathStack=p->pSucc;
delete p;
}
while (pPathList!=nullptr) {
OSPath * p=pPathList;
pPathList=p->pSucc;
delete p;
}
while (pFontList!=nullptr) {
OSFont * p=pFontList;
pFontList=p->pSucc;
delete p;
}
while (pBitmapList!=nullptr) {
OSBitmap * p=pBitmapList;
pBitmapList=p->pSucc;
delete p->pBMP;
delete p;
}
while (pAttrStack!=nullptr) {
OSAttr * p=pAttrStack;
pAttrStack=p->pSucc;
delete p;
}
while (pPaletteStack!=nullptr) {
OSPalette * p=pPaletteStack;
pPaletteStack=p->pSucc;
delete[] p->p0RGB;
delete p;
}
}
bool OS2METReader::IsLineInfo() const
{
return ( ! ( aLineInfo.IsDefault() || ( aLineInfo.GetStyle() == LineStyle::NONE ) || ( pVirDev->GetLineColor() == COL_TRANSPARENT ) ) );
}
void OS2METReader::DrawPolyLine( const tools::Polygon& rPolygon )
{
if ( aLineInfo.GetStyle() == LineStyle::Dash || ( aLineInfo.GetWidth() > 1 ) )
pVirDev->DrawPolyLine( rPolygon, aLineInfo );
else
pVirDev->DrawPolyLine( rPolygon );
}
void OS2METReader::DrawPolygon( const tools::Polygon& rPolygon )
{
if ( IsLineInfo() )
{
pVirDev->Push( vcl::PushFlags::LINECOLOR );
pVirDev->SetLineColor( COL_TRANSPARENT );
pVirDev->DrawPolygon( rPolygon );
pVirDev->Pop();
pVirDev->DrawPolyLine( rPolygon, aLineInfo );
}
else
pVirDev->DrawPolygon( rPolygon );
}
void OS2METReader::DrawPolyPolygon( const tools::PolyPolygon& rPolyPolygon )
{
if ( IsLineInfo() )
{
pVirDev->Push( vcl::PushFlags::LINECOLOR );
pVirDev->SetLineColor( COL_TRANSPARENT );
pVirDev->DrawPolyPolygon( rPolyPolygon );
pVirDev->Pop();
for ( auto const& rPoly : rPolyPolygon )
pVirDev->DrawPolyLine( rPoly, aLineInfo );
}
else
pVirDev->DrawPolyPolygon( rPolyPolygon );
}
void OS2METReader::AddPointsToArea(const tools::Polygon & rPoly)
{
sal_uInt16 nOldSize, nNewSize,i;
if (pAreaStack==nullptr || rPoly.GetSize()==0) return;
tools::PolyPolygon * pPP=&(pAreaStack->aPPoly);
if (pPP->Count()==0 || pAreaStack->bClosed) pPP->Insert(rPoly);
else {
tools::Polygon aLastPoly(pPP->GetObject(pPP->Count()-1));
nOldSize=aLastPoly.GetSize();
if (nOldSize && aLastPoly.GetPoint(nOldSize-1)==rPoly.GetPoint(0)) nOldSize--;
nNewSize=nOldSize+rPoly.GetSize();
aLastPoly.SetSize(nNewSize);
for (i=nOldSize; i<nNewSize; i++) {
aLastPoly.SetPoint(rPoly.GetPoint(i-nOldSize),i);
}
pPP->Replace(aLastPoly,pPP->Count()-1);
}
pAreaStack->bClosed=false;
}
void OS2METReader::AddPointsToPath(const tools::Polygon & rPoly)
{
sal_uInt16 nOldSize, nNewSize,i;
if (pPathStack==nullptr || rPoly.GetSize()==0) return;
tools::PolyPolygon * pPP=&(pPathStack->aPPoly);
if (pPP->Count()==0 /*|| pPathStack->bClosed==sal_True*/) pPP->Insert(rPoly);
else {
tools::Polygon aLastPoly(pPP->GetObject(pPP->Count()-1));
nOldSize=aLastPoly.GetSize();
if (nOldSize && aLastPoly.GetPoint(nOldSize-1)!=rPoly.GetPoint(0)) pPP->Insert(rPoly);
else {
nOldSize--;
nNewSize=nOldSize+rPoly.GetSize();
aLastPoly.SetSize(nNewSize);
for (i=nOldSize; i<nNewSize; i++) {
aLastPoly.SetPoint(rPoly.GetPoint(i-nOldSize),i);
}
pPP->Replace(aLastPoly,pPP->Count()-1);
}
}
pPathStack->bClosed=false;
}
void OS2METReader::CloseFigure()
{
if (pAreaStack!=nullptr) pAreaStack->bClosed=true;
else if (pPathStack!=nullptr) pPathStack->bClosed=true;
}
void OS2METReader::PushAttr(sal_uInt16 nPushOrder)
{
OSAttr * p;
p=new OSAttr;
*p=aAttr;
p->pSucc=pAttrStack; pAttrStack=p;
p->nPushOrder=nPushOrder;
}
void OS2METReader::PopAttr()
{
OSAttr * p=pAttrStack;
if (p==nullptr) return;
switch (p->nPushOrder) {
case GOrdPIvAtr:
switch (p->nIvAttrA) {
case 1: switch (p->nIvAttrP) {
case 1: aAttr.aLinCol=p->aLinCol; break;
case 2: aAttr.aChrCol=p->aChrCol; break;
case 3: aAttr.aMrkCol=p->aMrkCol; break;
case 4: aAttr.aPatCol=p->aPatCol; break;
case 5: aAttr.aImgCol=p->aImgCol; break;
} break;
case 2: switch (p->nIvAttrP) {
case 1: aAttr.aLinBgCol=p->aLinBgCol; break;
case 2: aAttr.aChrBgCol=p->aChrBgCol; break;
case 3: aAttr.aMrkBgCol=p->aMrkBgCol; break;
case 4: aAttr.aPatBgCol=p->aPatBgCol; break;
case 5: aAttr.aImgBgCol=p->aImgBgCol; break;
} break;
case 3: switch (p->nIvAttrP) {
case 1: aAttr.eLinMix=p->eLinMix; break;
case 2: aAttr.eChrMix=p->eChrMix; break;
case 3: aAttr.eMrkMix=p->eMrkMix; break;
case 4: aAttr.ePatMix=p->ePatMix; break;
case 5: aAttr.eImgMix=p->eImgMix; break;
} break;
case 4: switch (p->nIvAttrP) {
case 1: aAttr.eLinBgMix=p->eLinBgMix; break;
case 2: aAttr.eChrBgMix=p->eChrBgMix; break;
case 3: aAttr.eMrkBgMix=p->eMrkBgMix; break;
case 4: aAttr.ePatBgMix=p->ePatBgMix; break;
case 5: aAttr.eImgBgMix=p->eImgBgMix; break;
} break;
}
break;
case GOrdPLnTyp: aAttr.eLinStyle=p->eLinStyle; break;
case GOrdPLnWdt: aAttr.nLinWidth=p->nLinWidth; break;
case GOrdPStLWd: aAttr.nStrLinWidth=p->nStrLinWidth; break;
case GOrdPChSet: aAttr.nChrSet=p->nChrSet; break;
case GOrdPChAng: aAttr.nChrAng=p->nChrAng; break;
case GOrdPMixMd:
aAttr.eLinMix=p->eLinMix;
aAttr.eChrMix=p->eChrMix;
aAttr.eMrkMix=p->eMrkMix;
aAttr.ePatMix=p->ePatMix;
aAttr.eImgMix=p->eImgMix;
break;
case GOrdPBgMix:
aAttr.eLinBgMix=p->eLinBgMix;
aAttr.eChrBgMix=p->eChrBgMix;
aAttr.eMrkBgMix=p->eMrkBgMix;
aAttr.ePatBgMix=p->ePatBgMix;
aAttr.eImgBgMix=p->eImgBgMix;
break;
case GOrdPPtSym: aAttr.bFill = p->bFill; break;
case GOrdPColor:
case GOrdPIxCol:
case GOrdPXtCol:
aAttr.aLinCol=p->aLinCol;
aAttr.aChrCol=p->aChrCol;
aAttr.aMrkCol=p->aMrkCol;
aAttr.aPatCol=p->aPatCol;
aAttr.aImgCol=p->aImgCol;
break;
case GOrdPBgCol:
case GOrdPBxCol:
aAttr.aLinBgCol=p->aLinBgCol;
aAttr.aChrBgCol=p->aChrBgCol;
aAttr.aMrkBgCol=p->aMrkBgCol;
aAttr.aPatBgCol=p->aPatBgCol;
aAttr.aImgBgCol=p->aImgBgCol;
break;
case GOrdPMkPrc: aAttr.nMrkPrec=aDefAttr.nMrkPrec; break;
case GOrdPMkSet: aAttr.nMrkSet=aDefAttr.nMrkSet; break;
case GOrdPMkSym: aAttr.nMrkSymbol=aDefAttr.nMrkSymbol; break;
case GOrdPMkCel: aAttr.aMrkCellSize=aDefAttr.aMrkCellSize; break;
case GOrdPArcPa:
aAttr.nArcP=p->nArcP; aAttr.nArcQ=p->nArcQ;
aAttr.nArcR=p->nArcR; aAttr.nArcS=p->nArcS;
break;
case GOrdPCrPos:
aAttr.aCurPos=p->aCurPos;
break;
}
pAttrStack=p->pSucc;
delete p;
}
void OS2METReader::ChangeBrush(const Color& rPatColor, bool bFill )
{
Color aColor;
if( bFill )
aColor = rPatColor;
else
aColor = COL_TRANSPARENT;
if( pVirDev->GetFillColor() != aColor )
pVirDev->SetFillColor( aColor );
}
void OS2METReader::SetPen( const Color& rColor, sal_uInt16 nLineWidth, PenStyle ePenStyle )
{
LineStyle eLineStyle( LineStyle::Solid );
if ( pVirDev->GetLineColor() != rColor )
pVirDev->SetLineColor( rColor );
aLineInfo.SetWidth( nLineWidth );
if (ePenStyle == PEN_NULL)
eLineStyle = LineStyle::NONE;
else if (ePenStyle == PEN_DASHDOT || ePenStyle == PEN_DOT || ePenStyle == PEN_DASH)
{
sal_uInt16 nDotCount = 0;
sal_uInt16 nDashCount = 0;
if (ePenStyle == PEN_DASHDOT)
{
nDashCount++;
nDotCount++;
}
else if (ePenStyle == PEN_DOT)
nDotCount++;
else // (ePenStyle == PEN_DASH)
nDashCount++;
aLineInfo.SetDotCount( nDotCount );
aLineInfo.SetDashCount( nDashCount );
aLineInfo.SetDistance( nLineWidth );
aLineInfo.SetDotLen( nLineWidth );
aLineInfo.SetDashLen( nLineWidth << 2 );
eLineStyle = LineStyle::Dash;
}
aLineInfo.SetStyle( eLineStyle );
}
void OS2METReader::SetRasterOp(RasterOp eROP)
{
if (pVirDev->GetRasterOp()!=eROP) pVirDev->SetRasterOp(eROP);
}
void OS2METReader::SetPalette0RGB(sal_uInt16 nIndex, sal_uInt32 nCol)
{
if (pPaletteStack==nullptr) {
pPaletteStack=new OSPalette;
pPaletteStack->pSucc=nullptr;
pPaletteStack->p0RGB=nullptr;
pPaletteStack->nSize=0;
}
if (pPaletteStack->p0RGB==nullptr || nIndex>=pPaletteStack->nSize) {
sal_uInt32 * pOld0RGB=pPaletteStack->p0RGB;
size_t nOldSize = pPaletteStack->nSize;
if (pOld0RGB==nullptr) nOldSize=0;
pPaletteStack->nSize=2*(nIndex+1);
if (pPaletteStack->nSize<256) pPaletteStack->nSize=256;
pPaletteStack->p0RGB = new sal_uInt32[pPaletteStack->nSize];
for (size_t i=0; i < pPaletteStack->nSize; ++i)
{
if (i<nOldSize) pPaletteStack->p0RGB[i]=pOld0RGB[i];
else if (i==0) pPaletteStack->p0RGB[i]=0x00ffffff;
else pPaletteStack->p0RGB[i]=0;
}
delete[] pOld0RGB;
}
pPaletteStack->p0RGB[nIndex]=nCol;
}
sal_uInt32 OS2METReader::GetPalette0RGB(sal_uInt32 nIndex) const
{
if (pPaletteStack!=nullptr && pPaletteStack->p0RGB!=nullptr &&
pPaletteStack->nSize>nIndex) nIndex=pPaletteStack->p0RGB[nIndex];
return nIndex;
}
Color OS2METReader::GetPaletteColor(sal_uInt32 nIndex) const
{
nIndex=GetPalette0RGB(nIndex);
return Color(sal::static_int_cast< sal_uInt8 >((nIndex>>16)&0xff),
sal::static_int_cast< sal_uInt8 >((nIndex>>8)&0xff),
sal::static_int_cast< sal_uInt8 >(nIndex&0xff));
}
sal_uInt16 OS2METReader::ReadBigEndianWord()
{
sal_uInt8 nLo(0), nHi(0);
pOS2MET->ReadUChar( nHi ).ReadUChar( nLo );
return (static_cast<sal_uInt16>(nHi)<<8)|(static_cast<sal_uInt16>(nLo)&0x00ff);
}
sal_uInt32 OS2METReader::ReadBigEndian3BytesLong()
{
sal_uInt8 nHi(0);
pOS2MET->ReadUChar( nHi );
sal_uInt16 nLo = ReadBigEndianWord();
return ((static_cast<sal_uInt32>(nHi)<<16)&0x00ff0000)|static_cast<sal_uInt32>(nLo);
}
sal_uInt32 OS2METReader::ReadLittleEndian3BytesLong()
{
sal_uInt8 nHi(0), nMed(0), nLo(0);
pOS2MET->ReadUChar( nLo ).ReadUChar( nMed ).ReadUChar( nHi );
return ((static_cast<sal_uInt32>(nHi)&0xff)<<16)|((static_cast<sal_uInt32>(nMed)&0xff)<<8)|(static_cast<sal_uInt32>(nLo)&0xff);
}
sal_Int32 OS2METReader::ReadCoord(bool b32)
{
sal_Int32 l(0);
if (b32) pOS2MET->ReadInt32( l );
else { short s(0); pOS2MET->ReadInt16( s ); l = static_cast<sal_Int32>(s); }
return l;
}
Point OS2METReader::ReadPoint( const bool bAdjustBoundRect )
{
sal_Int32 x = ReadCoord(bCoord32);
sal_Int32 y = ReadCoord(bCoord32);
x=x-aBoundingRect.Left();
y=aBoundingRect.Bottom()-y;
if (bAdjustBoundRect)
{
if (x == SAL_MAX_INT32 || y == SAL_MAX_INT32)
pOS2MET->SetError(SVSTREAM_FILEFORMAT_ERROR);
else
aCalcBndRect.Union(tools::Rectangle(x, y, x + 1, y + 1));
}
return Point(x,y);
}
RasterOp OS2METReader::OS2MixToRasterOp(sal_uInt8 nMix)
{
switch (nMix) {
case 0x0c: return RasterOp::Invert;
case 0x04: return RasterOp::Xor;
case 0x0b: return RasterOp::Xor;
default: return RasterOp::OverPaint;
}
}
void OS2METReader::ReadLine(bool bGivenPos, sal_uInt16 nOrderLen)
{
sal_uInt16 i,nPolySize;
if (bCoord32) nPolySize=nOrderLen/8; else nPolySize=nOrderLen/4;
if (!bGivenPos) nPolySize++;
if (nPolySize==0) return;
tools::Polygon aPolygon(nPolySize);
for (i=0; i<nPolySize; i++) {
if (i==0 && !bGivenPos) aPolygon.SetPoint(aAttr.aCurPos,i);
else aPolygon.SetPoint(ReadPoint(),i);
}
aAttr.aCurPos=aPolygon.GetPoint(nPolySize-1);
if (pAreaStack!=nullptr) AddPointsToArea(aPolygon);
else if (pPathStack!=nullptr) AddPointsToPath(aPolygon);
else
{
SetPen( aAttr.aLinCol, aAttr.nStrLinWidth, aAttr.eLinStyle );
SetRasterOp(aAttr.eLinMix);
DrawPolyLine( aPolygon );
}
}
void OS2METReader::ReadRelLine(bool bGivenPos, sal_uInt16 nOrderLen)
{
sal_uInt16 i,nPolySize;
Point aP0;
if (bGivenPos) {
aP0=ReadPoint();
if (bCoord32) nOrderLen-=8; else nOrderLen-=4;
}
else aP0=aAttr.aCurPos;
if (nOrderLen > pOS2MET->remainingSize())
throw css::uno::Exception(u"attempt to read past end of input"_ustr, nullptr);
nPolySize=nOrderLen/2;
if (nPolySize==0) return;
tools::Polygon aPolygon(nPolySize);
for (i=0; i<nPolySize; i++) {
sal_Int8 nsignedbyte;
pOS2MET->ReadSChar( nsignedbyte ); aP0.AdjustX(static_cast<sal_Int32>(nsignedbyte));
pOS2MET->ReadSChar( nsignedbyte ); aP0.AdjustY(-static_cast<sal_Int32>(nsignedbyte));
aCalcBndRect.Union(tools::Rectangle(aP0,Size(1,1)));
aPolygon.SetPoint(aP0,i);
}
aAttr.aCurPos=aPolygon.GetPoint(nPolySize-1);
if (pAreaStack!=nullptr) AddPointsToArea(aPolygon);
else if (pPathStack!=nullptr) AddPointsToPath(aPolygon);
else
{
SetPen( aAttr.aLinCol, aAttr.nStrLinWidth, aAttr.eLinStyle );
SetRasterOp(aAttr.eLinMix);
DrawPolyLine( aPolygon );
}
}
void OS2METReader::ReadBox(bool bGivenPos)
{
sal_uInt8 nFlags;
Point P0;
pOS2MET->ReadUChar( nFlags );
pOS2MET->SeekRel(1);
if ( bGivenPos )
P0 = ReadPoint();
else
P0 = aAttr.aCurPos;
aAttr.aCurPos = ReadPoint();
sal_Int32 nHRound = ReadCoord(bCoord32);
sal_Int32 nVRound = ReadCoord(bCoord32);
if (!pOS2MET->good())
{
SAL_WARN("filter.os2met", "OS2METReader::ReadBox: short read");
return;
}
tools::Rectangle aBoxRect( P0, aAttr.aCurPos );
if ( pAreaStack )
AddPointsToArea( tools::Polygon( aBoxRect ) );
else if ( pPathStack )
AddPointsToPath( tools::Polygon( aBoxRect ) );
else
{
if ( nFlags & 0x20 )
SetPen( aAttr.aLinCol, aAttr.nStrLinWidth, aAttr.eLinStyle );
else
SetPen( COL_TRANSPARENT );
if ( nFlags & 0x40 )
{
ChangeBrush(aAttr.aPatCol, aAttr.bFill);
SetRasterOp(aAttr.ePatMix);
}
else
{
ChangeBrush( COL_TRANSPARENT, false );
SetRasterOp(aAttr.eLinMix);
}
if ( IsLineInfo() )
{
tools::Polygon aPolygon( aBoxRect, nHRound, nVRound );
if ( nFlags & 0x40 )
{
pVirDev->Push( vcl::PushFlags::LINECOLOR );
pVirDev->SetLineColor( COL_TRANSPARENT );
pVirDev->DrawRect( aBoxRect, nHRound, nVRound );
pVirDev->Pop();
}
pVirDev->DrawPolyLine( aPolygon, aLineInfo );
}
else
pVirDev->DrawRect( aBoxRect, nHRound, nVRound );
}
}
void OS2METReader::ReadBitBlt()
{
pOS2MET->SeekRel(4);
sal_uInt32 nID(0);
pOS2MET->ReadUInt32( nID );
pOS2MET->SeekRel(4);
Point aP1 = ReadPoint();
Point aP2 = ReadPoint();
if (aP1.X() > aP2.X()) { auto nt=aP1.X(); aP1.setX(aP2.X() ); aP2.setX(nt ); }
if (aP1.Y() > aP2.Y()) { auto nt=aP1.Y(); aP1.setY(aP2.Y() ); aP2.setY(nt ); }
Size aSize(aP2.X() - aP1.X(), aP2.Y() - aP1.Y());
OSBitmap* pB = pBitmapList;
while (pB!=nullptr && pB->nID!=nID) pB=pB->pSucc;
if (pB!=nullptr) {
SetRasterOp(aAttr.ePatMix);
pVirDev->DrawBitmapEx(aP1,aSize,pB->aBitmapEx);
}
}
void OS2METReader::ReadChrStr(bool bGivenPos, bool bMove, bool bExtra, sal_uInt16 nOrderLen)
{
Point aP0;
sal_uInt16 nLen;
OSFont * pF;
vcl::Font aFont;
Size aSize;
pF = pFontList;
while (pF!=nullptr && pF->nID!=aAttr.nChrSet) pF=pF->pSucc;
if (pF!=nullptr)
aFont = pF->aFont;
aFont.SetColor(aAttr.aChrCol);
aFont.SetFontSize(Size(0,aAttr.nChrCellHeight));
if ( aAttr.nChrAng )
aFont.SetOrientation(aAttr.nChrAng);
if (bGivenPos)
aP0 = ReadPoint();
else
aP0 = aAttr.aCurPos;
if (bExtra)
{
pOS2MET->SeekRel(2);
ReadPoint( false );
ReadPoint( false );
pOS2MET->ReadUInt16( nLen );
}
else
{
if ( !bGivenPos )
nLen = nOrderLen;
else if ( bCoord32 )
nLen = nOrderLen-8;
else
nLen = nOrderLen-4;
}
if (!pOS2MET->good() || nLen > pOS2MET->remainingSize())
throw css::uno::Exception(u"attempt to read past end of input"_ustr, nullptr);
std::unique_ptr<char[]> pChr(new char[nLen+1]);
for (sal_uInt16 i=0; i<nLen; i++)
pOS2MET->ReadChar( pChr[i] );
pChr[nLen] = 0;
OUString aStr( pChr.get(), strlen(pChr.get()), osl_getThreadTextEncoding() );
SetRasterOp(aAttr.eChrMix);
if (pVirDev->GetFont()!=aFont)
pVirDev->SetFont(aFont);
pVirDev->DrawText(aP0,aStr);
aSize = Size( pVirDev->GetTextWidth(aStr), pVirDev->GetTextHeight() );
if ( !aAttr.nChrAng )
{
aCalcBndRect.Union(tools::Rectangle( Point(aP0.X(),aP0.Y()-aSize.Height()),
Size(aSize.Width(),aSize.Height()*2)));
if (bMove)
aAttr.aCurPos = Point( aP0.X() + aSize.Width(), aP0.Y());
}
else
{
tools::Polygon aDummyPoly(4);
aDummyPoly.SetPoint( Point( aP0.X(), aP0.Y() ), 0); // TOP LEFT
aDummyPoly.SetPoint( Point( aP0.X(), aP0.Y() - aSize.Height() ), 1); // BOTTOM LEFT
aDummyPoly.SetPoint( Point( aP0.X() + aSize.Width(), aP0.Y() ), 2); // TOP RIGHT
aDummyPoly.SetPoint( Point( aP0.X() + aSize.Width(), aP0.Y() - aSize.Height() ), 3);// BOTTOM RIGHT
aDummyPoly.Rotate( aP0, aAttr.nChrAng );
if ( bMove )
aAttr.aCurPos = aDummyPoly.GetPoint( 0 );
aCalcBndRect.Union( tools::Rectangle( aDummyPoly.GetPoint( 0 ), aDummyPoly.GetPoint( 3 ) ) );
aCalcBndRect.Union( tools::Rectangle( aDummyPoly.GetPoint( 1 ), aDummyPoly.GetPoint( 2 ) ) );
}
}
void OS2METReader::ReadArc(bool bGivenPos)
{
Point aP1, aP2, aP3;
double x1,y1,x2,y2,x3,y3,p,q,cx,cy,ncx,ncy,r,rx,ry,w1,w3;
if (bGivenPos) aP1=ReadPoint(); else aP1=aAttr.aCurPos;
aP2=ReadPoint(); aP3=ReadPoint();
aAttr.aCurPos=aP3;
SetPen( aAttr.aLinCol, aAttr.nStrLinWidth, aAttr.eLinStyle );
SetRasterOp(aAttr.eLinMix);
// Ok, given are 3 point of the ellipse, and the relation
// of width and height (as p to q):
x1=aP1.X(); y1=aP1.Y();
x2=aP2.X(); y2=aP2.Y();
x3=aP3.X(); y3=aP3.Y();
p=aAttr.nArcP;q=aAttr.nArcQ;
// Calculation of the center point cx, cy of the ellipse:
ncy=2*p*p*((y3-y1)*(x2-x1)-(y1-y2)*(x1-x3));
ncx=2*q*q*(x2-x1);
if ( (ncx<0.001 && ncx>-0.001) || (ncy<0.001 && ncy>-0.001) ) {
// Calculation impossible, points are all on the same straight line
pVirDev->DrawLine(aP1,aP2);
pVirDev->DrawLine(aP2,aP3);
return;
}
cy=( q*q*((x3*x3-x1*x1)*(x2-x1)+(x2*x2-x1*x1)*(x1-x3)) +
p*p*((y3*y3-y1*y1)*(x2-x1)+(y2*y2-y1*y1)*(x1-x3)) ) / ncy;
cx=( q*q*(x2*x2-x1*x1)+p*p*(y2*y2-y1*y1)+cy*2*p*p*(y1-y2) ) / ncx;
// now we still need the radius in x and y direction:
r=hypot(q*(x1-cx), p*(y1-cy));
rx=r/q; ry=r/p;
// We now have to find out how the starting and the end point
// have to be chosen so that point no. 2 lies inside the drawn arc:
w1=fmod((atan2(x1-cx,y1-cy)-atan2(x2-cx,y2-cy)),6.28318530718); if (w1<0) w1+=6.28318530718;
w3=fmod((atan2(x3-cx,y3-cy)-atan2(x2-cx,y2-cy)),6.28318530718); if (w3<0) w3+=6.28318530718;
if (w3<w1) {
pVirDev->DrawArc(tools::Rectangle(static_cast<sal_Int32>(cx-rx),static_cast<sal_Int32>(cy-ry),
static_cast<sal_Int32>(cx+rx),static_cast<sal_Int32>(cy+ry)),aP1,aP3);
}
else {
pVirDev->DrawArc(tools::Rectangle(static_cast<sal_Int32>(cx-rx),static_cast<sal_Int32>(cy-ry),
static_cast<sal_Int32>(cx+rx),static_cast<sal_Int32>(cy+ry)),aP3,aP1);
}
}
void OS2METReader::ReadFullArc(bool bGivenPos, sal_uInt16 nOrderSize)
{
Point aCenter;
tools::Rectangle aRect;
if (bGivenPos) {
aCenter=ReadPoint();
if (bCoord32) nOrderSize-=8; else nOrderSize-=4;
}
else aCenter=aAttr.aCurPos;
sal_Int32 nP = aAttr.nArcP;
sal_Int32 nQ = aAttr.nArcQ;
if (nP < 0)
nP = o3tl::saturating_toggle_sign(nP);
if (nQ < 0)
nQ = o3tl::saturating_toggle_sign(nQ);
sal_uInt32 nMul(0);
if (nOrderSize>=4)
pOS2MET->ReadUInt32( nMul );
else {
sal_uInt16 nMulS(0);
pOS2MET->ReadUInt16( nMulS );
nMul=static_cast<sal_uInt32>(nMulS)<<8;
}
if (nMul!=0x00010000) {
nP=(nP*nMul)>>16;
nQ=(nQ*nMul)>>16;
}
aRect=tools::Rectangle(aCenter.X()-nP,aCenter.Y()-nQ,
aCenter.X()+nP,aCenter.Y()+nQ);
aCalcBndRect.Union(aRect);
if (pAreaStack!=nullptr) {
ChangeBrush(aAttr.aPatCol, aAttr.bFill);
SetRasterOp(aAttr.ePatMix);
if ((pAreaStack->nFlags&0x40)!=0)
SetPen( aAttr.aLinCol, aAttr.nStrLinWidth, aAttr.eLinStyle );
else
SetPen( COL_TRANSPARENT, 0, PEN_NULL );
}
else
{
SetPen( aAttr.aLinCol, aAttr.nStrLinWidth, aAttr.eLinStyle );
ChangeBrush(COL_TRANSPARENT, false);
SetRasterOp(aAttr.eLinMix);
}
pVirDev->DrawEllipse(aRect);
}
void OS2METReader::ReadPartialArc(bool bGivenPos, sal_uInt16 nOrderSize)
{
Point aP0, aCenter,aPStart,aPEnd;
tools::Rectangle aRect;
if (bGivenPos) {
aP0=ReadPoint();
if (bCoord32) nOrderSize-=8; else nOrderSize-=4;
}
else aP0=aAttr.aCurPos;
aCenter=ReadPoint();
sal_Int32 nP = aAttr.nArcP;
sal_Int32 nQ = aAttr.nArcQ;
if (nP < 0)
nP = o3tl::saturating_toggle_sign(nP);
if (nQ < 0)
nQ = o3tl::saturating_toggle_sign(nQ);
sal_uInt32 nMul(0);
if (nOrderSize>=12)
pOS2MET->ReadUInt32( nMul );
else {
sal_uInt16 nMulS(0);
pOS2MET->ReadUInt16( nMulS );
nMul=static_cast<sal_uInt32>(nMulS)<<8;
}
if (nMul!=0x00010000) {
nP=(nP*nMul)>>16;
nQ=(nQ*nMul)>>16;
}
sal_Int32 nStart(0), nSweep(0);
pOS2MET->ReadInt32( nStart ).ReadInt32( nSweep );
double fStart = basegfx::deg2rad<65536>(static_cast<double>(nStart));
double fEnd = fStart+ basegfx::deg2rad<65536>(static_cast<double>(nSweep));
aPStart=Point(aCenter.X()+static_cast<sal_Int32>( cos(fStart)*nP),
aCenter.Y()+static_cast<sal_Int32>(-sin(fStart)*nQ));
aPEnd= Point(aCenter.X()+static_cast<sal_Int32>( cos(fEnd)*nP),
aCenter.Y()+static_cast<sal_Int32>(-sin(fEnd)*nQ));
aRect=tools::Rectangle(aCenter.X()-nP,aCenter.Y()-nQ,
aCenter.X()+nP,aCenter.Y()+nQ);
aCalcBndRect.Union(aRect);
SetPen( aAttr.aLinCol, aAttr.nStrLinWidth, aAttr.eLinStyle );
SetRasterOp(aAttr.eLinMix);
pVirDev->DrawLine(aP0,aPStart);
pVirDev->DrawArc(aRect,aPStart,aPEnd);
aAttr.aCurPos=aPEnd;
}
void OS2METReader::ReadPolygons()
{
tools::PolyPolygon aPolyPoly;
tools::Polygon aPoly;
Point aPoint;
sal_uInt8 nFlags(0);
sal_uInt32 nNumPolys(0);
pOS2MET->ReadUChar(nFlags).ReadUInt32(nNumPolys);
if (!pOS2MET->good() || nNumPolys > SAL_MAX_UINT16)
{
pOS2MET->SetError(SVSTREAM_FILEFORMAT_ERROR);
ErrorCode=11;
return;
}
for (sal_uInt32 i=0; i<nNumPolys; ++i)
{
sal_uInt32 nNumPoints(0);
pOS2MET->ReadUInt32(nNumPoints);
sal_uInt32 nLimit = SAL_MAX_UINT16;
if (i==0) --nLimit;
if (!pOS2MET->good() || nNumPoints > nLimit)
{
pOS2MET->SetError(SVSTREAM_FILEFORMAT_ERROR);
ErrorCode=11;
return;
}
if (i==0) ++nNumPoints;
aPoly.SetSize(static_cast<short>(nNumPoints));
for (sal_uInt32 j=0; j<nNumPoints; ++j)
{
if (i==0 && j==0) aPoint=aAttr.aCurPos;
else aPoint=ReadPoint();
aPoly.SetPoint(aPoint,static_cast<short>(j));
if (i==nNumPolys-1 && j==nNumPoints-1) aAttr.aCurPos=aPoint;
}
aPolyPoly.Insert(aPoly);
}
ChangeBrush(aAttr.aPatCol, aAttr.bFill);
SetRasterOp(aAttr.ePatMix);
if ((nFlags&0x01)!=0)
SetPen( aAttr.aLinCol, aAttr.nStrLinWidth, aAttr.eLinStyle );
else
SetPen( COL_TRANSPARENT, 0, PEN_NULL );
DrawPolyPolygon( aPolyPoly );
}
void OS2METReader::ReadBezier(bool bGivenPos, sal_uInt16 nOrderLen)
{
sal_uInt16 i, nNumPoints = nOrderLen / ( bCoord32 ? 8 : 4 );
if( !bGivenPos )
nNumPoints++;
if( !nNumPoints )
return;
tools::Polygon aPolygon( nNumPoints );
for( i=0; i < nNumPoints; i++ )
{
if( i==0 && !bGivenPos)
aPolygon.SetPoint( aAttr.aCurPos, i );
else
aPolygon.SetPoint( ReadPoint(), i );
}
if( !( nNumPoints % 4 ) )
{
// create bezier polygon
const sal_uInt16 nSegPoints = 25;
const sal_uInt16 nSegments = aPolygon.GetSize() >> 2;
tools::Polygon aBezPoly( nSegments * nSegPoints );
sal_uInt16 nSeg, nBezPos, nStartPos;
for( nSeg = 0, nBezPos = 0, nStartPos = 0; nSeg < nSegments; nSeg++, nStartPos += 4 )
{
const tools::Polygon aSegPoly( aPolygon[ nStartPos ], aPolygon[ nStartPos + 1 ],
aPolygon[ nStartPos + 3 ], aPolygon[ nStartPos + 2 ],
nSegPoints );
for( sal_uInt16 nSegPos = 0; nSegPos < nSegPoints; )
aBezPoly[ nBezPos++ ] = aSegPoly[ nSegPos++ ];
}
nNumPoints = nBezPos;
if( nNumPoints != aBezPoly.GetSize() )
aBezPoly.SetSize( nNumPoints );
aPolygon = std::move(aBezPoly);
}
aAttr.aCurPos = aPolygon[ nNumPoints - 1 ];
if (pAreaStack!=nullptr)
AddPointsToArea(aPolygon);
else if (pPathStack!=nullptr)
AddPointsToPath(aPolygon);
else
{
SetPen( aAttr.aLinCol, aAttr.nStrLinWidth, aAttr.eLinStyle );
SetRasterOp(aAttr.eLinMix);
DrawPolyLine( aPolygon );
}
}
void OS2METReader::ReadFillet(bool bGivenPos, sal_uInt16 nOrderLen)
{
sal_uInt16 i,nNumPoints;
if (bCoord32) nNumPoints=nOrderLen/8; else nNumPoints=nOrderLen/4;
if (!bGivenPos) nNumPoints++;
if (nNumPoints==0) return;
tools::Polygon aPolygon(nNumPoints);
for (i=0; i<nNumPoints; i++) {
if (i==0 && !bGivenPos) aPolygon.SetPoint(aAttr.aCurPos,i);
else aPolygon.SetPoint(ReadPoint(),i);
}
aAttr.aCurPos=aPolygon.GetPoint(nNumPoints-1);
if (pAreaStack!=nullptr) AddPointsToArea(aPolygon);
else if (pPathStack!=nullptr) AddPointsToPath(aPolygon);
else {
SetPen( aAttr.aLinCol, aAttr.nStrLinWidth, aAttr.eLinStyle );
SetRasterOp(aAttr.eLinMix);
DrawPolyLine( aPolygon );
}
}
void OS2METReader::ReadFilletSharp(bool bGivenPos, sal_uInt16 nOrderLen)
{
if (bGivenPos) {
aAttr.aCurPos=ReadPoint();
if (bCoord32) nOrderLen-=8; else nOrderLen-=4;
}
sal_uInt16 nNumPoints;
if (bCoord32) nNumPoints=1+nOrderLen/10;
else nNumPoints=1+nOrderLen/6;
tools::Polygon aPolygon(nNumPoints);
aPolygon.SetPoint(aAttr.aCurPos, 0);
for (sal_uInt16 i = 1; i <nNumPoints; ++i)
aPolygon.SetPoint(ReadPoint(), i);
if (!pOS2MET->good())
{
SAL_WARN("filter.os2met", "OS2METReader::ReadFilletSharp: short read");
return;
}
aAttr.aCurPos=aPolygon.GetPoint(nNumPoints-1);
if (pAreaStack!=nullptr) AddPointsToArea(aPolygon);
else if (pPathStack!=nullptr) AddPointsToPath(aPolygon);
else
{
SetPen( aAttr.aLinCol, aAttr.nStrLinWidth, aAttr.eLinStyle );
SetRasterOp(aAttr.eLinMix);
DrawPolyLine( aPolygon );
}
}
void OS2METReader::ReadMarker(bool bGivenPos, sal_uInt16 nOrderLen)
{
sal_uInt16 i,nNumPoints;
SetPen( aAttr.aMrkCol );
SetRasterOp(aAttr.eMrkMix);
if (aAttr.nMrkSymbol>=5 && aAttr.nMrkSymbol<=9)
{
ChangeBrush(aAttr.aMrkCol, true);
}
else
{
ChangeBrush(COL_TRANSPARENT, false);
}
if (bCoord32) nNumPoints=nOrderLen/8; else nNumPoints=nOrderLen/4;
if (!bGivenPos) nNumPoints++;
for (i=0; i<nNumPoints; i++) {
if (i!=0 || bGivenPos) aAttr.aCurPos=ReadPoint();
const auto x = aAttr.aCurPos.X();
const auto y = aAttr.aCurPos.Y();
aCalcBndRect.Union(tools::Rectangle(x-5,y-5,x+5,y+5));
switch (aAttr.nMrkSymbol) {
case 2: // PLUS
pVirDev->DrawLine(Point(x-4,y),Point(x+4,y));
pVirDev->DrawLine(Point(x,y-4),Point(x,y+4));
break;
case 3: // DIAMOND
case 7: { // SOLIDDIAMOND
tools::Polygon aPoly(4);
aPoly.SetPoint(Point(x,y+4),0);
aPoly.SetPoint(Point(x+4,y),1);
aPoly.SetPoint(Point(x,y-4),2);
aPoly.SetPoint(Point(x-4,y),3);
pVirDev->DrawPolygon(aPoly);
break;
}
case 4: // SQUARE
case 8: { // SOLIDSUARE
tools::Polygon aPoly(4);
aPoly.SetPoint(Point(x+4,y+4),0);
aPoly.SetPoint(Point(x+4,y-4),1);
aPoly.SetPoint(Point(x-4,y-4),2);
aPoly.SetPoint(Point(x-4,y+4),3);
pVirDev->DrawPolygon(aPoly);
break;
}
case 5: { // SIXPOINTSTAR
tools::Polygon aPoly(12);
aPoly.SetPoint(Point(x ,y-4),0);
aPoly.SetPoint(Point(x+2,y-2),1);
aPoly.SetPoint(Point(x+4,y-2),2);
aPoly.SetPoint(Point(x+2,y ),3);
aPoly.SetPoint(Point(x+4,y+2),4);
aPoly.SetPoint(Point(x+2,y+2),5);
aPoly.SetPoint(Point(x ,y+4),6);
aPoly.SetPoint(Point(x-2,y+2),7);
aPoly.SetPoint(Point(x-4,y+2),8);
aPoly.SetPoint(Point(x-2,y ),9);
aPoly.SetPoint(Point(x-4,y-2),10);
aPoly.SetPoint(Point(x-2,y-2),11);
pVirDev->DrawPolygon(aPoly);
break;
}
case 6: { // EIGHTPOINTSTAR
tools::Polygon aPoly(16);
aPoly.SetPoint(Point(x ,y-4),0);
aPoly.SetPoint(Point(x+1,y-2),1);
aPoly.SetPoint(Point(x+3,y-3),2);
aPoly.SetPoint(Point(x+2,y-1),3);
aPoly.SetPoint(Point(x+4,y ),4);
aPoly.SetPoint(Point(x+2,y+1),5);
aPoly.SetPoint(Point(x+3,y+3),6);
aPoly.SetPoint(Point(x+1,y+2),7);
aPoly.SetPoint(Point(x ,y+4),8);
aPoly.SetPoint(Point(x-1,y+2),9);
aPoly.SetPoint(Point(x-3,y+3),10);
aPoly.SetPoint(Point(x-2,y+1),11);
aPoly.SetPoint(Point(x-4,y ),12);
aPoly.SetPoint(Point(x-2,y-1),13);
aPoly.SetPoint(Point(x-3,y-3),14);
aPoly.SetPoint(Point(x-1,y-2),15);
pVirDev->DrawPolygon(aPoly);
break;
}
case 9: // DOT
pVirDev->DrawEllipse(tools::Rectangle(x-1,y-1,x+1,y+1));
break;
case 10: // SMALLCIRCLE
pVirDev->DrawEllipse(tools::Rectangle(x-2,y-2,x+2,y+2));
break;
case 64: // BLANK
break;
default: // (=1) CROSS
pVirDev->DrawLine(Point(x-4,y-4),Point(x+4,y+4));
pVirDev->DrawLine(Point(x-4,y+4),Point(x+4,y-4));
break;
}
}
}
void OS2METReader::ReadOrder(sal_uInt16 nOrderID, sal_uInt16 nOrderLen)
{
switch (nOrderID) {
case GOrdGivArc: ReadArc(true); break;
case GOrdCurArc: ReadArc(false); break;
case GOrdGivBzr: ReadBezier(true,nOrderLen); break;
case GOrdCurBzr: ReadBezier(false,nOrderLen); break;
case GOrdGivBox: ReadBox(true); break;
case GOrdCurBox: ReadBox(false); break;
case GOrdGivFil: ReadFillet(true,nOrderLen); break;
case GOrdCurFil: ReadFillet(false,nOrderLen); break;
case GOrdGivCrc: ReadFullArc(true,nOrderLen); break;
case GOrdCurCrc: ReadFullArc(false,nOrderLen); break;
case GOrdGivLin: ReadLine(true, nOrderLen); break;
case GOrdCurLin: ReadLine(false, nOrderLen); break;
case GOrdGivMrk: ReadMarker(true, nOrderLen); break;
case GOrdCurMrk: ReadMarker(false, nOrderLen); break;
case GOrdGivArP: ReadPartialArc(true,nOrderLen); break;
case GOrdCurArP: ReadPartialArc(false,nOrderLen); break;
case GOrdGivRLn: ReadRelLine(true,nOrderLen); break;
case GOrdCurRLn: ReadRelLine(false,nOrderLen); break;
case GOrdGivSFl: ReadFilletSharp(true,nOrderLen); break;
case GOrdCurSFl: ReadFilletSharp(false,nOrderLen); break;
case GOrdGivStM: ReadChrStr(true , true , false, nOrderLen); break;
case GOrdCurStM: ReadChrStr(false, true , false, nOrderLen); break;
case GOrdGivStr: ReadChrStr(true , false, false, nOrderLen); break;
case GOrdCurStr: ReadChrStr(false, false, false, nOrderLen); break;
case GOrdGivStx: ReadChrStr(true , false, true , nOrderLen); break;
case GOrdCurStx: ReadChrStr(false, false, true , nOrderLen); break;
case GOrdGivImg: SAL_INFO("filter.os2met","GOrdGivImg");
break;
case GOrdCurImg: SAL_INFO("filter.os2met","GOrdCurImg");
break;
case GOrdImgDat: SAL_INFO("filter.os2met","GOrdImgDat");
break;
case GOrdEndImg: SAL_INFO("filter.os2met","GOrdEndImg");
break;
case GOrdBegAra: {
OSArea * p=new OSArea;
p->bClosed=false;
p->pSucc=pAreaStack; pAreaStack=p;
pOS2MET->ReadUChar( p->nFlags );
p->aCol=aAttr.aPatCol;
p->aBgCol=aAttr.aPatBgCol;
p->eMix=aAttr.ePatMix;
p->eBgMix=aAttr.ePatBgMix;
p->bFill=aAttr.bFill;
break;
}
case GOrdEndAra:
{
OSArea * p=pAreaStack;
if ( p )
{
pAreaStack = p->pSucc;
if ( pPathStack )
{
for ( sal_uInt16 i=0; i<p->aPPoly.Count(); i++ )
{
AddPointsToPath( p->aPPoly.GetObject( i ) );
CloseFigure();
}
}
else
{
if ( ( p->nFlags & 0x40 ) == 0 )
SetPen( COL_TRANSPARENT, 0, PEN_NULL );
else
SetPen( aAttr.aLinCol, aAttr.nStrLinWidth, aAttr.eLinStyle );
ChangeBrush(p->aCol, p->bFill);
SetRasterOp(p->eMix);
DrawPolyPolygon( p->aPPoly );
}
delete p;
}
}
break;
case GOrdBegElm: SAL_INFO("filter.os2met","GOrdBegElm");
break;
case GOrdEndElm: SAL_INFO("filter.os2met","GOrdEndElm");
break;
case GOrdBegPth: {
OSPath * p=new OSPath;
p->pSucc=pPathStack; pPathStack=p;
pOS2MET->SeekRel(2);
pOS2MET->ReadUInt32( p->nID );
p->bClosed=false;
p->bStroke=false;
break;
}
case GOrdEndPth: {
OSPath * p, * pprev, * psucc;
if (pPathStack==nullptr) break;
p=pPathList; pprev=nullptr;
while (p!=nullptr) {
psucc=p->pSucc;
if (p->nID==pPathStack->nID) {
if (pprev==nullptr) pPathList=psucc; else pprev->pSucc=psucc;
delete p;
}
else pprev=p;
p=psucc;
}
p=pPathStack;
pPathStack=p->pSucc;
p->pSucc=pPathList; pPathList=p;
break;
}
case GOrdFilPth:
{
sal_uInt32 nID(0);
sal_uInt16 nDummy(0);
OSPath* p = pPathList;
pOS2MET->ReadUInt16( nDummy )
.ReadUInt32( nID );
if ( ! ( nDummy & 0x20 ) ) // #30933# i do not know the exact meaning of this bit,
{ // but if set it seems to be better not to fill this path
while( p && p->nID != nID )
p = p->pSucc;
if( p )
{
if( p->bStroke )
{
SetPen( aAttr.aPatCol, aAttr.nStrLinWidth );
ChangeBrush(COL_TRANSPARENT, false);
SetRasterOp( aAttr.ePatMix );
if ( IsLineInfo() )
{
for ( sal_uInt16 i = 0; i < p->aPPoly.Count(); i++ )
pVirDev->DrawPolyLine( p->aPPoly.GetObject( i ), aLineInfo );
}
else
pVirDev->DrawPolyPolygon( p->aPPoly );
}
else
{
SetPen( COL_TRANSPARENT, 0, PEN_NULL );
ChangeBrush( aAttr.aPatCol, aAttr.bFill );
SetRasterOp( aAttr.ePatMix );
pVirDev->DrawPolyPolygon( p->aPPoly );
}
}
}
}
break;
case GOrdModPth:
{
OSPath* p = pPathList;
while( p && p->nID != 1 )
p = p->pSucc;
if( p )
p->bStroke = true;
}
break;
case GOrdOutPth:
{
sal_uInt32 nID;
sal_uInt16 i,nC;
OSPath* p=pPathList;
pOS2MET->SeekRel(2);
pOS2MET->ReadUInt32( nID );
while (p && pOS2MET->good() && p->nID != nID)
p = p->pSucc;
if (p)
{
SetPen( aAttr.aLinCol, aAttr.nStrLinWidth, aAttr.eLinStyle );
SetRasterOp(aAttr.eLinMix);
ChangeBrush(COL_TRANSPARENT, false);
nC=p->aPPoly.Count();
for (i=0; i<nC; i++)
{
if (i+1<nC || p->bClosed)
DrawPolygon( p->aPPoly.GetObject( i ) );
else
DrawPolyLine( p->aPPoly.GetObject( i ) );
}
}
break;
}
case GOrdSClPth: {
SAL_INFO("filter.os2met","GOrdSClPth");
sal_uInt32 nID(0);
OSPath * p=pPathList;
pOS2MET->SeekRel(2);
pOS2MET->ReadUInt32( nID );
if (nID==0) p=nullptr;
while (p!=nullptr && p->nID!=nID) p=p->pSucc;
if (p!=nullptr) pVirDev->SetClipRegion(vcl::Region(p->aPPoly));
else pVirDev->SetClipRegion();
break;
}
case GOrdNopNop:
break;
case GOrdRemark: SAL_INFO("filter.os2met","GOrdRemark");
break;
case GOrdSegLab: SAL_INFO("filter.os2met","GOrdSegLab");
break;
case GOrdBitBlt: ReadBitBlt(); break;
case GOrdCalSeg: SAL_INFO("filter.os2met","GOrdCalSeg");
break;
case GOrdSSgBnd: SAL_INFO("filter.os2met","GOrdSSgBnd");
break;
case GOrdSegChr: SAL_INFO("filter.os2met","GOrdSegChr");
break;
case GOrdCloFig:
CloseFigure();
break;
case GOrdEndSym: SAL_INFO("filter.os2met","GOrdEndSym");
break;
case GOrdEndPlg: SAL_INFO("filter.os2met","GOrdEndPlg");
break;
case GOrdEscape: SAL_INFO("filter.os2met","GOrdEscape");
break;
case GOrdExtEsc: SAL_INFO("filter.os2met","GOrdExtEsc");
break;
case GOrdPolygn: ReadPolygons(); break;
case GOrdStkPop: PopAttr(); break;
case GOrdPIvAtr: PushAttr(nOrderID);
[[fallthrough]];
case GOrdSIvAtr: {
sal_uInt8 nA(0), nP(0), nFlags(0);
Color aCol;
RasterOp eROP;
pOS2MET->ReadUChar( nA ).ReadUChar( nP ).ReadUChar( nFlags );
if (nOrderID==GOrdPIvAtr) {
pAttrStack->nIvAttrA=nA;
pAttrStack->nIvAttrP=nP;
}
if (nA<=2) {
if ((nFlags&0x80)!=0) {
if (nA==1) switch (nP) {
case 1: aAttr.aLinCol=aDefAttr.aLinCol; break;
case 2: aAttr.aChrCol=aDefAttr.aChrCol; break;
case 3: aAttr.aMrkCol=aDefAttr.aMrkCol; break;
case 4: aAttr.aPatCol=aDefAttr.aPatCol; break;
case 5: aAttr.aImgCol=aDefAttr.aImgCol; break;
}
else switch (nP) {
case 1: aAttr.aLinBgCol=aDefAttr.aLinBgCol; break;
case 2: aAttr.aChrBgCol=aDefAttr.aChrBgCol; break;
case 3: aAttr.aMrkBgCol=aDefAttr.aMrkBgCol; break;
case 4: aAttr.aPatBgCol=aDefAttr.aPatBgCol; break;
case 5: aAttr.aImgBgCol=aDefAttr.aImgBgCol; break;
}
}
else {
const auto nVal = ReadLittleEndian3BytesLong();
if ((nFlags&0x40)!=0 && nVal==1) aCol=COL_BLACK;
else if ((nFlags&0x40)!=0 && nVal==2) aCol=COL_WHITE;
else if ((nFlags&0x40)!=0 && nVal==4) aCol=COL_WHITE;
else if ((nFlags&0x40)!=0 && nVal==5) aCol=COL_BLACK;
else aCol=GetPaletteColor(nVal);
if (nA==1) switch (nP) {
case 1: aAttr.aLinCol=aCol; break;
case 2: aAttr.aChrCol=aCol; break;
case 3: aAttr.aMrkCol=aCol; break;
case 4: aAttr.aPatCol=aCol; break;
case 5: aAttr.aImgCol=aCol; break;
}
else switch (nP) {
case 1: aAttr.aLinBgCol=aCol; break;
case 2: aAttr.aChrBgCol=aCol; break;
case 3: aAttr.aMrkBgCol=aCol; break;
case 4: aAttr.aPatBgCol=aCol; break;
case 5: aAttr.aImgBgCol=aCol; break;
}
}
}
else {
sal_uInt8 nMix(0);
pOS2MET->ReadUChar( nMix );
if (nMix==0) {
switch (nP) {
case 1: aAttr.eLinBgMix=aDefAttr.eLinBgMix; break;
case 2: aAttr.eChrBgMix=aDefAttr.eChrBgMix; break;
case 3: aAttr.eMrkBgMix=aDefAttr.eMrkBgMix; break;
case 4: aAttr.ePatBgMix=aDefAttr.ePatBgMix; break;
case 5: aAttr.eImgBgMix=aDefAttr.eImgBgMix; break;
}
}
else {
eROP=OS2MixToRasterOp(nMix);
switch (nP) {
case 1: aAttr.eLinBgMix=eROP; break;
case 2: aAttr.eChrBgMix=eROP; break;
case 3: aAttr.eMrkBgMix=eROP; break;
case 4: aAttr.ePatBgMix=eROP; break;
case 5: aAttr.eImgBgMix=eROP; break;
}
}
}
break;
}
case GOrdPIxCol: PushAttr(nOrderID);
[[fallthrough]];
case GOrdSIxCol: {
sal_uInt8 nFlags(0);
pOS2MET->ReadUChar( nFlags );
if ((nFlags&0x80)!=0) {
aAttr.aLinCol=aDefAttr.aLinCol;
aAttr.aChrCol=aDefAttr.aChrCol;
aAttr.aMrkCol=aDefAttr.aMrkCol;
aAttr.aPatCol=aDefAttr.aPatCol;
aAttr.aImgCol=aDefAttr.aImgCol;
}
else {
Color aCol;
const auto nVal = ReadLittleEndian3BytesLong();
if ((nFlags&0x40)!=0 && nVal==1) aCol=COL_BLACK;
else if ((nFlags&0x40)!=0 && nVal==2) aCol=COL_WHITE;
else if ((nFlags&0x40)!=0 && nVal==4) aCol=COL_WHITE;
else if ((nFlags&0x40)!=0 && nVal==5) aCol=COL_BLACK;
else aCol=GetPaletteColor(nVal);
aAttr.aLinCol = aAttr.aChrCol = aAttr.aMrkCol = aAttr.aPatCol =
aAttr.aImgCol = aCol;
}
break;
}
case GOrdPColor:
case GOrdPXtCol: PushAttr(nOrderID);
[[fallthrough]];
case GOrdSColor:
case GOrdSXtCol: {
sal_uInt16 nVal(0);
if (nOrderID==GOrdPColor || nOrderID==GOrdSColor) {
sal_uInt8 nbyte(0);
pOS2MET->ReadUChar( nbyte ); nVal=static_cast<sal_uInt16>(nbyte)|0xff00;
}
else pOS2MET->ReadUInt16( nVal );
if (nVal==0x0000 || nVal==0xff00) {
aAttr.aLinCol=aDefAttr.aLinCol;
aAttr.aChrCol=aDefAttr.aChrCol;
aAttr.aMrkCol=aDefAttr.aMrkCol;
aAttr.aPatCol=aDefAttr.aPatCol;
aAttr.aImgCol=aDefAttr.aImgCol;
}
else {
Color aCol;
if (nVal==0x0007) aCol=COL_WHITE;
else if (nVal==0x0008) aCol=COL_BLACK;
else if (nVal==0xff08) aCol=GetPaletteColor(1);
else aCol=GetPaletteColor(static_cast<sal_uInt32>(nVal) & 0x000000ff);
aAttr.aLinCol = aAttr.aChrCol = aAttr.aMrkCol = aAttr.aPatCol =
aAttr.aImgCol = aCol;
}
break;
}
case GOrdPBgCol: PushAttr(nOrderID);
[[fallthrough]];
case GOrdSBgCol: {
sal_uInt16 nVal(0);
pOS2MET->ReadUInt16( nVal );
if (nVal==0x0000 || nVal==0xff00) {
aAttr.aLinBgCol=aDefAttr.aLinBgCol;
aAttr.aChrBgCol=aDefAttr.aChrBgCol;
aAttr.aMrkBgCol=aDefAttr.aMrkBgCol;
aAttr.aPatBgCol=aDefAttr.aPatBgCol;
aAttr.aImgBgCol=aDefAttr.aImgBgCol;
}
else {
Color aCol;
if (nVal==0x0007) aCol=COL_WHITE;
else if (nVal==0x0008) aCol=COL_BLACK;
else if (nVal==0xff08) aCol=GetPaletteColor(0);
else aCol=GetPaletteColor(static_cast<sal_uInt32>(nVal) & 0x000000ff);
aAttr.aLinBgCol = aAttr.aChrBgCol = aAttr.aMrkBgCol =
aAttr.aPatBgCol = aAttr.aImgBgCol = aCol;
}
break;
}
case GOrdPBxCol: PushAttr(nOrderID);
[[fallthrough]];
case GOrdSBxCol: {
sal_uInt8 nFlags(0);
pOS2MET->ReadUChar( nFlags );
if ((nFlags&0x80)!=0) {
aAttr.aLinBgCol=aDefAttr.aLinBgCol;
aAttr.aChrBgCol=aDefAttr.aChrBgCol;
aAttr.aMrkBgCol=aDefAttr.aMrkBgCol;
aAttr.aPatBgCol=aDefAttr.aPatBgCol;
aAttr.aImgBgCol=aDefAttr.aImgBgCol;
}
else {
Color aCol;
const auto nVal = ReadLittleEndian3BytesLong();
if ((nFlags&0x40)!=0 && nVal==1) aCol=COL_BLACK;
else if ((nFlags&0x40)!=0 && nVal==2) aCol=COL_WHITE;
else if ((nFlags&0x40)!=0 && nVal==4) aCol=COL_WHITE;
else if ((nFlags&0x40)!=0 && nVal==5) aCol=COL_BLACK;
else aCol=GetPaletteColor(nVal);
aAttr.aLinBgCol = aAttr.aChrBgCol = aAttr.aMrkBgCol =
aAttr.aPatBgCol = aAttr.aImgBgCol = aCol;
}
break;
}
case GOrdPMixMd: PushAttr(nOrderID);
[[fallthrough]];
case GOrdSMixMd: {
sal_uInt8 nMix(0);
pOS2MET->ReadUChar( nMix );
if (nMix==0) {
aAttr.eLinMix=aDefAttr.eLinMix;
aAttr.eChrMix=aDefAttr.eChrMix;
aAttr.eMrkMix=aDefAttr.eMrkMix;
aAttr.ePatMix=aDefAttr.ePatMix;
aAttr.eImgMix=aDefAttr.eImgMix;
}
else {
aAttr.eLinMix = aAttr.eChrMix = aAttr.eMrkMix =
aAttr.ePatMix = aAttr.eImgMix = OS2MixToRasterOp(nMix);
}
break;
}
case GOrdPBgMix: PushAttr(nOrderID);
[[fallthrough]];
case GOrdSBgMix: {
sal_uInt8 nMix(0);
pOS2MET->ReadUChar( nMix );
if (nMix==0) {
aAttr.eLinBgMix=aDefAttr.eLinBgMix;
aAttr.eChrBgMix=aDefAttr.eChrBgMix;
aAttr.eMrkBgMix=aDefAttr.eMrkBgMix;
aAttr.ePatBgMix=aDefAttr.ePatBgMix;
aAttr.eImgBgMix=aDefAttr.eImgBgMix;
}
else {
aAttr.eLinBgMix = aAttr.eChrBgMix = aAttr.eMrkBgMix =
aAttr.ePatBgMix = aAttr.eImgBgMix = OS2MixToRasterOp(nMix);
}
break;
}
case GOrdPPtSet: PushAttr(nOrderID);
[[fallthrough]];
case GOrdSPtSet: SAL_INFO("filter.os2met","GOrdSPtSet");
break;
case GOrdPPtSym: PushAttr(nOrderID);
[[fallthrough]];
case GOrdSPtSym: {
sal_uInt8 nPatt(0);
pOS2MET->ReadUChar( nPatt );
aAttr.bFill = ( nPatt != 0x0f );
break;
}
case GOrdPPtRef: PushAttr(nOrderID);
[[fallthrough]];
case GOrdSPtRef: SAL_INFO("filter.os2met","GOrdSPtRef");
break;
case GOrdPLnEnd: PushAttr(nOrderID);
[[fallthrough]];
case GOrdSLnEnd:
break;
case GOrdPLnJoi: PushAttr(nOrderID);
[[fallthrough]];
case GOrdSLnJoi:
break;
case GOrdPLnTyp: PushAttr(nOrderID);
[[fallthrough]];
case GOrdSLnTyp: {
sal_uInt8 nType(0);
pOS2MET->ReadUChar( nType );
switch (nType) {
case 0: aAttr.eLinStyle=aDefAttr.eLinStyle; break;
case 1: case 4: aAttr.eLinStyle=PEN_DOT; break;
case 2: case 5: aAttr.eLinStyle=PEN_DASH; break;
case 3: case 6: aAttr.eLinStyle=PEN_DASHDOT; break;
case 8: aAttr.eLinStyle=PEN_NULL; break;
default: aAttr.eLinStyle=PEN_SOLID;
}
break;
}
case GOrdPLnWdt: PushAttr(nOrderID);
[[fallthrough]];
case GOrdSLnWdt: {
sal_uInt8 nbyte(0);
pOS2MET->ReadUChar(nbyte);
if (nbyte > 0)
aAttr.nLinWidth = static_cast<sal_uInt16>(nbyte)-1;
else
aAttr.nLinWidth = aDefAttr.nLinWidth;
break;
}
case GOrdPFrLWd: PushAttr(nOrderID);
[[fallthrough]];
case GOrdSFrLWd:
break;
case GOrdPStLWd: PushAttr(nOrderID);
[[fallthrough]];
case GOrdSStLWd :
{
sal_uInt8 nFlags(0);
pOS2MET->ReadUChar( nFlags );
if ( nFlags & 0x80 )
aAttr.nStrLinWidth = aDefAttr.nStrLinWidth;
else
{
pOS2MET->SeekRel( 1 );
sal_Int32 nWd = ReadCoord( bCoord32 );
if (nWd < 0)
nWd = o3tl::saturating_toggle_sign(nWd);
aAttr.nStrLinWidth = static_cast<sal_uInt16>(nWd);
}
break;
}
case GOrdPChDir: PushAttr(nOrderID);
[[fallthrough]];
case GOrdSChDir:
break;
case GOrdPChPrc: PushAttr(nOrderID);
[[fallthrough]];
case GOrdSChPrc:
break;
case GOrdPChSet: PushAttr(nOrderID);
[[fallthrough]];
case GOrdSChSet: {
sal_uInt8 nbyte(0);
pOS2MET->ReadUChar( nbyte );
aAttr.nChrSet=static_cast<sal_uInt32>(nbyte)&0xff;
break;
}
case GOrdPChAng: PushAttr(nOrderID);
[[fallthrough]];
case GOrdSChAng: {
sal_Int32 nX = ReadCoord(bCoord32);
sal_Int32 nY = ReadCoord(bCoord32);
if (nX>=0 && nY==0) aAttr.nChrAng=0_deg10;
else {
aAttr.nChrAng = Degree10(static_cast<short>(basegfx::rad2deg<10>(atan2(static_cast<double>(nY),static_cast<double>(nX)))));
while (aAttr.nChrAng < 0_deg10) aAttr.nChrAng += 3600_deg10;
aAttr.nChrAng %= 3600_deg10;
}
break;
}
case GOrdPChBrx: PushAttr(nOrderID);
[[fallthrough]];
case GOrdSChBrx:
break;
case GOrdPChCel: PushAttr(nOrderID);
[[fallthrough]];
case GOrdSChCel: {
sal_uInt16 nLen=nOrderLen;
(void) ReadCoord(bCoord32); // Width, unused
auto nHeight = ReadCoord(bCoord32);
if (nHeight < 0 || nHeight > SAL_MAX_INT16)
{
SAL_WARN("filter.os2met", "ignoring out of sane range font height: " << nHeight);
aAttr.nChrCellHeight = aDefAttr.nChrCellHeight;
}
else
aAttr.nChrCellHeight = nHeight;
if (bCoord32) nLen-=8; else nLen-=4;
if (nLen>=4) {
pOS2MET->SeekRel(4); nLen-=4;
}
if (nLen>=2) {
sal_uInt8 nbyte(0);
pOS2MET->ReadUChar( nbyte );
if ((nbyte&0x80)==0 && aAttr.nChrCellHeight == 0)
aAttr.nChrCellHeight = aDefAttr.nChrCellHeight;
}
break;
}
case GOrdPChXtr: PushAttr(nOrderID);
[[fallthrough]];
case GOrdSChXtr:
break;
case GOrdPChShr: PushAttr(nOrderID);
[[fallthrough]];
case GOrdSChShr:
break;
case GOrdPTxAlg: PushAttr(nOrderID);
[[fallthrough]];
case GOrdSTxAlg: SAL_INFO("filter.os2met","GOrdSTxAlg");
break;
case GOrdPMkPrc: PushAttr(nOrderID);
[[fallthrough]];
case GOrdSMkPrc: {
sal_uInt8 nbyte(0);
pOS2MET->ReadUChar( nbyte );
if (nbyte==0) aAttr.nMrkPrec=aDefAttr.nMrkPrec;
else aAttr.nMrkPrec=nbyte;
break;
}
case GOrdPMkSet: PushAttr(nOrderID);
[[fallthrough]];
case GOrdSMkSet: {
sal_uInt8 nbyte(0);
pOS2MET->ReadUChar( nbyte );
if (nbyte==0) aAttr.nMrkSet=aDefAttr.nMrkSet;
else aAttr.nMrkSet=nbyte;
break;
}
case GOrdPMkSym: PushAttr(nOrderID);
[[fallthrough]];
case GOrdSMkSym: {
sal_uInt8 nbyte(0);
pOS2MET->ReadUChar( nbyte );
if (nbyte==0) aAttr.nMrkSymbol=aDefAttr.nMrkSymbol;
else aAttr.nMrkSymbol=nbyte;
break;
}
case GOrdPMkCel: PushAttr(nOrderID);
[[fallthrough]];
case GOrdSMkCel: {
sal_uInt16 nLen=nOrderLen;
aAttr.aMrkCellSize.setWidth(ReadCoord(bCoord32) );
aAttr.aMrkCellSize.setHeight(ReadCoord(bCoord32) );
if (bCoord32) nLen-=8; else nLen-=4;
if (nLen>=2) {
sal_uInt8 nbyte(0);
pOS2MET->ReadUChar( nbyte );
if ((nbyte&0x80)==0 && aAttr.aMrkCellSize==Size(0,0))
aAttr.aMrkCellSize=aDefAttr.aMrkCellSize;
}
break;
}
case GOrdPArcPa: PushAttr(nOrderID);
[[fallthrough]];
case GOrdSArcPa:
aAttr.nArcP=ReadCoord(bCoord32);
aAttr.nArcQ=ReadCoord(bCoord32);
aAttr.nArcR=ReadCoord(bCoord32);
aAttr.nArcS=ReadCoord(bCoord32);
break;
case GOrdPCrPos: PushAttr(nOrderID);
[[fallthrough]];
case GOrdSCrPos:
aAttr.aCurPos=ReadPoint();
break;
case GOrdPMdTrn: PushAttr(nOrderID);
[[fallthrough]];
case GOrdSMdTrn: SAL_INFO("filter.os2met","GOrdSMdTrn");
break;
case GOrdPPkIdn: PushAttr(nOrderID);
[[fallthrough]];
case GOrdSPkIdn: SAL_INFO("filter.os2met","GOrdSPkIdn");
break;
case GOrdSVwTrn: SAL_INFO("filter.os2met","GOrdSVwTrn");
break;
case GOrdPVwWin: PushAttr(nOrderID);
[[fallthrough]];
case GOrdSVwWin: SAL_INFO("filter.os2met","GOrdSVwWin");
break;
default: SAL_INFO("filter.os2met","Unknown order: " << nOrderID);
}
}
void OS2METReader::ReadDsc(sal_uInt16 nDscID)
{
switch (nDscID) {
case 0x00f7: { // 'Specify GVM Subset'
sal_uInt8 nbyte(0);
pOS2MET->SeekRel(6);
pOS2MET->ReadUChar( nbyte );
if (nbyte==0x05) bCoord32=true;
else if (nbyte==0x04) bCoord32=false;
else {
pOS2MET->SetError(SVSTREAM_FILEFORMAT_ERROR);
ErrorCode=1;
}
break;
}
case 0x00f6:
{
// 'Set Picture Descriptor'
bool b32;
sal_uInt8 nbyte(0), nUnitType(0);
pOS2MET->SeekRel(2);
pOS2MET->ReadUChar( nbyte );
if (nbyte==0x05)
b32=true;
else if(nbyte==0x04)
b32=false;
else
{
b32 = false; // -Wall added the case.
pOS2MET->SetError(SVSTREAM_FILEFORMAT_ERROR);
ErrorCode=2;
}
pOS2MET->ReadUChar( nUnitType );
sal_Int32 xr = ReadCoord(b32);
sal_Int32 yr = ReadCoord(b32);
ReadCoord(b32);
if (nUnitType==0x00 && xr>0 && yr>0)
aGlobMapMode=MapMode(MapUnit::MapInch,Point(0,0),Fraction(10,xr),Fraction(10,yr));
else if (nUnitType==0x01 && xr>0 && yr>0)
aGlobMapMode=MapMode(MapUnit::MapCM,Point(0,0),Fraction(10,xr),Fraction(10,yr));
else
aGlobMapMode=MapMode();
sal_Int32 x1 = ReadCoord(b32);
sal_Int32 x2 = ReadCoord(b32);
sal_Int32 y1 = ReadCoord(b32);
sal_Int32 y2 = ReadCoord(b32);
if (x1>x2)
{
const auto nt = x1;
x1=x2;
x2=nt;
}
if (y1>y2)
{
const auto nt = y1;
y1=y2;
y2=nt;
}
aBoundingRect.SetLeft( x1 );
aBoundingRect.SetRight( x2 );
aBoundingRect.SetTop( y1 );
aBoundingRect.SetBottom( y2 );
// no output beside this bounding rect
pVirDev->IntersectClipRegion( tools::Rectangle( Point(), aBoundingRect.GetSize() ) );
break;
}
case 0x0021: // 'Set Current Defaults'
break;
}
}
void OS2METReader::ReadImageData(sal_uInt16 nDataID, sal_uInt16 nDataLen)
{
OSBitmap * p=pBitmapList; if (p==nullptr) return;
switch (nDataID) {
case 0x0070: // Begin Segment
break;
case 0x0091: // Begin Image Content
break;
case 0x0094: // Image Size
pOS2MET->SeekRel(5);
p->nHeight=ReadBigEndianWord();
p->nWidth=ReadBigEndianWord();
break;
case 0x0095: // Image Encoding
break;
case 0x0096: { // Image IDE-Size
sal_uInt8 nbyte(0);
pOS2MET->ReadUChar( nbyte ); p->nBitsPerPixel=nbyte;
break;
}
case 0x0097: // Image LUT-ID
break;
case 0x009b: // IDE Structure
break;
case 0xfe92: { // Image Data
// At the latest we now need the temporary BMP file and
// inside this file we need the header and the palette.
if (p->pBMP==nullptr) {
p->pBMP=new SvMemoryStream();
p->pBMP->SetEndian(SvStreamEndian::LITTLE);
if (p->nWidth==0 || p->nHeight==0 || p->nBitsPerPixel==0) {
pOS2MET->SetError(SVSTREAM_FILEFORMAT_ERROR);
ErrorCode=3;
return;
}
// write (Windows-)BITMAPINFOHEADER:
p->pBMP->WriteUInt32( 40 ).WriteUInt32( p->nWidth ).WriteUInt32( p->nHeight );
p->pBMP->WriteUInt16( 1 ).WriteUInt16( p->nBitsPerPixel );
p->pBMP->WriteUInt32( 0 ).WriteUInt32( 0 ).WriteUInt32( 0 ).WriteUInt32( 0 );
p->pBMP->WriteUInt32( 0 ).WriteUInt32( 0 );
// write color table:
if (p->nBitsPerPixel<=8) {
sal_uInt16 i, nColTabSize=1<<(p->nBitsPerPixel);
for (i=0; i<nColTabSize; i++) p->pBMP->WriteUInt32( GetPalette0RGB(i) );
}
}
// OK, now the map data is being pushed. Unfortunately OS2 and BMP
// do have a different RGB ordering when using 24-bit
std::unique_ptr<sal_uInt8[]> pBuf(new sal_uInt8[nDataLen]);
pOS2MET->ReadBytes(pBuf.get(), nDataLen);
sal_uInt32 nBytesPerLineToSwap = (p->nBitsPerPixel == 24) ?
((p->nWidth * 3 + 3) & 0xfffffffc) : 0;
if (nBytesPerLineToSwap) {
sal_uInt32 nAlign = p->nMapPos - (p->nMapPos % nBytesPerLineToSwap);
sal_uInt32 i=0;
while (nAlign+i+2<p->nMapPos+nDataLen) {
if (nAlign+i>=p->nMapPos) {
sal_uInt32 j = nAlign + i - p->nMapPos;
std::swap(pBuf[j], pBuf[j+2]);
}
i+=3;
if (i + 2 >= nBytesPerLineToSwap) {
nAlign += nBytesPerLineToSwap;
i=0;
}
}
}
p->pBMP->WriteBytes(pBuf.get(), nDataLen);
p->nMapPos+=nDataLen;
break;
}
case 0x0093: // End Image Content
break;
case 0x0071: // End Segment
break;
}
}
void OS2METReader::ReadFont(sal_uInt16 nFieldSize)
{
OSFont * pF=new OSFont;
pF->pSucc=pFontList; pFontList=pF;
pF->nID=0;
pF->aFont.SetTransparent(true);
pF->aFont.SetAlignment(ALIGN_BASELINE);
auto nPos=pOS2MET->Tell();
auto nMaxPos = nPos + nFieldSize;
pOS2MET->SeekRel(2); nPos+=2;
while (nPos<nMaxPos && pOS2MET->good()) {
sal_uInt8 nByte(0);
pOS2MET->ReadUChar(nByte);
sal_uInt16 nLen = static_cast<sal_uInt16>(nByte) & 0x00ff;
if (nLen == 0)
{
pOS2MET->SetError(SVSTREAM_FILEFORMAT_ERROR);
ErrorCode=4;
}
sal_uInt8 nTripType(0);
pOS2MET->ReadUChar( nTripType );
switch (nTripType) {
case 0x02:
{
sal_uInt8 nTripType2(0);
pOS2MET->ReadUChar( nTripType2 );
switch (nTripType2) {
case 0x84: // Font name
break;
case 0x08: { // Font Typeface
char str[33];
pOS2MET->SeekRel(1);
str[pOS2MET->ReadBytes(str, 32)] = 0;
OUString aStr( str, strlen(str), osl_getThreadTextEncoding() );
if ( aStr.equalsIgnoreAsciiCase( "Helv" ) )
aStr = "Helvetica";
pF->aFont.SetFamilyName( aStr );
break;
}
}
break;
}
case 0x24: // Icid
{
sal_uInt8 nTripType2(0);
pOS2MET->ReadUChar( nTripType2 );
switch (nTripType2) {
case 0x05: //Icid
pOS2MET->ReadUChar( nByte );
pF->nID=static_cast<sal_uInt32>(nByte)&0xff;
break;
}
break;
}
case 0x20: // Font Binary GCID
break;
case 0x1f: { // Font Attributes
FontWeight eWeight;
sal_uInt8 nbyte(0);
pOS2MET->ReadUChar( nbyte );
switch (nbyte) {
case 1: eWeight=WEIGHT_THIN; break;
case 2: eWeight=WEIGHT_ULTRALIGHT; break;
case 3: eWeight=WEIGHT_LIGHT; break;
case 4: eWeight=WEIGHT_SEMILIGHT; break;
case 5: eWeight=WEIGHT_NORMAL; break;
case 6: eWeight=WEIGHT_SEMIBOLD; break;
case 7: eWeight=WEIGHT_BOLD; break;
case 8: eWeight=WEIGHT_ULTRABOLD; break;
case 9: eWeight=WEIGHT_BLACK; break;
default: eWeight=WEIGHT_DONTKNOW;
}
pF->aFont.SetWeight(eWeight);
break;
}
}
nPos+=nLen;
pOS2MET->Seek(nPos);
}
}
void OS2METReader::ReadField(sal_uInt16 nFieldType, sal_uInt16 nFieldSize)
{
switch (nFieldType) {
case BegDocumnMagic:
break;
case EndDocumnMagic:
break;
case BegResGrpMagic:
break;
case EndResGrpMagic:
break;
case BegColAtrMagic:
break;
case EndColAtrMagic:
break;
case BlkColAtrMagic: {
sal_uInt8 nbyte;
sal_uInt16 nStartIndex, nEndIndex, i, nElemLen, nBytesPerCol;
auto nPos = pOS2MET->Tell();
auto nMaxPos = nPos + nFieldSize;
pOS2MET->SeekRel(3); nPos+=3;
while (nPos<nMaxPos && pOS2MET->GetError()==ERRCODE_NONE) {
pOS2MET->ReadUChar( nbyte ); nElemLen=static_cast<sal_uInt16>(nbyte) & 0x00ff;
if (nElemLen>11) {
pOS2MET->SeekRel(4);
nStartIndex=ReadBigEndianWord();
pOS2MET->SeekRel(3);
pOS2MET->ReadUChar( nbyte );
nBytesPerCol=static_cast<sal_uInt16>(nbyte) & 0x00ff;
if (nBytesPerCol == 0)
{
pOS2MET->SetError(SVSTREAM_FILEFORMAT_ERROR);
ErrorCode=4;
break;
}
nEndIndex=nStartIndex+(nElemLen-11)/nBytesPerCol;
for (i=nStartIndex; i<nEndIndex; i++) {
if (nBytesPerCol > 3) pOS2MET->SeekRel(nBytesPerCol-3);
auto nCol = ReadBigEndian3BytesLong();
SetPalette0RGB(i, nCol);
}
}
else if (nElemLen<10) {
pOS2MET->SetError(SVSTREAM_FILEFORMAT_ERROR);
ErrorCode=4;
}
nPos += nElemLen;
pOS2MET->Seek(nPos);
}
break;
}
case MapColAtrMagic:
break;
case BegImgObjMagic: {
// create new bitmap by now: (will be filled later)
OSBitmap * pB=new OSBitmap;
pB->pSucc=pBitmapList; pBitmapList=pB;
pB->pBMP=nullptr; pB->nWidth=0; pB->nHeight=0; pB->nBitsPerPixel=0;
pB->nMapPos=0;
// determine ID of the bitmap:
pB->nID=0;
for (sal_uInt8 i = 0; i < 4; ++i) {
sal_uInt8 nbyte(0),nbyte2(0);
pOS2MET->ReadUChar(nbyte).ReadUChar(nbyte2);
nbyte = sal_uInt8(nbyte - 0x30);
nbyte2 = sal_uInt8(nbyte2 - 0x30);
nbyte = (nbyte << 4) | nbyte2;
pB->nID=(pB->nID>>8)|(static_cast<sal_uInt32>(nbyte)<<24);
}
// put new palette on the palette stack: (will be filled later)
OSPalette * pP=new OSPalette;
pP->pSucc=pPaletteStack; pPaletteStack=pP;
pP->p0RGB=nullptr; pP->nSize=0;
break;
}
case EndImgObjMagic: {
// read temporary Windows BMP file:
if (pBitmapList==nullptr || pBitmapList->pBMP==nullptr ||
pBitmapList->pBMP->GetError()!=ERRCODE_NONE) {
pOS2MET->SetError(SVSTREAM_FILEFORMAT_ERROR);
ErrorCode=5;
return;
}
pBitmapList->pBMP->Seek(0);
ReadDIBBitmapEx(pBitmapList->aBitmapEx, *(pBitmapList->pBMP), false);
if (pBitmapList->pBMP->GetError()!=ERRCODE_NONE) {
pOS2MET->SetError(SVSTREAM_FILEFORMAT_ERROR);
ErrorCode=6;
}
delete pBitmapList->pBMP; pBitmapList->pBMP=nullptr;
// kill palette from stack:
OSPalette * pP=pPaletteStack;
if (pP!=nullptr) {
pPaletteStack=pP->pSucc;
delete[] pP->p0RGB;
delete pP;
}
break;
}
case DscImgObjMagic:
break;
case DatImgObjMagic: {
sal_uInt16 nDataID, nDataLen;
sal_uInt8 nbyte;
auto nPos = pOS2MET->Tell();
auto nMaxPos = nPos + nFieldSize;
while (nPos<nMaxPos && pOS2MET->GetError()==ERRCODE_NONE) {
pOS2MET->ReadUChar( nbyte ); nDataID=static_cast<sal_uInt16>(nbyte)&0x00ff;
if (nDataID==0x00fe) {
pOS2MET->ReadUChar( nbyte );
nDataID=(nDataID<<8)|(static_cast<sal_uInt16>(nbyte)&0x00ff);
nDataLen=ReadBigEndianWord();
nPos+=4;
}
else {
pOS2MET->ReadUChar( nbyte ); nDataLen=static_cast<sal_uInt16>(nbyte)&0x00ff;
nPos+=2;
}
ReadImageData(nDataID, nDataLen);
nPos += nDataLen;
pOS2MET->Seek(nPos);
}
break;
}
case BegObEnv1Magic:
break;
case EndObEnv1Magic:
break;
case BegGrfObjMagic:
break;
case EndGrfObjMagic: {
if (!xOrdFile)
break;
auto nMaxPos = xOrdFile->Tell();
if (!nMaxPos)
break;
// In xOrdFile all "DatGrfObj" fields were collected so that the
// therein contained "Orders" are continuous and not segmented by fields.
// To read them from the memory stream without having any trouble,
// we use a little trick:
SvStream *pSave = pOS2MET;
pOS2MET=xOrdFile.get(); //(!)
pOS2MET->Seek(0);
// in a sane world this block is just: pOS2MET->SetStreamSize(nMaxPos);
if (nMaxPos)
{
#ifndef NDEBUG
const sal_uInt8 nLastByte = static_cast<const sal_uInt8*>(xOrdFile->GetData())[nMaxPos-1];
#endif
pOS2MET->SetStreamSize(nMaxPos); // shrink stream to written portion
assert(pOS2MET->remainingSize() == nMaxPos || pOS2MET->remainingSize() == nMaxPos - 1);
SAL_WARN_IF(pOS2MET->remainingSize() == nMaxPos, "filter.os2met", "this SetStreamSize workaround is no longer needed");
// The shrink case of SvMemoryStream::ReAllocateMemory, i.e. nEndOfData = nNewSize - 1, looks buggy to me, workaround
// it by using Seek to move the nEndOfData to the sane position
if (pOS2MET->remainingSize() < nMaxPos)
{
pOS2MET->Seek(nMaxPos);
pOS2MET->Seek(0);
}
assert(nLastByte == static_cast<const sal_uInt8*>(xOrdFile->GetData())[nMaxPos-1]);
}
assert(pOS2MET->remainingSize() == nMaxPos);
// disable stream growing past its current size
xOrdFile->SetResizeOffset(0);
// "Segment header":
sal_uInt8 nbyte(0);
pOS2MET->ReadUChar( nbyte );
if (nbyte==0x70) { // header exists
pOS2MET->SeekRel(15); // but we don't need it
}
else pOS2MET->SeekRel(-1); // no header, go back one byte
// loop through Order:
while (pOS2MET->Tell()<nMaxPos && pOS2MET->GetError()==ERRCODE_NONE) {
pOS2MET->ReadUChar( nbyte );
sal_uInt16 nOrderID = static_cast<sal_uInt16>(nbyte) & 0x00ff;
if (nOrderID==0x00fe) {
pOS2MET->ReadUChar( nbyte );
nOrderID=(nOrderID << 8) | (static_cast<sal_uInt16>(nbyte) & 0x00ff);
}
sal_uInt16 nOrderLen;
if (nOrderID>0x00ff || nOrderID==GOrdPolygn) {
// ooo: As written in OS2 documentation, the order length should now
// be written as big endian word. (Quote: "Highorder byte precedes loworder byte").
// In reality there are files in which the length is stored as little endian word
// (at least for nOrderID==GOrdPolygn)
// So we throw a coin or what else can we do?
pOS2MET->ReadUChar( nbyte ); nOrderLen=static_cast<sal_uInt16>(nbyte)&0x00ff;
pOS2MET->ReadUChar( nbyte ); if (nbyte!=0) nOrderLen=nOrderLen<<8|(static_cast<sal_uInt16>(nbyte)&0x00ff);
}
else if (nOrderID==GOrdSTxAlg || nOrderID==GOrdPTxAlg) nOrderLen=2;
else if ((nOrderID&0xff88)==0x0008) nOrderLen=1;
else if (nOrderID==0x0000 || nOrderID==0x00ff) nOrderLen=0;
else { pOS2MET->ReadUChar( nbyte ); nOrderLen=static_cast<sal_uInt16>(nbyte) & 0x00ff; }
auto nPos=pOS2MET->Tell();
ReadOrder(nOrderID, nOrderLen);
if (nPos+nOrderLen < pOS2MET->Tell()) {
SAL_INFO("filter.os2met","Order is shorter than expected. OrderID: " << nOrderID << " Position: " << nPos);
}
else if (nPos+nOrderLen != pOS2MET->Tell()) {
SAL_INFO("filter.os2met","Order was not read completely. OrderID: " << nOrderID << " Position: " << nPos);
}
pOS2MET->Seek(nPos+nOrderLen);
}
pOS2MET=pSave;
if (xOrdFile->GetError()) {
pOS2MET->SetError(SVSTREAM_FILEFORMAT_ERROR);
ErrorCode=10;
}
xOrdFile.reset();
break;
}
case DscGrfObjMagic: {
sal_uInt16 nDscID, nDscLen;
sal_uInt8 nbyte;
auto nMaxPos = pOS2MET->Tell() + nFieldSize;
while (pOS2MET->Tell()<nMaxPos && pOS2MET->GetError()==ERRCODE_NONE) {
pOS2MET->ReadUChar( nbyte ); nDscID =static_cast<sal_uInt16>(nbyte) & 0x00ff;
pOS2MET->ReadUChar( nbyte ); nDscLen=static_cast<sal_uInt16>(nbyte) & 0x00ff;
auto nPos = pOS2MET->Tell();
ReadDsc(nDscID);
pOS2MET->Seek(nPos+nDscLen);
}
break;
}
case DatGrfObjMagic: {
if (!xOrdFile) {
xOrdFile.reset(new SvMemoryStream);
xOrdFile->SetEndian(SvStreamEndian::LITTLE);
}
std::unique_ptr<sal_uInt8[]> pBuf(new sal_uInt8[nFieldSize]);
pOS2MET->ReadBytes(pBuf.get(), nFieldSize);
xOrdFile->WriteBytes(pBuf.get(), nFieldSize);
break;
}
case MapCodFntMagic:
ReadFont(nFieldSize);
break;
case MapDatResMagic:
break;
}
}
void OS2METReader::ReadOS2MET( SvStream & rStreamOS2MET, GDIMetaFile & rGDIMetaFile )
{
ErrorCode=0;
pOS2MET = &rStreamOS2MET;
auto nOrigPos = pOS2MET->Tell();
SvStreamEndian nOrigNumberFormat = pOS2MET->GetEndian();
bCoord32 = true;
pPaletteStack=nullptr;
pAreaStack=nullptr;
pPathStack=nullptr;
pPathList=nullptr;
pFontList=nullptr;
pBitmapList=nullptr;
pAttrStack=nullptr;
aDefAttr.aLinCol =COL_BLACK;
aDefAttr.aLinBgCol =COL_WHITE;
aDefAttr.eLinMix =RasterOp::OverPaint;
aDefAttr.eLinBgMix =RasterOp::OverPaint;
aDefAttr.aChrCol =COL_BLACK;
aDefAttr.aChrBgCol =COL_WHITE;
aDefAttr.eChrMix =RasterOp::OverPaint;
aDefAttr.eChrBgMix =RasterOp::OverPaint;
aDefAttr.aMrkCol =COL_BLACK;
aDefAttr.aMrkBgCol =COL_WHITE;
aDefAttr.eMrkMix =RasterOp::OverPaint;
aDefAttr.eMrkBgMix =RasterOp::OverPaint;
aDefAttr.aPatCol =COL_BLACK;
aDefAttr.aPatBgCol =COL_WHITE;
aDefAttr.ePatMix =RasterOp::OverPaint;
aDefAttr.ePatBgMix =RasterOp::OverPaint;
aDefAttr.aImgCol =COL_BLACK;
aDefAttr.aImgBgCol =COL_WHITE;
aDefAttr.eImgMix =RasterOp::OverPaint;
aDefAttr.eImgBgMix =RasterOp::OverPaint;
aDefAttr.nArcP =1;
aDefAttr.nArcQ =1;
aDefAttr.nArcR =0;
aDefAttr.nArcS =0;
aDefAttr.nChrAng =0_deg10;
aDefAttr.nChrCellHeight = 12;
aDefAttr.nChrSet =0;
aDefAttr.aCurPos =Point(0,0);
aDefAttr.eLinStyle =PEN_SOLID;
aDefAttr.nLinWidth =0;
aDefAttr.aMrkCellSize=Size(10,10);
aDefAttr.nMrkPrec =0x01;
aDefAttr.nMrkSet =0xff;
aDefAttr.nMrkSymbol =0x01;
aDefAttr.bFill =true;
aDefAttr.nStrLinWidth=0;
aAttr=aDefAttr;
xOrdFile.reset();
rGDIMetaFile.Record(pVirDev);
pOS2MET->SetEndian(SvStreamEndian::LITTLE);
sal_uInt64 nPos = pOS2MET->Tell();
for (;;) {
sal_uInt16 nFieldSize = ReadBigEndianWord();
sal_uInt8 nMagicByte(0);
pOS2MET->ReadUChar( nMagicByte );
if (nMagicByte!=0xd3) {
pOS2MET->SetError(SVSTREAM_FILEFORMAT_ERROR);
ErrorCode=7;
break;
}
sal_uInt16 nFieldType(0);
pOS2MET->ReadUInt16(nFieldType);
pOS2MET->SeekRel(3);
if (pOS2MET->GetError())
break;
if (nFieldType==EndDocumnMagic)
break;
if (pOS2MET->eof() || nFieldSize < 8)
{
pOS2MET->SetError(SVSTREAM_FILEFORMAT_ERROR);
ErrorCode=8;
break;
}
nPos+=8; nFieldSize-=8;
if (nFieldSize > pOS2MET->remainingSize())
{
pOS2MET->SetError(SVSTREAM_FILEFORMAT_ERROR);
ErrorCode=8;
break;
}
ReadField(nFieldType, nFieldSize);
nPos += nFieldSize;
if (pOS2MET->Tell() > nPos)
{
pOS2MET->SetError(SVSTREAM_FILEFORMAT_ERROR);
ErrorCode=9;
break;
}
pOS2MET->Seek(nPos);
}
rGDIMetaFile.Stop();
rGDIMetaFile.SetPrefMapMode( aGlobMapMode );
if( aBoundingRect.GetWidth() && aBoundingRect.GetHeight() )
rGDIMetaFile.SetPrefSize( aBoundingRect.GetSize() );
else
{
if( aCalcBndRect.Left() || aCalcBndRect.Top() )
rGDIMetaFile.Move( -aCalcBndRect.Left(), -aCalcBndRect.Top() );
rGDIMetaFile.SetPrefSize( aCalcBndRect.GetSize() );
}
pOS2MET->SetEndian(nOrigNumberFormat);
if (pOS2MET->GetError()) {
SAL_INFO("filter.os2met","Error code: " << ErrorCode);
pOS2MET->Seek(nOrigPos);
}
}
//================== GraphicImport - the exported function ================
bool ImportMetGraphic(SvStream & rStream, Graphic & rGraphic)
{
OS2METReader aOS2METReader;
GDIMetaFile aMTF;
bool bRet = false;
try
{
aOS2METReader.ReadOS2MET( rStream, aMTF );
if ( !rStream.GetError() )
{
rGraphic=Graphic( aMTF );
bRet = true;
}
}
catch (const css::uno::Exception&)
{
}
return bRet;
}
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
↑ V530 The return value of function 'Union' is required to be utilized.
↑ V530 The return value of function 'Union' is required to be utilized.
↑ V530 The return value of function 'ReadPoint' is required to be utilized.
↑ V530 The return value of function 'ReadPoint' is required to be utilized.
↑ V530 The return value of function 'Union' is required to be utilized.
↑ V530 The return value of function 'Union' is required to be utilized.
↑ V530 The return value of function 'Union' is required to be utilized.
↑ V530 The return value of function 'Union' is required to be utilized.
↑ V530 The return value of function 'Union' is required to be utilized.
↑ V530 The return value of function 'Union' is required to be utilized.
↑ V547 Expression 'nMaxPos' is always true.
↑ V1071 Consider inspecting the 'ReadCoord' function. The return value is not always used. Total calls: 22, discarded results: 1.