Files
core/PdfReader/Src/Annot.cpp
Alexander.Trofimov 04d197375c change license to 2017
2017-01-17 18:17:26 +03:00

1823 lines
47 KiB
C++
Raw Blame History

This file contains ambiguous Unicode characters

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

/*
* (c) Copyright Ascensio System SIA 2010-2017
*
* This program is a free software product. You can redistribute it and/or
* modify it under the terms of the GNU Affero General Public License (AGPL)
* version 3 as published by the Free Software Foundation. In accordance with
* Section 7(a) of the GNU AGPL its Section 15 shall be amended to the effect
* that Ascensio System SIA expressly excludes the warranty of non-infringement
* of any third-party rights.
*
* This program is distributed WITHOUT ANY WARRANTY; without even the implied
* warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For
* details, see the GNU AGPL at: http://www.gnu.org/licenses/agpl-3.0.html
*
* You can contact Ascensio System SIA at Lubanas st. 125a-25, Riga, Latvia,
* EU, LV-1021.
*
* The interactive user interfaces in modified source and object code versions
* of the Program must display Appropriate Legal Notices, as required under
* Section 5 of the GNU AGPL version 3.
*
* Pursuant to Section 7(b) of the License you must retain the original Product
* logo when distributing the program. Pursuant to Section 7(e) we decline to
* grant you any rights under trademark law for use of our trademarks.
*
* All the Product's GUI elements, including illustrations and icon sets, as
* well as technical writing content are licensed under the terms of the
* Creative Commons Attribution-ShareAlike 4.0 International. See the License
* terms at http://creativecommons.org/licenses/by-sa/4.0/legalcode
*
*/
#include <stdlib.h>
#include <math.h>
#include "MemoryUtils.h"
#include "List.h"
#include "Object.h"
#include "Catalog.h"
#include "Graphics.h"
#include "GFont.h"
#include "Lexer.h"
#include "Annot.h"
#include "Dict.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
namespace PdfReader
{
//-------------------------------------------------------------------------------------------------------------------------------
// 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, 0 != (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();
}
// Устанавливаем цвет для заливки или обводки; цвет здается массиом <pArray> (в котором должно быть
// 1, 3, или 4 элемента). Если <nAdjust> равно +1, цвет color is высветляется, если <nAdjust> равно
// -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, int nQuadding)
{
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 (nQuadding)
{
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;
}
}