Files
core/PdfReader/Src/Graphics.cpp
Ilya.Kirillov 6e3a49bb72 Пересобран pdfreader для linux64 с исправленной ошибкой с fclose.
git-svn-id: svn://fileserver/activex/AVS/Sources/TeamlabOffice/trunk/ServerComponents@64590 954022d7-b5bf-4e40-9824-e11837661b57
2016-05-21 00:31:22 +03:00

4940 lines
147 KiB
C++
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#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";
m_pDumpFile = NSFile::CFileBinary::OpenFileNative(wsFileName, L"wb");
#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;
}
}