Welcome, guest | Sign In | My Account | Store | Cart
#!/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 <inputfile.wiki> [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(('''<html><head><link rel="stylesheet" href="%s" type="text/css" />
</head><body>''' % CSS_FILE).encode(ENC_OUT))

out_file.write(HtmlFormatter(env, context, wiki).generate().encode(ENC_OUT))

out_file.write('''
</body>
</html>
'''.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()

History