feeds.php 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544
  1. <?php if (!defined('PmWiki')) exit();
  2. /* Copyright 2005-2006 Patrick R. Michaud (pmichaud@pobox.com)
  3. This file is part of PmWiki; you can redistribute it and/or modify
  4. it under the terms of the GNU General Public License as published
  5. by the Free Software Foundation; either version 2 of the License, or
  6. (at your option) any later version. See pmwiki.php for full details.
  7. This script provides a number of syndication feed and xml-based
  8. metadata options to PmWiki, including Atom, RSS 2.0, RSS 1.0 (RDF),
  9. and the Dublin Core Metadata extensions. This module is typically
  10. activated from a local configuration file via a line such as
  11. if ($action == 'atom') include_once("$FarmD/scripts/feeds.php");
  12. if ($action == 'dc') include_once("$FarmD/scripts/feeds.php");
  13. When enabled, ?action=atom, ?action=rss, and ?action=rdf produce
  14. syndication feeds based on any wikitrail contained in the page,
  15. or, for Category pages, on the pages in the category. The feeds
  16. are generated using pagelist, thus one can include parameters such
  17. as count=, list=, order=, etc. in the url to adjust the feed output.
  18. ?action=dc will normally generate Dublin Core Metadata for the
  19. current page only, but placing a group=, trail=, or link= argument
  20. in the url causes it to generate metadata for all pages in the
  21. associated group, trail, or backlink.
  22. There are a large number of customizations available, most of which
  23. are controlled by the $FeedFmt array. Elements $FeedFmt look like
  24. $FeedFmt['atom']['feed']['rights'] = 'All Rights Reserved';
  25. where the first index corresponds to the action (?action=atom),
  26. the second index indicates a per-feed or per-item element, and
  27. the third index is the name of the element being generated.
  28. The above setting would therefore generate a
  29. "<rights>All Rights Reserved</rights>" in the feed for
  30. ?action=atom. If the value of an entry begins with a '<',
  31. then feeds.php doesn't automatically add the tag around it.
  32. Elements can also be callable functions which are called to
  33. generate the appropriate output.
  34. For example, to set the RSS 2.0 <author> element to the
  35. value of the last author to modify a page, one can set
  36. (in local/config.php):
  37. $FeedFmt['rss']['item']['author'] = '$LastModifiedBy';
  38. To use the RSS 2.0 <description> element to contain the
  39. change summary of the most recent edit, set
  40. $FeedFmt['rss']['item']['description'] = '$LastModifiedSummary';
  41. Feeds.php can also be combined with attachments to support
  42. podcasting via ?action=rss. Any page such as "PageName"
  43. that has an mp3 attachment with the same name as the page
  44. ("PageName.mp3") will have an appropriate <enclosure> element
  45. in the feed output. The set of allowed attachments can be
  46. extended using the $RSSEnclosureFmt array:
  47. $RSSEnclosureFmt = array('{$Name}.mp3', '{$Name}.mp4');
  48. References:
  49. http://www.atomenabled.org/developers/syndication/
  50. http://dublincore.org/documents/dcmes-xml/
  51. http://en.wikipedia.org/wiki/Podcasting
  52. */
  53. ## Settings for ?action=atom
  54. SDVA($FeedFmt['atom']['feed'], array(
  55. '_header' => 'Content-type: text/xml; charset="$Charset"',
  56. '_start' => '<?xml version="1.0" encoding="$Charset"?'.'>
  57. <feed xmlns="http://www.w3.org/2005/Atom">'."\n",
  58. '_end' => "</feed>\n",
  59. 'title' => '$WikiTitle',
  60. 'link' => '<link rel="self" href="{$PageUrl}?action=atom" />',
  61. 'id' => '{$PageUrl}?action=atom',
  62. 'updated' => '$FeedISOTime',
  63. 'author' => "<author><name>$WikiTitle</name></author>\n",
  64. 'generator' => '$Version',
  65. 'logo' => '$PageLogoUrl'));
  66. SDVA($FeedFmt['atom']['item'], array(
  67. '_start' => "<entry>\n",
  68. 'id' => '{$PageUrl}',
  69. 'title' => '{$Title}',
  70. 'updated' => '$ItemISOTime',
  71. 'link' => "<link rel=\"alternate\" href=\"{\$PageUrl}\" />\n",
  72. 'author' => "<author><name>{\$LastModifiedBy}</name></author>\n",
  73. 'summary' => '{$Description}',
  74. 'category' => "<category term=\"\$Category\" />\n",
  75. '_end' => "</entry>\n"));
  76. ## Settings for ?action=dc
  77. SDVA($FeedFmt['dc']['feed'], array(
  78. '_header' => 'Content-type: text/xml; charset="$Charset"',
  79. '_start' => '<?xml version="1.0" encoding="$Charset"?'.'>
  80. <!DOCTYPE rdf:RDF PUBLIC "-//DUBLIN CORE//DCMES DTD 2002/07/31//EN"
  81. "http://dublincore.org/documents/2002/07/31/dcmes-xml/dcmes-xml-dtd.dtd">
  82. <rdf:RDF xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
  83. xmlns:dc="http://purl.org/dc/elements/1.1/">'."\n",
  84. '_end' => "</rdf:RDF>\n"));
  85. SDVA($FeedFmt['dc']['item'], array(
  86. '_start' => "<rdf:Description rdf:about=\"{\$PageUrl}\">\n",
  87. 'dc:title' => '{$Title}',
  88. 'dc:identifier' => '{$PageUrl}',
  89. 'dc:date' => '$ItemISOTime',
  90. 'dc:type' => 'Text',
  91. 'dc:format' => 'text/html',
  92. 'dc:description' => '{$Description}',
  93. 'dc:subject' => "<dc:subject>\$Category</dc:subject>\n",
  94. 'dc:publisher' => '$WikiTitle',
  95. 'dc:author' => '{$LastModifiedBy}',
  96. '_end' => "</rdf:Description>\n"));
  97. ## RSS 2.0 settings for ?action=rss
  98. SDVA($FeedFmt['rss']['feed'], array(
  99. '_header' => 'Content-type: text/xml; charset="$Charset"',
  100. '_start' => '<?xml version="1.0" encoding="$Charset"?'.'>
  101. <rss version="2.0" xmlns:dc="http://purl.org/dc/elements/1.1/">
  102. <channel>'."\n",
  103. '_end' => "</channel>\n</rss>\n",
  104. 'title' => '$WikiTitle | {$Group} / {$Title}',
  105. 'link' => '{$PageUrl}?action=rss',
  106. 'description' => '{$Group}.{$Title}',
  107. 'lastBuildDate' => '$FeedRSSTime'));
  108. SDVA($FeedFmt['rss']['item'], array(
  109. '_start' => "<item>\n",
  110. '_end' => "</item>\n",
  111. 'title' => '{$Group} / {$Title}',
  112. 'link' => '{$PageUrl}',
  113. 'description' => '{$Description}',
  114. 'dc:contributor' => '{$LastModifiedBy}',
  115. 'dc:date' => '$ItemISOTime',
  116. 'pubDate' => '$ItemRSSTime',
  117. 'enclosure' => 'RSSEnclosure'));
  118. ## RDF 1.0, for ?action=rdf
  119. SDVA($FeedFmt['rdf']['feed'], array(
  120. '_header' => 'Content-type: text/xml; charset="$Charset"',
  121. '_start' => '<?xml version="1.0" encoding="$Charset"?'.'>
  122. <rdf:RDF xmlns="http://purl.org/rss/1.0/"
  123. xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
  124. xmlns:dc="http://purl.org/dc/elements/1.1/">
  125. <channel rdf:about="{$PageUrl}?action=rdf">'."\n",
  126. 'title' => '$WikiTitle | {$Group} / {$Title}',
  127. 'link' => '{$PageUrl}?action=rdf',
  128. 'description' => '{$Group}.{$Title}',
  129. 'dc:date' => '$FeedISOTime',
  130. 'items' => "<items>\n<rdf:Seq>\n\$FeedRDFSeq</rdf:Seq>\n</items>\n",
  131. '_items' => "</channel>\n",
  132. '_end' => "</rdf:RDF>\n"));
  133. SDVA($FeedFmt['rdf']['item'], array(
  134. '_start' => "<item rdf:about=\"{\$PageUrl}\">\n",
  135. '_end' => "</item>\n",
  136. 'title' => '$WikiTitle | {$Group} / {$Title}',
  137. 'link' => '{$PageUrl}',
  138. 'description' => '{$Description}',
  139. 'dc:date' => '$ItemISOTime'));
  140. foreach(array_keys($FeedFmt) as $k) {
  141. SDV($HandleActions[$k], 'HandleFeed');
  142. SDV($HandleAuth[$k], 'read');
  143. }
  144. function HandleFeed($pagename, $auth = 'read') {
  145. global $FeedFmt, $action, $PCache, $FmtV, $ISOTimeFmt, $RSSTimeFmt,
  146. $FeedOpt, $FeedDescPatterns, $CategoryGroup, $EntitiesTable;
  147. SDV($ISOTimeFmt, '%Y-%m-%dT%H:%M:%SZ');
  148. SDV($RSSTimeFmt, 'D, d M Y H:i:s \G\M\T');
  149. SDV($FeedDescPatterns,
  150. array('/<[^>]*$/' => ' ', '/\\w+$/' => '', '/<[^>]+>/' => ''));
  151. SDVA($FeedPageListOpt, array());
  152. SDVA($FeedCategoryOpt, array('link' => $pagename));
  153. SDVA($FeedTrailOpt, array('trail' => $pagename, 'count' => 10));
  154. $f = $FeedFmt[$action];
  155. $page = RetrieveAuthPage($pagename, $auth, true, READPAGE_CURRENT);
  156. if (!$page) Abort("?cannot generate feed");
  157. $feedtime = $page['time'];
  158. # determine list of pages to display
  159. if (@($_REQUEST['trail'] || $_REQUEST['group'] || $_REQUEST['link']
  160. || $_REQUEST['name']))
  161. $opt = $FeedPageListOpt;
  162. else if (preg_match("/^$CategoryGroup\\./", $pagename))
  163. $opt = $FeedCategoryOpt;
  164. else if ($action != 'dc') $opt = $FeedTrailOpt;
  165. else {
  166. PCache($pagename, $page);
  167. $pagelist = array($pagename);
  168. }
  169. if (!$pagelist) {
  170. $opt = array_merge($opt, @$_REQUEST);
  171. $pagelist = MakePageList($pagename, $opt, 0);
  172. }
  173. # process list of pages in feed
  174. $rdfseq = '';
  175. $pl = array();
  176. foreach($pagelist as $pn) {
  177. if (!PageExists($pn)) continue;
  178. if (!isset($PCache[$pn]['time']))
  179. { $page = ReadPage($pn, READPAGE_CURRENT); PCache($pn, $page); }
  180. $pc = & $PCache[$pn];
  181. $pl[] = $pn;
  182. if (@$opt['count'] && count($pl) >= $opt['count']) break;
  183. $rdfseq .= FmtPageName("<rdf:li resource=\"{\$PageUrl}\" />\n", $pn);
  184. if ($pc['time'] > $feedtime) $feedtime = $pc['time'];
  185. }
  186. $pagelist = $pl;
  187. $FmtV['$FeedRDFSeq'] = $rdfseq;
  188. $FmtV['$FeedISOTime'] = gmstrftime($ISOTimeFmt, $feedtime);
  189. $FmtV['$FeedRSSTime'] = gmdate($RSSTimeFmt, $feedtime);
  190. # format start of feed
  191. $out = FmtPageName($f['feed']['_start'], $pagename);
  192. # format feed elements
  193. foreach($f['feed'] as $k => $v) {
  194. if ($k{0} == '_' || !$v) continue;
  195. $x = FmtPageName($v, $pagename);
  196. if (!$x) continue;
  197. $out .= ($v{0} == '<') ? $x : "<$k>$x</$k>\n";
  198. }
  199. # format items in feed
  200. if (@$f['feed']['_items'])
  201. $out .= FmtPageName($f['feed']['_items'], $pagename);
  202. foreach($pagelist as $pn) {
  203. $page = &$PCache[$pn];
  204. $FmtV['$ItemDesc'] = @$page['description'];
  205. $FmtV['$ItemISOTime'] = gmstrftime($ISOTimeFmt, $page['time']);
  206. $FmtV['$ItemRSSTime'] = gmdate($RSSTimeFmt, $page['time']);
  207. $out .= FmtPageName($f['item']['_start'], $pn);
  208. foreach((array)@$f['item'] as $k => $v) {
  209. if ($k{0} == '_' || !$v) continue;
  210. if (is_callable($v)) { $out .= $v($pn, $page, $k); continue; }
  211. if (strpos($v, '$LastModifiedBy') !== false && !@$page['author'])
  212. continue;
  213. if (strpos($v, '$Category') !== false) {
  214. if (preg_match_all("/(?<=^|,)$CategoryGroup\\.([^,]+)/",
  215. @$page['targets'], $match)) {
  216. foreach($match[1] as $c) {
  217. $FmtV['$Category'] = $c;
  218. $out .= FmtPageName($v, $pn);
  219. }
  220. }
  221. continue;
  222. }
  223. $x = FmtPageName($v, $pn);
  224. if (!$x) continue;
  225. $out .= ($v{0} == '<') ? $x : "<$k>$x</$k>\n";
  226. }
  227. $out .= FmtPageName($f['item']['_end'], $pn);
  228. }
  229. $out .= FmtPageName($f['feed']['_end'], $pagename);
  230. foreach((array)@$f['feed']['_header'] as $fmt)
  231. header(FmtPageName($fmt, $pagename));
  232. print str_replace(array_keys($EntitiesTable),
  233. array_values($EntitiesTable), $out);
  234. }
  235. ## RSSEnclosure is called in ?action=rss to generate <enclosure>
  236. ## tags for any pages that have an attached "PageName.mp3" file.
  237. ## The set of attachments to enclose is given by $RSSEnclosureFmt.
  238. function RSSEnclosure($pagename, &$page, $k) {
  239. global $RSSEnclosureFmt, $UploadFileFmt, $UploadExts;
  240. if (!function_exists('MakeUploadName')) return '';
  241. SDV($RSSEnclosureFmt, array('{$Name}.mp3'));
  242. $encl = '';
  243. foreach((array)$RSSEnclosureFmt as $fmt) {
  244. $path = FmtPageName($fmt, $pagename);
  245. $upname = MakeUploadName($pagename, $path);
  246. $filepath = FmtPageName("$UploadFileFmt/$upname", $pagename);
  247. if (file_exists($filepath)) {
  248. $length = filesize($filepath);
  249. $type = @$UploadExts[preg_replace('/.*\\./', '', $filepath)];
  250. $url = LinkUpload($pagename, 'Attach:', $path, '', '', '$LinkUrl');
  251. $encl .= "<$k url='$url' length='$length' type='$type' />";
  252. }
  253. }
  254. return $encl;
  255. }
  256. ## Since most feeds don't understand html character entities, we
  257. ## convert the common ones to their numeric form here.
  258. SDVA($EntitiesTable, array(
  259. # entities defined in "http://www.w3.org/TR/xhtml1/DTD/xhtml-lat1.ent"
  260. '&nbsp;' => '&#160;',
  261. '&iexcl;' => '&#161;',
  262. '&cent;' => '&#162;',
  263. '&pound;' => '&#163;',
  264. '&curren;' => '&#164;',
  265. '&yen;' => '&#165;',
  266. '&brvbar;' => '&#166;',
  267. '&sect;' => '&#167;',
  268. '&uml;' => '&#168;',
  269. '&copy;' => '&#169;',
  270. '&ordf;' => '&#170;',
  271. '&laquo;' => '&#171;',
  272. '&not;' => '&#172;',
  273. '&shy;' => '&#173;',
  274. '&reg;' => '&#174;',
  275. '&macr;' => '&#175;',
  276. '&deg;' => '&#176;',
  277. '&plusmn;' => '&#177;',
  278. '&sup2;' => '&#178;',
  279. '&sup3;' => '&#179;',
  280. '&acute;' => '&#180;',
  281. '&micro;' => '&#181;',
  282. '&para;' => '&#182;',
  283. '&middot;' => '&#183;',
  284. '&cedil;' => '&#184;',
  285. '&sup1;' => '&#185;',
  286. '&ordm;' => '&#186;',
  287. '&raquo;' => '&#187;',
  288. '&frac14;' => '&#188;',
  289. '&frac12;' => '&#189;',
  290. '&frac34;' => '&#190;',
  291. '&iquest;' => '&#191;',
  292. '&Agrave;' => '&#192;',
  293. '&Aacute;' => '&#193;',
  294. '&Acirc;' => '&#194;',
  295. '&Atilde;' => '&#195;',
  296. '&Auml;' => '&#196;',
  297. '&Aring;' => '&#197;',
  298. '&AElig;' => '&#198;',
  299. '&Ccedil;' => '&#199;',
  300. '&Egrave;' => '&#200;',
  301. '&Eacute;' => '&#201;',
  302. '&Ecirc;' => '&#202;',
  303. '&Euml;' => '&#203;',
  304. '&Igrave;' => '&#204;',
  305. '&Iacute;' => '&#205;',
  306. '&Icirc;' => '&#206;',
  307. '&Iuml;' => '&#207;',
  308. '&ETH;' => '&#208;',
  309. '&Ntilde;' => '&#209;',
  310. '&Ograve;' => '&#210;',
  311. '&Oacute;' => '&#211;',
  312. '&Ocirc;' => '&#212;',
  313. '&Otilde;' => '&#213;',
  314. '&Ouml;' => '&#214;',
  315. '&times;' => '&#215;',
  316. '&Oslash;' => '&#216;',
  317. '&Ugrave;' => '&#217;',
  318. '&Uacute;' => '&#218;',
  319. '&Ucirc;' => '&#219;',
  320. '&Uuml;' => '&#220;',
  321. '&Yacute;' => '&#221;',
  322. '&THORN;' => '&#222;',
  323. '&szlig;' => '&#223;',
  324. '&agrave;' => '&#224;',
  325. '&aacute;' => '&#225;',
  326. '&acirc;' => '&#226;',
  327. '&atilde;' => '&#227;',
  328. '&auml;' => '&#228;',
  329. '&aring;' => '&#229;',
  330. '&aelig;' => '&#230;',
  331. '&ccedil;' => '&#231;',
  332. '&egrave;' => '&#232;',
  333. '&eacute;' => '&#233;',
  334. '&ecirc;' => '&#234;',
  335. '&euml;' => '&#235;',
  336. '&igrave;' => '&#236;',
  337. '&iacute;' => '&#237;',
  338. '&icirc;' => '&#238;',
  339. '&iuml;' => '&#239;',
  340. '&eth;' => '&#240;',
  341. '&ntilde;' => '&#241;',
  342. '&ograve;' => '&#242;',
  343. '&oacute;' => '&#243;',
  344. '&ocirc;' => '&#244;',
  345. '&otilde;' => '&#245;',
  346. '&ouml;' => '&#246;',
  347. '&divide;' => '&#247;',
  348. '&oslash;' => '&#248;',
  349. '&ugrave;' => '&#249;',
  350. '&uacute;' => '&#250;',
  351. '&ucirc;' => '&#251;',
  352. '&uuml;' => '&#252;',
  353. '&yacute;' => '&#253;',
  354. '&thorn;' => '&#254;',
  355. '&yuml;' => '&#255;',
  356. # entities defined in "http://www.w3.org/TR/xhtml1/DTD/xhtml-special.ent"
  357. '&quot;' => '&#34;',
  358. #'&amp;' => '&#38;#38;',
  359. #'&lt;' => '&#38;#60;',
  360. #'&gt;' => '&#62;',
  361. '&apos;' => '&#39;',
  362. '&OElig;' => '&#338;',
  363. '&oelig;' => '&#339;',
  364. '&Scaron;' => '&#352;',
  365. '&scaron;' => '&#353;',
  366. '&Yuml;' => '&#376;',
  367. '&circ;' => '&#710;',
  368. '&tilde;' => '&#732;',
  369. '&ensp;' => '&#8194;',
  370. '&emsp;' => '&#8195;',
  371. '&thinsp;' => '&#8201;',
  372. '&zwnj;' => '&#8204;',
  373. '&zwj;' => '&#8205;',
  374. '&lrm;' => '&#8206;',
  375. '&rlm;' => '&#8207;',
  376. '&ndash;' => '&#8211;',
  377. '&mdash;' => '&#8212;',
  378. '&lsquo;' => '&#8216;',
  379. '&rsquo;' => '&#8217;',
  380. '&sbquo;' => '&#8218;',
  381. '&ldquo;' => '&#8220;',
  382. '&rdquo;' => '&#8221;',
  383. '&bdquo;' => '&#8222;',
  384. '&dagger;' => '&#8224;',
  385. '&Dagger;' => '&#8225;',
  386. '&permil;' => '&#8240;',
  387. '&lsaquo;' => '&#8249;',
  388. '&rsaquo;' => '&#8250;',
  389. '&euro;' => '&#8364;',
  390. # entities defined in "http://www.w3.org/TR/xhtml1/DTD/xhtml-symbol.ent"
  391. '&fnof;' => '&#402;',
  392. '&Alpha;' => '&#913;',
  393. '&Beta;' => '&#914;',
  394. '&Gamma;' => '&#915;',
  395. '&Delta;' => '&#916;',
  396. '&Epsilon;' => '&#917;',
  397. '&Zeta;' => '&#918;',
  398. '&Eta;' => '&#919;',
  399. '&Theta;' => '&#920;',
  400. '&Iota;' => '&#921;',
  401. '&Kappa;' => '&#922;',
  402. '&Lambda;' => '&#923;',
  403. '&Mu;' => '&#924;',
  404. '&Nu;' => '&#925;',
  405. '&Xi;' => '&#926;',
  406. '&Omicron;' => '&#927;',
  407. '&Pi;' => '&#928;',
  408. '&Rho;' => '&#929;',
  409. '&Sigma;' => '&#931;',
  410. '&Tau;' => '&#932;',
  411. '&Upsilon;' => '&#933;',
  412. '&Phi;' => '&#934;',
  413. '&Chi;' => '&#935;',
  414. '&Psi;' => '&#936;',
  415. '&Omega;' => '&#937;',
  416. '&alpha;' => '&#945;',
  417. '&beta;' => '&#946;',
  418. '&gamma;' => '&#947;',
  419. '&delta;' => '&#948;',
  420. '&epsilon;' => '&#949;',
  421. '&zeta;' => '&#950;',
  422. '&eta;' => '&#951;',
  423. '&theta;' => '&#952;',
  424. '&iota;' => '&#953;',
  425. '&kappa;' => '&#954;',
  426. '&lambda;' => '&#955;',
  427. '&mu;' => '&#956;',
  428. '&nu;' => '&#957;',
  429. '&xi;' => '&#958;',
  430. '&omicron;' => '&#959;',
  431. '&pi;' => '&#960;',
  432. '&rho;' => '&#961;',
  433. '&sigmaf;' => '&#962;',
  434. '&sigma;' => '&#963;',
  435. '&tau;' => '&#964;',
  436. '&upsilon;' => '&#965;',
  437. '&phi;' => '&#966;',
  438. '&chi;' => '&#967;',
  439. '&psi;' => '&#968;',
  440. '&omega;' => '&#969;',
  441. '&thetasym;' => '&#977;',
  442. '&upsih;' => '&#978;',
  443. '&piv;' => '&#982;',
  444. '&bull;' => '&#8226;',
  445. '&hellip;' => '&#8230;',
  446. '&prime;' => '&#8242;',
  447. '&Prime;' => '&#8243;',
  448. '&oline;' => '&#8254;',
  449. '&frasl;' => '&#8260;',
  450. '&weierp;' => '&#8472;',
  451. '&image;' => '&#8465;',
  452. '&real;' => '&#8476;',
  453. '&trade;' => '&#8482;',
  454. '&alefsym;' => '&#8501;',
  455. '&larr;' => '&#8592;',
  456. '&uarr;' => '&#8593;',
  457. '&rarr;' => '&#8594;',
  458. '&darr;' => '&#8595;',
  459. '&harr;' => '&#8596;',
  460. '&crarr;' => '&#8629;',
  461. '&lArr;' => '&#8656;',
  462. '&uArr;' => '&#8657;',
  463. '&rArr;' => '&#8658;',
  464. '&dArr;' => '&#8659;',
  465. '&hArr;' => '&#8660;',
  466. '&forall;' => '&#8704;',
  467. '&part;' => '&#8706;',
  468. '&exist;' => '&#8707;',
  469. '&empty;' => '&#8709;',
  470. '&nabla;' => '&#8711;',
  471. '&isin;' => '&#8712;',
  472. '&notin;' => '&#8713;',
  473. '&ni;' => '&#8715;',
  474. '&prod;' => '&#8719;',
  475. '&sum;' => '&#8721;',
  476. '&minus;' => '&#8722;',
  477. '&lowast;' => '&#8727;',
  478. '&radic;' => '&#8730;',
  479. '&prop;' => '&#8733;',
  480. '&infin;' => '&#8734;',
  481. '&ang;' => '&#8736;',
  482. '&and;' => '&#8743;',
  483. '&or;' => '&#8744;',
  484. '&cap;' => '&#8745;',
  485. '&cup;' => '&#8746;',
  486. '&int;' => '&#8747;',
  487. '&there4;' => '&#8756;',
  488. '&sim;' => '&#8764;',
  489. '&cong;' => '&#8773;',
  490. '&asymp;' => '&#8776;',
  491. '&ne;' => '&#8800;',
  492. '&equiv;' => '&#8801;',
  493. '&le;' => '&#8804;',
  494. '&ge;' => '&#8805;',
  495. '&sub;' => '&#8834;',
  496. '&sup;' => '&#8835;',
  497. '&nsub;' => '&#8836;',
  498. '&sube;' => '&#8838;',
  499. '&supe;' => '&#8839;',
  500. '&oplus;' => '&#8853;',
  501. '&otimes;' => '&#8855;',
  502. '&perp;' => '&#8869;',
  503. '&sdot;' => '&#8901;',
  504. '&lceil;' => '&#8968;',
  505. '&rceil;' => '&#8969;',
  506. '&lfloor;' => '&#8970;',
  507. '&rfloor;' => '&#8971;',
  508. '&lang;' => '&#9001;',
  509. '&rang;' => '&#9002;',
  510. '&loz;' => '&#9674;',
  511. '&spades;' => '&#9824;',
  512. '&clubs;' => '&#9827;',
  513. '&hearts;' => '&#9829;',
  514. '&diams;' => '&#9830;'));