mirror of
https://github.com/ONLYOFFICE/core.git
synced 2026-03-31 10:21:37 +08:00
Compare commits
9 Commits
fix/Fillin
...
feature/do
| Author | SHA1 | Date | |
|---|---|---|---|
| d18d3d1bdf | |||
| 920542cd78 | |||
| bd4d5ec1e9 | |||
| b49997e615 | |||
| d4b4fc04d9 | |||
| 78219fcba1 | |||
| fa3ae4e2e2 | |||
| e69b1cf2d6 | |||
| 8870516a8a |
@ -505,7 +505,13 @@ public:
|
||||
}
|
||||
|
||||
if (m_nType == 0)
|
||||
{
|
||||
std::vector<CPdfLink*> arrLinks = ((CPdfFile*)m_pFile)->GetPdfLinks(nPageIndex);
|
||||
// TODO Usage
|
||||
for (int i = 0; i < arrLinks.size(); ++i)
|
||||
RELEASEOBJECT(arrLinks[i]);
|
||||
((CPdfFile*)m_pFile)->SetPageFonts(nPageIndex);
|
||||
}
|
||||
|
||||
BYTE* res = oRes.GetBuffer();
|
||||
oRes.ClearWithoutAttack();
|
||||
|
||||
@ -141,3 +141,8 @@ void IOfficeDrawingFile::ConvertToRaster(int nPageIndex, const std::wstring& pat
|
||||
pFrame->SaveFile(path, nImageType);
|
||||
RELEASEOBJECT(pFrame);
|
||||
}
|
||||
|
||||
std::vector<CPdfLink*> IOfficeDrawingFile::GetPdfLinks(int nPageIndex)
|
||||
{
|
||||
return std::vector<CPdfLink*>();
|
||||
}
|
||||
|
||||
@ -56,6 +56,20 @@ struct COfficeDrawingPageParams
|
||||
}
|
||||
};
|
||||
|
||||
struct CPdfLink
|
||||
{
|
||||
~CPdfLink() { RELEASEOBJECT(pNext); }
|
||||
|
||||
double pRect[4];
|
||||
BYTE nType;
|
||||
BYTE nKind;
|
||||
unsigned int unPage;
|
||||
unsigned int unKindFlag;
|
||||
double pData[4];
|
||||
std::string sData;
|
||||
CPdfLink* pNext = NULL;
|
||||
};
|
||||
|
||||
class GRAPHICS_DECL IOfficeDrawingFile
|
||||
{
|
||||
public:
|
||||
@ -103,6 +117,8 @@ public:
|
||||
virtual std::wstring GetInfo() = 0;
|
||||
virtual unsigned char* GetStructure() = 0;
|
||||
virtual unsigned char* GetLinks(int nPageIndex) = 0;
|
||||
|
||||
virtual std::vector<CPdfLink*> GetPdfLinks(int nPageIndex);
|
||||
};
|
||||
|
||||
#endif // _OFFICE_DRAWING_FILE_H
|
||||
|
||||
@ -116,8 +116,8 @@ namespace XmlUtils
|
||||
void GetTextWithHHHH(bool bPreserve, wchar_t*& sBuffer, long& nSize, long& nLen);
|
||||
std::wstring GetTextWithHHHH(bool bPreserve);
|
||||
|
||||
std::wstring GetOuterXml(bool bEncodingXml = true);
|
||||
std::wstring GetInnerXml(bool bEncodingXml = true);
|
||||
std::wstring GetOuterXml();
|
||||
std::wstring GetInnerXml();
|
||||
|
||||
int GetAttributesCount();
|
||||
bool MoveToFirstAttribute();
|
||||
|
||||
@ -208,13 +208,13 @@ namespace XmlUtils
|
||||
return m_pInternal->GetTextWithHHHH(bPreserve);
|
||||
}
|
||||
|
||||
std::wstring CXmlLiteReader::GetOuterXml(bool bEncodingXml)
|
||||
std::wstring CXmlLiteReader::GetOuterXml()
|
||||
{
|
||||
return m_pInternal->GetOuterXml(bEncodingXml);
|
||||
return m_pInternal->GetOuterXml();
|
||||
}
|
||||
std::wstring CXmlLiteReader::GetInnerXml(bool bEncodingXml)
|
||||
std::wstring CXmlLiteReader::GetInnerXml()
|
||||
{
|
||||
return m_pInternal->GetInnerXml(bEncodingXml);
|
||||
return m_pInternal->GetInnerXml();
|
||||
}
|
||||
|
||||
int CXmlLiteReader::GetAttributesCount()
|
||||
|
||||
@ -635,13 +635,13 @@ namespace XmlUtils
|
||||
GetTextWithHHHH(bPreserve, pUnicodes, nSize, nLen);
|
||||
return std::wstring(pUnicodes, nLen);
|
||||
}
|
||||
inline std::wstring GetOuterXml(bool bEncodingXml = true)
|
||||
inline std::wstring GetOuterXml()
|
||||
{
|
||||
return GetXml(false, bEncodingXml);
|
||||
return GetXml(false);
|
||||
}
|
||||
inline std::wstring GetInnerXml(bool bEncodingXml = true)
|
||||
inline std::wstring GetInnerXml()
|
||||
{
|
||||
return GetXml(true, bEncodingXml);
|
||||
return GetXml(true);
|
||||
}
|
||||
|
||||
inline int GetAttributesCount()
|
||||
@ -700,7 +700,7 @@ namespace XmlUtils
|
||||
}
|
||||
|
||||
private:
|
||||
inline std::wstring GetXml(bool bInner, bool bEncodingXml = true)
|
||||
inline std::wstring GetXml(bool bInner)
|
||||
{
|
||||
if (!IsValid())
|
||||
return L"";
|
||||
@ -730,7 +730,7 @@ namespace XmlUtils
|
||||
eNodeType == XmlNodeType_Whitespace ||
|
||||
eNodeType == XmlNodeType_SIGNIFICANT_WHITESPACE ||
|
||||
eNodeType == XmlNodeType_CDATA)
|
||||
(bEncodingXml)? oResult.WriteEncodeXmlString(GetText().c_str()): oResult.WriteString(GetText().c_str());
|
||||
oResult.WriteEncodeXmlString(GetText().c_str());
|
||||
else if (eNodeType == XmlNodeType_Element)
|
||||
WriteElement(oResult);
|
||||
else if (eNodeType == XmlNodeType_EndElement)
|
||||
|
||||
@ -204,6 +204,30 @@ void CDocxRenderer::DrawPage(IOfficeDrawingFile* pFile, size_t nPage)
|
||||
put_Width(dWidth);
|
||||
put_Height(dHeight);
|
||||
|
||||
if (pFile->GetType() == OfficeDrawingFileType::odftPDF)
|
||||
{
|
||||
std::vector<CPdfLink*> arrLinks = pFile->GetPdfLinks(nPage);
|
||||
for (int i = 0; i < arrLinks.size(); ++i)
|
||||
{
|
||||
LONG lType = arrLinks[i]->nType;
|
||||
double x1, y1, x2, y2;
|
||||
x1 = arrLinks[i]->pRect[0] / c_dMMToPix;
|
||||
y1 = arrLinks[i]->pRect[1] / c_dMMToPix;
|
||||
x2 = arrLinks[i]->pRect[2] / c_dMMToPix;
|
||||
y2 = arrLinks[i]->pRect[3] / c_dMMToPix;
|
||||
|
||||
std::wstring wsData;
|
||||
if (lType == 1 || lType == 9)
|
||||
continue; // TODO: GoTo bookmarks or anchors
|
||||
if (lType == 6)
|
||||
wsData = NSFile::CUtf8Converter::GetUnicodeStringFromUTF8((BYTE*)arrLinks[i]->sData.c_str(), arrLinks[i]->sData.size());
|
||||
|
||||
m_pInternal->m_oDocument.AddLink(lType, x1, y1, x2, y2, wsData);
|
||||
RELEASEOBJECT(arrLinks[i]);
|
||||
}
|
||||
m_pInternal->m_oDocument.m_oImageManager.UpdateId(arrLinks.size());
|
||||
}
|
||||
|
||||
pFile->DrawPageOnRenderer(this, nPage, nullptr);
|
||||
|
||||
m_pInternal->m_oDocument.m_bIsDisablePageCommand = false;
|
||||
|
||||
@ -30,6 +30,7 @@ LIBS += \
|
||||
HEADERS += \
|
||||
src/logic/elements/BaseItem.h \
|
||||
src/logic/elements/ContText.h \
|
||||
src/logic/elements/Link.h \
|
||||
src/logic/elements/Paragraph.h \
|
||||
src/logic/elements/Shape.h \
|
||||
src/logic/elements/Table.h \
|
||||
@ -55,6 +56,7 @@ HEADERS += \
|
||||
SOURCES += \
|
||||
src/logic/elements/BaseItem.cpp \
|
||||
src/logic/elements/ContText.cpp \
|
||||
src/logic/elements/Link.cpp \
|
||||
src/logic/elements/Paragraph.cpp \
|
||||
src/logic/elements/Shape.cpp \
|
||||
src/logic/elements/Table.cpp \
|
||||
|
||||
@ -744,6 +744,18 @@ namespace NSDocxRenderer
|
||||
m_oCurrentPage.m_lClipMode = lMode;
|
||||
return S_OK;
|
||||
}
|
||||
HRESULT CDocument::AddLink(const LONG& lType, const double& x1, const double y1, const double& x2, const double& y2, const std::wstring& wsData)
|
||||
{
|
||||
CLink oLink;
|
||||
|
||||
UINT nId = m_oImageManager.GetId() + m_arLinks.size();
|
||||
oLink.AddLink(nId, lType, wsData);
|
||||
oLink.AddBBox(x1, y1, x2, y2);
|
||||
m_arLinks.push_back(oLink);
|
||||
m_oCurrentPage.AddLink(oLink);
|
||||
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
void CDocument::ApplyTransform(double d1, double d2, double d3, double d4, double d5, double d6)
|
||||
{
|
||||
@ -912,6 +924,17 @@ namespace NSDocxRenderer
|
||||
oWriter.WriteString(L"\"/>");
|
||||
}
|
||||
|
||||
for (const auto& pLink : m_arLinks)
|
||||
{
|
||||
oWriter.WriteString(L"<Relationship Id=\"rId");
|
||||
oWriter.AddUInt(c_iStartingIdForLinks + pLink.m_nId);
|
||||
oWriter.WriteString(L"\" Type=\"http://schemas.openxmlformats.org/officeDocument/2006/relationships/hyperlink\" Target=\"");
|
||||
oWriter.WriteString(pLink.m_wsData);
|
||||
oWriter.WriteString(L"\" TargetMode=\"");
|
||||
oWriter.WriteString(pLink.m_eType == eLinkType::ltUri ? L"External" : L"Internal");
|
||||
oWriter.WriteString(L"\"/>");
|
||||
}
|
||||
|
||||
oWriter.WriteString(L"</Relationships>");
|
||||
|
||||
NSFile::CFileBinary::SaveToFile(m_strTempDirectory + L"/word/_rels/document.xml.rels", oWriter.GetData());
|
||||
|
||||
@ -44,6 +44,7 @@ namespace NSDocxRenderer
|
||||
|
||||
NSStringUtils::CStringBuilder m_oPageBuilder;
|
||||
std::list<std::string> m_arXmlString;
|
||||
std::list<CLink> m_arLinks;
|
||||
|
||||
public:
|
||||
CDocument(IRenderer* pRenderer, NSFonts::IApplicationFonts* pFonts);
|
||||
@ -178,6 +179,8 @@ namespace NSDocxRenderer
|
||||
HRESULT get_ClipMode(LONG* plMode);
|
||||
HRESULT put_ClipMode(LONG lMode);
|
||||
|
||||
HRESULT AddLink(const LONG& lType, const double& x1, const double y1, const double& x2, const double& y2, const std::wstring& wsData);
|
||||
|
||||
protected:
|
||||
void ApplyTransform(double d1, double d2, double d3, double d4, double d5, double d6);
|
||||
void ApplyTransform2(double dAngle, double dLeft, double dTop, double dWidth, double dHeight, DWORD lFlags);
|
||||
|
||||
@ -273,6 +273,18 @@ namespace NSDocxRenderer
|
||||
shape->m_oBrush.Color1 == c_iWhiteColor && !info)
|
||||
return;
|
||||
|
||||
for (const auto& link : m_arLinks)
|
||||
{
|
||||
if (fabs(shape->m_dTop - link.m_dTop) <= c_dLINK_X_OFFSET_MM &&
|
||||
fabs(shape->m_dLeft - link.m_dLeft) <= c_dLINK_X_OFFSET_MM &&
|
||||
fabs(shape->m_dBot - link.m_dBottom) <= c_dLINK_X_OFFSET_MM &&
|
||||
fabs(shape->m_dRight - link.m_dRight) <= c_dLINK_X_OFFSET_MM)
|
||||
{
|
||||
shape->m_bIsHyperlink = true;
|
||||
shape->m_nRid = c_iStartingIdForLinks + link.m_nId;
|
||||
}
|
||||
}
|
||||
|
||||
if (!skip_shape)
|
||||
{
|
||||
shape->m_nOrder = ++m_nCurrentOrder;
|
||||
@ -488,6 +500,10 @@ namespace NSDocxRenderer
|
||||
{
|
||||
m_arCompleteObjectsBinBase64.push_back(oBase64);
|
||||
}
|
||||
void CPage::AddLink(const CLink& oLink)
|
||||
{
|
||||
m_arLinks.push_back(oLink);
|
||||
}
|
||||
void CPage::ReorderShapesForPptx()
|
||||
{
|
||||
// переместим nullptr в конец и удалим
|
||||
|
||||
@ -3,6 +3,7 @@
|
||||
#include "elements/Paragraph.h"
|
||||
#include "elements/Table.h"
|
||||
#include "elements/Shape.h"
|
||||
#include "elements/Link.h"
|
||||
#include "managers/ImageManager.h"
|
||||
#include "managers/FontStyleManager.h"
|
||||
#include "managers/ParagraphStyleManager.h"
|
||||
@ -91,6 +92,8 @@ namespace NSDocxRenderer
|
||||
void AddCompleteXml(const std::wstring& oXml);
|
||||
void AddCompleteBinBase64(const std::string& oBase64);
|
||||
|
||||
void AddLink(const CLink& pLink);
|
||||
|
||||
private:
|
||||
using shape_ptr_t = std::shared_ptr<CShape>;
|
||||
using cont_ptr_t = std::shared_ptr<CContText>;
|
||||
@ -235,6 +238,8 @@ namespace NSDocxRenderer
|
||||
std::vector<shape_ptr_t> m_arLuminosityShapes;
|
||||
std::vector<shape_ptr_t> m_arOneColorGradientShape;
|
||||
|
||||
std::list<CLink> m_arLinks;
|
||||
|
||||
long m_lLastType = 0;
|
||||
|
||||
size_t m_nCurrentOrder = 0;
|
||||
|
||||
31
DocxRenderer/src/logic/elements/Link.cpp
Normal file
31
DocxRenderer/src/logic/elements/Link.cpp
Normal file
@ -0,0 +1,31 @@
|
||||
#include "Link.h"
|
||||
|
||||
namespace NSDocxRenderer
|
||||
{
|
||||
CLink::~CLink()
|
||||
{
|
||||
Clear();
|
||||
}
|
||||
|
||||
void CLink::Clear()
|
||||
{
|
||||
m_nId = 0;
|
||||
m_eType = eLinkType::ltNone;
|
||||
m_wsData.clear();
|
||||
}
|
||||
|
||||
void CLink::AddLink(BYTE nId, const LONG& lType, const std::wstring& wsData)
|
||||
{
|
||||
m_nId = nId;
|
||||
m_eType = static_cast<eLinkType>(lType);
|
||||
m_wsData = wsData;
|
||||
}
|
||||
|
||||
void CLink::AddBBox(const double& x1, const double& y1, const double& x2, const double& y2)
|
||||
{
|
||||
m_dTop = y1;
|
||||
m_dLeft = x1;
|
||||
m_dBottom = y2;
|
||||
m_dRight = x2;
|
||||
}
|
||||
}
|
||||
34
DocxRenderer/src/logic/elements/Link.h
Normal file
34
DocxRenderer/src/logic/elements/Link.h
Normal file
@ -0,0 +1,34 @@
|
||||
#pragma once
|
||||
#include "BaseItem.h"
|
||||
|
||||
namespace NSDocxRenderer
|
||||
{
|
||||
enum class eLinkType
|
||||
{
|
||||
ltNone = 0,
|
||||
ltGoTo = 1,
|
||||
ltUri = 6,
|
||||
ltNamed = 9,
|
||||
};
|
||||
|
||||
class CLink : public CBaseItem
|
||||
{
|
||||
public:
|
||||
CLink() = default;
|
||||
virtual ~CLink();
|
||||
void Clear();
|
||||
|
||||
void AddLink(BYTE nId, const LONG& type, const std::wstring& wsData);
|
||||
void AddBBox(const double& x1, const double& y1, const double& x2, const double& y2);
|
||||
public:
|
||||
UINT m_nId{0};
|
||||
eLinkType m_eType{eLinkType::ltNone};
|
||||
|
||||
double m_dTop{0.0};
|
||||
double m_dLeft{0.0};
|
||||
double m_dBottom{0.0};
|
||||
double m_dRight{0.0};
|
||||
|
||||
std::wstring m_wsData{};
|
||||
};
|
||||
}
|
||||
@ -637,7 +637,14 @@ namespace NSDocxRenderer
|
||||
break;
|
||||
}
|
||||
oWriter.AddUInt(m_nShapeId);
|
||||
oWriter.WriteString(L"\"/>");
|
||||
oWriter.WriteString(L"\">");
|
||||
if (m_bIsHyperlink)
|
||||
{
|
||||
oWriter.WriteString(L"<a:hlinkClick xmlns:a=\"http://schemas.openxmlformats.org/drawingml/2006/main\" r:id=\"rId");
|
||||
oWriter.AddUInt(m_nRid);
|
||||
oWriter.WriteString(L"\"/>");
|
||||
}
|
||||
oWriter.WriteString(L"</wp:docPr>");
|
||||
oWriter.WriteString(L"<wp:cNvGraphicFramePr/>");
|
||||
BuildSpecificProperties(oWriter);
|
||||
oWriter.WriteString(L"</wp:anchor>");
|
||||
|
||||
@ -46,11 +46,13 @@ namespace NSDocxRenderer
|
||||
std::wstring m_strDstMedia {};
|
||||
|
||||
double m_dRotation {0.0};
|
||||
int m_nRid {0};
|
||||
|
||||
bool m_bIsNoFill {true};
|
||||
bool m_bIsNoStroke {true};
|
||||
bool m_bIsBehindDoc {true};
|
||||
bool m_bIsUseInTable{false};
|
||||
bool m_bIsHyperlink {false};
|
||||
|
||||
std::shared_ptr<CImageInfo> m_pImageInfo{nullptr};
|
||||
double m_dImageTop{};
|
||||
|
||||
@ -132,10 +132,21 @@ namespace NSDocxRenderer
|
||||
m_strDstMedia = L"";
|
||||
m_lMaxSizeImage = 1200;
|
||||
m_lNextIDImage = 0;
|
||||
m_lFileID = 0;
|
||||
|
||||
m_mapImageData.clear();
|
||||
}
|
||||
|
||||
void CImageManager::UpdateId(int idInc)
|
||||
{
|
||||
m_lNextIDImage += idInc;
|
||||
}
|
||||
|
||||
int CImageManager::GetId() const
|
||||
{
|
||||
return m_lNextIDImage;
|
||||
}
|
||||
|
||||
std::shared_ptr<CImageInfo> CImageManager::WriteImage(Aggplus::CImage* pImage, double& x, double& y, double& width, double& height)
|
||||
{
|
||||
if (height < 0)
|
||||
@ -176,10 +187,11 @@ namespace NSDocxRenderer
|
||||
return find->second;
|
||||
|
||||
++m_lNextIDImage;
|
||||
++m_lFileID;
|
||||
auto pInfo = std::make_shared<CImageInfo>();
|
||||
pInfo->m_nId = m_lNextIDImage;
|
||||
pInfo->m_eType = GetImageType(pImage);
|
||||
pInfo->m_strFileName = L"image" + std::to_wstring(pInfo->m_nId);
|
||||
pInfo->m_strFileName = L"image" + std::to_wstring(m_lFileID);
|
||||
pInfo->m_strFileName += ((pInfo->m_eType == CImageInfo::itJPG) ? L".jpg" : L".png");
|
||||
|
||||
UINT format = (pInfo->m_eType == CImageInfo::itJPG) ? 3 : 4;
|
||||
|
||||
@ -19,6 +19,9 @@ namespace NSDocxRenderer
|
||||
~CImageManager() = default;
|
||||
void Clear();
|
||||
|
||||
void UpdateId(int idInc);
|
||||
int GetId() const;
|
||||
|
||||
std::shared_ptr<CImageInfo> WriteImage(Aggplus::CImage* pImage, double& x, double& y, double& width, double& height);
|
||||
std::shared_ptr<CImageInfo> WriteImage(const std::wstring& strFile);
|
||||
std::shared_ptr<CImageInfo> GenerateImageID(Aggplus::CImage* pImage);
|
||||
@ -30,6 +33,7 @@ namespace NSDocxRenderer
|
||||
std::shared_ptr<CImageInfo> GenerateImageID(const std::wstring& strFileName);
|
||||
int m_lMaxSizeImage {1200};
|
||||
int m_lNextIDImage {0};
|
||||
int m_lFileID {0};
|
||||
CCalculatorCRC32 m_oCRC;
|
||||
};
|
||||
}
|
||||
|
||||
@ -46,6 +46,7 @@ const double c_dLINE_DISTANCE_MAX_MM = 50.0;
|
||||
const double c_dSHAPE_TROUGH_MAX_MM = 80.0;
|
||||
const double c_dLINE_SPLIT_DISTANCE_MM = 10.0;
|
||||
const double c_dSHAPE_X_OFFSET_MM = 1.5;
|
||||
const double c_dLINK_X_OFFSET_MM = 0.1;
|
||||
const double c_dAVERAGE_SPACE_WIDTH_COEF = 0.9;
|
||||
const double c_dSPACE_WIDTH_COEF = 0.4;
|
||||
const double c_dMIN_ROTATION = 0.01;
|
||||
@ -64,6 +65,7 @@ const double c_dSTANDART_FIRSTLINE_INDENT_MM = 12.5;
|
||||
const uint32_t c_SPACE_SYM = 0x20;
|
||||
|
||||
const UINT c_iStartingIdForImages = 6;
|
||||
const UINT c_iStartingIdForLinks = 6;
|
||||
constexpr size_t c_nAntiZero = ~0;
|
||||
const UINT c_iStandartRelativeHeight = 0x0400;
|
||||
|
||||
|
||||
@ -392,7 +392,6 @@ enum ElementType
|
||||
typeMathSemantics,
|
||||
typeMathAnnotation,
|
||||
typeMathAnnotationXml,
|
||||
typeMathSignatue,
|
||||
typeMN,
|
||||
typeMI,
|
||||
typeMS,
|
||||
|
||||
@ -37,7 +37,7 @@
|
||||
namespace cpdoccore {
|
||||
namespace common {
|
||||
|
||||
bool read_doc_element::read_sax( xml::sax * Reader, bool bOnlyText )
|
||||
bool read_doc_element::read_sax( xml::sax * Reader )
|
||||
{
|
||||
const unsigned int currentDepth = Reader->depth();
|
||||
|
||||
@ -63,15 +63,7 @@ bool read_doc_element::read_sax( xml::sax * Reader, bool bOnlyText )
|
||||
return false;
|
||||
}
|
||||
|
||||
if(bOnlyText)
|
||||
{
|
||||
const std::wstring value = Reader->getXML();
|
||||
if (false == value.empty())
|
||||
{
|
||||
add_text(value);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
while (true)
|
||||
{
|
||||
nodeType = Reader->next(currentDepth);
|
||||
|
||||
@ -57,7 +57,7 @@ namespace cpdoccore {
|
||||
class read_doc_element
|
||||
{
|
||||
public:
|
||||
bool read_sax( xml::sax * Reader, bool bOnlyText = false);
|
||||
bool read_sax( xml::sax * Reader);
|
||||
virtual ~read_doc_element() = 0;
|
||||
|
||||
virtual void add_child_element ( xml::sax * Reader, const std::wstring & Ns, const std::wstring & Name) = 0;
|
||||
|
||||
@ -99,9 +99,7 @@ public:
|
||||
virtual bool moveToAttrOwner() = 0;
|
||||
|
||||
virtual NodeType next(int Depth) = 0;
|
||||
|
||||
virtual std::wstring getXML() =0;
|
||||
|
||||
|
||||
virtual ~sax() = 0;
|
||||
|
||||
};
|
||||
|
||||
@ -70,8 +70,6 @@ public:
|
||||
virtual bool moveToAttrOwner();
|
||||
|
||||
virtual NodeType next(int Depth);
|
||||
|
||||
virtual std::wstring getXML();
|
||||
|
||||
private:
|
||||
smart_ptr< XmlUtils::CXmlLiteReader > xml_;
|
||||
@ -226,11 +224,6 @@ NodeType saxXmlLiteReader::next(int Depth)
|
||||
|
||||
}
|
||||
|
||||
std::wstring saxXmlLiteReader::getXML()
|
||||
{
|
||||
return xml_->GetInnerXml(false);
|
||||
}
|
||||
|
||||
sax_ptr create_sax_xmllite(const wchar_t * FileName)
|
||||
{
|
||||
return sax_ptr( new saxXmlLiteReader(FileName) );
|
||||
|
||||
@ -322,7 +322,6 @@ SOURCES += \
|
||||
../../Reader/Converter/xlsx_data_validation.cpp \
|
||||
../../Reader/Converter/xlsx_utils.cpp \
|
||||
../../Reader/Converter/xlsx_xf.cpp \
|
||||
../../Reader/Converter/StarMath2OOXML/shakey.cpp \
|
||||
../../Reader/Converter/StarMath2OOXML/cooxml2odf.cpp \
|
||||
\
|
||||
../../Writer/Format/office_document.cpp \
|
||||
@ -723,7 +722,6 @@ HEADERS += \
|
||||
../../Reader/Converter/xlsx_xf.h \
|
||||
../../Reader/Converter/conversionelement.h \
|
||||
../../Reader/Converter/ConvertOO2OOX.h \
|
||||
../../Reader/Converter/StarMath2OOXML/shakey.h \
|
||||
../../Reader/Converter/StarMath2OOXML/cooxml2odf.h \
|
||||
\
|
||||
../../Writer/Format/math_elementaries.h \
|
||||
@ -831,3 +829,4 @@ HEADERS += \
|
||||
../../Writer/Converter/VmlShapeTypes2Oox.h \
|
||||
../../Writer/Converter/XlsxConverter.h \
|
||||
../../Writer/Converter/PptxConverter.h \
|
||||
|
||||
|
||||
@ -3,7 +3,7 @@
|
||||
namespace StarMath
|
||||
{
|
||||
//class OOXml2Odf
|
||||
COOXml2Odf::COOXml2Odf():m_wsBaseColor(L""),m_wsHashAnnotation(L""),m_uiBaseSize(0),m_bStretchyAcc(false),m_bHeight(false)
|
||||
COOXml2Odf::COOXml2Odf():m_wsBaseColor(L""),m_uiBaseSize(0),m_bStretchyAcc(false),m_bHeight(false)
|
||||
{
|
||||
m_pXmlWrite = new XmlUtils::CXmlWriter;
|
||||
}
|
||||
@ -21,7 +21,7 @@ namespace StarMath
|
||||
if(pNode == nullptr)
|
||||
{
|
||||
m_pXmlWrite->WriteNodeBegin(L"annotation",false);
|
||||
EndOdf(pNode);
|
||||
EndOdf();
|
||||
return;
|
||||
}
|
||||
else
|
||||
@ -30,9 +30,8 @@ namespace StarMath
|
||||
m_pXmlWrite->WriteNodeBegin(L"annotation",true);
|
||||
m_pXmlWrite->WriteAttribute(L"encoding",L"StarMath 5.0");
|
||||
m_pXmlWrite->WriteNodeEnd(L"w",true,false);
|
||||
HashSM::RemovingSpaces(m_wsAnnotationStarMath);
|
||||
m_pXmlWrite->WriteString(m_wsAnnotationStarMath);
|
||||
EndOdf(pNode);
|
||||
EndOdf();
|
||||
}
|
||||
void COOXml2Odf::NodeDefinition(OOX::WritingElement *pNode,const bool& bMatrix)
|
||||
{
|
||||
@ -707,16 +706,9 @@ namespace StarMath
|
||||
else if(wsChr == L"\u2230") return L"lllint";
|
||||
else return L"oper " + wsChr;
|
||||
}
|
||||
void COOXml2Odf::EndOdf(OOX::WritingElement *pNode)
|
||||
void COOXml2Odf::EndOdf()
|
||||
{
|
||||
m_pXmlWrite->WriteNodeEnd(L"annotation",false,false);
|
||||
m_pXmlWrite->WriteNodeBegin(L"signature",true);
|
||||
m_pXmlWrite->WriteAttribute(L"encoding",L"OOXML");
|
||||
m_pXmlWrite->WriteAttribute(L"alg",L"sha256");
|
||||
m_pXmlWrite->WriteAttribute(L"shakey",HashSM::HashingAnnotation(m_wsAnnotationStarMath));
|
||||
m_pXmlWrite->WriteNodeEnd(L"w",true,false);
|
||||
m_pXmlWrite->WriteString(pNode->toXML());
|
||||
m_pXmlWrite->WriteNodeEnd(L"signature",false,false);
|
||||
m_pXmlWrite->WriteNodeEnd(L"semantics",false,false);
|
||||
m_pXmlWrite->WriteNodeEnd(L"math",false,false);
|
||||
}
|
||||
@ -1871,10 +1863,6 @@ namespace StarMath
|
||||
}
|
||||
return wsText16;
|
||||
}
|
||||
std::wstring COOXml2Odf::GetHashAnnotation()
|
||||
{
|
||||
return m_wsHashAnnotation;
|
||||
}
|
||||
//class COneElement
|
||||
COneElement::COneElement():m_stAttribute(nullptr),m_iStyle(0)
|
||||
{}
|
||||
|
||||
@ -37,8 +37,6 @@
|
||||
#include "../../../../OOXML/Base/Unit.h"
|
||||
#include "../../../../OOXML/Common/SimpleTypes_OMath.h"
|
||||
#include "../../../../DesktopEditor/common/StringUTF32.h"
|
||||
#include "../../../../../DesktopEditor/common/File.h"
|
||||
#include "shakey.h"
|
||||
#include "typeselements.h"
|
||||
#include "fontType.h"
|
||||
#include <vector>
|
||||
@ -182,17 +180,16 @@ namespace StarMath
|
||||
static bool ColorCheck(const std::wstring& wsColor,std::wstring& wsRecordColor);
|
||||
void CheckVectorElementsForMf(std::vector<OOX::WritingElement*> arWrElement);
|
||||
TFormulaSize GetFormulaSize();
|
||||
void EndOdf(OOX::WritingElement* pNode);
|
||||
void EndOdf();
|
||||
std::wstring GetOdf();
|
||||
std::wstring GetAnnotation();
|
||||
std::wstring GetSemantic();
|
||||
void SetBaseAttribute(std::wstring wsBaseColor = L"", unsigned int uiBaseSize = 0);
|
||||
std::wstring GetHashAnnotation();
|
||||
private:
|
||||
XmlUtils::CXmlWriter* m_pXmlWrite;
|
||||
std::wstring m_wsAnnotationStarMath,m_wsSemantic;
|
||||
std::stack<StValuePr*> m_stAttribute;
|
||||
std::wstring m_wsBaseColor,m_wsHashAnnotation;
|
||||
std::wstring m_wsBaseColor;
|
||||
unsigned int m_uiBaseSize;
|
||||
TFormulaSize m_stSize;
|
||||
bool m_bHeight;
|
||||
|
||||
@ -1,72 +0,0 @@
|
||||
#include "shakey.h"
|
||||
#include "../../../../../DesktopEditor/common/File.h"
|
||||
#include "../../../../../Common/3dParty/cryptopp/sha.h"
|
||||
#include "../../../../../Common/3dParty/cryptopp/hex.h"
|
||||
#include "../../../../../Common/3dParty/cryptopp/base64.h"
|
||||
#include "../../../../../Common/3dParty/cryptopp/filters.h"
|
||||
#include "../../../../../Common/3dParty/cryptopp/files.h"
|
||||
|
||||
namespace HashSM
|
||||
{
|
||||
std::wstring HashingAnnotation(std::wstring &wsAnnotation, const bool& bUnicodeConversionString)
|
||||
{
|
||||
RemovingSpaces(wsAnnotation);
|
||||
std::wstring wsTempAnnotation = L"";
|
||||
if(bUnicodeConversionString)
|
||||
{
|
||||
wsTempAnnotation = wsAnnotation;
|
||||
wsAnnotation.clear();
|
||||
for(wchar_t wcOneSymbol:wsTempAnnotation)
|
||||
{
|
||||
if(wcOneSymbol == L'"')
|
||||
wsAnnotation += L""";
|
||||
else
|
||||
wsAnnotation += wcOneSymbol;
|
||||
}
|
||||
}
|
||||
std::string sAnnotation = U_TO_UTF8(wsAnnotation),sResult;
|
||||
std::wstring wsResult;
|
||||
CryptoPP::SHA256 hash;
|
||||
CryptoPP::StringSource oStringSourse(sAnnotation, true, new CryptoPP::HashFilter(hash,new CryptoPP::HexEncoder(new CryptoPP::StringSink(sResult))));
|
||||
wsResult = UTF8_TO_U(sResult);
|
||||
if( bUnicodeConversionString && !wsTempAnnotation.empty())
|
||||
wsAnnotation = wsTempAnnotation;
|
||||
return wsResult;
|
||||
}
|
||||
|
||||
bool HashComparison(const std::wstring& wsHashFirst,const std::wstring& wsHashSecond)
|
||||
{
|
||||
if(wsHashFirst.empty() || wsHashSecond.empty() || wsHashFirst.length() != wsHashSecond.length())
|
||||
return false;
|
||||
|
||||
std::string sFirstHash = U_TO_UTF8(wsHashFirst),sSecondHash = U_TO_UTF8(wsHashSecond);
|
||||
|
||||
const char* cFirst = sFirstHash.c_str();
|
||||
const char* cSecond = sSecondHash.c_str();
|
||||
|
||||
size_t sizeLen = wsHashFirst.length();
|
||||
for(size_t i = 0; i< sizeLen;i++)
|
||||
{
|
||||
if((cFirst[i]^cSecond[i]) != 0)
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void RemovingSpaces(std::wstring &wsAnnotation)
|
||||
{
|
||||
if(!wsAnnotation.empty())
|
||||
{
|
||||
while(wsAnnotation.back() == L' ')
|
||||
wsAnnotation.pop_back();
|
||||
while(wsAnnotation[0] == L' ')
|
||||
{
|
||||
if(wsAnnotation.size() > 1)
|
||||
{
|
||||
wsAnnotation = wsAnnotation.substr(1,wsAnnotation.size());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@ -1,12 +0,0 @@
|
||||
#ifndef SHAKEY_H
|
||||
#define SHAKEY_H
|
||||
#include<string>
|
||||
|
||||
namespace HashSM
|
||||
{
|
||||
std::wstring HashingAnnotation(std::wstring & wsAnnotation, const bool &bUnicodeConversionString = false);
|
||||
bool HashComparison(const std::wstring& wsHashFirst,const std::wstring& wsHashSecond);
|
||||
void RemovingSpaces(std::wstring& wsAnnotation);
|
||||
}
|
||||
|
||||
#endif // SHAKEY_H
|
||||
@ -41,17 +41,16 @@ namespace odf_reader {
|
||||
|
||||
|
||||
bool create_element_and_read(xml::sax * Reader,
|
||||
const std::wstring & Ns,
|
||||
const std::wstring & Name,
|
||||
office_element_ptr & _Element,
|
||||
document_context * Context,
|
||||
bool isRoot,
|
||||
bool bOnlyText)
|
||||
const std::wstring & Ns,
|
||||
const std::wstring & Name,
|
||||
office_element_ptr & _Element,
|
||||
document_context * Context,
|
||||
bool isRoot)
|
||||
{
|
||||
if (office_element_ptr elm = office_element_creator::get()->create(Ns, Name, Context, isRoot))
|
||||
{
|
||||
elm->afterCreate();
|
||||
elm->read_sax( Reader, bOnlyText );
|
||||
elm->read_sax( Reader );
|
||||
elm->afterReadContent();
|
||||
|
||||
if (_Element) // элемент читается повторно
|
||||
|
||||
@ -31,7 +31,7 @@
|
||||
*/
|
||||
|
||||
#include "math_elements.h"
|
||||
#include "../Converter/StarMath2OOXML/shakey.h"
|
||||
// #include "../Converter/StarMath2OOXML/cconversionsmtoooxml.h"
|
||||
#include "../Converter/StarMath2OOXML/conversionmathformula.h"
|
||||
|
||||
namespace cpdoccore {
|
||||
@ -87,9 +87,7 @@ void math_semantics::add_child_element( xml::sax * Reader, const std::wstring &
|
||||
{
|
||||
CP_CREATE_ELEMENT(annotation_);
|
||||
}
|
||||
else if(CP_CHECK_NAME1(L"signature"))
|
||||
create_element_and_read(Reader,Ns,Name,signature_,getContext(),false,true);
|
||||
else
|
||||
else
|
||||
CP_CREATE_ELEMENT(content_);
|
||||
|
||||
}
|
||||
@ -101,24 +99,15 @@ void math_semantics::oox_convert(oox::math_context & Context)
|
||||
}
|
||||
void math_semantics::oox_convert(oox::math_context &Context, int iTypeConversion)
|
||||
{
|
||||
math_signature* pSignature = dynamic_cast<math_signature*>(signature_.get());
|
||||
math_annotation* annotation = dynamic_cast<math_annotation*>(annotation_.get());
|
||||
math_annotation_xml* annotation_xml = dynamic_cast<math_annotation_xml*>(annotation_.get());
|
||||
|
||||
std::wstring annotation_text(L"");
|
||||
|
||||
std::wstring annotation_text;
|
||||
if ((annotation) && (annotation->text_)) annotation_text = *annotation->text_;
|
||||
else if ((annotation_xml) && (annotation_xml->text_)) annotation_text = *annotation_xml->text_;
|
||||
bool result = false;
|
||||
if(pSignature)
|
||||
{
|
||||
if(pSignature->text_ && pSignature->GetAlg() == L"sha256" && HashSM::HashComparison(pSignature->GetShaKey(),HashSM::HashingAnnotation(annotation_text,true)))
|
||||
{
|
||||
Context.output_stream() << *pSignature->text_;
|
||||
result = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (!annotation_text.empty() && !result)
|
||||
bool result = false;
|
||||
if (!annotation_text.empty())
|
||||
{
|
||||
result = true;
|
||||
|
||||
@ -206,46 +195,11 @@ void math_annotation_xml::add_child_element( xml::sax * Reader, const std::wstri
|
||||
}
|
||||
|
||||
void math_annotation_xml::add_text(const std::wstring & Text)
|
||||
{
|
||||
text_->append(Text);
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------------------------------
|
||||
|
||||
const wchar_t * math_signature::ns = L"math";
|
||||
const wchar_t * math_signature::name = L"signature";
|
||||
//----------------------------------------------------------------------------------------------------
|
||||
|
||||
std::wstring math_signature::GetAlg() const
|
||||
{
|
||||
if(alg_)
|
||||
return alg_.get();
|
||||
return L"";
|
||||
}
|
||||
|
||||
std::wstring math_signature::GetShaKey() const
|
||||
{
|
||||
if(shakey_)
|
||||
return shakey_.get();
|
||||
return L"";
|
||||
}
|
||||
|
||||
void math_signature::add_attributes( const xml::attributes_wc_ptr & Attributes )
|
||||
{
|
||||
CP_APPLY_ATTR(L"encoding", encoding_);
|
||||
CP_APPLY_ATTR(L"alg", alg_);
|
||||
CP_APPLY_ATTR(L"shakey", shakey_);
|
||||
}
|
||||
|
||||
void math_signature::add_child_element( xml::sax * Reader, const std::wstring & Ns, const std::wstring & Name)
|
||||
{
|
||||
CP_CREATE_ELEMENT(content_);
|
||||
}
|
||||
|
||||
void math_signature::add_text(const std::wstring & Text)
|
||||
{
|
||||
text_ = Text;
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------------------------------
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@ -107,12 +107,12 @@ private:
|
||||
|
||||
office_element_ptr_array content_;
|
||||
office_element_ptr annotation_;
|
||||
office_element_ptr signature_;
|
||||
};
|
||||
|
||||
CP_REGISTER_OFFICE_ELEMENT2(math_semantics);
|
||||
CP_REGISTER_OFFICE_ELEMENT3(math_semantics);
|
||||
//-------------------------------------------------------------------------------------------
|
||||
|
||||
class math_annotation : public office_math_element
|
||||
{
|
||||
public:
|
||||
@ -159,33 +159,5 @@ private:
|
||||
CP_REGISTER_OFFICE_ELEMENT2(math_annotation_xml);
|
||||
CP_REGISTER_OFFICE_ELEMENT3(math_annotation_xml);
|
||||
//--------------------------------------------------------------------
|
||||
//-------------------------------------------------------------------------------------------
|
||||
class math_signature : public office_math_element
|
||||
{
|
||||
public:
|
||||
static const wchar_t * ns;
|
||||
static const wchar_t * name;
|
||||
static const xml::NodeType xml_type = xml::typeElement;
|
||||
static const ElementType type = typeMathSignatue;
|
||||
|
||||
virtual void oox_convert(oox::math_context& Context) {}
|
||||
|
||||
_CP_OPT(std::wstring) text_;
|
||||
|
||||
std::wstring GetAlg() const;
|
||||
std::wstring GetShaKey() const;
|
||||
private:
|
||||
virtual void add_attributes( const xml::attributes_wc_ptr & Attributes );
|
||||
virtual void add_child_element( xml::sax * Reader, const std::wstring & Ns, const std::wstring & Name);
|
||||
virtual void add_text(const std::wstring & Text);
|
||||
|
||||
office_element_ptr_array content_;
|
||||
_CP_OPT(std::wstring) encoding_;
|
||||
_CP_OPT(std::wstring) alg_;
|
||||
_CP_OPT(std::wstring) shakey_;
|
||||
};
|
||||
|
||||
CP_REGISTER_OFFICE_ELEMENT2(math_signature);
|
||||
CP_REGISTER_OFFICE_ELEMENT3(math_signature);
|
||||
}
|
||||
}
|
||||
|
||||
@ -137,8 +137,7 @@ bool create_element_and_read(xml::sax * Reader,
|
||||
const std::wstring & Name,
|
||||
office_element_ptr & _Element,
|
||||
document_context * Context,
|
||||
bool isRoot = false,
|
||||
bool bOnlyText = false);
|
||||
bool isRoot = false);
|
||||
|
||||
// Создать элемент и в случае успеха прочитать его содержимое из SAX, поместить в array
|
||||
bool create_element_and_read(xml::sax * Reader,
|
||||
@ -170,4 +169,4 @@ void not_applicable_element(const std::wstring & Current, const std::wstring & N
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1020,7 +1020,7 @@ void OoxConverter::convert(PPTX::Logic::SpPr *oox_spPr, PPTX::Logic::ShapeStyle*
|
||||
|
||||
bool bLine = odf_context()->drawing_context()->isLineShape();
|
||||
|
||||
if (custGeom && !custGeom->cxnLst.empty() && !odf_context()->drawing_context()->isCustomClosed() && !odf_context()->drawing_context()->isNonPrimitive())
|
||||
if (custGeom && !custGeom->cxnLst.empty() && !odf_context()->drawing_context()->isCustomClosed())
|
||||
bLine = true;
|
||||
|
||||
odf_context()->drawing_context()->start_area_properties();
|
||||
|
||||
@ -1265,11 +1265,6 @@ bool odf_drawing_context::isCustomClosed()
|
||||
return impl_->current_drawing_state_.path_closed_;
|
||||
}
|
||||
|
||||
bool odf_drawing_context::isNonPrimitive()
|
||||
{
|
||||
return (impl_->current_drawing_state_.oox_shape_->odf_type_name == L"non-primitive" || impl_->current_drawing_state_.oox_shape_->odf_type_name == L"ooxml-non-primitive");
|
||||
}
|
||||
|
||||
bool odf_drawing_context::isLineShape()
|
||||
{
|
||||
if (impl_->current_level_.empty()) return false;
|
||||
|
||||
@ -194,7 +194,6 @@ public:
|
||||
|
||||
bool isLineShape();
|
||||
bool isCustomClosed();
|
||||
bool isNonPrimitive();
|
||||
|
||||
void corrected_line_fill();
|
||||
|
||||
|
||||
@ -605,6 +605,12 @@ BYTE* CPdfFile::GetAPAnnots(int nRasterW, int nRasterH, int nBackgroundColor, in
|
||||
return NULL;
|
||||
return m_pInternal->pReader->GetAPAnnots(nRasterW, nRasterH, nBackgroundColor, nPageIndex, nAnnot, sView);
|
||||
}
|
||||
std::vector<CPdfLink*> CPdfFile::GetPdfLinks(int nPageIndex)
|
||||
{
|
||||
if (!m_pInternal->pReader)
|
||||
return std::vector<CPdfLink*>();
|
||||
return m_pInternal->pReader->GetPdfLinks(nPageIndex);
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------------------
|
||||
|
||||
|
||||
@ -136,6 +136,7 @@ public:
|
||||
virtual std::wstring GetInfo();
|
||||
virtual BYTE* GetStructure();
|
||||
virtual BYTE* GetLinks(int nPageIndex);
|
||||
std::vector<CPdfLink*> GetPdfLinks(int nPageIndex) override;
|
||||
|
||||
bool ValidMetaData();
|
||||
// Захватывает полученную память malloc data
|
||||
|
||||
@ -1124,7 +1124,6 @@ BYTE* CPdfReader::GetStructure()
|
||||
}
|
||||
BYTE* CPdfReader::GetLinks(int _nPageIndex)
|
||||
{
|
||||
// TODO Links должны стать частью Annots
|
||||
PDFDoc* pDoc = NULL;
|
||||
int nPageIndex = GetPageIndex(_nPageIndex, &pDoc);
|
||||
if (nPageIndex < 0 || !pDoc || !pDoc->getCatalog())
|
||||
@ -1257,6 +1256,64 @@ BYTE* CPdfReader::GetLinks(int _nPageIndex)
|
||||
|
||||
return oLinks.Serialize();
|
||||
}
|
||||
std::vector<CPdfLink*> CPdfReader::GetPdfLinks(int _nPageIndex)
|
||||
{
|
||||
std::vector<CPdfLink*> oRes;
|
||||
if (m_vPDFContext.empty())
|
||||
return oRes;
|
||||
|
||||
PDFDoc* pDoc = NULL;
|
||||
PdfReader::CPdfFontList* pFontList = NULL;
|
||||
int nStartRefID = 0;
|
||||
int nPageIndex = GetPageIndex(_nPageIndex, &pDoc, &pFontList, &nStartRefID);
|
||||
if (nPageIndex < 0 || !pDoc || !pFontList || !pDoc->getCatalog())
|
||||
return oRes;
|
||||
|
||||
Page* pPage = pDoc->getCatalog()->getPage(nPageIndex);
|
||||
if (!pPage)
|
||||
return oRes;
|
||||
|
||||
Object oAnnots;
|
||||
if (!pPage->getAnnots(&oAnnots)->isArray())
|
||||
{
|
||||
oAnnots.free();
|
||||
return oRes;
|
||||
}
|
||||
|
||||
for (int i = 0, nNum = oAnnots.arrayGetLength(); i < nNum; ++i)
|
||||
{
|
||||
Object oAnnot;
|
||||
if (!oAnnots.arrayGet(i, &oAnnot)->isDict())
|
||||
{
|
||||
oAnnot.free();
|
||||
continue;
|
||||
}
|
||||
|
||||
Object oSubtype;
|
||||
std::string sType;
|
||||
if (oAnnot.dictLookup("Subtype", &oSubtype)->isName())
|
||||
sType = oSubtype.getName();
|
||||
oSubtype.free(); oAnnot.free();
|
||||
|
||||
Object oAnnotRef;
|
||||
PdfReader::CAnnotLink* pAnnot = NULL;
|
||||
oAnnots.arrayGetNF(i, &oAnnotRef);
|
||||
|
||||
if (sType == "Link")
|
||||
pAnnot = new PdfReader::CAnnotLink(pDoc, &oAnnotRef, nPageIndex, nStartRefID);
|
||||
|
||||
CPdfLink* pLink = NULL;
|
||||
if (pAnnot)
|
||||
pLink = pAnnot->GetPdfLink();
|
||||
|
||||
if (pLink)
|
||||
oRes.push_back(pLink);
|
||||
RELEASEOBJECT(pAnnot);
|
||||
}
|
||||
|
||||
oAnnots.free();
|
||||
return oRes;
|
||||
}
|
||||
BYTE* CPdfReader::GetWidgets()
|
||||
{
|
||||
NSWasm::CData oRes;
|
||||
|
||||
@ -126,6 +126,7 @@ public:
|
||||
BYTE* GetButtonIcon(int nBackgroundColor, int nPageIndex, bool bBase64 = false, int nBWidget = -1, const char* sIView = NULL);
|
||||
BYTE* StreamToCData(BYTE* pSteam, int nLength);
|
||||
const std::map<std::wstring, std::wstring>& GetFonts() { return m_mFonts; }
|
||||
std::vector<CPdfLink*> GetPdfLinks(int nPageIndex);
|
||||
|
||||
private:
|
||||
void Clear();
|
||||
|
||||
@ -533,6 +533,75 @@ CAnnot::CBorderType* getBorder(Object* oBorder, bool bBSorBorder)
|
||||
return pBorderType;
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
// Action
|
||||
//------------------------------------------------------------------------
|
||||
|
||||
void CActionGoTo::GetPdfLink(CPdfLink* pLink)
|
||||
{
|
||||
pLink->nType = 1;
|
||||
pLink->unPage = unPage;
|
||||
pLink->nKind = nKind;
|
||||
switch (nKind)
|
||||
{
|
||||
case destXYZ:
|
||||
case destFitH:
|
||||
case destFitBH:
|
||||
case destFitV:
|
||||
case destFitBV:
|
||||
{
|
||||
pLink->unKindFlag = unKindFlag;
|
||||
if (unKindFlag & (1 << 0))
|
||||
pLink->pData[0] = pRect[0];
|
||||
if (unKindFlag & (1 << 1))
|
||||
pLink->pData[1] = pRect[1];
|
||||
if (unKindFlag & (1 << 2))
|
||||
pLink->pData[2] = pRect[2];
|
||||
break;
|
||||
}
|
||||
case destFitR:
|
||||
{
|
||||
pLink->pData[0] = pRect[0];
|
||||
pLink->pData[1] = pRect[1];
|
||||
pLink->pData[2] = pRect[2];
|
||||
pLink->pData[3] = pRect[3];
|
||||
break;
|
||||
}
|
||||
case destFit:
|
||||
case destFitB:
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
if (pNext)
|
||||
{
|
||||
pLink->pNext = new CPdfLink();
|
||||
GetPdfLink(pLink->pNext);
|
||||
}
|
||||
}
|
||||
void CActionURI::GetPdfLink(CPdfLink* pLink)
|
||||
{
|
||||
pLink->nType = 6;
|
||||
pLink->sData = sURI;
|
||||
|
||||
if (pNext)
|
||||
{
|
||||
pLink->pNext = new CPdfLink();
|
||||
GetPdfLink(pLink->pNext);
|
||||
}
|
||||
}
|
||||
void CActionNamed::GetPdfLink(CPdfLink* pLink)
|
||||
{
|
||||
pLink->nType = 10;
|
||||
pLink->sData = sNamed;
|
||||
|
||||
if (pNext)
|
||||
{
|
||||
pLink->pNext = new CPdfLink();
|
||||
GetPdfLink(pLink->pNext);
|
||||
}
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
// Widget
|
||||
//------------------------------------------------------------------------
|
||||
@ -1265,6 +1334,17 @@ CAnnotLink::~CAnnotLink()
|
||||
RELEASEOBJECT(m_pAction);
|
||||
RELEASEOBJECT(m_pPA);
|
||||
}
|
||||
CPdfLink* CAnnotLink::GetPdfLink()
|
||||
{
|
||||
CPdfLink* pRes = new CPdfLink();
|
||||
pRes->pRect[0] = m_pRect[0];
|
||||
pRes->pRect[1] = m_pRect[1];
|
||||
pRes->pRect[2] = m_pRect[2];
|
||||
pRes->pRect[3] = m_pRect[3];
|
||||
if (m_pAction)
|
||||
m_pAction->GetPdfLink(pRes);
|
||||
return pRes;
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
// Screen
|
||||
|
||||
@ -41,6 +41,7 @@
|
||||
#include "../../DesktopEditor/graphics/pro/Fonts.h"
|
||||
#include "../../DesktopEditor/graphics/pro/Graphics.h"
|
||||
#include "../../DesktopEditor/graphics/pro/js/wasm/src/serialize.h"
|
||||
#include "../../DesktopEditor/graphics/pro/officedrawingfile.h"
|
||||
|
||||
#include "RendererOutputDev.h"
|
||||
|
||||
@ -57,6 +58,7 @@ public:
|
||||
virtual ~CAction() { RELEASEOBJECT(pNext); }
|
||||
|
||||
virtual void ToWASM(NSWasm::CData& oRes);
|
||||
virtual void GetPdfLink(CPdfLink* pLink) {};
|
||||
|
||||
std::string sType;
|
||||
CAction* pNext;
|
||||
@ -70,18 +72,21 @@ struct CActionGoTo final : public CAction
|
||||
double pRect[4];
|
||||
BYTE nKind;
|
||||
|
||||
virtual void GetPdfLink(CPdfLink* pLink) override;
|
||||
void ToWASM(NSWasm::CData& oRes) override;
|
||||
};
|
||||
struct CActionURI final : public CAction
|
||||
{
|
||||
std::string sURI;
|
||||
|
||||
virtual void GetPdfLink(CPdfLink* pLink) override;
|
||||
void ToWASM(NSWasm::CData& oRes) override;
|
||||
};
|
||||
struct CActionNamed final : public CAction
|
||||
{
|
||||
std::string sNamed;
|
||||
|
||||
virtual void GetPdfLink(CPdfLink* pLink) override;
|
||||
void ToWASM(NSWasm::CData& oRes) override;
|
||||
};
|
||||
struct CActionJavaScript final : public CAction
|
||||
@ -347,6 +352,8 @@ public:
|
||||
CAnnotLink(PDFDoc* pdfDoc, Object* oAnnotRef, int nPageIndex, int nStartRefID);
|
||||
virtual ~CAnnotLink();
|
||||
|
||||
CPdfLink* GetPdfLink();
|
||||
|
||||
void ToWASM(NSWasm::CData& oRes) override;
|
||||
|
||||
private:
|
||||
|
||||
@ -293,7 +293,7 @@ Object *ObjectStream::getObject(int objIdx, int objNum, Object *obj) {
|
||||
|
||||
XRef::XRef(BaseStream *strA, GBool repair) {
|
||||
GFileOffset pos;
|
||||
Object obj;
|
||||
Object obj, root, rootType;
|
||||
XRefPosSet *posSet;
|
||||
int i;
|
||||
|
||||
@ -370,6 +370,17 @@ XRef::XRef(BaseStream *strA, GBool repair) {
|
||||
if (obj.isRef()) {
|
||||
rootNum = obj.getRefNum();
|
||||
rootGen = obj.getRefGen();
|
||||
|
||||
fetch(rootNum, rootGen, &root);
|
||||
if (!root.isDict() || !root.dictLookup("Type", &rootType)->isName("Catalog"))
|
||||
{
|
||||
root.free(); rootType.free();
|
||||
errCode = errDamaged;
|
||||
ok = gFalse;
|
||||
return;
|
||||
}
|
||||
root.free(); rootType.free();
|
||||
|
||||
obj.free();
|
||||
} else {
|
||||
obj.free();
|
||||
@ -972,6 +983,39 @@ GBool XRef::constructXRef() {
|
||||
error(errSyntaxError, -1, "Couldn't find trailer dictionary");
|
||||
return gFalse;
|
||||
}
|
||||
|
||||
// validate the catalog object found by the initial xref scan
|
||||
if (!quickCheckCatalog(str, entries[rootNum].offset + start)) {
|
||||
error(errSyntaxWarning, -1, "invalid Catalog, trying repair scan");
|
||||
// reset state before repair scan
|
||||
rootNum = -1;
|
||||
rootGen = -1;
|
||||
|
||||
gfree(entries);
|
||||
entries = NULL;
|
||||
size = 0;
|
||||
last = -1;
|
||||
|
||||
for (int i = 0; i < xrefCacheSize; ++i) {
|
||||
if (cache[i].num >= 0) {
|
||||
cache[i].num = -1;
|
||||
cache[i].obj.free();
|
||||
}
|
||||
}
|
||||
for (int i = 0; i < objStrCacheLength; ++i) {
|
||||
delete objStrs[i];
|
||||
objStrs[i] = NULL;
|
||||
}
|
||||
objStrCacheLength = 0;
|
||||
|
||||
gfree(xrefTablePos);
|
||||
xrefTablePos = NULL;
|
||||
xrefTablePosLen = 0;
|
||||
|
||||
if (!trailerDict.isNone()) trailerDict.free();
|
||||
return constructXRefRepair();
|
||||
}
|
||||
|
||||
return gTrue;
|
||||
}
|
||||
|
||||
@ -1016,6 +1060,316 @@ GBool XRef::saveTrailerDict(Dict *dict, GBool isXRefStream) {
|
||||
return bRes;
|
||||
}
|
||||
|
||||
// quick check: is the object at the given offset a /Type /Catalog dict?
|
||||
GBool XRef::quickCheckCatalog(BaseStream *str, GFileOffset pos) {
|
||||
if (pos < 0) return gFalse;
|
||||
char buf[512];
|
||||
str->setPos(pos);
|
||||
int n = str->getBlock(buf, sizeof(buf) - 1);
|
||||
if (n <= 0) return gFalse;
|
||||
buf[n] = '\0';
|
||||
|
||||
// skip "N G obj"
|
||||
char *p = strstr(buf, " obj");
|
||||
if (!p) return gFalse;
|
||||
p += 4;
|
||||
|
||||
// look for /Type key
|
||||
char *t = strstr(p, "/Type");
|
||||
if (!t || t - buf > 450) return gFalse;
|
||||
t += 5;
|
||||
|
||||
// skip whitespace between /Type and its value
|
||||
while (*t == ' ' || *t == '\t' || *t == '\r' || *t == '\n') ++t;
|
||||
|
||||
// value may be "/Catalog" or "/ Catalog" — skip the slash
|
||||
if (*t == '/') ++t;
|
||||
while (*t == ' ' || *t == '\t') ++t;
|
||||
|
||||
return strncmp(t, "Catalog", 7) == 0;
|
||||
}
|
||||
|
||||
GFileOffset XRef::findValidCutoff(XRefTempEntry *tempEntries, int tempSize, std::vector<XRefTrailerCandidate> &candidates) {
|
||||
for (auto it = candidates.rbegin(); it != candidates.rend(); ++it) {
|
||||
XRefTrailerCandidate &c = *it;
|
||||
|
||||
GFileOffset rootPos = c.rootObjPos;
|
||||
if (rootPos < 0 && c.rootNum < tempSize && tempEntries[c.rootNum].used)
|
||||
rootPos = tempEntries[c.rootNum].offset;
|
||||
|
||||
if (rootPos < 0) continue;
|
||||
|
||||
if (quickCheckCatalog(str, rootPos + start)) {
|
||||
return (c.sectionEnd >= 0) ? c.sectionEnd : c.trailerPos;
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
GBool XRef::constructXRefRepair() {
|
||||
int tempSize = 0;
|
||||
XRefTempEntry *tempEntries = NULL;
|
||||
|
||||
auto ensureTemp = [&](int num) {
|
||||
if (num >= tempSize) {
|
||||
int newSize = num + 256;
|
||||
tempEntries = (XRefTempEntry *)greallocn(
|
||||
tempEntries, newSize, sizeof(XRefTempEntry));
|
||||
for (int i = tempSize; i < newSize; ++i) {
|
||||
tempEntries[i].offset = -1;
|
||||
tempEntries[i].gen = 0;
|
||||
tempEntries[i].used = gFalse;
|
||||
}
|
||||
tempSize = newSize;
|
||||
}
|
||||
};
|
||||
|
||||
std::vector<XRefTrailerCandidate> candidates;
|
||||
|
||||
int tmpStreamEndsSize = 0;
|
||||
int tmpStreamEndsLen = 0;
|
||||
GFileOffset *tmpStreamEnds = NULL;
|
||||
|
||||
// ---------- PASS 1 ----------
|
||||
{
|
||||
char buf[4096 + 1];
|
||||
str->reset();
|
||||
GFileOffset bufPos = start;
|
||||
char *p = buf;
|
||||
char *end = buf;
|
||||
GBool startOfLine = gTrue;
|
||||
GBool eof = gFalse;
|
||||
|
||||
GFileOffset sectionStart = start;
|
||||
|
||||
while (1) {
|
||||
if (end - p < 256 && !eof) {
|
||||
memmove(buf, p, end - p);
|
||||
bufPos += p - buf;
|
||||
p = buf + (end - p);
|
||||
int n = (int)(buf + 4096 - p);
|
||||
int m = str->getBlock(p, n);
|
||||
end = p + m;
|
||||
*end = '\0';
|
||||
p = buf;
|
||||
eof = m < n;
|
||||
}
|
||||
if (p == end && eof) break;
|
||||
|
||||
GFileOffset curPos = (GFileOffset)(bufPos + (p - buf));
|
||||
|
||||
// %%EOF — close current section
|
||||
if (startOfLine && p[0] == '%' && !strncmp(p, "%%EOF", 5)) {
|
||||
for (auto &c : candidates)
|
||||
if (c.sectionEnd < 0 && c.sectionStart == sectionStart)
|
||||
c.sectionEnd = curPos;
|
||||
sectionStart = curPos + 5;
|
||||
p += 5;
|
||||
startOfLine = gFalse;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (startOfLine && !strncmp(p, "endstream", 9)) {
|
||||
if (tmpStreamEndsLen == tmpStreamEndsSize) {
|
||||
tmpStreamEndsSize += 64;
|
||||
tmpStreamEnds = (GFileOffset *)greallocn(
|
||||
tmpStreamEnds, tmpStreamEndsSize, sizeof(GFileOffset));
|
||||
}
|
||||
tmpStreamEnds[tmpStreamEndsLen++] = curPos;
|
||||
p += 9;
|
||||
startOfLine = gFalse;
|
||||
continue;
|
||||
}
|
||||
|
||||
if (startOfLine && !strncmp(p, "trailer", 7)) {
|
||||
GFileOffset tPos = (GFileOffset)(bufPos + (p + 7 - buf));
|
||||
Object tDict, obj;
|
||||
obj.initNull();
|
||||
Parser *parser = new Parser(
|
||||
NULL,
|
||||
new Lexer(NULL, str->makeSubStream(tPos, gFalse, 0, &obj)),
|
||||
gFalse);
|
||||
parser->getObj(&tDict);
|
||||
if (tDict.isDict()) {
|
||||
Object rootRef;
|
||||
tDict.getDict()->lookupNF("Root", &rootRef);
|
||||
if (rootRef.isRef()) {
|
||||
int rNum = rootRef.getRefNum();
|
||||
ensureTemp(rNum);
|
||||
XRefTrailerCandidate c;
|
||||
c.trailerPos = tPos;
|
||||
c.sectionStart = sectionStart;
|
||||
c.sectionEnd = -1;
|
||||
c.rootNum = rNum;
|
||||
c.rootGen = rootRef.getRefGen();
|
||||
// rootObjPos — берём то что уже видели к этому моменту
|
||||
c.rootObjPos = tempEntries[rNum].used
|
||||
? tempEntries[rNum].offset
|
||||
: -1;
|
||||
candidates.push_back(c);
|
||||
}
|
||||
rootRef.free();
|
||||
}
|
||||
tDict.free();
|
||||
delete parser;
|
||||
p += 7;
|
||||
startOfLine = gFalse;
|
||||
continue;
|
||||
}
|
||||
|
||||
// N G obj — write to tempEntries only
|
||||
if (startOfLine && *p >= '0' && *p <= '9') {
|
||||
char *q = p;
|
||||
int num = 0, gen = 0;
|
||||
do { num = num * 10 + (*q - '0'); ++q; }
|
||||
while (*q >= '0' && *q <= '9' && num < 100000000);
|
||||
if (*q == '\t' || *q == '\x0c' || *q == ' ') {
|
||||
do { ++q; } while (*q == '\t' || *q == '\x0c' || *q == ' ');
|
||||
if (*q >= '0' && *q <= '9') {
|
||||
do { gen = gen * 10 + (*q - '0'); ++q; }
|
||||
while (*q >= '0' && *q <= '9' && gen < 100000000);
|
||||
if ((*q == '\t' || *q == '\x0c' || *q == ' ') &&
|
||||
!strncmp(q + 1, "obj", 3)) {
|
||||
ensureTemp(num);
|
||||
// will be overwritten — pass 2 is bounded by cutoff
|
||||
tempEntries[num].offset = curPos - start;
|
||||
tempEntries[num].gen = gen;
|
||||
tempEntries[num].used = gTrue;
|
||||
}
|
||||
}
|
||||
}
|
||||
while (*p && *p != '\n' && *p != '\r') ++p;
|
||||
startOfLine = gFalse;
|
||||
continue;
|
||||
}
|
||||
|
||||
startOfLine = (*p == '\n' || *p == '\r');
|
||||
++p;
|
||||
}
|
||||
} // end of pass 1
|
||||
|
||||
// ---------- VALIDATION ----------
|
||||
GFileOffset cutoffPos = findValidCutoff(tempEntries, tempSize, candidates);
|
||||
gfree(tempEntries);
|
||||
gfree(tmpStreamEnds);
|
||||
|
||||
if (cutoffPos < 0) {
|
||||
error(errSyntaxWarning, -1, "no valid Catalog found, scanning entire file");
|
||||
cutoffPos = str->getPos();
|
||||
}
|
||||
|
||||
// ---------- PASS 2 ----------
|
||||
// identical to original constructXRef, but stops at curPos >= cutoffPos
|
||||
|
||||
int streamObjNumsSize = 0;
|
||||
int streamObjNumsLen = 0;
|
||||
int *streamObjNums = NULL;
|
||||
int lastObjNum = -1;
|
||||
rootNum = -1;
|
||||
|
||||
gfree(streamEnds);
|
||||
streamEnds = NULL;
|
||||
streamEndsLen = 0;
|
||||
int streamEndsSize = 0;
|
||||
|
||||
{
|
||||
char buf[4096 + 1];
|
||||
str->reset();
|
||||
GFileOffset bufPos = start;
|
||||
char *p = buf;
|
||||
char *end = buf;
|
||||
GBool startOfLine = gTrue;
|
||||
GBool eof = gFalse;
|
||||
|
||||
while (1) {
|
||||
if (end - p < 256 && !eof) {
|
||||
memmove(buf, p, end - p);
|
||||
bufPos += p - buf;
|
||||
p = buf + (end - p);
|
||||
int n = (int)(buf + 4096 - p);
|
||||
int m = str->getBlock(p, n);
|
||||
end = p + m;
|
||||
*end = '\0';
|
||||
p = buf;
|
||||
eof = m < n;
|
||||
}
|
||||
if (p == end && eof) break;
|
||||
|
||||
GFileOffset curPos = (GFileOffset)(bufPos + (p - buf));
|
||||
|
||||
if (curPos - start >= cutoffPos)
|
||||
break;
|
||||
|
||||
if (startOfLine && !strncmp(p, "trailer", 7)) {
|
||||
constructTrailerDict((GFileOffset)(bufPos + (p + 7 - buf)));
|
||||
p += 7;
|
||||
startOfLine = gFalse;
|
||||
} else if (startOfLine && !strncmp(p, "endstream", 9)) {
|
||||
if (streamEndsLen == streamEndsSize) {
|
||||
streamEndsSize += 64;
|
||||
streamEnds = (GFileOffset *)greallocn(
|
||||
streamEnds, streamEndsSize, sizeof(GFileOffset));
|
||||
}
|
||||
streamEnds[streamEndsLen++] = curPos;
|
||||
p += 9;
|
||||
startOfLine = gFalse;
|
||||
} else if (startOfLine && *p >= '0' && *p <= '9') {
|
||||
p = constructObjectEntry(p, (GFileOffset)(bufPos + (p - buf)),
|
||||
&lastObjNum);
|
||||
startOfLine = gFalse;
|
||||
} else if (p[0] == '>' && p[1] == '>') {
|
||||
p += 2;
|
||||
startOfLine = gFalse;
|
||||
while (*p == '\t' || *p == '\n' || *p == '\x0c' ||
|
||||
*p == '\r' || *p == ' ') {
|
||||
startOfLine = (*p == '\n' || *p == '\r');
|
||||
++p;
|
||||
}
|
||||
if (!strncmp(p, "stream", 6)) {
|
||||
if (lastObjNum >= 0) {
|
||||
if (streamObjNumsLen == streamObjNumsSize) {
|
||||
streamObjNumsSize += 64;
|
||||
streamObjNums = (int *)greallocn(
|
||||
streamObjNums, streamObjNumsSize, sizeof(int));
|
||||
}
|
||||
streamObjNums[streamObjNumsLen++] = lastObjNum;
|
||||
}
|
||||
p += 6;
|
||||
startOfLine = gFalse;
|
||||
}
|
||||
} else {
|
||||
startOfLine = (*p == '\n' || *p == '\r');
|
||||
++p;
|
||||
}
|
||||
}
|
||||
} // end of pass 2
|
||||
|
||||
GBool bRoot = gFalse;
|
||||
for (int i = 0; i < streamObjNumsLen; ++i) {
|
||||
Object obj;
|
||||
fetch(streamObjNums[i], entries[streamObjNums[i]].gen, &obj);
|
||||
if (obj.isStream()) {
|
||||
Dict *dict = obj.streamGetDict();
|
||||
Object type;
|
||||
dict->lookup("Type", &type);
|
||||
if (type.isName("XRef") && !bRoot) {
|
||||
bRoot = saveTrailerDict(dict, gTrue);
|
||||
} else if (type.isName("ObjStm")) {
|
||||
constructObjectStreamEntries(&obj, streamObjNums[i]);
|
||||
}
|
||||
type.free();
|
||||
}
|
||||
obj.free();
|
||||
}
|
||||
gfree(streamObjNums);
|
||||
|
||||
if (rootNum < 0) {
|
||||
error(errSyntaxError, -1, "Couldn't find trailer dictionary");
|
||||
return gFalse;
|
||||
}
|
||||
return gTrue;
|
||||
}
|
||||
|
||||
// Look for an object header ("nnn ggg obj") at [p]. The first
|
||||
// character at *[p] is a digit. [pos] is the position of *[p].
|
||||
char *XRef::constructObjectEntry(char *p, GFileOffset pos, int *objNum) {
|
||||
|
||||
@ -10,6 +10,7 @@
|
||||
#define XREF_H
|
||||
|
||||
#include <aconf.h>
|
||||
#include <vector>
|
||||
|
||||
#ifdef USE_GCC_PRAGMAS
|
||||
#pragma interface
|
||||
@ -50,6 +51,21 @@ struct XRefCacheEntry {
|
||||
Object obj;
|
||||
};
|
||||
|
||||
struct XRefTempEntry {
|
||||
GFileOffset offset;
|
||||
int gen;
|
||||
GBool used;
|
||||
};
|
||||
|
||||
struct XRefTrailerCandidate {
|
||||
GFileOffset trailerPos;
|
||||
GFileOffset sectionStart;
|
||||
GFileOffset sectionEnd;
|
||||
GFileOffset rootObjPos; // Root object offset at the time the trailer was encountered
|
||||
int rootNum;
|
||||
int rootGen;
|
||||
};
|
||||
|
||||
#define xrefCacheSize 16
|
||||
|
||||
#define objStrCacheSize 128
|
||||
@ -179,6 +195,9 @@ private:
|
||||
void constructObjectStreamEntries(Object *objStr, int objStrObjNum);
|
||||
GBool constructXRefEntry(int num, int gen, GFileOffset pos,
|
||||
XRefEntryType type);
|
||||
GBool constructXRefRepair();
|
||||
GFileOffset findValidCutoff(XRefTempEntry *tempEntries, int tempSize, std::vector<XRefTrailerCandidate> &candidates);
|
||||
static GBool quickCheckCatalog(BaseStream *str, GFileOffset pos);
|
||||
GBool getObjectStreamObject(int objStrNum, int objIdx,
|
||||
int objNum, Object *obj);
|
||||
ObjectStream *getObjectStream(int objStrNum);
|
||||
|
||||
Reference in New Issue
Block a user