#!/usr/bin/env python # Convert trac wiki markup to HTML # Dependencies: trac, install with `easy_install trac` # found on trac google group: # http://groups.google.com/group/trac-dev/browse_thread/thread/2c97c6c514487778?q= import sys import os from trac.test import EnvironmentStub, Mock, MockPerm from trac.mimeview import Context from trac.wiki.formatter import HtmlFormatter from trac.web.href import Href CSS_FILE = 'trac.css' ENC_IN = 'utf8' ENC_OUT = 'utf8' env = EnvironmentStub() req = Mock(href=Href('/'), abs_href=Href('http://www.example.com/'), authname='anonymous', perm=MockPerm(), args={}) context = Context.from_request(req, 'wiki') if len(sys.argv) == 1: print 'Usage %s [outputfile.html]' % sys.argv[0] sys.exit(-1) if len(sys.argv) > 1: try: wiki = open(sys.argv[1]).read().decode(ENC_IN) except IOError: wiki = sys.argv[1] # parse string as tracwiki if not a file else: wiki = '= The Trac Wiki to HTML converter =' if len(sys.argv) > 2: out_file = open(sys.argv[2],'w') else: out_file = sys.stdout out_file.write((''' ''' % CSS_FILE).encode(ENC_OUT)) out_file.write(HtmlFormatter(env, context, wiki).generate().encode(ENC_OUT)) out_file.write(''' '''.encode(ENC_OUT)) if not os.path.exists(CSS_FILE): CSS = ''' body { background: #fff; color: #000; margin: 10px; padding: 0; } body, th, td { font: normal 13px Verdana,Arial,'Bitstream Vera Sans',Helvetica,sans-serif; } h1, h2, h3, h4 { font-family: Arial,Verdana,'Bitstream Vera Sans',Helvetica,sans-serif; font-weight: bold; letter-spacing: -0.018em; page-break-after: avoid; } h1 { font-size: 19px; margin: .15em 1em 0.5em 0 } h2 { font-size: 16px } h3 { font-size: 14px } hr { border: none; border-top: 1px solid #ccb; margin: 2em 0 } address { font-style: normal } img { border: none } .underline { text-decoration: underline } ol.loweralpha { list-style-type: lower-alpha } ol.upperalpha { list-style-type: upper-alpha } ol.lowerroman { list-style-type: lower-roman } ol.upperroman { list-style-type: upper-roman } ol.arabic { list-style-type: decimal } /* Link styles */ :link, :visited { text-decoration: none; color: #b00; border-bottom: 1px dotted #bbb; } :link:hover, :visited:hover { background-color: #eee; color: #555 } h1 :link, h1 :visited ,h2 :link, h2 :visited, h3 :link, h3 :visited, h4 :link, h4 :visited, h5 :link, h5 :visited, h6 :link, h6 :visited { color: inherit; } .trac-rawlink { border-bottom: none } /* Heading anchors */ .anchor:link, .anchor:visited { border: none; color: #d7d7d7; font-size: .8em; vertical-align: text-top; } * > .anchor:link, * > .anchor:visited { visibility: hidden; } h1:hover .anchor, h2:hover .anchor, h3:hover .anchor, h4:hover .anchor, h5:hover .anchor, h6:hover .anchor { visibility: visible; } @media screen { a.ext-link .icon { background: url(../extlink.gif) center center no-repeat; padding-left: 12px; } a.mail-link .icon { background: url(../envelope.png) center center no-repeat; padding-left: 14px; } } /* Forms */ input, textarea, select { margin: 2px } input, select { vertical-align: middle } input[type=button], input[type=submit], input[type=reset] { background: #eee; color: #222; border: 1px outset #ccc; padding: .1em .5em; } input[type=button]:hover, input[type=submit]:hover, input[type=reset]:hover { background: #ccb; } input[type=button][disabled], input[type=submit][disabled], input[type=reset][disabled] { background: #f6f6f6; border-style: solid; color: #999; } input[type=text], input.textwidget, textarea { border: 1px solid #d7d7d7 } input[type=text], input.textwidget { padding: .25em .5em } input[type=text]:focus, input.textwidget:focus, textarea:focus { border: 1px solid #886; } option { border-bottom: 1px dotted #d7d7d7 } fieldset { border: 1px solid #d7d7d7; padding: .5em; margin: 1em 0 } form p.hint, form span.hint { color: #666; font-size: 85%; font-style: italic; margin: .5em 0; padding-left: 1em; } fieldset.iefix { background: transparent; border: none; padding: 0; margin: 0; } * html fieldset.iefix { width: 98% } fieldset.iefix p { margin: 0 } legend { color: #999; padding: 0 .25em; font-size: 90%; font-weight: bold } label.disabled { color: #d7d7d7 } .buttons { margin: .5em .5em .5em 0 } .buttons form, .buttons form div { display: inline } .buttons input { margin: 1em .5em .1em 0 } .inlinebuttons input { font-size: 70%; border-width: 1px; border-style: dotted; margin: 0 .1em; padding: 0.1em; background: none; } /* Header */ #header hr { display: none } #header h1 { margin: 1.5em 0 -1.5em; } #header img { border: none; margin: 0 0 -3em } #header :link, #header :visited, #header :link:hover, #header :visited:hover { background: transparent; color: #555; margin-bottom: 2px; border: none; } #header h1 :link:hover, #header h1 :visited:hover { color: #000 } /* Quick search */ #search { clear: both; font-size: 10px; height: 2.2em; margin: 0 0 1em; text-align: right; } #search input { font-size: 10px } #search label { display: none } /* Navigation */ .nav h2, .nav hr { display: none } .nav ul { font-size: 10px; list-style: none; margin: 0; text-align: right } .nav li { border-right: 1px solid #d7d7d7; display: inline; padding: 0 .75em; white-space: nowrap; } .nav li.last { border-right: none } /* Main navigation bar */ #mainnav { background: #f7f7f7 url(../topbar_gradient.png) 0 0; border: 1px solid #000; font: normal 10px verdana,'Bitstream Vera Sans',helvetica,arial,sans-serif; margin: .66em 0 .33em; padding: .2em 0; } #mainnav li { border-right: none; padding: .25em 0 } #mainnav :link, #mainnav :visited { background: url(../dots.gif) 0 0 no-repeat; border-right: 1px solid #fff; border-bottom: none; border-left: 1px solid #555; color: #000; padding: .2em 20px; } * html #mainnav :link, * html #mainnav :visited { background-position: 1px 0 } #mainnav :link:hover, #mainnav :visited:hover { background-color: #ccc; border-right: 1px solid #ddd; } #mainnav .active :link, #mainnav .active :visited { background: #333 url(../topbar_gradient2.png) 0 0 repeat-x; border-top: none; border-right: 1px solid #000; color: #eee; font-weight: bold; } #mainnav .active :link:hover, #mainnav .active :visited:hover { border-right: 1px solid #000; } /* Context-dependent navigation links */ #ctxtnav { height: 1em } #ctxtnav li ul { background: #f7f7f7; color: #ccc; border: 1px solid; padding: 0; display: inline; margin: 0; } #ctxtnav li li { padding: 0; } #ctxtnav li li :link, #ctxtnav li li :visited { padding: 0 1em } #ctxtnav li li :link:hover, #ctxtnav li li :visited:hover { background: #bba; color: #fff; } /* Alternate links */ #altlinks { clear: both; text-align: center } #altlinks h3 { font-size: 12px; letter-spacing: normal; margin: 0 } #altlinks ul { list-style: none; margin: 0; padding: 0 0 1em } #altlinks li { border-right: 1px solid #d7d7d7; display: inline; font-size: 11px; line-height: 1.5; padding: 0 1em; white-space: nowrap; } #altlinks li.last { border-right: none } #altlinks li :link, #altlinks li :visited { background-repeat: no-repeat; color: #666; border: none; padding: 0 0 2px; } #altlinks li a.ics { background-image: url(../ics.png); padding-left: 22px } #altlinks li a.rss { background-image: url(../feed.png); padding-left: 20px } /* Footer */ #footer { clear: both; color: #bbb; font-size: 10px; border-top: 1px solid; height: 31px; padding: .25em 0; } #footer :link, #footer :visited { color: #bbb; } #footer hr { display: none } #footer #tracpowered { border: 0; float: left } #footer #tracpowered:hover { background: transparent } #footer p { margin: 0 } #footer p.left { float: left; margin-left: 1em; padding: 0 1em; border-left: 1px solid #d7d7d7; border-right: 1px solid #d7d7d7; } #footer p.right { float: right; text-align: right; } #content { padding-bottom: 2em; position: relative } #help { clear: both; color: #999; font-size: 90%; margin: 1em; text-align: right; } #help :link, #help :visited { cursor: help } #help hr { display: none } /* Page preferences form */ #prefs { background: #f7f7f0; border: 1px outset #998; float: right; font-size: 9px; padding: .8em; position: relative; margin: 0 1em 1em; } * html #prefs { width: 26em } /* Set width only for IE */ #prefs input, #prefs select { font-size: 9px; vertical-align: middle } #prefs fieldset { background: transparent; border: none; margin: .5em; padding: 0; } #prefs fieldset legend { background: transparent; color: #000; font-size: 9px; font-weight: normal; margin: 0 0 0 -1.5em; padding: 0; } #prefs .buttons { text-align: right } /* Version information (browser, wiki, attachments) */ #info { margin: 1em 0 0 0; background: #f7f7f0; border: 1px solid #d7d7d7; border-collapse: collapse; border-spacing: 0; clear: both; width: 100%; } #info th, #info td { font-size: 85%; padding: 2px .5em; vertical-align: top } #info th { font-weight: bold; text-align: left; white-space: nowrap } #info td.message { width: 100% } #info .message ul { padding: 0; margin: 0 2em } #info .message p { margin: 0; padding: 0 } /* Wiki */ .wikipage { padding-left: 18px } .wikipage h1, .wikipage h2, .wikipage h3 { margin-left: -18px } a.missing:link, a.missing:visited, a.missing, span.missing, a.forbidden, span.forbidden { color: #998 } a.missing:hover { color: #000 } a.closed:link, a.closed:visited, span.closed { text-decoration: line-through } /* User-selectable styles for blocks */ .important { background: #fcb; border: 1px dotted #d00; color: #500; padding: 0 .5em 0 .5em; margin: .5em; } dl.wiki dt { font-weight: bold } dl.compact dt { float: left; padding-right: .5em } dl.compact dd { margin: 0; padding: 0 } pre.wiki, pre.literal-block { background: #f7f7f7; border: 1px solid #d7d7d7; margin: 1em 1.75em; padding: .25em; overflow: auto; } blockquote.citation { margin: -0.6em 0; border-style: solid; border-width: 0 0 0 2px; padding-left: .5em; border-color: #b44; } .citation blockquote.citation { border-color: #4b4; } .citation .citation blockquote.citation { border-color: #44b; } .citation .citation .citation blockquote.citation { border-color: #c55; } table.wiki { border: 2px solid #ccc; border-collapse: collapse; border-spacing: 0; } table.wiki td { border: 1px solid #ccc; padding: .1em .25em; } .wikitoolbar { margin-top: 0.3em; margin-left: 2px; border: solid #d7d7d7; border-width: 1px 1px 1px 0; height: 18px; width: 234px; } .wikitoolbar :link, .wikitoolbar :visited { background: transparent url(../edit_toolbar.png) no-repeat; border: 1px solid #fff; border-left-color: #d7d7d7; cursor: default; display: block; float: left; width: 24px; height: 16px; } .wikitoolbar :link:hover, .wikitoolbar :visited:hover { background-color: transparent; border: 1px solid #fb2; } .wikitoolbar a#em { background-position: 0 0 } .wikitoolbar a#strong { background-position: 0 -16px } .wikitoolbar a#heading { background-position: 0 -32px } .wikitoolbar a#link { background-position: 0 -48px } .wikitoolbar a#code { background-position: 0 -64px } .wikitoolbar a#hr { background-position: 0 -80px } .wikitoolbar a#np { background-position: 0 -96px } .wikitoolbar a#br { background-position: 0 -112px } .wikitoolbar a#img { background-position: 0 -128px } /* Styles for the form for adding attachments. */ #attachment .field { margin-top: 1.3em } #attachment label { padding-left: .2em } #attachment fieldset { margin-top: 2em } #attachment fieldset .field { float: left; margin: 0 1em .5em 0 } #attachment .options { float: left; padding: 0 0 1em 1em } #attachment br { clear: left } .attachment #preview { margin-top: 1em } /* Styles for the list of attachments. */ #attachments { border: 1px outset #996; padding: 1em } #attachments .attachments { margin-left: 2em; padding: 0 } #attachments dt { display: list-item; list-style: square; } #attachments dd { font-style: italic; margin-left: 0; padding-left: 0; } /* Styles for tabular listings such as those used for displaying directory contents and report results. */ table.listing { clear: both; border-bottom: 1px solid #d7d7d7; border-collapse: collapse; border-spacing: 0; margin-top: 1em; width: 100%; } table.listing th { text-align: left; padding: 0 1em .1em 0; font-size: 12px } table.listing thead { background: #f7f7f0 } table.listing thead th { border: 1px solid #d7d7d7; border-bottom-color: #999; font-size: 11px; font-weight: bold; padding: 2px .5em; vertical-align: bottom; } table.listing thead th :link:hover, table.listing thead th :visited:hover { background-color: transparent; } table.listing thead th a { border: none; padding-right: 12px } table.listing th.asc a, table.listing th.desc a { font-weight: bold } table.listing th.asc a, table.listing th.desc a { background-position: 100% 50%; background-repeat: no-repeat; } table.listing th.asc a { background-image: url(../asc.png) } table.listing th.desc a { background-image: url(../desc.png) } table.listing tbody td, table.listing tbody th { border: 1px dotted #ddd; padding: .3em .5em; vertical-align: top; } table.listing tbody td a:hover, table.listing tbody th a:hover { background-color: transparent; } table.listing tbody tr { border-top: 1px solid #ddd } table.listing tbody tr.even { background-color: #fcfcfc } table.listing tbody tr.odd { background-color: #f7f7f7 } table.listing tbody tr:hover { background: #eed !important } table.listing tbody tr.focus { background: #ddf !important } /* Styles for the page history table (extends the styles for "table.listing") */ #fieldhist td { padding: 0 .5em } #fieldhist td.date, #fieldhist td.diff, #fieldhist td.version, #fieldhist td.author { white-space: nowrap; } #fieldhist td.version { text-align: center } #fieldhist td.comment { width: 100% } /* Auto-completion interface */ .suggestions { background: #fff; border: 1px solid #886; color: #222; } .suggestions ul { font-family: sans-serif; max-height: 20em; min-height: 3em; list-style: none; margin: 0; overflow: auto; padding: 0; width: 440px; } * html .suggestions ul { height: 10em; } .suggestions li { background: #fff; cursor: pointer; padding: 2px 5px } .suggestions li.selected { background: #b9b9b9 } /* Styles for the error page (and rst errors) */ #content.error .message, div.system-message { background: #fdc; border: 2px solid #d00; color: #500; padding: .5em; margin: 1em 0; } #content.error div.message pre, div.system-message pre { margin-left: 1em; overflow: hidden; white-space: normal; } div.system-message p { margin: 0; } div.system-message p.system-message-title { font-weight: bold; } #warning.system-message { background: #ffb; border: 1px solid #000; } #warning.system-message li { list-style-type: square; } #notice.system-message { background: #dfd; border: 1px solid #000; } #notice.system-message li { list-style-type: square; } #content.error form.newticket { display: inline; } #content.error form.newticket textarea { display: none; } #content.error #systeminfo { margin: 1em; width: auto; } #content.error #systeminfo th { font-weight: bold; text-align: right; } #content.error #traceback { margin-left: 1em; } #content.error #traceback :link, #content.error #traceback :visited { border: none; } #content.error #tbtoggle { font-size: 80%; } #content.error #traceback div { margin-left: 1em; } #content.error #traceback h3 { font-size: 95%; margin: .5em 0 0; } #content.error #traceback :link var, #content.error #traceback :visited var { font-family: monospace; font-style: normal; font-weight: bold; } #content.error #traceback span.file { color: #666; font-size: 85%; } #content.error #traceback ul { list-style: none; margin: .5em 0; padding: 0; } #content.error #traceback ol { border: 1px dotted #d7d7d7; color: #999; font-size: 85%; line-height: 1; margin: .5em 0; } #content.error #traceback ol li { white-space: pre; } #content.error #traceback ol li.current { background: #e6e6e6; color: #333; } #content.error #traceback ol li code { color: #666; } #content.error #traceback ol li.current code { color: #000; } #content.error #traceback table { margin: .5em 0 1em; } #content.error #traceback th, #content.error #traceback td { font-size: 85%; padding: 1px; } #content.error #traceback th var { font-family: monospace; font-style: normal; } #content.error #traceback td code { white-space: pre; } #content.error #traceback pre { font-size: 95%; } #content .paging { margin: 0 0 2em; padding: .5em 0 0; font-size: 85%; line-height: 2em; text-align: center; } #content .paging .current { padding: .1em .3em; border: 1px solid #333; background: #999; color: #fff; } #content .paging :link, #content .paging :visited { padding: .1em .3em; border: 1px solid #666; background: transparent; color: #666; } #content .paging :link:hover, #content .paging :visited:hover { background: #999; color: #fff; border-color: #333; } #content .paging .previous a, #content .paging .next a { font-size: 150%; font-weight: bold; border: none; } #content .paging .previous a:hover, #content .paging .next a:hover { background: transparent; color: #666; } #content h2 .numresults { color: #666; font-size: 90%; } /* Styles for search word highlighting */ @media screen { .searchword0 { background: #ff9 } .searchword1 { background: #cfc } .searchword2 { background: #cff } .searchword3 { background: #ccf } .searchword4 { background: #fcf } } @media print { #header, #altlinks, #footer, #help { display: none } .nav, form, .buttons form, form .buttons, form .inlinebuttons, .noprint, .trac-rawlink { display: none; } form.printableform { display: block } } ''' open(CSS_FILE,'w').write(CSS) out_file.close()