mirror of
https://github.com/ONLYOFFICE/core.git
synced 2026-07-15 06:37:25 +08:00
398 lines
9.7 KiB
C++
398 lines
9.7 KiB
C++
#pragma once
|
|
#include "LiteXHTMLWriter.h"
|
|
#include "LiteHTMLReader.h"
|
|
#include "HTMLTagWrapper.h"
|
|
|
|
class ILiteHTMLConverterEvent
|
|
{
|
|
public:
|
|
virtual void OnProgress (double aProcent, bool &aAbort);
|
|
};
|
|
|
|
// flags
|
|
#define ECR_GENERAL 0x00000000
|
|
#define ECR_ALWAYSCLOSED 0x00000001
|
|
#define ECR_FORBIDDEN 0x00000002
|
|
#define ECR_HASNT_CHILDREN_TAG 0x00000004
|
|
#define ECR_EMULATE_HASTEXT 0x00000008
|
|
|
|
|
|
class CLiteHTMLConverter: public ILiteHTMLReaderEvents
|
|
{
|
|
protected:
|
|
CLiteHTMLReader m_oReader;
|
|
ILiteHTMLConverterEvent *pProgressEvent;
|
|
IXHTMLWriter *pXHTMLWriter;
|
|
bool m_bAutoInlineClosing;
|
|
|
|
typedef DWORD ECloseRules;
|
|
struct STagInfo
|
|
{
|
|
//enum ECloseRules {ECR_GENERAL, ECR_MAYBEUNCLOSED, ECR_ALWAYSUNCLOSED, ECR_ALWAYSCLOSED, ECR_FORBIDDEN, ECR_HASNT_CHILDREN_TAG, ECR_EMULATE_HASTEXT} m_eCloseRules;
|
|
ECloseRules m_eCloseRules;
|
|
|
|
};
|
|
typedef CMap<CString, LPCTSTR, STagInfo, STagInfo> CTagsInfo;
|
|
static CTagsInfo m_mTagsInfo; // collection of named colors
|
|
|
|
CArray<CHTMLTagWrapper *> m_aOpenedTag;
|
|
|
|
public:
|
|
CLiteHTMLConverter ()
|
|
: pProgressEvent (NULL)
|
|
, pXHTMLWriter (NULL)
|
|
, m_bAutoInlineClosing (false)
|
|
{
|
|
InitTagInfo ();
|
|
}
|
|
virtual ~CLiteHTMLConverter ()
|
|
{
|
|
|
|
}
|
|
HRESULT ToXHTML (HANDLE hHtmlFile, IXHTMLWriter *pWriter)
|
|
{
|
|
// both input and output files are opened
|
|
HRESULT hRes = S_OK;
|
|
|
|
// parser
|
|
CLiteHTMLReader oReader;
|
|
oReader.setEventHandler (this);
|
|
|
|
// Store writer
|
|
pXHTMLWriter = pWriter;
|
|
|
|
|
|
// Start parsing
|
|
UINT uiOpened = oReader.ReadFile (hHtmlFile);
|
|
return hRes;
|
|
}
|
|
protected:
|
|
|
|
bool CheckEmulateHasText (CLiteHTMLTag *aLastUnclosedTag)
|
|
{
|
|
BOOL bRes = false;
|
|
STagInfo rValue;
|
|
|
|
if (TRUE == m_mTagsInfo.Lookup (aLastUnclosedTag->getTagName(), rValue))
|
|
{
|
|
if (rValue.m_eCloseRules & ECR_EMULATE_HASTEXT)
|
|
return true;
|
|
}
|
|
return bRes;
|
|
}
|
|
bool CheckHasNoChildrenTags (CLiteHTMLTag *aLastUnclosedTag)
|
|
{
|
|
BOOL bRes = false;
|
|
STagInfo rValue;
|
|
|
|
if (TRUE == m_mTagsInfo.Lookup (aLastUnclosedTag->getTagName(), rValue))
|
|
{
|
|
if (rValue.m_eCloseRules & ECR_HASNT_CHILDREN_TAG)
|
|
return true;
|
|
}
|
|
return bRes;
|
|
}
|
|
bool CheckForbiddenTag (CLiteHTMLTag *aLastUnclosedTag)
|
|
{
|
|
BOOL bRes = false;
|
|
STagInfo rValue;
|
|
|
|
if (TRUE == m_mTagsInfo.Lookup (aLastUnclosedTag->getTagName(), rValue))
|
|
{
|
|
if (rValue.m_eCloseRules & ECR_FORBIDDEN)
|
|
return true;
|
|
}
|
|
return bRes;
|
|
}
|
|
bool CheckAutoInlineClosing (CLiteHTMLTag *aLastUnclosedTag)
|
|
{
|
|
// check auto inline closing
|
|
// TODO: check avrious tags ("br", "meta", ...)
|
|
|
|
BOOL bRes = false;
|
|
STagInfo rValue;
|
|
|
|
if (TRUE == m_mTagsInfo.Lookup (aLastUnclosedTag->getTagName(), rValue))
|
|
{
|
|
if (rValue.m_eCloseRules & ECR_ALWAYSCLOSED)
|
|
return true;
|
|
}
|
|
return bRes;
|
|
}
|
|
virtual void BeginParse(DWORD dwAppData, bool &bAbort)
|
|
{
|
|
UNUSED_ALWAYS(dwAppData);
|
|
TRACE (_T("CLiteHTMLConverter::BeginParse\n"));
|
|
bAbort = false;
|
|
|
|
if (pXHTMLWriter)
|
|
pXHTMLWriter->BeginFile ();
|
|
|
|
if (pProgressEvent)
|
|
pProgressEvent->OnProgress (0, bAbort);
|
|
|
|
}
|
|
|
|
virtual void StartTag(CLiteHTMLTag *pTag, DWORD dwAppData, bool &bAbort)
|
|
{
|
|
UNUSED_ALWAYS(pTag);
|
|
UNUSED_ALWAYS(dwAppData);
|
|
bAbort = false;
|
|
|
|
ATLTRACE2 ("CLiteHTMLConverter::StartTag: %s %s\n", pTag->getTagName(), pTag->IsTagInline() ? "(inline)" : "");
|
|
|
|
// Check: if previous tags must be closed
|
|
CHTMLTagWrapper *pLastTag = NULL;
|
|
const size_t szRecords = m_aOpenedTag.GetCount();
|
|
if (0 != szRecords)
|
|
{
|
|
pLastTag = m_aOpenedTag.GetAt(szRecords - 1);
|
|
if ((NULL != pLastTag)
|
|
&& (CheckAutoInlineClosing (pLastTag->Tag()) || CheckHasNoChildrenTags (pLastTag->Tag()))
|
|
)
|
|
{
|
|
// previous tag must be closed
|
|
if (pXHTMLWriter)
|
|
pXHTMLWriter->EndTag (pLastTag);
|
|
|
|
// remove last unclosed tag from tags quene
|
|
m_aOpenedTag.RemoveAt (szRecords - 1);
|
|
delete pLastTag;
|
|
pLastTag = NULL;
|
|
}
|
|
}
|
|
|
|
// Add unclosed tag to the opened tag quene
|
|
CHTMLTagWrapper* pTagWrapper = new CHTMLTagWrapper(pTag);
|
|
|
|
if (!CheckForbiddenTag (pTag))
|
|
{
|
|
// Write started tag
|
|
if (pXHTMLWriter)
|
|
pXHTMLWriter->StartTag (pTagWrapper, m_aOpenedTag.IsEmpty() ? NULL : m_aOpenedTag.GetAt (m_aOpenedTag.GetCount() - 1));
|
|
}
|
|
|
|
if (CheckEmulateHasText (pTag))
|
|
{
|
|
// emulate having text
|
|
if (pXHTMLWriter)
|
|
pXHTMLWriter->Characters (pTagWrapper, "");
|
|
}
|
|
|
|
m_aOpenedTag.Add (pTagWrapper);
|
|
|
|
// Fire event
|
|
if (pProgressEvent)
|
|
pProgressEvent->OnProgress (0, bAbort);
|
|
}
|
|
|
|
virtual void EndTag(CLiteHTMLTag *pTag, DWORD dwAppData, bool &bAbort)
|
|
{
|
|
UNUSED_ALWAYS(pTag);
|
|
UNUSED_ALWAYS(dwAppData);
|
|
bAbort = false;
|
|
|
|
ATLTRACE2 ("CLiteHTMLConverter::EndTag: %s\n", pTag->getTagName());
|
|
|
|
// Check unclosed tags
|
|
size_t nLastTag = m_aOpenedTag.GetCount() - 1;
|
|
while (nLastTag > 0)
|
|
{
|
|
//
|
|
CHTMLTagWrapper* pOpenedTag = m_aOpenedTag.GetAt (nLastTag);
|
|
if (NULL != pOpenedTag)
|
|
{
|
|
//
|
|
if (pOpenedTag->Tag()->getTagName() == pTag->getTagName())
|
|
{
|
|
// ok, it's a normal situation. removing last tag and closing it in the writer
|
|
if (!CheckForbiddenTag (pOpenedTag->Tag()))
|
|
{
|
|
if (pXHTMLWriter)
|
|
pXHTMLWriter->EndTag (pOpenedTag); // XHTMLWriter->EndTag (pOpenedTag);
|
|
}
|
|
|
|
m_aOpenedTag.RemoveAt (nLastTag);
|
|
|
|
delete pOpenedTag;
|
|
pOpenedTag = NULL;
|
|
break;
|
|
}
|
|
else
|
|
{
|
|
// we must to close a last opened tag, because
|
|
ATLTRACE2 ("Converter: EndTag(): force closing last opened tag (%s)\n", pOpenedTag->Tag()->getTagName());
|
|
if (!CheckForbiddenTag (pOpenedTag->Tag()))
|
|
{
|
|
if (pXHTMLWriter)
|
|
pXHTMLWriter->EndTag (pOpenedTag);
|
|
}
|
|
m_aOpenedTag.RemoveAt (nLastTag);
|
|
|
|
delete pOpenedTag;
|
|
pOpenedTag = NULL;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
ATLTRACE2 ("Converter: EndTag(): m_aOpenedTag.GetAt() returns NULL");
|
|
break;
|
|
}
|
|
|
|
nLastTag--;
|
|
}
|
|
|
|
|
|
if (pProgressEvent)
|
|
pProgressEvent->OnProgress (0, bAbort);
|
|
}
|
|
|
|
virtual void Characters(const CString &rText, DWORD dwAppData, bool &bAbort)
|
|
{
|
|
UNUSED_ALWAYS(rText);
|
|
UNUSED_ALWAYS(dwAppData);
|
|
bAbort = false;
|
|
|
|
ATLTRACE2 ("CLiteHTMLConverter::Characters: %s\n", rText);
|
|
|
|
// give last opened tag and add text to tag
|
|
if (!m_aOpenedTag.IsEmpty())
|
|
{
|
|
CHTMLTagWrapper *pTag = m_aOpenedTag.GetAt (m_aOpenedTag.GetCount() - 1);
|
|
if (NULL == pTag)
|
|
{
|
|
ATLTRACE2 ("CLiteHTMLConverter::Characters: %s : fail to get last opened tag\n", rText);
|
|
m_aOpenedTag.RemoveAt (m_aOpenedTag.GetCount() - 1);
|
|
return;
|
|
}
|
|
|
|
// attach text to tag
|
|
//pTag->Tag()->addText (rText);
|
|
if (!CheckAutoInlineClosing (pTag->Tag()) && !CheckForbiddenTag (pTag->Tag()))
|
|
{
|
|
if (pXHTMLWriter)
|
|
pXHTMLWriter->Characters (pTag, rText);
|
|
}
|
|
|
|
}
|
|
else
|
|
{
|
|
ATLTRACE2 ("CLiteHTMLConverter::Characters: %s : there are no opened tag\n", rText);
|
|
}
|
|
|
|
if (pProgressEvent)
|
|
pProgressEvent->OnProgress (0, bAbort);
|
|
}
|
|
|
|
virtual void Comment(const CString &rComment, DWORD dwAppData, bool &bAbort)
|
|
{
|
|
UNUSED_ALWAYS(rComment);
|
|
UNUSED_ALWAYS(dwAppData);
|
|
bAbort = false;
|
|
|
|
ATLTRACE2 ("CLiteHTMLConverter::Comment: %s\n", rComment);
|
|
|
|
// emty comments
|
|
|
|
if (pProgressEvent)
|
|
pProgressEvent->OnProgress (0, bAbort);
|
|
}
|
|
|
|
virtual void EndParse(DWORD dwAppData, bool bIsAborted)
|
|
{
|
|
UNUSED_ALWAYS(dwAppData);
|
|
UNUSED_ALWAYS(bIsAborted);
|
|
|
|
ATLTRACE2 ("CLiteHTMLConverter::EndParse()\n");
|
|
|
|
// Close all unclosed tags
|
|
while (m_aOpenedTag.GetCount() > 0)
|
|
{
|
|
//
|
|
const size_t nLastTag = m_aOpenedTag.GetCount() - 1;
|
|
|
|
CHTMLTagWrapper* pOpenedTag = m_aOpenedTag.GetAt (nLastTag);
|
|
if (NULL != pOpenedTag)
|
|
{
|
|
// we must to close a last opened tag, because
|
|
ATLTRACE2 ("Converter: EndParse(): force closing last opened tag (%s)\n", pOpenedTag->Tag()->getTagName());
|
|
if (pXHTMLWriter)
|
|
pXHTMLWriter->EndTag (pOpenedTag);
|
|
m_aOpenedTag.RemoveAt (nLastTag);
|
|
|
|
delete pOpenedTag;
|
|
pOpenedTag = NULL;
|
|
}
|
|
else
|
|
{
|
|
TRACE (_T("Converter: EndParse(): m_aOpenedTag.GetAt() returns NULL"));
|
|
}
|
|
}
|
|
|
|
if (pProgressEvent)
|
|
pProgressEvent->OnProgress (100.0, bIsAborted);
|
|
}
|
|
|
|
public:
|
|
// Event Handler
|
|
void SetEventHandler (ILiteHTMLConverterEvent *aEventHandler)
|
|
{
|
|
pProgressEvent = aEventHandler;
|
|
}
|
|
ILiteHTMLConverterEvent *GetEventHandler (void) const
|
|
{
|
|
return pProgressEvent;
|
|
}
|
|
void SetWriter (IXHTMLWriter *aXHTMLWriter)
|
|
{
|
|
pXHTMLWriter = aXHTMLWriter;
|
|
}
|
|
IXHTMLWriter *GetWriter ()
|
|
{
|
|
return pXHTMLWriter;
|
|
}
|
|
|
|
protected:
|
|
void InitTagInfo ()
|
|
{
|
|
// tag parsing rules
|
|
STagInfo sTagInfo;
|
|
|
|
// meta
|
|
sTagInfo.m_eCloseRules = ECR_ALWAYSCLOSED;
|
|
m_mTagsInfo.SetAt ("meta", sTagInfo);
|
|
|
|
|
|
// link
|
|
sTagInfo.m_eCloseRules = ECR_ALWAYSCLOSED;
|
|
m_mTagsInfo.SetAt ("link", sTagInfo);
|
|
|
|
// br
|
|
sTagInfo.m_eCloseRules = ECR_ALWAYSCLOSED;
|
|
m_mTagsInfo.SetAt ("br", sTagInfo);
|
|
|
|
// script
|
|
sTagInfo.m_eCloseRules = ECR_FORBIDDEN;
|
|
m_mTagsInfo.SetAt ("script", sTagInfo);
|
|
|
|
// style
|
|
sTagInfo.m_eCloseRules = ECR_HASNT_CHILDREN_TAG | ECR_EMULATE_HASTEXT;
|
|
m_mTagsInfo.SetAt ("style", sTagInfo);
|
|
|
|
// iframe
|
|
sTagInfo.m_eCloseRules = ECR_HASNT_CHILDREN_TAG | ECR_EMULATE_HASTEXT;
|
|
m_mTagsInfo.SetAt ("iframe", sTagInfo);
|
|
|
|
// div
|
|
sTagInfo.m_eCloseRules = ECR_EMULATE_HASTEXT; // emulate having text (<tag attr="attr1"></tag> insteadof <tag attr="attr1"/> form)
|
|
m_mTagsInfo.SetAt ("div", sTagInfo);
|
|
m_mTagsInfo.SetAt ("p", sTagInfo);
|
|
m_mTagsInfo.SetAt ("a", sTagInfo);
|
|
|
|
// input
|
|
sTagInfo.m_eCloseRules = ECR_ALWAYSCLOSED | ECR_HASNT_CHILDREN_TAG;
|
|
m_mTagsInfo.SetAt ("input", sTagInfo);
|
|
|
|
}
|
|
//
|
|
}; |