htmlpars.cpp 27 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949
  1. /////////////////////////////////////////////////////////////////////////////
  2. // Name: src/html/htmlpars.cpp
  3. // Purpose: wx28HtmlParser class (generic parser)
  4. // Author: Vaclav Slavik
  5. // Copyright: (c) 1999 Vaclav Slavik
  6. // Licence: wxWindows licence
  7. /////////////////////////////////////////////////////////////////////////////
  8. #include "wx/wxprec.h"
  9. #ifdef __BORLANDC__
  10. #pragma hdrstop
  11. #endif
  12. #include "htmlpars.h"
  13. #ifndef WXPRECOMP
  14. #include "wx/dynarray.h"
  15. #include "wx/log.h"
  16. #include "wx/intl.h"
  17. #include "wx/app.h"
  18. #endif
  19. #include "wx/tokenzr.h"
  20. #include "wx/wfstream.h"
  21. #include "wx/url.h"
  22. #include "wx/fontmap.h"
  23. #include "wx/html/htmldefs.h"
  24. #include "wx/arrimpl.cpp"
  25. #ifdef __WXWINCE__
  26. #include "wx/msw/wince/missing.h" // for bsearch()
  27. #endif
  28. // DLL options compatibility check:
  29. WX_CHECK_BUILD_OPTIONS("wxHTML")
  30. const wxChar *wxTRACE_HTML_DEBUG = wxT("htmldebug");
  31. //-----------------------------------------------------------------------------
  32. // wx28HtmlParser helpers
  33. //-----------------------------------------------------------------------------
  34. class wx28HtmlTextPiece
  35. {
  36. public:
  37. wx28HtmlTextPiece(int pos, int lng) : m_pos(pos), m_lng(lng) {}
  38. int m_pos, m_lng;
  39. };
  40. WX_DECLARE_OBJARRAY(wx28HtmlTextPiece, wx28HtmlTextPieces);
  41. WX_DEFINE_OBJARRAY(wx28HtmlTextPieces)
  42. class wx28HtmlParserState
  43. {
  44. public:
  45. wx28HtmlTag *m_curTag;
  46. wx28HtmlTag *m_tags;
  47. wx28HtmlTextPieces *m_textPieces;
  48. int m_curTextPiece;
  49. wxString m_source;
  50. wx28HtmlParserState *m_nextState;
  51. };
  52. //-----------------------------------------------------------------------------
  53. // wx28HtmlParser
  54. //-----------------------------------------------------------------------------
  55. IMPLEMENT_ABSTRACT_CLASS(wx28HtmlParser,wxObject)
  56. wx28HtmlParser::wx28HtmlParser()
  57. : wxObject(), m_HandlersHash(wxKEY_STRING),
  58. m_FS(NULL), m_HandlersStack(NULL)
  59. {
  60. m_entitiesParser = new wx28HtmlEntitiesParser;
  61. m_Tags = NULL;
  62. m_CurTag = NULL;
  63. m_TextPieces = NULL;
  64. m_CurTextPiece = 0;
  65. m_SavedStates = NULL;
  66. }
  67. wx28HtmlParser::~wx28HtmlParser()
  68. {
  69. while (RestoreState()) {}
  70. DestroyDOMTree();
  71. if (m_HandlersStack)
  72. {
  73. wxList& tmp = *m_HandlersStack;
  74. wxList::iterator it, en;
  75. for( it = tmp.begin(), en = tmp.end(); it != en; ++it )
  76. delete (wxHashTable*)*it;
  77. tmp.clear();
  78. }
  79. delete m_HandlersStack;
  80. m_HandlersHash.Clear();
  81. WX_CLEAR_LIST(wxList, m_HandlersList);
  82. delete m_entitiesParser;
  83. }
  84. wxObject* wx28HtmlParser::Parse(const wxString& source)
  85. {
  86. InitParser(source);
  87. DoParsing();
  88. wxObject *result = GetProduct();
  89. DoneParser();
  90. return result;
  91. }
  92. void wx28HtmlParser::InitParser(const wxString& source)
  93. {
  94. SetSource(source);
  95. m_stopParsing = false;
  96. }
  97. void wx28HtmlParser::DoneParser()
  98. {
  99. DestroyDOMTree();
  100. }
  101. void wx28HtmlParser::SetSource(const wxString& src)
  102. {
  103. DestroyDOMTree();
  104. m_Source = src;
  105. CreateDOMTree();
  106. m_CurTag = NULL;
  107. m_CurTextPiece = 0;
  108. }
  109. void wx28HtmlParser::CreateDOMTree()
  110. {
  111. wx28HtmlTagsCache cache(m_Source);
  112. m_TextPieces = new wx28HtmlTextPieces;
  113. CreateDOMSubTree(NULL, 0, m_Source.length(), &cache);
  114. m_CurTextPiece = 0;
  115. }
  116. extern bool wxIsCDATAElement(const wxChar *tag);
  117. void wx28HtmlParser::CreateDOMSubTree(wx28HtmlTag *cur,
  118. int begin_pos, int end_pos,
  119. wx28HtmlTagsCache *cache)
  120. {
  121. if (end_pos <= begin_pos) return;
  122. wxChar c;
  123. int i = begin_pos;
  124. int textBeginning = begin_pos;
  125. // If the tag contains CDATA text, we include the text between beginning
  126. // and ending tag verbosely. Setting i=end_pos will skip to the very
  127. // end of this function where text piece is added, bypassing any child
  128. // tags parsing (CDATA element can't have child elements by definition):
  129. if (cur != NULL && wxIsCDATAElement(cur->GetName().c_str()))
  130. {
  131. i = end_pos;
  132. }
  133. while (i < end_pos)
  134. {
  135. c = m_Source.GetChar(i);
  136. if (c == wxT('<'))
  137. {
  138. // add text to m_TextPieces:
  139. if (i - textBeginning > 0)
  140. m_TextPieces->Add(
  141. wx28HtmlTextPiece(textBeginning, i - textBeginning));
  142. // if it is a comment, skip it:
  143. if (i < end_pos-6 && m_Source.GetChar(i+1) == wxT('!') &&
  144. m_Source.GetChar(i+2) == wxT('-') &&
  145. m_Source.GetChar(i+3) == wxT('-'))
  146. {
  147. // Comments begin with "<!--" and end with "--[ \t\r\n]*>"
  148. // according to HTML 4.0
  149. int dashes = 0;
  150. i += 4;
  151. while (i < end_pos)
  152. {
  153. c = m_Source.GetChar(i++);
  154. if ((c == wxT(' ') || c == wxT('\n') ||
  155. c == wxT('\r') || c == wxT('\t')) && dashes >= 2) {}
  156. else if (c == wxT('>') && dashes >= 2)
  157. {
  158. textBeginning = i;
  159. break;
  160. }
  161. else if (c == wxT('-'))
  162. dashes++;
  163. else
  164. dashes = 0;
  165. }
  166. }
  167. // add another tag to the tree:
  168. else if (i < end_pos-1 && m_Source.GetChar(i+1) != wxT('/'))
  169. {
  170. wx28HtmlTag *chd;
  171. if (cur)
  172. chd = new wx28HtmlTag(cur, m_Source,
  173. i, end_pos, cache, m_entitiesParser);
  174. else
  175. {
  176. chd = new wx28HtmlTag(NULL, m_Source,
  177. i, end_pos, cache, m_entitiesParser);
  178. if (!m_Tags)
  179. {
  180. // if this is the first tag to be created make the root
  181. // m_Tags point to it:
  182. m_Tags = chd;
  183. }
  184. else
  185. {
  186. // if there is already a root tag add this tag as
  187. // the last sibling:
  188. chd->m_Prev = m_Tags->GetLastSibling();
  189. chd->m_Prev->m_Next = chd;
  190. }
  191. }
  192. if (chd->HasEnding())
  193. {
  194. CreateDOMSubTree(chd,
  195. chd->GetBeginPos(), chd->GetEndPos1(),
  196. cache);
  197. i = chd->GetEndPos2();
  198. }
  199. else
  200. i = chd->GetBeginPos();
  201. textBeginning = i;
  202. }
  203. // ... or skip ending tag:
  204. else
  205. {
  206. while (i < end_pos && m_Source.GetChar(i) != wxT('>')) i++;
  207. textBeginning = i+1;
  208. }
  209. }
  210. else i++;
  211. }
  212. // add remaining text to m_TextPieces:
  213. if (end_pos - textBeginning > 0)
  214. m_TextPieces->Add(
  215. wx28HtmlTextPiece(textBeginning, end_pos - textBeginning));
  216. }
  217. void wx28HtmlParser::DestroyDOMTree()
  218. {
  219. wx28HtmlTag *t1, *t2;
  220. t1 = m_Tags;
  221. while (t1)
  222. {
  223. t2 = t1->GetNextSibling();
  224. delete t1;
  225. t1 = t2;
  226. }
  227. m_Tags = m_CurTag = NULL;
  228. delete m_TextPieces;
  229. m_TextPieces = NULL;
  230. }
  231. void wx28HtmlParser::DoParsing()
  232. {
  233. m_CurTag = m_Tags;
  234. m_CurTextPiece = 0;
  235. DoParsing(0, m_Source.length());
  236. }
  237. void wx28HtmlParser::DoParsing(int begin_pos, int end_pos)
  238. {
  239. if (end_pos <= begin_pos) return;
  240. wx28HtmlTextPieces& pieces = *m_TextPieces;
  241. size_t piecesCnt = pieces.GetCount();
  242. while (begin_pos < end_pos)
  243. {
  244. while (m_CurTag && m_CurTag->GetBeginPos() < begin_pos)
  245. m_CurTag = m_CurTag->GetNextTag();
  246. while (m_CurTextPiece < piecesCnt &&
  247. pieces[m_CurTextPiece].m_pos < begin_pos)
  248. m_CurTextPiece++;
  249. if (m_CurTextPiece < piecesCnt &&
  250. (!m_CurTag ||
  251. pieces[m_CurTextPiece].m_pos < m_CurTag->GetBeginPos()))
  252. {
  253. // Add text:
  254. AddText(GetEntitiesParser()->Parse(
  255. m_Source.Mid(pieces[m_CurTextPiece].m_pos,
  256. pieces[m_CurTextPiece].m_lng)).t_str());
  257. begin_pos = pieces[m_CurTextPiece].m_pos +
  258. pieces[m_CurTextPiece].m_lng;
  259. m_CurTextPiece++;
  260. }
  261. else if (m_CurTag)
  262. {
  263. if (m_CurTag->HasEnding())
  264. begin_pos = m_CurTag->GetEndPos2();
  265. else
  266. begin_pos = m_CurTag->GetBeginPos();
  267. wx28HtmlTag *t = m_CurTag;
  268. m_CurTag = m_CurTag->GetNextTag();
  269. AddTag(*t);
  270. if (m_stopParsing)
  271. return;
  272. }
  273. else break;
  274. }
  275. }
  276. void wx28HtmlParser::AddTag(const wx28HtmlTag& tag)
  277. {
  278. wx28HtmlTagHandler *h;
  279. bool inner = false;
  280. h = (wx28HtmlTagHandler*) m_HandlersHash.Get(tag.GetName());
  281. if (h)
  282. {
  283. inner = h->HandleTag(tag);
  284. if (m_stopParsing)
  285. return;
  286. }
  287. if (!inner)
  288. {
  289. if (tag.HasEnding())
  290. DoParsing(tag.GetBeginPos(), tag.GetEndPos1());
  291. }
  292. }
  293. void wx28HtmlParser::AddTagHandler(wx28HtmlTagHandler *handler)
  294. {
  295. wxString s(handler->GetSupportedTags());
  296. wxStringTokenizer tokenizer(s, wxT(", "));
  297. while (tokenizer.HasMoreTokens())
  298. m_HandlersHash.Put(tokenizer.GetNextToken(), handler);
  299. if (m_HandlersList.IndexOf(handler) == wxNOT_FOUND)
  300. m_HandlersList.Append(handler);
  301. handler->SetParser(this);
  302. }
  303. void wx28HtmlParser::PushTagHandler(wx28HtmlTagHandler *handler, const wxString& tags)
  304. {
  305. wxStringTokenizer tokenizer(tags, wxT(", "));
  306. wxString key;
  307. if (m_HandlersStack == NULL)
  308. {
  309. m_HandlersStack = new wxList;
  310. }
  311. m_HandlersStack->Insert((wxObject*)new wxHashTable(m_HandlersHash));
  312. while (tokenizer.HasMoreTokens())
  313. {
  314. key = tokenizer.GetNextToken();
  315. m_HandlersHash.Delete(key);
  316. m_HandlersHash.Put(key, handler);
  317. }
  318. }
  319. void wx28HtmlParser::PopTagHandler()
  320. {
  321. wxList::compatibility_iterator first;
  322. if ( !m_HandlersStack ||
  323. #if wxUSE_STL
  324. !(first = m_HandlersStack->GetFirst())
  325. #else // !wxUSE_STL
  326. ((first = m_HandlersStack->GetFirst()) == NULL)
  327. #endif // wxUSE_STL/!wxUSE_STL
  328. )
  329. {
  330. wxLogWarning(_("Warning: attempt to remove HTML tag handler from empty stack."));
  331. return;
  332. }
  333. m_HandlersHash = *((wxHashTable*) first->GetData());
  334. delete (wxHashTable*) first->GetData();
  335. m_HandlersStack->Erase(first);
  336. }
  337. void wx28HtmlParser::SetSourceAndSaveState(const wxString& src)
  338. {
  339. wx28HtmlParserState *s = new wx28HtmlParserState;
  340. s->m_curTag = m_CurTag;
  341. s->m_tags = m_Tags;
  342. s->m_textPieces = m_TextPieces;
  343. s->m_curTextPiece = m_CurTextPiece;
  344. s->m_source = m_Source;
  345. s->m_nextState = m_SavedStates;
  346. m_SavedStates = s;
  347. m_CurTag = NULL;
  348. m_Tags = NULL;
  349. m_TextPieces = NULL;
  350. m_CurTextPiece = 0;
  351. m_Source = wxEmptyString;
  352. SetSource(src);
  353. }
  354. bool wx28HtmlParser::RestoreState()
  355. {
  356. if (!m_SavedStates) return false;
  357. DestroyDOMTree();
  358. wx28HtmlParserState *s = m_SavedStates;
  359. m_SavedStates = s->m_nextState;
  360. m_CurTag = s->m_curTag;
  361. m_Tags = s->m_tags;
  362. m_TextPieces = s->m_textPieces;
  363. m_CurTextPiece = s->m_curTextPiece;
  364. m_Source = s->m_source;
  365. delete s;
  366. return true;
  367. }
  368. wxString wx28HtmlParser::GetInnerSource(const wx28HtmlTag& tag)
  369. {
  370. return GetSource()->Mid(tag.GetBeginPos(),
  371. tag.GetEndPos1() - tag.GetBeginPos());
  372. }
  373. //-----------------------------------------------------------------------------
  374. // wx28HtmlTagHandler
  375. //-----------------------------------------------------------------------------
  376. IMPLEMENT_ABSTRACT_CLASS(wx28HtmlTagHandler,wxObject)
  377. void wx28HtmlTagHandler::ParseInnerSource(const wxString& source)
  378. {
  379. // It is safe to temporarily change the source being parsed,
  380. // provided we restore the state back after parsing
  381. m_Parser->SetSourceAndSaveState(source);
  382. m_Parser->DoParsing();
  383. m_Parser->RestoreState();
  384. }
  385. //-----------------------------------------------------------------------------
  386. // wx28HtmlEntitiesParser
  387. //-----------------------------------------------------------------------------
  388. IMPLEMENT_DYNAMIC_CLASS(wx28HtmlEntitiesParser,wxObject)
  389. wx28HtmlEntitiesParser::wx28HtmlEntitiesParser()
  390. #if !wxUSE_UNICODE
  391. : m_conv(NULL), m_encoding(wxFONTENCODING_SYSTEM)
  392. #endif
  393. {
  394. }
  395. wx28HtmlEntitiesParser::~wx28HtmlEntitiesParser()
  396. {
  397. #if !wxUSE_UNICODE
  398. delete m_conv;
  399. #endif
  400. }
  401. void wx28HtmlEntitiesParser::SetEncoding(wxFontEncoding encoding)
  402. {
  403. #if !wxUSE_UNICODE
  404. if (encoding == m_encoding)
  405. return;
  406. delete m_conv;
  407. m_encoding = encoding;
  408. if (m_encoding == wxFONTENCODING_SYSTEM)
  409. m_conv = NULL;
  410. else
  411. m_conv = new wxCSConv(wxFontMapper::GetEncodingName(m_encoding));
  412. #else
  413. (void) encoding;
  414. #endif
  415. }
  416. wxString wx28HtmlEntitiesParser::Parse(const wxString& input)
  417. {
  418. const wxChar *c, *last;
  419. const wxChar *in_str = input.c_str();
  420. wxString output;
  421. for (c = in_str, last = in_str; *c != wxT('\0'); c++)
  422. {
  423. if (*c == wxT('&'))
  424. {
  425. if ( output.empty() )
  426. output.reserve(input.length());
  427. if (c - last > 0)
  428. output.append(last, c - last);
  429. if ( *++c == wxT('\0') )
  430. break;
  431. wxString entity;
  432. const wxChar *ent_s = c;
  433. wxChar entity_char;
  434. for (; (*c >= wxT('a') && *c <= wxT('z')) ||
  435. (*c >= wxT('A') && *c <= wxT('Z')) ||
  436. (*c >= wxT('0') && *c <= wxT('9')) ||
  437. *c == wxT('_') || *c == wxT('#'); c++) {}
  438. entity.append(ent_s, c - ent_s);
  439. if (*c != wxT(';')) c--;
  440. last = c+1;
  441. entity_char = GetEntityChar(entity);
  442. if (entity_char)
  443. output << entity_char;
  444. else
  445. {
  446. output.append(ent_s-1, c-ent_s+2);
  447. wxLogTrace(wxTRACE_HTML_DEBUG,
  448. wxT("Unrecognized HTML entity: '%s'"),
  449. entity.c_str());
  450. }
  451. }
  452. }
  453. if (last == in_str) // common case: no entity
  454. return input;
  455. if (*last != wxT('\0'))
  456. output.append(last);
  457. return output;
  458. }
  459. struct wx28HtmlEntityInfo
  460. {
  461. const wxChar *name;
  462. unsigned code;
  463. };
  464. extern "C" int LINKAGEMODE wx28HtmlEntityCompare(const void *key, const void *item)
  465. {
  466. return wxStrcmp((wxChar*)key, ((wx28HtmlEntityInfo*)item)->name);
  467. }
  468. #if !wxUSE_UNICODE
  469. wxChar wx28HtmlEntitiesParser::GetCharForCode(unsigned code)
  470. {
  471. char buf[2];
  472. wchar_t wbuf[2];
  473. wbuf[0] = (wchar_t)code;
  474. wbuf[1] = 0;
  475. wxMBConv *conv = m_conv ? m_conv : &wxConvLocal;
  476. if (conv->WC2MB(buf, wbuf, 2) == (size_t)-1)
  477. return '?';
  478. return buf[0];
  479. }
  480. #endif
  481. wxChar wx28HtmlEntitiesParser::GetEntityChar(const wxString& entity)
  482. {
  483. unsigned code = 0;
  484. if (entity[0] == wxT('#'))
  485. {
  486. const wxChar *ent_s = entity.c_str();
  487. const wxChar *format;
  488. if (ent_s[1] == wxT('x') || ent_s[1] == wxT('X'))
  489. {
  490. format = wxT("%x");
  491. ent_s++;
  492. }
  493. else
  494. format = wxT("%u");
  495. ent_s++;
  496. if (wxSscanf(ent_s, format, &code) != 1)
  497. code = 0;
  498. }
  499. else
  500. {
  501. static wx28HtmlEntityInfo substitutions[] = {
  502. { wxT("AElig"),198 },
  503. { wxT("Aacute"),193 },
  504. { wxT("Acirc"),194 },
  505. { wxT("Agrave"),192 },
  506. { wxT("Alpha"),913 },
  507. { wxT("Aring"),197 },
  508. { wxT("Atilde"),195 },
  509. { wxT("Auml"),196 },
  510. { wxT("Beta"),914 },
  511. { wxT("Ccedil"),199 },
  512. { wxT("Chi"),935 },
  513. { wxT("Dagger"),8225 },
  514. { wxT("Delta"),916 },
  515. { wxT("ETH"),208 },
  516. { wxT("Eacute"),201 },
  517. { wxT("Ecirc"),202 },
  518. { wxT("Egrave"),200 },
  519. { wxT("Epsilon"),917 },
  520. { wxT("Eta"),919 },
  521. { wxT("Euml"),203 },
  522. { wxT("Gamma"),915 },
  523. { wxT("Iacute"),205 },
  524. { wxT("Icirc"),206 },
  525. { wxT("Igrave"),204 },
  526. { wxT("Iota"),921 },
  527. { wxT("Iuml"),207 },
  528. { wxT("Kappa"),922 },
  529. { wxT("Lambda"),923 },
  530. { wxT("Mu"),924 },
  531. { wxT("Ntilde"),209 },
  532. { wxT("Nu"),925 },
  533. { wxT("OElig"),338 },
  534. { wxT("Oacute"),211 },
  535. { wxT("Ocirc"),212 },
  536. { wxT("Ograve"),210 },
  537. { wxT("Omega"),937 },
  538. { wxT("Omicron"),927 },
  539. { wxT("Oslash"),216 },
  540. { wxT("Otilde"),213 },
  541. { wxT("Ouml"),214 },
  542. { wxT("Phi"),934 },
  543. { wxT("Pi"),928 },
  544. { wxT("Prime"),8243 },
  545. { wxT("Psi"),936 },
  546. { wxT("Rho"),929 },
  547. { wxT("Scaron"),352 },
  548. { wxT("Sigma"),931 },
  549. { wxT("THORN"),222 },
  550. { wxT("Tau"),932 },
  551. { wxT("Theta"),920 },
  552. { wxT("Uacute"),218 },
  553. { wxT("Ucirc"),219 },
  554. { wxT("Ugrave"),217 },
  555. { wxT("Upsilon"),933 },
  556. { wxT("Uuml"),220 },
  557. { wxT("Xi"),926 },
  558. { wxT("Yacute"),221 },
  559. { wxT("Yuml"),376 },
  560. { wxT("Zeta"),918 },
  561. { wxT("aacute"),225 },
  562. { wxT("acirc"),226 },
  563. { wxT("acute"),180 },
  564. { wxT("aelig"),230 },
  565. { wxT("agrave"),224 },
  566. { wxT("alefsym"),8501 },
  567. { wxT("alpha"),945 },
  568. { wxT("amp"),38 },
  569. { wxT("and"),8743 },
  570. { wxT("ang"),8736 },
  571. { wxT("aring"),229 },
  572. { wxT("asymp"),8776 },
  573. { wxT("atilde"),227 },
  574. { wxT("auml"),228 },
  575. { wxT("bdquo"),8222 },
  576. { wxT("beta"),946 },
  577. { wxT("brvbar"),166 },
  578. { wxT("bull"),8226 },
  579. { wxT("cap"),8745 },
  580. { wxT("ccedil"),231 },
  581. { wxT("cedil"),184 },
  582. { wxT("cent"),162 },
  583. { wxT("chi"),967 },
  584. { wxT("circ"),710 },
  585. { wxT("clubs"),9827 },
  586. { wxT("cong"),8773 },
  587. { wxT("copy"),169 },
  588. { wxT("crarr"),8629 },
  589. { wxT("cup"),8746 },
  590. { wxT("curren"),164 },
  591. { wxT("dArr"),8659 },
  592. { wxT("dagger"),8224 },
  593. { wxT("darr"),8595 },
  594. { wxT("deg"),176 },
  595. { wxT("delta"),948 },
  596. { wxT("diams"),9830 },
  597. { wxT("divide"),247 },
  598. { wxT("eacute"),233 },
  599. { wxT("ecirc"),234 },
  600. { wxT("egrave"),232 },
  601. { wxT("empty"),8709 },
  602. { wxT("emsp"),8195 },
  603. { wxT("ensp"),8194 },
  604. { wxT("epsilon"),949 },
  605. { wxT("equiv"),8801 },
  606. { wxT("eta"),951 },
  607. { wxT("eth"),240 },
  608. { wxT("euml"),235 },
  609. { wxT("euro"),8364 },
  610. { wxT("exist"),8707 },
  611. { wxT("fnof"),402 },
  612. { wxT("forall"),8704 },
  613. { wxT("frac12"),189 },
  614. { wxT("frac14"),188 },
  615. { wxT("frac34"),190 },
  616. { wxT("frasl"),8260 },
  617. { wxT("gamma"),947 },
  618. { wxT("ge"),8805 },
  619. { wxT("gt"),62 },
  620. { wxT("hArr"),8660 },
  621. { wxT("harr"),8596 },
  622. { wxT("hearts"),9829 },
  623. { wxT("hellip"),8230 },
  624. { wxT("iacute"),237 },
  625. { wxT("icirc"),238 },
  626. { wxT("iexcl"),161 },
  627. { wxT("igrave"),236 },
  628. { wxT("image"),8465 },
  629. { wxT("infin"),8734 },
  630. { wxT("int"),8747 },
  631. { wxT("iota"),953 },
  632. { wxT("iquest"),191 },
  633. { wxT("isin"),8712 },
  634. { wxT("iuml"),239 },
  635. { wxT("kappa"),954 },
  636. { wxT("lArr"),8656 },
  637. { wxT("lambda"),955 },
  638. { wxT("lang"),9001 },
  639. { wxT("laquo"),171 },
  640. { wxT("larr"),8592 },
  641. { wxT("lceil"),8968 },
  642. { wxT("ldquo"),8220 },
  643. { wxT("le"),8804 },
  644. { wxT("lfloor"),8970 },
  645. { wxT("lowast"),8727 },
  646. { wxT("loz"),9674 },
  647. { wxT("lrm"),8206 },
  648. { wxT("lsaquo"),8249 },
  649. { wxT("lsquo"),8216 },
  650. { wxT("lt"),60 },
  651. { wxT("macr"),175 },
  652. { wxT("mdash"),8212 },
  653. { wxT("micro"),181 },
  654. { wxT("middot"),183 },
  655. { wxT("minus"),8722 },
  656. { wxT("mu"),956 },
  657. { wxT("nabla"),8711 },
  658. { wxT("nbsp"),160 },
  659. { wxT("ndash"),8211 },
  660. { wxT("ne"),8800 },
  661. { wxT("ni"),8715 },
  662. { wxT("not"),172 },
  663. { wxT("notin"),8713 },
  664. { wxT("nsub"),8836 },
  665. { wxT("ntilde"),241 },
  666. { wxT("nu"),957 },
  667. { wxT("oacute"),243 },
  668. { wxT("ocirc"),244 },
  669. { wxT("oelig"),339 },
  670. { wxT("ograve"),242 },
  671. { wxT("oline"),8254 },
  672. { wxT("omega"),969 },
  673. { wxT("omicron"),959 },
  674. { wxT("oplus"),8853 },
  675. { wxT("or"),8744 },
  676. { wxT("ordf"),170 },
  677. { wxT("ordm"),186 },
  678. { wxT("oslash"),248 },
  679. { wxT("otilde"),245 },
  680. { wxT("otimes"),8855 },
  681. { wxT("ouml"),246 },
  682. { wxT("para"),182 },
  683. { wxT("part"),8706 },
  684. { wxT("permil"),8240 },
  685. { wxT("perp"),8869 },
  686. { wxT("phi"),966 },
  687. { wxT("pi"),960 },
  688. { wxT("piv"),982 },
  689. { wxT("plusmn"),177 },
  690. { wxT("pound"),163 },
  691. { wxT("prime"),8242 },
  692. { wxT("prod"),8719 },
  693. { wxT("prop"),8733 },
  694. { wxT("psi"),968 },
  695. { wxT("quot"),34 },
  696. { wxT("rArr"),8658 },
  697. { wxT("radic"),8730 },
  698. { wxT("rang"),9002 },
  699. { wxT("raquo"),187 },
  700. { wxT("rarr"),8594 },
  701. { wxT("rceil"),8969 },
  702. { wxT("rdquo"),8221 },
  703. { wxT("real"),8476 },
  704. { wxT("reg"),174 },
  705. { wxT("rfloor"),8971 },
  706. { wxT("rho"),961 },
  707. { wxT("rlm"),8207 },
  708. { wxT("rsaquo"),8250 },
  709. { wxT("rsquo"),8217 },
  710. { wxT("sbquo"),8218 },
  711. { wxT("scaron"),353 },
  712. { wxT("sdot"),8901 },
  713. { wxT("sect"),167 },
  714. { wxT("shy"),173 },
  715. { wxT("sigma"),963 },
  716. { wxT("sigmaf"),962 },
  717. { wxT("sim"),8764 },
  718. { wxT("spades"),9824 },
  719. { wxT("sub"),8834 },
  720. { wxT("sube"),8838 },
  721. { wxT("sum"),8721 },
  722. { wxT("sup"),8835 },
  723. { wxT("sup1"),185 },
  724. { wxT("sup2"),178 },
  725. { wxT("sup3"),179 },
  726. { wxT("supe"),8839 },
  727. { wxT("szlig"),223 },
  728. { wxT("tau"),964 },
  729. { wxT("there4"),8756 },
  730. { wxT("theta"),952 },
  731. { wxT("thetasym"),977 },
  732. { wxT("thinsp"),8201 },
  733. { wxT("thorn"),254 },
  734. { wxT("tilde"),732 },
  735. { wxT("times"),215 },
  736. { wxT("trade"),8482 },
  737. { wxT("uArr"),8657 },
  738. { wxT("uacute"),250 },
  739. { wxT("uarr"),8593 },
  740. { wxT("ucirc"),251 },
  741. { wxT("ugrave"),249 },
  742. { wxT("uml"),168 },
  743. { wxT("upsih"),978 },
  744. { wxT("upsilon"),965 },
  745. { wxT("uuml"),252 },
  746. { wxT("weierp"),8472 },
  747. { wxT("xi"),958 },
  748. { wxT("yacute"),253 },
  749. { wxT("yen"),165 },
  750. { wxT("yuml"),255 },
  751. { wxT("zeta"),950 },
  752. { wxT("zwj"),8205 },
  753. { wxT("zwnj"),8204 },
  754. {NULL, 0}};
  755. static size_t substitutions_cnt = 0;
  756. if (substitutions_cnt == 0)
  757. while (substitutions[substitutions_cnt].code != 0)
  758. substitutions_cnt++;
  759. wx28HtmlEntityInfo *info = NULL;
  760. #ifdef __WXWINCE__
  761. // bsearch crashes under WinCE for some reason
  762. size_t i;
  763. for (i = 0; i < substitutions_cnt; i++)
  764. {
  765. if (entity == substitutions[i].name)
  766. {
  767. info = & substitutions[i];
  768. break;
  769. }
  770. }
  771. #else
  772. info = (wx28HtmlEntityInfo*) bsearch(entity.c_str(), substitutions,
  773. substitutions_cnt,
  774. sizeof(wx28HtmlEntityInfo),
  775. wx28HtmlEntityCompare);
  776. #endif
  777. if (info)
  778. code = info->code;
  779. }
  780. if (code == 0)
  781. return 0;
  782. else
  783. return GetCharForCode(code);
  784. }
  785. wxFSFile *wx28HtmlParser::OpenURL(wx28HtmlURLType WXUNUSED(type),
  786. const wxString& url) const
  787. {
  788. return m_FS ? m_FS->OpenFile(url) : NULL;
  789. }
  790. //-----------------------------------------------------------------------------
  791. // wx28HtmlParser::ExtractCharsetInformation
  792. //-----------------------------------------------------------------------------
  793. class wxMetaTagParser : public wx28HtmlParser
  794. {
  795. public:
  796. wxMetaTagParser() { }
  797. wxObject* GetProduct() { return NULL; }
  798. protected:
  799. virtual void AddText(const wxChar* WXUNUSED(txt)) {}
  800. DECLARE_NO_COPY_CLASS(wxMetaTagParser)
  801. };
  802. class wxMetaTagHandler : public wx28HtmlTagHandler
  803. {
  804. public:
  805. wxMetaTagHandler(wxString *retval) : wx28HtmlTagHandler(), m_retval(retval) {}
  806. wxString GetSupportedTags() { return wxT("META,BODY"); }
  807. bool HandleTag(const wx28HtmlTag& tag);
  808. private:
  809. wxString *m_retval;
  810. DECLARE_NO_COPY_CLASS(wxMetaTagHandler)
  811. };
  812. bool wxMetaTagHandler::HandleTag(const wx28HtmlTag& tag)
  813. {
  814. if (tag.GetName() == wxT("BODY"))
  815. {
  816. m_Parser->StopParsing();
  817. return false;
  818. }
  819. if (tag.HasParam(wxT("HTTP-EQUIV")) &&
  820. tag.GetParam(wxT("HTTP-EQUIV")).IsSameAs(wxT("Content-Type"), false) &&
  821. tag.HasParam(wxT("CONTENT")))
  822. {
  823. wxString content = tag.GetParam(wxT("CONTENT")).Lower();
  824. if (content.Left(19) == wxT("text/html; charset="))
  825. {
  826. *m_retval = content.Mid(19);
  827. m_Parser->StopParsing();
  828. }
  829. }
  830. return false;
  831. }
  832. /*static*/
  833. wxString wx28HtmlParser::ExtractCharsetInformation(const wxString& markup)
  834. {
  835. wxString charset;
  836. wxMetaTagParser *parser = new wxMetaTagParser();
  837. if(parser)
  838. {
  839. parser->AddTagHandler(new wxMetaTagHandler(&charset));
  840. parser->Parse(markup);
  841. delete parser;
  842. }
  843. return charset;
  844. }