mirror of
https://github.com/ONLYOFFICE/core.git
synced 2026-07-11 06:22:44 +08:00
Also checked: Common/3dParty/html/htmltoxhtml.cpp DesktopEditor/doctrenderer/drawingfile.h DjVuFile EpubFile Fb2File HtmlFile2/htmlfile2.cpp PdfFile XpsFile
1841 lines
50 KiB
C++
1841 lines
50 KiB
C++
/*
|
|
* (c) Copyright Ascensio System SIA 2010-2023
|
|
*
|
|
* 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 20A-6 Ernesta Birznieka-Upish
|
|
* street, Riga, Latvia, EU, LV-1050.
|
|
*
|
|
* 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 "Objects.h"
|
|
#include "Pages.h"
|
|
#include "Utils.h"
|
|
#include "GState.h"
|
|
#include "Streams.h"
|
|
#include "Annotation.h"
|
|
#include "Font.h"
|
|
#include "Image.h"
|
|
#include "Shading.h"
|
|
#include "Pattern.h"
|
|
#include "Document.h"
|
|
#include "Field.h"
|
|
#include "ResourcesDictionary.h"
|
|
|
|
#ifdef DrawText
|
|
#undef DrawText
|
|
#endif
|
|
|
|
#define MIN_HORIZONTALSCALING 0.01
|
|
#define MAX_HORIZONTALSCALING 1000
|
|
#define MIN_CHARSPACE -30
|
|
#define MAX_CHARSPACE 300
|
|
#define MAX_FONTSIZE 1000
|
|
|
|
#define STR_BUF 200
|
|
|
|
namespace PdfWriter
|
|
{
|
|
static const double c_dKappa = 0.552;
|
|
const static char* c_sRenderingIntent[] =
|
|
{
|
|
"AbsoluteColorimetric",
|
|
"RelativeColorimetric",
|
|
"Saturation",
|
|
"Perceptual"
|
|
};
|
|
static void QuarterEllipseA(CStream* pStream, double dX, double dY, double dXRad, double dYRad)
|
|
{
|
|
pStream->WriteReal(dX - dXRad);
|
|
pStream->WriteChar(' ');
|
|
pStream->WriteReal(dY + dYRad * c_dKappa);
|
|
pStream->WriteChar(' ');
|
|
pStream->WriteReal(dX - dXRad * c_dKappa);
|
|
pStream->WriteChar(' ');
|
|
pStream->WriteReal(dY + dYRad);
|
|
pStream->WriteChar(' ');
|
|
pStream->WriteReal(dX);
|
|
pStream->WriteChar(' ');
|
|
pStream->WriteReal(dY + dYRad);
|
|
pStream->WriteStr(" c\012");
|
|
}
|
|
static void QuarterEllipseB(CStream* pStream, double dX, double dY, double dXRad, double dYRad)
|
|
{
|
|
pStream->WriteReal(dX + dXRad * c_dKappa);
|
|
pStream->WriteChar(' ');
|
|
pStream->WriteReal(dY + dYRad);
|
|
pStream->WriteChar(' ');
|
|
pStream->WriteReal(dX + dXRad);
|
|
pStream->WriteChar(' ');
|
|
pStream->WriteReal(dY + dYRad * c_dKappa);
|
|
pStream->WriteChar(' ');
|
|
pStream->WriteReal(dX + dXRad);
|
|
pStream->WriteChar(' ');
|
|
pStream->WriteReal(dY);
|
|
pStream->WriteStr(" c\012");
|
|
}
|
|
static void QuarterEllipseC(CStream* pStream, double dX, double dY, double dXRad, double dYRad)
|
|
{
|
|
pStream->WriteReal(dX + dXRad);
|
|
pStream->WriteChar(' ');
|
|
pStream->WriteReal(dY - dYRad * c_dKappa);
|
|
pStream->WriteChar(' ');
|
|
pStream->WriteReal(dX + dXRad * c_dKappa);
|
|
pStream->WriteChar(' ');
|
|
pStream->WriteReal(dY - dYRad);
|
|
pStream->WriteChar(' ');
|
|
pStream->WriteReal(dX);
|
|
pStream->WriteChar(' ');
|
|
pStream->WriteReal(dY - dYRad);
|
|
pStream->WriteStr(" c\012");
|
|
}
|
|
static void QuarterEllipseD(CStream* pStream, double dX, double dY, double dXRad, double dYRad)
|
|
{
|
|
pStream->WriteReal(dX - dXRad * c_dKappa);
|
|
pStream->WriteChar(' ');
|
|
pStream->WriteReal(dY - dYRad);
|
|
pStream->WriteChar(' ');
|
|
pStream->WriteReal(dX - dXRad);
|
|
pStream->WriteChar(' ');
|
|
pStream->WriteReal(dY - dYRad * c_dKappa);
|
|
pStream->WriteChar(' ');
|
|
pStream->WriteReal(dX - dXRad);
|
|
pStream->WriteChar(' ');
|
|
pStream->WriteReal(dY);
|
|
pStream->WriteStr(" c\012");
|
|
}
|
|
static double AngToEllPrm(double dAngle, double dXRad, double dYRad)
|
|
{
|
|
// Function to convert real angle to parametric ellipse representation
|
|
// i.e. x= a cos(t) y = b sin(t) - parametric ellipse representation.
|
|
// x = r cos(p), y = r sin(p) => t = atan2( sin(p) / b, cos(p) / a );
|
|
return atan2(sin(dAngle) / dYRad, cos(dAngle) / dXRad);
|
|
}
|
|
static void WriteEllipseArc(CStream* pStream, double dX, double dY, double dXRad, double dYRad, double dAngle1, double dAngle2, double& dXCur, double& dYCur, bool bClockDirection = false)
|
|
{
|
|
// Calculate start, end and control points
|
|
double dX1 = 0.0, dX2 = 0.0, dY1 = 0.0, dY2 = 0.0;
|
|
double dCX1 = 0.0, dCX2 = 0.0, dCY1 = 0.0, dCY2 = 0.0;
|
|
|
|
double dAlpha = sin(dAngle2 - dAngle1) * (sqrt(4.0 + 3.0 * tan((dAngle2 - dAngle1) / 2.0) * tan((dAngle2 - dAngle1) / 2.0)) - 1.0) / 3.0;
|
|
|
|
dX1 = dX + dXRad * cos(dAngle1);
|
|
dY1 = dY + dYRad * sin(dAngle1);
|
|
|
|
dX2 = dX + dXRad * cos(dAngle2);
|
|
dY2 = dY + dYRad * sin(dAngle2);
|
|
|
|
dCX1 = dX1 - dAlpha * dXRad * sin(dAngle1);
|
|
dCY1 = dY1 + dAlpha * dYRad * cos(dAngle1);
|
|
|
|
dCX2 = dX2 + dAlpha * dXRad * sin(dAngle2);
|
|
dCY2 = dY2 - dAlpha * dYRad * cos(dAngle2);
|
|
|
|
if ( !bClockDirection )
|
|
{
|
|
pStream->WriteReal(dCX1);
|
|
pStream->WriteChar(' ');
|
|
pStream->WriteReal(dCY1);
|
|
pStream->WriteChar(' ');
|
|
pStream->WriteReal(dCX2);
|
|
pStream->WriteChar(' ');
|
|
pStream->WriteReal(dCY2);
|
|
pStream->WriteChar(' ');
|
|
pStream->WriteReal(dX2);
|
|
pStream->WriteChar(' ');
|
|
pStream->WriteReal(dY2);
|
|
dXCur = dX2;
|
|
dYCur = dY2;
|
|
}
|
|
else
|
|
{
|
|
pStream->WriteReal(dCX2);
|
|
pStream->WriteChar(' ');
|
|
pStream->WriteReal(dCY2);
|
|
pStream->WriteChar(' ');
|
|
pStream->WriteReal(dCX1);
|
|
pStream->WriteChar(' ');
|
|
pStream->WriteReal(dCY1);
|
|
pStream->WriteChar(' ');
|
|
pStream->WriteReal(dX1);
|
|
pStream->WriteChar(' ');
|
|
pStream->WriteReal(dY1);
|
|
dXCur = dX1;
|
|
dYCur = dY1;
|
|
}
|
|
pStream->WriteStr(" c\012");
|
|
}
|
|
//----------------------------------------------------------------------------------------
|
|
// CPageTree
|
|
//----------------------------------------------------------------------------------------
|
|
CPageTree::CPageTree(CXref* pXref)
|
|
{
|
|
pXref->Add(this);
|
|
|
|
m_pPages = new CArrayObject();
|
|
m_pCount = new CNumberObject(0);
|
|
|
|
Add("Type", "Pages");
|
|
Add("Kids", m_pPages);
|
|
Add("Count", m_pCount);
|
|
}
|
|
CPageTree::CPageTree()
|
|
{
|
|
m_pPages = NULL;
|
|
m_pCount = NULL;
|
|
}
|
|
void CPageTree::Fix()
|
|
{
|
|
// Initialize current m_pPages
|
|
CObjectBase* pPages = Get("Kids");
|
|
if (pPages && pPages->GetType() == object_type_ARRAY)
|
|
m_pPages = (CArrayObject*)pPages;
|
|
else
|
|
{
|
|
m_pPages = new CArrayObject();
|
|
Add("Kids", m_pPages);
|
|
}
|
|
|
|
// Initialize current m_pCount
|
|
CObjectBase* pCount = Get("Count");
|
|
if (pCount && pCount->GetType() == object_type_NUMBER)
|
|
m_pCount = (CNumberObject*)pCount;
|
|
else
|
|
{
|
|
m_pCount = new CNumberObject(0);
|
|
Add("Count", m_pCount);
|
|
}
|
|
|
|
CResourcesDict* pResources = (CResourcesDict*)Get("Resources");
|
|
if (pResources)
|
|
pResources->Fix();
|
|
}
|
|
void CPageTree::AddPage(CObjectBase* pObj)
|
|
{
|
|
m_pPages->Add(pObj);
|
|
(*m_pCount)++;
|
|
if (pObj->GetType() == object_type_DICT && ((CDictObject*)pObj)->GetDictType() == dict_type_PAGE)
|
|
((CPage*)pObj)->Add("Parent", this);
|
|
}
|
|
CObjectBase* CPageTree::GetObj(int nPageIndex)
|
|
{
|
|
int nI = 0;
|
|
return GetFromPageTree(nPageIndex, nI);
|
|
}
|
|
CPage* CPageTree::GetPage(int nPageIndex)
|
|
{
|
|
int nI = 0;
|
|
CObjectBase* pObj = GetFromPageTree(nPageIndex, nI);
|
|
if (pObj && pObj->GetType() == object_type_DICT && ((CDictObject*)pObj)->GetDictType() == dict_type_PAGE)
|
|
return (CPage*)pObj;
|
|
return NULL;
|
|
}
|
|
CObjectBase* CPageTree::RemovePage(int nPageIndex)
|
|
{
|
|
int nI = 0;
|
|
return GetFromPageTree(nPageIndex, nI, true);
|
|
}
|
|
bool CPageTree::InsertPage(int nPageIndex, CObjectBase* pPage)
|
|
{
|
|
if (nPageIndex >= m_pCount->Get())
|
|
{
|
|
AddPage(pPage);
|
|
return true;
|
|
}
|
|
int nI = 0;
|
|
CObjectBase* pObj = GetFromPageTree(nPageIndex, nI, false, true, pPage);
|
|
if (pObj)
|
|
return true;
|
|
return false;
|
|
}
|
|
bool CPageTree::ReplacePage(int nPageIndex, CPage* pPage)
|
|
{
|
|
if (nPageIndex >= m_pCount->Get())
|
|
return false;
|
|
int nI = 0;
|
|
CObjectBase* pObj = GetFromPageTree(nPageIndex, nI, true, true, pPage);
|
|
if (pObj)
|
|
return true;
|
|
return false;
|
|
}
|
|
bool CPageTree::Find(CPage* pPage, int& nI)
|
|
{
|
|
for (int i = 0, count = m_pPages->GetCount(); i < count; ++i)
|
|
{
|
|
CObjectBase* pObj = m_pPages->Get(i);
|
|
if (pObj->GetType() == object_type_DICT && ((CDictObject*)pObj)->GetDictType() == dict_type_PAGES && ((CPageTree*)pObj)->Find(pPage, nI))
|
|
return true;
|
|
else
|
|
{
|
|
if (pPage == pObj)
|
|
return true;
|
|
nI++;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
CObjectBase* CPageTree::GetFromPageTree(int nPageIndex, int& nI, bool bRemove, bool bInsert, CObjectBase* pPage)
|
|
{
|
|
for (int i = 0, count = m_pPages->GetCount(); i < count; ++i)
|
|
{
|
|
CObjectBase* pObj = m_pPages->Get(i);
|
|
CObjectBase* pRes = NULL;
|
|
if (pObj->GetType() == object_type_DICT && ((CDictObject*)pObj)->GetDictType() == dict_type_PAGES)
|
|
pRes = ((CPageTree*)pObj)->GetFromPageTree(nPageIndex, nI, bRemove, bInsert, pPage);
|
|
else
|
|
{
|
|
if (nPageIndex == nI)
|
|
{
|
|
pRes = pObj;
|
|
if (bRemove && bInsert)
|
|
{
|
|
m_pPages->Insert(pObj, pPage, true);
|
|
if (pPage->GetType() == object_type_DICT && ((CDictObject*)pPage)->GetDictType() == dict_type_PAGE)
|
|
((CPage*)pPage)->Add("Parent", this);
|
|
}
|
|
else if (bRemove)
|
|
pRes = m_pPages->Remove(i);
|
|
else if (bInsert)
|
|
{
|
|
m_pPages->Insert(pObj, pPage);
|
|
if (pPage->GetType() == object_type_DICT && ((CDictObject*)pPage)->GetDictType() == dict_type_PAGE)
|
|
((CPage*)pPage)->Add("Parent", this);
|
|
}
|
|
}
|
|
nI++;
|
|
}
|
|
if (pRes)
|
|
{
|
|
if (bRemove)
|
|
(*m_pCount)--;
|
|
if (bInsert)
|
|
(*m_pCount)++;
|
|
return pRes;
|
|
}
|
|
}
|
|
return NULL;
|
|
}
|
|
bool CPageTree::Join(CPageTree* pPageTree)
|
|
{
|
|
unsigned int nObjId = pPageTree->GetObjId();
|
|
unsigned int nGenNo = pPageTree->GetGenNo();
|
|
for (int i = 0, count = m_pPages->GetCount(); i < count; ++i)
|
|
{
|
|
CObjectBase* pObj = m_pPages->Get(i);
|
|
if (pObj->GetObjId() == nObjId && pObj->GetGenNo() == nGenNo)
|
|
{
|
|
m_pPages->Insert(pObj, pPageTree, true);
|
|
return true;
|
|
}
|
|
if (pObj->GetType() == object_type_DICT && ((CDictObject*)pObj)->GetDictType() == dict_type_PAGES && ((CPageTree*)pObj)->Join(pPageTree))
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
void CPageTree::CreateFakePages(int nPages, int nPageIndex)
|
|
{
|
|
for (int i = 0; i < nPages; ++i)
|
|
{
|
|
if (nPageIndex < 0)
|
|
m_pPages->Add(new CFakePage(i));
|
|
else
|
|
{
|
|
CObjectBase* pTarget = GetObj(nPageIndex);
|
|
if (pTarget)
|
|
m_pPages->Insert(pTarget, new CFakePage(nPageIndex));
|
|
else
|
|
m_pPages->Add(new CFakePage(m_pPages->GetCount()));
|
|
}
|
|
(*m_pCount)++;
|
|
}
|
|
}
|
|
void CPageTree::ClearFakePages()
|
|
{
|
|
for (int i = 0; i < GetCount(); ++i)
|
|
{
|
|
CObjectBase* pObj = GetObj(i);
|
|
if (pObj->GetType() == object_type_DICT && ((CDictObject*)pObj)->GetDictType() == dict_type_PAGE)
|
|
continue;
|
|
pObj = m_pPages->Remove(i);
|
|
delete pObj;
|
|
(*m_pCount)--;
|
|
--i;
|
|
}
|
|
}
|
|
//----------------------------------------------------------------------------------------
|
|
// CPage
|
|
//----------------------------------------------------------------------------------------
|
|
CPage::CPage(CDocument* pDocument)
|
|
{
|
|
Init(pDocument);
|
|
}
|
|
void CPage::Fix()
|
|
{
|
|
// Initialize current contents
|
|
CObjectBase* pContents = Get("Contents");
|
|
if (pContents)
|
|
{
|
|
if (pContents->GetType() == object_type_ARRAY)
|
|
m_pContents = (CArrayObject*)pContents;
|
|
else if (pContents->GetType() == object_type_UNKNOWN)
|
|
{
|
|
CProxyObject* pNewContents = new CProxyObject(pContents->Copy(), true);
|
|
pNewContents->Get()->SetRef(pContents->GetObjId(), pContents->GetGenNo());
|
|
m_pContents = new CArrayObject();
|
|
m_pContents->Add(pNewContents);
|
|
Add("Contents", m_pContents);
|
|
}
|
|
else if (pContents->GetType() == object_type_DICT)
|
|
{
|
|
m_pContents = new CArrayObject();
|
|
m_pContents->Add(pContents);
|
|
Add("Contents", m_pContents);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
m_pContents = new CArrayObject();
|
|
Add("Contents", m_pContents);
|
|
}
|
|
|
|
// Initialize current MediaBox
|
|
CObjectBase* pMediaBox = Get("MediaBox");
|
|
if (pMediaBox && pMediaBox->GetType() == object_type_ARRAY)
|
|
{
|
|
double dL = 0.0, dB = 0.0, dR = DEF_PAGE_WIDTH, dT = DEF_PAGE_HEIGHT;
|
|
|
|
CObjectBase* val = ((CArrayObject*)pMediaBox)->Get(0);
|
|
if (val && val->GetType() == object_type_NUMBER)
|
|
dL = ((CNumberObject*)val)->Get();
|
|
else if (val && val->GetType() == object_type_REAL)
|
|
dL = ((CRealObject*)val)->Get();
|
|
|
|
val = ((CArrayObject*)pMediaBox)->Get(1);
|
|
if (val && val->GetType() == object_type_NUMBER)
|
|
dB = ((CNumberObject*)val)->Get();
|
|
else if (val && val->GetType() == object_type_REAL)
|
|
dB = ((CRealObject*)val)->Get();
|
|
|
|
val = ((CArrayObject*)pMediaBox)->Get(2);
|
|
if (val && val->GetType() == object_type_NUMBER)
|
|
dR = ((CNumberObject*)val)->Get();
|
|
else if (val && val->GetType() == object_type_REAL)
|
|
dR = ((CRealObject*)val)->Get();
|
|
|
|
val = ((CArrayObject*)pMediaBox)->Get(3);
|
|
if (val && val->GetType() == object_type_NUMBER)
|
|
dT = ((CNumberObject*)val)->Get();
|
|
else if (val && val->GetType() == object_type_REAL)
|
|
dT = ((CRealObject*)val)->Get();
|
|
|
|
Add("MediaBox", CArrayObject::CreateBox(dL, dB, dR, dT));
|
|
}
|
|
else
|
|
Add("MediaBox", CArrayObject::CreateBox(0, 0, DEF_PAGE_WIDTH, DEF_PAGE_HEIGHT));
|
|
|
|
// Initialize current Rotate
|
|
CObjectBase* pRotate = GetRotateItem();
|
|
if (pRotate && pRotate->GetType() == object_type_NUMBER)
|
|
Add("Rotate", ((CNumberObject*)pRotate)->Get() % 360);
|
|
|
|
CResourcesDict* pResources = GetResourcesItem();
|
|
if (pResources)
|
|
{
|
|
pResources->Fix();
|
|
|
|
// Initialize current Shading
|
|
CObjectBase* pShading = pResources->Get("Shading");
|
|
if (pShading && pShading->GetType() == object_type_DICT)
|
|
{
|
|
m_pShadings = (CDictObject*)pShading;
|
|
m_unShadingsCount = m_pShadings->GetSize();
|
|
}
|
|
|
|
// Initialize current Pattern
|
|
CObjectBase* pPattern = pResources->Get("Pattern");
|
|
if (pPattern && pPattern->GetType() == object_type_DICT)
|
|
{
|
|
m_pPatterns = (CDictObject*)pPattern;
|
|
m_unPatternsCount = m_pPatterns->GetSize();
|
|
}
|
|
}
|
|
|
|
m_pStream = NULL;
|
|
}
|
|
CPage::CPage(CXref* pXref, CPageTree* pParent, CDocument* pDocument)
|
|
{
|
|
pXref->Add(this);
|
|
Init(pDocument);
|
|
|
|
m_pContents = new CArrayObject();
|
|
CDictObject* pContent = new CDictObject(pXref);
|
|
m_pContents->Add(pContent);
|
|
m_pStream = pContent->GetStream();
|
|
|
|
Add("Parent", pParent);
|
|
Add("MediaBox", CArrayObject::CreateBox(0, 0, DEF_PAGE_WIDTH, DEF_PAGE_HEIGHT));
|
|
Add("Type", "Page");
|
|
Add("Contents", m_pContents);
|
|
AddResource();
|
|
}
|
|
CPage::~CPage()
|
|
{
|
|
CGrState* pGrState = m_pGrState, *pPrev = NULL;
|
|
while (pGrState)
|
|
{
|
|
pPrev = m_pGrState->GetPrev();
|
|
delete pGrState;
|
|
pGrState = pPrev;
|
|
}
|
|
}
|
|
void CPage::Init(CDocument* pDocument)
|
|
{
|
|
m_pDocument = pDocument;
|
|
m_eGrMode = grmode_PAGE;
|
|
m_pGrState = new CGrState(NULL);
|
|
|
|
m_pFont = NULL;
|
|
m_pShadings = NULL;
|
|
m_unShadingsCount = 0;
|
|
m_pPatterns = NULL;
|
|
m_unPatternsCount = 0;
|
|
m_eType = fontUnknownType;
|
|
}
|
|
void CPage::SetWidth(double dValue)
|
|
{
|
|
dValue = std::min(std::max(dValue, 1.0), 14400.0);
|
|
SetMediaBoxValue(2, dValue);
|
|
}
|
|
double CPage::GetWidth()
|
|
{
|
|
TBox oBox = GetMediaBox();
|
|
return oBox.fRight - oBox.fLeft;
|
|
}
|
|
void CPage::SetHeight(double dValue)
|
|
{
|
|
dValue = std::min(std::max(dValue, 1.0), 14400.0);
|
|
SetMediaBoxValue(3, dValue);
|
|
}
|
|
double CPage::GetHeight()
|
|
{
|
|
TBox oBox = GetMediaBox();
|
|
return oBox.fTop - oBox.fBottom;
|
|
}
|
|
TBox CPage::GetBox(const std::string& sBox)
|
|
{
|
|
TBox oBox = TRect( 0, 0, 0, 0 );
|
|
|
|
CArrayObject* pArray = (CArrayObject*)Get(sBox);
|
|
|
|
if (pArray)
|
|
{
|
|
PdfWriter::CObjectBase* pD = pArray->Get(0);
|
|
oBox.fLeft = pD->GetType() == PdfWriter::object_type_NUMBER ? ((PdfWriter::CNumberObject*)pD)->Get() : ((PdfWriter::CRealObject*)pD)->Get();
|
|
|
|
pD = pArray->Get(1);
|
|
oBox.fBottom = pD->GetType() == PdfWriter::object_type_NUMBER ? ((PdfWriter::CNumberObject*)pD)->Get() : ((PdfWriter::CRealObject*)pD)->Get();
|
|
|
|
pD = pArray->Get(2);
|
|
oBox.fRight = pD->GetType() == PdfWriter::object_type_NUMBER ? ((PdfWriter::CNumberObject*)pD)->Get() : ((PdfWriter::CRealObject*)pD)->Get();
|
|
|
|
pD = pArray->Get(3);
|
|
oBox.fTop = pD->GetType() == PdfWriter::object_type_NUMBER ? ((PdfWriter::CNumberObject*)pD)->Get() : ((PdfWriter::CRealObject*)pD)->Get();
|
|
}
|
|
|
|
return oBox;
|
|
}
|
|
TBox CPage::GetMediaBox()
|
|
{
|
|
return GetBox("MediaBox");
|
|
}
|
|
void CPage::SetMediaBoxValue(unsigned int unIndex, double dValue)
|
|
{
|
|
CArrayObject* pArray = GetMediaBoxItem();
|
|
if (!pArray)
|
|
return;
|
|
|
|
CRealObject* pReal = (CRealObject*)pArray->Get(unIndex);
|
|
if (!pReal)
|
|
return;
|
|
|
|
pReal->Set(dValue);
|
|
}
|
|
CArrayObject* CPage::GetMediaBoxItem()
|
|
{
|
|
return (CArrayObject*)Get("MediaBox");
|
|
}
|
|
CResourcesDict* CPage::GetResourcesItem()
|
|
{
|
|
CObjectBase* pObject = Get("Resources");
|
|
|
|
// If Resources object is null, search for Resources in parent object recursively
|
|
if (!pObject)
|
|
{
|
|
CPageTree* pPageTree = NULL;
|
|
CObjectBase* pObj = Get("Parent");
|
|
if (pObj->GetType() == object_type_DICT)
|
|
pPageTree = (CPageTree*)pObj;
|
|
while (pPageTree)
|
|
{
|
|
pObject = pPageTree->Get("Resources");
|
|
|
|
if (pObject)
|
|
break;
|
|
|
|
pPageTree = (CPageTree*)pPageTree->Get("Parent");
|
|
}
|
|
}
|
|
|
|
return (CResourcesDict*)pObject;
|
|
}
|
|
CArrayObject* CPage::GetCropBoxItem()
|
|
{
|
|
return (CArrayObject*)Get("CropBox");
|
|
}
|
|
CObjectBase* CPage::GetRotateItem()
|
|
{
|
|
return Get("Rotate");
|
|
}
|
|
void CPage::AddResource(CXref* pXref)
|
|
{
|
|
CResourcesDict* pResource = new CResourcesDict(pXref, !pXref, true);
|
|
if (!pResource)
|
|
return;
|
|
|
|
Add("Resources", pResource);
|
|
}
|
|
void CPage::BeforeWrite()
|
|
{
|
|
if (grmode_PATH == m_eGrMode)
|
|
EndPath();
|
|
|
|
if (grmode_TEXT == m_eGrMode)
|
|
EndText();
|
|
|
|
while (m_pGrState->GetPrev())
|
|
{
|
|
GrRestore();
|
|
}
|
|
}
|
|
void CPage::SetGrMode(EGrMode eMode)
|
|
{
|
|
m_eGrMode = eMode;
|
|
// TODO: Add validation for error cases
|
|
}
|
|
void CPage::CheckGrMode(EGrMode eMode)
|
|
{
|
|
// TODO: Add validation for error cases
|
|
}
|
|
void CPage::MoveTo (double dX, double dY)
|
|
{
|
|
// Operator : m
|
|
// Description: Begin a new subpath, moving the current pointer to point (x, y) (which is also the start point).
|
|
|
|
SetGrMode(grmode_PATH);
|
|
m_pStream->WriteReal(dX);
|
|
m_pStream->WriteChar(' ');
|
|
m_pStream->WriteReal(dY);
|
|
m_pStream->WriteStr(" m\012");
|
|
|
|
m_oCurPos.Set(dX, dY);
|
|
m_oStartPos = m_oCurPos;
|
|
}
|
|
void CPage::LineTo (double dX, double dY)
|
|
{
|
|
// Operator : l
|
|
// Description: Add a line from current point to point (x, y). Set current point to (x, y).
|
|
CheckGrMode(grmode_PATH);
|
|
|
|
m_pStream->WriteReal(dX);
|
|
m_pStream->WriteChar(' ');
|
|
m_pStream->WriteReal(dY);
|
|
m_pStream->WriteStr(" l\012");
|
|
|
|
m_oCurPos.Set(dX, dY);
|
|
}
|
|
void CPage::CurveTo(double dX1, double dY1, double dX2, double dY2, double dX3, double dY3)
|
|
{
|
|
// Operator : c
|
|
// Description: Add a cubic Bezier curve. The curve starts at current position and ends
|
|
// at point (x3, y3). (x1, y1) and (x2, y2) are control points. Set current point
|
|
// to (x3, y3).
|
|
CheckGrMode(grmode_PATH);
|
|
|
|
m_pStream->WriteReal(dX1);
|
|
m_pStream->WriteChar(' ');
|
|
m_pStream->WriteReal(dY1);
|
|
m_pStream->WriteChar(' ');
|
|
m_pStream->WriteReal(dX2);
|
|
m_pStream->WriteChar(' ');
|
|
m_pStream->WriteReal(dY2);
|
|
m_pStream->WriteChar(' ');
|
|
m_pStream->WriteReal(dX3);
|
|
m_pStream->WriteChar(' ');
|
|
m_pStream->WriteReal(dY3);
|
|
m_pStream->WriteStr(" c\012");
|
|
|
|
m_oCurPos.Set(dX3, dY3);
|
|
}
|
|
void CPage::Ellipse(double dX, double dY, double dXRay, double dYRay)
|
|
{
|
|
SetGrMode(grmode_PATH);
|
|
|
|
m_pStream->WriteReal(dX - dXRay);
|
|
m_pStream->WriteChar(' ');
|
|
m_pStream->WriteReal(dY);
|
|
m_pStream->WriteStr(" m\012");
|
|
|
|
QuarterEllipseA(m_pStream, dX, dY, dXRay, dYRay);
|
|
QuarterEllipseB(m_pStream, dX, dY, dXRay, dYRay);
|
|
QuarterEllipseC(m_pStream, dX, dY, dXRay, dYRay);
|
|
QuarterEllipseD(m_pStream, dX, dY, dXRay, dYRay);
|
|
|
|
m_oCurPos.Set(dX - dXRay, dY);
|
|
m_oStartPos = m_oCurPos;
|
|
}
|
|
void CPage::EllipseArc(double dX, double dY, double dXRad, double dYRad, double _dAngle1, double _dAngle2, bool bClockDirection)
|
|
{
|
|
CheckGrMode(grmode_PATH);
|
|
|
|
// Convert angles to radians
|
|
double dAngle1 = _dAngle1 * 3.141592f / 180;
|
|
double dAngle2 = _dAngle2 * 3.141592f / 180;
|
|
|
|
// Determine which quadrants contain the start and end points
|
|
int nFirstPointQuard = int(_dAngle1) / 90 + 1;
|
|
int nSecondPointQuard = int(_dAngle2) / 90 + 1;
|
|
|
|
nSecondPointQuard = std::min(4, std::max(1, nSecondPointQuard));
|
|
nFirstPointQuard = std::min(4, std::max(1, nFirstPointQuard));
|
|
|
|
// Draw line to arc start point
|
|
double dStartX = 0.0, dStartY = 0.0, dEndX = 0.0, dEndY = 0.0;
|
|
|
|
dStartX = dX + dXRad * cos(AngToEllPrm(dAngle1, dXRad, dYRad));
|
|
dStartY = dY + dYRad * sin(AngToEllPrm(dAngle1, dXRad, dYRad));
|
|
|
|
//m_pStream->WriteReal(dStartX);
|
|
//m_pStream->WriteChar(' ');
|
|
//m_pStream->WriteReal(dStartY);
|
|
//m_pStream->WriteStr(" m\012");
|
|
|
|
// Continue drawing by quadrants
|
|
double dCurX = dStartX, dCurY = dStartY;
|
|
double dStartAngle = dAngle1;
|
|
double dEndAngle = 0;
|
|
|
|
if ( !bClockDirection )
|
|
{
|
|
for (unsigned int nIndex = nFirstPointQuard; nIndex <= nSecondPointQuard; nIndex++)
|
|
{
|
|
if (nIndex == nSecondPointQuard)
|
|
dEndAngle = dAngle2;
|
|
else
|
|
dEndAngle = (90 * (nIndex)) * 3.141592f / 180;
|
|
if (!(nIndex == nFirstPointQuard))
|
|
dStartAngle = (90 * (nIndex - 1)) * 3.141592f / 180;
|
|
|
|
WriteEllipseArc(m_pStream, dX, dY, dXRad, dYRad, AngToEllPrm(dStartAngle, dXRad, dYRad), AngToEllPrm(dEndAngle, dXRad, dYRad), dEndX, dEndY, false);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
for( unsigned int nIndex = nFirstPointQuard; nIndex >= nSecondPointQuard; nIndex-- )
|
|
{
|
|
if ( nIndex == nFirstPointQuard )
|
|
dStartAngle = dAngle1;
|
|
else
|
|
dStartAngle = (90 * (nIndex ) ) * 3.141592f / 180;
|
|
if ( !( nIndex == nSecondPointQuard ) )
|
|
dEndAngle = (90 * (nIndex - 1 ) ) * 3.141592f / 180;
|
|
else
|
|
dEndAngle = dAngle2;
|
|
|
|
WriteEllipseArc(m_pStream, dX, dY, dXRad, dYRad, AngToEllPrm(dStartAngle, dXRad, dYRad), AngToEllPrm(dEndAngle, dXRad, dYRad), dEndX, dEndY, false);
|
|
}
|
|
}
|
|
|
|
m_oCurPos.Set(dEndX, dEndY);
|
|
m_oStartPos = m_oCurPos;
|
|
}
|
|
void CPage::EllipseArcTo(double dX, double dY, double dXRad, double dYRad, double _dAngle1, double _dAngle2, bool bClockDirection)
|
|
{
|
|
// Check ellipse for degeneracy
|
|
if (dXRad < 0.001 || dYRad < 0.001)
|
|
{
|
|
double dAngle1 = _dAngle1 * 3.141592f / 180;
|
|
double dAngle2 = _dAngle2 * 3.141592f / 180;
|
|
|
|
if (dXRad < 0.001 && dYRad < 0.001)
|
|
LineTo(dX, dY);
|
|
else if (dXRad < 0.001)
|
|
{
|
|
LineTo(dX, dY + sin(dAngle1) * dYRad);
|
|
LineTo(dX, dY + sin(dAngle2) * dYRad);
|
|
}
|
|
else // if (dYRad < 0.001)
|
|
{
|
|
LineTo(dX + cos(dAngle1) * dXRad, dY);
|
|
LineTo(dX + cos(dAngle2) * dXRad, dY);
|
|
}
|
|
return;
|
|
}
|
|
|
|
|
|
while (_dAngle1 < 0)
|
|
_dAngle1 += 360;
|
|
|
|
while (_dAngle1 > 360)
|
|
_dAngle1 -= 360;
|
|
|
|
while (_dAngle2 < 0)
|
|
_dAngle2 += 360;
|
|
|
|
while (_dAngle2 > 360)
|
|
_dAngle2 -= 360;
|
|
|
|
if (!bClockDirection)
|
|
{
|
|
if (_dAngle1 <= _dAngle2)
|
|
EllipseArc(dX, dY, dXRad, dYRad, _dAngle1, _dAngle2, false);
|
|
else
|
|
{
|
|
EllipseArc(dX, dY, dXRad, dYRad, _dAngle1, 360, false);
|
|
EllipseArc(dX, dY, dXRad, dYRad, 0, _dAngle2, false);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (_dAngle1 >= _dAngle2)
|
|
EllipseArc(dX, dY, dXRad, dYRad, _dAngle1, _dAngle2, true);
|
|
else
|
|
{
|
|
EllipseArc(dX, dY, dXRad, dYRad, _dAngle1, 0, true);
|
|
EllipseArc(dX, dY, dXRad, dYRad, 360, _dAngle2, true);
|
|
}
|
|
}
|
|
}
|
|
void CPage::ClosePath()
|
|
{
|
|
// Operator : h
|
|
// Description: Close subpath by connecting current point to start point with a straight line. If subpath
|
|
// is already closed, do nothing
|
|
CheckGrMode(grmode_PATH);
|
|
m_pStream->WriteStr("h\012");
|
|
m_oCurPos = m_oStartPos;
|
|
}
|
|
void CPage::Stroke()
|
|
{
|
|
// Operator : S
|
|
// Description: Stroke the path.
|
|
|
|
SetGrMode(grmode_PAGE);
|
|
m_pStream->WriteStr("S\012");
|
|
m_oCurPos.Reset();
|
|
}
|
|
void CPage::Fill()
|
|
{
|
|
// Operator : f
|
|
// Description: Fill path using Nonzero Winding Number Rule (see PDF Part1: PDF 1.7 specification
|
|
// page 136, section 8.5.3.3.2).
|
|
SetGrMode(grmode_PAGE);
|
|
m_pStream->WriteStr("f\012");
|
|
m_oCurPos.Reset();
|
|
}
|
|
void CPage::EoFill()
|
|
{
|
|
// Operator : f*
|
|
// Description: Fill path using Even-Odd Rule (see PDF Part1: PDF 1.7 specification page 137,
|
|
// section 8.5.3.3.3).
|
|
SetGrMode(grmode_PAGE);
|
|
m_pStream->WriteStr("f*\012");
|
|
m_oCurPos.Reset();
|
|
}
|
|
void CPage::FillStroke()
|
|
{
|
|
// Operator : B
|
|
// Description: Fill and stroke path using Nonzero Winding Number Rule for fill (see PDF Part1: PDF 1.7
|
|
// specification page 136, section 8.5.3.3.2). This operator should produce the same result
|
|
// as constructing two identical path objects, applying operator f to the first
|
|
// and operator S to the second.
|
|
SetGrMode(grmode_PAGE);
|
|
m_pStream->WriteStr("B\012");
|
|
m_oCurPos.Reset();
|
|
}
|
|
void CPage::EoFillStroke()
|
|
{
|
|
// Operator : B*
|
|
// Description: Fill and stroke path using Even-Odd Rule for fill (see PDF Part1: PDF 1.7
|
|
// specification page 137, section 8.5.3.3.3). This operator should produce the same result
|
|
// as constructing two identical path objects, applying operator f* to the first
|
|
// and operator S to the second.
|
|
SetGrMode(grmode_PAGE);
|
|
m_pStream->WriteStr("B*\012");
|
|
m_oCurPos.Reset();
|
|
}
|
|
void CPage::EndPath()
|
|
{
|
|
// Operator : n
|
|
// Description: Close path without filling or stroking it. This operator is primarily used for
|
|
// modifying the current path.
|
|
SetGrMode(grmode_PAGE);
|
|
m_pStream->WriteStr("n\012");
|
|
m_oCurPos.Reset();
|
|
}
|
|
void CPage::SetLineWidth(double dLineWidth)
|
|
{
|
|
// Operator : w
|
|
// Description: Set line width
|
|
|
|
dLineWidth = std::max(dLineWidth, 0.0);
|
|
m_pStream->WriteReal(dLineWidth);
|
|
m_pStream->WriteStr(" w\012");
|
|
m_pGrState->m_dLineWidth = dLineWidth;
|
|
}
|
|
void CPage::SetLineCap(ELineCapStyle eLineCap)
|
|
{
|
|
// Operator : J
|
|
// Description: Set line cap style (LineCapStyle)
|
|
|
|
eLineCap = std::max(linecap_Min, std::min(linecap_Max, eLineCap));
|
|
m_pStream->WriteInt((unsigned int)eLineCap);
|
|
m_pStream->WriteStr(" J\012");
|
|
m_pGrState->m_eLineCap = eLineCap;
|
|
}
|
|
void CPage::SetLineJoin(ELineJoinStyle eLineJoin)
|
|
{
|
|
// Operator : j
|
|
// Description: Set line join style (LineJoinStyle)
|
|
eLineJoin = std::max(linejoin_Min, std::min(linejoin_Max, eLineJoin));
|
|
m_pStream->WriteInt((unsigned int)eLineJoin);
|
|
m_pStream->WriteStr(" j\012");
|
|
m_pGrState->m_eLineJoin = eLineJoin;
|
|
}
|
|
void CPage::SetMiterLimit(double dMiterLimit)
|
|
{
|
|
// Operator : M
|
|
// Description: Set MiterLimit - constant related to line join style
|
|
dMiterLimit = std::max(1.0, dMiterLimit);
|
|
m_pStream->WriteReal(dMiterLimit);
|
|
m_pStream->WriteStr(" M\012");
|
|
m_pGrState->m_dMiterLimit = dMiterLimit;
|
|
}
|
|
void CPage::SetDash(const double* pPattern, unsigned int unCount, double dPhase)
|
|
{
|
|
// Operator : d
|
|
// Description: Set line dash pattern (DashMode)
|
|
|
|
if (0 == unCount || !pPattern)
|
|
return;
|
|
|
|
bool bFalseDash = true;
|
|
for (unsigned int unIndex = 0; unIndex < unCount; unIndex++)
|
|
{
|
|
if (0 != pPattern[unIndex])
|
|
{
|
|
bFalseDash = false;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (bFalseDash)
|
|
return;
|
|
|
|
m_pStream->WriteChar('[');
|
|
for (unsigned int unIndex = 0; unIndex < unCount; unIndex++)
|
|
{
|
|
m_pStream->WriteReal(pPattern[unIndex]);
|
|
m_pStream->WriteChar(' ');
|
|
}
|
|
m_pStream->WriteStr("] ");
|
|
m_pStream->WriteReal(dPhase);
|
|
m_pStream->WriteStr(" d\012");
|
|
|
|
m_pGrState->m_oDashMode.Set(pPattern, unCount, dPhase);
|
|
}
|
|
void CPage::SetFlat(double dFlatness)
|
|
{
|
|
// Operator : i
|
|
// Description: Set flatness tolerance
|
|
dFlatness = std::min(100.0, std::max(0.0, dFlatness));
|
|
m_pStream->WriteReal(dFlatness);
|
|
m_pStream->WriteStr(" i\012");
|
|
m_pGrState->m_dFlatness = dFlatness;
|
|
}
|
|
void CPage::GrSave()
|
|
{
|
|
// Operator : q
|
|
// Description: Save current GState to graphics stack
|
|
CheckGrMode(grmode_PAGE);
|
|
CGrState* pState = new CGrState(m_pGrState);
|
|
if (!pState)
|
|
return;
|
|
|
|
m_pStream->WriteStr("q\012");
|
|
m_pGrState = pState;
|
|
}
|
|
void CPage::GrRestore()
|
|
{
|
|
// Operator : Q
|
|
// Description: Restore GState by removing the most recent GState and making it current
|
|
CheckGrMode(grmode_PAGE);
|
|
|
|
if (!m_pGrState->m_pPrev)
|
|
return;
|
|
|
|
CGrState* pPrev = m_pGrState->m_pPrev;
|
|
delete m_pGrState;
|
|
m_pGrState = pPrev;
|
|
|
|
m_pStream->WriteStr("Q\012");
|
|
}
|
|
void CPage::SetStrokeColor(unsigned char unR, unsigned char unG, unsigned char unB)
|
|
{
|
|
// Operator : RG
|
|
// Description: Set color space for stroking to DeviceRGB and set color for
|
|
// stroking operations.
|
|
|
|
double dR = unR / 255.0;
|
|
double dG = unG / 255.0;
|
|
double dB = unB / 255.0;
|
|
|
|
SetStrokeRGB(dR, dG, dB);
|
|
|
|
m_pGrState->m_oStrokeColor.r = dR;
|
|
m_pGrState->m_oStrokeColor.g = dG;
|
|
m_pGrState->m_oStrokeColor.b = dB;
|
|
}
|
|
void CPage::SetFillColor(unsigned char unR, unsigned char unG, unsigned char unB)
|
|
{
|
|
// Operator : rg
|
|
// Description: Set color space for filling to DeviceRGB and set color for
|
|
// fill operations.
|
|
|
|
double dR = unR / 255.0;
|
|
double dG = unG / 255.0;
|
|
double dB = unB / 255.0;
|
|
|
|
SetFillRGB(dR, dG, dB);
|
|
|
|
m_pGrState->m_oFillColor.r = dR;
|
|
m_pGrState->m_oFillColor.g = dG;
|
|
m_pGrState->m_oFillColor.b = dB;
|
|
}
|
|
void CPage::SetStrokeG(double dG)
|
|
{
|
|
// Operator : G
|
|
// Description: Stroke in DeviceGray
|
|
|
|
m_pStream->WriteReal(dG);
|
|
m_pStream->WriteStr(" G\012");
|
|
}
|
|
void CPage::SetStrokeRGB(double dR, double dG, double dB)
|
|
{
|
|
// Operator : RG
|
|
// Description: Stroke in DeviceRGB
|
|
|
|
m_pStream->WriteReal(dR);
|
|
m_pStream->WriteChar(' ');
|
|
m_pStream->WriteReal(dG);
|
|
m_pStream->WriteChar(' ');
|
|
m_pStream->WriteReal(dB);
|
|
m_pStream->WriteStr(" RG\012");
|
|
}
|
|
void CPage::SetStrokeCMYK(double dC, double dM, double dY, double dK)
|
|
{
|
|
// Operator : K
|
|
// Description: Stroke in DeviceCMYK
|
|
|
|
m_pStream->WriteReal(dC);
|
|
m_pStream->WriteChar(' ');
|
|
m_pStream->WriteReal(dM);
|
|
m_pStream->WriteChar(' ');
|
|
m_pStream->WriteReal(dY);
|
|
m_pStream->WriteChar(' ');
|
|
m_pStream->WriteReal(dK);
|
|
m_pStream->WriteStr(" K\012");
|
|
}
|
|
void CPage::SetFillG(double dG)
|
|
{
|
|
// Operator : g
|
|
// Description: Fill in DeviceGray
|
|
|
|
m_pStream->WriteReal(dG);
|
|
m_pStream->WriteStr(" g\012");
|
|
}
|
|
void CPage::SetFillRGB(double dR, double dG, double dB)
|
|
{
|
|
// Operator : rg
|
|
// Description: Fill in DeviceRGB
|
|
|
|
m_pStream->WriteReal(dR);
|
|
m_pStream->WriteChar(' ');
|
|
m_pStream->WriteReal(dG);
|
|
m_pStream->WriteChar(' ');
|
|
m_pStream->WriteReal(dB);
|
|
m_pStream->WriteStr(" rg\012");
|
|
}
|
|
void CPage::SetFillCMYK(double dC, double dM, double dY, double dK)
|
|
{
|
|
// Operator : k
|
|
// Description: Fill in DeviceCMYK
|
|
|
|
m_pStream->WriteReal(dC);
|
|
m_pStream->WriteChar(' ');
|
|
m_pStream->WriteReal(dM);
|
|
m_pStream->WriteChar(' ');
|
|
m_pStream->WriteReal(dY);
|
|
m_pStream->WriteChar(' ');
|
|
m_pStream->WriteReal(dK);
|
|
m_pStream->WriteStr(" k\012");
|
|
}
|
|
void CPage::Concat(double dM11, double dM12, double dM21, double dM22, double dX, double dY)
|
|
{
|
|
// Operator : cm
|
|
// Description: Modify the transformation matrix (CTM - Current Transformation Matrix)
|
|
|
|
m_pStream->WriteReal(dM11);
|
|
m_pStream->WriteChar(' ');
|
|
m_pStream->WriteReal(dM12);
|
|
m_pStream->WriteChar(' ');
|
|
m_pStream->WriteReal(dM21);
|
|
m_pStream->WriteChar(' ');
|
|
m_pStream->WriteReal(dM22);
|
|
m_pStream->WriteChar(' ');
|
|
m_pStream->WriteReal(dX);
|
|
m_pStream->WriteChar(' ');
|
|
m_pStream->WriteReal(dY);
|
|
m_pStream->WriteStr(" cm\012");
|
|
|
|
CMatrix oCTM = m_pGrState->m_oMatrix;
|
|
|
|
// Multiply matrices oCTM(new) = oCTM(transformation(given by parameters)) x oCTM(old)
|
|
m_pGrState->m_oMatrix.m11 = dM11 * oCTM.m11 + dM12 * oCTM.m21;
|
|
m_pGrState->m_oMatrix.m12 = dM11 * oCTM.m12 + dM12 * oCTM.m22;
|
|
m_pGrState->m_oMatrix.m21 = dM21 * oCTM.m11 + dM22 * oCTM.m21;
|
|
m_pGrState->m_oMatrix.m22 = dM21 * oCTM.m12 + dM22 * oCTM.m22;
|
|
m_pGrState->m_oMatrix.x = dX * oCTM.m11 + dY * oCTM.m21 + oCTM.x;
|
|
m_pGrState->m_oMatrix.y = dX * oCTM.m12 + dY * oCTM.m22 + oCTM.y;
|
|
}
|
|
void CPage::StartTransform(double dM11, double dM12, double dM21, double dM22, double dX, double dY)
|
|
{
|
|
m_pGrState->m_oMatrix.m11 = dM11;
|
|
m_pGrState->m_oMatrix.m12 = dM12;
|
|
m_pGrState->m_oMatrix.m21 = dM21;
|
|
m_pGrState->m_oMatrix.m22 = dM22;
|
|
m_pGrState->m_oMatrix.x = dX;
|
|
m_pGrState->m_oMatrix.y = dY;
|
|
}
|
|
void CPage::SetTransform(double dM11, double dM12, double dM21, double dM22, double dX, double dY)
|
|
{
|
|
CMatrix oInverse = m_pGrState->m_oMatrix.Inverse();
|
|
|
|
CMatrix oResult;
|
|
oResult.m11 = dM11 * oInverse.m11 + dM12 * oInverse.m21;
|
|
oResult.m12 = dM11 * oInverse.m12 + dM12 * oInverse.m22;
|
|
oResult.m21 = dM21 * oInverse.m11 + dM22 * oInverse.m21;
|
|
oResult.m22 = dM21 * oInverse.m12 + dM22 * oInverse.m22;
|
|
oResult.x = dX * oInverse.m11 + dY * oInverse.m21 + oInverse.x;
|
|
oResult.y = dX * oInverse.m12 + dY * oInverse.m22 + oInverse.y;
|
|
|
|
if (!oResult.IsIdentity())
|
|
Concat(oResult.m11, oResult.m12, oResult.m21, oResult.m22, oResult.x, oResult.y);
|
|
}
|
|
void CPage::Clip()
|
|
{
|
|
// Operator : W
|
|
// Description: Modify current clipping path by intersecting it with current path, using Nonzero
|
|
// Winding Number Rule to determine which regions are inside the clipping path.
|
|
SetGrMode(grmode_CLIP);
|
|
m_pStream->WriteStr("W\012");
|
|
}
|
|
void CPage::Eoclip()
|
|
{
|
|
// Operator : W*
|
|
// Description: Modify current clipping path by intersecting it with current path, using Even-Odd
|
|
// Rule to determine which regions are inside the clipping path.
|
|
SetGrMode(grmode_CLIP);
|
|
m_pStream->WriteStr("W*\012");
|
|
}
|
|
void CPage::SetExtGrState(CExtGrState* pState)
|
|
{
|
|
if (!pState)
|
|
return;
|
|
|
|
// Operator : gs
|
|
// Description: Set all settings of the given graphics state (ExtGState) at once
|
|
|
|
CResourcesDict* pResources = GetResourcesItem();
|
|
if (!pResources)
|
|
return;
|
|
|
|
const char* sGsName = pResources->GetExtGrStateName(pState);
|
|
SetExtGrStateKey(sGsName);
|
|
}
|
|
void CPage::SetExtGrStateKey(const char* sKey)
|
|
{
|
|
// Operator : gs
|
|
// Description: Set all settings of the given graphics state (ExtGState) at once
|
|
|
|
if (!sKey)
|
|
return;
|
|
|
|
m_pStream->WriteEscapeName(sKey);
|
|
m_pStream->WriteStr(" gs\012");
|
|
}
|
|
void CPage::AddAnnotation(CDictObject* pAnnot)
|
|
{
|
|
CArrayObject* pArray = (CArrayObject*)Get("Annots");
|
|
if (!pArray)
|
|
{
|
|
pArray = new CArrayObject();
|
|
if (!pArray)
|
|
return;
|
|
|
|
Add("Annots", pArray);
|
|
}
|
|
|
|
return pArray->Add(pAnnot);
|
|
}
|
|
bool CPage::DeleteAnnotation(unsigned int nID)
|
|
{
|
|
CArrayObject* pArray = (CArrayObject*)Get("Annots");
|
|
if (!pArray)
|
|
return false;
|
|
|
|
for (int i = 0; i < pArray->GetCount(); i++)
|
|
{
|
|
CObjectBase* pObj = pArray->Get(i);
|
|
if (pObj->GetObjId() == nID)
|
|
{
|
|
CObjectBase* pDelete = pArray->Remove(i);
|
|
if (pDelete->GetType() == object_type_UNKNOWN)
|
|
RELEASEOBJECT(pDelete);
|
|
return true;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
void CPage::BeginText()
|
|
{
|
|
// Operator : BT
|
|
// Description: Begin text
|
|
SetGrMode(grmode_TEXT);
|
|
m_pStream->WriteStr("BT\012");
|
|
|
|
m_oTextPos.Reset();
|
|
m_oTextMatrix.Reset();
|
|
}
|
|
void CPage::EndText()
|
|
{
|
|
// Operator : ET
|
|
// Description: End text
|
|
CheckGrMode(grmode_TEXT);
|
|
m_pStream->WriteStr("ET\012");
|
|
SetGrMode(grmode_PAGE);
|
|
}
|
|
void CPage::MoveTextPos(double dX, double dY)
|
|
{
|
|
// Operator : Td
|
|
// Description: Move to the beginning of next line, offset from current line start by (fX, fY).
|
|
CheckGrMode(grmode_TEXT);
|
|
|
|
m_pStream->WriteReal(dX);
|
|
m_pStream->WriteChar(' ');
|
|
m_pStream->WriteReal(dY);
|
|
m_pStream->WriteStr(" Td\012");
|
|
|
|
m_oTextMatrix.x += dX * m_oTextMatrix.m11 + dY * m_oTextMatrix.m21;
|
|
m_oTextMatrix.y += dX * m_oTextMatrix.m12 + dY * m_oTextMatrix.m22;
|
|
m_oTextPos.Set(m_oTextMatrix.x, m_oTextMatrix.y);
|
|
}
|
|
void CPage::ShowText(const BYTE* sText, unsigned int unLen)
|
|
{
|
|
// Operator : Tj
|
|
// Description: Show text string.
|
|
CheckGrMode(grmode_TEXT);
|
|
WriteText(sText, unLen);
|
|
m_pStream->WriteStr(" Tj\012");
|
|
}
|
|
void CPage::WriteText(const BYTE* sText, unsigned int unLen)
|
|
{
|
|
EFontType eType = m_eType != fontUnknownType ? m_eType : (m_pFont ? m_pFont->GetFontType() : fontCIDType0);
|
|
if (fontCIDType0 == eType || fontCIDType0C == eType || fontCIDType0COT == eType || fontCIDType2 == eType || fontCIDType2OT == eType)
|
|
{
|
|
m_pStream->WriteChar('<');
|
|
m_pStream->WriteBinary(sText, unLen, NULL);
|
|
m_pStream->WriteChar('>');
|
|
}
|
|
else
|
|
{
|
|
unLen = unLen / 2;
|
|
BYTE* sText2 = new BYTE[unLen];
|
|
for (int i = 0; i < unLen; ++i)
|
|
sText2[i] = sText[i * 2 + 1];
|
|
m_pStream->WriteChar('<');
|
|
m_pStream->WriteBinary(sText2, unLen, NULL);
|
|
m_pStream->WriteChar('>');
|
|
RELEASEARRAYOBJECTS(sText2);
|
|
}
|
|
}
|
|
void CPage::DrawText(double dXpos, double dYpos, const BYTE* sText, unsigned int unLen)
|
|
{
|
|
CheckGrMode(grmode_TEXT);
|
|
|
|
double dX = 0.0;
|
|
double dY = 0.0;
|
|
|
|
if (0 == m_oTextMatrix.m11)
|
|
{
|
|
dY = (dXpos - m_oTextMatrix.x) / m_oTextMatrix.m21;
|
|
dX = (dYpos - m_oTextMatrix.y - (dXpos - m_oTextMatrix.x) * m_oTextMatrix.m22 / m_oTextMatrix.m21) / m_oTextMatrix.m12;
|
|
}
|
|
else
|
|
{
|
|
dY = (dYpos - m_oTextMatrix.y - (dXpos - m_oTextMatrix.x) * m_oTextMatrix.m12 / m_oTextMatrix.m11) / (m_oTextMatrix.m22 - m_oTextMatrix.m21 * m_oTextMatrix.m12 / m_oTextMatrix.m11);
|
|
dX = (dXpos - m_oTextMatrix.x - dY * m_oTextMatrix.m21) / m_oTextMatrix.m11;
|
|
}
|
|
|
|
MoveTextPos(dX, dY);
|
|
ShowText(sText, unLen);
|
|
}
|
|
void CPage::DrawTextLine(const CTextLine* pTextLine)
|
|
{
|
|
if (!pTextLine)
|
|
return;
|
|
|
|
int nCount = pTextLine->m_vWords.size();
|
|
if (nCount <= 0)
|
|
return;
|
|
|
|
CheckGrMode(grmode_TEXT);
|
|
|
|
double dXpos = pTextLine->m_dX;
|
|
double dYpos = pTextLine->m_dY;
|
|
|
|
double dX = 0.0;
|
|
double dY = 0.0;
|
|
|
|
if (0 == m_oTextMatrix.m11)
|
|
{
|
|
dY = (dXpos - m_oTextMatrix.x) / m_oTextMatrix.m21;
|
|
dX = (dYpos - m_oTextMatrix.y - (dXpos - m_oTextMatrix.x) * m_oTextMatrix.m22 / m_oTextMatrix.m21) / m_oTextMatrix.m12;
|
|
}
|
|
else
|
|
{
|
|
dY = (dYpos - m_oTextMatrix.y - (dXpos - m_oTextMatrix.x) * m_oTextMatrix.m12 / m_oTextMatrix.m11) / (m_oTextMatrix.m22 - m_oTextMatrix.m21 * m_oTextMatrix.m12 / m_oTextMatrix.m11);
|
|
dX = (dXpos - m_oTextMatrix.x - dY * m_oTextMatrix.m21) / m_oTextMatrix.m11;
|
|
}
|
|
MoveTextPos(dX, dY);
|
|
|
|
if (1 == nCount)
|
|
{
|
|
CTextWord* pWord = pTextLine->m_vWords.at(0);
|
|
ShowText(pWord->m_pText, pWord->m_nIndex * 2);
|
|
}
|
|
else
|
|
{
|
|
CTextWord* pWord = NULL;
|
|
double dShift = 0;
|
|
m_pStream->WriteChar('[');
|
|
for (int nIndex = 0; nIndex < nCount; nIndex++)
|
|
{
|
|
pWord = pTextLine->m_vWords.at(nIndex);
|
|
WriteText(pWord->m_pText, pWord->m_nIndex * 2);
|
|
if (nIndex != nCount - 1)
|
|
{
|
|
dShift = pTextLine->m_vShifts.at(nIndex);
|
|
m_pStream->WriteReal(dShift);
|
|
}
|
|
}
|
|
m_pStream->WriteStr("]TJ\012");
|
|
}
|
|
}
|
|
void CPage::SetTextRise(double dS)
|
|
{
|
|
// Operator : Ts
|
|
// Description: Set text rise
|
|
CheckGrMode(grmode_TEXT);
|
|
|
|
m_pStream->WriteReal(dS);
|
|
m_pStream->WriteStr(" Ts\012");
|
|
}
|
|
void CPage::SetCharSpace(double dValue)
|
|
{
|
|
// Operator : Tc
|
|
// Description: Set character spacing
|
|
CheckGrMode(grmode_TEXT);
|
|
|
|
dValue = std::min((double)MAX_CHARSPACE, std::max((double)MIN_CHARSPACE, dValue));
|
|
m_pStream->WriteReal(dValue);
|
|
m_pStream->WriteStr(" Tc\012");
|
|
}
|
|
void CPage::SetWordSpace(double dValue)
|
|
{
|
|
// Operator : Tw
|
|
// Description: Set word spacing
|
|
CheckGrMode(grmode_TEXT);
|
|
|
|
m_pStream->WriteReal(dValue);
|
|
m_pStream->WriteStr(" Tw\012");
|
|
}
|
|
void CPage::SetHorizontalScaling(double dValue)
|
|
{
|
|
// Operator : Tz
|
|
// Description: Set horizontal scaling
|
|
CheckGrMode(grmode_TEXT);
|
|
|
|
dValue = std::min((double)MAX_HORIZONTALSCALING, std::max((double)MIN_HORIZONTALSCALING, dValue));
|
|
m_pStream->WriteReal(dValue);
|
|
m_pStream->WriteStr(" Tz\012");
|
|
}
|
|
void CPage::SetFontAndSize(CFontDict* pFont, double dSize)
|
|
{
|
|
// Operator : Tf
|
|
// Description: Set font and font size
|
|
|
|
dSize = std::min((double)MAX_FONTSIZE, std::max(0.0, dSize));
|
|
CResourcesDict* pResources = GetResourcesItem();
|
|
if (!pResources)
|
|
return;
|
|
|
|
const char* sFontName = pResources->GetFontName(pFont);
|
|
if (!sFontName)
|
|
return;
|
|
|
|
m_pStream->WriteEscapeName(sFontName);
|
|
m_pStream->WriteChar(' ');
|
|
m_pStream->WriteReal(dSize);
|
|
m_pStream->WriteStr(" Tf\012");
|
|
|
|
m_pFont = pFont;
|
|
}
|
|
void CPage::SetFontKeyAndSize(const char* sKey, double dSize)
|
|
{
|
|
// Operator : Tf
|
|
// Description: Set font and font size
|
|
|
|
dSize = std::min((double)MAX_FONTSIZE, std::max(0.0, dSize));
|
|
if (!sKey)
|
|
return;
|
|
|
|
m_pStream->WriteEscapeName(sKey);
|
|
m_pStream->WriteChar(' ');
|
|
m_pStream->WriteReal(dSize);
|
|
m_pStream->WriteStr(" Tf\012");
|
|
}
|
|
void CPage::SetFontType(EFontType nType)
|
|
{
|
|
m_eType = nType;
|
|
}
|
|
void CPage::SetTextRenderingMode(ETextRenderingMode eMode)
|
|
{
|
|
// Operator : Tr
|
|
// Description: Set text rendering mode (TextRenderingMode)
|
|
CheckGrMode(grmode_TEXT);
|
|
m_pStream->WriteInt((int)eMode);
|
|
m_pStream->WriteStr(" Tr\012");
|
|
}
|
|
void CPage::SetTextMatrix(double dM11, double dM12, double dM21, double dM22, double dX, double dY)
|
|
{
|
|
// Operator : Tm
|
|
// Description: Set text transformation matrix.
|
|
CheckGrMode(grmode_TEXT);
|
|
|
|
m_pStream->WriteReal(dM11);
|
|
m_pStream->WriteChar(' ');
|
|
m_pStream->WriteReal(dM12);
|
|
m_pStream->WriteChar(' ');
|
|
m_pStream->WriteReal(dM21);
|
|
m_pStream->WriteChar(' ');
|
|
m_pStream->WriteReal(dM22);
|
|
m_pStream->WriteChar(' ');
|
|
m_pStream->WriteReal(dX);
|
|
m_pStream->WriteChar(' ');
|
|
m_pStream->WriteReal(dY);
|
|
m_pStream->WriteStr(" Tm\012");
|
|
|
|
m_oTextMatrix.m11 = dM11;
|
|
m_oTextMatrix.m12 = dM12;
|
|
m_oTextMatrix.m21 = dM21;
|
|
m_oTextMatrix.m22 = dM22;
|
|
m_oTextMatrix.x = dX;
|
|
m_oTextMatrix.y = dY;
|
|
m_oTextPos.Set(m_oTextMatrix.x, m_oTextMatrix.y);
|
|
}
|
|
void CPage::ExecuteXObject(CXObject* pXObject)
|
|
{
|
|
CResourcesDict* pResources = GetResourcesItem();
|
|
if (!pResources)
|
|
return;
|
|
|
|
const char* sXObjectName = pResources->GetXObjectName(pXObject);
|
|
ExecuteXObject(sXObjectName);
|
|
}
|
|
void CPage::ExecuteXObject(const char* sXObjectName)
|
|
{
|
|
if (!sXObjectName)
|
|
return;
|
|
m_pStream->WriteEscapeName(sXObjectName);
|
|
m_pStream->WriteStr(" Do\012");
|
|
}
|
|
void CPage::DrawImage(CImageDict* pImage, double dX, double dY, double dWidth, double dHeight)
|
|
{
|
|
GrSave();
|
|
Concat(dWidth, 0, 0, dHeight, dX, dY);
|
|
ExecuteXObject(pImage);
|
|
GrRestore();
|
|
}
|
|
void CPage::DrawShading(CShading* pShading)
|
|
{
|
|
// Operator : sh
|
|
// Description: Draw gradient
|
|
|
|
const char* sShadingName = GetLocalShadingName(pShading);
|
|
if (!sShadingName)
|
|
return;
|
|
|
|
m_pStream->WriteEscapeName(sShadingName);
|
|
m_pStream->WriteStr(" sh\012");
|
|
}
|
|
void CPage::SetStrokeAlpha(unsigned char unAlpha)
|
|
{
|
|
CExtGrState* pExtGrState = m_pDocument->GetStrokeAlpha((double)(unAlpha / 255.0));
|
|
if (pExtGrState)
|
|
SetExtGrState(pExtGrState);
|
|
}
|
|
void CPage::SetFillAlpha(unsigned char unAlpha)
|
|
{
|
|
CExtGrState* pExtGrState = m_pDocument->GetFillAlpha((double)(unAlpha / 255.0));
|
|
if (pExtGrState)
|
|
SetExtGrState(pExtGrState);
|
|
}
|
|
const char* CPage::GetLocalShadingName(CShading* pShading)
|
|
{
|
|
if (!m_pShadings)
|
|
{
|
|
CDictObject* pResources = GetResourcesItem();
|
|
if (!pResources)
|
|
return NULL;
|
|
|
|
m_pShadings = new CDictObject();
|
|
if (!m_pShadings)
|
|
return NULL;
|
|
|
|
pResources->Add("Shading", m_pShadings);
|
|
}
|
|
|
|
const char* sKey = m_pShadings->GetKey(pShading);
|
|
if (!sKey)
|
|
{
|
|
char sShadingName[LIMIT_MAX_NAME_LEN + 1];
|
|
char *pPointer;
|
|
char *pEndPointer = sShadingName + LIMIT_MAX_NAME_LEN;
|
|
|
|
++m_unShadingsCount;
|
|
while (m_unShadingsCount < LIMIT_MAX_DICT_ELEMENT)
|
|
{
|
|
if (m_pShadings->Get("S" + std::to_string(m_unShadingsCount)))
|
|
++m_unShadingsCount;
|
|
else
|
|
break;
|
|
}
|
|
|
|
pPointer = (char*)StrCpy(sShadingName, "S", pEndPointer);
|
|
ItoA(pPointer, m_unShadingsCount, pEndPointer);
|
|
m_pShadings->Add(sShadingName, pShading);
|
|
sKey = m_pShadings->GetKey(pShading);
|
|
}
|
|
|
|
return sKey;
|
|
}
|
|
const char* CPage::GetLocalPatternName(CImageTilePattern* pPattern)
|
|
{
|
|
if (!m_pPatterns)
|
|
{
|
|
CDictObject* pResources = GetResourcesItem();
|
|
if (!pResources)
|
|
return NULL;
|
|
|
|
m_pPatterns = new CDictObject();
|
|
if (!m_pPatterns)
|
|
return NULL;
|
|
|
|
pResources->Add("Pattern", m_pPatterns);
|
|
}
|
|
|
|
const char* sKey = m_pPatterns->GetKey(pPattern);
|
|
if (!sKey)
|
|
{
|
|
char sPatternName[LIMIT_MAX_NAME_LEN + 1];
|
|
char *pPointer;
|
|
char *pEndPointer = sPatternName + LIMIT_MAX_NAME_LEN;
|
|
|
|
++m_unPatternsCount;
|
|
while (m_unPatternsCount < LIMIT_MAX_DICT_ELEMENT)
|
|
{
|
|
if (m_pPatterns->Get("P" + std::to_string(m_unPatternsCount)))
|
|
++m_unPatternsCount;
|
|
else
|
|
break;
|
|
}
|
|
|
|
pPointer = (char*)StrCpy(sPatternName, "P", pEndPointer);
|
|
ItoA(pPointer, m_unPatternsCount, pEndPointer);
|
|
m_pPatterns->Add(sPatternName, pPattern);
|
|
sKey = m_pPatterns->GetKey(pPattern);
|
|
}
|
|
|
|
return sKey;
|
|
}
|
|
void CPage::SetPatternColorSpace(CImageTilePattern* pPattern)
|
|
{
|
|
// Operator : csn
|
|
// Description: Set pattern for drawing
|
|
|
|
const char* sPatternName = GetLocalPatternName(pPattern);
|
|
if (!sPatternName)
|
|
return;
|
|
|
|
m_pStream->WriteStr("/Pattern cs\012");
|
|
m_pStream->WriteEscapeName(sPatternName);
|
|
m_pStream->WriteStr(" scn\012");
|
|
}
|
|
void CPage::SetFilter(unsigned int unFiler)
|
|
{
|
|
if (m_pContents)
|
|
{
|
|
for (unsigned int unKidIndex = 0, unKidsCount = m_pContents->GetCount(); unKidIndex < unKidsCount; ++unKidIndex)
|
|
{
|
|
CObjectBase* pKid = m_pContents->Get(unKidIndex);
|
|
if (pKid->GetType() == object_type_DICT)
|
|
((CDictObject*)pKid)->SetFilter(unFiler);
|
|
}
|
|
}
|
|
}
|
|
CMatrix* CPage::GetTransform()
|
|
{
|
|
return &m_pGrState->m_oMatrix;
|
|
}
|
|
void CPage::AddGroup(CDictObject* pDict)
|
|
{
|
|
Add("Group", pDict);
|
|
}
|
|
void CPage::AddContents(CXref* pXref)
|
|
{
|
|
CDictObject* pContent = new CDictObject(pXref);
|
|
m_pContents->Add(pContent);
|
|
m_pStream = pContent->GetStream();
|
|
}
|
|
void CPage::SetRotate(int nRotate)
|
|
{
|
|
// The value shall be a multiple of 90
|
|
if (nRotate % 90 == 0)
|
|
Add("Rotate", nRotate % 360);
|
|
}
|
|
void CPage::ClearContent(CXref* pXref)
|
|
{
|
|
m_pContents = new CArrayObject();
|
|
Add("Contents", m_pContents);
|
|
AddContents(pXref);
|
|
#ifndef FILTER_FLATE_DECODE_DISABLED
|
|
SetFilter(STREAM_FILTER_FLATE_DECODE);
|
|
#endif
|
|
}
|
|
void CPage::ClearContentFull(CXref* pXref)
|
|
{
|
|
if (m_pContents)
|
|
{
|
|
for (int i = 0; i < m_pContents->GetCount(); ++i)
|
|
{
|
|
CObjectBase* pObj = m_pContents->Get(i);
|
|
if (pObj->GetType() == object_type_DICT)
|
|
{
|
|
CObjectBase* pLength = ((CDictObject*)pObj)->Get("Length");
|
|
pXref->Remove(pLength);
|
|
}
|
|
pXref->Remove(pObj);
|
|
}
|
|
}
|
|
ClearContent(pXref);
|
|
}
|
|
int CPage::GetRotate()
|
|
{
|
|
CNumberObject* pRotate = (CNumberObject*)GetRotateItem();
|
|
return pRotate ? pRotate->Get() : 0;
|
|
}
|
|
void CPage::BeginMarkedContent(const std::string& sName)
|
|
{
|
|
// Operator : BMC
|
|
// Description: Begin marked content
|
|
|
|
m_pStream->WriteEscapeName(sName.c_str());
|
|
m_pStream->WriteStr(" BMC\012");
|
|
}
|
|
void CPage::BeginMarkedContentDict(const std::string& sName, CDictObject* pBDC)
|
|
{
|
|
// Operator : BDC
|
|
// Description: Begin marked content with dict
|
|
|
|
m_pStream->WriteEscapeName(sName.c_str());
|
|
m_pStream->WriteChar(' ');
|
|
m_pStream->Write(pBDC, NULL);
|
|
m_pStream->WriteStr(" BDC\012");
|
|
}
|
|
void CPage::EndMarkedContent()
|
|
{
|
|
// Operator : EMC
|
|
// Description: End marked content
|
|
|
|
m_pStream->WriteStr("EMC\012");
|
|
}
|
|
void CPage::SetRenderingIntent(ERenderingIntent eRenderingIntent)
|
|
{
|
|
// Operator : ri
|
|
// Description: Rendering intent / color rendering methods
|
|
|
|
m_pStream->WriteEscapeName(c_sRenderingIntent[(int)eRenderingIntent]);
|
|
m_pStream->WriteStr(" ri\012");
|
|
}
|
|
|
|
CFakePage::CFakePage(int nOriginIndex) : m_nOriginIndex(nOriginIndex)
|
|
{
|
|
}
|
|
int CFakePage::GetOriginIndex()
|
|
{
|
|
return m_nOriginIndex;
|
|
}
|
|
//----------------------------------------------------------------------------------------
|
|
// CTextWord
|
|
//----------------------------------------------------------------------------------------
|
|
CTextWord::CTextWord()
|
|
{
|
|
m_nIndex = 0;
|
|
m_pText = (unsigned char*)malloc(STR_BUF);
|
|
m_nSize = STR_BUF;
|
|
}
|
|
CTextWord::~CTextWord()
|
|
{
|
|
if (m_pText)
|
|
free(m_pText);
|
|
}
|
|
void CTextWord::CheckBuffer()
|
|
{
|
|
if (2 * m_nIndex >= m_nSize)
|
|
{
|
|
m_nSize += STR_BUF;
|
|
m_pText = (unsigned char*)realloc(m_pText, m_nSize);
|
|
}
|
|
}
|
|
bool CTextWord::Add(unsigned char* pCodes, unsigned int unLen, double dX, double dY, double dWidth)
|
|
{
|
|
if (2 != unLen)
|
|
return false;
|
|
|
|
CheckBuffer();
|
|
|
|
if (0 == m_nIndex)
|
|
{
|
|
m_pText[0] = pCodes[0];
|
|
m_pText[1] = pCodes[1];
|
|
m_nIndex++;
|
|
m_dStartX = dX;
|
|
m_dStartY = dY;
|
|
|
|
m_dCurX = dX + dWidth;
|
|
}
|
|
else
|
|
{
|
|
if (fabs(dY - m_dStartY) > 0.001 || fabs(dX - m_dCurX) > 0.01)
|
|
return false;
|
|
|
|
m_pText[m_nIndex * 2 + 0] = pCodes[0];
|
|
m_pText[m_nIndex * 2 + 1] = pCodes[1];
|
|
m_nIndex++;
|
|
|
|
m_dCurX = dX + dWidth;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
//----------------------------------------------------------------------------------------
|
|
// CTextLine
|
|
//----------------------------------------------------------------------------------------
|
|
CTextLine::CTextLine()
|
|
{
|
|
}
|
|
CTextLine::~CTextLine()
|
|
{
|
|
Clear();
|
|
}
|
|
bool CTextLine::Add(unsigned char* pCodes, unsigned int unLen, double dX, double dY, double dWidth, double dSize)
|
|
{
|
|
if (2 != unLen)
|
|
return false;
|
|
|
|
if (0 == m_vWords.size())
|
|
{
|
|
CTextWord* pText = new CTextWord();
|
|
if (!pText || !pText->Add(pCodes, unLen, dX, dY, dWidth))
|
|
return false;
|
|
|
|
m_vWords.push_back(pText);
|
|
m_dX = dX;
|
|
m_dY = dY;
|
|
return true;
|
|
}
|
|
|
|
if (fabs(dY - m_dY) > 0.001)
|
|
return false;
|
|
|
|
CTextWord* pLastText = m_vWords.at(m_vWords.size() - 1);
|
|
if (pLastText->Add(pCodes, unLen, dX, dY, dWidth))
|
|
return true;
|
|
|
|
CTextWord* pText = new CTextWord();
|
|
if (!pText || !pText->Add(pCodes, unLen, dX, dY, dWidth))
|
|
return false;
|
|
|
|
m_vWords.push_back(pText);
|
|
double dShift = (pLastText->m_dCurX - dX) * 1000 / (dSize ? dSize : 1);
|
|
m_vShifts.push_back(dShift);
|
|
return true;
|
|
}
|
|
void CTextLine::Flush(CPage* pPage)
|
|
{
|
|
pPage->DrawTextLine(this);
|
|
Clear();
|
|
}
|
|
void CTextLine::Clear()
|
|
{
|
|
for (int nIndex = 0, nCount = m_vWords.size(); nIndex < nCount; nIndex++)
|
|
{
|
|
CTextWord* pText = m_vWords.at(nIndex);
|
|
RELEASEOBJECT(pText);
|
|
}
|
|
m_vWords.clear();
|
|
m_vShifts.clear();
|
|
}
|
|
}
|