#include "stdafx.h" #include #include #include "MemoryUtils.h" #include "List.h" #include "Object.h" #include "Catalog.h" #include "Graphics.h" #include "GFont.h" #include "Lexer.h" #include "Annot.h" //------------------------------------------------------------------------------------------------------------------------------- #define annotFlagHidden 0x0002 #define annotFlagPrint 0x0004 #define annotFlagNoView 0x0020 #define fieldFlagReadOnly 0x00000001 #define fieldFlagRequired 0x00000002 #define fieldFlagNoExport 0x00000004 #define fieldFlagMultiline 0x00001000 #define fieldFlagPassword 0x00002000 #define fieldFlagNoToggleToOff 0x00004000 #define fieldFlagRadio 0x00008000 #define fieldFlagPushbutton 0x00010000 #define fieldFlagCombo 0x00020000 #define fieldFlagEdit 0x00040000 #define fieldFlagSort 0x00080000 #define fieldFlagFileSelect 0x00100000 #define fieldFlagMultiSelect 0x00200000 #define fieldFlagDoNotSpellCheck 0x00400000 #define fieldFlagDoNotScroll 0x00800000 #define fieldFlagComb 0x01000000 #define fieldFlagRichText 0x02000000 #define fieldFlagRadiosInUnison 0x02000000 #define fieldFlagCommitOnSelChange 0x04000000 #define fieldQuadLeft 0 #define fieldQuadCenter 1 #define fieldQuadRight 2 //------------------------------------------------------------------------------------------------------------------------------- // AnnotBorderStyle //------------------------------------------------------------------------------------------------------------------------------- AnnotBorderStyle::AnnotBorderStyle(AnnotBorderType eType, double dWidth, double *pDash, int nDashLength, double dR, double dG, double dB) { m_eType = eType; m_dWidth = dWidth; m_pDash = pDash; m_nDashLength = nDashLength; m_dR = dR; m_dG = dG; m_dB = dB; } AnnotBorderStyle::~AnnotBorderStyle() { MemUtilsFree( m_pDash ); } //------------------------------------------------------------------------------------------------------------------------------- // Annot //------------------------------------------------------------------------------------------------------------------------------- Annot::Annot(GlobalParams *pGlobalParams, XRef *pXref, Dict *pAcroForm, Dict *pDict, Ref *pRef) { m_pGlobalParams = pGlobalParams; m_bValid = TRUE; m_pXref = pXref; m_oRef = *pRef; m_seType = NULL; m_seAppBuffer = NULL; m_pBorderStyle = NULL; // SubType Object oDictItem; if ( pDict->Search("Subtype", &oDictItem)->IsName() ) { m_seType = new StringExt( oDictItem.GetName() ); } oDictItem.Free(); // Rect if ( pDict->Search("Rect", &oDictItem)->IsArray() && oDictItem.ArrayGetLength() == 4 ) { Object oTemp; m_dMinX = m_dMinY = m_dMaxX = m_dMaxY = 0; if ( oDictItem.ArrayGet( 0, &oTemp)->IsNum() ) { m_dMinX = oTemp.GetNum(); } oTemp.Free(); if ( oDictItem.ArrayGet( 1, &oTemp)->IsNum() ) { m_dMinY = oTemp.GetNum(); } oTemp.Free(); if ( oDictItem.ArrayGet( 2, &oTemp)->IsNum() ) { m_dMaxX = oTemp.GetNum(); } oTemp.Free(); if ( oDictItem.ArrayGet( 3, &oTemp)->IsNum() ) { m_dMaxY = oTemp.GetNum(); } oTemp.Free(); if ( m_dMinX > m_dMaxX ) { double dTemp = m_dMinX; m_dMinX = m_dMaxX; m_dMaxX = dTemp; } if ( m_dMinY > m_dMaxY ) { double dTemp = m_dMinY; m_dMinY = m_dMaxY; m_dMaxY = dTemp; } } else { // TO DO: Error "Bad bounding box for annotation" m_bValid = FALSE; } oDictItem.Free(); // F (flags) if ( pDict->Search("F", &oDictItem)->IsInt() ) { m_nFlags = oDictItem.GetInt(); } else { m_nFlags = 0; } oDictItem.Free(); //Border style AnnotBorderType eBorderType = annotBorderSolid; double dBorderWidth = 1; double *pBorderDash = NULL; int nBorderDashLength = 0; double dBorderR = 0; double dBorderG = 0; double dBorderB = 1; if ( pDict->Search("BS", &oDictItem)->IsDict() ) { Object oTemp; if ( oDictItem.DictLookup("S", &oTemp)->IsName() ) { if ( oTemp.IsName("S") ) { eBorderType = annotBorderSolid; } else if ( oTemp.IsName("D") ) { eBorderType = annotBorderDashed; } else if ( oTemp.IsName("B") ) { eBorderType = annotBorderBeveled; } else if ( oTemp.IsName("I") ) { eBorderType = annotBorderInset; } else if ( oTemp.IsName("U") ) { eBorderType = annotBorderUnderlined; } } oTemp.Free(); if ( oDictItem.DictLookup("W", &oTemp)->IsNum() ) { dBorderWidth = oTemp.GetNum(); } oTemp.Free(); if ( oDictItem.DictLookup("D", &oTemp)->IsArray() ) { nBorderDashLength = oTemp.ArrayGetLength(); pBorderDash = (double *)MemUtilsMallocArray( nBorderDashLength, sizeof(double) ); for ( int nIndex = 0; nIndex < nBorderDashLength; ++nIndex ) { Object oArrayItem; if ( oTemp.ArrayGet( nIndex, &oArrayItem)->IsNum() ) { pBorderDash[nIndex] = oArrayItem.GetNum(); } else { pBorderDash[nIndex] = 1; } oArrayItem.Free(); } } oTemp.Free(); } else { oDictItem.Free(); if ( pDict->Search("Border", &oDictItem)->IsArray() ) { Object oTemp; if ( oDictItem.ArrayGetLength() >= 3 ) { if ( oDictItem.ArrayGet( 2, &oTemp)->IsNum() ) { dBorderWidth = oTemp.GetNum(); } oTemp.Free(); if ( oDictItem.ArrayGetLength() >= 4 ) { if ( oDictItem.ArrayGet( 3, &oTemp)->IsArray() ) { eBorderType = annotBorderDashed; nBorderDashLength = oTemp.ArrayGetLength(); pBorderDash = (double *)MemUtilsMallocArray( nBorderDashLength, sizeof(double) ); for ( int nIndex = 0; nIndex < nBorderDashLength; ++nIndex ) { Object oArrayItem; if ( oTemp.ArrayGet( nIndex, &oArrayItem)->IsNum() ) { pBorderDash[nIndex] = oArrayItem.GetNum(); } else { pBorderDash[nIndex] = 1; } oArrayItem.Free(); } } else { dBorderWidth = 0; } oTemp.Free(); } } } } oDictItem.Free(); // C (border color) // TO DO: надо сделать чтение случаев, когда в массике не только 3 элемента, а 0, 1, 3, 4 if ( pDict->Search("C", &oDictItem)->IsArray() && oDictItem.ArrayGetLength() == 3 ) { Object oTemp; if ( oDictItem.ArrayGet(0, &oTemp)->IsNum() ) { dBorderR = oTemp.GetNum(); } oTemp.Free(); if ( oDictItem.ArrayGet(1, &oTemp)->IsNum() ) { dBorderG = oTemp.GetNum(); } oTemp.Free(); if ( oDictItem.ArrayGet(2, &oTemp)->IsNum() ) { dBorderB = oTemp.GetNum(); } oTemp.Free(); } oDictItem.Free(); m_pBorderStyle = new AnnotBorderStyle( eBorderType, dBorderWidth, pBorderDash, nBorderDashLength, dBorderR, dBorderG, dBorderB); // AP (appereance) Object oAP; if ( pDict->Search("AP", &oAP)->IsDict() ) { Object oAS; if ( pDict->Search("AS", &oAS)->IsName() ) { Object oItem; if ( oAP.DictLookup("N", &oItem)->IsDict() ) { Object oTemp; if ( oItem.DictLookupAndCopy( oAS.GetName(), &oTemp)->IsRef() ) { oTemp.Copy(&m_oAppearance); m_bValid = TRUE; } else { oTemp.Free(); if ( oItem.DictLookupAndCopy("Off", &oTemp)->IsRef() ) { oTemp.Copy(&m_oAppearance); } } oTemp.Free(); } oItem.Free(); } else { Object oItem; if ( oAP.DictLookupAndCopy("N", &oItem)->IsRef() ) { oItem.Copy(&m_oAppearance); } oItem.Free(); } oAS.Free(); } oAP.Free(); } Annot::~Annot() { if ( m_seType ) { delete m_seType; } m_oAppearance.Free(); if ( m_seAppBuffer ) { delete m_seAppBuffer; } if ( m_pBorderStyle ) { delete m_pBorderStyle; } } void Annot::GenerateFieldAppearance(Dict *pField, Dict *pAnnot, Dict *pAcroForm) { // Тип Annotation должен быть Widget if ( m_seType->Compare("Widget") ) { return; } m_seAppBuffer = new StringExt(); // Appearance characteristics (MK) dictionary Object oMKObject; Dict *pMKDict; if ( pAnnot->Search("MK", &oMKObject)->IsDict() ) { pMKDict = oMKObject.GetDict(); } else { pMKDict = NULL; } // Рисуем фон (BackGround) Object oDictItem; if ( pMKDict ) { if ( pMKDict->Search("BG", &oDictItem)->IsArray() && oDictItem.ArrayGetLength() > 0 ) { SetColor( oDictItem.GetArray(), TRUE, 0 ); m_seAppBuffer->AppendFormat("0 0 {0:.2f} {1:.2f} re f\n", m_dMaxX - m_dMinX, m_dMaxY - m_dMinY); } oDictItem.Free(); } // Считываем тип поля // FT (File Type) Object oFTobject; FieldLookup( pField, "FT", &oFTobject); // Ff (Filed flags) int nFiledFlags = 0; if ( FieldLookup( pField, "Ff", &oDictItem)->IsInt() ) { nFiledFlags = oDictItem.GetInt(); } else { nFiledFlags = 0; } oDictItem.Free(); // Рисуем рамку if ( pMKDict ) { double dWidth = m_pBorderStyle->GetWidth(); if ( dWidth > 0 ) { // BC (Border Color), если такого поля нет, тогда используем цвет из BG pMKDict->Search("BC", &oDictItem); if ( !(oDictItem.IsArray() && oDictItem.ArrayGetLength() > 0) ) { pMKDict->Search("BG", &oDictItem); } if ( oDictItem.IsArray() && oDictItem.ArrayGetLength() > 0 ) { double dDx = m_dMaxX - m_dMinX; double dDy = m_dMaxY - m_dMinY; double *pDash; int nDashLength = 0; Object oTemp; BOOL bHasCaption = pMKDict->Search("CA", &oTemp)->IsString(); oTemp.Free(); if ( oFTobject.IsName("Btn") && ( nFiledFlags & fieldFlagRadio ) && !bHasCaption ) { double dRad = 0.5 * (dDx < dDy ? dDx : dDy); switch ( m_pBorderStyle->GetType() ) { case annotBorderDashed: m_seAppBuffer->Append("["); m_pBorderStyle->GetDash( &pDash, &nDashLength ); for ( int nIndex = 0; nIndex < nDashLength; ++nIndex ) { m_seAppBuffer->AppendFormat(" {0:.2f}", pDash[nIndex]); } m_seAppBuffer->Append("] 0 d\n"); // Дальше как в случае не пунктирной линии case annotBorderSolid: case annotBorderUnderlined: m_seAppBuffer->AppendFormat("{0:.2f} w\n", dWidth); SetColor( oDictItem.GetArray(), FALSE, 0); DrawCircle( 0.5 * dDx, 0.5 * dDy, dRad - 0.5 * dWidth, FALSE); break; case annotBorderBeveled: case annotBorderInset: m_seAppBuffer->AppendFormat("{0:.2f} w\n", 0.5 * dWidth); SetColor( oDictItem.GetArray(), FALSE, 0); DrawCircle( 0.5 * dDx, 0.5 * dDy, dRad - 0.25 * dWidth, FALSE); SetColor( oDictItem.GetArray(), FALSE, m_pBorderStyle->GetType() == annotBorderBeveled ? 1 : -1); DrawCircleTopLeft( 0.5 * dDx, 0.5 * dDy, dRad - 0.75 * dWidth); SetColor( oDictItem.GetArray(), FALSE, m_pBorderStyle->GetType() == annotBorderBeveled ? -1 : 1); DrawCircleBottomRight(0.5 * dDx, 0.5 * dDy, dRad - 0.75 * dWidth); break; } } else { switch ( m_pBorderStyle->GetType() ) { case annotBorderDashed: m_seAppBuffer->Append("["); m_pBorderStyle->GetDash( &pDash, &nDashLength ); for ( int nIndex = 0; nIndex < nDashLength; ++nIndex ) { m_seAppBuffer->AppendFormat(" {0:.2f}", pDash[nIndex]); } m_seAppBuffer->Append("] 0 d\n"); // Дальше как в случае не пунктирной линии case annotBorderSolid: m_seAppBuffer->AppendFormat("{0:.2f} w\n", dWidth); SetColor( oDictItem.GetArray(), FALSE, 0); m_seAppBuffer->AppendFormat("{0:.2f} {0:.2f} {1:.2f} {2:.2f} re s\n", 0.5 * dWidth, dDx - dWidth, dDy - dWidth); break; case annotBorderBeveled: case annotBorderInset: SetColor( oDictItem.GetArray(), TRUE, m_pBorderStyle->GetType() == annotBorderBeveled ? 1 : -1); m_seAppBuffer->Append("0 0 m\n"); m_seAppBuffer->AppendFormat("0 {0:.2f} l\n", dDy); m_seAppBuffer->AppendFormat("{0:.2f} {1:.2f} l\n", dDx, dDy); m_seAppBuffer->AppendFormat("{0:.2f} {1:.2f} l\n", dDx - dWidth, dDy - dWidth); m_seAppBuffer->AppendFormat("{0:.2f} {1:.2f} l\n", dWidth, dDy - dWidth); m_seAppBuffer->AppendFormat("{0:.2f} {0:.2f} l\n", dWidth); m_seAppBuffer->Append("f\n"); SetColor( oDictItem.GetArray(), TRUE, m_pBorderStyle->GetType() == annotBorderBeveled ? -1 : 1); m_seAppBuffer->Append("0 0 m\n"); m_seAppBuffer->AppendFormat("{0:.2f} 0 l\n", dDx); m_seAppBuffer->AppendFormat("{0:.2f} {1:.2f} l\n", dDx, dDy); m_seAppBuffer->AppendFormat("{0:.2f} {1:.2f} l\n", dDx - dWidth, dDy - dWidth); m_seAppBuffer->AppendFormat("{0:.2f} {1:.2f} l\n", dDx - dWidth, dWidth); m_seAppBuffer->AppendFormat("{0:.2f} {0:.2f} l\n", dWidth); m_seAppBuffer->Append("f\n"); break; case annotBorderUnderlined: m_seAppBuffer->AppendFormat("{0:.2f} w\n", dWidth); SetColor( oDictItem.GetArray(), FALSE, 0); m_seAppBuffer->AppendFormat("0 0 m {0:.2f} 0 l s\n", dDx); break; } m_seAppBuffer->AppendFormat("{0:.2f} {0:.2f} {1:.2f} {2:.2f} re W n\n", dWidth, dDx - 2 * dWidth, dDy - 2 * dWidth); } } oDictItem.Free(); } } // DR Object oDRobject; pAcroForm->Search("DR", &oDRobject); // Font dictionary GrFontDict *pFontDict; if ( oDRobject.IsDict() && oDRobject.DictLookup("Font", &oDictItem)->IsDict() ) { pFontDict = new GrFontDict( m_pXref, NULL, oDictItem.GetDict(), m_pGlobalParams ); } else { pFontDict = NULL; } oDictItem.Free(); // DA (Default appereance string) StringExt *seDA; if ( FieldLookup( pField, "DA", &oDictItem )->IsNull() ) { oDictItem.Free(); pAcroForm->Search("DA", &oDictItem); } if ( oDictItem.IsString() ) { seDA = oDictItem.GetString()->Copy(); } else { seDA = NULL; } oDictItem.Free(); // Рисуем содержимео Filed if ( oFTobject.IsName("Btn") ) { StringExt *seCaption = NULL; if ( pMKDict ) { if ( pMKDict->Search("CA", &oDictItem)->IsString() ) { seCaption = oDictItem.GetString()->Copy(); } oDictItem.Free(); } // Radio button if ( nFiledFlags & fieldFlagRadio ) { // Акробат не рисует Caption, если нет AP if ( FieldLookup( pField, "V", &oDictItem )->IsName() ) { Object oAppStream; if ( pAnnot->Search("AS", &oAppStream)->IsName( oDictItem.GetName() ) ) { if ( seCaption ) { DrawText( seCaption, seDA, pFontDict, FALSE, 0, fieldQuadCenter, FALSE, TRUE); } else { if ( pMKDict ) { Object oTemp; if ( pMKDict->Search("BC", &oTemp)->IsArray() && oTemp.ArrayGetLength() > 0 ) { double dDx = m_dMaxX - m_dMinX; double dDy = m_dMaxY - m_dMinY; SetColor( oTemp.GetArray(), TRUE, 0 ); DrawCircle( 0.5 * dDx, 0.5 * dDy, 0.2 * (dDx < dDy ? dDx : dDy), TRUE); } oTemp.Free(); } } } oAppStream.Free(); } oDictItem.Free(); } else if ( nFiledFlags & fieldFlagPushbutton ) // PushButton { if ( seCaption ) { DrawText( seCaption, seDA, pFontDict, FALSE, 0, fieldQuadCenter, FALSE, FALSE); } } else // CheckBox { // "Yes" - для включенного состояние, "off" - для выключенного. if ( FieldLookup( pField, "V", &oDictItem )->IsName("Yes") ) { if ( !seCaption ) { seCaption = new StringExt("3"); // ZapfDingbats checkmark } DrawText( seCaption, seDA, pFontDict, FALSE, 0, fieldQuadCenter, FALSE, TRUE); } oDictItem.Free(); } if ( seCaption ) { delete seCaption; } } else if ( oFTobject.IsName("Tx") ) { // Строковые значения могут быть юникодными Object oValue; if ( FieldLookup( pField, "V", &oValue )->IsString() ) { Object oTemp; int nQuadding = 0; if ( FieldLookup( pField, "Q", &oTemp )->IsInt() ) { nQuadding = oTemp.GetInt(); } else { nQuadding = fieldQuadLeft; } oTemp.Free(); int nComb = 0; if ( nFiledFlags & fieldFlagComb ) { if ( FieldLookup( pField, "MaxLen", &oTemp )->IsInt()) { nComb = oTemp.GetInt(); } oTemp.Free(); } DrawText( oValue.GetString(), seDA, pFontDict, nFiledFlags & fieldFlagMultiline, nComb, nQuadding, TRUE, FALSE); } oValue.Free(); } else if ( oFTobject.IsName("Ch") ) { // Строковые значения могут быть юникодными int nQuadding = 0; Object oTemp; if ( FieldLookup( pField, "Q", &oTemp )->IsInt() ) { nQuadding = oTemp.GetInt(); } else { nQuadding = fieldQuadLeft; } oTemp.Free(); // ComboBox if ( nFiledFlags & fieldFlagCombo ) { Object oValue; if ( FieldLookup( pField, "V", &oValue)->IsString() ) { DrawText( oValue.GetString(), seDA, pFontDict, FALSE, 0, nQuadding, TRUE, FALSE); //~ Acrobat draws a popup icon on the right side } oValue.Free(); } else // ListBox { Object oOptions; if ( pField->Search("Opt", &oOptions)->IsArray() ) { int nOptionsCount = oOptions.ArrayGetLength(); // Option text StringExt **ppText = (StringExt **)MemUtilsMallocArray(nOptionsCount, sizeof(StringExt *)); for ( int nIndex = 0; nIndex < nOptionsCount; ++nIndex ) { ppText[nIndex] = NULL; Object oTemp; oOptions.ArrayGet( nIndex, &oTemp ); if ( oTemp.IsString() ) { ppText[nIndex] = oTemp.GetString()->Copy(); } else if ( oTemp.IsArray() && oTemp.ArrayGetLength() == 2 ) { Object oArrayItem; if ( oTemp.ArrayGet( 1, &oArrayItem)->IsString() ) { ppText[nIndex] = oArrayItem.GetString()->Copy(); } oArrayItem.Free(); } oTemp.Free(); if ( !ppText[nIndex] ) { ppText[nIndex] = new StringExt(); } } // Selected option(s) BOOL *pSelection = (BOOL *)MemUtilsMallocArray( nOptionsCount, sizeof(BOOL)); //~ need to use the I field in addition to the V field Object oValue; FieldLookup( pField, "V", &oValue); for ( int nIndex = 0; nIndex < nOptionsCount; ++nIndex ) { pSelection[nIndex] = FALSE; if ( oValue.IsString() ) { if ( !oValue.GetString()->Compare( ppText[nIndex] ) ) { pSelection[nIndex] = TRUE; } } else if ( oValue.IsArray() ) { for ( int nJ = 0; nJ < oValue.ArrayGetLength(); ++nJ ) { Object oTemp; if ( oValue.ArrayGet( nJ, &oTemp)->IsString() && !oTemp.GetString()->Compare( ppText[nIndex] ) ) { pSelection[nIndex] = TRUE; } oTemp.Free(); } } } oValue.Free(); // TI (Top Index) int nTopIndex = 0; if ( pField->Search("TI", &oValue)->IsInt() ) { nTopIndex = oValue.GetInt(); } else { nTopIndex = 0; } oValue.Free(); DrawListBox( ppText, pSelection, nOptionsCount, nTopIndex, seDA, pFontDict, nQuadding); for ( int nIndex = 0; nIndex < nOptionsCount; ++nIndex ) { delete ppText[nIndex]; } MemUtilsFree( ppText ); MemUtilsFree( pSelection ); } oOptions.Free(); } } else if ( oFTobject.IsName("Sig") ) { //~unimp } else { // TO DO: Error "Unknown field type" } if ( seDA ) { delete seDA; } // Appearance stream dictionary Object oAppearanceDict; oAppearanceDict.InitDict( m_pXref ); oAppearanceDict.DictAdd(CopyString("Length"), oDictItem.InitInt(m_seAppBuffer->GetLength())); oAppearanceDict.DictAdd(CopyString("Subtype"), oDictItem.InitName("Form")); oDictItem.InitArray( m_pXref ); Object oTemp; oDictItem.ArrayAdd( oTemp.InitReal(0) ); oDictItem.ArrayAdd( oTemp.InitReal(0) ); oDictItem.ArrayAdd( oTemp.InitReal(m_dMaxX - m_dMinX) ); oDictItem.ArrayAdd( oTemp.InitReal(m_dMaxY - m_dMinY) ); oAppearanceDict.DictAdd(CopyString("BBox"), &oDictItem); // Resource dictionary if ( oDRobject.IsDict() ) { oAppearanceDict.DictAdd(CopyString("Resources"), oDRobject.Copy(&oDictItem)); } oDRobject.Free(); // Appearance stream MemoryStream *pAppearStream = new MemoryStream( m_seAppBuffer->GetBuffer(), 0, m_seAppBuffer->GetLength(), &oAppearanceDict); m_oAppearance.Free(); m_oAppearance.InitStream( pAppearStream ); if ( pFontDict ) { delete pFontDict; } oFTobject.Free(); oMKObject.Free(); } // Устанавливаем цвет для заливки или обводки; цвет здается массиом (в котором должно быть // 1, 3, или 4 элемента). Если равно +1, цвет color is высветляется, если равно // -1, цвет затемняется, в противном случае, цвет не меняется. void Annot::SetColor(Array *pArray, BOOL bFill, int nAdjust) { double arrColor[4]; int nComponentsCount = pArray->GetCount(); if ( nComponentsCount > 4 ) { nComponentsCount = 4; } for ( int nIndex = 0; nIndex < nComponentsCount && nIndex < 4; ++nIndex ) { Object oTemp; if ( pArray->Get( nIndex, &oTemp)->IsNum() ) { arrColor[nIndex] = oTemp.GetNum(); } else { arrColor[nIndex] = 0; } oTemp.Free(); } if ( 4 == nComponentsCount ) { nAdjust = -nAdjust; } if ( nAdjust > 0 ) { for ( int nIndex = 0; nIndex < nComponentsCount; ++nIndex ) { arrColor[nIndex] = 0.5 * arrColor[nIndex] + 0.5; } } else if ( nAdjust < 0 ) { for ( int nIndex = 0; nIndex < nComponentsCount; ++nIndex ) { arrColor[nIndex] = 0.5 * arrColor[nIndex]; } } if ( 4 == nComponentsCount ) { m_seAppBuffer->AppendFormat("{0:.2f} {1:.2f} {2:.2f} {3:.2f} {4:c}\n", arrColor[0], arrColor[1], arrColor[2], arrColor[3], bFill ? 'k' : 'K'); } else if ( 3 == nComponentsCount ) { m_seAppBuffer->AppendFormat("{0:.2f} {1:.2f} {2:.2f} {3:s}\n", arrColor[0], arrColor[1], arrColor[2], bFill ? "rg" : "RG"); } else { m_seAppBuffer->AppendFormat("{0:.2f} {1:c}\n", arrColor[0], bFill ? 'g' : 'G'); } } // Рисуем текст или название для Field. void Annot::DrawText(StringExt *seText, StringExt *seDA, GrFontDict *pFontDict, BOOL bMultiline, int nComb, int nQuadding, BOOL bTextField, BOOL bForceZapfDingbats) { CList *pListDA; StringExt *seToken; double dX, dY, dWidth; int i, j, k, nChar; // Парсим Default Appearance string int nTfPos = -1, nTmPos = -1; if ( seDA ) { pListDA = new CList(); i = 0; while ( i < seDA->GetLength() ) { while ( i < seDA->GetLength() && Lexer::IsSpace( seDA->GetAt(i) ) ) { ++i; } if ( i < seDA->GetLength() ) { for ( j = i + 1; j < seDA->GetLength() && !Lexer::IsSpace(seDA->GetAt(j) ); ++j) ; pListDA->Append(new StringExt(seDA, i, j - i)); i = j; } } for ( i = 2; i < pListDA->GetLength(); ++i ) { if ( i >= 2 && !((StringExt *)pListDA->GetByIndex(i))->Compare("Tf") ) { nTfPos = i - 2; } else if ( i >= 6 && !((StringExt *)pListDA->GetByIndex(i))->Compare("Tm") ) { nTmPos = i - 6; } } } else { pListDA = NULL; } // ZapfDingbats if ( bForceZapfDingbats ) { if ( nTfPos >= 0 ) { seToken = (StringExt *)pListDA->GetByIndex( nTfPos ); if ( seToken->Compare("/ZaDb") ) { seToken->Clear(); seToken->Append("/ZaDb"); } } } // Считываем шрифт и размер шрифта GrFont *pFont = NULL; double dFontSize = 0; if ( nTfPos >= 0 ) { seToken = (StringExt *)pListDA->GetByIndex( nTfPos ); if ( seToken->GetLength() >= 1 && seToken->GetAt(0) == '/' ) { if ( !pFontDict || !(pFont = pFontDict->Search(seToken->GetBuffer() + 1) ) ) { // TO DO: Error "Unknown font in field's DA string" } } else { // TO DO: Error "Invalid font name in 'Tf' operator in field's DA string" } seToken = (StringExt *)pListDA->GetByIndex( nTfPos + 1 ); dFontSize = atof( seToken->GetBuffer()); } else { // TO DO: Error "Missing 'Tf' operator in field's DA string" } // Border Width double dBorderWidth = m_pBorderStyle->GetWidth(); if ( bTextField ) { m_seAppBuffer->Append("/Tx BMC\n"); } m_seAppBuffer->Append("q\n"); m_seAppBuffer->Append("BT\n"); // Многострочный текст if ( bMultiline ) { // Флаги nComb игнорируются в многострочном тексте double dWMax = m_dMaxX - m_dMinX - 2 * dBorderWidth - 4; // Вычислим размер шрифта (если он не был установлен) if ( 0 == dFontSize ) { for ( dFontSize = 20; dFontSize > 1; --dFontSize ) { dY = m_dMaxY - m_dMinY; double dMaxLineWidth = 0; i = 0; while ( i < seText->GetLength() ) { GetNextLine( seText, i, pFont, dFontSize, dWMax, &j, &dWidth, &k); if ( dWidth > dMaxLineWidth ) { dMaxLineWidth = dWidth; } i = k; dY -= dFontSize; } if ( dY >= 0.33 * dFontSize ) { break; } } if ( nTfPos >= 0 ) { seToken = (StringExt *)pListDA->GetByIndex( nTfPos + 1 ); seToken->Clear(); seToken->AppendFormat("{0:.2f}", dFontSize); } } // Начальная координата Y // (Каждая новая линий начинается с оператора Td) dY = m_dMaxY - m_dMinY; // Font Matrix if ( nTmPos >= 0 ) { seToken = (StringExt *)pListDA->GetByIndex( nTmPos + 4); seToken->Clear(); seToken->Append('0'); seToken = (StringExt *)pListDA->GetByIndex( nTmPos + 5); seToken->Clear(); seToken->AppendFormat("{0:.2f}", dY); } // Пишем DA-string if ( pListDA ) { for (i = 0; i < pListDA->GetLength(); ++i) { m_seAppBuffer->Append((StringExt *)pListDA->GetByIndex(i))->Append(' '); } } // Пишем Font Matrix (если она не была уже в DA-string) if ( nTmPos < 0 ) { m_seAppBuffer->AppendFormat("1 0 0 1 0 {0:.2f} Tm\n", dY); } i = 0; double dPrevX = 0; while ( i < seText->GetLength() ) { GetNextLine( seText, i, pFont, dFontSize, dWMax, &j, &dWidth, &k); // Вычислим начальную позицию текста switch ( nQuadding ) { case fieldQuadLeft: default: dX = dBorderWidth + 2; break; case fieldQuadCenter: dX = (m_dMaxX - m_dMinX - dWidth) / 2; break; case fieldQuadRight: dX = m_dMaxX - m_dMinX - dBorderWidth - 2 - dWidth; break; } m_seAppBuffer->AppendFormat("{0:.2f} {1:.2f} Td\n", dX - dPrevX, -dFontSize); m_seAppBuffer->Append('('); for (; i < j; ++i) { nChar = seText->GetAt(i) & 0xff; if ( nChar == '(' || nChar == ')' || nChar == '\\' ) { m_seAppBuffer->Append('\\'); m_seAppBuffer->Append(nChar); } else if ( nChar < 0x20 || nChar >= 0x80 ) { m_seAppBuffer->AppendFormat("\\{0:03o}", nChar); } else { m_seAppBuffer->Append(nChar); } } m_seAppBuffer->Append(") Tj\n"); // Следующая строка i = k; dPrevX = dX; } } else // Однострочный текст { if ( nComb > 0 ) { dWidth = (m_dMaxX - m_dMinX - 2 * dBorderWidth) / nComb; // Вычислим размер шрифта (если он не был установлен) if ( 0 == dFontSize ) { dFontSize = m_dMaxY - m_dMinY - 2 * dBorderWidth; if ( dWidth < dFontSize ) { dFontSize = dWidth; } dFontSize = floor(dFontSize); if ( nTfPos >= 0 ) { seToken = (StringExt *)pListDA->GetByIndex( nTfPos + 1 ); seToken->Clear(); seToken->AppendFormat("{0:.2f}", dFontSize); } } // Вычислим начальную позицию текста switch ( nQuadding ) { case fieldQuadLeft: default: dX = dBorderWidth + 2; break; case fieldQuadCenter: dX = dBorderWidth + 2 + 0.5 * (nComb - seText->GetLength()) * dWidth; break; case fieldQuadRight: dX = dBorderWidth + 2 + (nComb - seText->GetLength()) * dWidth; break; } dY = 0.5 * (m_dMaxY - m_dMinY) - 0.4 * dFontSize; // Font Matrix if ( nTmPos >= 0 ) { seToken = (StringExt *)pListDA->GetByIndex( nTmPos + 4 ); seToken->Clear(); seToken->AppendFormat("{0:.2f}", dX); seToken = (StringExt *)pListDA->GetByIndex( nTmPos + 5 ); seToken->Clear(); seToken->AppendFormat("{0:.2f}", dY); } // DA-string if ( pListDA ) { for (i = 0; i < pListDA->GetLength(); ++i) { m_seAppBuffer->Append((StringExt *)pListDA->GetByIndex(i))->Append(' '); } } // Пишем Font Matrix (если она не была уже в DA-string) if ( nTmPos < 0 ) { m_seAppBuffer->AppendFormat("1 0 0 1 {0:.2f} {1:.2f} Tm\n", dX, dY); } // write the text string //~ this should center (instead of left-justify) each character within //~ its comb cell for ( i = 0; i < seText->GetLength(); ++i ) { if ( i > 0 ) { m_seAppBuffer->AppendFormat("{0:.2f} 0 Td\n", dWidth); } m_seAppBuffer->Append('('); nChar = seText->GetAt(i) & 0xff; if ( nChar == '(' || nChar == ')' || nChar == '\\' ) { m_seAppBuffer->Append('\\'); m_seAppBuffer->Append(nChar); } else if ( nChar < 0x20 || nChar >= 0x80 ) { m_seAppBuffer->AppendFormat("{0:.2f} 0 Td\n", dWidth); } else { m_seAppBuffer->Append(nChar); } m_seAppBuffer->Append(") Tj\n"); } } else // обычное форматирование (non-comb) { // Вычислим ширину строки if ( pFont && !pFont->IsCIDFont() ) { dWidth = 0; for ( i = 0; i < seText->GetLength(); ++i ) { dWidth += ( (Gr8BitFont *)pFont )->GetWidth( seText->GetAt(i) ); } } else { dWidth = seText->GetLength() * 0.5; } // Вычислим размер шрифта (если он не был установлен) if ( 0 == dFontSize ) { dFontSize = m_dMaxY - m_dMinY - 2 * dBorderWidth; double dTempFontSize = (m_dMaxX - m_dMinX - 4 - 2 * dBorderWidth) / dWidth; if ( dTempFontSize < dFontSize ) { dFontSize = dTempFontSize; } dFontSize = floor(dFontSize); if ( nTfPos >= 0 ) { seToken = (StringExt *)pListDA->GetByIndex( nTfPos + 1 ); seToken->Clear(); seToken->AppendFormat("{0:.2f}", dFontSize); } } // Вычислим начальную позицию текста dWidth *= dFontSize; switch ( nQuadding ) { case fieldQuadLeft: default: dX = dBorderWidth + 2; break; case fieldQuadCenter: dX = (m_dMaxX - m_dMinX - dWidth) / 2; break; case fieldQuadRight: dX = m_dMaxX - m_dMinX - dBorderWidth - 2 - dWidth; break; } dY = 0.5 * (m_dMaxY - m_dMinY) - 0.4 * dFontSize; // Font Matrix if ( nTmPos >= 0 ) { seToken = (StringExt *)pListDA->GetByIndex( nTmPos + 4 ); seToken->Clear(); seToken->AppendFormat("{0:.2f}", dX); seToken = (StringExt *)pListDA->GetByIndex( nTmPos + 5 ); seToken->Clear(); seToken->AppendFormat("{0:.2f}", dY); } // DA-string if ( pListDA ) { for ( i = 0; i < pListDA->GetLength(); ++i) { m_seAppBuffer->Append((StringExt *)pListDA->GetByIndex(i))->Append(' '); } } // Пишем Font Matrix (если она не была уже в DA-string) if ( nTmPos < 0 ) { m_seAppBuffer->AppendFormat("1 0 0 1 {0:.2f} {1:.2f} Tm\n", dX, dY); } // Пишем текст m_seAppBuffer->Append('('); for ( i = 0; i < seText->GetLength(); ++i ) { nChar = seText->GetAt(i) & 0xff; if ( nChar == '(' || nChar == ')' || nChar == '\\' ) { m_seAppBuffer->Append('\\'); m_seAppBuffer->Append(nChar); } else if ( nChar < 0x20 || nChar >= 0x80 ) { m_seAppBuffer->AppendFormat("\\{0:03o}", nChar); } else { m_seAppBuffer->Append(nChar); } } m_seAppBuffer->Append(") Tj\n"); } } m_seAppBuffer->Append("ET\n"); m_seAppBuffer->Append("Q\n"); if ( bTextField ) { m_seAppBuffer->Append("EMC\n"); } if ( pListDA ) { DeleteCList( pListDA, StringExt); } } // Рисуем текст или название для Field. void Annot::DrawListBox(StringExt **ppText, BOOL *pSelection, int nOptionsCount, int nTopIndex, StringExt *seDA, GrFontDict *pFontDict, BOOL bQuadding) { CList *pListDA; StringExt *seToken; int i, j; // Парсим Default appearance string int nTfPos = -1, nTmPos = -1; if ( seDA ) { pListDA = new CList(); i = 0; while ( i < seDA->GetLength() ) { while ( i < seDA->GetLength() && Lexer::IsSpace(seDA->GetAt(i)) ) { ++i; } if ( i < seDA->GetLength() ) { for (j = i + 1; j < seDA->GetLength() && !Lexer::IsSpace(seDA->GetAt(j)); ++j) ; pListDA->Append(new StringExt(seDA, i, j - i)); i = j; } } for ( i = 2; i < pListDA->GetLength(); ++i ) { if ( i >= 2 && !((StringExt *)pListDA->GetByIndex(i))->Compare("Tf") ) { nTfPos = i - 2; } else if ( i >= 6 && !((StringExt *)pListDA->GetByIndex(i))->Compare("Tm") ) { nTmPos = i - 6; } } } else { pListDA = NULL; } // Считываем шрифт и размер шрифта GrFont *pFont = NULL; double dFontSize = 0; if ( nTfPos >= 0 ) { seToken = (StringExt *)pListDA->GetByIndex(nTfPos); if ( seToken->GetLength() >= 1 && seToken->GetAt(0) == '/' ) { if ( !pFontDict || !(pFont = pFontDict->Search(seToken->GetBuffer() + 1)) ) { // TO DO: Error "Unknown font in field's DA string" } } else { // TO DO: Error "Invalid font name in 'Tf' operator in field's DA string" } seToken = (StringExt *)pListDA->GetByIndex(nTfPos + 1); dFontSize = atof(seToken->GetBuffer()); } else { // TO DO: Error "Missing 'Tf' operator in field's DA string" } // Border width double dBorder = m_pBorderStyle->GetWidth(); // Вычислим размер фонта, если он не был задан double dWidth = 0; if ( 0 == dFontSize ) { double dMaxW = 0; for ( i = 0; i < nOptionsCount; ++i ) { if ( pFont && !pFont->IsCIDFont() ) { dWidth = 0; for ( j = 0; j < ppText[i]->GetLength(); ++j ) { dWidth += ((Gr8BitFont *)pFont)->GetWidth(ppText[i]->GetAt(j)); } } else { // грубая оценка dWidth = ppText[i]->GetLength() * 0.5; } if ( dWidth > dMaxW ) { dMaxW = dWidth; } } dFontSize = m_dMaxY - m_dMinY - 2 * dBorder; double dFontSize2 = (m_dMaxX - m_dMinX - 4 - 2 * dBorder) / dMaxW; if ( dFontSize2 < dFontSize ) { dFontSize = dFontSize2; } dFontSize = floor(dFontSize); if ( nTfPos >= 0 ) { seToken = (StringExt *)pListDA->GetByIndex(nTfPos + 1); seToken->Clear(); seToken->AppendFormat("{0:.2f}", dFontSize); } } // Рисуем текст double dY = m_dMaxY - m_dMinY - 1.1 * dFontSize; for ( i = nTopIndex; i < nOptionsCount; ++i ) { m_seAppBuffer->Append("q\n"); // Background if ( pSelection[i] ) { m_seAppBuffer->Append("0 g f\n"); m_seAppBuffer->AppendFormat("{0:.2f} {1:.2f} {2:.2f} {3:.2f} re f\n", dBorder, dY - 0.2 * dFontSize, m_dMaxX - m_dMinX - 2 * dBorder, 1.1 * dFontSize); } m_seAppBuffer->Append("BT\n"); // Вычислим ширину строки if ( pFont && !pFont->IsCIDFont() ) { dWidth = 0; for ( j = 0; j < ppText[i]->GetLength(); ++j ) { dWidth += ((Gr8BitFont *)pFont)->GetWidth(ppText[i]->GetAt(j)); } } else { // грубая оценка dWidth = ppText[i]->GetLength() * 0.5; } // Вычислим начальную позицию текста double dX = 0; dWidth *= dFontSize; switch ( bQuadding ) { case fieldQuadLeft: default: dX = dBorder + 2; break; case fieldQuadCenter: dX = (m_dMaxX - m_dMinX - dWidth) / 2; break; case fieldQuadRight: dX = m_dMaxX - m_dMinX - dBorder - 2 - dWidth; break; } // Font Matrix if ( nTmPos >= 0 ) { seToken = (StringExt *)pListDA->GetByIndex( nTmPos + 4 ); seToken->Clear(); seToken->AppendFormat("{0:.2f}", dX); seToken = (StringExt *)pListDA->GetByIndex( nTmPos + 5 ); seToken->Clear(); seToken->AppendFormat("{0:.2f}", dY); } // DA string if ( pListDA ) { for (j = 0; j < pListDA->GetLength(); ++j) { m_seAppBuffer->Append((StringExt *)pListDA->GetByIndex(j))->Append(' '); } } // Пишем Font Matrix (если он не был частью DA-string) if ( nTmPos < 0 ) { m_seAppBuffer->AppendFormat("1 0 0 1 {0:.2f} {1:.2f} Tm\n", dX, dY); } if ( pSelection[i] ) { m_seAppBuffer->Append("1 g\n"); } // Пишем текст m_seAppBuffer->Append('('); for ( j = 0; j < ppText[i]->GetLength(); ++j ) { int nChar = ppText[i]->GetAt(j) & 0xff; if ( nChar == '(' || nChar == ')' || nChar == '\\' ) { m_seAppBuffer->Append('\\'); m_seAppBuffer->Append(nChar); } else if ( nChar < 0x20 || nChar >= 0x80 ) { m_seAppBuffer->AppendFormat("\\{0:03o}", nChar); } else { m_seAppBuffer->Append(nChar); } } m_seAppBuffer->Append(") Tj\n"); m_seAppBuffer->Append("ET\n"); m_seAppBuffer->Append("Q\n"); // Следующая строка dY -= 1.1 * dFontSize; } if ( pListDA ) { DeleteCList( pListDA, StringExt ); } } void Annot::GetNextLine(StringExt *seText, int nStart, GrFont *pFont, double dFontSize, double dMaxW, int *pEnd, double *pWidth, int *pNext) { double dWidth = 0, dTempWidth; int j, k; for ( j = nStart; j < seText->GetLength() && dWidth <= dMaxW; ++j ) { int nChar = seText->GetAt(j) & 0xff; if ( nChar == 0x0a || nChar == 0x0d ) { break; } if ( pFont && !pFont->IsCIDFont() ) { dTempWidth = ((Gr8BitFont *)pFont)->GetWidth(nChar) * dFontSize; } else { // грубая оценка dTempWidth = 0.5 * dFontSize; } dWidth += dTempWidth; } if ( dWidth > dMaxW ) { for ( k = j; k > nStart && seText->GetAt(k - 1) != ' '; --k ); for ( ; k > nStart && seText->GetAt(k - 1) == ' '; --k ); if ( k > nStart ) { j = k; } if ( j == nStart ) { // Обрабатываем странный случай, когда первый символ слишком широкий для первой строки j = nStart + 1; } } *pEnd = j; // Вычисляем ширину dWidth = 0; for ( k = nStart; k < j; ++k ) { if ( pFont && !pFont->IsCIDFont() ) { dTempWidth = ((Gr8BitFont *)pFont)->GetWidth(seText->GetAt(k)) * dFontSize; } else { dTempWidth = 0.5 * dFontSize; } dWidth += dTempWidth; } *pWidth = dWidth; while (j < seText->GetLength() && seText->GetAt(j) == ' ') { ++j; } if ( j < seText->GetLength() && seText->GetAt(j) == 0x0d ) { ++j; } if ( j < seText->GetLength() && seText->GetAt(j) == 0x0a ) { ++j; } *pNext = j; } // Расстояние от центра до контрольных точек кривой Безье // = (4 * (sqrt(2) - 1) / 3) * r #define Kappa 0.55228475 // Рисуем окружность с помощью кривых Безье. (dX, dY) - центр, dRad - радиус. // bFill - будем заливать или обводить? void Annot::DrawCircle(double dX, double dY, double dRad, BOOL bFill) { m_seAppBuffer->AppendFormat("{0:.2f} {1:.2f} m\n", dX + dRad, dY); m_seAppBuffer->AppendFormat("{0:.2f} {1:.2f} {2:.2f} {3:.2f} {4:.2f} {5:.2f} c\n", dX + dRad, dY + Kappa * dRad, dX + Kappa * dRad, dY + dRad, dX, dY + dRad); m_seAppBuffer->AppendFormat("{0:.2f} {1:.2f} {2:.2f} {3:.2f} {4:.2f} {5:.2f} c\n", dX - Kappa * dRad, dY + dRad, dX - dRad, dY + Kappa * dRad, dX - dRad, dY ); m_seAppBuffer->AppendFormat("{0:.2f} {1:.2f} {2:.2f} {3:.2f} {4:.2f} {5:.2f} c\n", dX - dRad, dY - Kappa * dRad, dX - Kappa * dRad, dY - dRad, dX, dY - dRad); m_seAppBuffer->AppendFormat("{0:.2f} {1:.2f} {2:.2f} {3:.2f} {4:.2f} {5:.2f} c\n", dX + Kappa * dRad, dY - dRad, dX + dRad, dY - Kappa * dRad, dX + dRad, dY ); m_seAppBuffer->Append( bFill ? "f\n" : "s\n" ); } // Рисуем половину окружности (левую верхнюю) с помощью кривых Безье. (dX, dY) - центр, dRad - радиус. void Annot::DrawCircleTopLeft(double dX, double dY, double dRad) { double dRad2 = dRad / sqrt(2.0); m_seAppBuffer->AppendFormat("{0:.2f} {1:.2f} m\n", dX + dRad2, dY + dRad2); m_seAppBuffer->AppendFormat("{0:.2f} {1:.2f} {2:.2f} {3:.2f} {4:.2f} {5:.2f} c\n", dX + (1 - Kappa) * dRad2, dY + (1 + Kappa) * dRad2, dX - (1 - Kappa) * dRad2, dY + (1 + Kappa) * dRad2, dX - dRad2, dY + dRad2); m_seAppBuffer->AppendFormat("{0:.2f} {1:.2f} {2:.2f} {3:.2f} {4:.2f} {5:.2f} c\n", dX - (1 + Kappa) * dRad2, dY + (1 - Kappa) * dRad2, dX - (1 + Kappa) * dRad2, dY - (1 - Kappa) * dRad2, dX - dRad2, dY - dRad2); m_seAppBuffer->Append("S\n"); } // Рисуем половину окружности (правую нижнюю) с помощью кривых Безье. (dX, dY) - центр, dRad - радиус. void Annot::DrawCircleBottomRight(double dX, double dY, double dRad) { double dRad2 = dRad / sqrt(2.0); m_seAppBuffer->AppendFormat("{0:.2f} {1:.2f} m\n", dX - dRad2, dY - dRad2); m_seAppBuffer->AppendFormat("{0:.2f} {1:.2f} {2:.2f} {3:.2f} {4:.2f} {5:.2f} c\n", dX - (1 - Kappa) * dRad2, dY - (1 + Kappa) * dRad2, dX + (1 - Kappa) * dRad2, dY - (1 + Kappa) * dRad2, dX + dRad2, dY - dRad2); m_seAppBuffer->AppendFormat("{0:.2f} {1:.2f} {2:.2f} {3:.2f} {4:.2f} {5:.2f} c\n", dX + (1 + Kappa) * dRad2, dY - (1 - Kappa) * dRad2, dX + (1 + Kappa) * dRad2, dY + (1 - Kappa) * dRad2, dX + dRad2, dY + dRad2); m_seAppBuffer->Append("S\n"); } Object *Annot::FieldLookup(Dict *pField, char *sKey, Object *pObject) { Dict *pDict = pField; if ( !pDict->Search(sKey, pObject)->IsNull() ) { return pObject; } pObject->Free(); Object oParent; if ( pDict->Search("Parent", &oParent)->IsDict() ) { FieldLookup( oParent.GetDict(), sKey, pObject); } else { pObject->InitNull(); } oParent.Free(); return pObject; } void Annot::Draw(Graphics *pGraphics, BOOL bPrinting) { // Flags if ( (m_nFlags & annotFlagHidden) || (bPrinting && !(m_nFlags & annotFlagPrint)) || (!bPrinting && (m_nFlags & annotFlagNoView)) ) { return; } // Appearance stream BOOL bIsLink = m_seType && !m_seType->Compare("Link"); Object oTemp; m_oAppearance.Fetch( m_pXref, &oTemp); pGraphics->DrawAnnotation( &oTemp, (bIsLink ? m_pBorderStyle : (AnnotBorderStyle *)NULL), m_dMinX, m_dMinY, m_dMaxX, m_dMaxY); oTemp.Free(); } //------------------------------------------------------------------------------------------------------------------------------- // Annots //------------------------------------------------------------------------------------------------------------------------------- Annots::Annots(GlobalParams *pGlobalParams, XRef *pXref, Catalog *pCatalog, Object *pAnnotsObject) { m_pGlobalParams = pGlobalParams; Annot *pAnnot = NULL; Ref oRef; m_ppAnnots = NULL; int nSize = 0; m_nAnnotsCount = 0; Dict *pAcroForm = (pCatalog->GetAcroForm()->IsDict() ? pCatalog->GetAcroForm()->GetDict() : NULL); if ( pAnnotsObject->IsArray() ) { for ( int nIndex = 0; nIndex < pAnnotsObject->ArrayGetLength(); ++nIndex ) { Object oTemp; if ( pAnnotsObject->ArrayGetCopy(nIndex, &oTemp)->IsRef() ) { oRef = oTemp.GetRef(); oTemp.Free(); pAnnotsObject->ArrayGet(nIndex, &oTemp); } else { oRef.nNum = oRef.nGen = -1; } if ( oTemp.IsDict() ) { pAnnot = new Annot( m_pGlobalParams, pXref, pAcroForm, oTemp.GetDict(), &oRef ); if ( pAnnot->CheckValidation() ) { if ( m_nAnnotsCount >= nSize ) { nSize += 16; m_ppAnnots = (Annot **)MemUtilsReallocArray(m_ppAnnots, nSize, sizeof(Annot *)); } m_ppAnnots[m_nAnnotsCount++] = pAnnot; } else { delete pAnnot; } } oTemp.Free(); } } } Annots::~Annots() { for ( int nIndex = 0; nIndex < m_nAnnotsCount; ++nIndex ) { delete m_ppAnnots[nIndex]; } MemUtilsFree( m_ppAnnots ); } void Annots::GenerateAppearances(Dict *pAcroForm) { Ref oRef; Object oField; if ( pAcroForm->Search("Fields", &oField)->IsArray() ) { for ( int nIndex = 0; nIndex < oField.ArrayGetLength(); ++nIndex ) { Object oItem; if ( oField.ArrayGetCopy(nIndex, &oItem)->IsRef() ) { oRef = oItem.GetRef(); oItem.Free(); oField.ArrayGet(nIndex, &oItem); } else { oRef.nNum = oRef.nGen = -1; } if ( oItem.IsDict() ) { ScanFieldAppearances( oItem.GetDict(), &oRef, NULL, pAcroForm); } oItem.Free(); } } oField.Free(); } void Annots::ScanFieldAppearances(Dict *pNode, Ref *pRef, Dict *pParent, Dict *pAcroForm) { Annot *pAnnot = NULL; Ref oRef2; Object oNodeItem; if ( pNode->Search("Kids", &oNodeItem)->IsArray() ) { for ( int nIndex = 0; nIndex < oNodeItem.ArrayGetLength(); ++nIndex ) { Object oTemp; if ( oNodeItem.ArrayGetCopy(nIndex, &oTemp)->IsRef() ) { oRef2 = oTemp.GetRef(); oTemp.Free(); oNodeItem.ArrayGet(nIndex, &oTemp); } else { oRef2.nNum = oRef2.nGen = -1; } if ( oTemp.IsDict() ) { ScanFieldAppearances( oTemp.GetDict(), &oRef2, pNode, pAcroForm); } oTemp.Free(); } oNodeItem.Free(); return; } oNodeItem.Free(); if ( (pAnnot = FindAnnot(pRef)) ) { pNode->SearchAndCopy("Parent", &oNodeItem); if ( !pParent || !oNodeItem.IsNull() ) { pAnnot->GenerateFieldAppearance( pNode, pNode, pAcroForm ); } else { pAnnot->GenerateFieldAppearance( pParent, pNode, pAcroForm ); } oNodeItem.Free(); } } Annot *Annots::FindAnnot(Ref *pRef) { for ( int nIndex = 0; nIndex < m_nAnnotsCount; ++nIndex) { if ( m_ppAnnots[nIndex]->Match(pRef) ) { return m_ppAnnots[nIndex]; } } return NULL; }