diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-07 09:06:44 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-07 09:06:44 +0000 |
commit | ed5640d8b587fbcfed7dd7967f3de04b37a76f26 (patch) | |
tree | 7a5f7c6c9d02226d7471cb3cc8fbbf631b415303 /vcl/source/filter/imet/ios2met.cxx | |
parent | Initial commit. (diff) | |
download | libreoffice-upstream.tar.xz libreoffice-upstream.zip |
Adding upstream version 4:7.4.7.upstream/4%7.4.7upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'vcl/source/filter/imet/ios2met.cxx')
-rw-r--r-- | vcl/source/filter/imet/ios2met.cxx | 2883 |
1 files changed, 2883 insertions, 0 deletions
diff --git a/vcl/source/filter/imet/ios2met.cxx b/vcl/source/filter/imet/ios2met.cxx new file mode 100644 index 000000000..fe856c1d7 --- /dev/null +++ b/vcl/source/filter/imet/ios2met.cxx @@ -0,0 +1,2883 @@ +/* -*- 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 ( sal_uInt16 i = 0; i < rPolyPolygon.Count(); i++ ) + pVirDev->DrawPolyLine( rPolyPolygon.GetObject( i ), 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 ); + + sal_uInt16 nDotCount = 0; + sal_uInt16 nDashCount = 0; + switch ( ePenStyle ) + { + case PEN_NULL : + eLineStyle = LineStyle::NONE; + break; + case PEN_DASHDOT : + nDashCount++; + [[fallthrough]]; + case PEN_DOT : + nDotCount++; + nDashCount--; + [[fallthrough]]; + case PEN_DASH : + nDashCount++; + aLineInfo.SetDotCount( nDotCount ); + aLineInfo.SetDashCount( nDashCount ); + aLineInfo.SetDistance( nLineWidth ); + aLineInfo.SetDotLen( nLineWidth ); + aLineInfo.SetDashLen( nLineWidth << 2 ); + eLineStyle = LineStyle::Dash; + break; + case PEN_SOLID: + break; // -Wall not handled... + } + 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("attempt to read past end of input", 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("attempt to read past end of input", 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=sqrt(q*q*(x1-cx)*(x1-cx)+p*p*(y1-cy)*(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 = 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=aDefAttr.nLinWidth; + else aAttr.nLinWidth=static_cast<sal_uInt16>(nbyte)-1; + 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 -= 0x30; + 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: */ |