#pragma once //#include "LiteXHTMLWriter.h" #include "IXHTMLWriter.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; bool m_bAddSpaceCharacter; bool m_bNewLineStarted; // new line started flag 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 CAtlMap CTagsInfo; static CTagsInfo m_mTagsInfo; // collection of named colors CAtlArray m_aOpenedTag; CHTMLTagWrapper *m_pTopTagWrapper; public: CLiteHTMLConverter () : pProgressEvent (NULL) , pXHTMLWriter (NULL) , m_bAutoInlineClosing (false) , m_bAddSpaceCharacter (false) , m_bNewLineStarted (true) , m_pTopTagWrapper (NULL) { InitTagInfo (); } virtual ~CLiteHTMLConverter () { if (NULL != m_pTopTagWrapper) { m_pTopTagWrapper->DestroyChildren (); delete m_pTopTagWrapper; m_pTopTagWrapper = NULL; } } HRESULT ToXHTML (HANDLE hHtmlFile, IXHTMLWriter *pWriter, int aDefaultCodePage = CP_ACP); HRESULT ToXHTML (CStringW& aBuffer, IXHTMLWriter *pWriter); 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) { ATLTRACE2 (_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) { bAbort = false; CStringA sTagName; sTagName = pTag->getTagName(); ATLTRACE2 ("CLiteHTMLConverter::StartTag: %s %s\n", sTagName, pTag->IsTagInline() ? "(inline)" : ""); // Check: if previous tags must be closed size_t szRecords = m_aOpenedTag.GetCount(); if (0 != szRecords) { CHTMLTagWrapper *pLastTag = m_aOpenedTag.GetAt(szRecords - 1); if ((NULL != pLastTag) && (CheckAutoInlineClosing (pLastTag->Tag()) || CheckHasNoChildrenTags (pLastTag->Tag()) || pLastTag->Tag()->IsTagInline()) ) { // 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; } } // check custom rules szRecords = m_aOpenedTag.GetCount(); // 1.
      1. tag wouldn't present inside of
      2. tag if (0 == pTag->getTagName().CompareNoCase (_T("li"))) { for (long nTag = (long) szRecords - 1; nTag >= 0; nTag--) { CHTMLTagWrapper *pTagItem = m_aOpenedTag.GetAt(nTag); if (NULL != pTagItem) { bool bFound = false; CString sTagItemName = pTagItem->Tag()->getTagName(); if (0 == sTagItemName.CompareNoCase (_T("li"))) { // current
      3. -tag is situated inside of
      4. tag // we must close parent
      5. tag long nTagsToClose = szRecords - nTag; while (nTagsToClose > 0) { nTagsToClose--; CHTMLTagWrapper *pLastTag = m_aOpenedTag.GetAt (m_aOpenedTag.GetCount() - 1); if (pXHTMLWriter) pXHTMLWriter->EndTag (pLastTag); // remove last unclosed tag from tags quene m_aOpenedTag.RemoveAt (m_aOpenedTag.GetCount() - 1); //delete pLastTag; //pLastTag = NULL; } bFound = true; break; } else if (0 == sTagItemName.CompareNoCase (_T("ol")) || 0 == sTagItemName.CompareNoCase (_T("ul"))) { bFound = true; break; } if (bFound) break; } } } // 2.
        tag wouldn't present inside of
        tag szRecords = m_aOpenedTag.GetCount(); if (0 == pTag->getTagName().CompareNoCase (_T("dt"))) { for (long nTag = (long) szRecords - 1; nTag >= 0; nTag--) { CHTMLTagWrapper *pTagItem = m_aOpenedTag.GetAt(nTag); if (NULL != pTagItem) { bool bFound = false; CString sTagItemName = pTagItem->Tag()->getTagName(); if (0 == sTagItemName.CompareNoCase (_T("dt"))) { // current
        -tag is situated inside of
        tag // we must close parent
        tag long nTagsToClose = szRecords - nTag; while (nTagsToClose > 0) { nTagsToClose--; CHTMLTagWrapper *pLastTag = m_aOpenedTag.GetAt (m_aOpenedTag.GetCount() - 1); if (pXHTMLWriter) pXHTMLWriter->EndTag (pLastTag); // remove last unclosed tag from tags quene m_aOpenedTag.RemoveAt (m_aOpenedTag.GetCount() - 1); //delete pLastTag; //pLastTag = NULL; } bFound = true; break; } else if (0 == sTagItemName.CompareNoCase (_T("dl"))) { // dt must to be inside of
        tag bFound = true; break; } if (bFound) break; } } } // 3. tag should be in the tag szRecords = m_aOpenedTag.GetCount(); if (0 == pTag->getTagName().CompareNoCase (_T("tbody"))) { long nTagsToClose (0); for (long nTag = (long) szRecords - 1; nTag >= 0; nTag--) { CHTMLTagWrapper *pTagItem = m_aOpenedTag.GetAt(nTag); if (NULL != pTagItem) { bool bFound = false; CString sTagItemName = pTagItem->Tag()->getTagName(); if (0 == sTagItemName.CompareNoCase (_T("table"))) { break; } else if (0 == sTagItemName.CompareNoCase (_T("boby"))) { nTagsToClose = 0; break; } else { // parent of is not a
        ++nTagsToClose; } } } // close tags between (parent) tbody and table while (nTagsToClose > 0) { nTagsToClose--; CHTMLTagWrapper *pLastTag = m_aOpenedTag.GetAt (m_aOpenedTag.GetCount() - 1); if (pXHTMLWriter) pXHTMLWriter->EndTag (pLastTag); // remove last unclosed tag from tags quene m_aOpenedTag.RemoveAt (m_aOpenedTag.GetCount() - 1); //delete pLastTag; //pLastTag = NULL; } } // 4. clear m_bAddSpaceCharacter flag, if new tag must to caret-return. if (CHTMLTagWrapper::IsNewLineStarting (pTag->getTagName().MakeLower())) { m_bAddSpaceCharacter = false; m_bNewLineStarted = true; } else { } // Add unclosed tag to the opened tag quene CHTMLTagWrapper* pTagWrapper = new CHTMLTagWrapper(pTag); CHTMLTagWrapper* pParentTagWrapper = m_aOpenedTag.IsEmpty() ? NULL : m_aOpenedTag.GetAt (m_aOpenedTag.GetCount() - 1); if (NULL != pParentTagWrapper) { pParentTagWrapper->AddChild (pTagWrapper); } else { m_pTopTagWrapper = pTagWrapper; // head of tags tree } pTagWrapper->setParentTag (pParentTagWrapper); if (!CheckForbiddenTag (pTag)) { // Write started tag if (pXHTMLWriter) pXHTMLWriter->StartTag (pTagWrapper, pParentTagWrapper); } m_aOpenedTag.Add (pTagWrapper); // Fire event if (pProgressEvent) pProgressEvent->OnProgress (0, bAbort); } virtual void EndTag(CLiteHTMLTag *pTag, DWORD dwAppData, bool &bAbort) { bAbort = false; CStringA sTagName; sTagName = pTag->getTagName(); ATLTRACE2 ("CLiteHTMLConverter::EndTag: %s\n", sTagName); // 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 CStringA sTagLastName; sTagLastName = pOpenedTag->Tag()->getTagName(); ATLTRACE2 ("Converter: EndTag(): force closing last opened tag (%s)\n", sTagLastName); if (0 == sTagLastName.CompareNoCase ("body") && 0 != sTagName.CompareNoCase("body")) { ATLTRACE2 ("Converter: EndTag(): force closing last opened tag, but it is the tag! skipping.\n"); break; } if (0 == sTagLastName.CompareNoCase ("html") && 0 != sTagName.CompareNoCase("html")) { ATLTRACE2 ("Converter: EndTag(): force closing last opened tag, but it is the tag! skipping.\n"); break; } 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) { bAbort = false; CStringA sText; sText = rText; ATLTRACE2 ("CLiteHTMLConverter::Characters: %s\n", sText); // 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", sText); m_aOpenedTag.RemoveAt (m_aOpenedTag.GetCount() - 1); return; } // attach text to tag //pTag->Tag()->addText (rText); bool cbAutoInlineClosing = CheckAutoInlineClosing (pTag->Tag()); const bool cbForbidden = CheckForbiddenTag (pTag->Tag()); if (cbAutoInlineClosing && !cbForbidden) // this tag is "inline closed" - ex. '
        ' tag { // Attach a text to the parent tag, because it wouldn't has text if (m_aOpenedTag.GetCount() > 1) { for (int nParentTag = m_aOpenedTag.GetCount() - 2; nParentTag >= 0; --nParentTag) { CHTMLTagWrapper *pParentTag = m_aOpenedTag[nParentTag]; const bool cbParentTagAutoInlineClosing = CheckAutoInlineClosing (pParentTag->Tag()); const bool cbParentTagForbidden = CheckForbiddenTag (pParentTag->Tag()); if (!cbParentTagAutoInlineClosing && !cbParentTagAutoInlineClosing) { pTag = pParentTag; // we found parent tag fot text adding cbAutoInlineClosing = false; break; } } } } if (!cbAutoInlineClosing && !cbForbidden) { CString sTagName = pTag->Tag()->getTagName(); pTag->Tag()->addText (rText); // check parent 'pre' tag CHTMLTagWrapper *pParentTag = pTag; bool bPreTag = false; while (NULL != pParentTag) { bPreTag = (0 == pParentTag->Tag()->getTagName().CompareNoCase(_T("pre"))); if (bPreTag) break; pParentTag = pParentTag->getParentTag(); } if (bPreTag || 0 == sTagName.CompareNoCase (_T("textarea"))) // save all tabs, enters and speces for
         and