mirror of
https://github.com/ONLYOFFICE/core.git
synced 2026-07-11 03:42:47 +08:00
git-svn-id: svn://fileserver/activex/AVS/Sources/TeamlabOffice/trunk/ServerComponents@62550 954022d7-b5bf-4e40-9824-e11837661b57
4943 lines
147 KiB
C++
4943 lines
147 KiB
C++
#include <stdlib.h>
|
||
#include <stdio.h>
|
||
#include <stddef.h>
|
||
#include <string.h>
|
||
#include <math.h>
|
||
#include "MemoryUtils.h"
|
||
#include "GlobalParams.h"
|
||
#include "CharTypes.h"
|
||
#include "Object.h"
|
||
#include "Array.h"
|
||
#include "Dict.h"
|
||
#include "Stream.h"
|
||
#include "Lexer.h"
|
||
#include "Parser.h"
|
||
#include "GFont.h"
|
||
#include "GState.h"
|
||
#include "RendererOutputDev.h"
|
||
#include "OutputDevice.h"
|
||
#include "Page.h"
|
||
#include "Annot.h"
|
||
#include "Graphics.h"
|
||
|
||
// Под виндой переопределяется
|
||
#ifdef GetObject
|
||
#undef GetObject
|
||
#endif
|
||
|
||
#ifndef M_PI
|
||
#define M_PI 3.14159265358979323846
|
||
#endif
|
||
|
||
//-------------------------------------------------------------------------------------------------------------------------------
|
||
// Константы
|
||
//-------------------------------------------------------------------------------------------------------------------------------
|
||
|
||
#define functionMaxDepth 6
|
||
|
||
#define functionColorDelta (DoubleToColor(1 / 256.0))
|
||
|
||
#define axialMaxSplits 256
|
||
|
||
#define axialColorDelta (DoubleToColor(1 / 256.0))
|
||
|
||
#define radialMaxSplits 256
|
||
|
||
#define radialColorDelta (DoubleToColor(1 / 256.0))
|
||
|
||
#define gouraudMaxDepth 1
|
||
|
||
#define gouraudColorDelta (DoubleToColor(1 / 256.0))
|
||
|
||
#define patchMaxDepth 6
|
||
|
||
#define patchColorDelta (DoubleToColor(1 / 256.0))
|
||
|
||
//-------------------------------------------------------------------------------------------------------------------------------
|
||
// Таблица графических операторов
|
||
//-------------------------------------------------------------------------------------------------------------------------------
|
||
|
||
#ifdef WIN32 // this works around a bug in the VC7 compiler
|
||
# pragma optimize("",off)
|
||
#endif
|
||
|
||
namespace PdfReader
|
||
{
|
||
Operator Graphics::OperatorsTable[] =
|
||
{
|
||
{ "\"", 3, { argNum, argNum, argString }, &Graphics::OperatorMoveSetShowText },
|
||
{ "'", 1, { argString }, &Graphics::OperatorMoveShowText },
|
||
{ "B", 0, { argNone }, &Graphics::OperatorFillStroke },
|
||
{ "B*", 0, { argNone }, &Graphics::OperatorEOFillStroke },
|
||
{ "BDC", 2, { argName, argProps }, &Graphics::OperatorBeginMarkedContent },
|
||
{ "BI", 0, { argNone }, &Graphics::OperatorBeginImage },
|
||
{ "BMC", 1, { argName }, &Graphics::OperatorBeginMarkedContent },
|
||
{ "BT", 0, { argNone }, &Graphics::OperatorBeginText },
|
||
{ "BX", 0, { argNone }, &Graphics::OperatorBeginIgnoreUndef },
|
||
{ "CS", 1, { argName }, &Graphics::OperatorSetStrokeColorSpace },
|
||
{ "DP", 2, { argName, argProps }, &Graphics::OperatorMarkPoint },
|
||
{ "Do", 1, { argName }, &Graphics::OperatorXObject },
|
||
{ "EI", 0, { argNone }, &Graphics::OperatorEndImage },
|
||
{ "EMC", 0, { argNone }, &Graphics::OperatorEndMarkedContent },
|
||
{ "ET", 0, { argNone }, &Graphics::OperatorEndText },
|
||
{ "EX", 0, { argNone }, &Graphics::OperatorEndIgnoreUndef },
|
||
{ "F", 0, { argNone }, &Graphics::OperatorFill },
|
||
{ "G", 1, { argNum }, &Graphics::OperatorSetStrokeGray },
|
||
{ "ID", 0, { argNone }, &Graphics::OperatorImageData },
|
||
{ "J", 1, { argInt }, &Graphics::OperatorSetLineCap },
|
||
{ "K", 4, { argNum, argNum, argNum, argNum }, &Graphics::OperatorSetStrokeCMYKColor },
|
||
{ "M", 1, { argNum }, &Graphics::OperatorSetMiterLimit },
|
||
{ "MP", 1, { argName }, &Graphics::OperatorMarkPoint },
|
||
{ "Q", 0, { argNone }, &Graphics::OperatorRestore },
|
||
{ "RG", 3, { argNum, argNum, argNum }, &Graphics::OperatorSetStrokeRGBColor },
|
||
{ "S", 0, { argNone }, &Graphics::OperatorStroke },
|
||
{ "SC", -4, { argNum, argNum, argNum, argNum }, &Graphics::OperatorSetStrokeColor },
|
||
{ "SCN", -33, { argSCN, argSCN, argSCN, argSCN, argSCN, argSCN,
|
||
argSCN, argSCN, argSCN, argSCN, argSCN, argSCN,
|
||
argSCN, argSCN, argSCN, argSCN, argSCN, argSCN,
|
||
argSCN, argSCN, argSCN, argSCN, argSCN, argSCN,
|
||
argSCN, argSCN, argSCN, argSCN, argSCN, argSCN,
|
||
argSCN, argSCN, argSCN }, &Graphics::OperatorSetStrokeColorN },
|
||
{ "T*", 0, { argNone }, &Graphics::OperatorTextNextLine },
|
||
{ "TD", 2, { argNum, argNum }, &Graphics::OperatorTextMoveSet },
|
||
{ "TJ", 1, { argArray }, &Graphics::OperatorShowSpaceText },
|
||
{ "TL", 1, { argNum }, &Graphics::OperatorSetTextLeading },
|
||
{ "Tc", 1, { argNum }, &Graphics::OperatorSetCharSpacing },
|
||
{ "Td", 2, { argNum, argNum }, &Graphics::OperatorTextMove },
|
||
{ "Tf", 2, { argName, argNum }, &Graphics::OperatorSetFont },
|
||
{ "Tj", 1, { argString }, &Graphics::OperatorShowText },
|
||
{ "Tm", 6, { argNum, argNum, argNum, argNum, argNum, argNum }, &Graphics::OperatorSetTextMatrix },
|
||
{ "Tr", 1, { argInt }, &Graphics::OperatorSetTextRender },
|
||
{ "Ts", 1, { argNum }, &Graphics::OperatorSetTextRise },
|
||
{ "Tw", 1, { argNum }, &Graphics::OperatorSetWordSpacing },
|
||
{ "Tz", 1, { argNum }, &Graphics::OperatorSetHorizScaling },
|
||
{ "W", 0, { argNone }, &Graphics::OperatorClip },
|
||
{ "W*", 0, { argNone }, &Graphics::OperatorEOClip },
|
||
{ "b", 0, { argNone }, &Graphics::OperatorCloseFillStroke },
|
||
{ "b*", 0, { argNone }, &Graphics::OperatorCloseEOFillStroke },
|
||
{ "c", 6, { argNum, argNum, argNum, argNum, argNum, argNum }, &Graphics::OperatorCurveTo },
|
||
{ "cm", 6, { argNum, argNum, argNum, argNum, argNum, argNum }, &Graphics::OperatorConcat },
|
||
{ "cs", 1, { argName }, &Graphics::OperatorSetFillColorSpace },
|
||
{ "d", 2, { argArray, argNum }, &Graphics::OperatorSetDash },
|
||
{ "d0", 2, { argNum, argNum }, &Graphics::OperatorSetCharWidth },
|
||
{ "d1", 6, { argNum, argNum, argNum, argNum, argNum, argNum }, &Graphics::OperatorSetCacheDevice },
|
||
{ "f", 0, { argNone }, &Graphics::OperatorFill },
|
||
{ "f*", 0, { argNone }, &Graphics::OperatorEOFill },
|
||
{ "g", 1, { argNum }, &Graphics::OperatorSetFillGray },
|
||
{ "gs", 1, { argName }, &Graphics::OperatorSetExtGState },
|
||
{ "h", 0, { argNone }, &Graphics::OperatorClosePath },
|
||
{ "i", 1, { argNum }, &Graphics::OperatorSetFlat },
|
||
{ "j", 1, { argInt }, &Graphics::OperatorSetLineJoin },
|
||
{ "k", 4, { argNum, argNum, argNum, argNum }, &Graphics::OperatorSetFillCMYKColor },
|
||
{ "l", 2, { argNum, argNum }, &Graphics::OperatorLineTo },
|
||
{ "m", 2, { argNum, argNum }, &Graphics::OperatorMoveTo },
|
||
{ "n", 0, { argNone }, &Graphics::OperatorEndPath },
|
||
{ "q", 0, { argNone }, &Graphics::OperatorSave },
|
||
{ "re", 4, { argNum, argNum, argNum, argNum }, &Graphics::OperatorRectangle },
|
||
{ "rg", 3, { argNum, argNum, argNum }, &Graphics::OperatorSetFillRGBColor },
|
||
{ "ri", 1, { argName }, &Graphics::OperatorSetRenderingIntent },
|
||
{ "s", 0, { argNone }, &Graphics::OperatorCloseStroke },
|
||
{ "sc", -4, { argNum, argNum, argNum, argNum }, &Graphics::OperatorSetFillColor },
|
||
{ "scn", -33, { argSCN, argSCN, argSCN, argSCN, argSCN, argSCN,
|
||
argSCN, argSCN, argSCN, argSCN, argSCN, argSCN,
|
||
argSCN, argSCN, argSCN, argSCN, argSCN, argSCN,
|
||
argSCN, argSCN, argSCN, argSCN, argSCN, argSCN,
|
||
argSCN, argSCN, argSCN, argSCN, argSCN, argSCN,
|
||
argSCN, argSCN, argSCN }, &Graphics::OperatorSetFillColorN },
|
||
{ "sh", 1, { argName }, &Graphics::OperatorShadingFill },
|
||
{ "v", 4, { argNum, argNum, argNum, argNum }, &Graphics::OperatorCurveTo1 },
|
||
{ "w", 1, { argNum }, &Graphics::OperatorSetLineWidth },
|
||
{ "y", 4, { argNum, argNum, argNum, argNum }, &Graphics::OperatorCurveTo2 },
|
||
};
|
||
|
||
#ifdef WIN32 // this works around a bug in the VC7 compiler
|
||
# pragma optimize("",on)
|
||
#endif
|
||
|
||
#define OperatorsCount (sizeof(OperatorsTable) / sizeof(Operator))
|
||
|
||
//-------------------------------------------------------------------------------------------------------------------------------
|
||
// GrResources
|
||
//-------------------------------------------------------------------------------------------------------------------------------
|
||
|
||
GrResources::GrResources(XRef *pXref, Dict *pResourcesDict, GrResources *pNext, GlobalParams *pGlobalParams)
|
||
{
|
||
m_pGlobalParams = pGlobalParams;
|
||
|
||
if (pResourcesDict)
|
||
{
|
||
|
||
// build font dictionary
|
||
m_pFonts = NULL;
|
||
Object oFont;
|
||
pResourcesDict->SearchAndCopy("Font", &oFont);
|
||
if (oFont.IsRef())
|
||
{
|
||
Object oTemp;
|
||
oFont.Fetch(pXref, &oTemp);
|
||
if (oTemp.IsDict())
|
||
{
|
||
Ref oRef = oFont.GetRef();
|
||
m_pFonts = new GrFontDict(pXref, &oRef, oTemp.GetDict(), pGlobalParams);
|
||
}
|
||
oTemp.Free();
|
||
}
|
||
else if (oFont.IsDict())
|
||
{
|
||
m_pFonts = new GrFontDict(pXref, NULL, oFont.GetDict(), pGlobalParams);
|
||
}
|
||
oFont.Free();
|
||
|
||
//XObject
|
||
pResourcesDict->Search("XObject", &m_oXObjectDict);
|
||
|
||
//ColorSpace
|
||
pResourcesDict->Search("ColorSpace", &m_oColorSpaceDict);
|
||
|
||
//Pattern
|
||
pResourcesDict->Search("Pattern", &m_oPatternDict);
|
||
|
||
//Shading
|
||
pResourcesDict->Search("Shading", &m_oShadingDict);
|
||
|
||
//ExtGState
|
||
pResourcesDict->Search("ExtGState", &m_oExtGStateDict);
|
||
}
|
||
else
|
||
{
|
||
m_pFonts = NULL;
|
||
m_oXObjectDict.InitNull();
|
||
m_oColorSpaceDict.InitNull();
|
||
m_oPatternDict.InitNull();
|
||
m_oShadingDict.InitNull();
|
||
m_oExtGStateDict.InitNull();
|
||
}
|
||
|
||
m_pNext = pNext;
|
||
}
|
||
|
||
GrResources::~GrResources()
|
||
{
|
||
if (m_pFonts)
|
||
{
|
||
delete m_pFonts;
|
||
}
|
||
m_oXObjectDict.Free();
|
||
m_oColorSpaceDict.Free();
|
||
m_oPatternDict.Free();
|
||
m_oShadingDict.Free();
|
||
m_oExtGStateDict.Free();
|
||
}
|
||
|
||
GrFont *GrResources::LookupFont(char *sName)
|
||
{
|
||
GrFont *pFont = NULL;
|
||
GrResources *pResources = NULL;
|
||
|
||
for (pResources = this; pResources; pResources = pResources->m_pNext)
|
||
{
|
||
if (pResources->m_pFonts)
|
||
{
|
||
if ((pFont = pResources->m_pFonts->Search(sName)))
|
||
return pFont;
|
||
}
|
||
}
|
||
// TO DO: Error "Unknown font tag"
|
||
return NULL;
|
||
}
|
||
|
||
bool GrResources::LookupXObject(char *sName, Object *pObject)
|
||
{
|
||
GrResources *pResources = NULL;
|
||
|
||
for (pResources = this; pResources; pResources = pResources->m_pNext)
|
||
{
|
||
if (pResources->m_oXObjectDict.IsDict())
|
||
{
|
||
if (!pResources->m_oXObjectDict.DictLookup(sName, pObject)->IsNull())
|
||
return true;
|
||
pObject->Free();
|
||
}
|
||
}
|
||
// TO DO: Error "XObject is unknown"
|
||
return false;
|
||
}
|
||
|
||
bool GrResources::LookupAndCopyXObject(char *sName, Object *pObject)
|
||
{
|
||
GrResources *pResources = NULL;
|
||
|
||
for (pResources = this; pResources; pResources = pResources->m_pNext)
|
||
{
|
||
if (pResources->m_oXObjectDict.IsDict())
|
||
{
|
||
if (!pResources->m_oXObjectDict.DictLookupAndCopy(sName, pObject)->IsNull())
|
||
return true;
|
||
pObject->Free();
|
||
}
|
||
}
|
||
// TO DO: Error "XObject is unknown"
|
||
return false;
|
||
}
|
||
|
||
void GrResources::LookupColorSpace(char *sName, Object *pObject)
|
||
{
|
||
GrResources *pResources = NULL;
|
||
|
||
for (pResources = this; pResources; pResources = pResources->m_pNext)
|
||
{
|
||
if (pResources->m_oColorSpaceDict.IsDict())
|
||
{
|
||
if (!pResources->m_oColorSpaceDict.DictLookup(sName, pObject)->IsNull())
|
||
{
|
||
return;
|
||
}
|
||
pObject->Free();
|
||
}
|
||
}
|
||
pObject->InitNull();
|
||
}
|
||
|
||
GrPattern *GrResources::LookupPattern(char *sName)
|
||
{
|
||
GrResources *pResources = NULL;
|
||
GrPattern *pPattern = NULL;
|
||
Object oTemp;
|
||
|
||
for (pResources = this; pResources; pResources = pResources->m_pNext)
|
||
{
|
||
if (pResources->m_oPatternDict.IsDict())
|
||
{
|
||
if (!pResources->m_oPatternDict.DictLookup(sName, &oTemp)->IsNull())
|
||
{
|
||
pPattern = GrPattern::Parse(&oTemp);
|
||
oTemp.Free();
|
||
return pPattern;
|
||
}
|
||
oTemp.Free();
|
||
}
|
||
}
|
||
// TO DO: Error "Unknown pattern"
|
||
return NULL;
|
||
}
|
||
|
||
GrShading *GrResources::LookupShading(char *sName)
|
||
{
|
||
GrResources *pResources = NULL;
|
||
GrShading *pShading = NULL;
|
||
Object oTemp;
|
||
|
||
for (pResources = this; pResources; pResources = pResources->m_pNext)
|
||
{
|
||
if (pResources->m_oShadingDict.IsDict())
|
||
{
|
||
if (!pResources->m_oShadingDict.DictLookup(sName, &oTemp)->IsNull())
|
||
{
|
||
pShading = GrShading::Parse(&oTemp);
|
||
oTemp.Free();
|
||
return pShading;
|
||
}
|
||
oTemp.Free();
|
||
}
|
||
}
|
||
// TO DO: Error "Unknown shading"
|
||
return NULL;
|
||
}
|
||
|
||
bool GrResources::LookupExtGState(char *sName, Object *pObject)
|
||
{
|
||
GrResources *pResources = NULL;
|
||
|
||
for (pResources = this; pResources; pResources = pResources->m_pNext)
|
||
{
|
||
if (pResources->m_oExtGStateDict.IsDict())
|
||
{
|
||
if (!pResources->m_oExtGStateDict.DictLookup(sName, pObject)->IsNull())
|
||
{
|
||
return true;
|
||
}
|
||
pObject->Free();
|
||
}
|
||
}
|
||
// TO DO: Error "ExtGState is unknown"
|
||
return false;
|
||
}
|
||
|
||
//-------------------------------------------------------------------------------------------------------------------------------
|
||
// Graphics
|
||
//-------------------------------------------------------------------------------------------------------------------------------
|
||
|
||
Graphics::Graphics(GlobalParams *pGlobalParams, XRef *pXref, OutputDev *pOut, int nPageNumber, Dict *pResorcesDict, double dHorDPI, double dVerDPI, PDFRectangle *pBox, PDFRectangle *pCropBox, int nRotate, bool(*pAbortCheckCallBack)(void *pData), void *pAbortCheckData)
|
||
{
|
||
#ifdef _DEBUG
|
||
std::wstring wsFileName = L"D:\\Output\\PDF Dump\\DumpPDF_Page" + std::to_wstring(nPageNumber) + L".bin";
|
||
NSFile::CFileBinary oFile;
|
||
if (!oFile.CreateFileW(wsFileName))
|
||
m_pDumpFile = NULL;
|
||
else
|
||
m_pDumpFile = oFile.GetFileNative();
|
||
#endif
|
||
|
||
m_pGlobalParams = pGlobalParams;
|
||
|
||
m_pXref = pXref;
|
||
m_bSubPage = false;
|
||
|
||
// Resource stack
|
||
m_pResources = new GrResources(m_pXref, pResorcesDict, NULL, m_pGlobalParams);
|
||
|
||
// Initialize
|
||
m_pOut = pOut;
|
||
m_pGState = new GrState(dHorDPI, dVerDPI, pBox, nRotate, m_pOut->UpSideDown());
|
||
m_bFontChanged = false;
|
||
m_eClip = clipNone;
|
||
m_nIgnoreUndef = 0;
|
||
m_pOut->StartPage(nPageNumber, m_pGState);
|
||
m_pOut->SetDefaultCTM(m_pGState->GetCTM());
|
||
m_pOut->UpdateAll(m_pGState);
|
||
|
||
for (int nIndex = 0; nIndex < 6; ++nIndex)
|
||
{
|
||
m_arrBaseMatrix[nIndex] = m_pGState->GetCTM()[nIndex];
|
||
}
|
||
m_nFormDepth = 0;
|
||
m_pAbortCheckCallBack = pAbortCheckCallBack;
|
||
m_pAbortCheckData = pAbortCheckData;
|
||
|
||
if (pCropBox)
|
||
{
|
||
m_pGState->MoveTo(pCropBox->m_dLeft, pCropBox->m_dBottom);
|
||
m_pGState->LineTo(pCropBox->m_dRight, pCropBox->m_dBottom);
|
||
m_pGState->LineTo(pCropBox->m_dRight, pCropBox->m_dTop);
|
||
m_pGState->LineTo(pCropBox->m_dLeft, pCropBox->m_dTop);
|
||
m_pGState->ClosePath();
|
||
m_pGState->Clip();
|
||
m_pGState->GetClip()->ClipToPath(m_pGState->GetPath()->Copy(), m_pGState->GetCTM(), false);
|
||
|
||
if (m_pOut->UseClipTo())
|
||
m_pOut->ClipToPath(m_pGState, m_pGState->GetPath(), m_pGState->GetCTM(), false);
|
||
else
|
||
m_pOut->Clip(m_pGState);
|
||
|
||
m_pGState->ClearPath();
|
||
}
|
||
}
|
||
|
||
Graphics::Graphics(GlobalParams *pGlobalParams, XRef *pXref, OutputDev *pOut, Dict *pResorcesDict, PDFRectangle *pBox, PDFRectangle *pCropBox, bool(*pAbortCheckCallBack)(void *pData), void *pAbortCheckData)
|
||
{
|
||
#ifdef _DEBUG
|
||
m_pDumpFile = NULL;
|
||
#endif
|
||
|
||
m_pGlobalParams = pGlobalParams;
|
||
|
||
m_pXref = pXref;
|
||
m_bSubPage = true;
|
||
|
||
// Resource stack
|
||
m_pResources = new GrResources(m_pXref, pResorcesDict, NULL, m_pGlobalParams);
|
||
|
||
// Initialize
|
||
m_pOut = pOut;
|
||
m_pGState = new GrState(72, 72, pBox, 0, false);
|
||
m_bFontChanged = false;
|
||
m_eClip = clipNone;
|
||
m_nIgnoreUndef = 0;
|
||
|
||
for (int nIndex = 0; nIndex < 6; ++nIndex)
|
||
{
|
||
m_arrBaseMatrix[nIndex] = m_pGState->GetCTM()[nIndex];
|
||
}
|
||
|
||
m_nFormDepth = 0;
|
||
m_pAbortCheckCallBack = pAbortCheckCallBack;
|
||
m_pAbortCheckData = pAbortCheckData;
|
||
|
||
if (pCropBox)
|
||
{
|
||
m_pGState->MoveTo(pCropBox->m_dLeft, pCropBox->m_dBottom);
|
||
m_pGState->LineTo(pCropBox->m_dRight, pCropBox->m_dBottom);
|
||
m_pGState->LineTo(pCropBox->m_dRight, pCropBox->m_dTop);
|
||
m_pGState->LineTo(pCropBox->m_dLeft, pCropBox->m_dTop);
|
||
m_pGState->ClosePath();
|
||
m_pGState->Clip();
|
||
m_pGState->GetClip()->ClipToPath(m_pGState->GetPath()->Copy(), m_pGState->GetCTM(), false);
|
||
|
||
if (m_pOut->UseClipTo())
|
||
m_pOut->ClipToPath(m_pGState, m_pGState->GetPath(), m_pGState->GetCTM(), false);
|
||
else
|
||
m_pOut->Clip(m_pGState);
|
||
|
||
m_pGState->ClearPath();
|
||
}
|
||
}
|
||
|
||
Graphics::~Graphics()
|
||
{
|
||
#ifdef _DEBUG
|
||
|
||
if (m_pDumpFile)
|
||
::fclose(m_pDumpFile);
|
||
|
||
#endif
|
||
while (m_pGState->HasSaves())
|
||
{
|
||
RestoreGState();
|
||
}
|
||
if (!m_bSubPage)
|
||
{
|
||
m_pOut->EndPage();
|
||
}
|
||
while (m_pResources)
|
||
{
|
||
PopResources();
|
||
}
|
||
if (m_pGState)
|
||
{
|
||
delete m_pGState;
|
||
}
|
||
}
|
||
|
||
void Graphics::Display(Object *pObject, bool bTopLevel)
|
||
{
|
||
if (pObject->IsArray())
|
||
{
|
||
for (int nIndex = 0; nIndex < pObject->ArrayGetLength(); ++nIndex)
|
||
{
|
||
Object oTemp;
|
||
pObject->ArrayGet(nIndex, &oTemp);
|
||
if (!oTemp.IsStream())
|
||
{
|
||
// TO DO: Error "Weird page contents"
|
||
oTemp.Free();
|
||
return;
|
||
}
|
||
oTemp.Free();
|
||
}
|
||
}
|
||
else if (!pObject->IsStream())
|
||
{
|
||
// TO DO: Error "Weird page contents"
|
||
return;
|
||
}
|
||
m_pParser = new Parser(m_pXref, new Lexer(m_pXref, pObject), false);
|
||
if (NULL == m_pParser)
|
||
return;
|
||
StartParse(bTopLevel);
|
||
delete m_pParser;
|
||
m_pParser = NULL;
|
||
}
|
||
|
||
void Graphics::StartParse(bool bTopLevel)
|
||
{
|
||
Object arrArguments[maxArgs];
|
||
int nArgumentsCount = 0;
|
||
int nLastAbortCheck = 0;
|
||
|
||
m_nUpdateLevel = 0;
|
||
|
||
Object oTemp;
|
||
m_pParser->GetObject(&oTemp);
|
||
while (!oTemp.IsEOF())
|
||
{
|
||
if (m_pOut && m_pOut->IsStopped())
|
||
break;
|
||
|
||
if (oTemp.IsCommand()) // Если получили команду, тогда выполняем ее
|
||
{
|
||
// Тут распечатываются посланные команды
|
||
|
||
#ifdef _DEBUG
|
||
if (m_pDumpFile)
|
||
{
|
||
oTemp.Print(m_pDumpFile);
|
||
for (int nIndex = 0; nIndex < nArgumentsCount; nIndex++)
|
||
{
|
||
::fprintf(m_pDumpFile, " ");
|
||
arrArguments[nIndex].Print(m_pDumpFile);
|
||
}
|
||
::fprintf(m_pDumpFile, "\n");
|
||
::fflush(m_pDumpFile);
|
||
}
|
||
#endif
|
||
|
||
ExecuteOperator(&oTemp, arrArguments, nArgumentsCount);
|
||
oTemp.Free();
|
||
|
||
for (int nIndex = 0; nIndex < nArgumentsCount; ++nIndex)
|
||
arrArguments[nIndex].Free();
|
||
nArgumentsCount = 0;
|
||
|
||
// Производим переодические обновления того что выводим
|
||
if (++m_nUpdateLevel >= 20000)
|
||
{
|
||
m_pOut->Dump();
|
||
m_nUpdateLevel = 0;
|
||
}
|
||
|
||
// Проверяем отмену
|
||
if (m_pAbortCheckCallBack)
|
||
{
|
||
if (m_nUpdateLevel - nLastAbortCheck > 10)
|
||
{
|
||
if ((*m_pAbortCheckCallBack)(m_pAbortCheckData))
|
||
{
|
||
break;
|
||
}
|
||
nLastAbortCheck = m_nUpdateLevel;
|
||
}
|
||
}
|
||
}
|
||
else if (nArgumentsCount < maxArgs) // Считываем аргумент
|
||
{
|
||
arrArguments[nArgumentsCount++] = oTemp;
|
||
}
|
||
else // Аргументов слишком много, значит что-то неправильно
|
||
{
|
||
// TO DO: Error "Too many args in content stream"
|
||
#ifdef _DEBUG
|
||
if (m_pDumpFile)
|
||
{
|
||
::fprintf(m_pDumpFile, "throwing away arg: ");
|
||
oTemp.Print(m_pDumpFile);
|
||
::fprintf(m_pDumpFile, "\n");
|
||
::fflush(m_pDumpFile);
|
||
}
|
||
#endif
|
||
oTemp.Free();
|
||
}
|
||
|
||
// Считываем следующий объект
|
||
m_pParser->GetObject(&oTemp);
|
||
}
|
||
oTemp.Free();
|
||
|
||
// args at end with no command
|
||
if (nArgumentsCount > 0)
|
||
{
|
||
// TO DO: Error "Leftover args in content stream"
|
||
|
||
#ifdef _DEBUG
|
||
if (m_pDumpFile)
|
||
{
|
||
::fprintf(m_pDumpFile, "%d leftovers:", nArgumentsCount);
|
||
for (int nIndex = 0; nIndex < nArgumentsCount; ++nIndex)
|
||
{
|
||
::fprintf(m_pDumpFile, " ");
|
||
arrArguments[nIndex].Print(m_pDumpFile);
|
||
}
|
||
::fprintf(m_pDumpFile, "\n");
|
||
::fflush(m_pDumpFile);
|
||
}
|
||
#endif
|
||
for (int nIndex = 0; nIndex < nArgumentsCount; ++nIndex)
|
||
arrArguments[nIndex].Free();
|
||
}
|
||
|
||
if (bTopLevel && m_nUpdateLevel > 0)
|
||
{
|
||
m_pOut->Dump();
|
||
}
|
||
}
|
||
|
||
void Graphics::ExecuteOperator(Object *pCommand, Object arrArguments[], int nArgumentsCount)
|
||
{
|
||
Operator *pOperator = NULL;
|
||
// Ищем оператор
|
||
char* sName = pCommand->GetCommand();
|
||
|
||
if (!(pOperator = FindOperator(sName)))
|
||
{
|
||
if (m_nIgnoreUndef == 0) // Проверяем наличие незакрытого оператора BX
|
||
{
|
||
// TO DO: Error "Unknown operator"
|
||
}
|
||
return;
|
||
}
|
||
|
||
// Проверяем количество аргументов
|
||
Object *pArguments = arrArguments;
|
||
if (pOperator->nArgumentsCount >= 0)
|
||
{
|
||
if (nArgumentsCount < pOperator->nArgumentsCount)
|
||
{
|
||
// TO DO: Error "Too few args in operator"
|
||
return;
|
||
}
|
||
if (nArgumentsCount > pOperator->nArgumentsCount)
|
||
{
|
||
// TO DO: Error "Too many args in operator"
|
||
|
||
// Используем последние значения в качесте аргументов
|
||
pArguments += nArgumentsCount - pOperator->nArgumentsCount;
|
||
nArgumentsCount = pOperator->nArgumentsCount;
|
||
}
|
||
}
|
||
else
|
||
{
|
||
if (nArgumentsCount > -pOperator->nArgumentsCount)
|
||
{
|
||
// TO DO: Error "Too many args in operator"
|
||
return;
|
||
}
|
||
}
|
||
|
||
// Проверяем типы аргументов
|
||
for (int nIndex = 0; nIndex < nArgumentsCount; ++nIndex)
|
||
{
|
||
if (!CheckArgumentType(&pArguments[nIndex], pOperator->arrArguments[nIndex]))
|
||
{
|
||
// TO DO: Error "Some argument is wrong type"
|
||
return;
|
||
}
|
||
}
|
||
|
||
// Выполняем сам оператор
|
||
(this->*pOperator->pFunction)(pArguments, nArgumentsCount);
|
||
}
|
||
Operator *Graphics::FindOperator(char *sName)
|
||
{
|
||
int nCompareRes = 0;
|
||
|
||
int nStart = -1;
|
||
int nEnd = OperatorsCount;
|
||
// OperatorsTable[nStart] < sName < OperatorsTable[nEnd]
|
||
|
||
while (nEnd - nStart > 1)
|
||
{
|
||
int nMiddle = (nStart + nEnd) / 2;
|
||
nCompareRes = strcmp(OperatorsTable[nMiddle].sName, sName);
|
||
if (nCompareRes < 0)
|
||
nStart = nMiddle;
|
||
else if (nCompareRes > 0)
|
||
nEnd = nMiddle;
|
||
else
|
||
nStart = nEnd = nMiddle;
|
||
}
|
||
if (nCompareRes != 0)
|
||
return NULL;
|
||
return &OperatorsTable[nStart];
|
||
}
|
||
|
||
bool Graphics::CheckArgumentType(Object *pArgument, ArgType eType)
|
||
{
|
||
switch (eType)
|
||
{
|
||
case argBool: return pArgument->IsBool();
|
||
case argInt: return pArgument->IsInt();
|
||
case argNum: return pArgument->IsNum();
|
||
case argString: return pArgument->IsString();
|
||
case argName: return pArgument->IsName();
|
||
case argArray: return pArgument->IsArray();
|
||
case argProps: return pArgument->IsDict() || pArgument->IsName();
|
||
case argSCN: return pArgument->IsNum() || pArgument->IsName();
|
||
case argNone: return false;
|
||
}
|
||
return false;
|
||
}
|
||
|
||
int Graphics::GetPos()
|
||
{
|
||
return m_pParser ? m_pParser->GetPos() : -1;
|
||
}
|
||
|
||
//-------------------------------------------------------------------------------------------------------------------------------
|
||
// Graphics state
|
||
//-------------------------------------------------------------------------------------------------------------------------------
|
||
|
||
// q
|
||
void Graphics::OperatorSave(Object arrArguments[], int nArgumentsCount)
|
||
{
|
||
SaveGState();
|
||
}
|
||
// Q
|
||
void Graphics::OperatorRestore(Object arrArguments[], int nArgumentsCount)
|
||
{
|
||
RestoreGState();
|
||
}
|
||
// cm
|
||
void Graphics::OperatorConcat(Object arrArguments[], int nArgumentsCount)
|
||
{
|
||
m_pGState->ConcatCTM(arrArguments[0].GetNum(), arrArguments[1].GetNum(), arrArguments[2].GetNum(), arrArguments[3].GetNum(), arrArguments[4].GetNum(), arrArguments[5].GetNum());
|
||
m_pOut->UpdateCTM(m_pGState, arrArguments[0].GetNum(), arrArguments[1].GetNum(), arrArguments[2].GetNum(), arrArguments[3].GetNum(), arrArguments[4].GetNum(), arrArguments[5].GetNum());
|
||
m_bFontChanged = true;
|
||
}
|
||
// d
|
||
void Graphics::OperatorSetDash(Object arrArguments[], int nArgumentsCount)
|
||
{
|
||
double *pDash;
|
||
Array *pArray = arrArguments[0].GetArray();
|
||
int nCount = pArray->GetCount();
|
||
if (0 == nCount)
|
||
{
|
||
pDash = NULL;
|
||
}
|
||
else
|
||
{
|
||
pDash = (double *)MemUtilsMallocArray(nCount, sizeof(double));
|
||
for (int nIndex = 0; nIndex < nCount; ++nIndex)
|
||
{
|
||
Object oTemp;
|
||
pDash[nIndex] = pArray->Get(nIndex, &oTemp)->GetNum();
|
||
oTemp.Free();
|
||
}
|
||
}
|
||
m_pGState->SetLineDash(pDash, nCount, arrArguments[1].GetNum());
|
||
m_pOut->UpdateLineDash(m_pGState);
|
||
}
|
||
// i
|
||
void Graphics::OperatorSetFlat(Object arrArguments[], int nArgumentsCount)
|
||
{
|
||
m_pGState->SetFlatness((int)arrArguments[0].GetNum());
|
||
m_pOut->UpdateFlatness(m_pGState);
|
||
}
|
||
// j
|
||
void Graphics::OperatorSetLineJoin(Object arrArguments[], int nArgumentsCount)
|
||
{
|
||
m_pGState->SetLineJoin(arrArguments[0].GetInt());
|
||
m_pOut->UpdateLineJoin(m_pGState);
|
||
}
|
||
// J
|
||
void Graphics::OperatorSetLineCap(Object arrArguments[], int nArgumentsCount)
|
||
{
|
||
m_pGState->SetLineCap(arrArguments[0].GetInt());
|
||
m_pOut->UpdateLineCap(m_pGState);
|
||
}
|
||
// M
|
||
void Graphics::OperatorSetMiterLimit(Object arrArguments[], int nArgumentsCount)
|
||
{
|
||
m_pGState->SetMiterLimit(arrArguments[0].GetNum());
|
||
m_pOut->UpdateMiterLimit(m_pGState);
|
||
}
|
||
// w
|
||
void Graphics::OperatorSetLineWidth(Object arrArguments[], int nArgumentsCount)
|
||
{
|
||
m_pGState->SetLineWidth(arrArguments[0].GetNum());
|
||
m_pOut->UpdateLineWidth(m_pGState);
|
||
}
|
||
// ri
|
||
void Graphics::OperatorSetRenderingIntent(Object arrArguments[], int nArgumentsCount)
|
||
{
|
||
}
|
||
// gs
|
||
void Graphics::OperatorSetExtGState(Object arrArguments[], int nArgumentsCount)
|
||
{
|
||
Object oDict;
|
||
if (!m_pResources->LookupExtGState(arrArguments[0].GetName(), &oDict))
|
||
{
|
||
return;
|
||
}
|
||
if (!oDict.IsDict())
|
||
{
|
||
// TO DO: Error "ExtGState is wrong type"
|
||
oDict.Free();
|
||
return;
|
||
}
|
||
|
||
// Прозрачность
|
||
Object oDictItem;
|
||
// BM (blend mode)
|
||
if (!oDict.DictLookup("BM", &oDictItem)->IsNull())
|
||
{
|
||
GraphicsBlendMode eMode;
|
||
if (m_pGState->ParseBlendMode(&oDictItem, &eMode))
|
||
{
|
||
m_pGState->SetBlendMode(eMode);
|
||
m_pOut->UpdateBlendMode(m_pGState);
|
||
}
|
||
else
|
||
{
|
||
// TO DO: Error "Invalid blend mode in ExtGState"
|
||
}
|
||
}
|
||
oDictItem.Free();
|
||
|
||
// ca (current alpha for filling)
|
||
if (oDict.DictLookup("ca", &oDictItem)->IsNum())
|
||
{
|
||
m_pGState->SetFillOpacity(oDictItem.GetNum());
|
||
m_pOut->UpdateFillOpacity(m_pGState);
|
||
}
|
||
oDictItem.Free();
|
||
|
||
// CA (current alpha for stroking)
|
||
if (oDict.DictLookup("CA", &oDictItem)->IsNum())
|
||
{
|
||
m_pGState->SetStrokeOpacity(oDictItem.GetNum());
|
||
m_pOut->UpdateStrokeOpacity(m_pGState);
|
||
}
|
||
oDictItem.Free();
|
||
|
||
// op (over print for filling)
|
||
bool bHaveFillOP;
|
||
if ((bHaveFillOP = (oDict.DictLookup("op", &oDictItem)->IsBool())))
|
||
{
|
||
m_pGState->SetFillOverprint(oDictItem.GetBool());
|
||
m_pOut->UpdateFillOverprint(m_pGState);
|
||
}
|
||
oDictItem.Free();
|
||
|
||
// OP (over print for stroking)
|
||
if (oDict.DictLookup("OP", &oDictItem)->IsBool())
|
||
{
|
||
m_pGState->SetStrokeOverprint(oDictItem.GetBool());
|
||
m_pOut->UpdateStrokeOverprint(m_pGState);
|
||
if (!bHaveFillOP)
|
||
{
|
||
m_pGState->SetFillOverprint(oDictItem.GetBool());
|
||
m_pOut->UpdateFillOverprint(m_pGState);
|
||
}
|
||
}
|
||
oDictItem.Free();
|
||
|
||
// SA (stroke adjustment)
|
||
if (oDict.DictLookup("SA", &oDictItem)->IsBool())
|
||
{
|
||
m_pGState->SetStrokeAdjust(oDictItem.GetBool());
|
||
m_pOut->UpdateStrokeAdjust(m_pGState);
|
||
}
|
||
oDictItem.Free();
|
||
|
||
// TR2/TR (transfer function)
|
||
// приоритетной является TR2
|
||
Function *ppFunctions[4];
|
||
if (oDict.DictLookup("TR2", &oDictItem)->IsNull())
|
||
{
|
||
oDictItem.Free();
|
||
oDict.DictLookup("TR", &oDictItem);
|
||
}
|
||
if (oDictItem.IsName("Default") || oDictItem.IsName("Identity"))
|
||
{
|
||
ppFunctions[0] = ppFunctions[1] = ppFunctions[2] = ppFunctions[3] = NULL;
|
||
m_pGState->SetTransfer(ppFunctions);
|
||
m_pOut->UpdateTransfer(m_pGState);
|
||
}
|
||
else if (oDictItem.IsArray() && oDictItem.ArrayGetLength() == 4)
|
||
{
|
||
int nIndex = 0;
|
||
for (nIndex = 0; nIndex < 4; ++nIndex)
|
||
{
|
||
Object oTemp;
|
||
oDictItem.ArrayGet(nIndex, &oTemp);
|
||
ppFunctions[nIndex] = Function::Parse(&oTemp);
|
||
oTemp.Free();
|
||
if (!ppFunctions[nIndex])
|
||
{
|
||
break;
|
||
}
|
||
}
|
||
if (4 == nIndex) // нормально ли все функции прочитались
|
||
{
|
||
m_pGState->SetTransfer(ppFunctions);
|
||
m_pOut->UpdateTransfer(m_pGState);
|
||
}
|
||
}
|
||
else if (oDictItem.IsName() || oDictItem.IsDict() || oDictItem.IsStream())
|
||
{
|
||
if ((ppFunctions[0] = Function::Parse(&oDictItem)))
|
||
{
|
||
ppFunctions[1] = ppFunctions[2] = ppFunctions[3] = NULL;
|
||
m_pGState->SetTransfer(ppFunctions);
|
||
m_pOut->UpdateTransfer(m_pGState);
|
||
}
|
||
}
|
||
else if (!oDictItem.IsNull())
|
||
{
|
||
// TO DO: Error "Invalid transfer function in ExtGState"
|
||
}
|
||
oDictItem.Free();
|
||
|
||
// SMask (soft mask)
|
||
if (!oDict.DictLookup("SMask", &oDictItem)->IsNull())
|
||
{
|
||
if (oDictItem.IsName("None"))
|
||
{
|
||
m_pOut->ClearSoftMask(m_pGState);
|
||
}
|
||
else if (oDictItem.IsDict())
|
||
{
|
||
Object oTemp;
|
||
bool bAlpha = false;
|
||
if (oDictItem.DictLookup("S", &oTemp)->IsName("Alpha"))
|
||
{
|
||
bAlpha = true;
|
||
}
|
||
else // "Luminosity"
|
||
{
|
||
bAlpha = false;
|
||
}
|
||
oTemp.Free();
|
||
|
||
ppFunctions[0] = NULL;
|
||
if (!oDictItem.DictLookup("TR", &oTemp)->IsNull())
|
||
{
|
||
ppFunctions[0] = Function::Parse(&oTemp);
|
||
if (ppFunctions[0]->GetInputSize() != 1 || ppFunctions[0]->GetOutputSize() != 1)
|
||
{
|
||
// TO DO: Error "Invalid transfer function in soft mask in ExtGState"
|
||
if (ppFunctions[0])
|
||
delete ppFunctions[0];
|
||
ppFunctions[0] = NULL;
|
||
}
|
||
}
|
||
oTemp.Free();
|
||
|
||
bool bHaveBackDropColor = false;
|
||
GrColor oBackDropColor;
|
||
if ((bHaveBackDropColor = oDictItem.DictLookup("BC", &oTemp)->IsArray()))
|
||
{
|
||
for (int nIndex = 0; nIndex < GrColorMaxComps; ++nIndex)
|
||
{
|
||
oBackDropColor.arrComp[nIndex] = 0;
|
||
}
|
||
for (int nIndex = 0; nIndex < oTemp.ArrayGetLength() && nIndex < GrColorMaxComps; ++nIndex)
|
||
{
|
||
Object oArrayItem;
|
||
oTemp.ArrayGet(nIndex, &oArrayItem);
|
||
if (oArrayItem.IsNum())
|
||
{
|
||
oBackDropColor.arrComp[nIndex] = DoubleToColor(oArrayItem.GetNum());
|
||
}
|
||
oArrayItem.Free();
|
||
}
|
||
}
|
||
oTemp.Free();
|
||
|
||
if (oDictItem.DictLookup("G", &oTemp)->IsStream())
|
||
{
|
||
Object oDict2;
|
||
if (oTemp.StreamGetDict()->Search("Group", &oDict2)->IsDict())
|
||
{
|
||
GrColorSpace *pBlendingColorSpace = NULL;
|
||
bool bIsolated = false, bKnockout = false;
|
||
Object oTemp2;
|
||
if (!oDict2.DictLookup("CS", &oTemp2)->IsNull())
|
||
{
|
||
pBlendingColorSpace = GrColorSpace::Parse(&oTemp2);
|
||
}
|
||
oTemp2.Free();
|
||
if (oDict2.DictLookup("I", &oTemp2)->IsBool())
|
||
{
|
||
bIsolated = oTemp2.GetBool();
|
||
}
|
||
oTemp2.Free();
|
||
if (oDict2.DictLookup("K", &oTemp2)->IsBool())
|
||
{
|
||
bKnockout = oTemp2.GetBool();
|
||
}
|
||
oTemp2.Free();
|
||
if (!bHaveBackDropColor)
|
||
{
|
||
if (pBlendingColorSpace)
|
||
{
|
||
pBlendingColorSpace->GetDefaultColor(&oBackDropColor);
|
||
}
|
||
else
|
||
{
|
||
for (int nIndex = 0; nIndex < GrColorMaxComps; ++nIndex)
|
||
{
|
||
oBackDropColor.arrComp[nIndex] = 0;
|
||
}
|
||
}
|
||
}
|
||
MakeSoftMask(&oTemp, bAlpha, pBlendingColorSpace, bIsolated, bKnockout, ppFunctions[0], &oBackDropColor);
|
||
if (ppFunctions[0])
|
||
{
|
||
delete ppFunctions[0];
|
||
}
|
||
}
|
||
else
|
||
{
|
||
// TO DO: Error "Invalid soft mask in ExtGState - missing group"
|
||
}
|
||
oDict2.Free();
|
||
}
|
||
else
|
||
{
|
||
// TO DO: Error "Invalid soft mask in ExtGState - missing group"
|
||
}
|
||
oTemp.Free();
|
||
}
|
||
else if (!oDictItem.IsNull())
|
||
{
|
||
// TO DO: Error "Invalid soft mask in ExtGState"
|
||
}
|
||
}
|
||
oDictItem.Free();
|
||
oDict.Free();
|
||
}
|
||
|
||
//-------------------------------------------------------------------------------------------------------------------------------
|
||
void Graphics::MakeSoftMask(Object *pStream, bool bAlpha, GrColorSpace *pBlendingColorSpace, bool bIsolated, bool bKnockout, Function *pTransferFunction, GrColor *pBackDropColor)
|
||
{
|
||
// Проверяем на чрезмерную рекурсию
|
||
if (m_nFormDepth > 20)
|
||
{
|
||
return;
|
||
}
|
||
|
||
Dict *pDict = pStream->StreamGetDict();
|
||
|
||
// Form type
|
||
Object oDictItem;
|
||
pDict->Search("FormType", &oDictItem);
|
||
if (!(oDictItem.IsNull() || (oDictItem.IsInt() && oDictItem.GetInt() == 1)))
|
||
{
|
||
// TO DO: Error "Unknown form type"
|
||
}
|
||
oDictItem.Free();
|
||
|
||
// BBox
|
||
pDict->Search("BBox", &oDictItem);
|
||
if (!oDictItem.IsArray())
|
||
{
|
||
oDictItem.Free();
|
||
// TO DO: Error "Bad form bounding box"
|
||
return;
|
||
}
|
||
double arrBBox[4];
|
||
for (int nIndex = 0; nIndex < 4; ++nIndex)
|
||
{
|
||
Object oTemp;
|
||
oDictItem.ArrayGet(nIndex, &oTemp);
|
||
arrBBox[nIndex] = oTemp.GetNum();
|
||
oTemp.Free();
|
||
}
|
||
oDictItem.Free();
|
||
|
||
// Matrix
|
||
double arrMatrix[6];
|
||
pDict->Search("Matrix", &oDictItem);
|
||
if (oDictItem.IsArray())
|
||
{
|
||
for (int nIndex = 0; nIndex < 6; ++nIndex)
|
||
{
|
||
Object oTemp;
|
||
oDictItem.ArrayGet(nIndex, &oTemp);
|
||
arrMatrix[nIndex] = oTemp.GetNum();
|
||
oTemp.Free();
|
||
}
|
||
}
|
||
else
|
||
{
|
||
arrMatrix[0] = 1; arrMatrix[1] = 0;
|
||
arrMatrix[2] = 0; arrMatrix[3] = 1;
|
||
arrMatrix[4] = 0; arrMatrix[5] = 0;
|
||
}
|
||
oDictItem.Free();
|
||
|
||
// Resources
|
||
pDict->Search("Resources", &oDictItem);
|
||
Dict *pResourcesDict = (oDictItem.IsDict() ? oDictItem.GetDict() : (Dict *)NULL);
|
||
|
||
// Рисуем
|
||
++m_nFormDepth;
|
||
DoForm(pStream, pResourcesDict, arrMatrix, arrBBox, true, true, pBlendingColorSpace, bIsolated, bKnockout, bAlpha, pTransferFunction, pBackDropColor);
|
||
--m_nFormDepth;
|
||
|
||
if (pBlendingColorSpace)
|
||
{
|
||
delete pBlendingColorSpace;
|
||
}
|
||
oDictItem.Free();
|
||
}
|
||
//-------------------------------------------------------------------------------------------------------------------------------
|
||
// Color and Color spaces
|
||
//-------------------------------------------------------------------------------------------------------------------------------
|
||
|
||
// g
|
||
void Graphics::OperatorSetFillGray(Object arrArguments[], int nArgumentsCount)
|
||
{
|
||
GrColor oColor;
|
||
|
||
m_pGState->SetFillPattern(NULL);
|
||
m_pGState->SetFillColorSpace(new GrDeviceGrayColorSpace());
|
||
m_pOut->UpdateFillColorSpace(m_pGState);
|
||
oColor.arrComp[0] = DoubleToColor(arrArguments[0].GetNum());
|
||
m_pGState->SetFillColor(&oColor);
|
||
m_pOut->UpdateFillColor(m_pGState);
|
||
}
|
||
|
||
// G
|
||
void Graphics::OperatorSetStrokeGray(Object arrArguments[], int nArgumentsCount)
|
||
{
|
||
GrColor oColor;
|
||
|
||
m_pGState->SetStrokePattern(NULL);
|
||
m_pGState->SetStrokeColorSpace(new GrDeviceGrayColorSpace());
|
||
m_pOut->UpdateStrokeColorSpace(m_pGState);
|
||
oColor.arrComp[0] = DoubleToColor(arrArguments[0].GetNum());
|
||
m_pGState->SetStrokeColor(&oColor);
|
||
m_pOut->UpdateStrokeColor(m_pGState);
|
||
}
|
||
|
||
// k
|
||
void Graphics::OperatorSetFillCMYKColor(Object arrArguments[], int nArgumentsCount)
|
||
{
|
||
m_pGState->SetFillPattern(NULL);
|
||
m_pGState->SetFillColorSpace(new GrDeviceCMYKColorSpace());
|
||
m_pOut->UpdateFillColorSpace(m_pGState);
|
||
|
||
GrColor oColor;
|
||
for (int nIndex = 0; nIndex < 4; ++nIndex)
|
||
{
|
||
oColor.arrComp[nIndex] = DoubleToColor(arrArguments[nIndex].GetNum());
|
||
}
|
||
m_pGState->SetFillColor(&oColor);
|
||
m_pOut->UpdateFillColor(m_pGState);
|
||
}
|
||
|
||
// K
|
||
void Graphics::OperatorSetStrokeCMYKColor(Object arrArguments[], int nArgumentsCount)
|
||
{
|
||
m_pGState->SetStrokePattern(NULL);
|
||
m_pGState->SetStrokeColorSpace(new GrDeviceCMYKColorSpace());
|
||
m_pOut->UpdateStrokeColorSpace(m_pGState);
|
||
|
||
GrColor oColor;
|
||
for (int nIndex = 0; nIndex < 4; ++nIndex)
|
||
{
|
||
oColor.arrComp[nIndex] = DoubleToColor(arrArguments[nIndex].GetNum());
|
||
}
|
||
m_pGState->SetStrokeColor(&oColor);
|
||
m_pOut->UpdateStrokeColor(m_pGState);
|
||
}
|
||
|
||
// rg
|
||
void Graphics::OperatorSetFillRGBColor(Object arrArguments[], int nArgumentsCount)
|
||
{
|
||
m_pGState->SetFillPattern(NULL);
|
||
m_pGState->SetFillColorSpace(new GrDeviceRGBColorSpace());
|
||
m_pOut->UpdateFillColorSpace(m_pGState);
|
||
|
||
GrColor oColor;
|
||
for (int nIndex = 0; nIndex < 3; ++nIndex)
|
||
{
|
||
oColor.arrComp[nIndex] = DoubleToColor(arrArguments[nIndex].GetNum());
|
||
}
|
||
m_pGState->SetFillColor(&oColor);
|
||
m_pOut->UpdateFillColor(m_pGState);
|
||
}
|
||
|
||
// RG
|
||
void Graphics::OperatorSetStrokeRGBColor(Object arrArguments[], int nArgumentsCount)
|
||
{
|
||
m_pGState->SetStrokePattern(NULL);
|
||
m_pGState->SetStrokeColorSpace(new GrDeviceRGBColorSpace());
|
||
m_pOut->UpdateStrokeColorSpace(m_pGState);
|
||
|
||
GrColor oColor;
|
||
for (int nIndex = 0; nIndex < 3; ++nIndex)
|
||
{
|
||
oColor.arrComp[nIndex] = DoubleToColor(arrArguments[nIndex].GetNum());
|
||
}
|
||
m_pGState->SetStrokeColor(&oColor);
|
||
m_pOut->UpdateStrokeColor(m_pGState);
|
||
}
|
||
|
||
// cs
|
||
void Graphics::OperatorSetFillColorSpace(Object arrArguments[], int nArgumentsCount)
|
||
{
|
||
m_pGState->SetFillPattern(NULL);
|
||
Object oTemp;
|
||
m_pResources->LookupColorSpace(arrArguments[0].GetName(), &oTemp);
|
||
GrColorSpace *pColorSpace = NULL;
|
||
if (oTemp.IsNull())
|
||
{
|
||
pColorSpace = GrColorSpace::Parse(&arrArguments[0]);
|
||
}
|
||
else
|
||
{
|
||
pColorSpace = GrColorSpace::Parse(&oTemp);
|
||
}
|
||
oTemp.Free();
|
||
|
||
if (pColorSpace)
|
||
{
|
||
m_pGState->SetFillColorSpace(pColorSpace);
|
||
m_pOut->UpdateFillColorSpace(m_pGState);
|
||
GrColor oColor;
|
||
pColorSpace->GetDefaultColor(&oColor);
|
||
m_pGState->SetFillColor(&oColor);
|
||
m_pOut->UpdateFillColor(m_pGState);
|
||
}
|
||
else
|
||
{
|
||
// TO DO: Error "Bad color space (fill)"
|
||
}
|
||
}
|
||
|
||
// CS
|
||
void Graphics::OperatorSetStrokeColorSpace(Object arrArguments[], int nArgumentsCount)
|
||
{
|
||
m_pGState->SetStrokePattern(NULL);
|
||
Object oTemp;
|
||
m_pResources->LookupColorSpace(arrArguments[0].GetName(), &oTemp);
|
||
GrColorSpace *pColorSpace = NULL;
|
||
if (oTemp.IsNull())
|
||
{
|
||
pColorSpace = GrColorSpace::Parse(&arrArguments[0]);
|
||
}
|
||
else
|
||
{
|
||
pColorSpace = GrColorSpace::Parse(&oTemp);
|
||
}
|
||
oTemp.Free();
|
||
|
||
if (pColorSpace)
|
||
{
|
||
m_pGState->SetStrokeColorSpace(pColorSpace);
|
||
m_pOut->UpdateStrokeColorSpace(m_pGState);
|
||
GrColor oColor;
|
||
pColorSpace->GetDefaultColor(&oColor);
|
||
m_pGState->SetStrokeColor(&oColor);
|
||
m_pOut->UpdateStrokeColor(m_pGState);
|
||
}
|
||
else
|
||
{
|
||
// TO DO: Error "Bad color space (stroke)"
|
||
}
|
||
}
|
||
|
||
// sc
|
||
void Graphics::OperatorSetFillColor(Object arrArguments[], int nArgumentsCount)
|
||
{
|
||
if (nArgumentsCount != m_pGState->GetFillColorSpace()->GetComponentsCount())
|
||
{
|
||
// TO DO: Error "Incorrect number of arguments in 'sc' command"
|
||
return;
|
||
}
|
||
m_pGState->SetFillPattern(NULL);
|
||
|
||
GrColor oColor;
|
||
for (int nIndex = 0; nIndex < nArgumentsCount; ++nIndex)
|
||
{
|
||
oColor.arrComp[nIndex] = DoubleToColor(arrArguments[nIndex].GetNum());
|
||
}
|
||
m_pGState->SetFillColor(&oColor);
|
||
m_pOut->UpdateFillColor(m_pGState);
|
||
}
|
||
|
||
// SC
|
||
void Graphics::OperatorSetStrokeColor(Object arrArguments[], int nArgumentsCount)
|
||
{
|
||
if (nArgumentsCount != m_pGState->GetStrokeColorSpace()->GetComponentsCount())
|
||
{
|
||
// TO DO: Error "Incorrect number of arguments in 'SC' command"
|
||
return;
|
||
}
|
||
m_pGState->SetStrokePattern(NULL);
|
||
|
||
GrColor oColor;
|
||
for (int nIndex = 0; nIndex < nArgumentsCount; ++nIndex)
|
||
{
|
||
oColor.arrComp[nIndex] = DoubleToColor(arrArguments[nIndex].GetNum());
|
||
}
|
||
m_pGState->SetStrokeColor(&oColor);
|
||
m_pOut->UpdateStrokeColor(m_pGState);
|
||
}
|
||
|
||
// scn
|
||
void Graphics::OperatorSetFillColorN(Object arrArguments[], int nArgumentsCount)
|
||
{
|
||
GrPattern *pPattern = NULL;
|
||
GrColor oColor;
|
||
if (m_pGState->GetFillColorSpace()->GetMode() == csPattern)
|
||
{
|
||
if (nArgumentsCount > 1)
|
||
{
|
||
if (!((GrPatternColorSpace *)m_pGState->GetFillColorSpace())->GetUnder() || nArgumentsCount - 1 != ((GrPatternColorSpace *)m_pGState->GetFillColorSpace())->GetUnder()->GetComponentsCount())
|
||
{
|
||
// TO DO: Error "Incorrect number of arguments in 'scn' command"
|
||
return;
|
||
}
|
||
for (int nIndex = 0; nIndex < nArgumentsCount - 1 && nIndex < GrColorMaxComps; ++nIndex)
|
||
{
|
||
if (arrArguments[nIndex].IsNum())
|
||
{
|
||
oColor.arrComp[nIndex] = DoubleToColor(arrArguments[nIndex].GetNum());
|
||
}
|
||
}
|
||
m_pGState->SetFillColor(&oColor);
|
||
m_pOut->UpdateFillColor(m_pGState);
|
||
}
|
||
if (arrArguments[nArgumentsCount - 1].IsName() && (pPattern = m_pResources->LookupPattern(arrArguments[nArgumentsCount - 1].GetName())))
|
||
{
|
||
m_pGState->SetFillPattern(pPattern);
|
||
}
|
||
}
|
||
else
|
||
{
|
||
if (nArgumentsCount != m_pGState->GetFillColorSpace()->GetComponentsCount())
|
||
{
|
||
// TO DO: Error "Incorrect number of arguments in 'scn' command"
|
||
return;
|
||
}
|
||
m_pGState->SetFillPattern(NULL);
|
||
for (int nIndex = 0; nIndex < nArgumentsCount && nIndex < GrColorMaxComps; ++nIndex)
|
||
{
|
||
if (arrArguments[nIndex].IsNum())
|
||
{
|
||
oColor.arrComp[nIndex] = DoubleToColor(arrArguments[nIndex].GetNum());
|
||
}
|
||
}
|
||
m_pGState->SetFillColor(&oColor);
|
||
m_pOut->UpdateFillColor(m_pGState);
|
||
}
|
||
}
|
||
|
||
// SCN
|
||
void Graphics::OperatorSetStrokeColorN(Object arrArguments[], int nArgumentsCount)
|
||
{
|
||
GrPattern *pPattern = NULL;
|
||
GrColor oColor;
|
||
if (m_pGState->GetStrokeColorSpace()->GetMode() == csPattern)
|
||
{
|
||
if (nArgumentsCount > 1)
|
||
{
|
||
if (!((GrPatternColorSpace *)m_pGState->GetStrokeColorSpace())->GetUnder() || nArgumentsCount - 1 != ((GrPatternColorSpace *)m_pGState->GetStrokeColorSpace())->GetUnder()->GetComponentsCount())
|
||
{
|
||
// TO DO: Error "Incorrect number of arguments in 'SCN' command"
|
||
return;
|
||
}
|
||
for (int nIndex = 0; nIndex < nArgumentsCount - 1 && nIndex < GrColorMaxComps; ++nIndex)
|
||
{
|
||
if (arrArguments[nIndex].IsNum())
|
||
{
|
||
oColor.arrComp[nIndex] = DoubleToColor(arrArguments[nIndex].GetNum());
|
||
}
|
||
}
|
||
m_pGState->SetStrokeColor(&oColor);
|
||
m_pOut->UpdateStrokeColor(m_pGState);
|
||
}
|
||
if (arrArguments[nArgumentsCount - 1].IsName() && (pPattern = m_pResources->LookupPattern(arrArguments[nArgumentsCount - 1].GetName())))
|
||
{
|
||
m_pGState->SetStrokePattern(pPattern);
|
||
}
|
||
}
|
||
else
|
||
{
|
||
if (nArgumentsCount != m_pGState->GetStrokeColorSpace()->GetComponentsCount())
|
||
{
|
||
// TO DO: Error "Incorrect number of arguments in 'SCN' command"
|
||
return;
|
||
}
|
||
m_pGState->SetStrokePattern(NULL);
|
||
for (int nIndex = 0; nIndex < nArgumentsCount && nIndex < GrColorMaxComps; ++nIndex)
|
||
{
|
||
if (arrArguments[nIndex].IsNum())
|
||
{
|
||
oColor.arrComp[nIndex] = DoubleToColor(arrArguments[nIndex].GetNum());
|
||
}
|
||
}
|
||
m_pGState->SetStrokeColor(&oColor);
|
||
m_pOut->UpdateStrokeColor(m_pGState);
|
||
}
|
||
}
|
||
|
||
//-------------------------------------------------------------------------------------------------------------------------------
|
||
// Path construction
|
||
//-------------------------------------------------------------------------------------------------------------------------------
|
||
|
||
// m
|
||
void Graphics::OperatorMoveTo(Object arrArguments[], int nArgumentsCount)
|
||
{
|
||
m_pGState->MoveTo(arrArguments[0].GetNum(), arrArguments[1].GetNum());
|
||
}
|
||
|
||
// l
|
||
void Graphics::OperatorLineTo(Object arrArguments[], int nArgumentsCount)
|
||
{
|
||
if (!m_pGState->IsCurPoint())
|
||
{
|
||
// TO DO: Error "No current point in lineto"
|
||
return;
|
||
}
|
||
m_pGState->LineTo(arrArguments[0].GetNum(), arrArguments[1].GetNum());
|
||
}
|
||
|
||
// c
|
||
void Graphics::OperatorCurveTo(Object arrArguments[], int nArgumentsCount)
|
||
{
|
||
if (!m_pGState->IsCurPoint())
|
||
{
|
||
// TO DO: Error "No current point in curveto"
|
||
return;
|
||
}
|
||
m_pGState->CurveTo((double)arrArguments[0].GetNum(), (double)arrArguments[1].GetNum(), (double)arrArguments[2].GetNum(), (double)arrArguments[3].GetNum(), (double)arrArguments[4].GetNum(), (double)arrArguments[5].GetNum());
|
||
}
|
||
|
||
// v
|
||
void Graphics::OperatorCurveTo1(Object arrArguments[], int nArgumentsCount)
|
||
{
|
||
if (!m_pGState->IsCurPoint())
|
||
{
|
||
// TO DO: Error "No current point in curveto1"
|
||
return;
|
||
}
|
||
m_pGState->CurveTo(m_pGState->GetCurX(), m_pGState->GetCurY(), (double)arrArguments[0].GetNum(), (double)arrArguments[1].GetNum(), (double)arrArguments[2].GetNum(), (double)arrArguments[3].GetNum());
|
||
}
|
||
|
||
// y
|
||
void Graphics::OperatorCurveTo2(Object arrArguments[], int nArgumentsCount)
|
||
{
|
||
if (!m_pGState->IsCurPoint())
|
||
{
|
||
// TO DO: Error "No current point in curveto2"
|
||
return;
|
||
}
|
||
m_pGState->CurveTo((double)arrArguments[0].GetNum(), (double)arrArguments[1].GetNum(), (double)arrArguments[2].GetNum(), (double)arrArguments[3].GetNum(), (double)arrArguments[2].GetNum(), (double)arrArguments[3].GetNum());
|
||
}
|
||
|
||
// re
|
||
void Graphics::OperatorRectangle(Object arrArguments[], int nArgumentsCount)
|
||
{
|
||
double dX = arrArguments[0].GetNum();
|
||
double dY = arrArguments[1].GetNum();
|
||
double dWidth = arrArguments[2].GetNum();
|
||
double dHeight = arrArguments[3].GetNum();
|
||
m_pGState->MoveTo(dX, dY);
|
||
m_pGState->LineTo(dX + dWidth, dY);
|
||
m_pGState->LineTo(dX + dWidth, dY + dHeight);
|
||
m_pGState->LineTo(dX, dY + dHeight);
|
||
m_pGState->ClosePath();
|
||
}
|
||
|
||
// h
|
||
void Graphics::OperatorClosePath(Object arrArguments[], int nArgumentsCount)
|
||
{
|
||
if (!m_pGState->IsCurPoint())
|
||
{
|
||
// TO DO: Error "No current point in closepath"
|
||
return;
|
||
}
|
||
m_pGState->ClosePath();
|
||
}
|
||
|
||
//-------------------------------------------------------------------------------------------------------------------------------
|
||
// path painting operators
|
||
//-------------------------------------------------------------------------------------------------------------------------------
|
||
|
||
// n
|
||
void Graphics::OperatorEndPath(Object arrArguments[], int nArgumentsCount)
|
||
{
|
||
DoEndPath();
|
||
}
|
||
|
||
// S
|
||
void Graphics::OperatorStroke(Object arrArguments[], int nArgumentsCount)
|
||
{
|
||
if (!m_pGState->IsCurPoint())
|
||
{
|
||
// TO DO: Error "No path in stroke"
|
||
return;
|
||
}
|
||
if (m_pGState->IsPathNonEmpty())
|
||
{
|
||
if (m_pGState->GetStrokeColorSpace()->GetMode() == csPattern)
|
||
{
|
||
DoPatternStroke();
|
||
}
|
||
else
|
||
{
|
||
m_pOut->Stroke(m_pGState);
|
||
}
|
||
}
|
||
DoEndPath();
|
||
}
|
||
|
||
// s
|
||
void Graphics::OperatorCloseStroke(Object arrArguments[], int nArgumentsCount)
|
||
{
|
||
if (!m_pGState->IsCurPoint())
|
||
{
|
||
// TO DO: Error "No path in closepath/stroke"
|
||
return;
|
||
}
|
||
if (m_pGState->IsPathNonEmpty())
|
||
{
|
||
m_pGState->ClosePath();
|
||
if (m_pGState->GetStrokeColorSpace()->GetMode() == csPattern)
|
||
{
|
||
DoPatternStroke();
|
||
}
|
||
else
|
||
{
|
||
m_pOut->Stroke(m_pGState);
|
||
}
|
||
}
|
||
DoEndPath();
|
||
}
|
||
|
||
// f/F
|
||
void Graphics::OperatorFill(Object arrArguments[], int nArgumentsCount)
|
||
{
|
||
if (!m_pGState->IsCurPoint())
|
||
{
|
||
// TO DO: Error "No path in fill"
|
||
return;
|
||
}
|
||
if (m_pGState->IsPathNonEmpty())
|
||
{
|
||
if (m_pGState->GetFillColorSpace()->GetMode() == csPattern)
|
||
{
|
||
DoPatternFill(false);
|
||
}
|
||
else
|
||
{
|
||
m_pOut->Fill(m_pGState);
|
||
}
|
||
}
|
||
DoEndPath();
|
||
}
|
||
|
||
// f*
|
||
void Graphics::OperatorEOFill(Object arrArguments[], int nArgumentsCount)
|
||
{
|
||
if (!m_pGState->IsCurPoint())
|
||
{
|
||
// TO DO: Error "No path in eofill"
|
||
return;
|
||
}
|
||
if (m_pGState->IsPathNonEmpty())
|
||
{
|
||
if (m_pGState->GetFillColorSpace()->GetMode() == csPattern)
|
||
{
|
||
DoPatternFill(true);
|
||
}
|
||
else
|
||
{
|
||
m_pOut->EoFill(m_pGState);
|
||
}
|
||
}
|
||
DoEndPath();
|
||
}
|
||
|
||
// B
|
||
void Graphics::OperatorFillStroke(Object arrArguments[], int nArgumentsCount)
|
||
{
|
||
if (!m_pGState->IsCurPoint())
|
||
{
|
||
// TO DO: Error "No path in fill/stroke"
|
||
return;
|
||
}
|
||
if (m_pGState->IsPathNonEmpty())
|
||
{
|
||
if (!(m_pGState->GetFillColorSpace()->GetMode() == csPattern) && !(m_pGState->GetStrokeColorSpace()->GetMode() == csPattern) && m_pOut->UseFillAndStroke())
|
||
{
|
||
m_pOut->FillStroke(m_pGState);
|
||
}
|
||
else
|
||
{
|
||
if (m_pGState->GetFillColorSpace()->GetMode() == csPattern)
|
||
{
|
||
DoPatternFill(false);
|
||
}
|
||
else
|
||
{
|
||
m_pOut->Fill(m_pGState);
|
||
}
|
||
if (m_pGState->GetStrokeColorSpace()->GetMode() == csPattern)
|
||
{
|
||
DoPatternStroke();
|
||
}
|
||
else
|
||
{
|
||
m_pOut->Stroke(m_pGState);
|
||
}
|
||
}
|
||
}
|
||
DoEndPath();
|
||
}
|
||
|
||
// b
|
||
void Graphics::OperatorCloseFillStroke(Object arrArguments[], int nArgumentsCount)
|
||
{
|
||
if (!m_pGState->IsCurPoint())
|
||
{
|
||
// TO DO: Error "No path in closepath/fill/stroke"
|
||
return;
|
||
}
|
||
if (m_pGState->IsPathNonEmpty())
|
||
{
|
||
m_pGState->ClosePath();
|
||
|
||
if (!(m_pGState->GetFillColorSpace()->GetMode() == csPattern) && !(m_pGState->GetStrokeColorSpace()->GetMode() == csPattern) && m_pOut->UseFillAndStroke())
|
||
{
|
||
m_pOut->FillStroke(m_pGState);
|
||
}
|
||
else
|
||
{
|
||
if (m_pGState->GetFillColorSpace()->GetMode() == csPattern)
|
||
{
|
||
DoPatternFill(false);
|
||
}
|
||
else
|
||
{
|
||
m_pOut->Fill(m_pGState);
|
||
}
|
||
if (m_pGState->GetStrokeColorSpace()->GetMode() == csPattern)
|
||
{
|
||
DoPatternStroke();
|
||
}
|
||
else
|
||
{
|
||
m_pOut->Stroke(m_pGState);
|
||
}
|
||
}
|
||
}
|
||
DoEndPath();
|
||
}
|
||
|
||
// B*
|
||
void Graphics::OperatorEOFillStroke(Object arrArguments[], int nArgumentsCount)
|
||
{
|
||
if (!m_pGState->IsCurPoint())
|
||
{
|
||
// TO DO: Error "No path in eofill/stroke"
|
||
return;
|
||
}
|
||
if (m_pGState->IsPathNonEmpty())
|
||
{
|
||
if (!(m_pGState->GetFillColorSpace()->GetMode() == csPattern) && !(m_pGState->GetStrokeColorSpace()->GetMode() == csPattern) && m_pOut->UseFillAndStroke())
|
||
{
|
||
m_pOut->EoFillStroke(m_pGState);
|
||
}
|
||
else
|
||
{
|
||
if (m_pGState->GetFillColorSpace()->GetMode() == csPattern)
|
||
{
|
||
DoPatternFill(true);
|
||
}
|
||
else
|
||
{
|
||
m_pOut->EoFill(m_pGState);
|
||
}
|
||
if (m_pGState->GetStrokeColorSpace()->GetMode() == csPattern)
|
||
{
|
||
DoPatternStroke();
|
||
}
|
||
else
|
||
{
|
||
m_pOut->Stroke(m_pGState);
|
||
}
|
||
}
|
||
}
|
||
DoEndPath();
|
||
}
|
||
|
||
// b*
|
||
void Graphics::OperatorCloseEOFillStroke(Object arrArguments[], int nArgumentsCount)
|
||
{
|
||
if (!m_pGState->IsCurPoint())
|
||
{
|
||
// TO DO: Error "No path in closepath/eofill/stroke"
|
||
return;
|
||
}
|
||
if (m_pGState->IsPathNonEmpty())
|
||
{
|
||
m_pGState->ClosePath();
|
||
if (!(m_pGState->GetFillColorSpace()->GetMode() == csPattern) && !(m_pGState->GetStrokeColorSpace()->GetMode() == csPattern) && m_pOut->UseFillAndStroke())
|
||
{
|
||
m_pOut->EoFillStroke(m_pGState);
|
||
}
|
||
else
|
||
{
|
||
if (m_pGState->GetFillColorSpace()->GetMode() == csPattern)
|
||
{
|
||
DoPatternFill(true);
|
||
}
|
||
else
|
||
{
|
||
m_pOut->EoFill(m_pGState);
|
||
}
|
||
if (m_pGState->GetStrokeColorSpace()->GetMode() == csPattern)
|
||
{
|
||
DoPatternStroke();
|
||
}
|
||
else
|
||
{
|
||
m_pOut->Stroke(m_pGState);
|
||
}
|
||
}
|
||
}
|
||
DoEndPath();
|
||
}
|
||
|
||
// sh
|
||
void Graphics::OperatorShadingFill(Object arrArguments[], int nArgumentsCount)
|
||
{
|
||
GrShading *pShading = NULL;
|
||
|
||
if (!(pShading = m_pResources->LookupShading(arrArguments[0].GetName())))
|
||
{
|
||
return;
|
||
}
|
||
|
||
// Сохраняем текущий GState
|
||
GrPath *pSavedPath = m_pGState->GetPath()->Copy();
|
||
SaveGState();
|
||
|
||
if (pShading->GetHasBBox())
|
||
{
|
||
double dXMin, dYMin, dXMax, dYMax;
|
||
pShading->GetBBox(&dXMin, &dYMin, &dXMax, &dYMax);
|
||
m_pGState->MoveTo(dXMin, dYMin);
|
||
m_pGState->LineTo(dXMax, dYMin);
|
||
m_pGState->LineTo(dXMax, dYMax);
|
||
m_pGState->LineTo(dXMin, dYMax);
|
||
m_pGState->ClosePath();
|
||
m_pGState->Clip();
|
||
m_pGState->GetClip()->ClipToPath(m_pGState->GetPath()->Copy(), m_pGState->GetCTM(), false);
|
||
|
||
if (m_pOut->UseClipTo())
|
||
m_pOut->ClipToPath(m_pGState, m_pGState->GetPath(), m_pGState->GetCTM(), false);
|
||
else
|
||
m_pOut->Clip(m_pGState);
|
||
|
||
m_pGState->ClearPath();
|
||
}
|
||
|
||
// Устанавливаем цветовое пространство
|
||
m_pGState->SetFillColorSpace(pShading->GetColorSpace()->Copy());
|
||
m_pOut->UpdateFillColorSpace(m_pGState);
|
||
|
||
bool bAntialias = m_pOut->GetVectorAntialias();
|
||
if (bAntialias)
|
||
{
|
||
m_pOut->SetVectorAntialias(false);
|
||
}
|
||
|
||
m_pOut->StartShadedFill(m_pGState);
|
||
|
||
// Выполняем закраску, в зависимости от типа Shading
|
||
switch (pShading->GetType())
|
||
{
|
||
case 1:
|
||
DoFunctionShadingFill((GrFunctionShading *)pShading);
|
||
break;
|
||
case 2:
|
||
DoAxialShadingFill((GrAxialShading *)pShading);
|
||
break;
|
||
case 3:
|
||
DoRadialShadingFill((GrRadialShading *)pShading);
|
||
break;
|
||
case 4:
|
||
case 5:
|
||
DoGouraudTriangleShadingFill((GrGouraudTriangleShading *)pShading);
|
||
break;
|
||
case 6:
|
||
case 7:
|
||
DoPatchMeshShadingFill((GrPatchMeshShading *)pShading);
|
||
break;
|
||
}
|
||
|
||
m_pOut->EndShadedFill();
|
||
|
||
if (bAntialias)
|
||
{
|
||
m_pOut->SetVectorAntialias(true);
|
||
}
|
||
|
||
// Восстанавливаем GState
|
||
RestoreGState();
|
||
m_pGState->SetPath(pSavedPath);
|
||
|
||
delete pShading;
|
||
}
|
||
|
||
//-------------------------------------------------------------------------------------------------------------------------------
|
||
void Graphics::DoPatternFill(bool bEOFill)
|
||
{
|
||
// Пропускаем объект Patterns, если мы выводим только текст из PDF
|
||
if (!m_pOut->NeedNonText())
|
||
{
|
||
return;
|
||
}
|
||
|
||
GrPattern *pPattern = NULL;
|
||
if (!(pPattern = m_pGState->GetFillPattern()))
|
||
{
|
||
return;
|
||
}
|
||
switch (pPattern->GetType())
|
||
{
|
||
case 1:
|
||
DoTilingPatternFill((GrTilingPattern *)pPattern, false, bEOFill);
|
||
break;
|
||
case 2:
|
||
DoShadingPatternFill((GrShadingPattern *)pPattern, false, bEOFill);
|
||
break;
|
||
default:
|
||
// TO DO: Error "Unimplemented pattern type in fill"
|
||
break;
|
||
}
|
||
}
|
||
|
||
void Graphics::DoPatternStroke()
|
||
{
|
||
// Пропускаем объект Patterns, если мы выводим только текст из PDF
|
||
if (!m_pOut->NeedNonText())
|
||
{
|
||
return;
|
||
}
|
||
|
||
GrPattern *pPattern = NULL;
|
||
if (!(pPattern = m_pGState->GetStrokePattern()))
|
||
{
|
||
return;
|
||
}
|
||
switch (pPattern->GetType())
|
||
{
|
||
case 1:
|
||
DoTilingPatternFill((GrTilingPattern *)pPattern, true, false);
|
||
break;
|
||
case 2:
|
||
DoShadingPatternFill((GrShadingPattern *)pPattern, true, false);
|
||
break;
|
||
default:
|
||
// TO DO: Error "Unimplemented pattern type in stroke"
|
||
break;
|
||
}
|
||
}
|
||
|
||
void Graphics::DoTilingPatternFill(GrTilingPattern *pPattern, bool bStroke, bool bEOFill)
|
||
{
|
||
// Color space
|
||
GrPatternColorSpace *pPatternCS = (GrPatternColorSpace *)(bStroke ? m_pGState->GetStrokeColorSpace() : m_pGState->GetFillColorSpace());
|
||
|
||
// Строим матрицу преобразования (Pattern space) -> (Current space)
|
||
double *pCTM = m_pGState->GetCTM();
|
||
double *pBaseMatrix = m_arrBaseMatrix;
|
||
double *pTilingMatrix = pPattern->GetMatrix();
|
||
double dDet = 1 / (pCTM[0] * pCTM[3] - pCTM[1] * pCTM[2]);
|
||
// InvCTM = (CTM)^(-1)
|
||
double pInvCTM[6];
|
||
pInvCTM[0] = pCTM[3] * dDet;
|
||
pInvCTM[1] = -pCTM[1] * dDet;
|
||
pInvCTM[2] = -pCTM[2] * dDet;
|
||
pInvCTM[3] = pCTM[0] * dDet;
|
||
pInvCTM[4] = (pCTM[2] * pCTM[5] - pCTM[3] * pCTM[4]) * dDet;
|
||
pInvCTM[5] = (pCTM[1] * pCTM[4] - pCTM[0] * pCTM[5]) * dDet;
|
||
// TempMatrix = TilingMatrix * BaseMatrix
|
||
double pTempMatrix[6];
|
||
pTempMatrix[0] = pTilingMatrix[0] * pBaseMatrix[0] + pTilingMatrix[1] * pBaseMatrix[2];
|
||
pTempMatrix[1] = pTilingMatrix[0] * pBaseMatrix[1] + pTilingMatrix[1] * pBaseMatrix[3];
|
||
pTempMatrix[2] = pTilingMatrix[2] * pBaseMatrix[0] + pTilingMatrix[3] * pBaseMatrix[2];
|
||
pTempMatrix[3] = pTilingMatrix[2] * pBaseMatrix[1] + pTilingMatrix[3] * pBaseMatrix[3];
|
||
pTempMatrix[4] = pTilingMatrix[4] * pBaseMatrix[0] + pTilingMatrix[5] * pBaseMatrix[2] + pBaseMatrix[4];
|
||
pTempMatrix[5] = pTilingMatrix[4] * pBaseMatrix[1] + pTilingMatrix[5] * pBaseMatrix[3] + pBaseMatrix[5];
|
||
// Matrix = TempMatrix * InvCTM
|
||
double pMatrix[6];
|
||
pMatrix[0] = pTempMatrix[0] * pInvCTM[0] + pTempMatrix[1] * pInvCTM[2];
|
||
pMatrix[1] = pTempMatrix[0] * pInvCTM[1] + pTempMatrix[1] * pInvCTM[3];
|
||
pMatrix[2] = pTempMatrix[2] * pInvCTM[0] + pTempMatrix[3] * pInvCTM[2];
|
||
pMatrix[3] = pTempMatrix[2] * pInvCTM[1] + pTempMatrix[3] * pInvCTM[3];
|
||
pMatrix[4] = pTempMatrix[4] * pInvCTM[0] + pTempMatrix[5] * pInvCTM[2] + pInvCTM[4];
|
||
pMatrix[5] = pTempMatrix[4] * pInvCTM[1] + pTempMatrix[5] * pInvCTM[3] + pInvCTM[5];
|
||
|
||
// Строим матрицу преобразования (Device space) -> (Pattern space)
|
||
double pInvTemp[6];
|
||
dDet = 1 / (pTempMatrix[0] * pTempMatrix[3] - pTempMatrix[1] * pTempMatrix[2]);
|
||
pInvTemp[0] = pTempMatrix[3] * dDet;
|
||
pInvTemp[1] = -pTempMatrix[1] * dDet;
|
||
pInvTemp[2] = -pTempMatrix[2] * dDet;
|
||
pInvTemp[3] = pTempMatrix[0] * dDet;
|
||
pInvTemp[4] = (pTempMatrix[2] * pTempMatrix[5] - pTempMatrix[3] * pTempMatrix[4]) * dDet;
|
||
pInvTemp[5] = (pTempMatrix[1] * pTempMatrix[4] - pTempMatrix[0] * pTempMatrix[5]) * dDet;
|
||
|
||
// Сохраняем текущий GState
|
||
GrPath *pSavedPath = m_pGState->GetPath()->Copy();
|
||
SaveGState();
|
||
|
||
// Устанавливаем цветовое пространство для подкладки (для не цветовых Tiling patterns);
|
||
// Устанавливаем разлиные параметры (цвет обводки, толщина линии)
|
||
GrColorSpace *pColorSpace = NULL;
|
||
if (pPattern->GetPaintType() == 2 && (pColorSpace = pPatternCS->GetUnder()))
|
||
{
|
||
m_pGState->SetFillColorSpace(pColorSpace->Copy());
|
||
m_pOut->UpdateFillColorSpace(m_pGState);
|
||
m_pGState->SetStrokeColorSpace(pColorSpace->Copy());
|
||
m_pOut->UpdateStrokeColorSpace(m_pGState);
|
||
m_pGState->SetStrokeColor(m_pGState->GetFillColor());
|
||
}
|
||
else
|
||
{
|
||
m_pGState->SetFillColorSpace(new GrDeviceGrayColorSpace());
|
||
m_pOut->UpdateFillColorSpace(m_pGState);
|
||
m_pGState->SetStrokeColorSpace(new GrDeviceGrayColorSpace());
|
||
m_pOut->UpdateStrokeColorSpace(m_pGState);
|
||
}
|
||
m_pGState->SetFillPattern(NULL);
|
||
m_pOut->UpdateFillColor(m_pGState);
|
||
m_pGState->SetStrokePattern(NULL);
|
||
m_pOut->UpdateStrokeColor(m_pGState);
|
||
if (!bStroke)
|
||
{
|
||
m_pGState->SetLineWidth(0);
|
||
m_pOut->UpdateLineWidth(m_pGState);
|
||
}
|
||
|
||
if (bStroke)
|
||
{
|
||
m_pGState->ClipToStrokePath();
|
||
m_pGState->GetClip()->ClipToPath(m_pGState->GetPath()->Copy(), m_pGState->GetCTM(), false);
|
||
|
||
if (m_pOut->UseClipTo())
|
||
m_pOut->ClipToPath(m_pGState, m_pGState->GetPath(), m_pGState->GetCTM(), false);
|
||
else
|
||
m_pOut->ClipToStrokePath(m_pGState);
|
||
}
|
||
else
|
||
{
|
||
m_pGState->Clip();
|
||
if (bEOFill)
|
||
{
|
||
m_pGState->GetClip()->ClipToPath(m_pGState->GetPath()->Copy(), m_pGState->GetCTM(), true);
|
||
|
||
if (m_pOut->UseClipTo())
|
||
m_pOut->ClipToPath(m_pGState, m_pGState->GetPath(), m_pGState->GetCTM(), true);
|
||
else
|
||
m_pOut->EoClip(m_pGState);
|
||
}
|
||
else
|
||
{
|
||
m_pGState->GetClip()->ClipToPath(m_pGState->GetPath()->Copy(), m_pGState->GetCTM(), false);
|
||
|
||
if (m_pOut->UseClipTo())
|
||
m_pOut->ClipToPath(m_pGState, m_pGState->GetPath(), m_pGState->GetCTM(), false);
|
||
else
|
||
m_pOut->Clip(m_pGState);
|
||
}
|
||
}
|
||
m_pGState->ClearPath();
|
||
|
||
double dClipXMin, dClipYMin, dClipXMax, dClipYMax;
|
||
m_pGState->GetClipBBox(&dClipXMin, &dClipYMin, &dClipXMax, &dClipYMax);
|
||
if (dClipXMin > dClipXMax || dClipYMin > dClipYMax)
|
||
{
|
||
RestoreGState();
|
||
m_pGState->SetPath(pSavedPath);
|
||
return;
|
||
}
|
||
|
||
double dXMin, dYMin, dXMax, dYMax;
|
||
dXMin = dXMax = dClipXMin * pInvTemp[0] + dClipYMin * pInvTemp[2] + pInvTemp[4];
|
||
dYMin = dYMax = dClipXMin * pInvTemp[1] + dClipYMin * pInvTemp[3] + pInvTemp[5];
|
||
double dTempX = dClipXMin * pInvTemp[0] + dClipYMax * pInvTemp[2] + pInvTemp[4];
|
||
double dTempY = dClipXMin * pInvTemp[1] + dClipYMax * pInvTemp[3] + pInvTemp[5];
|
||
if (dTempX < dXMin)
|
||
{
|
||
dXMin = dTempX;
|
||
}
|
||
else if (dTempX > dXMax)
|
||
{
|
||
dXMax = dTempX;
|
||
}
|
||
if (dTempY < dYMin)
|
||
{
|
||
dYMin = dTempY;
|
||
}
|
||
else if (dTempY > dYMax)
|
||
{
|
||
dYMax = dTempY;
|
||
}
|
||
dTempX = dClipXMax * pInvTemp[0] + dClipYMin * pInvTemp[2] + pInvTemp[4];
|
||
dTempY = dClipXMax * pInvTemp[1] + dClipYMin * pInvTemp[3] + pInvTemp[5];
|
||
if (dTempX < dXMin)
|
||
{
|
||
dXMin = dTempX;
|
||
}
|
||
else if (dTempX > dXMax)
|
||
{
|
||
dXMax = dTempX;
|
||
}
|
||
if (dTempY < dYMin)
|
||
{
|
||
dYMin = dTempY;
|
||
}
|
||
else if (dTempY > dYMax)
|
||
{
|
||
dYMax = dTempY;
|
||
}
|
||
dTempX = dClipXMax * pInvTemp[0] + dClipYMax * pInvTemp[2] + pInvTemp[4];
|
||
dTempY = dClipXMax * pInvTemp[1] + dClipYMax * pInvTemp[3] + pInvTemp[5];
|
||
if (dTempX < dXMin)
|
||
{
|
||
dXMin = dTempX;
|
||
}
|
||
else if (dTempX > dXMax)
|
||
{
|
||
dXMax = dTempX;
|
||
}
|
||
if (dTempY < dYMin)
|
||
{
|
||
dYMin = dTempY;
|
||
}
|
||
else if (dTempY > dYMax)
|
||
{
|
||
dYMax = dTempY;
|
||
}
|
||
|
||
double dStepX = fabs(pPattern->GetXStep());
|
||
double dStepY = fabs(pPattern->GetYStep());
|
||
int nX0 = (int)ceil((dXMin - pPattern->GetBBox()[2]) / dStepX);
|
||
int nX1 = (int)floor((dXMax - pPattern->GetBBox()[0]) / dStepX) + 1;
|
||
int nY0 = (int)ceil((dYMin - pPattern->GetBBox()[3]) / dStepY);
|
||
int nY1 = (int)floor((dYMax - pPattern->GetBBox()[1]) / dStepY) + 1;
|
||
for (int nIndex = 0; nIndex < 4; ++nIndex)
|
||
{
|
||
pTempMatrix[nIndex] = pMatrix[nIndex];
|
||
}
|
||
|
||
m_pOut->StartTilingFill(m_pGState);
|
||
if (m_pOut->UseTilingPatternFill())
|
||
{
|
||
pTempMatrix[4] = pMatrix[4];
|
||
pTempMatrix[5] = pMatrix[5];
|
||
m_pOut->TilingPatternFill(m_pGState, pPattern->GetContentStream(), pPattern->GetPaintType(), pPattern->GetResourcesDict(), pTempMatrix, pPattern->GetBBox(), nX0, nY0, nX1, nY1, dStepX, dStepY);
|
||
}
|
||
else
|
||
{
|
||
if (m_pOut->UseSimpleTilingPatternFill())
|
||
{
|
||
m_pOut->StartSimpleTilingFill(m_pGState, nX0, nY0, nX1, nY1, dStepX, dStepY, dXMin, dYMin, dXMax, dYMax, pTempMatrix);
|
||
|
||
for (int nY = nY0; nY < nY1; ++nY)
|
||
{
|
||
for (int nX = nX0; nX < nX1; ++nX)
|
||
{
|
||
double dX = nX * dStepX;
|
||
double dY = nY * dStepY;
|
||
pTempMatrix[4] = dX * pMatrix[0] + dY * pMatrix[2] + pMatrix[4];
|
||
pTempMatrix[5] = dX * pMatrix[1] + dY * pMatrix[3] + pMatrix[5];
|
||
DoForm(pPattern->GetContentStream(), pPattern->GetResourcesDict(), pTempMatrix, pPattern->GetBBox());
|
||
}
|
||
}
|
||
|
||
m_pOut->EndSimpleTilingFill();
|
||
}
|
||
else
|
||
{
|
||
for (int nY = nY0; nY < nY1; ++nY)
|
||
{
|
||
for (int nX = nX0; nX < nX1; ++nX)
|
||
{
|
||
if (m_pOut->IsStopped())
|
||
{
|
||
// Восстанавливаем GState
|
||
RestoreGState();
|
||
m_pGState->SetPath(pSavedPath);
|
||
return;
|
||
}
|
||
|
||
m_pOut->StartTilingFillIteration();
|
||
|
||
double dX = nX * dStepX;
|
||
double dY = nY * dStepY;
|
||
pTempMatrix[4] = dX * pMatrix[0] + dY * pMatrix[2] + pMatrix[4];
|
||
pTempMatrix[5] = dX * pMatrix[1] + dY * pMatrix[3] + pMatrix[5];
|
||
DoForm(pPattern->GetContentStream(), pPattern->GetResourcesDict(), pTempMatrix, pPattern->GetBBox());
|
||
|
||
m_pOut->EndTilingFillIteration();
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
m_pOut->EndTilingFill();
|
||
|
||
// Восстанавливаем GState
|
||
RestoreGState();
|
||
m_pGState->SetPath(pSavedPath);
|
||
}
|
||
|
||
void Graphics::DoShadingPatternFill(GrShadingPattern *pPattern, bool bStroke, bool bEOFill)
|
||
{
|
||
GrShading *pShading = pPattern->GetShading();
|
||
|
||
// Сохраняем текущий GState
|
||
GrPath *pSavedPath = m_pGState->GetPath()->Copy();
|
||
SaveGState();
|
||
|
||
if (pShading->GetHasBBox())
|
||
{
|
||
double dXMin, dYMin, dXMax, dYMax;
|
||
pShading->GetBBox(&dXMin, &dYMin, &dXMax, &dYMax);
|
||
m_pGState->MoveTo(dXMin, dYMin);
|
||
m_pGState->LineTo(dXMax, dYMin);
|
||
m_pGState->LineTo(dXMax, dYMax);
|
||
m_pGState->LineTo(dXMin, dYMax);
|
||
m_pGState->ClosePath();
|
||
m_pGState->Clip();
|
||
m_pGState->GetClip()->ClipToPath(m_pGState->GetPath()->Copy(), m_pGState->GetCTM(), false);
|
||
|
||
if (m_pOut->UseClipTo())
|
||
m_pOut->ClipToPath(m_pGState, m_pGState->GetPath(), m_pGState->GetCTM(), false);
|
||
else
|
||
m_pOut->Clip(m_pGState);
|
||
|
||
m_pGState->SetPath(pSavedPath->Copy());
|
||
}
|
||
|
||
if (bStroke)
|
||
{
|
||
m_pGState->ClipToStrokePath();
|
||
m_pGState->GetClip()->ClipToPath(m_pGState->GetPath()->Copy(), m_pGState->GetCTM(), false);
|
||
|
||
if (m_pOut->UseClipTo())
|
||
m_pOut->ClipToPath(m_pGState, m_pGState->GetPath(), m_pGState->GetCTM(), false);
|
||
else
|
||
m_pOut->ClipToStrokePath(m_pGState);
|
||
}
|
||
else
|
||
{
|
||
m_pGState->Clip();
|
||
if (bEOFill)
|
||
{
|
||
m_pGState->GetClip()->ClipToPath(m_pGState->GetPath()->Copy(), m_pGState->GetCTM(), true);
|
||
|
||
if (m_pOut->UseClipTo())
|
||
m_pOut->ClipToPath(m_pGState, m_pGState->GetPath(), m_pGState->GetCTM(), true);
|
||
else
|
||
m_pOut->EoClip(m_pGState);
|
||
}
|
||
else
|
||
{
|
||
m_pGState->GetClip()->ClipToPath(m_pGState->GetPath()->Copy(), m_pGState->GetCTM(), false);
|
||
|
||
if (m_pOut->UseClipTo())
|
||
m_pOut->ClipToPath(m_pGState, m_pGState->GetPath(), m_pGState->GetCTM(), false);
|
||
else
|
||
m_pOut->Clip(m_pGState);
|
||
}
|
||
}
|
||
|
||
// Устанавливаем цветовое пространство
|
||
m_pGState->SetFillColorSpace(pShading->GetColorSpace()->Copy());
|
||
m_pOut->UpdateFillColorSpace(m_pGState);
|
||
|
||
// Задний фон
|
||
if (pShading->GetHasBackground())
|
||
{
|
||
m_pGState->SetFillColor(pShading->GetBackground());
|
||
m_pOut->UpdateFillColor(m_pGState);
|
||
m_pOut->Fill(m_pGState);
|
||
}
|
||
m_pGState->ClearPath();
|
||
|
||
// Строим матрицу преобразования (Pattern space) -> (Current space)
|
||
double *pCTM = m_pGState->GetCTM();
|
||
double *pBaseMatrix = m_arrBaseMatrix;
|
||
double *pPatternMatrix = pPattern->GetMatrix();
|
||
|
||
// InvCTM = (CTM)^(-1)
|
||
double pInvCTM[6];
|
||
double dDet = 1 / (pCTM[0] * pCTM[3] - pCTM[1] * pCTM[2]);
|
||
pInvCTM[0] = pCTM[3] * dDet;
|
||
pInvCTM[1] = -pCTM[1] * dDet;
|
||
pInvCTM[2] = -pCTM[2] * dDet;
|
||
pInvCTM[3] = pCTM[0] * dDet;
|
||
pInvCTM[4] = (pCTM[2] * pCTM[5] - pCTM[3] * pCTM[4]) * dDet;
|
||
pInvCTM[5] = (pCTM[1] * pCTM[4] - pCTM[0] * pCTM[5]) * dDet;
|
||
// TempMatrix = PatternMatrix * BaseMatrix
|
||
double pTempMatrix[6];
|
||
pTempMatrix[0] = pPatternMatrix[0] * pBaseMatrix[0] + pPatternMatrix[1] * pBaseMatrix[2];
|
||
pTempMatrix[1] = pPatternMatrix[0] * pBaseMatrix[1] + pPatternMatrix[1] * pBaseMatrix[3];
|
||
pTempMatrix[2] = pPatternMatrix[2] * pBaseMatrix[0] + pPatternMatrix[3] * pBaseMatrix[2];
|
||
pTempMatrix[3] = pPatternMatrix[2] * pBaseMatrix[1] + pPatternMatrix[3] * pBaseMatrix[3];
|
||
pTempMatrix[4] = pPatternMatrix[4] * pBaseMatrix[0] + pPatternMatrix[5] * pBaseMatrix[2] + pBaseMatrix[4];
|
||
pTempMatrix[5] = pPatternMatrix[4] * pBaseMatrix[1] + pPatternMatrix[5] * pBaseMatrix[3] + pBaseMatrix[5];
|
||
// Matrix = TempMatrix * InvCTM
|
||
double pMatrix[6];
|
||
pMatrix[0] = pTempMatrix[0] * pInvCTM[0] + pTempMatrix[1] * pInvCTM[2];
|
||
pMatrix[1] = pTempMatrix[0] * pInvCTM[1] + pTempMatrix[1] * pInvCTM[3];
|
||
pMatrix[2] = pTempMatrix[2] * pInvCTM[0] + pTempMatrix[3] * pInvCTM[2];
|
||
pMatrix[3] = pTempMatrix[2] * pInvCTM[1] + pTempMatrix[3] * pInvCTM[3];
|
||
pMatrix[4] = pTempMatrix[4] * pInvCTM[0] + pTempMatrix[5] * pInvCTM[2] + pInvCTM[4];
|
||
pMatrix[5] = pTempMatrix[4] * pInvCTM[1] + pTempMatrix[5] * pInvCTM[3] + pInvCTM[5];
|
||
|
||
// Устанавливаем новую матрицу
|
||
m_pGState->ConcatCTM(pMatrix[0], pMatrix[1], pMatrix[2], pMatrix[3], pMatrix[4], pMatrix[5]);
|
||
m_pOut->UpdateCTM(m_pGState, pMatrix[0], pMatrix[1], pMatrix[2], pMatrix[3], pMatrix[4], pMatrix[5]);
|
||
|
||
bool bAntialias = m_pOut->GetVectorAntialias();
|
||
if (bAntialias)
|
||
{
|
||
m_pOut->SetVectorAntialias(false);
|
||
}
|
||
|
||
m_pOut->StartShadedFill(m_pGState);
|
||
|
||
// Выполняем закраску, в зависимости от типа Shading
|
||
switch (pShading->GetType())
|
||
{
|
||
case 1:
|
||
DoFunctionShadingFill((GrFunctionShading *)pShading);
|
||
break;
|
||
case 2:
|
||
DoAxialShadingFill((GrAxialShading *)pShading);
|
||
break;
|
||
case 3:
|
||
DoRadialShadingFill((GrRadialShading *)pShading);
|
||
break;
|
||
case 4:
|
||
case 5:
|
||
DoGouraudTriangleShadingFill((GrGouraudTriangleShading *)pShading);
|
||
break;
|
||
case 6:
|
||
case 7:
|
||
DoPatchMeshShadingFill((GrPatchMeshShading *)pShading);
|
||
break;
|
||
}
|
||
|
||
m_pOut->EndShadedFill();
|
||
|
||
if (bAntialias)
|
||
{
|
||
m_pOut->SetVectorAntialias(true);
|
||
}
|
||
|
||
// Восстанавливаем GState
|
||
RestoreGState();
|
||
m_pGState->SetPath(pSavedPath);
|
||
}
|
||
|
||
void Graphics::DoFunctionShadingFill(GrFunctionShading *pShading)
|
||
{
|
||
// Сначала предоставляем возможность OuputDevice самому сделать Shading
|
||
if (m_pOut->UseFunctionalShadedFills() && m_pOut->FunctionShadedFill(m_pGState, pShading))
|
||
{
|
||
return;
|
||
}
|
||
|
||
// Если Output Device не поддерживает данный ShadingType, тогда делаем его сами
|
||
// с помощью GrPath.
|
||
double dMinX, dMinY, dMaxX, dMaxY;
|
||
GrColor arrColors[4];
|
||
|
||
pShading->GetDomain(&dMinX, &dMinY, &dMaxX, &dMaxY);
|
||
pShading->GetColor(dMinX, dMinY, &arrColors[0]);
|
||
pShading->GetColor(dMinX, dMaxY, &arrColors[1]);
|
||
pShading->GetColor(dMaxX, dMinY, &arrColors[2]);
|
||
pShading->GetColor(dMaxX, dMaxY, &arrColors[3]);
|
||
DoFunctionShadingFill(pShading, dMinX, dMinY, dMaxX, dMaxY, arrColors, 0);
|
||
}
|
||
|
||
void Graphics::DoFunctionShadingFill(GrFunctionShading *pShading, double dMinX, double dMinY, double dMaxX, double dMaxY, GrColor *pColors, int nDepth)
|
||
{
|
||
if (m_pOut->IsStopped())
|
||
return;
|
||
|
||
int nComponentsCount = pShading->GetColorSpace()->GetComponentsCount();
|
||
|
||
// Сравниваем цвета в углах
|
||
int nColorIndex = 0;
|
||
for (nColorIndex = 0; nColorIndex < 4; ++nColorIndex)
|
||
{
|
||
int nComponentIndex;
|
||
for (nComponentIndex = 0; nComponentIndex < nComponentsCount; ++nComponentIndex)
|
||
{
|
||
if (abs(pColors[nColorIndex].arrComp[nComponentIndex] - pColors[(nColorIndex + 1) & 3].arrComp[nComponentIndex]) > functionColorDelta)
|
||
{
|
||
break;
|
||
}
|
||
}
|
||
if (nComponentIndex < nComponentsCount)
|
||
{
|
||
break;
|
||
}
|
||
}
|
||
|
||
double dCenterX = 0.5 * (dMinX + dMaxX);
|
||
double dCenterY = 0.5 * (dMinY + dMaxY);
|
||
|
||
// Угловые вершины прямоугольников достаточно близки (или мы достигли предела количетсву иттераций)
|
||
// Заливаем прямоугольник. Даже если изначально вершины близки проводим одну иттерацию деления, чтобы
|
||
// случая, когда все 4 вершины имеют одинаковый цвет.
|
||
if ((nColorIndex == 4 && nDepth > 0) || nDepth == functionMaxDepth)
|
||
{
|
||
// Используем цвет центра прямоугольника
|
||
GrColor oFillColor;
|
||
pShading->GetColor(dCenterX, dCenterY, &oFillColor);
|
||
m_pGState->SetFillColor(&oFillColor);
|
||
m_pOut->UpdateFillColor(m_pGState);
|
||
|
||
double *pMatrix = pShading->GetMatrix();
|
||
// Заливка прямоугольника
|
||
m_pGState->MoveTo(dMinX * pMatrix[0] + dMinY * pMatrix[2] + pMatrix[4], dMinX * pMatrix[1] + dMinY * pMatrix[3] + pMatrix[5]);
|
||
m_pGState->LineTo(dMaxX * pMatrix[0] + dMinY * pMatrix[2] + pMatrix[4], dMaxX * pMatrix[1] + dMinY * pMatrix[3] + pMatrix[5]);
|
||
m_pGState->LineTo(dMaxX * pMatrix[0] + dMaxY * pMatrix[2] + pMatrix[4], dMaxX * pMatrix[1] + dMaxY * pMatrix[3] + pMatrix[5]);
|
||
m_pGState->LineTo(dMinX * pMatrix[0] + dMaxY * pMatrix[2] + pMatrix[4], dMinX * pMatrix[1] + dMaxY * pMatrix[3] + pMatrix[5]);
|
||
m_pGState->ClosePath();
|
||
m_pOut->Fill(m_pGState);
|
||
m_pGState->ClearPath();
|
||
}
|
||
else // 4 угловые вершины не достаточно близки, делим дальше на прямоугольники
|
||
{
|
||
|
||
// pColors[0] oColorCT pColors[2]
|
||
// (dMinX,dMinY) (dCenterX,dMinY) (dMaxX,dMinY)
|
||
// +---------------------+---------------------+
|
||
// | | |
|
||
// | Top Left | Top Right |
|
||
// oColorLC | oColorCC| | oColorRC
|
||
// +---------------------+---------------------+
|
||
// (dMinX,dCenterY) (dCenterX,dCenterY) (dMaxX,dCenterY)
|
||
// | Bottom Left | Bottom Right |
|
||
// | | |
|
||
// +---------------------+---------------------+
|
||
// (dMinX,dMaxY) (dCenterX,dMaxY) (dMaxX,dMaxY)
|
||
// pColors[1] oColorCB pColors[3]
|
||
|
||
GrColor oColorLC, oColorRC, oColorCT, oColorCB, oColorCC, arrSubColors[4];
|
||
pShading->GetColor(dMinX, dCenterY, &oColorLC);
|
||
pShading->GetColor(dMaxX, dCenterY, &oColorRC);
|
||
pShading->GetColor(dCenterX, dMinY, &oColorCT);
|
||
pShading->GetColor(dCenterX, dMaxY, &oColorCB);
|
||
pShading->GetColor(dCenterX, dCenterY, &oColorCC);
|
||
|
||
// Верхний левый прямоугольник
|
||
arrSubColors[0] = pColors[0];
|
||
arrSubColors[1] = oColorLC;
|
||
arrSubColors[2] = oColorCT;
|
||
arrSubColors[3] = oColorCC;
|
||
DoFunctionShadingFill(pShading, dMinX, dMinY, dCenterX, dCenterY, arrSubColors, nDepth + 1);
|
||
|
||
// Левый нижний прямоугольник
|
||
arrSubColors[0] = oColorLC;
|
||
arrSubColors[1] = pColors[1];
|
||
arrSubColors[2] = oColorCC;
|
||
arrSubColors[3] = oColorCB;
|
||
DoFunctionShadingFill(pShading, dMinX, dCenterY, dCenterX, dMaxY, arrSubColors, nDepth + 1);
|
||
|
||
// Правый верхний прямоугольник
|
||
arrSubColors[0] = oColorCT;
|
||
arrSubColors[1] = oColorCC;
|
||
arrSubColors[2] = pColors[2];
|
||
arrSubColors[3] = oColorRC;
|
||
DoFunctionShadingFill(pShading, dCenterX, dMinY, dMaxX, dCenterY, arrSubColors, nDepth + 1);
|
||
|
||
// Правый нижний прямоугольник
|
||
arrSubColors[0] = oColorCC;
|
||
arrSubColors[1] = oColorCB;
|
||
arrSubColors[2] = oColorRC;
|
||
arrSubColors[3] = pColors[3];
|
||
DoFunctionShadingFill(pShading, dCenterX, dCenterY, dMaxX, dMaxY, arrSubColors, nDepth + 1);
|
||
}
|
||
}
|
||
|
||
void Graphics::DoAxialShadingFill(GrAxialShading *pShading)
|
||
{
|
||
int nJ, nK, nKK;
|
||
|
||
// Сначала предоставляем возможность OuputDevice самому сделать Shading
|
||
if (m_pOut->UseAxialShadedFills() && m_pOut->AxialShadedFill(m_pGState, pShading))
|
||
{
|
||
return;
|
||
}
|
||
|
||
// get the clip region bbox
|
||
double dMinX, dMinY, dMaxX, dMaxY;
|
||
m_pGState->GetUserClipBBox(&dMinX, &dMinY, &dMaxX, &dMaxY);
|
||
|
||
// Вычисляем макимальное и минимальное значения параметра T
|
||
double dTmin, dTmax;
|
||
double dX0, dY0, dX1, dY1;
|
||
pShading->GetCoords(&dX0, &dY0, &dX1, &dY1);
|
||
double dDx = dX1 - dX0;
|
||
double dDy = dY1 - dY0;
|
||
bool bDxZero = fabs(dDx) < 0.01;
|
||
bool bDyZero = fabs(dDy) < 0.01;
|
||
if (bDxZero && bDyZero)
|
||
{
|
||
dTmin = dTmax = 0;
|
||
}
|
||
else
|
||
{
|
||
double dMult = 1 / (dDx * dDx + dDy * dDy);
|
||
dTmin = dTmax = ((dMinX - dX0) * dDx + (dMinY - dY0) * dDy) * dMult;
|
||
double dTemp = ((dMinX - dX0) * dDx + (dMaxY - dY0) * dDy) * dMult;
|
||
if (dTemp < dTmin)
|
||
{
|
||
dTmin = dTemp;
|
||
}
|
||
else if (dTemp > dTmax)
|
||
{
|
||
dTmax = dTemp;
|
||
}
|
||
dTemp = ((dMaxX - dX0) * dDx + (dMinY - dY0) * dDy) * dMult;
|
||
if (dTemp < dTmin)
|
||
{
|
||
dTmin = dTemp;
|
||
}
|
||
else if (dTemp > dTmax)
|
||
{
|
||
dTmax = dTemp;
|
||
}
|
||
dTemp = ((dMaxX - dX0) * dDx + (dMaxY - dY0) * dDy) * dMult;
|
||
if (dTemp < dTmin)
|
||
{
|
||
dTmin = dTemp;
|
||
}
|
||
else if (dTemp > dTmax)
|
||
{
|
||
dTmax = dTemp;
|
||
}
|
||
if (dTmin < 0 && !pShading->GetExtendStart())
|
||
{
|
||
dTmin = 0;
|
||
}
|
||
if (dTmax > 1 && !pShading->GetExtendEnd())
|
||
{
|
||
dTmax = 1;
|
||
}
|
||
}
|
||
|
||
// Считываем пределы параметра T
|
||
double dT0 = pShading->GetDomain0();
|
||
double dT1 = pShading->GetDomain1();
|
||
|
||
// Для каждой точки (Tx, Ty) оси, рассмотрим линию ,проходяющую через данную точку перпендикулярно оси:
|
||
//
|
||
// x(s) = Tx + s * -dDy --> s = (x - Tx) / -dDy
|
||
// y(s) = Ty + s * dDx --> s = (y - Ty) / dDx
|
||
//
|
||
// Рассмотрим точки пересечения данной линии с внешним ректом(в котором все рисуем Bbox). В общем случае
|
||
// мы имеем 4 точки пересечения:
|
||
//
|
||
// s0 = (dMinX - Tx) / -dDy
|
||
// s1 = (dMaxX - Tx) / -dDy
|
||
// s2 = (dMinY - Ty) / dDx
|
||
// s3 = (dMaxY - Ty) / dDx
|
||
//
|
||
// нас интересуют два средних значения s.
|
||
//
|
||
// В случае, когда dDx = 0, возьмем s0 и s1; в случае, когда dDy = 0, возьмем s2 и s3.
|
||
//
|
||
// Далее каждый полигон, который мы будем заполнять будет ограничен двумя такими линиями, перпендикулярными оси.
|
||
//
|
||
// Делим ось таким образом до тех пор, пока разница цветов между двумя соседними линиями не станет достаточно
|
||
// малой. Далее кажды отдельный полигон закрашиваем одним цветом.
|
||
|
||
// Делаем как минимум одну такую иттерацию, чтобы избежать проблем в случае, когда оба конца оси имеют
|
||
// одинаковый цвет.
|
||
|
||
int nComponentsCount = pShading->GetColorSpace()->GetComponentsCount();
|
||
double arrTa[axialMaxSplits + 1];
|
||
arrTa[0] = dTmin;
|
||
int arrNext[axialMaxSplits + 1];
|
||
arrNext[0] = axialMaxSplits / 2;
|
||
arrTa[axialMaxSplits / 2] = 0.5 * (dTmin + dTmax);
|
||
arrNext[axialMaxSplits / 2] = axialMaxSplits;
|
||
arrTa[axialMaxSplits] = dTmax;
|
||
|
||
// Вычисляем значение цвета при t = dTmin
|
||
double dTt = 0;
|
||
if (dTmin < 0)
|
||
{
|
||
dTt = dT0;
|
||
}
|
||
else if (dTmin > 1)
|
||
{
|
||
dTt = dT1;
|
||
}
|
||
else
|
||
{
|
||
dTt = dT0 + (dT1 - dT0) * dTmin;
|
||
}
|
||
GrColor oColor0, oColor1;
|
||
pShading->GetColor(dTt, &oColor0);
|
||
|
||
// Вычисляем координаты точки, лежаще на оси, при значении параметра t = dTmin;
|
||
// после этого вычисляем координаты точек пересечения для линии при t = dTmin.
|
||
double dTx = dX0 + dTmin * dDx;
|
||
double dTy = dY0 + dTmin * dDy;
|
||
double arrS[4];
|
||
|
||
double dSmin, dSmax;
|
||
|
||
if (bDxZero && bDyZero)
|
||
{
|
||
dSmin = dSmax = 0;
|
||
}
|
||
else if (bDxZero)
|
||
{
|
||
dSmin = (dMinX - dTx) / -dDy;
|
||
dSmax = (dMaxX - dTx) / -dDy;
|
||
if (dSmin > dSmax)
|
||
{
|
||
double dTemp = dSmin;
|
||
dSmin = dSmax;
|
||
dSmax = dTemp;
|
||
}
|
||
}
|
||
else if (bDyZero)
|
||
{
|
||
dSmin = (dMinY - dTy) / dDx;
|
||
dSmax = (dMaxY - dTy) / dDx;
|
||
if (dSmin > dSmax)
|
||
{
|
||
double dTemp = dSmin;
|
||
dSmin = dSmax;
|
||
dSmax = dTemp;
|
||
}
|
||
}
|
||
else
|
||
{
|
||
arrS[0] = (dMinY - dTy) / dDx;
|
||
arrS[1] = (dMaxY - dTy) / dDx;
|
||
arrS[2] = (dMinX - dTx) / -dDy;
|
||
arrS[3] = (dMaxX - dTx) / -dDy;
|
||
for (nJ = 0; nJ < 3; ++nJ)
|
||
{
|
||
nKK = nJ;
|
||
for (nK = nJ + 1; nK < 4; ++nK)
|
||
{
|
||
if (arrS[nK] < arrS[nKK])
|
||
{
|
||
nKK = nK;
|
||
}
|
||
}
|
||
double dTemp = arrS[nJ];
|
||
arrS[nJ] = arrS[nKK];
|
||
arrS[nKK] = dTemp;
|
||
}
|
||
dSmin = arrS[1];
|
||
dSmax = arrS[2];
|
||
}
|
||
double dStartX0 = dTx - dSmin * dDy;
|
||
double dStartY0 = dTy + dSmin * dDx;
|
||
double dEndX0 = dTx - dSmax * dDy;
|
||
double dEndY0 = dTy + dSmax * dDx;
|
||
|
||
int nSplitsCount = 0;
|
||
while (nSplitsCount < axialMaxSplits)
|
||
{
|
||
if (m_pOut->IsStopped())
|
||
return;
|
||
|
||
// Делим пока разница цветов не станет достаточной маленькой или пока мы не достигнем предела количеству иттераций
|
||
nJ = arrNext[nSplitsCount];
|
||
while (nJ > nSplitsCount + 1)
|
||
{
|
||
if (m_pOut->IsStopped())
|
||
return;
|
||
|
||
if (arrTa[nJ] < 0)
|
||
{
|
||
dTt = dT0;
|
||
}
|
||
else if (arrTa[nJ] > 1)
|
||
{
|
||
dTt = dT1;
|
||
}
|
||
else
|
||
{
|
||
dTt = dT0 + (dT1 - dT0) * arrTa[nJ];
|
||
}
|
||
pShading->GetColor(dTt, &oColor1);
|
||
for (nK = 0; nK < nComponentsCount; ++nK)
|
||
{
|
||
if (abs(oColor1.arrComp[nK] - oColor0.arrComp[nK]) > axialColorDelta)
|
||
{
|
||
break;
|
||
}
|
||
}
|
||
if (nK == nComponentsCount)
|
||
{
|
||
break;
|
||
}
|
||
nK = (nSplitsCount + nJ) / 2;
|
||
arrTa[nK] = 0.5 * (arrTa[nSplitsCount] + arrTa[nJ]);
|
||
arrNext[nSplitsCount] = nK;
|
||
arrNext[nK] = nJ;
|
||
nJ = nK;
|
||
}
|
||
|
||
// Используем среднее значение цвета
|
||
for (nK = 0; nK < nComponentsCount; ++nK)
|
||
{
|
||
oColor0.arrComp[nK] = (oColor0.arrComp[nK] + oColor1.arrComp[nK]) / 2;
|
||
}
|
||
|
||
// Вычисляем координаты точки, лежаще на оси, при данном значении параметра t;
|
||
// после этого вычисляем координаты точек пересечения для линии данном t.
|
||
dTx = dX0 + arrTa[nJ] * dDx;
|
||
dTy = dY0 + arrTa[nJ] * dDy;
|
||
if (bDxZero && bDyZero)
|
||
{
|
||
dSmin = dSmax = 0;
|
||
}
|
||
else if (bDxZero)
|
||
{
|
||
dSmin = (dMinX - dTx) / -dDy;
|
||
dSmax = (dMaxX - dTx) / -dDy;
|
||
if (dSmin > dSmax)
|
||
{
|
||
double dTemp = dSmin;
|
||
dSmin = dSmax;
|
||
dSmax = dTemp;
|
||
}
|
||
}
|
||
else if (bDyZero)
|
||
{
|
||
dSmin = (dMinY - dTy) / dDx;
|
||
dSmax = (dMaxY - dTy) / dDx;
|
||
if (dSmin > dSmax)
|
||
{
|
||
double dTemp = dSmin;
|
||
dSmin = dSmax;
|
||
dSmax = dTemp;
|
||
}
|
||
}
|
||
else
|
||
{
|
||
arrS[0] = (dMinY - dTy) / dDx;
|
||
arrS[1] = (dMaxY - dTy) / dDx;
|
||
arrS[2] = (dMinX - dTx) / -dDy;
|
||
arrS[3] = (dMaxX - dTx) / -dDy;
|
||
for (nJ = 0; nJ < 3; ++nJ)
|
||
{
|
||
nKK = nJ;
|
||
for (nK = nJ + 1; nK < 4; ++nK)
|
||
{
|
||
if (arrS[nK] < arrS[nKK])
|
||
{
|
||
nKK = nK;
|
||
}
|
||
}
|
||
double dTemp = arrS[nJ];
|
||
arrS[nJ] = arrS[nKK];
|
||
arrS[nKK] = dTemp;
|
||
}
|
||
dSmin = arrS[1];
|
||
dSmax = arrS[2];
|
||
}
|
||
double dStartX1 = dTx - dSmin * dDy;
|
||
double dStartY1 = dTy + dSmin * dDx;
|
||
double dEndX1 = dTx - dSmax * dDy;
|
||
double dEndY1 = dTy + dSmax * dDx;
|
||
|
||
// Устанавливаем цвет
|
||
m_pGState->SetFillColor(&oColor0);
|
||
m_pOut->UpdateFillColor(m_pGState);
|
||
|
||
// Закрашиваем
|
||
m_pGState->MoveTo(dStartX0, dStartY0);
|
||
m_pGState->LineTo(dEndX0, dEndY0);
|
||
m_pGState->LineTo(dEndX1, dEndY1);
|
||
m_pGState->LineTo(dStartX1, dStartY1);
|
||
m_pGState->ClosePath();
|
||
m_pOut->Fill(m_pGState);
|
||
m_pGState->ClearPath();
|
||
|
||
// Начальные значения для следующего полигона
|
||
dStartX0 = dStartX1;
|
||
dStartY0 = dStartY1;
|
||
dEndX0 = dEndX1;
|
||
dEndY0 = dEndY1;
|
||
oColor0 = oColor1;
|
||
nSplitsCount = arrNext[nSplitsCount];
|
||
}
|
||
}
|
||
|
||
void Graphics::DoRadialShadingFill(GrRadialShading *pShading)
|
||
{
|
||
// Сначала предоставляем возможность OuputDevice самому сделать Shading
|
||
if (m_pOut->UseRadialShadedFills() && m_pOut->RadialShadedFill(m_pGState, pShading))
|
||
{
|
||
return;
|
||
}
|
||
// Если Output Device не поддерживает данный ShadingType, тогда делаем его сами
|
||
// с помощью GrPath.
|
||
|
||
double dFirstX, dFirstY, dFirstRad, dSecondX, dSecondY, dSecondRad;
|
||
pShading->GetCoords(&dFirstX, &dFirstY, &dFirstRad, &dSecondX, &dSecondY, &dSecondRad);
|
||
double dT0 = pShading->GetDomain0();
|
||
double dT1 = pShading->GetDomain1();
|
||
int nComponentsCount = pShading->GetColorSpace()->GetComponentsCount();
|
||
|
||
// Вычисляем точку, в которой r(s) = 0; проверяме вложенность окружностей; и
|
||
// вычисляем углы тангенциальных линий
|
||
bool bEnclosed = false;
|
||
double dTheta = 0, dAlpha = 0;;
|
||
double dZeroS = 0;
|
||
if (dFirstX == dSecondX && dFirstY == dSecondY)
|
||
{
|
||
bEnclosed = true;
|
||
dTheta = 0;
|
||
dZeroS = 0;
|
||
}
|
||
else if (dFirstRad == dSecondRad)
|
||
{
|
||
bEnclosed = false;
|
||
dTheta = 0;
|
||
dZeroS = 0;
|
||
}
|
||
else
|
||
{
|
||
dZeroS = -dFirstRad / (dSecondRad - dFirstRad);
|
||
double dZeroX = dFirstX + dZeroS * (dSecondX - dFirstX);
|
||
double dZeroY = dFirstY + dZeroS * (dSecondY - dFirstY);
|
||
bEnclosed = ((dZeroX - dFirstX) * (dZeroX - dFirstX) + (dZeroY - dFirstY) * (dZeroY - dFirstY) <= dFirstRad * dFirstRad);
|
||
dTheta = asin(dFirstRad / sqrt((dFirstX - dZeroX) * (dFirstX - dZeroX) + (dFirstY - dZeroY) * (dFirstY - dZeroY)));
|
||
if (dFirstRad > dSecondRad)
|
||
{
|
||
dTheta = -dTheta;
|
||
}
|
||
}
|
||
if (bEnclosed)
|
||
{
|
||
dAlpha = 0;
|
||
}
|
||
else
|
||
{
|
||
dAlpha = atan2(dSecondY - dFirstY, dSecondX - dFirstX);
|
||
}
|
||
|
||
double dXMin, dYMin, dXMax, dYMax;
|
||
m_pGState->GetUserClipBBox(&dXMin, &dYMin, &dXMax, &dYMax);
|
||
|
||
double dSmin, dSmax;
|
||
if (bEnclosed)
|
||
{
|
||
dSmin = 0;
|
||
dSmax = 1;
|
||
}
|
||
else
|
||
{
|
||
dSmin = 1;
|
||
dSmax = 0;
|
||
// x(s) + r(s) = dXMin
|
||
if ((dSecondX + dSecondRad) - (dFirstX + dFirstRad) != 0)
|
||
{
|
||
double dTempS = (dXMin - (dFirstX + dFirstRad)) / ((dSecondX + dSecondRad) - (dFirstX + dFirstRad));
|
||
if (dTempS < dSmin)
|
||
{
|
||
dSmin = dTempS;
|
||
}
|
||
else if (dTempS > dSmax)
|
||
{
|
||
dSmax = dTempS;
|
||
}
|
||
}
|
||
// x(s) - r(s) = dXMax
|
||
if ((dSecondX - dSecondRad) - (dFirstX - dFirstRad) != 0)
|
||
{
|
||
double dTempS = (dXMax - (dFirstX - dFirstRad)) / ((dSecondX - dSecondRad) - (dFirstX - dFirstRad));
|
||
if (dTempS < dSmin)
|
||
{
|
||
dSmin = dTempS;
|
||
}
|
||
else if (dTempS > dSmax)
|
||
{
|
||
dSmax = dTempS;
|
||
}
|
||
}
|
||
// y(s) + r(s) = dYMin
|
||
if ((dSecondY + dSecondRad) - (dFirstY + dFirstRad) != 0)
|
||
{
|
||
double dTempS = (dYMin - (dFirstY + dFirstRad)) / ((dSecondY + dSecondRad) - (dFirstY + dFirstRad));
|
||
if (dTempS < dSmin)
|
||
{
|
||
dSmin = dTempS;
|
||
}
|
||
else if (dTempS > dSmax)
|
||
{
|
||
dSmax = dTempS;
|
||
}
|
||
}
|
||
// y(s) - r(s) = dYMax
|
||
if ((dSecondY - dSecondRad) - (dFirstY - dFirstRad) != 0)
|
||
{
|
||
double dTempS = (dYMax - (dFirstY - dFirstRad)) / ((dSecondY - dSecondRad) - (dFirstY - dFirstRad));
|
||
if (dTempS < dSmin)
|
||
{
|
||
dSmin = dTempS;
|
||
}
|
||
else if (dTempS > dSmax)
|
||
{
|
||
dSmax = dTempS;
|
||
}
|
||
}
|
||
|
||
// Проверяем относительно dZeroS
|
||
if (dFirstRad < dSecondRad)
|
||
{
|
||
if (dSmin < dZeroS)
|
||
{
|
||
dSmin = dZeroS;
|
||
}
|
||
}
|
||
else if (dFirstRad > dSecondRad)
|
||
{
|
||
if (dSmax > dZeroS)
|
||
{
|
||
dSmax = dZeroS;
|
||
}
|
||
}
|
||
|
||
if (!pShading->GetExtendFirst() && dSmin < 0)
|
||
{
|
||
dSmin = 0;
|
||
}
|
||
if (!pShading->GetExtendSecond() && dSmax > 1)
|
||
{
|
||
dSmax = 1;
|
||
}
|
||
}
|
||
|
||
double *pCTM = m_pGState->GetCTM();
|
||
double dTemp = fabs(pCTM[0]);
|
||
if (fabs(pCTM[1]) > dTemp)
|
||
{
|
||
dTemp = fabs(pCTM[1]);
|
||
}
|
||
if (fabs(pCTM[2]) > dTemp)
|
||
{
|
||
dTemp = fabs(pCTM[2]);
|
||
}
|
||
if (fabs(pCTM[3]) > dTemp)
|
||
{
|
||
dTemp = fabs(pCTM[3]);
|
||
}
|
||
if (dFirstRad > dSecondRad)
|
||
{
|
||
dTemp *= dFirstRad;
|
||
}
|
||
else
|
||
{
|
||
dTemp *= dSecondRad;
|
||
}
|
||
int nStepsCount = 0;
|
||
if (dTemp < 1)
|
||
{
|
||
nStepsCount = 3;
|
||
}
|
||
else
|
||
{
|
||
nStepsCount = (int)(M_PI / acos(1 - 0.1 / dTemp));
|
||
if (nStepsCount < 3)
|
||
{
|
||
nStepsCount = 3;
|
||
}
|
||
else if (nStepsCount > 200)
|
||
{
|
||
nStepsCount = 200;
|
||
}
|
||
}
|
||
|
||
// Везде далее A - первая окружность, B - вторая
|
||
int nIndexA = 0;
|
||
double dSA = dSmin;
|
||
double dTA = dT0 + dSA * (dT1 - dT0);
|
||
double dXA = dFirstX + dSA * (dSecondX - dFirstX);
|
||
double dYA = dFirstY + dSA * (dSecondY - dFirstY);
|
||
double dRadA = dFirstRad + dSA * (dSecondRad - dFirstRad);
|
||
double dAngle = 0;
|
||
int nCounter = 0;
|
||
GrColor oColorA;
|
||
if (dTA < dT0)
|
||
{
|
||
pShading->GetColor(dT0, &oColorA);
|
||
}
|
||
else if (dTA > dT1)
|
||
{
|
||
pShading->GetColor(dT1, &oColorA);
|
||
}
|
||
else
|
||
{
|
||
pShading->GetColor(dTA, &oColorA);
|
||
}
|
||
|
||
while (nIndexA < radialMaxSplits)
|
||
{
|
||
if (m_pOut->IsStopped())
|
||
return;
|
||
|
||
int nIndexB = radialMaxSplits;
|
||
double dSB = dSmax;
|
||
double dTB = dT0 + dSB * (dT1 - dT0);
|
||
GrColor oColorB;
|
||
if (dTB < dT0)
|
||
{
|
||
pShading->GetColor(dT0, &oColorB);
|
||
}
|
||
else if (dTB > dT1)
|
||
{
|
||
pShading->GetColor(dT1, &oColorB);
|
||
}
|
||
else
|
||
{
|
||
pShading->GetColor(dTB, &oColorB);
|
||
}
|
||
|
||
while (nIndexB - nIndexA > 1)
|
||
{
|
||
if (m_pOut->IsStopped())
|
||
return;
|
||
|
||
for (nCounter = 0; nCounter < nComponentsCount; ++nCounter)
|
||
{
|
||
if (abs(oColorB.arrComp[nCounter] - oColorA.arrComp[nCounter]) > radialColorDelta)
|
||
{
|
||
break;
|
||
}
|
||
}
|
||
if (nCounter == nComponentsCount && nIndexB < radialMaxSplits)
|
||
{
|
||
break;
|
||
}
|
||
nIndexB = (nIndexA + nIndexB) / 2;
|
||
dSB = dSmin + ((double)nIndexB / (double)radialMaxSplits) * (dSmax - dSmin);
|
||
dTB = dT0 + dSB * (dT1 - dT0);
|
||
if (dTB < dT0)
|
||
{
|
||
pShading->GetColor(dT0, &oColorB);
|
||
}
|
||
else if (dTB > dT1)
|
||
{
|
||
pShading->GetColor(dT1, &oColorB);
|
||
}
|
||
else
|
||
{
|
||
pShading->GetColor(dTB, &oColorB);
|
||
}
|
||
}
|
||
|
||
// Вычислим центр и радиус окружности
|
||
double dXB = dFirstX + dSB * (dSecondX - dFirstX);
|
||
double dYB = dFirstY + dSB * (dSecondY - dFirstY);
|
||
double dRadB = dFirstRad + dSB * (dSecondRad - dFirstRad);
|
||
|
||
// Используем среднее значение цвета двух окружностей
|
||
for (nCounter = 0; nCounter < nComponentsCount; ++nCounter)
|
||
{
|
||
oColorA.arrComp[nCounter] = (oColorA.arrComp[nCounter] + oColorB.arrComp[nCounter]) / 2;
|
||
}
|
||
m_pGState->SetFillColor(&oColorA);
|
||
m_pOut->UpdateFillColor(m_pGState);
|
||
|
||
if (bEnclosed)
|
||
{
|
||
|
||
// Строим Path для первой окружности (против часовой)
|
||
m_pGState->MoveTo(dXA + dRadA, dYA);
|
||
for (nCounter = 1; nCounter < nStepsCount; ++nCounter)
|
||
{
|
||
dAngle = ((double)nCounter / (double)nStepsCount) * 2 * M_PI;
|
||
m_pGState->LineTo(dXA + dRadA * cos(dAngle), dYA + dRadA * sin(dAngle));
|
||
}
|
||
m_pGState->ClosePath();
|
||
|
||
// Строим Path для второй окружности (по часовой)
|
||
m_pGState->MoveTo(dXB + dRadB, dYB);
|
||
for (nCounter = 1; nCounter < nStepsCount; ++nCounter)
|
||
{
|
||
dAngle = -((double)nCounter / (double)nStepsCount) * 2 * M_PI;
|
||
m_pGState->LineTo(dXB + dRadB * cos(dAngle), dYB + dRadB * sin(dAngle));
|
||
}
|
||
m_pGState->ClosePath();
|
||
|
||
}
|
||
else
|
||
{
|
||
|
||
// Строим первый subpath (по часовой)
|
||
m_pGState->MoveTo(dXA + dRadA * cos(dAlpha + dTheta + 0.5 * M_PI), dYA + dRadA * sin(dAlpha + dTheta + 0.5 * M_PI));
|
||
for (nCounter = 0; nCounter < nStepsCount; ++nCounter)
|
||
{
|
||
dAngle = dAlpha + dTheta + 0.5 * M_PI - ((double)nCounter / (double)nStepsCount) * (2 * dTheta + M_PI);
|
||
m_pGState->LineTo(dXB + dRadB * cos(dAngle), dYB + dRadB * sin(dAngle));
|
||
}
|
||
for (nCounter = 0; nCounter < nStepsCount; ++nCounter)
|
||
{
|
||
dAngle = dAlpha - dTheta - 0.5 * M_PI + ((double)nCounter / (double)nStepsCount) * (2 * dTheta - M_PI);
|
||
m_pGState->LineTo(dXA + dRadA * cos(dAngle), dYA + dRadA * sin(dAngle));
|
||
}
|
||
m_pGState->ClosePath();
|
||
|
||
// Строим второй subpath (против часовой)
|
||
m_pGState->MoveTo(dXA + dRadA * cos(dAlpha + dTheta + 0.5 * M_PI), dYA + dRadA * sin(dAlpha + dTheta + 0.5 * M_PI));
|
||
for (nCounter = 0; nCounter < nStepsCount; ++nCounter)
|
||
{
|
||
dAngle = dAlpha + dTheta + 0.5 * M_PI + ((double)nCounter / (double)nStepsCount) * (-2 * dTheta + M_PI);
|
||
m_pGState->LineTo(dXB + dRadB * cos(dAngle), dYB + dRadB * sin(dAngle));
|
||
}
|
||
for (nCounter = 0; nCounter < nStepsCount; ++nCounter)
|
||
{
|
||
dAngle = dAlpha - dTheta - 0.5 * M_PI + ((double)nCounter / (double)nStepsCount) * (2 * dTheta + M_PI);
|
||
m_pGState->LineTo(dXA + dRadA * cos(dAngle), dYA + dRadA * sin(dAngle));
|
||
}
|
||
m_pGState->ClosePath();
|
||
}
|
||
|
||
// Закрашиваем
|
||
m_pOut->Fill(m_pGState);
|
||
m_pGState->ClearPath();
|
||
|
||
// Начальные данные для следующего шага
|
||
nIndexA = nIndexB;
|
||
dSA = dSB;
|
||
dTA = dTB;
|
||
dXA = dXB;
|
||
dYA = dYB;
|
||
dRadA = dRadB;
|
||
oColorA = oColorB;
|
||
}
|
||
|
||
// Если выставлены флаги продолжать рисовать за пределами двух окружностей
|
||
if (bEnclosed)
|
||
{
|
||
// Продолжаем за меньшую окружность
|
||
if ((pShading->GetExtendFirst() && dFirstRad <= dSecondRad) || (pShading->GetExtendSecond() && dSecondRad < dFirstRad))
|
||
{
|
||
if (dFirstRad <= dSecondRad)
|
||
{
|
||
dTA = dT0;
|
||
dRadA = dFirstRad;
|
||
dXA = dFirstX;
|
||
dYA = dFirstY;
|
||
}
|
||
else
|
||
{
|
||
dTA = dT1;
|
||
dRadA = dSecondRad;
|
||
dXA = dSecondX;
|
||
dYA = dSecondY;
|
||
}
|
||
pShading->GetColor(dTA, &oColorA);
|
||
m_pGState->SetFillColor(&oColorA);
|
||
m_pOut->UpdateFillColor(m_pGState);
|
||
m_pGState->MoveTo(dXA + dRadA, dYA);
|
||
for (nCounter = 1; nCounter < nStepsCount; ++nCounter)
|
||
{
|
||
dAngle = ((double)nCounter / (double)nStepsCount) * 2 * M_PI;
|
||
m_pGState->LineTo(dXA + dRadA * cos(dAngle), dYA + dRadA * sin(dAngle));
|
||
}
|
||
m_pGState->ClosePath();
|
||
m_pOut->Fill(m_pGState);
|
||
m_pGState->ClearPath();
|
||
}
|
||
|
||
// Продолжаем за большую окружность
|
||
if ((pShading->GetExtendFirst() && dFirstRad > dSecondRad) || (pShading->GetExtendSecond() && dSecondRad >= dFirstRad))
|
||
{
|
||
if (dFirstRad > dSecondRad)
|
||
{
|
||
dTA = dT0;
|
||
dRadA = dFirstRad;
|
||
dXA = dFirstX;
|
||
dYA = dFirstY;
|
||
}
|
||
else
|
||
{
|
||
dTA = dT1;
|
||
dRadA = dSecondRad;
|
||
dXA = dSecondX;
|
||
dYA = dSecondY;
|
||
}
|
||
pShading->GetColor(dTA, &oColorA);
|
||
m_pGState->SetFillColor(&oColorA);
|
||
m_pOut->UpdateFillColor(m_pGState);
|
||
m_pGState->MoveTo(dXMin, dYMin);
|
||
m_pGState->LineTo(dXMin, dYMax);
|
||
m_pGState->LineTo(dXMax, dYMax);
|
||
m_pGState->LineTo(dXMax, dYMin);
|
||
m_pGState->ClosePath();
|
||
m_pGState->MoveTo(dXA + dRadA, dYA);
|
||
for (nCounter = 1; nCounter < nStepsCount; ++nCounter)
|
||
{
|
||
dAngle = ((double)nCounter / (double)nStepsCount) * 2 * M_PI;
|
||
m_pGState->LineTo(dXA + dRadA * cos(dAngle), dYA + dRadA * sin(dAngle));
|
||
}
|
||
m_pGState->ClosePath();
|
||
m_pOut->Fill(m_pGState);
|
||
m_pGState->ClearPath();
|
||
}
|
||
}
|
||
}
|
||
|
||
void Graphics::DoGouraudTriangleShadingFill(GrGouraudTriangleShading *pShading)
|
||
{
|
||
double dAx, dAy, dBx, dBy, dCx, dCy;
|
||
GrColor oColorA, oColorB, oColorC;
|
||
for (int nIndex = 0; nIndex < pShading->GetTrianglesCount(); ++nIndex)
|
||
{
|
||
pShading->GetTriangle(nIndex, &dAx, &dAy, &oColorA, &dBx, &dBy, &oColorB, &dCx, &dCy, &oColorC);
|
||
GouraudFillTriangle(dAx, dAy, &oColorA, dBx, dBy, &oColorB, dCx, dCy, &oColorC, pShading->GetColorSpace()->GetComponentsCount(), 0);
|
||
}
|
||
}
|
||
|
||
void Graphics::GouraudFillTriangle(double dAx, double dAy, GrColor *pColorA, double dBx, double dBy, GrColor *pColorB, double dCx, double dCy, GrColor *pColorC, int nComponentsCount, int nDepth)
|
||
{
|
||
if (m_pOut->IsStopped())
|
||
return;
|
||
|
||
double dABx, dABy, dBCx, dBCy, dACx, dACy;
|
||
GrColor oColorAB, oColorBC, oColorAC;
|
||
int nIndex;
|
||
|
||
for (nIndex = 0; nIndex < nComponentsCount; ++nIndex)
|
||
{
|
||
if (abs(pColorA->arrComp[nIndex] - pColorB->arrComp[nIndex]) > gouraudColorDelta || abs(pColorB->arrComp[nIndex] - pColorC->arrComp[nIndex]) > gouraudColorDelta)
|
||
{
|
||
break;
|
||
}
|
||
}
|
||
if (nIndex == nComponentsCount || nDepth == gouraudMaxDepth)
|
||
{
|
||
m_pGState->SetFillColor(pColorA);
|
||
m_pOut->UpdateFillColor(m_pGState);
|
||
m_pGState->MoveTo(dAx, dAy);
|
||
m_pGState->LineTo(dBx, dBy);
|
||
m_pGState->LineTo(dCx, dCy);
|
||
m_pGState->ClosePath();
|
||
m_pOut->Fill(m_pGState);
|
||
m_pGState->ClearPath();
|
||
}
|
||
else
|
||
{
|
||
dABx = 0.5 * (dAx + dBx);
|
||
dABy = 0.5 * (dAy + dBy);
|
||
dBCx = 0.5 * (dBx + dCx);
|
||
dBCy = 0.5 * (dBy + dCy);
|
||
dACx = 0.5 * (dCx + dAx);
|
||
dACy = 0.5 * (dCy + dAy);
|
||
for (nIndex = 0; nIndex < nComponentsCount; ++nIndex)
|
||
{
|
||
oColorAB.arrComp[nIndex] = (pColorA->arrComp[nIndex] + pColorB->arrComp[nIndex]) / 2;
|
||
oColorBC.arrComp[nIndex] = (pColorB->arrComp[nIndex] + pColorC->arrComp[nIndex]) / 2;
|
||
oColorAC.arrComp[nIndex] = (pColorC->arrComp[nIndex] + pColorA->arrComp[nIndex]) / 2;
|
||
}
|
||
GouraudFillTriangle(dAx, dAy, pColorA, dABx, dABy, &oColorAB, dACx, dACy, &oColorAC, nComponentsCount, nDepth + 1);
|
||
GouraudFillTriangle(dABx, dABy, &oColorAB, dBx, dBy, pColorB, dBCx, dBCy, &oColorBC, nComponentsCount, nDepth + 1);
|
||
GouraudFillTriangle(dABx, dABy, &oColorAB, dBCx, dBCy, &oColorBC, dACx, dACy, &oColorAC, nComponentsCount, nDepth + 1);
|
||
GouraudFillTriangle(dACx, dACy, &oColorAC, dBCx, dBCy, &oColorBC, dCx, dCy, pColorC, nComponentsCount, nDepth + 1);
|
||
}
|
||
}
|
||
|
||
void Graphics::DoPatchMeshShadingFill(GrPatchMeshShading *pShading)
|
||
{
|
||
// Устанавливаем цветовое пространство
|
||
m_pGState->SetStrokeColorSpace(pShading->GetColorSpace()->Copy());
|
||
m_pOut->UpdateStrokeColorSpace(m_pGState);
|
||
|
||
int nStart;
|
||
|
||
if (pShading->GetPatchesCount() > 128)
|
||
{
|
||
nStart = 3;
|
||
}
|
||
else if (pShading->GetPatchesCount() > 64)
|
||
{
|
||
nStart = 2;
|
||
}
|
||
else if (pShading->GetPatchesCount() > 16)
|
||
{
|
||
nStart = 1;
|
||
}
|
||
else
|
||
{
|
||
nStart = 0;
|
||
}
|
||
for (int nIndex = 0; nIndex < pShading->GetPatchesCount(); ++nIndex)
|
||
{
|
||
MeshFillPatch(pShading->GetPatch(nIndex), pShading->GetColorSpace()->GetComponentsCount(), nStart);
|
||
}
|
||
}
|
||
|
||
void Graphics::MeshFillPatch(GrPatch *pPatch, int nComponentsCount, int nDepth)
|
||
{
|
||
if (m_pOut->IsStopped())
|
||
return;
|
||
|
||
GrPatch oPatch00, oPatch01, oPatch10, oPatch11;
|
||
double arrX[4][8], arrY[4][8];
|
||
double dMidX, dMidY;
|
||
int nIndex;
|
||
|
||
for (nIndex = 0; nIndex < nComponentsCount; ++nIndex)
|
||
{
|
||
if (abs(pPatch->arrColor[0][0].arrComp[nIndex] - pPatch->arrColor[0][1].arrComp[nIndex]) > patchColorDelta ||
|
||
abs(pPatch->arrColor[0][1].arrComp[nIndex] - pPatch->arrColor[1][1].arrComp[nIndex]) > patchColorDelta ||
|
||
abs(pPatch->arrColor[1][1].arrComp[nIndex] - pPatch->arrColor[1][0].arrComp[nIndex]) > patchColorDelta ||
|
||
abs(pPatch->arrColor[1][0].arrComp[nIndex] - pPatch->arrColor[0][0].arrComp[nIndex]) > patchColorDelta
|
||
)
|
||
{
|
||
break;
|
||
}
|
||
}
|
||
if (nIndex == nComponentsCount || nDepth == patchMaxDepth)
|
||
{
|
||
m_pGState->SetFillColor(&pPatch->arrColor[0][0]);
|
||
m_pGState->SetStrokeColor(&pPatch->arrColor[0][0]);
|
||
m_pOut->UpdateFillColor(m_pGState);
|
||
m_pOut->UpdateStrokeColor(m_pGState);
|
||
m_pGState->MoveTo(pPatch->arrX[0][0], pPatch->arrY[0][0]);
|
||
m_pGState->CurveTo(pPatch->arrX[0][1], pPatch->arrY[0][1], pPatch->arrX[0][2], pPatch->arrY[0][2], pPatch->arrX[0][3], pPatch->arrY[0][3]);
|
||
m_pGState->CurveTo(pPatch->arrX[1][3], pPatch->arrY[1][3], pPatch->arrX[2][3], pPatch->arrY[2][3], pPatch->arrX[3][3], pPatch->arrY[3][3]);
|
||
m_pGState->CurveTo(pPatch->arrX[3][2], pPatch->arrY[3][2], pPatch->arrX[3][1], pPatch->arrY[3][1], pPatch->arrX[3][0], pPatch->arrY[3][0]);
|
||
m_pGState->CurveTo(pPatch->arrX[2][0], pPatch->arrY[2][0], pPatch->arrX[1][0], pPatch->arrY[1][0], pPatch->arrX[0][0], pPatch->arrY[0][0]);
|
||
m_pGState->ClosePath();
|
||
m_pOut->FillStroke(m_pGState);
|
||
m_pGState->ClearPath();
|
||
}
|
||
else
|
||
{
|
||
for (nIndex = 0; nIndex < 4; ++nIndex)
|
||
{
|
||
arrX[nIndex][0] = pPatch->arrX[nIndex][0];
|
||
arrY[nIndex][0] = pPatch->arrY[nIndex][0];
|
||
arrX[nIndex][1] = 0.5 * (pPatch->arrX[nIndex][0] + pPatch->arrX[nIndex][1]);
|
||
arrY[nIndex][1] = 0.5 * (pPatch->arrY[nIndex][0] + pPatch->arrY[nIndex][1]);
|
||
dMidX = 0.5 * (pPatch->arrX[nIndex][1] + pPatch->arrX[nIndex][2]);
|
||
dMidY = 0.5 * (pPatch->arrY[nIndex][1] + pPatch->arrY[nIndex][2]);
|
||
arrX[nIndex][6] = 0.5 * (pPatch->arrX[nIndex][2] + pPatch->arrX[nIndex][3]);
|
||
arrY[nIndex][6] = 0.5 * (pPatch->arrY[nIndex][2] + pPatch->arrY[nIndex][3]);
|
||
arrX[nIndex][2] = 0.5 * (arrX[nIndex][1] + dMidX);
|
||
arrY[nIndex][2] = 0.5 * (arrY[nIndex][1] + dMidY);
|
||
arrX[nIndex][5] = 0.5 * (dMidX + arrX[nIndex][6]);
|
||
arrY[nIndex][5] = 0.5 * (dMidY + arrY[nIndex][6]);
|
||
arrX[nIndex][3] = arrX[nIndex][4] = 0.5 * (arrX[nIndex][2] + arrX[nIndex][5]);
|
||
arrY[nIndex][3] = arrY[nIndex][4] = 0.5 * (arrY[nIndex][2] + arrY[nIndex][5]);
|
||
arrX[nIndex][7] = pPatch->arrX[nIndex][3];
|
||
arrY[nIndex][7] = pPatch->arrY[nIndex][3];
|
||
}
|
||
for (nIndex = 0; nIndex < 4; ++nIndex)
|
||
{
|
||
oPatch00.arrX[0][nIndex] = arrX[0][nIndex];
|
||
oPatch00.arrY[0][nIndex] = arrY[0][nIndex];
|
||
oPatch00.arrX[1][nIndex] = 0.5 * (arrX[0][nIndex] + arrX[1][nIndex]);
|
||
oPatch00.arrY[1][nIndex] = 0.5 * (arrY[0][nIndex] + arrY[1][nIndex]);
|
||
dMidX = 0.5 * (arrX[1][nIndex] + arrX[2][nIndex]);
|
||
dMidY = 0.5 * (arrY[1][nIndex] + arrY[2][nIndex]);
|
||
oPatch10.arrX[2][nIndex] = 0.5 * (arrX[2][nIndex] + arrX[3][nIndex]);
|
||
oPatch10.arrY[2][nIndex] = 0.5 * (arrY[2][nIndex] + arrY[3][nIndex]);
|
||
oPatch00.arrX[2][nIndex] = 0.5 * (oPatch00.arrX[1][nIndex] + dMidX);
|
||
oPatch00.arrY[2][nIndex] = 0.5 * (oPatch00.arrY[1][nIndex] + dMidY);
|
||
oPatch10.arrX[1][nIndex] = 0.5 * (dMidX + oPatch10.arrX[2][nIndex]);
|
||
oPatch10.arrY[1][nIndex] = 0.5 * (dMidY + oPatch10.arrY[2][nIndex]);
|
||
oPatch00.arrX[3][nIndex] = 0.5 * (oPatch00.arrX[2][nIndex] + oPatch10.arrX[1][nIndex]);
|
||
oPatch00.arrY[3][nIndex] = 0.5 * (oPatch00.arrY[2][nIndex] + oPatch10.arrY[1][nIndex]);
|
||
oPatch10.arrX[0][nIndex] = oPatch00.arrX[3][nIndex];
|
||
oPatch10.arrY[0][nIndex] = oPatch00.arrY[3][nIndex];
|
||
oPatch10.arrX[3][nIndex] = arrX[3][nIndex];
|
||
oPatch10.arrY[3][nIndex] = arrY[3][nIndex];
|
||
}
|
||
for (nIndex = 4; nIndex < 8; ++nIndex)
|
||
{
|
||
oPatch01.arrX[0][nIndex - 4] = arrX[0][nIndex];
|
||
oPatch01.arrY[0][nIndex - 4] = arrY[0][nIndex];
|
||
oPatch01.arrX[1][nIndex - 4] = 0.5 * (arrX[0][nIndex] + arrX[1][nIndex]);
|
||
oPatch01.arrY[1][nIndex - 4] = 0.5 * (arrY[0][nIndex] + arrY[1][nIndex]);
|
||
dMidX = 0.5 * (arrX[1][nIndex] + arrX[2][nIndex]);
|
||
dMidY = 0.5 * (arrY[1][nIndex] + arrY[2][nIndex]);
|
||
oPatch11.arrX[2][nIndex - 4] = 0.5 * (arrX[2][nIndex] + arrX[3][nIndex]);
|
||
oPatch11.arrY[2][nIndex - 4] = 0.5 * (arrY[2][nIndex] + arrY[3][nIndex]);
|
||
oPatch01.arrX[2][nIndex - 4] = 0.5 * (oPatch01.arrX[1][nIndex - 4] + dMidX);
|
||
oPatch01.arrY[2][nIndex - 4] = 0.5 * (oPatch01.arrY[1][nIndex - 4] + dMidY);
|
||
oPatch11.arrX[1][nIndex - 4] = 0.5 * (dMidX + oPatch11.arrX[2][nIndex - 4]);
|
||
oPatch11.arrY[1][nIndex - 4] = 0.5 * (dMidY + oPatch11.arrY[2][nIndex - 4]);
|
||
oPatch01.arrX[3][nIndex - 4] = 0.5 * (oPatch01.arrX[2][nIndex - 4] + oPatch11.arrX[1][nIndex - 4]);
|
||
oPatch01.arrY[3][nIndex - 4] = 0.5 * (oPatch01.arrY[2][nIndex - 4] + oPatch11.arrY[1][nIndex - 4]);
|
||
oPatch11.arrX[0][nIndex - 4] = oPatch01.arrX[3][nIndex - 4];
|
||
oPatch11.arrY[0][nIndex - 4] = oPatch01.arrY[3][nIndex - 4];
|
||
oPatch11.arrX[3][nIndex - 4] = arrX[3][nIndex];
|
||
oPatch11.arrY[3][nIndex - 4] = arrY[3][nIndex];
|
||
}
|
||
for (nIndex = 0; nIndex < nComponentsCount; ++nIndex)
|
||
{
|
||
oPatch00.arrColor[0][0].arrComp[nIndex] = pPatch->arrColor[0][0].arrComp[nIndex];
|
||
oPatch00.arrColor[0][1].arrComp[nIndex] = (pPatch->arrColor[0][0].arrComp[nIndex] + pPatch->arrColor[0][1].arrComp[nIndex]) / 2;
|
||
oPatch01.arrColor[0][0].arrComp[nIndex] = oPatch00.arrColor[0][1].arrComp[nIndex];
|
||
oPatch01.arrColor[0][1].arrComp[nIndex] = pPatch->arrColor[0][1].arrComp[nIndex];
|
||
oPatch01.arrColor[1][1].arrComp[nIndex] = (pPatch->arrColor[0][1].arrComp[nIndex] + pPatch->arrColor[1][1].arrComp[nIndex]) / 2;
|
||
oPatch11.arrColor[0][1].arrComp[nIndex] = oPatch01.arrColor[1][1].arrComp[nIndex];
|
||
oPatch11.arrColor[1][1].arrComp[nIndex] = pPatch->arrColor[1][1].arrComp[nIndex];
|
||
oPatch11.arrColor[1][0].arrComp[nIndex] = (pPatch->arrColor[1][1].arrComp[nIndex] + pPatch->arrColor[1][0].arrComp[nIndex]) / 2;
|
||
oPatch10.arrColor[1][1].arrComp[nIndex] = oPatch11.arrColor[1][0].arrComp[nIndex];
|
||
oPatch10.arrColor[1][0].arrComp[nIndex] = pPatch->arrColor[1][0].arrComp[nIndex];
|
||
oPatch10.arrColor[0][0].arrComp[nIndex] = (pPatch->arrColor[1][0].arrComp[nIndex] + pPatch->arrColor[0][0].arrComp[nIndex]) / 2;
|
||
oPatch00.arrColor[1][0].arrComp[nIndex] = oPatch10.arrColor[0][0].arrComp[nIndex];
|
||
oPatch00.arrColor[1][1].arrComp[nIndex] = (oPatch00.arrColor[1][0].arrComp[nIndex] + oPatch01.arrColor[1][1].arrComp[nIndex]) / 2;
|
||
oPatch01.arrColor[1][0].arrComp[nIndex] = oPatch00.arrColor[1][1].arrComp[nIndex];
|
||
oPatch11.arrColor[0][0].arrComp[nIndex] = oPatch00.arrColor[1][1].arrComp[nIndex];
|
||
oPatch10.arrColor[0][1].arrComp[nIndex] = oPatch00.arrColor[1][1].arrComp[nIndex];
|
||
}
|
||
MeshFillPatch(&oPatch00, nComponentsCount, nDepth + 1);
|
||
MeshFillPatch(&oPatch10, nComponentsCount, nDepth + 1);
|
||
MeshFillPatch(&oPatch01, nComponentsCount, nDepth + 1);
|
||
MeshFillPatch(&oPatch11, nComponentsCount, nDepth + 1);
|
||
}
|
||
}
|
||
|
||
void Graphics::DoEndPath()
|
||
{
|
||
if (m_pGState->IsCurPoint() && m_eClip != clipNone)
|
||
{
|
||
m_pGState->Clip();
|
||
if (m_eClip == clipNormal)
|
||
{
|
||
m_pGState->GetClip()->ClipToPath(m_pGState->GetPath()->Copy(), m_pGState->GetCTM(), false);
|
||
|
||
if (m_pOut->UseClipTo())
|
||
m_pOut->ClipToPath(m_pGState, m_pGState->GetPath(), m_pGState->GetCTM(), false);
|
||
else
|
||
m_pOut->Clip(m_pGState);
|
||
}
|
||
else
|
||
{
|
||
m_pGState->GetClip()->ClipToPath(m_pGState->GetPath()->Copy(), m_pGState->GetCTM(), true);
|
||
|
||
if (m_pOut->UseClipTo())
|
||
m_pOut->ClipToPath(m_pGState, m_pGState->GetPath(), m_pGState->GetCTM(), true);
|
||
else
|
||
m_pOut->EoClip(m_pGState);
|
||
}
|
||
}
|
||
m_eClip = clipNone;
|
||
m_pGState->ClearPath();
|
||
}
|
||
|
||
//-------------------------------------------------------------------------------------------------------------------------------
|
||
// Clipping paths
|
||
//-------------------------------------------------------------------------------------------------------------------------------
|
||
|
||
// W
|
||
void Graphics::OperatorClip(Object arrArguments[], int nArgumentsCount)
|
||
{
|
||
m_eClip = clipNormal;
|
||
}
|
||
|
||
// W*
|
||
void Graphics::OperatorEOClip(Object arrArguments[], int nArgumentsCount)
|
||
{
|
||
m_eClip = clipEO;
|
||
}
|
||
|
||
//-------------------------------------------------------------------------------------------------------------------------------
|
||
// Text objects
|
||
//-------------------------------------------------------------------------------------------------------------------------------
|
||
|
||
// BT
|
||
void Graphics::OperatorBeginText(Object arrArguments[], int nArgumentsCount)
|
||
{
|
||
m_pOut->BegintTextObject(m_pGState);
|
||
m_pGState->SetTextMatrix(1, 0, 0, 1, 0, 0);
|
||
m_pGState->TextMoveTo(0, 0);
|
||
m_pOut->UpdateTextMatrix(m_pGState);
|
||
m_pOut->UpdateTextPos(m_pGState);
|
||
m_bFontChanged = true;
|
||
}
|
||
|
||
// ET
|
||
void Graphics::OperatorEndText(Object arrArguments[], int nArgumentsCount)
|
||
{
|
||
m_pOut->EndTextObject(m_pGState);
|
||
}
|
||
|
||
//-------------------------------------------------------------------------------------------------------------------------------
|
||
// Text state
|
||
//-------------------------------------------------------------------------------------------------------------------------------
|
||
|
||
// Tc
|
||
void Graphics::OperatorSetCharSpacing(Object arrArguments[], int nArgumentsCount)
|
||
{
|
||
m_pGState->SetCharSpace(arrArguments[0].GetNum());
|
||
m_pOut->UpdateCharSpace(m_pGState);
|
||
}
|
||
|
||
// Tf
|
||
void Graphics::OperatorSetFont(Object arrArguments[], int nArgumentsCount)
|
||
{
|
||
GrFont *pFont = NULL;
|
||
|
||
if (!(pFont = m_pResources->LookupFont(arrArguments[0].GetName())))
|
||
{
|
||
return;
|
||
}
|
||
#ifdef _DEBUG
|
||
if (m_pDumpFile)
|
||
{
|
||
::fprintf(m_pDumpFile, " Font:\n");
|
||
::fprintf(m_pDumpFile, " Tag = %s\n", pFont->GetTag()->GetBuffer());
|
||
::fprintf(m_pDumpFile, " Name = %s\n", pFont->GetBaseName() ? pFont->GetBaseName()->GetBuffer() : "???");
|
||
::fprintf(m_pDumpFile, " %g\n", arrArguments[1].GetNum());
|
||
::fflush(m_pDumpFile);
|
||
}
|
||
#endif
|
||
m_pGState->SetFont(pFont, arrArguments[1].GetNum());
|
||
m_bFontChanged = true;
|
||
}
|
||
|
||
// TL
|
||
void Graphics::OperatorSetTextLeading(Object arrArguments[], int nArgumentsCount)
|
||
{
|
||
m_pGState->SetLeading(arrArguments[0].GetNum());
|
||
}
|
||
|
||
// Tr
|
||
void Graphics::OperatorSetTextRender(Object arrArguments[], int nArgumentsCount)
|
||
{
|
||
m_pGState->SetRenderMode(arrArguments[0].GetInt());
|
||
m_pOut->UpdateRender(m_pGState);
|
||
}
|
||
|
||
// Ts
|
||
void Graphics::OperatorSetTextRise(Object arrArguments[], int nArgumentsCount)
|
||
{
|
||
m_pGState->SetRise(arrArguments[0].GetNum());
|
||
m_pOut->UpdateRise(m_pGState);
|
||
}
|
||
|
||
// Tw
|
||
void Graphics::OperatorSetWordSpacing(Object arrArguments[], int nArgumentsCount)
|
||
{
|
||
m_pGState->SetWordSpace(arrArguments[0].GetNum());
|
||
m_pOut->UpdateWordSpace(m_pGState);
|
||
}
|
||
|
||
// Tz
|
||
void Graphics::OperatorSetHorizScaling(Object arrArguments[], int nArgumentsCount)
|
||
{
|
||
m_pGState->SetHorizScaling(arrArguments[0].GetNum());
|
||
m_pOut->UpdateHorizScaling(m_pGState);
|
||
m_bFontChanged = true;
|
||
}
|
||
|
||
//-------------------------------------------------------------------------------------------------------------------------------
|
||
// Text positioning
|
||
//-------------------------------------------------------------------------------------------------------------------------------
|
||
|
||
// Td
|
||
void Graphics::OperatorTextMove(Object arrArguments[], int nArgumentsCount)
|
||
{
|
||
double dDX = m_pGState->GetTextLineX() + arrArguments[0].GetNum();
|
||
double dDy = m_pGState->GetTextLineY() + arrArguments[1].GetNum();
|
||
m_pGState->TextMoveTo(dDX, dDy);
|
||
m_pOut->UpdateTextPos(m_pGState);
|
||
}
|
||
|
||
// TD
|
||
void Graphics::OperatorTextMoveSet(Object arrArguments[], int nArgumentsCount)
|
||
{
|
||
double dDx = m_pGState->GetTextLineX() + arrArguments[0].GetNum();
|
||
double dDy = arrArguments[1].GetNum();
|
||
m_pGState->SetLeading(-dDy);
|
||
dDy += m_pGState->GetTextLineY();
|
||
m_pGState->TextMoveTo(dDx, dDy);
|
||
m_pOut->UpdateTextPos(m_pGState);
|
||
}
|
||
|
||
// Tm
|
||
void Graphics::OperatorSetTextMatrix(Object arrArguments[], int nArgumentsCount)
|
||
{
|
||
m_pGState->SetTextMatrix(arrArguments[0].GetNum(), arrArguments[1].GetNum(), arrArguments[2].GetNum(), arrArguments[3].GetNum(), arrArguments[4].GetNum(), arrArguments[5].GetNum());
|
||
m_pGState->TextMoveTo(0, 0);
|
||
m_pOut->UpdateTextMatrix(m_pGState);
|
||
m_pOut->UpdateTextPos(m_pGState);
|
||
m_bFontChanged = true;
|
||
}
|
||
|
||
// T*
|
||
void Graphics::OperatorTextNextLine(Object arrArguments[], int nArgumentsCount)
|
||
{
|
||
double dDx = m_pGState->GetTextLineX();
|
||
double dDy = m_pGState->GetTextLineY() - m_pGState->GetLeading();
|
||
m_pGState->TextMoveTo(dDx, dDy);
|
||
m_pOut->UpdateTextPos(m_pGState);
|
||
}
|
||
|
||
//-------------------------------------------------------------------------------------------------------------------------------
|
||
// Text showing
|
||
//-------------------------------------------------------------------------------------------------------------------------------
|
||
|
||
// Tj
|
||
void Graphics::OperatorShowText(Object arrArguments[], int nArgumentsCount)
|
||
{
|
||
if (!m_pGState->GetFont())
|
||
{
|
||
// TO DO: Error "No font in show")
|
||
return;
|
||
}
|
||
if (m_bFontChanged)
|
||
{
|
||
m_pOut->UpdateFont(m_pGState);
|
||
m_bFontChanged = false;
|
||
}
|
||
m_pOut->BeginStringOperator(m_pGState);
|
||
DoShowText(arrArguments[0].GetString());
|
||
m_pOut->EndStringOperator(m_pGState);
|
||
}
|
||
|
||
// '
|
||
void Graphics::OperatorMoveShowText(Object arrArguments[], int nArgumentsCount)
|
||
{
|
||
if (!m_pGState->GetFont())
|
||
{
|
||
// TO DO: Error "No font in move/show"
|
||
return;
|
||
}
|
||
if (m_bFontChanged)
|
||
{
|
||
m_pOut->UpdateFont(m_pGState);
|
||
m_bFontChanged = false;
|
||
}
|
||
double dDx = m_pGState->GetTextLineX();
|
||
double dDy = m_pGState->GetTextLineY() - m_pGState->GetLeading();
|
||
m_pGState->TextMoveTo(dDx, dDy);
|
||
m_pOut->UpdateTextPos(m_pGState);
|
||
m_pOut->BeginStringOperator(m_pGState);
|
||
DoShowText(arrArguments[0].GetString());
|
||
m_pOut->EndStringOperator(m_pGState);
|
||
}
|
||
|
||
// \
|
||
void Graphics::OperatorMoveSetShowText(Object arrArguments[], int nArgumentsCount)
|
||
{
|
||
if (m_pGState->GetFont())
|
||
{
|
||
// TO DO: Error "No font in move/set/show"
|
||
return;
|
||
}
|
||
if (m_bFontChanged)
|
||
{
|
||
m_pOut->UpdateFont(m_pGState);
|
||
m_bFontChanged = false;
|
||
}
|
||
m_pGState->SetWordSpace(arrArguments[0].GetNum());
|
||
m_pGState->SetCharSpace(arrArguments[1].GetNum());
|
||
|
||
double dDx = m_pGState->GetTextLineX();
|
||
double dDy = m_pGState->GetTextLineY() - m_pGState->GetLeading();
|
||
|
||
m_pGState->TextMoveTo(dDx, dDy);
|
||
m_pOut->UpdateWordSpace(m_pGState);
|
||
m_pOut->UpdateCharSpace(m_pGState);
|
||
m_pOut->UpdateTextPos(m_pGState);
|
||
m_pOut->BeginStringOperator(m_pGState);
|
||
DoShowText(arrArguments[2].GetString());
|
||
m_pOut->EndStringOperator(m_pGState);
|
||
}
|
||
|
||
// TJ
|
||
void Graphics::OperatorShowSpaceText(Object arrArguments[], int nArgumentsCount)
|
||
{
|
||
if (!m_pGState->GetFont())
|
||
{
|
||
// TO DO: Error "No font in show/space"
|
||
return;
|
||
}
|
||
if (m_bFontChanged)
|
||
{
|
||
m_pOut->UpdateFont(m_pGState);
|
||
m_bFontChanged = false;
|
||
}
|
||
m_pOut->BeginStringOperator(m_pGState);
|
||
int nWMode = m_pGState->GetFont()->GetWMode();
|
||
Array *pArray = arrArguments[0].GetArray();
|
||
|
||
for (int nIndex = 0; nIndex < pArray->GetCount(); ++nIndex)
|
||
{
|
||
Object oTemp;
|
||
pArray->Get(nIndex, &oTemp);
|
||
if (oTemp.IsNum())
|
||
{
|
||
if (nWMode)
|
||
{
|
||
m_pGState->TextShift(0, -oTemp.GetNum() * 0.001 * fabs(m_pGState->GetFontSize()));
|
||
}
|
||
else
|
||
{
|
||
m_pGState->TextShift(-oTemp.GetNum() * 0.001 * fabs(m_pGState->GetFontSize()), 0);
|
||
}
|
||
m_pOut->UpdateTextShift(m_pGState, oTemp.GetNum());
|
||
}
|
||
else if (oTemp.IsString())
|
||
{
|
||
DoShowText(oTemp.GetString());
|
||
}
|
||
else
|
||
{
|
||
// TO DO: Error "Element of show/space array must be number or string"
|
||
}
|
||
oTemp.Free();
|
||
}
|
||
m_pOut->EndStringOperator(m_pGState);
|
||
}
|
||
|
||
//-------------------------------------------------------------------------------------------------------------------------------
|
||
void Graphics::DoShowText(StringExt *seString)
|
||
{
|
||
CharCode nCode;
|
||
Unicode arrUnicode[8];
|
||
double x, y, dx, dy, dx2, dy2, tdx, tdy;
|
||
double originX, originY, tOriginX, tOriginY;
|
||
|
||
char *pBuffer;
|
||
int nLen, nCurLen, uLen;
|
||
|
||
GrFont *pFont = m_pGState->GetFont();
|
||
int nWMode = pFont->GetWMode();
|
||
|
||
if (m_pOut->UseDrawChar())
|
||
{
|
||
m_pOut->BeginString(m_pGState, seString);
|
||
}
|
||
|
||
// Обработка шрифта Type 3
|
||
if (pFont->GetType() == fontType3 && m_pOut->InterpretType3Chars())
|
||
{
|
||
double *pMatrix = m_pGState->GetCTM();
|
||
double pOldCTM[6], pNewCTM[6];
|
||
for (int nIndex = 0; nIndex < 6; ++nIndex)
|
||
{
|
||
pOldCTM[nIndex] = pMatrix[nIndex];
|
||
}
|
||
pMatrix = m_pGState->GetTextMatrix();
|
||
pNewCTM[0] = pMatrix[0] * pOldCTM[0] + pMatrix[1] * pOldCTM[2];
|
||
pNewCTM[1] = pMatrix[0] * pOldCTM[1] + pMatrix[1] * pOldCTM[3];
|
||
pNewCTM[2] = pMatrix[2] * pOldCTM[0] + pMatrix[3] * pOldCTM[2];
|
||
pNewCTM[3] = pMatrix[2] * pOldCTM[1] + pMatrix[3] * pOldCTM[3];
|
||
|
||
pMatrix = pFont->GetFontMatrix();
|
||
pNewCTM[0] = pMatrix[0] * pNewCTM[0] + pMatrix[1] * pNewCTM[2];
|
||
pNewCTM[1] = pMatrix[0] * pNewCTM[1] + pMatrix[1] * pNewCTM[3];
|
||
pNewCTM[2] = pMatrix[2] * pNewCTM[0] + pMatrix[3] * pNewCTM[2];
|
||
pNewCTM[3] = pMatrix[2] * pNewCTM[1] + pMatrix[3] * pNewCTM[3];
|
||
|
||
pNewCTM[0] *= m_pGState->GetFontSize();
|
||
pNewCTM[1] *= m_pGState->GetFontSize();
|
||
pNewCTM[2] *= m_pGState->GetFontSize();
|
||
pNewCTM[3] *= m_pGState->GetFontSize();
|
||
pNewCTM[0] *= m_pGState->GetHorizScaling();
|
||
pNewCTM[2] *= m_pGState->GetHorizScaling();
|
||
|
||
double dRiseX, dRiseY;
|
||
m_pGState->TextTransformDelta(0, m_pGState->GetRise(), &dRiseX, &dRiseY);
|
||
double dCurX = m_pGState->GetCurX();
|
||
double dCurY = m_pGState->GetCurY();
|
||
double dLineX = m_pGState->GetTextLineX();
|
||
double dLineY = m_pGState->GetTextLineY();
|
||
Parser *pOldParser = m_pParser;
|
||
pBuffer = seString->GetBuffer();
|
||
nLen = seString->GetLength();
|
||
while (nLen > 0)
|
||
{
|
||
nCurLen = pFont->GetNextChar(pBuffer, nLen, &nCode, arrUnicode, (int)(sizeof(arrUnicode) / sizeof(Unicode)), &uLen, &dx, &dy, &originX, &originY);
|
||
dx = dx * m_pGState->GetFontSize() + m_pGState->GetCharSpace();
|
||
if (nCurLen == 1 && *pBuffer == ' ')
|
||
{
|
||
dx += m_pGState->GetWordSpace();
|
||
}
|
||
dx *= m_pGState->GetHorizScaling();
|
||
dy *= m_pGState->GetFontSize();
|
||
m_pGState->TextTransformDelta(dx, dy, &tdx, &tdy);
|
||
m_pGState->Transform(dCurX + dRiseX, dCurY + dRiseY, &x, &y);
|
||
SaveGState();
|
||
m_pGState->SetCTM(pNewCTM[0], pNewCTM[1], pNewCTM[2], pNewCTM[3], x, y);
|
||
//Значения CTM concat здесь неправильные (но они никогда не используются)
|
||
m_pOut->UpdateCTM(m_pGState, 1, 0, 0, 1, 0, 0);
|
||
if (!m_pOut->BeginType3Char(m_pGState, dCurX + dRiseX, dCurY + dRiseY, tdx, tdy, nCode, arrUnicode, uLen))
|
||
{
|
||
Object oCharProc;
|
||
((Gr8BitFont *)pFont)->GetCharProc(nCode, &oCharProc);
|
||
Dict *pResourcesDict;
|
||
if ((pResourcesDict = ((Gr8BitFont *)pFont)->GetResources()))
|
||
{
|
||
PushResources(pResourcesDict);
|
||
}
|
||
if (oCharProc.IsStream())
|
||
{
|
||
Display(&oCharProc, false);
|
||
}
|
||
else
|
||
{
|
||
// TO DO: Error "Missing or bad Type3 CharProc entry"
|
||
}
|
||
m_pOut->EndType3Char(m_pGState);
|
||
if (pResourcesDict)
|
||
{
|
||
PopResources();
|
||
}
|
||
oCharProc.Free();
|
||
}
|
||
RestoreGState();
|
||
// Поскольку функция RestoreGState() не восстанавливает текущей позиции,
|
||
// поэтому нам нужно сохранять и восстанавливать ее самим
|
||
dCurX += tdx;
|
||
dCurY += tdy;
|
||
m_pGState->MoveTo(dCurX, dCurY);
|
||
m_pGState->TextSetPos(dLineX, dLineY);
|
||
pBuffer += nCurLen;
|
||
nLen -= nCurLen;
|
||
}
|
||
m_pParser = pOldParser;
|
||
}
|
||
else if (m_pOut->UseDrawChar())
|
||
{
|
||
double dRiseX, dRiseY;
|
||
m_pGState->TextTransformDelta(0, m_pGState->GetRise(), &dRiseX, &dRiseY);
|
||
pBuffer = seString->GetBuffer();
|
||
nLen = seString->GetLength();
|
||
while (nLen > 0)
|
||
{
|
||
nCurLen = pFont->GetNextChar(pBuffer, nLen, &nCode, arrUnicode, (int)(sizeof(arrUnicode) / sizeof(Unicode)), &uLen, &dx, &dy, &originX, &originY);
|
||
if (nWMode)
|
||
{
|
||
dx *= m_pGState->GetFontSize();
|
||
dy = dy * m_pGState->GetFontSize() + m_pGState->GetCharSpace();
|
||
if (nCurLen == 1 && *pBuffer == ' ')
|
||
{
|
||
dy += m_pGState->GetWordSpace();
|
||
}
|
||
}
|
||
else
|
||
{
|
||
dx = dx * m_pGState->GetFontSize() + m_pGState->GetCharSpace();
|
||
if (nCurLen == 1 && *pBuffer == ' ')
|
||
{
|
||
dx += m_pGState->GetWordSpace();
|
||
}
|
||
dx *= m_pGState->GetHorizScaling();
|
||
dy *= m_pGState->GetFontSize();
|
||
}
|
||
m_pGState->TextTransformDelta(dx, dy, &tdx, &tdy);
|
||
originX *= m_pGState->GetFontSize();
|
||
originY *= m_pGState->GetFontSize();
|
||
m_pGState->TextTransformDelta(originX, originY, &tOriginX, &tOriginY);
|
||
m_pOut->DrawChar(m_pGState, m_pGState->GetCurX() + dRiseX, m_pGState->GetCurY() + dRiseY, tdx, tdy, tOriginX, tOriginY, nCode, nCurLen, arrUnicode, uLen);
|
||
m_pGState->Shift(tdx, tdy);
|
||
pBuffer += nCurLen;
|
||
nLen -= nCurLen;
|
||
}
|
||
}
|
||
else
|
||
{
|
||
dx = dy = 0;
|
||
pBuffer = seString->GetBuffer();
|
||
nLen = seString->GetLength();
|
||
int nCharsCount = 0, nSpacesCount = 0;
|
||
while (nLen > 0)
|
||
{
|
||
nCurLen = pFont->GetNextChar(pBuffer, nLen, &nCode, arrUnicode, (int)(sizeof(arrUnicode) / sizeof(Unicode)), &uLen, &dx2, &dy2, &originX, &originY);
|
||
dx += dx2;
|
||
dy += dy2;
|
||
if (nCurLen == 1 && *pBuffer == ' ')
|
||
{
|
||
++nSpacesCount;
|
||
}
|
||
++nCharsCount;
|
||
pBuffer += nCurLen;
|
||
nLen -= nCurLen;
|
||
}
|
||
if (nWMode)
|
||
{
|
||
dx *= m_pGState->GetFontSize();
|
||
dy = dy * m_pGState->GetFontSize() + nCharsCount * m_pGState->GetCharSpace() + nSpacesCount * m_pGState->GetWordSpace();
|
||
}
|
||
else
|
||
{
|
||
dx = dx * m_pGState->GetFontSize() + nCharsCount * m_pGState->GetCharSpace() + nSpacesCount * m_pGState->GetWordSpace();
|
||
dx *= m_pGState->GetHorizScaling();
|
||
dy *= m_pGState->GetFontSize();
|
||
}
|
||
m_pGState->TextTransformDelta(dx, dy, &tdx, &tdy);
|
||
m_pOut->DrawString(m_pGState, seString);
|
||
m_pGState->Shift(tdx, tdy);
|
||
}
|
||
|
||
if (m_pOut->UseDrawChar())
|
||
{
|
||
m_pOut->EndString(m_pGState);
|
||
}
|
||
|
||
m_nUpdateLevel += 10 * seString->GetLength();
|
||
}
|
||
|
||
//-------------------------------------------------------------------------------------------------------------------------------
|
||
// XObjects
|
||
//-------------------------------------------------------------------------------------------------------------------------------
|
||
|
||
// Do
|
||
void Graphics::OperatorXObject(Object arrArguments[], int nArgumentsCount)
|
||
{
|
||
char *sName = arrArguments[0].GetName();
|
||
Object oXObject;
|
||
if (!m_pResources->LookupXObject(sName, &oXObject))
|
||
{
|
||
return;
|
||
}
|
||
if (!oXObject.IsStream())
|
||
{
|
||
// TO DO: Error "XObject is wrong type"
|
||
oXObject.Free();
|
||
return;
|
||
}
|
||
#if OPI_SUPPORT
|
||
Object oOPI;
|
||
oXObject.streamGetDict()->lookup("OPI", &oOPI);
|
||
if ( oOPI.IsDict() )
|
||
{
|
||
m_pOut->opiBegin( state, oOPI.GetDict() );
|
||
}
|
||
#endif
|
||
Object oSub;
|
||
oXObject.StreamGetDict()->Search("Subtype", &oSub);
|
||
if (oSub.IsName("Image"))
|
||
{
|
||
if (m_pOut->NeedNonText())
|
||
{
|
||
Object oRef;
|
||
m_pResources->LookupAndCopyXObject(sName, &oRef);
|
||
DoImage(&oRef, oXObject.GetStream(), false);
|
||
oRef.Free();
|
||
}
|
||
}
|
||
else if (oSub.IsName("Form"))
|
||
{
|
||
Object oRef;
|
||
m_pResources->LookupAndCopyXObject(sName, &oRef);
|
||
if (m_pOut->UseDrawForm() && oRef.IsRef())
|
||
{
|
||
m_pOut->DrawForm(oRef.GetRef());
|
||
}
|
||
else
|
||
{
|
||
DoForm(&oXObject);
|
||
}
|
||
oRef.Free();
|
||
}
|
||
else if (oSub.IsName("PS"))
|
||
{
|
||
Object oLevel;
|
||
oXObject.StreamGetDict()->Search("Level1", &oLevel);
|
||
m_pOut->PSXObject(oXObject.GetStream(), oLevel.IsStream() ? oLevel.GetStream() : (Stream *)NULL);
|
||
// Добавленно
|
||
oLevel.Free();
|
||
}
|
||
else if (oSub.IsName())
|
||
{
|
||
// TO DO: Error "Unknown XObject subtype"
|
||
}
|
||
else
|
||
{
|
||
// TO DO: Error "XObject subtype is missing or wrong type"
|
||
}
|
||
oSub.Free();
|
||
#if OPI_SUPPORT
|
||
if ( oOPI.IsDict() )
|
||
{
|
||
m_pOut->opiEnd( state, oOPI.GetDict() );
|
||
}
|
||
oOPI.Free();
|
||
#endif
|
||
oXObject.Free();
|
||
}
|
||
|
||
void Graphics::DoImage(Object *pRef, Stream *pStream, bool bInlineImage)
|
||
{
|
||
// Считываем информацию из потока
|
||
int nBitsPerComponent = 0;
|
||
StreamColorSpaceMode eCSMode = streamCSNone;
|
||
pStream->GetImageParams(&nBitsPerComponent, &eCSMode);
|
||
|
||
Dict *pDict = pStream->GetDict();
|
||
|
||
// Width
|
||
Object oDictItem;
|
||
pDict->Search("Width", &oDictItem);
|
||
if (oDictItem.IsNull())
|
||
{
|
||
oDictItem.Free();
|
||
pDict->Search("W", &oDictItem);
|
||
}
|
||
if (!oDictItem.IsInt())
|
||
{
|
||
oDictItem.Free();
|
||
// TO DO: Error "Bad image parameters"
|
||
return;
|
||
}
|
||
int nWidth = oDictItem.GetInt();
|
||
oDictItem.Free();
|
||
|
||
// Height
|
||
pDict->Search("Height", &oDictItem);
|
||
if (oDictItem.IsNull())
|
||
{
|
||
oDictItem.Free();
|
||
pDict->Search("H", &oDictItem);
|
||
}
|
||
if (!oDictItem.IsInt())
|
||
{
|
||
oDictItem.Free();
|
||
// TO DO: Error "Bad image parameters"
|
||
return;
|
||
}
|
||
int nHeight = oDictItem.GetInt();
|
||
oDictItem.Free();
|
||
|
||
// Проверяем: может быть это маска?
|
||
pDict->Search("ImageMask", &oDictItem);
|
||
if (oDictItem.IsNull())
|
||
{
|
||
oDictItem.Free();
|
||
pDict->Search("IM", &oDictItem);
|
||
}
|
||
bool bMask = false;
|
||
if (oDictItem.IsBool())
|
||
{
|
||
bMask = oDictItem.GetBool();
|
||
}
|
||
else if (!oDictItem.IsNull())
|
||
{
|
||
oDictItem.Free();
|
||
// TO DO: Error "Bad image parameters"
|
||
return;
|
||
}
|
||
oDictItem.Free();
|
||
|
||
// BitsPerComponent
|
||
if (nBitsPerComponent == 0)
|
||
{
|
||
pDict->Search("BitsPerComponent", &oDictItem);
|
||
if (oDictItem.IsNull())
|
||
{
|
||
oDictItem.Free();
|
||
pDict->Search("BPC", &oDictItem);
|
||
}
|
||
if (oDictItem.IsInt())
|
||
{
|
||
nBitsPerComponent = oDictItem.GetInt();
|
||
}
|
||
else if (bMask)
|
||
{
|
||
nBitsPerComponent = 1;
|
||
}
|
||
else
|
||
{
|
||
oDictItem.Free();
|
||
// TO DO: Error "Bad image parameters"
|
||
return;
|
||
}
|
||
oDictItem.Free();
|
||
}
|
||
|
||
if (bMask)
|
||
{
|
||
// У маски это значение nBitsPerComponent должно быть 1
|
||
if (nBitsPerComponent != 1)
|
||
{
|
||
// TO DO: Error "Bad image parameters"
|
||
return;
|
||
}
|
||
bool bInvert = false;
|
||
pDict->Search("Decode", &oDictItem);
|
||
if (oDictItem.IsNull())
|
||
{
|
||
oDictItem.Free();
|
||
pDict->Search("D", &oDictItem);
|
||
}
|
||
if (oDictItem.IsArray())
|
||
{
|
||
Object oTemp;
|
||
oDictItem.ArrayGet(0, &oTemp);
|
||
if (oTemp.IsInt() && oTemp.GetInt() == 1)
|
||
bInvert = true;
|
||
oTemp.Free();
|
||
}
|
||
else if (!oDictItem.IsNull())
|
||
{
|
||
oDictItem.Free();
|
||
// TO DO: Error "Bad image parameters"
|
||
return;
|
||
}
|
||
oDictItem.Free();
|
||
|
||
// Рисуем маску
|
||
m_pOut->DrawImageMask(m_pGState, pRef, pStream, nWidth, nHeight, bInvert, bInlineImage);
|
||
}
|
||
else
|
||
{
|
||
// ColorSpace
|
||
pDict->Search("ColorSpace", &oDictItem);
|
||
if (oDictItem.IsNull())
|
||
{
|
||
oDictItem.Free();
|
||
pDict->Search("CS", &oDictItem);
|
||
}
|
||
if (oDictItem.IsName())
|
||
{
|
||
Object oTemp;
|
||
m_pResources->LookupColorSpace(oDictItem.GetName(), &oTemp);
|
||
if (!oTemp.IsNull())
|
||
{
|
||
oDictItem.Free();
|
||
oDictItem = oTemp;
|
||
}
|
||
else
|
||
{
|
||
oTemp.Free();
|
||
}
|
||
}
|
||
GrColorSpace *pColorSpace;
|
||
if (!oDictItem.IsNull())
|
||
{
|
||
pColorSpace = GrColorSpace::Parse(&oDictItem);
|
||
}
|
||
else if (eCSMode == streamCSDeviceGray)
|
||
{
|
||
pColorSpace = new GrDeviceGrayColorSpace();
|
||
}
|
||
else if (eCSMode == streamCSDeviceRGB)
|
||
{
|
||
pColorSpace = new GrDeviceRGBColorSpace();
|
||
}
|
||
else if (eCSMode == streamCSDeviceCMYK)
|
||
{
|
||
pColorSpace = new GrDeviceCMYKColorSpace();
|
||
}
|
||
else
|
||
{
|
||
pColorSpace = NULL;
|
||
}
|
||
oDictItem.Free();
|
||
if (!pColorSpace)
|
||
{
|
||
// TO DO: Error "Bad image parameters"
|
||
return;
|
||
}
|
||
|
||
// Decode
|
||
pDict->Search("Decode", &oDictItem);
|
||
if (oDictItem.IsNull())
|
||
{
|
||
oDictItem.Free();
|
||
pDict->Search("D", &oDictItem);
|
||
}
|
||
GrImageColorMap *pColorMap = new GrImageColorMap(nBitsPerComponent, &oDictItem, pColorSpace);
|
||
oDictItem.Free();
|
||
if (!pColorMap->IsValid())
|
||
{
|
||
delete pColorMap;
|
||
// TO DO: Error "Bad image parameters"
|
||
return;
|
||
}
|
||
|
||
// Mask/SMask
|
||
bool bHaveColorKeyMask = false, bHaveExplicitMask = false, bHaveSoftMask = false;
|
||
Stream *pMaskStream = NULL;
|
||
int nMaskWidth = 0, nMaskHeight = 0;
|
||
bool bMaskInvert = false;
|
||
GrImageColorMap *pMaskColorMap = NULL;
|
||
int arrMaskColors[2 * GrColorMaxComps];
|
||
|
||
Object oMask, oSMask;
|
||
pDict->Search("Mask", &oMask);
|
||
pDict->Search("SMask", &oSMask);
|
||
|
||
Dict *pMaskDict = NULL;
|
||
|
||
if (oSMask.IsStream())
|
||
{
|
||
if (bInlineImage)
|
||
{
|
||
// Тут наверное нужно освободить память oMask, oSMask
|
||
// TO DO: Error "Bad image parameters"
|
||
return;
|
||
}
|
||
pMaskStream = oSMask.GetStream();
|
||
pMaskDict = oSMask.StreamGetDict();
|
||
|
||
// Width
|
||
pMaskDict->Search("Width", &oDictItem);
|
||
if (oDictItem.IsNull())
|
||
{
|
||
oDictItem.Free();
|
||
pMaskDict->Search("W", &oDictItem);
|
||
}
|
||
if (!oDictItem.IsInt())
|
||
{
|
||
oDictItem.Free();
|
||
// TO DO: Error "Bad image parameters"
|
||
return;
|
||
}
|
||
nMaskWidth = oDictItem.GetInt();
|
||
oDictItem.Free();
|
||
|
||
// Height
|
||
pMaskDict->Search("Height", &oDictItem);
|
||
if (oDictItem.IsNull())
|
||
{
|
||
oDictItem.Free();
|
||
pMaskDict->Search("H", &oDictItem);
|
||
}
|
||
if (!oDictItem.IsInt())
|
||
{
|
||
oDictItem.Free();
|
||
// TO DO: Error "Bad image parameters"
|
||
return;
|
||
}
|
||
nMaskHeight = oDictItem.GetInt();
|
||
oDictItem.Free();
|
||
|
||
// BitsPerComponent
|
||
pMaskDict->Search("BitsPerComponent", &oDictItem);
|
||
if (oDictItem.IsNull())
|
||
{
|
||
oDictItem.Free();
|
||
pMaskDict->Search("BPC", &oDictItem);
|
||
}
|
||
if (!oDictItem.IsInt())
|
||
{
|
||
oDictItem.Free();
|
||
// TO DO: Error "Bad image parameters"
|
||
return;
|
||
}
|
||
int nMaskBitsPerComponent = oDictItem.GetInt();
|
||
oDictItem.Free();
|
||
|
||
// ColorSpace
|
||
pMaskDict->Search("ColorSpace", &oDictItem);
|
||
if (oDictItem.IsNull())
|
||
{
|
||
oDictItem.Free();
|
||
pMaskDict->Search("CS", &oDictItem);
|
||
}
|
||
if (oDictItem.IsName())
|
||
{
|
||
Object oTemp;
|
||
m_pResources->LookupColorSpace(oDictItem.GetName(), &oTemp);
|
||
if (!oTemp.IsNull())
|
||
{
|
||
oDictItem.Free();
|
||
oDictItem = oTemp;
|
||
}
|
||
else
|
||
{
|
||
oTemp.Free();
|
||
}
|
||
}
|
||
GrColorSpace *pMaskColorSpace = GrColorSpace::Parse(&oDictItem);
|
||
oDictItem.Free();
|
||
if (!pMaskColorSpace || pMaskColorSpace->GetMode() != csDeviceGray)
|
||
{
|
||
// TO DO: Error "Bad image parameters"
|
||
return;
|
||
}
|
||
|
||
// Decode
|
||
pMaskDict->Search("Decode", &oDictItem);
|
||
if (oDictItem.IsNull())
|
||
{
|
||
oDictItem.Free();
|
||
pMaskDict->Search("D", &oDictItem);
|
||
}
|
||
pMaskColorMap = new GrImageColorMap(nMaskBitsPerComponent, &oDictItem, pMaskColorSpace);
|
||
oDictItem.Free();
|
||
if (!pMaskColorMap->IsValid())
|
||
{
|
||
delete pMaskColorMap;
|
||
// TO DO: Error "Bad image parameters"
|
||
return;
|
||
}
|
||
|
||
bHaveSoftMask = true;
|
||
}
|
||
else if (oMask.IsArray())
|
||
{
|
||
for (int nIndex = 0; nIndex < oMask.ArrayGetLength() && nIndex < 2 * GrColorMaxComps; ++nIndex)
|
||
{
|
||
Object oTemp;
|
||
oMask.ArrayGet(nIndex, &oTemp);
|
||
arrMaskColors[nIndex] = oTemp.GetInt();
|
||
oTemp.Free();
|
||
}
|
||
bHaveColorKeyMask = true;
|
||
}
|
||
else if (oMask.IsStream())
|
||
{
|
||
if (bInlineImage)
|
||
{
|
||
// TO DO: Error "Bad image parameters"
|
||
return;
|
||
}
|
||
pMaskStream = oMask.GetStream();
|
||
pMaskDict = oMask.StreamGetDict();
|
||
|
||
// Width
|
||
pMaskDict->Search("Width", &oDictItem);
|
||
if (oDictItem.IsNull())
|
||
{
|
||
oDictItem.Free();
|
||
pMaskDict->Search("W", &oDictItem);
|
||
}
|
||
if (!oDictItem.IsInt())
|
||
{
|
||
oDictItem.Free();
|
||
// TO DO: Error "Bad image parameters"
|
||
return;
|
||
}
|
||
nMaskWidth = oDictItem.GetInt();
|
||
oDictItem.Free();
|
||
|
||
// Height
|
||
pMaskDict->Search("Height", &oDictItem);
|
||
if (oDictItem.IsNull())
|
||
{
|
||
oDictItem.Free();
|
||
pMaskDict->Search("H", &oDictItem);
|
||
}
|
||
if (!oDictItem.IsInt())
|
||
{
|
||
oDictItem.Free();
|
||
// TO DO: Error "Bad image parameters"
|
||
return;
|
||
}
|
||
nMaskHeight = oDictItem.GetInt();
|
||
oDictItem.Free();
|
||
|
||
// ImageMask
|
||
pMaskDict->Search("ImageMask", &oDictItem);
|
||
if (oDictItem.IsNull())
|
||
{
|
||
oDictItem.Free();
|
||
pMaskDict->Search("IM", &oDictItem);
|
||
}
|
||
if (!oDictItem.IsBool() || !oDictItem.GetBool())
|
||
{
|
||
oDictItem.Free();
|
||
// TO DO: Error "Bad image parameters"
|
||
return;
|
||
}
|
||
oDictItem.Free();
|
||
|
||
// Decode
|
||
bMaskInvert = false;
|
||
pMaskDict->Search("Decode", &oDictItem);
|
||
if (oDictItem.IsNull())
|
||
{
|
||
oDictItem.Free();
|
||
pMaskDict->Search("D", &oDictItem);
|
||
}
|
||
if (oDictItem.IsArray())
|
||
{
|
||
Object oTemp;
|
||
oDictItem.ArrayGet(0, &oTemp);
|
||
if (oTemp.IsInt() && oTemp.GetInt() == 1)
|
||
{
|
||
bMaskInvert = true;
|
||
}
|
||
oTemp.Free();
|
||
}
|
||
else if (!oDictItem.IsNull())
|
||
{
|
||
oDictItem.Free();
|
||
// TO DO: Error "Bad image parameters"
|
||
return;
|
||
}
|
||
oDictItem.Free();
|
||
bHaveExplicitMask = true;
|
||
}
|
||
|
||
// Рисуем
|
||
if (bHaveSoftMask)
|
||
{
|
||
m_pOut->DrawSoftMaskedImage(m_pGState, pRef, pStream, nWidth, nHeight, pColorMap, pMaskStream, nMaskWidth, nMaskHeight, pMaskColorMap);
|
||
delete pMaskColorMap;
|
||
}
|
||
else if (bHaveExplicitMask)
|
||
{
|
||
m_pOut->DrawMaskedImage(m_pGState, pRef, pStream, nWidth, nHeight, pColorMap, pMaskStream, nMaskWidth, nMaskHeight, bMaskInvert);
|
||
}
|
||
else
|
||
{
|
||
m_pOut->DrawImage(m_pGState, pRef, pStream, nWidth, nHeight, pColorMap, bHaveColorKeyMask ? arrMaskColors : (int *)NULL, bInlineImage);
|
||
}
|
||
delete pColorMap;
|
||
|
||
oMask.Free();
|
||
oSMask.Free();
|
||
}
|
||
|
||
|
||
int nUpdater = 0;
|
||
if ((nUpdater = nWidth * nHeight) > 1000)
|
||
{
|
||
nUpdater = 1000;
|
||
}
|
||
m_nUpdateLevel += nUpdater;
|
||
|
||
return;
|
||
}
|
||
|
||
void Graphics::DoForm(Object *pStream)
|
||
{
|
||
double arrMatrix[6], arrBBox[4];
|
||
|
||
// Проверяем глубину рекурсии
|
||
if (m_nFormDepth > 20)
|
||
{
|
||
return;
|
||
}
|
||
|
||
Dict *pDict = pStream->StreamGetDict();
|
||
Object oDictItem;
|
||
|
||
// FormType
|
||
pDict->Search("FormType", &oDictItem);
|
||
if (!(oDictItem.IsNull() || (oDictItem.IsInt() && oDictItem.GetInt() == 1)))
|
||
{
|
||
// TO DO: Error "Unknown form type"
|
||
}
|
||
oDictItem.Free();
|
||
|
||
// BBox
|
||
Object oBBox;
|
||
pDict->Search("BBox", &oBBox);
|
||
if (!oBBox.IsArray())
|
||
{
|
||
oBBox.Free();
|
||
// TO DO: Error "Bad form bounding box"
|
||
return;
|
||
}
|
||
|
||
for (int nIndex = 0; nIndex < 4; ++nIndex)
|
||
{
|
||
Object oTemp;
|
||
oBBox.ArrayGet(nIndex, &oTemp);
|
||
arrBBox[nIndex] = oTemp.GetNum();
|
||
oTemp.Free();
|
||
}
|
||
oBBox.Free();
|
||
|
||
// Matrix
|
||
Object oMatrix;
|
||
pDict->Search("Matrix", &oMatrix);
|
||
if (oMatrix.IsArray())
|
||
{
|
||
for (int nIndex = 0; nIndex < 6; ++nIndex)
|
||
{
|
||
Object oTemp;
|
||
oMatrix.ArrayGet(nIndex, &oTemp);
|
||
arrMatrix[nIndex] = oTemp.GetNum();
|
||
oTemp.Free();
|
||
}
|
||
}
|
||
else
|
||
{
|
||
arrMatrix[0] = 1; arrMatrix[1] = 0;
|
||
arrMatrix[2] = 0; arrMatrix[3] = 1;
|
||
arrMatrix[4] = 0; arrMatrix[5] = 0;
|
||
}
|
||
oMatrix.Free();
|
||
|
||
// Resources
|
||
Object oResources;
|
||
pDict->Search("Resources", &oResources);
|
||
Dict *pResourcesDict = oResources.IsDict() ? oResources.GetDict() : (Dict *)NULL;
|
||
|
||
// Transparency Group
|
||
bool bTranspGroup = false, bIsolated = false, bKnockout = false;
|
||
GrColorSpace *pBlendingColorSpace = NULL;
|
||
if (pDict->Search("Group", &oDictItem)->IsDict())
|
||
{
|
||
Object oTransp;
|
||
if (oDictItem.DictLookup("S", &oTransp)->IsName("Transparency"))
|
||
{
|
||
bTranspGroup = true;
|
||
Object oTemp;
|
||
if (!oDictItem.DictLookup("CS", &oTemp)->IsNull())
|
||
{
|
||
pBlendingColorSpace = GrColorSpace::Parse(&oTemp);
|
||
}
|
||
oTemp.Free();
|
||
if (oDictItem.DictLookup("I", &oTemp)->IsBool())
|
||
{
|
||
bIsolated = oTemp.GetBool();
|
||
}
|
||
oTemp.Free();
|
||
if (oDictItem.DictLookup("K", &oTemp)->IsBool())
|
||
{
|
||
bKnockout = oTemp.GetBool();
|
||
}
|
||
oTemp.Free();
|
||
}
|
||
oTransp.Free();
|
||
}
|
||
oDictItem.Free();
|
||
|
||
// Рисуем
|
||
++m_nFormDepth;
|
||
DoForm(pStream, pResourcesDict, arrMatrix, arrBBox, bTranspGroup, false, pBlendingColorSpace, bIsolated, bKnockout);
|
||
--m_nFormDepth;
|
||
|
||
if (pBlendingColorSpace)
|
||
{
|
||
delete pBlendingColorSpace;
|
||
}
|
||
oResources.Free();
|
||
}
|
||
|
||
void Graphics::DoForm(Object *pStream, Dict *pResourcesDict, double *pMatrix, double *pBBox, bool bTranspGroup, bool bSMask, GrColorSpace *pBlendingColorSpace, bool bIsolated, bool bKnockout, bool bAlpha, Function *pTransferFunctions, GrColor *pBackdropColor)
|
||
{
|
||
// Записываем текущую директорию ресурсов в стек
|
||
PushResources(pResourcesDict);
|
||
|
||
// Сохраняем текущий GState
|
||
SaveGState();
|
||
|
||
// Очищаем Path(если там что-то было)
|
||
m_pGState->ClearPath();
|
||
|
||
// Сохраняем текущий парсер
|
||
Parser *pOldParser = m_pParser;
|
||
|
||
// CTM
|
||
m_pGState->ConcatCTM(pMatrix[0], pMatrix[1], pMatrix[2], pMatrix[3], pMatrix[4], pMatrix[5]);
|
||
m_pOut->UpdateCTM(m_pGState, pMatrix[0], pMatrix[1], pMatrix[2], pMatrix[3], pMatrix[4], pMatrix[5]);
|
||
|
||
// BBox
|
||
m_pGState->MoveTo(pBBox[0], pBBox[1]);
|
||
m_pGState->LineTo(pBBox[2], pBBox[1]);
|
||
m_pGState->LineTo(pBBox[2], pBBox[3]);
|
||
m_pGState->LineTo(pBBox[0], pBBox[3]);
|
||
m_pGState->ClosePath();
|
||
m_pGState->Clip();
|
||
m_pGState->GetClip()->ClipToPath(m_pGState->GetPath()->Copy(), m_pGState->GetCTM(), false);
|
||
|
||
if (m_pOut->UseClipTo())
|
||
m_pOut->ClipToPath(m_pGState, m_pGState->GetPath(), m_pGState->GetCTM(), false);
|
||
else
|
||
m_pOut->Clip(m_pGState);
|
||
|
||
m_pGState->ClearPath();
|
||
|
||
if (bSMask || bTranspGroup)
|
||
{
|
||
if (m_pGState->GetBlendMode() != grBlendNormal)
|
||
{
|
||
m_pGState->SetBlendMode(grBlendNormal);
|
||
m_pOut->UpdateBlendMode(m_pGState);
|
||
}
|
||
if (!m_pOut->UseSimpleTransparentGroup())
|
||
{
|
||
if (m_pGState->GetFillOpacity() != 1)
|
||
{
|
||
m_pGState->SetFillOpacity(1);
|
||
m_pOut->UpdateFillOpacity(m_pGState);
|
||
}
|
||
if (m_pGState->GetStrokeOpacity() != 1)
|
||
{
|
||
m_pGState->SetStrokeOpacity(1);
|
||
m_pOut->UpdateStrokeOpacity(m_pGState);
|
||
}
|
||
}
|
||
m_pOut->ClearSoftMask(m_pGState);
|
||
m_pOut->BeginTransparencyGroup(m_pGState, pBBox, pBlendingColorSpace, bIsolated, bKnockout, bSMask);
|
||
}
|
||
|
||
double arrOldBaseMatrix[6];
|
||
for (int nIndex = 0; nIndex < 6; ++nIndex)
|
||
{
|
||
arrOldBaseMatrix[nIndex] = m_arrBaseMatrix[nIndex];
|
||
m_arrBaseMatrix[nIndex] = m_pGState->GetCTM()[nIndex];
|
||
}
|
||
|
||
// Рисуем саму форму
|
||
Display(pStream, false);
|
||
|
||
if (bSMask || bTranspGroup)
|
||
{
|
||
m_pOut->EndTransparencyGroup(m_pGState);
|
||
}
|
||
|
||
for (int nIndex = 0; nIndex < 6; ++nIndex)
|
||
{
|
||
m_arrBaseMatrix[nIndex] = arrOldBaseMatrix[nIndex];
|
||
}
|
||
|
||
|
||
// Восстанавливаем начальные данные
|
||
m_pParser = pOldParser;
|
||
RestoreGState();
|
||
PopResources();
|
||
|
||
if (bSMask)
|
||
{
|
||
m_pOut->SetSoftMask(m_pGState, pBBox, bAlpha, pTransferFunctions, pBackdropColor);
|
||
}
|
||
else if (bTranspGroup)
|
||
{
|
||
m_pOut->PaintTransparencyGroup(m_pGState, pBBox);
|
||
}
|
||
|
||
return;
|
||
}
|
||
|
||
//-------------------------------------------------------------------------------------------------------------------------------
|
||
// Inline images
|
||
//-------------------------------------------------------------------------------------------------------------------------------
|
||
|
||
// BI
|
||
void Graphics::OperatorBeginImage(Object arrArguments[], int nArgumentsCount)
|
||
{
|
||
Stream *pStream = BuildImageStream();
|
||
|
||
if (pStream)
|
||
{
|
||
DoImage(NULL, pStream, true);
|
||
|
||
// Ищем оператор EI
|
||
int nChar1 = pStream->GetUndecodedStream()->GetChar();
|
||
int nChar2 = pStream->GetUndecodedStream()->GetChar();
|
||
|
||
while (!(nChar1 == 'E' && nChar2 == 'I') && nChar2 != EOF)
|
||
{
|
||
nChar1 = nChar2;
|
||
nChar2 = pStream->GetUndecodedStream()->GetChar();
|
||
}
|
||
delete pStream;
|
||
}
|
||
}
|
||
|
||
// ID
|
||
void Graphics::OperatorImageData(Object arrArguments[], int nArgumentsCount)
|
||
{
|
||
// TO DO: Error "Internal: got 'ID' operator"
|
||
}
|
||
|
||
// EI
|
||
void Graphics::OperatorEndImage(Object arrArguments[], int nArgumentsCount)
|
||
{
|
||
// TO DO: Error "Internal: got 'EI' operator"
|
||
}
|
||
|
||
//-------------------------------------------------------------------------------------------------------------------------------
|
||
Stream *Graphics::BuildImageStream()
|
||
{
|
||
Object oDict;
|
||
oDict.InitDict(m_pXref);
|
||
Object oObject;
|
||
m_pParser->GetObject(&oObject);
|
||
|
||
while (!oObject.IsCommand("ID") && !oObject.IsEOF())
|
||
{
|
||
if (!oObject.IsName())
|
||
{
|
||
// TO DO: Error "Inline image dictionary key must be a name object"
|
||
oObject.Free();
|
||
}
|
||
else
|
||
{
|
||
char* sKey = CopyString(oObject.GetName());
|
||
oObject.Free();
|
||
m_pParser->GetObject(&oObject);
|
||
if (oObject.IsEOF() || oObject.IsError())
|
||
{
|
||
MemUtilsFree(sKey);
|
||
break;
|
||
}
|
||
oDict.DictAdd(sKey, &oObject);
|
||
}
|
||
m_pParser->GetObject(&oObject);
|
||
}
|
||
if (oObject.IsEOF())
|
||
{
|
||
// TO DO: Error "End of file in inline image"
|
||
oObject.Free();
|
||
oDict.Free();
|
||
return NULL;
|
||
}
|
||
oObject.Free();
|
||
|
||
Stream *pStream = new EmbedStream(m_pParser->GetStream(), &oDict, false, 0);
|
||
pStream = pStream->AddFilters(&oDict);
|
||
|
||
return pStream;
|
||
}
|
||
|
||
|
||
//-------------------------------------------------------------------------------------------------------------------------------
|
||
// Type 3 fonts
|
||
//-------------------------------------------------------------------------------------------------------------------------------
|
||
|
||
// d0
|
||
void Graphics::OperatorSetCharWidth(Object arrArguments[], int nArgumentsCount)
|
||
{
|
||
m_pOut->Type3D0(m_pGState, arrArguments[0].GetNum(), arrArguments[1].GetNum());
|
||
}
|
||
|
||
// d1
|
||
void Graphics::OperatorSetCacheDevice(Object arrArguments[], int nArgumentsCount)
|
||
{
|
||
m_pOut->Type3D1(m_pGState, arrArguments[0].GetNum(), arrArguments[1].GetNum(), arrArguments[2].GetNum(), arrArguments[3].GetNum(), arrArguments[4].GetNum(), arrArguments[5].GetNum());
|
||
}
|
||
|
||
//-------------------------------------------------------------------------------------------------------------------------------
|
||
// Compatibility
|
||
//-------------------------------------------------------------------------------------------------------------------------------
|
||
|
||
// BX
|
||
void Graphics::OperatorBeginIgnoreUndef(Object arrArguments[], int nArgumentsCount)
|
||
{
|
||
++m_nIgnoreUndef;
|
||
}
|
||
|
||
// EX
|
||
void Graphics::OperatorEndIgnoreUndef(Object arrArguments[], int nArgumentsCount)
|
||
{
|
||
if (m_nIgnoreUndef > 0)
|
||
--m_nIgnoreUndef;
|
||
}
|
||
|
||
//-------------------------------------------------------------------------------------------------------------------------------
|
||
// Marked content
|
||
//-------------------------------------------------------------------------------------------------------------------------------
|
||
|
||
// BDC/BMC
|
||
void Graphics::OperatorBeginMarkedContent(Object arrArguments[], int nArgumentsCount)
|
||
{
|
||
}
|
||
|
||
// EMC
|
||
void Graphics::OperatorEndMarkedContent(Object arrArguments[], int nArgumentsCount)
|
||
{
|
||
}
|
||
|
||
// DP/MP
|
||
void Graphics::OperatorMarkPoint(Object arrArguments[], int nArgumentsCount)
|
||
{
|
||
}
|
||
|
||
//-------------------------------------------------------------------------------------------------------------------------------
|
||
// Дополнительные функции
|
||
//-------------------------------------------------------------------------------------------------------------------------------
|
||
|
||
void Graphics::DrawAnnotation(Object *pStream, AnnotBorderStyle *pBorderStyle, double dMinX, double dMinY, double dMaxX, double dMaxY)
|
||
{
|
||
double dX, dY;
|
||
|
||
// Преобразовываем Annotation BBox из стандартного пользовательского пространства в
|
||
// текущее пользовательское пространство: (BBox * BaseMatrix) * InvCTM
|
||
double *pCTM = m_pGState->GetCTM();
|
||
double dDet = 1 / (pCTM[0] * pCTM[3] - pCTM[1] * pCTM[2]);
|
||
double arrInvCTM[6];
|
||
arrInvCTM[0] = pCTM[3] * dDet;
|
||
arrInvCTM[1] = -pCTM[1] * dDet;
|
||
arrInvCTM[2] = -pCTM[2] * dDet;
|
||
arrInvCTM[3] = pCTM[0] * dDet;
|
||
arrInvCTM[4] = (pCTM[2] * pCTM[5] - pCTM[3] * pCTM[4]) * dDet;
|
||
arrInvCTM[5] = (pCTM[1] * pCTM[4] - pCTM[0] * pCTM[5]) * dDet;
|
||
|
||
dX = m_arrBaseMatrix[0] * dMinX + m_arrBaseMatrix[2] * dMinY + m_arrBaseMatrix[4];
|
||
dY = m_arrBaseMatrix[1] * dMinX + m_arrBaseMatrix[3] * dMinY + m_arrBaseMatrix[5];
|
||
double dAnnotX0 = arrInvCTM[0] * dX + arrInvCTM[2] * dY + arrInvCTM[4];
|
||
double dAnnotY0 = arrInvCTM[1] * dX + arrInvCTM[3] * dY + arrInvCTM[5];
|
||
|
||
dX = m_arrBaseMatrix[0] * dMaxX + m_arrBaseMatrix[2] * dMaxY + m_arrBaseMatrix[4];
|
||
dY = m_arrBaseMatrix[1] * dMaxX + m_arrBaseMatrix[3] * dMaxY + m_arrBaseMatrix[5];
|
||
double dAnnotX1 = arrInvCTM[0] * dX + arrInvCTM[2] * dY + arrInvCTM[4];
|
||
double dAnnotY1 = arrInvCTM[1] * dX + arrInvCTM[3] * dY + arrInvCTM[5];
|
||
|
||
if (dAnnotX0 > dAnnotX1)
|
||
{
|
||
dX = dAnnotX0;
|
||
dAnnotX0 = dAnnotX1;
|
||
dAnnotX1 = dX;
|
||
}
|
||
if (dAnnotY0 > dAnnotY1)
|
||
{
|
||
dY = dAnnotY0;
|
||
dAnnotY0 = dAnnotY1;
|
||
dAnnotY1 = dY;
|
||
}
|
||
|
||
if (pStream->IsStream())
|
||
{
|
||
Dict *pDict = pStream->StreamGetDict();
|
||
|
||
// BBox
|
||
Object oBBox;
|
||
pDict->Search("BBox", &oBBox);
|
||
double arrBBox[4];
|
||
if (!oBBox.IsArray())
|
||
{
|
||
oBBox.Free();
|
||
// TO DO: Error "Bad form bounding box"
|
||
return;
|
||
}
|
||
for (int nIndex = 0; nIndex < 4; ++nIndex)
|
||
{
|
||
Object oTemp;
|
||
oBBox.ArrayGet(nIndex, &oTemp);
|
||
arrBBox[nIndex] = oTemp.GetNum();
|
||
oTemp.Free();
|
||
}
|
||
oBBox.Free();
|
||
|
||
// Matrix
|
||
Object oMatrix;
|
||
pDict->Search("Matrix", &oMatrix);
|
||
double arrMatrix[6];
|
||
if (oMatrix.IsArray())
|
||
{
|
||
for (int nIndex = 0; nIndex < 6; ++nIndex)
|
||
{
|
||
Object oTemp;
|
||
oMatrix.ArrayGet(nIndex, &oTemp);
|
||
arrMatrix[nIndex] = oTemp.GetNum();
|
||
oTemp.Free();
|
||
}
|
||
}
|
||
else
|
||
{
|
||
arrMatrix[0] = 1; arrMatrix[1] = 0;
|
||
arrMatrix[2] = 0; arrMatrix[3] = 1;
|
||
arrMatrix[4] = 0; arrMatrix[5] = 0;
|
||
}
|
||
oMatrix.Free();
|
||
|
||
double dFormX0 = arrBBox[0] * arrMatrix[0] + arrBBox[1] * arrMatrix[2] + arrMatrix[4];
|
||
double dFormY0 = arrBBox[0] * arrMatrix[1] + arrBBox[1] * arrMatrix[3] + arrMatrix[5];
|
||
double dFormX1 = arrBBox[2] * arrMatrix[0] + arrBBox[3] * arrMatrix[2] + arrMatrix[4];
|
||
double dFormY1 = arrBBox[2] * arrMatrix[1] + arrBBox[3] * arrMatrix[3] + arrMatrix[5];
|
||
|
||
if (dFormX0 > dFormX1)
|
||
{
|
||
dX = dFormX0;
|
||
dFormX0 = dFormX1;
|
||
dFormX1 = dX;
|
||
}
|
||
if (dFormY0 > dFormY1)
|
||
{
|
||
dY = dFormY0;
|
||
dFormY0 = dFormY1;
|
||
dFormY1 = dY;
|
||
}
|
||
|
||
// Растягиваем до размеров Annotation BBox
|
||
double dScaleX, dScaleY;
|
||
if (dFormX1 == dFormX0)
|
||
{
|
||
// Такого не должно быть
|
||
dScaleX = 1;
|
||
}
|
||
else
|
||
{
|
||
dScaleX = (dAnnotX1 - dAnnotX0) / (dFormX1 - dFormX0);
|
||
}
|
||
if (dFormY1 == dFormY0)
|
||
{
|
||
// Такого не должно быть
|
||
dScaleY = 1;
|
||
}
|
||
else
|
||
{
|
||
dScaleY = (dAnnotY1 - dAnnotY0) / (dFormY1 - dFormY0);
|
||
}
|
||
|
||
arrMatrix[0] *= dScaleX;
|
||
arrMatrix[2] *= dScaleX;
|
||
arrMatrix[4] = (arrMatrix[4] - dFormX0) * dScaleX + dAnnotX0;
|
||
arrMatrix[1] *= dScaleY;
|
||
arrMatrix[3] *= dScaleY;
|
||
arrMatrix[5] = (arrMatrix[5] - dFormY0) * dScaleY + dAnnotY0;
|
||
|
||
// Resources
|
||
Object oResources;
|
||
pDict->Search("Resources", &oResources);
|
||
Dict *pResourcesDict = oResources.IsDict() ? oResources.GetDict() : (Dict *)NULL;
|
||
|
||
// Рисуем
|
||
DoForm(pStream, pResourcesDict, arrMatrix, arrBBox);
|
||
|
||
oResources.Free();
|
||
}
|
||
|
||
// Рисуем рамку
|
||
if (pBorderStyle && pBorderStyle->GetWidth() > 0)
|
||
{
|
||
if (m_pGState->GetStrokeColorSpace()->GetMode() != csDeviceRGB)
|
||
{
|
||
m_pGState->SetStrokePattern(NULL);
|
||
m_pGState->SetStrokeColorSpace(new GrDeviceRGBColorSpace());
|
||
m_pOut->UpdateStrokeColorSpace(m_pGState);
|
||
}
|
||
double dR, dG, dB;
|
||
pBorderStyle->GetColor(&dR, &dG, &dB);
|
||
GrColor oColor;
|
||
oColor.arrComp[0] = DoubleToColor(dR);
|
||
oColor.arrComp[1] = DoubleToColor(dG);
|
||
oColor.arrComp[2] = DoubleToColor(dB);
|
||
m_pGState->SetStrokeColor(&oColor);
|
||
m_pOut->UpdateStrokeColor(m_pGState);
|
||
|
||
dX = (m_arrBaseMatrix[0] + m_arrBaseMatrix[2]) * arrInvCTM[0] + (m_arrBaseMatrix[1] + m_arrBaseMatrix[3]) * arrInvCTM[2];
|
||
dY = (m_arrBaseMatrix[0] + m_arrBaseMatrix[2]) * arrInvCTM[1] + (m_arrBaseMatrix[1] + m_arrBaseMatrix[3]) * arrInvCTM[3];
|
||
dX = sqrt(0.5 * (dX * dX + dY * dY));
|
||
m_pGState->SetLineWidth(dX * pBorderStyle->GetWidth());
|
||
m_pOut->UpdateLineWidth(m_pGState);
|
||
|
||
double *pDash;
|
||
int nDashLength;
|
||
|
||
pBorderStyle->GetDash(&pDash, &nDashLength);
|
||
|
||
if (pBorderStyle->GetType() == annotBorderDashed && nDashLength > 0)
|
||
{
|
||
double *pDashNew = (double *)MemUtilsMallocArray(nDashLength, sizeof(double));
|
||
for (int nIndex = 0; nIndex < nDashLength; ++nIndex)
|
||
{
|
||
pDashNew[nIndex] = dX * pDash[nIndex];
|
||
}
|
||
m_pGState->SetLineDash(pDashNew, nDashLength, 0);
|
||
m_pOut->UpdateLineDash(m_pGState);
|
||
}
|
||
|
||
m_pGState->ClearPath();
|
||
m_pGState->MoveTo(dAnnotX0, m_pOut->UpSideDown() ? dAnnotY1 : dAnnotY0);
|
||
m_pGState->LineTo(dAnnotX1, m_pOut->UpSideDown() ? dAnnotY1 : dAnnotY0);
|
||
if (pBorderStyle->GetType() != annotBorderUnderlined)
|
||
{
|
||
m_pGState->LineTo(dAnnotX1, m_pOut->UpSideDown() ? dAnnotY0 : dAnnotY1);
|
||
m_pGState->LineTo(dAnnotX0, m_pOut->UpSideDown() ? dAnnotY0 : dAnnotY1);
|
||
m_pGState->ClosePath();
|
||
}
|
||
m_pOut->Stroke(m_pGState);
|
||
}
|
||
}
|
||
//-------------------------------------------------------------------------------------------------------------------------------
|
||
// Работаем со стеками GState и ResourcesDict
|
||
//-------------------------------------------------------------------------------------------------------------------------------
|
||
void Graphics::SaveGState()
|
||
{
|
||
m_pOut->SaveGState(m_pGState);
|
||
m_pGState = m_pGState->Save();
|
||
}
|
||
|
||
void Graphics::RestoreGState()
|
||
{
|
||
m_pGState = m_pGState->Restore();
|
||
m_pOut->RestoreGState(m_pGState);
|
||
}
|
||
|
||
void Graphics::PushResources(Dict *pResourcesDict)
|
||
{
|
||
m_pResources = new GrResources(m_pXref, pResourcesDict, m_pResources, m_pGlobalParams);
|
||
}
|
||
|
||
void Graphics::PopResources()
|
||
{
|
||
if (m_pResources)
|
||
{
|
||
GrResources *pResourcesNext = m_pResources->GetNext();
|
||
delete m_pResources;
|
||
m_pResources = pResourcesNext;
|
||
}
|
||
else
|
||
m_pResources = NULL;
|
||
}
|
||
} |