#include "stdafx.h" #include #include #include #include #include #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" #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 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 char sFileName[MAX_PATH]; sprintf( sFileName, "E:\\Output\\PDF Dump\\DumpPDF_Page%d.bin", nPageNumber ); m_pDumpFile = NULL; m_pDumpFile = ::fopen( sFileName, "wb" ); #endif m_pGlobalParams = pGlobalParams; m_pXref = pXref; m_bSubPage = FALSE; m_bPrintCommands = ( m_pGlobalParams ? m_pGlobalParams->GetPrintCommands() : 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; m_bPrintCommands = ( m_pGlobalParams ? m_pGlobalParams->GetPrintCommands() : FALSE ); // 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 ); } //if ( m_bPrintCommands ) //{ // oTemp.Print( stdout ); // for ( int nIndex = 0; nIndex < nArgumentsCount; ++nIndex ) // { // printf(" "); // arrArguments[nIndex].Print(stdout); // } // printf("\n"); // fflush(stdout); //} #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 ); } //if ( m_bPrintCommands ) //{ // printf("throwing away arg: "); // oTemp.Print(stdout); // printf("\n"); // fflush(stdout); //} #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 ); } //if ( m_bPrintCommands ) //{ // printf("%d leftovers:", nArgumentsCount); // for ( int nIndex = 0; nIndex < nArgumentsCount; ++nIndex ) // { // printf(" "); // arrArguments[nIndex].Print(stdout); // } // printf("\n"); // fflush(stdout); //} #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; } if ( m_bPrintCommands ) { //printf(" gfx state dict: "); //oDict.Print(); //printf("\n"); } // Прозрачность 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 { bool bIsSupportSmartTiling = m_pOut->IsSupportFuture(_T("TilingHtmlPattern")); if (true == bIsSupportSmartTiling) { m_pOut->ClipAttack(m_pGState); m_pOut->BeginCommand(c_nPDFTilingFill); CString strXml = _T(""); strXml.Format(_T("\ \ \ "), nX0, nY0, nX1 - nX0, nY1 - nY0, dStepX, dStepY, dXMin, dYMin, dXMax, dYMax, pTempMatrix[0], pTempMatrix[1], pTempMatrix[2], pTempMatrix[3], pTempMatrix[4], pTempMatrix[5]); VARIANT var; var.vt = VT_BSTR; var.bstrVal = strXml.AllocSysString(); m_pOut->SetAdditionalParam(_T("TilingHtmlPattern"), var); SysFreeString(var.bstrVal); 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->EndCommand(c_nPDFTilingFill); } 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->BeginCommand(c_nPDFTilingFillIteration); 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->EndCommand(c_nPDFTilingFillIteration); } } } } 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 ); } //if ( m_bPrintCommands ) //{ // printf(" font: tag=%s name='%s' %g\n", pFont->GetTag()->GetBuffer(), pFont->GetBaseName() ? pFont->GetBaseName()->GetBuffer() : "???", arrArguments[1].GetNum()); // fflush(stdout); //} #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) { if ( m_bPrintCommands ) { //printf(" marked content: %s ", arrArguments[0].GetName()); //if ( nArgumentsCount == 2) // arrArguments[2].Print(stdout); //printf("\n"); //fflush(stdout); } } // EMC void Graphics::OperatorEndMarkedContent(Object arrArguments[], int nArgumentsCount) { } // DP/MP void Graphics::OperatorMarkPoint(Object arrArguments[], int nArgumentsCount) { if ( m_bPrintCommands ) { //printf(" mark point: %s ", arrArguments[0].GetName()); //if ( nArgumentsCount == 2 ) // arrArguments[2].Print(stdout); //printf("\n"); //fflush(stdout); } } //------------------------------------------------------------------------------------------------------------------------------- // Дополнительные функции //------------------------------------------------------------------------------------------------------------------------------- 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; }