mirror of
https://github.com/ONLYOFFICE/core.git
synced 2026-04-07 13:55:33 +08:00
434 lines
16 KiB
C++
434 lines
16 KiB
C++
#pragma once
|
|
#include "..\..\..\..\Common\XmlUtils.h"
|
|
|
|
#define USE_ATL_CSTRING
|
|
#include "..\..\..\..\Common\StringUtils.h"
|
|
|
|
#include "../Common/OfficeFileErrorDescription.h"
|
|
|
|
class SimpleDocxFormat
|
|
{
|
|
private: CAtlArray<CString> m_aRelsWalked;
|
|
private: CAtlArray<CString> m_aImagesToExtract;
|
|
public: void ExtractImages( CString sSourcePath, CString sDestPath )
|
|
{
|
|
m_aRelsWalked.RemoveAll();
|
|
m_aImagesToExtract.RemoveAll();
|
|
|
|
NormalizePath( sSourcePath, true );
|
|
NormalizePath( sDestPath, true );
|
|
ExtractImagesFromRels( sSourcePath + _T("_rels/.rels"), sSourcePath, sDestPath );
|
|
}
|
|
private: void ExtractImagesFromRels( CString sSourceRels, CString sSourcePath, CString& sDestPath )
|
|
{
|
|
//íîðìàëèçóåì èìÿ
|
|
TCHAR buffer[1024];
|
|
GetFullPathName( sSourceRels, 1024, buffer, NULL );
|
|
sSourceRels = CString( buffer );
|
|
|
|
if( ::GetFileAttributes( sSourceRels ) == INVALID_FILE_ATTRIBUTES )
|
|
return;
|
|
|
|
//ïðîâåðÿåì íå îáðàáàòûâàëè ëè ðàíåå
|
|
for( int i = 0; i < m_aRelsWalked.GetCount(); i++ )
|
|
if( m_aRelsWalked[i] == sSourceRels )
|
|
return;
|
|
m_aRelsWalked.Add( sSourceRels );
|
|
|
|
XmlUtils::CXmlReader oXmlReader;
|
|
if( TRUE == oXmlReader.OpenFromFile( sSourceRels ) )
|
|
if( TRUE == oXmlReader.ReadRootNode( _T("Relationships") ) )
|
|
if( TRUE == oXmlReader.ReadNodeList( _T("Relationship") ) )
|
|
{
|
|
for( int i = 0; i < oXmlReader.GetLengthList(); i++ )
|
|
{
|
|
CString sTargetMode = oXmlReader.ReadNodeAttribute( i, _T("TargetMode") );
|
|
if( _T("External") != sTargetMode )
|
|
{
|
|
CString sType = oXmlReader.ReadNodeAttribute( i, _T("Type") );
|
|
CString sTargetRel = oXmlReader.ReadNodeAttribute( i, _T("Target") );
|
|
NormalizePath( sTargetRel, false );
|
|
CString sTargetFile = sSourcePath + sTargetRel;
|
|
|
|
TCHAR tDrive[256];
|
|
TCHAR tDir[256];
|
|
TCHAR tFilename[256];
|
|
TCHAR tExt[256];
|
|
_tsplitpath( sTargetFile, tDrive, tDir, tFilename, tExt );
|
|
CString sDrive = CString( tDrive );
|
|
CString sDir = CString( tDir );
|
|
CString sFilename = CString( tFilename );
|
|
CString sExt = CString( tExt );
|
|
|
|
if( _T("http://schemas.openxmlformats.org/officeDocument/2006/relationships/image") == sType )
|
|
{
|
|
//íîðìàëèçóåì èìÿ
|
|
GetFullPathName( sTargetFile, 1024, buffer, NULL );
|
|
sTargetFile = CString( buffer );
|
|
|
|
//ïðîâåðÿåì íå êîïèðîâàëè ëè ýòó êàðòèíêó
|
|
bool bDuplicate = false;
|
|
for( int j = 0; j < m_aImagesToExtract.GetCount(); j++ )
|
|
if( m_aImagesToExtract[j] == sTargetFile )
|
|
{
|
|
bDuplicate = true;
|
|
break;
|
|
}
|
|
if( false == bDuplicate )
|
|
{
|
|
m_aImagesToExtract.Add( sTargetFile );
|
|
|
|
CString sNewTarget = sDestPath + sFilename + sExt;
|
|
//åñëè òàêîé ôàéë óæå ñóùåñòâóåò - äîáàâëÿåì ñ÷åò÷èê
|
|
if( INVALID_FILE_ATTRIBUTES != ::GetFileAttributes( sNewTarget ) )
|
|
{
|
|
DWORD dwFileAtrr = !INVALID_FILE_ATTRIBUTES;
|
|
int nCounter = 1;
|
|
while( INVALID_FILE_ATTRIBUTES != dwFileAtrr )
|
|
{
|
|
CString sCounter;
|
|
sCounter.AppendFormat( _T("%d"), nCounter );
|
|
while( sCounter.GetLength() < 3 )
|
|
sCounter.Insert( 0, '0' );
|
|
sNewTarget = sDestPath + sFilename + _T("_") + sCounter + sExt;
|
|
nCounter++;
|
|
dwFileAtrr = ::GetFileAttributes( sNewTarget );
|
|
}
|
|
}
|
|
CopyFile( sTargetFile, sNewTarget, FALSE );
|
|
}
|
|
|
|
}
|
|
else//ïûòàåìñÿ ïðî÷èòàòü rels ýòîãî ôàéëà
|
|
ExtractImagesFromRels( sDrive + sDir + _T("_rels/") + sFilename + sExt + _T(".rels"), sDrive + sDir, sDestPath );
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
public: HRESULT AddObject( CString sPath, CString sOptions )
|
|
{
|
|
HRESULT hRes = S_FALSE;
|
|
NormalizePath( sPath, true );
|
|
|
|
bool bNeedFindRels = false; // íóæíî ëè èñêàòü document.xml.rels( Íåò ññûëîê íå íàäî èñêàòü rels)
|
|
CString sDocumentPath = _T("");//àáñîëþòíûé ïóòü ê document.xml
|
|
CString sDocumentRelsPath = _T("");//àáñîëþòíûé ïóòü ê document.xml.rels
|
|
int nStartRelsIndex = 1; //èíäåêñ ñ êîòîðîãî ìîæíî äåëàòü óíèêàëüíûå ññûëêè ôîðìàòà rIdN
|
|
CString sDocumentAddition; //äîáàâêà ê ôàéëó document.xml
|
|
CString sRelsAddition; //äîáàâêà ê ôàéëó document.xml.rels
|
|
|
|
bool bEmptyOptions = true; //åñëè íè÷åãî íå íàäî äîáàâëÿòü - true;
|
|
//ïåðâûé ðàç ïðîáèãàåì ÷òîáû óçíàòü åñòü ëè òàì ññûëêè (Íåò ññûëîê íå íàäî èñêàòü rels)
|
|
XmlUtils::CXmlReader oXmlOptionsReader;
|
|
if( TRUE == oXmlOptionsReader.OpenFromXmlString( sOptions ) )
|
|
if( TRUE == oXmlOptionsReader.ReadRootNode( _T("AddObjects") ) )
|
|
if( TRUE == oXmlOptionsReader.ReadNodeList( _T("Paragraph") ) )
|
|
{
|
|
bEmptyOptions = false;
|
|
for( int i = 0; i < oXmlOptionsReader.GetLengthList(); i++ )
|
|
{
|
|
bool bContinue = true;
|
|
XML::IXMLDOMNodePtr pXmlNode;
|
|
if( TRUE == oXmlOptionsReader.GetNode( i, pXmlNode ) )
|
|
{
|
|
XmlUtils::CXmlReader oXmlSubReader;
|
|
if( TRUE == oXmlSubReader.OpenFromXmlNode( pXmlNode ) )
|
|
if( TRUE == oXmlSubReader.ReadNodeList( _T("*") ) )
|
|
{
|
|
for( int j = 0; j < oXmlSubReader.GetLengthList(); j++ )
|
|
{
|
|
CString sNodeName = oXmlSubReader.ReadNodeName( j );
|
|
if( _T("Link") == sNodeName )
|
|
{
|
|
bNeedFindRels = true;
|
|
bContinue = false;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
if( false == bContinue )
|
|
break;
|
|
}
|
|
}
|
|
if( true == bEmptyOptions )
|
|
return S_OK;
|
|
|
|
//Èùåì äîêóìåíò ïî ãëàâíûì rels
|
|
XmlUtils::CXmlReader oXmlRelsReader;
|
|
if( ::GetFileAttributes( sPath + _T("_rels/.rels") ) != INVALID_FILE_ATTRIBUTES )
|
|
if( TRUE == oXmlRelsReader.OpenFromFile( sPath + _T("_rels/.rels") ) )
|
|
if( TRUE == oXmlRelsReader.ReadRootNode( _T("Relationships") ) )
|
|
if( TRUE == oXmlRelsReader.ReadNodeList( _T("Relationship") ) )
|
|
{
|
|
for( int i = 0; i < oXmlRelsReader.GetLengthList(); i++ )
|
|
{
|
|
CString sType = oXmlRelsReader.ReadNodeAttribute( i, _T("Type") );
|
|
if( _T("http://schemas.openxmlformats.org/officeDocument/2006/relationships/officeDocument") == sType )
|
|
{
|
|
CString sTargetRel = oXmlRelsReader.ReadNodeAttribute( i, _T("Target") );
|
|
NormalizePath( sTargetRel, false );
|
|
//ôîðìèðóåì àáñîëþòíûé ïóòü ê document.xml
|
|
sDocumentPath = sPath + sTargetRel;
|
|
if( true == bNeedFindRels )//íóæíî èñêàòü rels äîêóìåíòà
|
|
{
|
|
TCHAR tDrive[256];
|
|
TCHAR tDir[256];
|
|
TCHAR tFilename[256];
|
|
TCHAR tExtension[256];
|
|
_tsplitpath( sDocumentPath, tDrive, tDir, tFilename, tExtension );
|
|
|
|
sDocumentRelsPath = CString(tDrive) + CString(tDir) + _T("_rels/") + tFilename + tExtension + _T(".rels");
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
if( _T("") == sDocumentPath )
|
|
return AVS_ERROR_UNEXPECTED;
|
|
|
|
//ïðîáåãàåìñÿ ïî document.xml.rels ÷òîáû âû÷èñëèòü nStartRelsIndex
|
|
if( true == bNeedFindRels )
|
|
{
|
|
XmlUtils::CXmlReader oXmlReader;
|
|
if( ::GetFileAttributes( sDocumentRelsPath ) != INVALID_FILE_ATTRIBUTES )
|
|
if( TRUE == oXmlReader.OpenFromFile( sDocumentRelsPath ) )
|
|
if( TRUE == oXmlReader.ReadRootNode( _T("Relationships") ) )
|
|
if( TRUE == oXmlReader.ReadNodeList( _T("Relationship") ) )
|
|
{
|
|
for( int i = 0; i < oXmlReader.GetLengthList(); i++ )
|
|
{
|
|
CString sCurRID = oXmlReader.ReadNodeAttribute( i , _T("Id") );
|
|
if( 0 == sCurRID.Find( _T("rId") ) )
|
|
{
|
|
//èùåì ÷èñëî çà rId
|
|
int nStartIndex = 3;// strlen("rId");
|
|
CString sDigitAfter = _T("");
|
|
int nCurRIDLen = sCurRID.GetLength();
|
|
while( nStartIndex < nCurRIDLen && _istdigit( sCurRID[nStartIndex] ) )
|
|
{
|
|
sDigitAfter.AppendChar( sCurRID[nStartIndex] );
|
|
nStartIndex++;
|
|
}
|
|
if( _T("") != sDigitAfter )
|
|
{
|
|
int nDigitAfter = Strings::ToInteger( sDigitAfter );
|
|
if( nDigitAfter >= nStartRelsIndex )
|
|
nStartRelsIndex = nDigitAfter + 1;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
//÷èòàåì sOptions è ôîðìèðóåì äîáàâêè ê document.xml.rels è document.xml
|
|
XmlUtils::CXmlReader oXmlOptionsReader2;
|
|
if( TRUE == oXmlOptionsReader2.OpenFromXmlString( sOptions ) )
|
|
if( TRUE == oXmlOptionsReader2.ReadRootNode( _T("AddObjects") ) )
|
|
if( TRUE == oXmlOptionsReader2.ReadNodeList( _T("Paragraph") ) )
|
|
{
|
|
XML::IXMLDOMDocumentPtr pXmlDocument;
|
|
pXmlDocument.CreateInstance(CLSID_DOMDocument);
|
|
if( NULL != pXmlDocument )
|
|
{
|
|
for( int i = 0; i < oXmlOptionsReader2.GetLengthList(); i++ )
|
|
{
|
|
CString sCurParagraph = _T("");
|
|
bool bContinue = true;
|
|
XML::IXMLDOMNodePtr pXmlNode;
|
|
if( TRUE == oXmlOptionsReader2.GetNode( i, pXmlNode ) )
|
|
{
|
|
XmlUtils::CXmlReader oXmlSubReader;
|
|
if( TRUE == oXmlSubReader.OpenFromXmlNode( pXmlNode ) )
|
|
{
|
|
if( TRUE == oXmlSubReader.ReadNodeList( _T("*") ) )
|
|
{
|
|
int nNodeListLen = oXmlSubReader.GetLengthList();
|
|
if( 0 == i )
|
|
sCurParagraph.Append( _T("<w:p><w:pPr><w:spacing w:after=\"200\" w:before=\"200\" w:line=\"276\" w:lineRule=\"auto\"/></w:pPr>") );
|
|
else
|
|
sCurParagraph.Append( _T("<w:p><w:pPr><w:spacing w:after=\"200\" w:line=\"276\" w:lineRule=\"auto\"/></w:pPr>") );
|
|
if( nNodeListLen > 0 )
|
|
{
|
|
for( int j = 0; j < oXmlSubReader.GetLengthList(); j++ )
|
|
{
|
|
CString sNodeName = oXmlSubReader.ReadNodeName( j );
|
|
CString sValue = oXmlSubReader.ReadNodeAttribute( j, _T("value") );
|
|
CString sHref = oXmlSubReader.ReadNodeAttribute( j, _T("href") );
|
|
|
|
sHref = sHref.Trim();
|
|
//çàìåíÿåì ïðîáåëû íà %20
|
|
sHref.Replace( _T(" "), _T("%20") );
|
|
|
|
if( _T("Text") == sNodeName )
|
|
sCurParagraph.AppendFormat( _T("<w:r><w:rPr><w:sz w:val=\"24\"/></w:rPr><w:t xml:space=\"preserve\">%s</w:t></w:r>"), sValue );
|
|
else if( _T("Link") == sNodeName )
|
|
{
|
|
CString sCurRID = GenerateRID( nStartRelsIndex );
|
|
if( _T("") != sCurRID )
|
|
{
|
|
//äîáàâëÿåì <w:hyperlink> â document.xml
|
|
sCurParagraph.AppendFormat( _T("<w:hyperlink r:id=\"%s\"><w:r><w:rPr><w:sz w:val=\"24\"/><w:u w:val=\"single\"/><w:color w:val=\"0000ff\"/></w:rPr><w:t xml:space=\"preserve\" >%s</w:t></w:r></w:hyperlink>"), sCurRID, sValue );
|
|
sRelsAddition.AppendFormat( _T("<Relationship Id=\"%s\" Type=\"http://schemas.openxmlformats.org/officeDocument/2006/relationships/hyperlink\" Target=\"%s\" TargetMode=\"External\"/>"), sCurRID, sHref );
|
|
|
|
}
|
|
}
|
|
}
|
|
}
|
|
sCurParagraph.Append( _T("</w:p>") );
|
|
}
|
|
}
|
|
}
|
|
sDocumentAddition.Append( sCurParagraph );
|
|
}
|
|
}
|
|
}
|
|
//äîáàâëÿåì â document.xml.rels
|
|
bool bRelsSucceeded = false;
|
|
if( _T("") != sRelsAddition )
|
|
{
|
|
//îáîðà÷èâàåì äîïîëíèòåëüíî ÷òîáû ìîæíî áûëî ÷èòàòü êàê xml
|
|
sRelsAddition = _T("<root xmlns=\"http://schemas.openxmlformats.org/package/2006/relationships\">") + sRelsAddition +_T("</root>");
|
|
XML::IXMLDOMDocumentPtr m_pXmlDocument;
|
|
m_pXmlDocument.CreateInstance(CLSID_DOMDocument);
|
|
if( NULL != m_pXmlDocument )
|
|
{
|
|
VARIANT vtLoad;
|
|
vtLoad.vt = VT_BSTR;
|
|
vtLoad.bstrVal = sDocumentRelsPath.AllocSysString();
|
|
|
|
try{
|
|
if( VARIANT_TRUE == m_pXmlDocument->load( vtLoad ) )
|
|
{
|
|
XML::IXMLDOMNodePtr oRootNode = m_pXmlDocument->selectSingleNode( _T("Relationships") );
|
|
if( NULL != oRootNode )
|
|
{
|
|
XmlUtils::CXmlReader oRelationReader;
|
|
if( TRUE == oRelationReader.OpenFromXmlString( sRelsAddition ) )
|
|
if( TRUE == oRelationReader.ReadNodeList( _T("*") ) )
|
|
{
|
|
int nLengthList = oRelationReader.GetLengthList();
|
|
for( int i = 0; i < nLengthList ; i++ )
|
|
{
|
|
XML::IXMLDOMNodePtr oCurNode = NULL;
|
|
oRelationReader.GetNode( i, oCurNode );
|
|
if( NULL != oCurNode )
|
|
XML::IXMLDOMNodePtr pBuf = oRootNode->appendChild( oCurNode );
|
|
}
|
|
}
|
|
VARIANT vtSave;
|
|
vtSave.vt = VT_BSTR;
|
|
vtSave.bstrVal = sDocumentRelsPath.AllocSysString();
|
|
m_pXmlDocument->save( vtSave );
|
|
SysFreeString( vtSave.bstrVal );
|
|
|
|
bRelsSucceeded = true;
|
|
}
|
|
}
|
|
}
|
|
catch( ... )
|
|
{
|
|
}
|
|
SysFreeString( vtLoad.bstrVal );
|
|
}
|
|
}
|
|
//äîáàâëÿåì â document.xml
|
|
if( _T("") != sDocumentAddition )
|
|
{
|
|
//îáîðà÷èâàåì äîïîëíèòåëüíî ÷òîáû ìîæíî áûëî ÷èòàòü êàê xml
|
|
sDocumentAddition = _T("<root xmlns:w=\"http://schemas.openxmlformats.org/wordprocessingml/2006/main\" xmlns:r=\"http://schemas.openxmlformats.org/officeDocument/2006/relationships\">") + sDocumentAddition +_T("</root>");
|
|
|
|
if( false == bNeedFindRels || true == bRelsSucceeded )
|
|
{
|
|
XML::IXMLDOMDocumentPtr m_pXmlDocument;
|
|
m_pXmlDocument.CreateInstance(CLSID_DOMDocument);
|
|
if( NULL != m_pXmlDocument )
|
|
{
|
|
VARIANT vtLoad;
|
|
vtLoad.vt = VT_BSTR;
|
|
vtLoad.bstrVal = sDocumentPath.AllocSysString();
|
|
|
|
try{
|
|
if( VARIANT_TRUE == m_pXmlDocument->load( vtLoad ) )
|
|
{
|
|
XML::IXMLDOMNodePtr oRootNode = m_pXmlDocument->selectSingleNode( _T("w:document") );
|
|
if( NULL == oRootNode )
|
|
XML::IXMLDOMNodePtr oRootNode = m_pXmlDocument->selectSingleNode( _T("w:wordDocument") );
|
|
if( NULL != oRootNode )
|
|
{
|
|
XML::IXMLDOMNodePtr oBodyNode = oRootNode->selectSingleNode( _T("w:body") );
|
|
if( NULL != oBodyNode )
|
|
{
|
|
XML::IXMLDOMNodePtr oLastNode = NULL;
|
|
oBodyNode->get_lastChild( &oLastNode );
|
|
if( NULL != oLastNode )
|
|
{
|
|
VARIANT vtInsert;
|
|
vtInsert.vt = VT_UNKNOWN;
|
|
vtInsert.punkVal = oLastNode;
|
|
|
|
XmlUtils::CXmlReader oRelationReader;
|
|
if( TRUE == oRelationReader.OpenFromXmlString( sDocumentAddition ) )
|
|
if( TRUE == oRelationReader.ReadNodeList( _T("*") ) )
|
|
{
|
|
int nLengthList = oRelationReader.GetLengthList();
|
|
for( int i = 0; i < nLengthList ; i++ )
|
|
{
|
|
XML::IXMLDOMNodePtr oCurNode = NULL;
|
|
oRelationReader.GetNode( i, oCurNode );
|
|
if( NULL != oCurNode )
|
|
XML::IXMLDOMNodePtr pBuf = oBodyNode->insertBefore( oCurNode, vtInsert );
|
|
}
|
|
}
|
|
|
|
VARIANT vtSave;
|
|
vtSave.vt = VT_BSTR;
|
|
vtSave.bstrVal = sDocumentPath.AllocSysString();
|
|
m_pXmlDocument->save( vtSave );
|
|
SysFreeString( vtSave.bstrVal );
|
|
|
|
hRes = S_OK;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
catch( ... )
|
|
{
|
|
}
|
|
SysFreeString( vtLoad.bstrVal );
|
|
}
|
|
}
|
|
}
|
|
return hRes;
|
|
}
|
|
|
|
private: CString GenerateRID( int& nRID )
|
|
{
|
|
CString sResult;
|
|
sResult.AppendFormat( _T("rId%d"), nRID );
|
|
nRID++;
|
|
return sResult;
|
|
}
|
|
|
|
// bEnd == true (äåëàåò ÷òîáû â êîíöå áûë '/' èëè '\'), bEnd == false (äåëàåò ÷òîáû â íà÷àëå íå áûëî '/' èëè '\')
|
|
private: void NormalizePath( CString& sPath, bool bEnd = true )
|
|
{
|
|
int nPathLen = sPath.GetLength();
|
|
if( nPathLen > 0 ) // sPath äîëæåí çàêàí÷èâàòüñÿ '/' èëè '\'
|
|
{
|
|
if( true == bEnd )
|
|
{
|
|
TCHAR tcLastChar = sPath[ nPathLen - 1 ];
|
|
if( '\\' != tcLastChar && '/' != tcLastChar )
|
|
sPath.AppendChar( '/' );
|
|
}
|
|
else
|
|
{
|
|
TCHAR tcFirstChar = sPath[ 0 ];
|
|
if( '\\' == tcFirstChar || '/' == tcFirstChar )
|
|
sPath = sPath.Right( sPath.GetLength() - 1 );
|
|
}
|
|
}
|
|
}
|
|
}; |