// CAVSOfficeOfficeEpubFile.cpp : Implementation of CAVSOfficeOfficeEpubFile #include "stdafx.h" #include "COfficeEpubFile.h" #include "EpubContainer.h" #include "EventHandler.h" #include "StringUtils.h" #include "../Common/OfficeFileErrorDescription.h" #include "../Common/OfficeFileFormats.h" #include "../Common/OfficeDefines.h" #include "../AVSOfficeUniversalConverter/Utils.h" #include "../../AVSVideoStudio3/Common/MediaFormatDefine.h" #define ZLIB_WINAPI #define CODEPAGE_ISSUE_FIX #include "..\AVSOfficeUtils\ZLIB\zlib-1.2.3\contrib\minizip\zip.h" #define ONE_INCH 2.54 // CAVSOfficeOfficeEpubFile class FolderRemove { private: CString m_sFolder; public: FolderRemove(CString sFolder ) { m_sFolder = sFolder; } public: ~FolderRemove( ) { Utils::RemoveDirOrFile( m_sFolder ); } }; // bEnd == true (делает чтобы в конце был '/' или '\'), bEnd == false (делает чтобы в начале не было '/' или '\') 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 ); } } } void MergeRels( CString sDest, CString sSource, CString sDestDir, CString sSourceDir, CString& sSourceDoc ) { int nImageCounter = 1; int nStartRelsIndex = 0; //пробегаемся по document.xml.rels чтобы вычислить nStartRelsIndex XmlUtils::CXmlReader oXmlReader; if( ::GetFileAttributes( sDest ) != INVALID_FILE_ATTRIBUTES ) if( TRUE == oXmlReader.OpenFromFile( sDest ) ) 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; } } } } if( ::GetFileAttributes( sSource ) != INVALID_FILE_ATTRIBUTES ) if( TRUE == oXmlReader.OpenFromFile( sSource ) ) 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; } } } } nStartRelsIndex++; //читаем из rels только Image и Hyperlink CString sAdditionalRels = _T(""); if( ::GetFileAttributes( sSource ) != INVALID_FILE_ATTRIBUTES ) if( TRUE == oXmlReader.OpenFromFile( sSource ) ) if( TRUE == oXmlReader.ReadRootNode( _T("Relationships") ) ) if( TRUE == oXmlReader.ReadNodeList( _T("Relationship") ) ) { for( int i = 0; i < oXmlReader.GetLengthList(); i++ ) { CString sCurType = oXmlReader.ReadNodeAttribute( i , _T("Type") ); CString sCurTarget = oXmlReader.ReadNodeAttribute( i , _T("Target") ); CString sTargetMode = oXmlReader.ReadNodeAttribute( i , _T("TargetMode") ); CString sCurRID = oXmlReader.ReadNodeAttribute( i , _T("Id") ); bool bImage = (-1 != sCurType.Find( _T("http://schemas.openxmlformats.org/officeDocument/2006/relationships/image") )); bool bHyp = (-1 != sCurType.Find( _T("http://schemas.openxmlformats.org/officeDocument/2006/relationships/hyperlink") )); if( bImage || bHyp ) { CString sNewName = _T(""); if( bImage && "External" != sTargetMode ) { //копируем картинку NormalizePath( sCurTarget, false ); if( ::GetFileAttributes( sDestDir + _T("media") ) == INVALID_FILE_ATTRIBUTES ) CreateDirectory( sDestDir + _T("media"), NULL ); CString sDestLocation = sDestDir + sCurTarget; //создаем новое имя TCHAR tDrive[256]; TCHAR tDir[256]; TCHAR tFilename[256]; TCHAR tExt[256]; _tsplitpath( sDestLocation, tDrive, tDir, tFilename, tExt ); CString sNewFilename; while ( ::GetFileAttributes( sDestLocation ) != INVALID_FILE_ATTRIBUTES) { sNewFilename = _T("");sNewFilename.AppendFormat( CString(_T("image%d")) + tExt, nImageCounter++ ); sDestLocation = CString(tDrive) + CString(tDir) + sNewFilename; } sNewName = sCurTarget; if( _T("") != sNewFilename ) sNewName.Replace( CString(tFilename) + CString(tExt), sNewFilename ); //Копируем CopyFile( sSourceDir + sCurTarget, sDestLocation, FALSE ); } CString sCurXml = oXmlReader.ReadNodeXml( i ); CString sNewRID;sNewRID.AppendFormat( _T("\"rId%d\""), nStartRelsIndex++ ); sCurXml.Replace( CString(_T("\"")) + sCurRID + _T("\""), sNewRID ); sSourceDoc.Replace( CString( _T("\"")) + sCurRID + _T("\""), sNewRID ); if( _T("") != sNewName ) sCurXml.Replace( sCurTarget, sNewName ); sAdditionalRels += sCurXml; } } } //добавляем if( _T("") != sAdditionalRels ) { CString sAddition = _T("") + sAdditionalRels +_T(""); XML::IXMLDOMDocumentPtr m_pXmlDocument; m_pXmlDocument.CreateInstance(CLSID_DOMDocument); if( NULL != m_pXmlDocument ) { VARIANT vtLoad; vtLoad.vt = VT_BSTR; vtLoad.bstrVal = sDest.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( sAddition ) ) 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 = sDest.AllocSysString(); m_pXmlDocument->save( vtSave ); SysFreeString( vtSave.bstrVal ); } } } catch( ... ) { } SysFreeString( vtLoad.bstrVal ); } } } //мерж основан на тех docx что приходят из html: //fontTable.xml, styles.xml стандартные их мержить не надо. //картинки заданы тегами //numbering.xml надо мержить void MergeDocx( CString sDest, CString sSource ) { NormalizePath( sSource, true ); NormalizePath( sDest, true ); //находим document.xml из [Content_Types].xml CString sDestDocument; CString sSourceDocument; XmlUtils::CXmlReader oXmlReader; if( TRUE == oXmlReader.OpenFromFile( sDest + _T("[Content_Types].xml") ) ) if( TRUE == oXmlReader.ReadRootNode( _T("Types") ) ) if( TRUE == oXmlReader.ReadNodeList( _T("Override") ) ) for( int i = 0; i < oXmlReader.GetLengthList(); i++ ) { CString sXml = oXmlReader.ReadNodeAttribute( i, _T("ContentType") ); if( _T("application/vnd.openxmlformats-officedocument.wordprocessingml.document.main+xml") == sXml ) { sDestDocument = oXmlReader.ReadNodeAttribute( i, _T("PartName") ); NormalizePath( sDestDocument, false ); } } if( TRUE == oXmlReader.OpenFromFile( sSource + _T("[Content_Types].xml") ) ) if( TRUE == oXmlReader.ReadRootNode( _T("Types") ) ) if( TRUE == oXmlReader.ReadNodeList( _T("Override") ) ) for( int i = 0; i < oXmlReader.GetLengthList(); i++ ) { if( _T("application/vnd.openxmlformats-officedocument.wordprocessingml.document.main+xml") == oXmlReader.ReadNodeAttribute( i, _T("ContentType") ) ) { sSourceDocument = oXmlReader.ReadNodeAttribute( i, _T("PartName") ); NormalizePath( sSourceDocument, false ); } } if( _T("") == sDestDocument || _T("") == sSourceDocument ) return; sDestDocument = sDest + sDestDocument; sSourceDocument = sSource + sSourceDocument; //соединяем документы //читаем документ для вставки в строку CString sSourceContent; if( TRUE == oXmlReader.OpenFromFile( sSourceDocument ) ) if( TRUE == oXmlReader.ReadRootNode( _T("w:document") ) ) sSourceContent = oXmlReader.ReadNodeXml(); if( _T("") == sSourceContent ) return; //ищем пусти к rels TCHAR tDrive[256]; TCHAR tDir[256]; TCHAR tFilename[256]; TCHAR tExt[256]; _tsplitpath( sDestDocument, tDrive, tDir, tFilename, tExt ); CString sDestDir = CString(tDrive) + CString(tDir); CString sDestRels = sDestDir + _T("_rels\\") +CString(tFilename) + CString(tExt) + _T(".rels"); _tsplitpath( sSourceDocument, tDrive, tDir, tFilename, tExt ); CString sSourceDir = CString(tDrive) + CString(tDir); CString sSourceRels = sSourceDir + _T("_rels\\") +CString(tFilename) + CString(tExt) + _T(".rels"); //мержим rels MergeRels( sDestRels, sSourceRels, sDestDir, sSourceDir, sSourceContent ); std::wstring sStdSource = sSourceContent; //убираем numbering, потому что пока не мержим numbering.xml boost::wregex xRegEx(_T("].*?") ); sStdSource = boost::regex_replace( sStdSource, xRegEx, _T("") ); XML::IXMLDOMDocumentPtr pXmlDocument; pXmlDocument.CreateInstance(CLSID_DOMDocument); if( NULL != pXmlDocument ) { VARIANT vtLoad; vtLoad.vt = VT_BSTR; vtLoad.bstrVal = sDestDocument.AllocSysString(); try{ if( VARIANT_TRUE == pXmlDocument->load( vtLoad ) ) { XML::IXMLDOMNodePtr oBodyNode = pXmlDocument->selectSingleNode( _T("w:document/w:body") ); if( NULL != oBodyNode ) { //Ищем w:sectPr, чтобы вставлять дополнительные ноды перед ним CComVariant varSectNode(&(*oBodyNode->GetlastChild())); //если документ не пустой вставляем разрывы страниц if( oBodyNode->GetchildNodes()->Getlength() > 1 ) { CString sPageBreak = L""; XmlUtils::CXmlReader oPageBreakReader; if( TRUE == oPageBreakReader.OpenFromXmlString( sPageBreak ) ) if( TRUE == oPageBreakReader.ReadNode( _T("w:p") ) ) { XML::IXMLDOMNodePtr oCurNode = NULL; oPageBreakReader.GetNode( oCurNode ); if( NULL != oCurNode ) oBodyNode->insertBefore( oCurNode, varSectNode ); } } XmlUtils::CXmlReader oRelationReader; if( TRUE == oRelationReader.OpenFromXmlString( sStdSource.c_str() ) ) if( TRUE == oRelationReader.ReadRootNode( _T("w:document") ) ) if( TRUE == oRelationReader.ReadNode( _T("w:body") ) ) if( TRUE == oRelationReader.ReadNodeList( _T("*") ) ) { int nLengthList = oRelationReader.GetLengthList(); //nLengthList - 1, чтобы не вставлять w:sectPr for( int i = 0; i < nLengthList - 1 ; i++ ) { XML::IXMLDOMNodePtr oCurNode = NULL; oRelationReader.GetNode( i, oCurNode ); if( NULL != oCurNode ) oBodyNode->insertBefore( oCurNode, varSectNode ); } } varSectNode.Clear(); VARIANT vtSave; vtSave.vt = VT_BSTR; vtSave.bstrVal = sDestDocument.AllocSysString(); pXmlDocument->save( vtSave ); SysFreeString( vtSave.bstrVal ); } } } catch( ... ) { } ::SysFreeString( vtLoad.bstrVal ); } } int _WriteFileToZip( CString sSource, CString sSourceRel, zipFile zf, int nCompressionLevel ) { int err = 0; if( Z_NO_COMPRESSION == nCompressionLevel ) err = zipOpenNewFileInZip( zf, Utils::UnicodeToCodepage (sSourceRel, CP_OEMCP), NULL, NULL, 0, NULL, 0, NULL, NULL, nCompressionLevel ); else err = zipOpenNewFileInZip( zf, Utils::UnicodeToCodepage (sSourceRel, CP_OEMCP), NULL, NULL, 0, NULL, 0, NULL, Z_DEFLATED, nCompressionLevel ); HANDLE hSourceFile = ::CreateFile( sSource, GENERIC_READ, 0, 0, OPEN_EXISTING,FILE_ATTRIBUTE_NORMAL, 0); if( INVALID_HANDLE_VALUE == hSourceFile ) err = -1; DWORD dwBytesRead = 1; BYTE bBuffer[ BUFSIZE ]; while( -1 != err && 0 != dwBytesRead ) { ReadFile( hSourceFile, bBuffer, BUFSIZE, &dwBytesRead, NULL ); if( dwBytesRead > 0 ) err = zipWriteInFileInZip( zf, bBuffer, dwBytesRead ); } RELEASEHANDLE( hSourceFile ); if( 0 == err ) err = zipCloseFileInZip( zf ); else zipCloseFileInZip( zf ); return err; } int _CompressEpubDir( CString sSource, CString sPathRel, zipFile zf, int nCompresstionLevel ) { DWORD dwFileAttrib = ::GetFileAttributes( sSource ); if( dwFileAttrib != INVALID_FILE_ATTRIBUTES ) { DWORD dwResult = 0; if( 0 != (FILE_ATTRIBUTE_DIRECTORY & dwFileAttrib) ) { HANDLE Handle; WIN32_FIND_DATA FindData; DWORDLONG Result = 0; Handle = FindFirstFile( ( sSource + _T("\\*.*") ), &FindData ); if ( Handle == INVALID_HANDLE_VALUE ) return 0; do { int nRes = 0; if( ( CString( FindData.cFileName ) != _T("mimetype") ) && ( CString( FindData.cFileName ) != _T(".") ) && ( CString( FindData.cFileName ) != _T("..") ) ) { if( FindData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY ) { if( _T("fonts") == CString( FindData.cFileName ) ) dwResult += _CompressEpubDir( sSource + _T("\\") + FindData.cFileName, sPathRel + FindData.cFileName + _T("/"), zf, Z_NO_COMPRESSION );//фонты и так уже запакованы else dwResult += _CompressEpubDir( sSource + _T("\\") + FindData.cFileName, sPathRel + FindData.cFileName + _T("/"), zf, nCompresstionLevel ); } else dwResult += _CompressEpubDir( sSource + _T("\\") + FindData.cFileName, sPathRel, zf, nCompresstionLevel ); } } while( FindNextFile( Handle, &FindData ) != 0 ); FindClose( Handle ); } else { TCHAR tExt[256]; TCHAR tFilename[256]; _wsplitpath( sSource, NULL, NULL, tFilename, tExt ); CString sExt = tExt; CString sFilename = tFilename; _WriteFileToZip( sSource, sPathRel + sFilename + sExt, zf, nCompresstionLevel ); } return dwResult; }// 0 - все закончилось хорошо return 0; } int CompressEpubDir( CString sSource, CString sDestination ) { int err = 0; zipFile zf = zipOpen( sDestination, APPEND_STATUS_CREATE ); if( NULL == zf ) return 1; //первый в архиве всегда должен быть mimetype CString sMimeFile = sSource + _T("\\mimetype"); if( ::GetFileAttributes( sMimeFile ) != INVALID_FILE_ATTRIBUTES ) err = _WriteFileToZip( sMimeFile, _T("mimetype"), zf, Z_NO_COMPRESSION ); if( err == 0 ) { //записываем все остальное err = _CompressEpubDir( sSource, _T(""), zf, Z_BEST_COMPRESSION ); } if( 0 == err ) err = zipClose( zf, NULL ); else zipClose( zf, NULL ); return err; } HRESULT COfficeEpubFile::OnProgressHtml( LONG nID, LONG nPercent ) {//прогресс от 0 до c_shMaxProgressPercent long nNewProgress = ( c_nMaxProgressPercent * m_nTasksComplete + nPercent ) / m_nTasksAll; OnProgress( nID, nNewProgress ); return S_OK; } HRESULT COfficeEpubFile::OnProgressHtmlEx( LONG nID, LONG nPercent, SHORT* Cancel ) { long nNewProgress = ( c_nMaxProgressPercent * m_nTasksComplete + nPercent ) / m_nTasksAll; OnProgressEx( nID, nNewProgress, Cancel ); if( c_shProgressCancel == *Cancel ) m_bCancel = true; return S_OK; } STDMETHODIMP COfficeEpubFile::LoadFromFile( BSTR bstrSrcFileName, BSTR bstrDstPath, BSTR bstrXMLOptions) { //проверяем входной файл на epub long nError; if( FALSE == IsEpubFile( CString(bstrSrcFileName), nError ) ) { if( NOERROR == nError ) return AVS_ERROR_FILEFORMAT; else return nError; } HRESULT hRes = S_OK; //создаем временную папку для распаковки архива CString sTempFolder = Utils::CreateTempDir( CString( bstrDstPath ) ); FolderRemove oFolderRemove( sTempFolder ); //распаковываем архив в папку OfficeUtils::IOfficeUtilsPtr piOfficeUtils; piOfficeUtils.CreateInstance( __uuidof(OfficeUtils::COfficeUtils) ); if( NULL == piOfficeUtils ) return AVS_ERROR_UNEXPECTED; BSTR bstrTempFolder = sTempFolder.AllocSysString(); hRes = piOfficeUtils->ExtractToDirectory( bstrSrcFileName, bstrTempFolder, NULL, 0 ); SysFreeString( bstrTempFolder ); if( FAILED( hRes ) ) return AVS_ERROR_UNEXPECTED; //load EpubContainer oEpubContainer; if( 0 != oEpubContainer.Load( sTempFolder ) ) return AVS_ERROR_UNEXPECTED; m_sMetadata = oEpubContainer.m_oEpubPackage.m_sMetadata; OfficeHtmlFile::IHtmlFilePtr piHtmlFile; piHtmlFile.CreateInstance( __uuidof(OfficeHtmlFile::CHtmlFile) ); if( NULL == piHtmlFile ) return AVS_ERROR_UNEXPECTED; //подцепляемся к событиям COfficeHtmlEvent< OfficeHtmlFile::_IAVSOfficeFileTemplateEvents, COfficeEpubFile >* oHtmlEvents = new COfficeHtmlEvent< OfficeHtmlFile::_IAVSOfficeFileTemplateEvents, COfficeEpubFile >( this ); oHtmlEvents->AddRef(); oHtmlEvents->Advise( piHtmlFile ); COfficeHtmlEvent< OfficeHtmlFile::_IAVSOfficeFileTemplateEvents2, COfficeEpubFile >* oHtmlEvents2 = new COfficeHtmlEvent< OfficeHtmlFile::_IAVSOfficeFileTemplateEvents2, COfficeEpubFile >( this ); oHtmlEvents2->AddRef(); oHtmlEvents2->Advise( piHtmlFile ); //заначения для прогресса m_nTasksAll = oEpubContainer.m_oEpubPackage.GetCount(); m_nTasksComplete = 0; if( oEpubContainer.m_oEpubPackage.GetCount() > 0 ) {//конвертируем Html в docx и соединяем docx int nHtmlsCount = oEpubContainer.m_oEpubPackage.GetCount(); CString sDstPath = CString( bstrDstPath ); CString sTempHtml = Utils::CreateTempDir( bstrDstPath ); FolderRemove oTempFolderRemove(sTempHtml); bool bFirst = true; for( int i = 0; i < nHtmlsCount; i++ ) { CString sCurHtml = oEpubContainer.m_oEpubPackage.GetItem( i ); BSTR bstrCurHtml = sCurHtml.AllocSysString(); BSTR bstrDst; if( true == bFirst ) bstrDst = sDstPath.AllocSysString(); else bstrDst = sTempHtml.AllocSysString(); CString sOptions; sOptions.AppendFormat( _T("%d%d"), AVS_OFFICESTUDIO_FILE_DOCUMENT_HTML, CP_UTF8 ); BSTR bstrOptions = sOptions.AllocSysString(); hRes = piHtmlFile->LoadFromFile( bstrCurHtml, bstrDst, bstrOptions ); SysFreeString( bstrOptions ); SysFreeString( bstrDst ); SysFreeString( bstrCurHtml ); if( true == m_bCancel ) break; m_nTasksComplete++; if( S_OK != hRes ) continue; if( true == bFirst ) bFirst = false; else { MergeDocx( sDstPath, sTempHtml ); Utils::RemoveDirOrFile( sTempHtml ); CreateDirectory( sTempHtml, NULL ); } } } //отцепляемся от событий oHtmlEvents->UnAdvise( piHtmlFile ); RELEASEINTERFACE( oHtmlEvents ); oHtmlEvents2->UnAdvise( piHtmlFile ); RELEASEINTERFACE( oHtmlEvents2 ); return hRes; } STDMETHODIMP COfficeEpubFile::SaveToFile( BSTR bstrDstFileName, BSTR bstrSrcPath, BSTR bstrXMLOptions) { HRESULT hRes = S_OK; //создаем временную папку для формирования структуры epub CString sTempFolder = Utils::CreateTempDir( CString(bstrSrcPath) ); FolderRemove oFolderRemove( sTempFolder ); //создаем epub контейнер EpubContainer oEpubContainer; oEpubContainer.m_oEpubPackage.m_bEmbedFont = true; oEpubContainer.Create( sTempFolder ); //Смотрим опции bool bEpubFromHtml = false; bool bSaveCover = true; bool bOnePageHtml = true; bool bLoadInCommandRenderer = false; CString sSourceHtmlPath = _T(""); XmlUtils::CXmlReader oXmlOptionsReader; if( TRUE == oXmlOptionsReader.OpenFromXmlString( bstrXMLOptions ) ) { XML::IXMLDOMNodePtr pXmlSubNode; if( TRUE ==oXmlOptionsReader.GetNode( pXmlSubNode ) ) { XmlUtils::CXmlReader oXmlSubReader; if( TRUE == oXmlSubReader.OpenFromXmlNode( pXmlSubNode ) ) if( TRUE == oXmlSubReader.ReadNode( _T("LoadInCommandRenderer") ) ) { bLoadInCommandRenderer = true; } } if( TRUE == oXmlOptionsReader.ReadNode( _T("EpubOptions") ) ) { CString sXml = oXmlOptionsReader.ReadNodeXml(); CString sFontEncryption = oXmlOptionsReader.ReadNodeValue( _T("FontEncryption") ); if( _T("") != sFontEncryption ) oEpubContainer.m_oEpubPackage.m_nFontEncryption = (FontEncription)Strings::ToInteger( sFontEncryption ); CString sFontEmbed = oXmlOptionsReader.ReadNodeValue( _T("FontEmbed") );sFontEmbed.MakeLower(); if( _T("") != sFontEmbed ) { if( _T("false") != sFontEmbed && _T("0") != sFontEmbed ) oEpubContainer.m_oEpubPackage.m_bEmbedFont = false; else oEpubContainer.m_oEpubPackage.m_bEmbedFont = true; } CString sSaveCover = oXmlOptionsReader.ReadNodeValue( _T("SaveCover") );sSaveCover.MakeLower(); if( _T("") != sSaveCover ) { if( _T("false") != sSaveCover && _T("0") != sSaveCover ) bSaveCover = false; else bSaveCover = true; } XML::IXMLDOMNodePtr pXmlSubNode; if( TRUE ==oXmlOptionsReader.GetNode( pXmlSubNode ) ) { XmlUtils::CXmlReader oXmlSubReader; if( TRUE == oXmlSubReader.OpenFromXmlNode( pXmlSubNode ) ) if( TRUE == oXmlSubReader.ReadNode( _T("OnePageHtml") ) ) { CString sValue = oXmlSubReader.ReadNodeAttribute( _T("val") ); sValue.MakeLower(); if( _T("true") == sValue ) bOnePageHtml = true; else if( _T("false") == sValue ) bOnePageHtml = false; } } if( TRUE == oXmlOptionsReader.ReadNode( _T("NoStandartSave") ) ) sSourceHtmlPath = oXmlOptionsReader.ReadNodeAttribute( _T("destinationpath") ); } } CString sNewHtml = bstrSrcPath; //создаем IHtmlFile OfficeHtmlFile::IHtmlFilePtr piHtmlFile; piHtmlFile.CreateInstance( __uuidof(OfficeHtmlFile::CHtmlFile) ); if( NULL == piHtmlFile ) return AVS_ERROR_UNEXPECTED; //подцепляемся к событиям COfficeHtmlEvent< OfficeHtmlFile::_IAVSOfficeFileTemplateEvents, COfficeEpubFile >* oHtmlEvents = new COfficeHtmlEvent< OfficeHtmlFile::_IAVSOfficeFileTemplateEvents, COfficeEpubFile >( this ); oHtmlEvents->AddRef(); oHtmlEvents->Advise( piHtmlFile ); COfficeHtmlEvent< OfficeHtmlFile::_IAVSOfficeFileTemplateEvents2, COfficeEpubFile >* oHtmlEvents2 = new COfficeHtmlEvent< OfficeHtmlFile::_IAVSOfficeFileTemplateEvents2, COfficeEpubFile >( this ); oHtmlEvents2->AddRef(); oHtmlEvents2->Advise( piHtmlFile ); //заначения для прогресса m_nTasksAll = 2; m_nTasksComplete = 0; if( false == bEpubFromHtml ) { if( true == bLoadInCommandRenderer && true == bOnePageHtml ) { m_nTasksAll = 1; m_nTasksComplete = 0; long nPageCount = 0; m_piCommandsRenderer->get_PageCount( &nPageCount ); DocxRenderer::IAVSDocxRendererPtr piDocxRenderer; piDocxRenderer.CreateInstance( __uuidof( DocxRenderer::CAVSDocxRenderer ) ); if( NULL != piDocxRenderer ) { for( int i = 0; i < nPageCount; i++ ) { CString sCurTempDir = Utils::CreateTempDir( bstrSrcPath ); CString sDocxRendererOptions = _T("") ); //выставляем опции BSTR bstrDocxRendererOptions = sDocxRendererOptions.AllocSysString(); piDocxRenderer->Initialize( bstrDocxRendererOptions ); SysFreeString( bstrDocxRendererOptions ); piDocxRenderer->NewPage(); hRes = m_piCommandsRenderer->DrawPage( i, piDocxRenderer ); if( FAILED( hRes ) ) m_bCancel = true; if( false == m_bCancel ) { hRes = piDocxRenderer->Save(); if( FAILED( hRes ) ) m_bCancel = true; if( false == m_bCancel ) { OfficeHtmlFile::IHtmlFilePtr piCurHtmlFile; piCurHtmlFile.CreateInstance( __uuidof(OfficeHtmlFile::CHtmlFile) ); //конвертируем docx в html sNewHtml = oEpubContainer.m_oEpubPackage.m_sPackegePath; sNewHtml.AppendFormat( _T("Chapter%d.html"), oEpubContainer.m_oEpubPackage.GetCount() + 1 ); BSTR bstrDestHtml = sNewHtml.AllocSysString(); CString sOptions; sOptions.AppendFormat( _T("%d"), AVS_OFFICESTUDIO_FILE_DOCUMENT_HTML ); BSTR bstrDocxDir = sCurTempDir.AllocSysString(); BSTR bstrOptions = sOptions.AllocSysString(); hRes = piCurHtmlFile->SaveToFile( bstrDestHtml, bstrDocxDir, bstrOptions ); SysFreeString( bstrDocxDir ); SysFreeString( bstrOptions ); SysFreeString( bstrDestHtml ); if( FAILED( hRes ) ) m_bCancel = true; //добавляем html oEpubContainer.m_oEpubPackage.AddHtml( sNewHtml ); } } SHORT shCancel = c_shProgressContinue; OnProgressHtmlEx( 0, long( c_nMaxProgressPercent * ( 1.0 * ( i + 1 ) / nPageCount )), &shCancel ); if( true == m_bCancel ) break; Utils::RemoveDirOrFile( sCurTempDir ); } } } else { if( true == bLoadInCommandRenderer ) { DocxRenderer::IAVSDocxRendererPtr piDocxRenderer; piDocxRenderer.CreateInstance( __uuidof( DocxRenderer::CAVSDocxRenderer ) ); if( NULL != piDocxRenderer ) { CString sDocxRendererOptions = _T("") ); //выставляем опции BSTR bstrDocxRendererOptions = sDocxRendererOptions.AllocSysString(); piDocxRenderer->Initialize( bstrDocxRendererOptions ); SysFreeString( bstrDocxRendererOptions ); HRESULT hRes = S_OK; long nPageCount = 0; m_piCommandsRenderer->get_PageCount( &nPageCount ); for( int i = 0; i < nPageCount; i++ ) { piDocxRenderer->NewPage(); hRes = m_piCommandsRenderer->DrawPage( i, piDocxRenderer ); if( FAILED( hRes ) ) m_bCancel = true; SHORT shCancel = c_shProgressContinue; OnProgressHtmlEx( 0, long( c_nMaxProgressPercent * ( 1.0 * ( i + 1 ) / nPageCount )), &shCancel ); if( c_shProgressContinue != shCancel ) m_bCancel = true; if( true == m_bCancel ) break; } if( false == m_bCancel ) { hRes = piDocxRenderer->Save(); if( FAILED( hRes ) ) m_bCancel = true; } } } m_nTasksComplete = 1; if( false == m_bCancel ) { //конвертируем docx в html sNewHtml = oEpubContainer.m_oEpubPackage.m_sPackegePath; sNewHtml.AppendFormat( _T("Chapter%d.html"), oEpubContainer.m_oEpubPackage.GetCount() + 1 ); BSTR bstrDestHtml = sNewHtml.AllocSysString(); CString sOptions; sOptions.AppendFormat( _T("%d"), AVS_OFFICESTUDIO_FILE_DOCUMENT_HTML ); BSTR bstrOptions = sOptions.AllocSysString(); hRes = piHtmlFile->SaveToFile( bstrDestHtml, bstrSrcPath, bstrOptions ); SysFreeString( bstrOptions ); SysFreeString( bstrDestHtml ); //добавляем html oEpubContainer.m_oEpubPackage.AddHtml( sNewHtml ); } } } else { //получаем имя файла. TCHAR tExt[256]; TCHAR tFilename[256]; TCHAR tFolder[256]; TCHAR tDrive[256]; _tsplitpath( CString(bstrSrcPath), tDrive, tFolder, tFilename, tExt ); CString sExt = CString(tExt); CString sFilename = CString(tFilename); CString sFolder = CString(tFolder); CString sDrive = CString(tDrive); //конвертируем html в xhtml BSTR bstrSourceHtml = sSourceHtmlPath.AllocSysString(); sNewHtml = oEpubContainer.m_oEpubPackage.m_sPackegePath; sNewHtml.AppendFormat( _T("Chapter%d.html"), oEpubContainer.m_oEpubPackage.GetCount() + 1 ); BSTR bstrDestHtml = sNewHtml.AllocSysString(); CString sOptions;sOptions.AppendFormat( _T("%d"), AVS_OFFICESTUDIO_FILE_DOCUMENT_HTML, AVS_OFFICESTUDIO_FILE_DOCUMENT_HTML, sNewHtml ); BSTR bstrOptions = sOptions.AllocSysString(); hRes = piHtmlFile->LoadFromFile( bstrSourceHtml, bstrDestHtml, bstrOptions ); SysFreeString( bstrOptions ); SysFreeString( bstrDestHtml ); SysFreeString( bstrSourceHtml ); //копируем сопутствующую папку CString sSourceHtmlDir = sDrive + sFolder + sFilename + _T("_files/"); CString sDestHtmlDir ; sDestHtmlDir.AppendFormat( oEpubContainer.m_oEpubPackage.m_sPackegePath + _T("Chapter%d_files"), oEpubContainer.m_oEpubPackage.GetCount() + 1 ); Utils::CopyDirOrFile( sSourceHtmlDir, sDestHtmlDir ); } if( FAILED( hRes ) ) return hRes; //отцепляемся от событий oHtmlEvents->UnAdvise( piHtmlFile ); RELEASEINTERFACE( oHtmlEvents ); oHtmlEvents2->UnAdvise( piHtmlFile ); RELEASEINTERFACE( oHtmlEvents2 ); if( true == m_bCancel ) return S_OK; //Title по имени TCHAR tFilename[256]; _tsplitpath( CString(bstrDstFileName), NULL, NULL, tFilename, NULL ); CString sFilename = CString(tFilename); oEpubContainer.m_oEpubPackage.m_sTitle = sFilename; oEpubContainer.m_oEpubPackage.m_sAuthor = _T("Unknown"); //добавляем титульник if( true == bSaveCover && true == SaveCover( oEpubContainer.m_oEpubPackage.m_sPackegePath + _T("cover.jpeg"), oEpubContainer.m_oEpubPackage.m_nCoverWidth, oEpubContainer.m_oEpubPackage.m_nCoverHeight ) ) oEpubContainer.m_oEpubPackage.m_sCover = _T("cover.jpeg"); //сохраняем epub oEpubContainer.Save( sTempFolder ); //запаковываем epub файл CompressEpubDir( sTempFolder, bstrDstFileName ); return hRes; } STDMETHODIMP COfficeEpubFile::SetAdditionalParam( BSTR ParamName, VARIANT ParamValue) { return S_OK; } STDMETHODIMP COfficeEpubFile::GetAdditionalParam( BSTR ParamName, VARIANT* ParamValue) { CComBSTR ParamStr = ParamName; if (ParamStr == L"Metadata") { (*ParamValue).vt = VT_BSTR; (*ParamValue).bstrVal = m_sMetadata.AllocSysString(); } return S_OK; } STDMETHODIMP COfficeEpubFile::get_CommandRenderer(IUnknown** ppunkRend) { if(NULL == ppunkRend) return S_FALSE; (*ppunkRend) = NULL; if(NULL == m_piCommandsRenderer) return S_OK; return m_piCommandsRenderer->QueryInterface(__uuidof(AVSGraphics::IAVSDocumentRenderer), (void**)&ppunkRend); } STDMETHODIMP COfficeEpubFile::put_CommandRenderer(IUnknown* ppunkRend) { RELEASEINTERFACE(m_piCommandsRenderer); if(NULL != ppunkRend) ppunkRend->QueryInterface(__uuidof( AVSGraphics::IAVSDocumentRenderer), (void**)&m_piCommandsRenderer); return S_OK; } BOOL COfficeEpubFile::IsEpubFile(CString sFilename, long& nError ) { nError = NOERROR; BOOL bResult = FALSE; OfficeUtils::IOfficeUtils2Ptr piOfficeUtils; piOfficeUtils.CreateInstance( __uuidof(OfficeUtils::COfficeUtils) ); if( NULL == piOfficeUtils ) { nError = AVS_ERROR_UNEXPECTED; return FALSE; } BYTE* pBuffer; const char *odtFormatLine = "application/epub+zip"; BSTR bstrFilePath = sFilename.AllocSysString(); HRESULT hRes = piOfficeUtils->LoadFileFromArchive(bstrFilePath, L"mimetype", &pBuffer); SysFreeString( bstrFilePath ); if ( hRes == S_OK && pBuffer != NULL ) { if (strstr((char*)pBuffer, odtFormatLine)) bResult = TRUE; } RELEASEHEAP(pBuffer); return bResult; } bool COfficeEpubFile::SaveCover( CString sFilename, long& nWidth, long& nHeight ) { if( NULL != m_piCommandsRenderer ) { long nPageCount = 0; m_piCommandsRenderer->get_PageCount( &nPageCount ); if( nPageCount > 0 ) { double dWidthMM = 0; double dHeightMM = 0; m_piCommandsRenderer->GetPageSize( 0, &dWidthMM, &dHeightMM ); // if( 0 != dHeightMM ) { nHeight = 1186; nWidth = nHeight * dWidthMM / dHeightMM; } AVSMediaCore3::IAVSUncompressedVideoFrame* piImage = NULL; AVSGraphics::IAVSGraphicsRenderer* piGraphicsRenderer = NULL; UpdateGdiPlusRenderer( dWidthMM, dHeightMM, &piImage, &piGraphicsRenderer ); if( NULL != piGraphicsRenderer ) { piGraphicsRenderer->NewPage(); m_piCommandsRenderer->DrawPage( 0, piGraphicsRenderer ); //сохраняем файл ImageFile::IImageFile3Ptr piImageFile = NULL; piImageFile.CreateInstance( __uuidof(ImageFile::ImageFile3) ); if( NULL != piImageFile ) { VARIANT_BOOL vbSuccess = VARIANT_TRUE; BSTR bstrFilename = sFilename.AllocSysString(); piImageFile->SaveImage2( (IUnknown**)&piImage, IMAGEFORMAT_JPE, bstrFilename, &vbSuccess ); SysFreeString( bstrFilename ); if( VARIANT_FALSE != vbSuccess ) return true; } } } } return false; } void COfficeEpubFile::UpdateGdiPlusRenderer(double dWidthMm, double dHeightMm, AVSMediaCore3::IAVSUncompressedVideoFrame** piImage, AVSGraphics::IAVSGraphicsRenderer** piRend) { (*piImage) = NULL; (*piRend) = NULL; CoCreateInstance(__uuidof( AVSMediaCore3::CAVSUncompressedVideoFrame), NULL ,CLSCTX_INPROC_SERVER, __uuidof(AVSMediaCore3::IAVSUncompressedVideoFrame), (void **)piImage); AVSGraphics::IAVSFontManagerPtr piFontManager; piFontManager.CreateInstance( __uuidof(AVSGraphics::CAVSFontManager) ); piFontManager->Initialize( L"" ); //задаем dpi без привязки к монитору int nLOGPIXELSX = 96; int nLOGPIXELSY = 96; //размер исходной картинки long nWidthPix = (long)(nLOGPIXELSX * dWidthMm / (10 * ONE_INCH)); long nHeightPix = (long)(nLOGPIXELSY * dWidthMm * dHeightMm / (10 * dWidthMm * ONE_INCH)); (*piImage)->Width = nWidthPix; (*piImage)->Height = nHeightPix; (*piImage)->put_Stride(0, nWidthPix * 4); (*piImage)->AspectRatioX = nWidthPix; (*piImage)->AspectRatioY = nHeightPix; (*piImage)->DataSize = 4 * nWidthPix * nHeightPix; (*piImage)->ColorSpace = 64 + (1 << 31); (*piImage)->AllocateBuffer(-1); memset((*piImage)->Buffer, 255, (*piImage)->BufferSize); CoCreateInstance(__uuidof( AVSGraphics::CAVSGraphicsRenderer), NULL ,CLSCTX_INPROC_SERVER, __uuidof(AVSGraphics::IAVSGraphicsRenderer), (void **)piRend); //ставим FontManager VARIANT vtVariant; vtVariant.vt = VT_UNKNOWN; vtVariant.punkVal = piFontManager; (*piRend)->SetAdditionalParam( L"FontManager", vtVariant ); (*piRend)->put_Width(dWidthMm); (*piRend)->put_Height(dHeightMm); (*piRend)->CreateFromMediaData(*piImage, 0, 0, nWidthPix, nHeightPix ); }