mirror of
https://github.com/ONLYOFFICE/core.git
synced 2026-04-07 13:55:33 +08:00
Add PrefixForm for rename same full names
This commit is contained in:
@ -2782,15 +2782,15 @@ void CPdfEditor::EndMarkedContent()
|
||||
}
|
||||
bool CPdfEditor::IsBase14(const std::wstring& wsFontName, bool& bBold, bool& bItalic, std::wstring& wsFontPath)
|
||||
{
|
||||
std::map<std::wstring, std::wstring>::iterator it = m_mFonts.find(wsFontName);
|
||||
std::map<std::wstring, std::wstring>::const_iterator it = m_mFonts.find(wsFontName);
|
||||
if (it != m_mFonts.end())
|
||||
wsFontPath = it->second;
|
||||
if (wsFontPath.empty())
|
||||
{
|
||||
std::map<std::wstring, std::wstring> mFonts = m_pReader->GetFonts();
|
||||
it = mFonts.find(wsFontName);
|
||||
if (it != mFonts.end())
|
||||
wsFontPath = it->second;
|
||||
const std::map<std::wstring, std::wstring>& mFonts = m_pReader->GetFonts();
|
||||
std::map<std::wstring, std::wstring>::const_iterator it2 = mFonts.find(wsFontName);
|
||||
if (it2 != mFonts.end())
|
||||
wsFontPath = it2->second;
|
||||
}
|
||||
if (wsFontPath.empty())
|
||||
return false;
|
||||
|
||||
@ -336,11 +336,11 @@ void CPdfFile::GetPageInfo(int nPageIndex, double* pdWidth, double* pdHeight, do
|
||||
else
|
||||
m_pInternal->pReader->GetPageInfo(nPageIndex, pdWidth, pdHeight, pdDpiX, pdDpiY);
|
||||
}
|
||||
bool CPdfFile::MergePages(BYTE* data, DWORD length, int nMaxID)
|
||||
bool CPdfFile::MergePages(BYTE* data, DWORD length, int nMaxID, const std::string& sPrefixForm)
|
||||
{
|
||||
if (!m_pInternal->pReader)
|
||||
return false;
|
||||
return m_pInternal->pReader->MergePages(data, length, L"", nMaxID) && (m_pInternal->pReader->GetError() == 0);
|
||||
return m_pInternal->pReader->MergePages(data, length, L"", nMaxID, sPrefixForm) && (m_pInternal->pReader->GetError() == 0);
|
||||
}
|
||||
int CPdfFile::GetRotate(int nPageIndex)
|
||||
{
|
||||
|
||||
@ -127,7 +127,7 @@ public:
|
||||
virtual BYTE* GetLinks(int nPageIndex);
|
||||
|
||||
bool ValidMetaData();
|
||||
bool MergePages(BYTE* data, DWORD length, int nMaxID = 0);
|
||||
bool MergePages(BYTE* data, DWORD length, int nMaxID = 0, const std::string& sPrefixForm = "");
|
||||
int GetRotate(int nPageIndex);
|
||||
int GetMaxRefID();
|
||||
BYTE* GetWidgets();
|
||||
|
||||
@ -565,7 +565,7 @@ bool CPdfReader::ValidMetaData()
|
||||
oID.free(); oID2.free();
|
||||
return bRes;
|
||||
}
|
||||
bool CPdfReader::MergePages(BYTE* pData, DWORD nLength, const std::wstring& wsPassword, int nMaxID)
|
||||
bool CPdfReader::MergePages(BYTE* pData, DWORD nLength, const std::wstring& wsPassword, int nMaxID, const std::string& sPrefixForm)
|
||||
{
|
||||
if (m_eError)
|
||||
return false;
|
||||
@ -580,6 +580,7 @@ bool CPdfReader::MergePages(BYTE* pData, DWORD nLength, const std::wstring& wsPa
|
||||
CPdfReaderContext* pContext = new CPdfReaderContext();
|
||||
pContext->m_pDocument = new PDFDoc(str, owner_pswd, user_pswd);
|
||||
pContext->m_pFontList = new PdfReader::CPdfFontList();
|
||||
pContext->sPrefixForm = sPrefixForm;
|
||||
if (nMaxID != 0)
|
||||
pContext->m_nStartID = nMaxID;
|
||||
else if (!m_vPDFContext.empty())
|
||||
@ -603,7 +604,7 @@ bool CPdfReader::MergePages(BYTE* pData, DWORD nLength, const std::wstring& wsPa
|
||||
|
||||
return true;
|
||||
}
|
||||
bool CPdfReader::MergePages(const std::wstring& wsFile, const std::wstring& wsPassword, int nMaxID)
|
||||
bool CPdfReader::MergePages(const std::wstring& wsFile, const std::wstring& wsPassword, int nMaxID, const std::string& sPrefixForm)
|
||||
{
|
||||
if (m_eError)
|
||||
return false;
|
||||
@ -615,9 +616,10 @@ bool CPdfReader::MergePages(const std::wstring& wsFile, const std::wstring& wsPa
|
||||
CPdfReaderContext* pContext = new CPdfReaderContext();
|
||||
pContext->m_pDocument = new PDFDoc((char*)sPathUtf8.c_str(), owner_pswd, user_pswd);
|
||||
pContext->m_pFontList = new PdfReader::CPdfFontList();
|
||||
pContext->sPrefixForm = sPrefixForm;
|
||||
if (nMaxID != 0)
|
||||
pContext->m_nStartID = nMaxID;
|
||||
if (!m_vPDFContext.empty())
|
||||
else if (!m_vPDFContext.empty())
|
||||
pContext->m_nStartID = m_vPDFContext.back()->m_nStartID + m_vPDFContext.back()->m_pDocument->getXRef()->getNumObjects();
|
||||
PDFDoc* pDoc = pContext->m_pDocument;
|
||||
m_vPDFContext.push_back(pContext);
|
||||
@ -1131,10 +1133,11 @@ BYTE* CPdfReader::GetWidgets()
|
||||
NSWasm::CData oRes;
|
||||
oRes.SkipLen();
|
||||
|
||||
std::map<std::string, std::string> mForms;
|
||||
int nStartPage = 0;
|
||||
for (CPdfReaderContext* pPDFContext : m_vPDFContext)
|
||||
for (int iPDF = 0; iPDF < m_vPDFContext.size(); ++iPDF)
|
||||
{
|
||||
PDFDoc* pDoc = pPDFContext->m_pDocument;
|
||||
PDFDoc* pDoc = m_vPDFContext[iPDF]->m_pDocument;
|
||||
if (!pDoc || !pDoc->getCatalog())
|
||||
continue;
|
||||
if (!pDoc->getCatalog()->getForm() || !pDoc->getXRef())
|
||||
@ -1143,9 +1146,35 @@ BYTE* CPdfReader::GetWidgets()
|
||||
continue;
|
||||
}
|
||||
|
||||
PdfReader::CAnnots* pAnnots = new PdfReader::CAnnots(pDoc, m_pFontManager, pPDFContext->m_pFontList, nStartPage, pPDFContext->m_nStartID);
|
||||
PdfReader::CAnnots* pAnnots = new PdfReader::CAnnots(pDoc, m_pFontManager, m_vPDFContext[iPDF]->m_pFontList, nStartPage, m_vPDFContext[iPDF]->m_nStartID);
|
||||
if (pAnnots)
|
||||
{
|
||||
const std::vector<PdfReader::CAnnotWidget*>& arrAnnots = pAnnots->GetAnnots();
|
||||
for (int i = 0; i < arrAnnots.size(); ++i)
|
||||
{
|
||||
const std::string& sFullName = arrAnnots[i]->GetFullName();
|
||||
std::map<std::string, std::string>::iterator it = mForms.find(sFullName);
|
||||
if (it == mForms.end())
|
||||
mForms[sFullName] = arrAnnots[i]->GetType();
|
||||
else if (mForms[sFullName] != arrAnnots[i]->GetType())
|
||||
{
|
||||
if (iPDF == 0)
|
||||
{
|
||||
// error
|
||||
// throw "Same full names for forms of different types within the same file";
|
||||
}
|
||||
else
|
||||
{
|
||||
int nPrefix = 0;
|
||||
std::string sPrefix = m_vPDFContext[iPDF]->sPrefixForm + "_" + std::to_string(nPrefix);
|
||||
while (!pAnnots->ChangeFullNameAnnot(i, sPrefix))
|
||||
sPrefix = m_vPDFContext[iPDF]->sPrefixForm + "_" + std::to_string(++nPrefix);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pAnnots->ToWASM(oRes);
|
||||
}
|
||||
RELEASEOBJECT(pAnnots);
|
||||
nStartPage += pDoc->getNumPages();
|
||||
}
|
||||
|
||||
@ -46,6 +46,7 @@ struct CPdfReaderContext
|
||||
PDFDoc* m_pDocument;
|
||||
PdfReader::CPdfFontList* m_pFontList;
|
||||
unsigned int m_nStartID;
|
||||
std::string sPrefixForm;
|
||||
|
||||
CPdfReaderContext() : m_pDocument(NULL), m_pFontList(NULL), m_nStartID(0) {}
|
||||
CPdfReaderContext(PDFDoc* pDocument, PdfReader::CPdfFontList* pFontList, unsigned int nStartID) : m_pDocument(pDocument), m_pFontList(pFontList), m_nStartID(nStartID) {}
|
||||
@ -77,8 +78,8 @@ public:
|
||||
int GetMaxRefID();
|
||||
int GetNumPages();
|
||||
bool ValidMetaData();
|
||||
bool MergePages(BYTE* pData, DWORD nLength, const std::wstring& wsPassword = L"", int nMaxID = 0);
|
||||
bool MergePages(const std::wstring& wsFile, const std::wstring& wsPassword = L"", int nMaxID = 0);
|
||||
bool MergePages(BYTE* pData, DWORD nLength, const std::wstring& wsPassword = L"", int nMaxID = 0, const std::string& sPrefixForm = "");
|
||||
bool MergePages(const std::wstring& wsFile, const std::wstring& wsPassword = L"", int nMaxID = 0, const std::string& sPrefixForm = "");
|
||||
void GetPageInfo(int nPageIndex, double* pdWidth, double* pdHeight, double* pdDpiX, double* pdDpiY);
|
||||
void DrawPageOnRenderer(IRenderer* pRenderer, int nPageIndex, bool* pBreak);
|
||||
std::wstring GetInfo();
|
||||
@ -102,7 +103,7 @@ public:
|
||||
BYTE* GetAPWidget (int nRasterW, int nRasterH, int nBackgroundColor, int nPageIndex, int nWidget = -1, const char* sView = NULL, const char* sBView = NULL);
|
||||
BYTE* GetAPAnnots (int nRasterW, int nRasterH, int nBackgroundColor, int nPageIndex, int nAnnot = -1, const char* sView = NULL);
|
||||
BYTE* GetButtonIcon(int nBackgroundColor, int nPageIndex, bool bBase64 = false, int nBWidget = -1, const char* sIView = NULL);
|
||||
std::map<std::wstring, std::wstring> GetFonts() { return m_mFonts; }
|
||||
const std::map<std::wstring, std::wstring>& GetFonts() { return m_mFonts; }
|
||||
|
||||
private:
|
||||
void Clear();
|
||||
|
||||
@ -1458,6 +1458,7 @@ CAnnotWidget::CAnnotWidget(PDFDoc* pdfDoc, AcroFormField* pField, int nStartRefI
|
||||
oObj.free();
|
||||
|
||||
// 17 - Родитель - Parent
|
||||
m_unRefNumParent = 0;
|
||||
if (oField.dictLookupNF("Parent", &oObj)->isRef())
|
||||
{
|
||||
m_unRefNumParent = oObj.getRefNum() + nStartRefID;
|
||||
@ -1467,6 +1468,7 @@ CAnnotWidget::CAnnotWidget(PDFDoc* pdfDoc, AcroFormField* pField, int nStartRefI
|
||||
|
||||
// 18 - Частичное имя поля - T
|
||||
m_sT = DictLookupString(&oField, "T", 18);
|
||||
m_sFullName = m_sT;
|
||||
|
||||
// 20 - OO метаданные форм - OMetadata
|
||||
m_sOMetadata = DictLookupString(&oField, "OMetadata", 20);
|
||||
@ -1510,8 +1512,7 @@ CAnnotWidget::CAnnotWidget(PDFDoc* pdfDoc, AcroFormField* pField, int nStartRefI
|
||||
}
|
||||
CAnnotWidget::~CAnnotWidget()
|
||||
{
|
||||
for (int i = 0; i < m_arrAction.size(); ++i)
|
||||
RELEASEOBJECT(m_arrAction[i]);
|
||||
ClearActions();
|
||||
}
|
||||
std::string CAnnotWidget::FieldLookupString(AcroFormField* pField, const char* sName, int nByte)
|
||||
{
|
||||
@ -1580,6 +1581,23 @@ void CAnnotWidget::SetButtonFont(PDFDoc* pdfDoc, AcroFormField* pField, NSFonts:
|
||||
|
||||
oFontRef.free();
|
||||
}
|
||||
bool CAnnotWidget::ChangeFullName(const std::string& sPrefixForm)
|
||||
{
|
||||
if (m_unFlags & (1 << 18))
|
||||
{
|
||||
m_sT += sPrefixForm;
|
||||
m_sFullName += sPrefixForm;
|
||||
// ClearActions();
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
void CAnnotWidget::ClearActions()
|
||||
{
|
||||
for (int i = 0; i < m_arrAction.size(); ++i)
|
||||
RELEASEOBJECT(m_arrAction[i]);
|
||||
m_arrAction.clear();
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
// Popup
|
||||
@ -2485,6 +2503,21 @@ CAnnots::CAnnots(PDFDoc* pdfDoc, NSFonts::IFontManager* pFontManager, CPdfFontLi
|
||||
if (pField->getAcroFormFieldType() == acroFormFieldPushbutton)
|
||||
pAnnot->SetButtonFont(pdfDoc, pField, pFontManager, pFontList);
|
||||
pAnnot->SetPage(nStartPage + pField->getPageNum());
|
||||
|
||||
unsigned int unRefNumParent = pAnnot->GetRefNumParent();
|
||||
if (unRefNumParent)
|
||||
{
|
||||
std::vector<CAnnotParent*>::iterator it = std::find_if(m_arrParents.begin(), m_arrParents.end(), [unRefNumParent](CAnnotParent* pP) { return pP->unRefNum == unRefNumParent; });
|
||||
if (it != m_arrParents.end() && !((*it)->sFullName.empty()))
|
||||
{
|
||||
const std::string& sFullNameChild = pAnnot->GetFullName();
|
||||
if (sFullNameChild.empty())
|
||||
pAnnot->SetFullName((*it)->sFullName);
|
||||
else
|
||||
pAnnot->SetFullName((*it)->sFullName + "." + sFullNameChild);
|
||||
}
|
||||
}
|
||||
|
||||
m_arrAnnots.push_back(pAnnot);
|
||||
}
|
||||
}
|
||||
@ -2521,6 +2554,7 @@ void CAnnots::getParents(PDFDoc* pdfDoc, Object* oFieldRef, int nStartRefID)
|
||||
std::string sStr = NSStringExt::CConverter::GetUtf8FromUTF32(s->getUnicode(), s->getLength());
|
||||
pAnnotParent->unFlags |= (1 << 0);
|
||||
pAnnotParent->sT = sStr;
|
||||
pAnnotParent->sFullName = sStr;
|
||||
delete s;
|
||||
}
|
||||
oObj.free();
|
||||
@ -2673,11 +2707,75 @@ void CAnnots::getParents(PDFDoc* pdfDoc, Object* oFieldRef, int nStartRefID)
|
||||
pAnnotParent->unFlags |= (1 << 4);
|
||||
pAnnotParent->unRefNumParent = oParentRefObj.getRefNum() + nStartRefID;
|
||||
getParents(pdfDoc, &oParentRefObj, nStartRefID);
|
||||
|
||||
unsigned int unRefNumParent = pAnnotParent->unRefNumParent;
|
||||
std::vector<CAnnotParent*>::iterator it = std::find_if(m_arrParents.begin(), m_arrParents.end(), [unRefNumParent](CAnnotParent* pP) { return pP->unRefNum == unRefNumParent; });
|
||||
if (it != m_arrParents.end() && !((*it)->sFullName.empty()))
|
||||
{
|
||||
if (pAnnotParent->sFullName.empty())
|
||||
pAnnotParent->sFullName = (*it)->sFullName;
|
||||
else
|
||||
pAnnotParent->sFullName = (*it)->sFullName + "." + pAnnotParent->sFullName;
|
||||
}
|
||||
}
|
||||
oParentRefObj.free();
|
||||
|
||||
oField.free();
|
||||
}
|
||||
bool CAnnots::ChangeFullNameAnnot(int nAnnot, const std::string& sPrefixForm)
|
||||
{
|
||||
if (nAnnot < 0 || nAnnot > m_arrAnnots.size())
|
||||
return false;
|
||||
|
||||
CAnnotWidget* pWidget = m_arrAnnots[nAnnot];
|
||||
if (pWidget->ChangeFullName(sPrefixForm))
|
||||
return true;
|
||||
unsigned int unRefNumParent = pWidget->GetRefNumParent();
|
||||
if (unRefNumParent)
|
||||
{
|
||||
std::vector<CAnnotParent*>::iterator it = std::find_if(m_arrParents.begin(), m_arrParents.end(), [unRefNumParent](CAnnotParent* pP) { return pP->unRefNum == unRefNumParent; });
|
||||
if (it != m_arrParents.end() && ChangeFullNameParent(std::distance(m_arrParents.begin(), it), sPrefixForm))
|
||||
{
|
||||
pWidget->AddFullName(sPrefixForm);
|
||||
// pWidget->ClearActions();
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
bool CAnnots::ChangeFullNameParent(int nParent, const std::string& sPrefixForm)
|
||||
{
|
||||
if (nParent < 0 || nParent > m_arrParents.size())
|
||||
return false;
|
||||
|
||||
CAnnotParent* pParent = m_arrParents[nParent];
|
||||
if (pParent->unFlags & (1 << 0))
|
||||
{
|
||||
pParent->sT += sPrefixForm;
|
||||
pParent->sFullName += sPrefixForm;
|
||||
// pParent->ClearActions();
|
||||
return true;
|
||||
}
|
||||
else if (pParent->unFlags & (1 << 4))
|
||||
{
|
||||
unsigned int unRefNumParent = pParent->unRefNumParent;
|
||||
std::vector<CAnnotParent*>::iterator it = std::find_if(m_arrParents.begin(), m_arrParents.end(), [unRefNumParent](CAnnotParent* pP) { return pP->unRefNum == unRefNumParent; });
|
||||
if (it != m_arrParents.end() && ChangeFullNameParent(std::distance(m_arrParents.begin(), it), sPrefixForm))
|
||||
{
|
||||
pParent->sFullName += sPrefixForm;
|
||||
// pParent->ClearActions();
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
void CAnnots::CAnnotParent::ClearActions()
|
||||
{
|
||||
unFlags &= ~(1 << 8);
|
||||
for (int i = 0; i < arrAction.size(); ++i)
|
||||
RELEASEOBJECT(arrAction[i]);
|
||||
arrAction.clear();
|
||||
}
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
// Markup
|
||||
|
||||
@ -219,12 +219,19 @@ public:
|
||||
|
||||
void SetFont(PDFDoc* pdfDoc, AcroFormField* pField, NSFonts::IFontManager* pFontManager, CPdfFontList *pFontList);
|
||||
void SetButtonFont(PDFDoc* pdfDoc, AcroFormField* pField, NSFonts::IFontManager* pFontManager, CPdfFontList *pFontList);
|
||||
unsigned int GetRefNumParent() { return m_unRefNumParent; }
|
||||
const std::string& GetFullName() { return m_sFullName; }
|
||||
void SetFullName(const std::string& sFullName) { m_sFullName = sFullName; }
|
||||
void AddFullName(const std::string& sPrefixForm) { m_sFullName += sPrefixForm; }
|
||||
bool ChangeFullName(const std::string& sPrefixForm);
|
||||
void ClearActions();
|
||||
virtual std::string GetType() = 0;
|
||||
virtual void ToWASM(NSWasm::CData& oRes) override;
|
||||
|
||||
protected:
|
||||
CAnnotWidget(PDFDoc* pdfDoc, AcroFormField* pField, int nStartRefID);
|
||||
|
||||
std::string FieldLookupString(AcroFormField* pField, const char* sName, int nByte);
|
||||
virtual void ToWASM(NSWasm::CData& oRes) override;
|
||||
|
||||
BYTE m_nType; // Тип - FT + флаги
|
||||
unsigned int m_unFieldFlag; // Флаг - Ff
|
||||
@ -245,6 +252,7 @@ private:
|
||||
std::string m_sDV; // Значение по-умолчанию - DV
|
||||
std::string m_sT; // Частичное имя поля - T
|
||||
std::string m_sFontKey; // Уникальный идентификатор шрифта
|
||||
std::string m_sFullName; // Полное имя поля
|
||||
std::string m_sFontName; // Имя шрифта - из DA
|
||||
std::string m_sOMetadata; // OO метаданные формы
|
||||
std::string m_sActualFontName; // Имя замененного шрифта
|
||||
@ -255,6 +263,7 @@ class CAnnotWidgetBtn final : public CAnnotWidget
|
||||
{
|
||||
public:
|
||||
CAnnotWidgetBtn(PDFDoc* pdfDoc, AcroFormField* pField, int nStartRefID);
|
||||
virtual std::string GetType() override { return "Btn"; }
|
||||
|
||||
void ToWASM(NSWasm::CData& oRes) override;
|
||||
private:
|
||||
@ -275,6 +284,7 @@ class CAnnotWidgetTx final : public CAnnotWidget
|
||||
{
|
||||
public:
|
||||
CAnnotWidgetTx(PDFDoc* pdfDoc, AcroFormField* pField, int nStartRefID);
|
||||
virtual std::string GetType() override { return "Tx"; }
|
||||
|
||||
void ToWASM(NSWasm::CData& oRes) override;
|
||||
private:
|
||||
@ -287,6 +297,7 @@ class CAnnotWidgetCh final : public CAnnotWidget
|
||||
{
|
||||
public:
|
||||
CAnnotWidgetCh(PDFDoc* pdfDoc, AcroFormField* pField, int nStartRefID);
|
||||
virtual std::string GetType() override { return "Ch"; }
|
||||
|
||||
void ToWASM(NSWasm::CData& oRes) override;
|
||||
private:
|
||||
@ -301,6 +312,7 @@ class CAnnotWidgetSig final : public CAnnotWidget
|
||||
{
|
||||
public:
|
||||
CAnnotWidgetSig(PDFDoc* pdfDoc, AcroFormField* pField, int nStartRefID);
|
||||
virtual std::string GetType() override { return "Sig"; }
|
||||
|
||||
void ToWASM(NSWasm::CData& oRes) override;
|
||||
};
|
||||
@ -595,6 +607,9 @@ public:
|
||||
~CAnnots();
|
||||
|
||||
void ToWASM(NSWasm::CData& oRes);
|
||||
bool ChangeFullNameAnnot(int nAnnot, const std::string& sPrefixForm);
|
||||
bool ChangeFullNameParent(int nParent, const std::string& sPrefixForm);
|
||||
const std::vector<CAnnotWidget*>& GetAnnots() { return m_arrAnnots; }
|
||||
|
||||
private:
|
||||
struct CAnnotParent final
|
||||
@ -605,6 +620,11 @@ private:
|
||||
unRefNum = 0;
|
||||
unRefNumParent = 0;
|
||||
}
|
||||
~CAnnotParent()
|
||||
{
|
||||
ClearActions();
|
||||
}
|
||||
void ClearActions();
|
||||
|
||||
void ToWASM(NSWasm::CData& oRes);
|
||||
|
||||
@ -619,13 +639,14 @@ private:
|
||||
std::string sT;
|
||||
std::string sV;
|
||||
std::string sDV;
|
||||
std::string sFullName;
|
||||
};
|
||||
|
||||
void getParents(PDFDoc* pdfDoc, Object* oFieldRef, int nStartRefID);
|
||||
|
||||
std::vector<int> m_arrCO; // Порядок вычислений - CO
|
||||
std::vector<CAnnotParent*> m_arrParents; // Родительские Fields
|
||||
std::vector<CAnnot*> m_arrAnnots;
|
||||
std::vector<CAnnotWidget*> m_arrAnnots;
|
||||
};
|
||||
|
||||
//------------------------------------------------------------------------
|
||||
|
||||
Reference in New Issue
Block a user