Files
core/PdfFile/SrcWriter/Pages.cpp

1848 lines
50 KiB
C++

/*
* Copyright (C) Ascensio System SIA, 2009-2026
*
* 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, together with the
* additional terms provided in the LICENSE file.
*
* 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: https://www.gnu.org/licenses/agpl-3.0.html
*
* You can contact Ascensio System SIA by email at info@onlyoffice.com
* or by postal mail at 20A-6 Ernesta Birznieka-Upisha Street, Riga,
* LV-1050, Latvia, European Union.
*
* The interactive user interfaces in modified versions of the Program
* are required to display Appropriate Legal Notices in accordance with
* Section 5 of the GNU AGPL version 3.
*
* No trademark rights are granted under this License.
*
* All non-code elements of the Product, including illustrations,
* icon sets, and technical writing content, are licensed under the
* Creative Commons Attribution-ShareAlike 4.0 International License:
* https://creativecommons.org/licenses/by-sa/4.0/legalcode
*
* This license applies only to such non-code elements and does not
* modify or replace the licensing terms applicable to the Program's
* source code, which remains licensed under the GNU Affero General
* Public License v3.
*
* SPDX-License-Identifier: AGPL-3.0-only
*/
#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)
Transform(dM11, dM12, dM21, dM22, dX, dY);
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::Transform(double dM11, double dM12, double dM21, double dM22, double dX, double dY)
{
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");
}
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();
}
}