#include "Fb2File.h"
#include "../DesktopEditor/xml/include/xmlutils.h"
#include "../DesktopEditor/xml/include/xmlencoding.h"
#include "../DesktopEditor/common/Base64.h"
#include "../DesktopEditor/common/File.h"
#include "../DesktopEditor/common/Directory.h"
#include "../DesktopEditor/common/StringBuilder.h"
#include "../DesktopEditor/common/SystemUtils.h"
#include "../DesktopEditor/raster/BgraFrame.h"
#include "../OfficeUtils/src/OfficeUtils.h"
#include "../Common/3dParty/html/htmltoxhtml.h"
#include "./template/template.h"
#include
#include
");
else
oXml.WriteString(L"
");
}
else if (sName == L"div")
{
std::wstring sFootnoteName;
NSStringUtils::CStringBuilder oFootnote;
while (m_oLightReader.MoveToNextAttribute())
{
std::wstring sAtrName = m_oLightReader.GetName();
std::wstring sAtrText = m_oLightReader.GetText();
if (sAtrName == L"style" && sAtrText == L"mso-element:footnote")
m_bFootnote = true;
else if (sAtrName == L"id")
sFootnoteName = sAtrText;
}
m_oLightReader.MoveToElement();
if (m_bFootnote && !sFootnoteName.empty())
{
readStream(oFootnote, arPProperties);
m_mFootnotes.insert(std::make_pair(sFootnoteName, oFootnote.GetData()));
m_bFootnote = false;
}
else
readStream(oXml, arPProperties);
}
else if (sName == L"p")
{
OpenP(oXml);
readStream(oXml, arPProperties);
CloseP(oXml);
}
else if (sName == L"title")
{
m_oTitleInfo.m_sBookTitle = EncodeXmlString(m_oLightReader.GetText2());
}
else if (sName == L"meta")
{
std::wstring sAtrName, sAtrContent;
while (m_oLightReader.MoveToNextAttribute())
{
std::wstring sAtr = m_oLightReader.GetName();
if (sAtr == L"name")
sAtrName = m_oLightReader.GetText();
else if (sAtr == L"content")
sAtrContent = m_oLightReader.GetText();
}
m_oLightReader.MoveToElement();
if (!sAtrName.empty())
{
if (sAtrName == L"creator")
m_oTitleInfo.m_sAuthors = EncodeXmlString(sAtrContent);
else if (sAtrName == L"description")
m_oTitleInfo.m_sAnnotation = EncodeXmlString(sAtrContent);
else if (sAtrName == L"subject")
m_oTitleInfo.m_sGenres = EncodeXmlString(sAtrContent);
else if (sAtrName == L"keywords")
m_oTitleInfo.m_sKeywords = EncodeXmlString(sAtrContent);
}
}
else if (sName == L"h1" || sName == L"h2" || sName == L"h3" || sName == L"h4" || sName == L"h5" || sName == L"h6")
{
bool bInH = false;
int nH = 0;
if (!m_bInTable && !m_bInP)
{
nH = stoi(sName.substr(1));
for (int i = 0; i < nH; ++i)
oXml.WriteString(L"");
oXml.WriteString(L"");
bInH = true;
m_bInP = true;
}
readStream(oXml, arPProperties);
if (bInH)
{
oXml.WriteString(L"
");
for (int i = 0; i < nH; ++i)
oXml.WriteString(L"");
m_bInP = false;
}
}
else if (sName == L"span")
{
std::wstring sStyle;
while (m_oLightReader.MoveToNextAttribute())
if (m_oLightReader.GetName() == L"style")
sStyle = m_oLightReader.GetText();
m_oLightReader.MoveToElement();
std::wstring sAlign;
size_t nAlign = sStyle.find(L"vertical-align");
if (nAlign != std::wstring::npos)
{
nAlign = sStyle.find(L':', nAlign);
size_t nAlignEnd = sStyle.find(L';', nAlign);
sAlign = sStyle.substr(nAlign + 1, (nAlignEnd < sStyle.length() ? nAlignEnd : sStyle.length()) - nAlign);
if (sAlign == L"super")
{
arPProperties.push_back(EParagraphPropertie::Sup);
readStream(oXml, arPProperties);
arPProperties.pop_back();
}
else if (sAlign == L"sub")
{
arPProperties.push_back(EParagraphPropertie::Sub);
readStream(oXml, arPProperties);
arPProperties.pop_back();
}
else
readStream(oXml, arPProperties);
}
else
readStream(oXml, arPProperties);
}
else if (sName == L"s")
{
arPProperties.push_back(EParagraphPropertie::Strikethrough);
readStream(oXml, arPProperties);
arPProperties.pop_back();
}
else if (sName == L"i")
{
arPProperties.push_back(EParagraphPropertie::Emphasis);
readStream(oXml, arPProperties);
arPProperties.pop_back();
}
else if (sName == L"b")
{
arPProperties.push_back(EParagraphPropertie::Strong);
readStream(oXml, arPProperties);
arPProperties.pop_back();
}
else if (sName == L"table")
{
oXml.WriteString(L"");
m_bInTable = true;
ParagraphProperties arTableProperties;
readStream(oXml, arTableProperties);
oXml.WriteString(L"
");
m_bInTable = false;
}
else if (sName == L"tr")
{
oXml.WriteString(L"");
readStream(oXml, arPProperties);
oXml.WriteString(L"
");
}
else if (sName == L"td" || sName == L"th")
{
oXml.WriteString(L"");
readStream(oXml, arPProperties);
oXml.WriteString(L" | ");
}
else if (sName == L"a")
{
bool bFootnote = false;
oXml.WriteString(L"");
readStream(oXml, arPProperties);
oXml.WriteString(L"");
}
else if (sName == L"ul")
readLi(oXml, true, arPProperties);
else if (sName == L"ol")
readLi(oXml, false, arPProperties);
else if (sName == L"img")
{
std::wstring sId, sBinary;
while (m_oLightReader.MoveToNextAttribute())
{
if (m_oLightReader.GetName() == L"src")
{
sBinary = m_oLightReader.GetText();
sBinary.erase(0, sBinary.find(L',') + 1);
std::vector vImage;
vImage.push_back(sBinary);
sId = std::to_wstring(m_mImages.size() + 1);
m_mImages.insert(std::make_pair(sId, vImage));
}
}
m_oLightReader.MoveToElement();
oXml.WriteString(L"");
}
else
readStream(oXml, arPProperties);
} while (m_oLightReader.ReadNextSiblingNode2(nDepth));
}
void readLi(NSStringUtils::CStringBuilder& oXml, bool bUl, ParagraphProperties& arProperties)
{
int nNum = 1;
while (m_oLightReader.MoveToNextAttribute())
if (m_oLightReader.GetName() == L"start")
nNum = std::stoi(m_oLightReader.GetText());
m_oLightReader.MoveToElement();
int nDeath = m_oLightReader.GetDepth();
if (m_oLightReader.IsEmptyNode() || !m_oLightReader.ReadNextSiblingNode2(nDeath))
return;
do
{
if (m_oLightReader.GetName() == L"li")
{
OpenP(oXml);
if (bUl)
oXml.AddCharSafe(183);
else
{
std::wstring sPoint = std::to_wstring(nNum) + L'.';
while (m_oLightReader.MoveToNextAttribute())
{
if (m_oLightReader.GetName() == L"style")
{
std::wstring sText = m_oLightReader.GetText();
size_t nListType = sText.find(L"list-style-type: ");
if (nListType != std::wstring::npos)
{
nListType += 17;
size_t nListTypeEnd = sText.find(L';', nListType);
std::wstring sListType = sText.substr(nListType, nListTypeEnd - nListType);
if (sListType == L"decimal")
break;
else if (sListType == L"upper-alpha")
sPoint = std::wstring((nNum - 1) / 26 + 1, L'A' + (nNum - 1) % 26);
else if (sListType == L"lower-alpha")
sPoint = std::wstring((nNum - 1) / 26 + 1, L'a' + (nNum - 1) % 26);
else if (sListType == L"lower-roman")
sPoint = ToLowerRoman(nNum);
else if (sListType == L"upper-roman")
sPoint = ToUpperRoman(nNum);
sPoint += L'.';
}
}
}
m_oLightReader.MoveToElement();
nNum++;
oXml.WriteString(sPoint);
}
oXml.WriteString(L" ");
readStream(oXml, arProperties);
CloseP(oXml);
}
} while (m_oLightReader.ReadNextSiblingNode2(nDeath));
}
void readTitleInfo(NSStringUtils::CStringBuilder& oTitleInfo)
{
if (!m_oTitleInfo.m_sBookTitle.empty())
{
oTitleInfo.WriteString(L"");
oTitleInfo.WriteString(m_oTitleInfo.m_sBookTitle);
oTitleInfo.WriteString(L"");
}
if (!m_oTitleInfo.m_sAuthors.empty())
{
oTitleInfo.WriteString(L"");
oTitleInfo.WriteString(m_oTitleInfo.m_sAuthors);
oTitleInfo.WriteString(L"");
}
if (!m_oTitleInfo.m_sGenres.empty())
{
oTitleInfo.WriteString(L"");
oTitleInfo.WriteString(m_oTitleInfo.m_sGenres);
oTitleInfo.WriteString(L"");
}
if (!m_oTitleInfo.m_sKeywords.empty())
{
oTitleInfo.WriteString(L"");
oTitleInfo.WriteString(m_oTitleInfo.m_sKeywords);
oTitleInfo.WriteString(L"");
}
if (!m_oTitleInfo.m_sAnnotation.empty())
{
oTitleInfo.WriteString(L"");
oTitleInfo.WriteString(m_oTitleInfo.m_sAnnotation);
oTitleInfo.WriteString(L"
");
}
}
std::wstring ToUpperRoman(int number)
{
if (number < 0 || number > 3999) return L"";
if (number < 1) return L"";
if (number >= 1000) return L"M" + ToUpperRoman(number - 1000);
if (number >= 900) return L"CM" + ToUpperRoman(number - 900);
if (number >= 500) return L"D" + ToUpperRoman(number - 500);
if (number >= 400) return L"CD" + ToUpperRoman(number - 400);
if (number >= 100) return L"C" + ToUpperRoman(number - 100);
if (number >= 90) return L"XC" + ToUpperRoman(number - 90);
if (number >= 50) return L"L" + ToUpperRoman(number - 50);
if (number >= 40) return L"XL" + ToUpperRoman(number - 40);
if (number >= 10) return L"X" + ToUpperRoman(number - 10);
if (number >= 9) return L"IX" + ToUpperRoman(number - 9);
if (number >= 5) return L"V" + ToUpperRoman(number - 5);
if (number >= 4) return L"IV" + ToUpperRoman(number - 4);
if (number >= 1) return L"I" + ToUpperRoman(number - 1);
return L"";
}
std::wstring ToLowerRoman(int number)
{
if (number < 0 || number > 3999) return L"";
if (number < 1) return L"";
if (number >= 1000) return L"m" + ToLowerRoman(number - 1000);
if (number >= 900) return L"cm" + ToLowerRoman(number - 900);
if (number >= 500) return L"d" + ToLowerRoman(number - 500);
if (number >= 400) return L"cd" + ToLowerRoman(number - 400);
if (number >= 100) return L"c" + ToLowerRoman(number - 100);
if (number >= 90) return L"xc" + ToLowerRoman(number - 90);
if (number >= 50) return L"l" + ToLowerRoman(number - 50);
if (number >= 40) return L"xl" + ToLowerRoman(number - 40);
if (number >= 10) return L"x" + ToLowerRoman(number - 10);
if (number >= 9) return L"ix" + ToLowerRoman(number - 9);
if (number >= 5) return L"v" + ToLowerRoman(number - 5);
if (number >= 4) return L"iv" + ToLowerRoman(number - 4);
if (number >= 1) return L"i" + ToLowerRoman(number - 1);
return L"";
}
private:
void ReadFootnote(NSStringUtils::CStringBuilder& oFootnotes)
{
oFootnotes += L"";
m_bFootnote = true;
readP(L"", oFootnotes);
m_bFootnote = false;
oFootnotes += L"";
}
};
CFb2File::CFb2File()
{
m_internal = new CFb2File_Private();
}
CFb2File::~CFb2File()
{
RELEASEOBJECT(m_internal);
}
// Проверяет, соответствует ли fb2 файл формату
bool CFb2File::IsFb2File(const std::wstring& sFile)
{
// Открывает файл на проверку
if (!m_internal->OpenFromFile(sFile))
return false;
// Читаем FictionBook
return m_internal->isFictionBook();
}
// Выставление рабочей папки
void CFb2File::SetTmpDirectory(const std::wstring& sFolder)
{
// m_internal->m_sTmpFolder = sFolder;
}
// sPath - путь к файлу fb2, sDirectory - директория, где формируется и создается docx
HRESULT CFb2File::Open(const std::wstring& sPath, const std::wstring& sDirectory, CFb2Params* oParams)
{
// Копирование шаблона
if (!ExtractTemplate(sDirectory))
return S_FALSE;
// Начало файла
NSStringUtils::CStringBuilder oDocument;
oDocument += L"";
// Создаем сноски
NSStringUtils::CStringBuilder oFootnotes;
oFootnotes += L"";
oFootnotes += L"";
// Создаем рельсы
m_internal->m_oDocXmlRels += L"";
m_internal->m_oDocXmlRels += L"";
m_internal->m_oDocXmlRels += L"";
m_internal->m_oDocXmlRels += L"";
m_internal->m_oDocXmlRels += L"";
m_internal->m_oDocXmlRels += L"";
m_internal->m_oDocXmlRels += L"";
m_internal->m_oNoteXmlRels += L"";
// Директория картинок
std::wstring sMediaDirectory = sDirectory + L"/word/media";
NSDirectory::CreateDirectory(sMediaDirectory);
NSStringUtils::CStringBuilder oBuilder;
NSStringUtils::CStringBuilder oDescription;
NSStringUtils::CStringBuilder oContents;
// Читаем содержание, картинки, сноски
bool bNeedContents = false;
if (oParams)
bNeedContents = oParams->bNeedContents;
if (!m_internal->readText(sPath, sMediaDirectory, oContents, oFootnotes))
return S_FALSE;
// Переходим в начало
if (!m_internal->m_oLightReader.MoveToStart())
return S_FALSE;
int nDeath = m_internal->m_oLightReader.GetDepth();
while (m_internal->m_oLightReader.ReadNextSiblingNode(nDeath))
{
std::wstring sName = m_internal->m_oLightReader.GetName();
if (sName == L"description")
m_internal->readDescription(oDescription);
else if (sName == L"body")
{
bool bNotes = false;
while (m_internal->m_oLightReader.MoveToNextAttribute())
{
if (m_internal->m_oLightReader.GetName() == L"name" &&
m_internal->m_oLightReader.GetText() == L"notes")
bNotes = true;
}
m_internal->m_oLightReader.MoveToElement();
if (bNotes)
continue;
m_internal->readBody(oBuilder);
}
}
// Конец сносок
oFootnotes += L"";
// Пишем сноски в файл
NSFile::CFileBinary oFootnotesWriter;
if (oFootnotesWriter.CreateFileW(sDirectory + L"/word/footnotes.xml"))
{
oFootnotesWriter.WriteStringUTF8(oFootnotes.GetData());
oFootnotesWriter.CloseFile();
}
// Вставляем description
oDocument += oDescription.GetData();
// Вставляем содержание
if (bNeedContents)
oDocument += oContents.GetData();
// Вставляем основное тело
oDocument += oBuilder.GetData();
// Конец документа
oDocument += L"";
// Пишем документ в файл
NSFile::CFileBinary oDocumentXmlWriter;
if (oDocumentXmlWriter.CreateFileW(sDirectory + L"/word/document.xml"))
{
oDocumentXmlWriter.WriteStringUTF8(oDocument.GetData());
oDocumentXmlWriter.CloseFile();
}
// Директория app и core
std::wstring sDocPropsDirectory = sDirectory + L"/docProps";
NSDirectory::CreateDirectory(sDocPropsDirectory);
// Создаем core.xml
NSStringUtils::CStringBuilder oCore;
// Заголовок
oCore += L"";
oCore.WriteString(EncodeXmlString(m_internal->m_oTitleInfo.m_sBookTitle));
// Жанры
oCore.WriteString(L"");
if (!m_internal->m_oTitleInfo.m_sGenres.empty())
{
oCore.WriteString(L"");
oCore.WriteString(EncodeXmlString(m_internal->m_oTitleInfo.m_sGenres));
oCore.WriteString(L"");
}
// Авторы
if (!m_internal->m_oTitleInfo.m_sAuthors.empty())
{
oCore.WriteString(L"");
oCore.WriteString(EncodeXmlString(m_internal->m_oTitleInfo.m_sAuthors));
oCore.WriteString(L"");
}
// Ключевые слова
if (!m_internal->m_oTitleInfo.m_sKeywords.empty())
{
oCore.WriteString(L"");
oCore.WriteString(EncodeXmlString(m_internal->m_oTitleInfo.m_sKeywords));
oCore.WriteString(L"");
}
if (!m_internal->m_oTitleInfo.m_sAnnotation.empty())
{
oCore.WriteString(L"");
oCore.WriteString(EncodeXmlString(m_internal->m_oTitleInfo.m_sAnnotation));
oCore.WriteString(L"");
}
// Конец core
oCore += L"";
// Пишем core в файл
NSFile::CFileBinary oCoreWriter;
if (oCoreWriter.CreateFileW(sDocPropsDirectory + L"/core.xml"))
{
oCoreWriter.WriteStringUTF8(oCore.GetData());
oCoreWriter.CloseFile();
}
// Получаем версию ONLYOFFICE
std::wstring sApplication = NSSystemUtils::GetEnvVariable(NSSystemUtils::gc_EnvApplicationName);
if (sApplication.empty())
sApplication = NSSystemUtils::gc_EnvApplicationNameDefault;
#if defined(INTVER)
std::string sVersion = VALUE2STR(INTVER);
#endif
sApplication += L"/";
sApplication += UTF8_TO_U(sVersion);
// Создаем app.xml
NSStringUtils::CStringBuilder oApp;
oApp += L"";
oApp += sApplication;
oApp += L"0falsefalsefalsefalse";
// Пишем app в файл
NSFile::CFileBinary oAppWriter;
if (oAppWriter.CreateFileW(sDocPropsDirectory + L"/app.xml"))
{
oAppWriter.WriteStringUTF8(oApp.GetData());
oAppWriter.CloseFile();
}
// Конец рельсов
m_internal->m_oDocXmlRels += L"";
// Пишем рельсы в файл
NSFile::CFileBinary oRelsWriter;
if (oRelsWriter.CreateFileW(sDirectory + L"/word/_rels/document.xml.rels"))
{
oRelsWriter.WriteStringUTF8(m_internal->m_oDocXmlRels.GetData());
oRelsWriter.CloseFile();
}
m_internal->m_oNoteXmlRels += L"";
if (oRelsWriter.CreateFileW(sDirectory + L"/word/_rels/footnotes.xml.rels"))
{
oRelsWriter.WriteStringUTF8(m_internal->m_oNoteXmlRels.GetData());
oRelsWriter.CloseFile();
}
// Архивим в docx
bool bNeedDocx = false;
if (oParams)
bNeedDocx = oParams->bNeedDocx;
if (bNeedDocx)
{
COfficeUtils oZip;
return oZip.CompressFileOrDirectory(sDirectory, sDirectory + L"/" + NSFile::GetFileName(sPath) + L".docx");
}
return S_OK;
}
// sHtmlFile - путь к файлу html, sDst - путь к результирующему файлу fb2, sInpTitle - входящий заголовок файла
HRESULT CFb2File::FromHtml(const std::wstring& sHtmlFile, const std::wstring& sDst, const std::wstring& sInpTitle)
{
std::wstring sTitle = sInpTitle;
if (sTitle.empty())
sTitle = NSFile::GetFileName(sDst);
BYTE* pData;
DWORD nLength;
if (!NSFile::CFileBinary::ReadAllBytes(sHtmlFile, &pData, nLength))
return S_FALSE;
std::string sContent = XmlUtils::GetUtf8FromFileContent(pData, nLength);
bool bNeedConvert = true;
if (nLength > 4)
{
if (pData[0] == 0xFF && pData[1] == 0xFE && !(pData[2] == 0x00 && pData[3] == 0x00))
bNeedConvert = false;
if (pData[0] == 0xFE && pData[1] == 0xFF)
bNeedConvert = false;
if (pData[0] == 0xFF && pData[1] == 0xFE && pData[2] == 0x00 && pData[3] == 0x00)
bNeedConvert = false;
if (pData[0] == 0 && pData[1] == 0 && pData[2] == 0xFE && pData[3] == 0xFF)
bNeedConvert = false;
}
RELEASEARRAYOBJECTS(pData);
//XmlUtils::CXmlLiteReader oIndexHtml;
std::wstring xhtml = htmlToXhtml(sContent, bNeedConvert);
if (!m_internal->m_oLightReader.FromString(xhtml))
return S_FALSE;
m_internal->m_oLightReader.ReadNextNode(); // html
int nDepth = m_internal->m_oLightReader.GetDepth();
m_internal->m_oLightReader.ReadNextSiblingNode(nDepth); // head
m_internal->m_oLightReader.ReadNextSiblingNode(nDepth); // body
//std::vector arrBinary;
NSStringUtils::CStringBuilder oDocument;
ParagraphProperties arProperties;
m_internal->readStream(oDocument, arProperties);
NSStringUtils::CStringBuilder oRes;
oRes.WriteString(L"");
// description title-info
oRes.WriteString(L"");
if (m_internal->m_oTitleInfo.m_sBookTitle.empty())
m_internal->m_oTitleInfo.m_sBookTitle = EncodeXmlString(sTitle);
NSStringUtils::CStringBuilder oTitleInfo;
m_internal->readTitleInfo(oTitleInfo);
oRes.WriteString(oTitleInfo.GetData());
oRes.WriteString(L"");
// body
oRes.WriteString(L"");
oRes.WriteString(oDocument.GetData());
oRes.WriteString(L"");
// notes
if (!m_internal->m_mFootnotes.empty())
{
oRes.WriteString(L"");
for (std::map::iterator i = m_internal->m_mFootnotes.begin(); i != m_internal->m_mFootnotes.end(); i++)
{
oRes.WriteString(L"first + L"\">");
oRes.WriteString(i->second);
oRes.WriteString(L"");
}
oRes.WriteString(L"");
}
// binary
for (std::map>::iterator i = m_internal->m_mImages.begin(); i != m_internal->m_mImages.end(); i++)
{
oRes.WriteString(L"first + L".png\" content-type=\"image/png\">");
oRes.WriteString(i->second[0]);
oRes.WriteString(L"");
}
oRes.WriteString(L"");
// Запись в файл
NSFile::CFileBinary oWriter;
if (oWriter.CreateFileW(sDst))
{
oWriter.WriteStringUTF8(oRes.GetData());
oWriter.CloseFile();
return S_OK;
}
return S_FALSE;
}