Python Mac Pasteboard Access

I recently discussed the Google prettify tool that I started using to display colour-coded JavaScript source code in HTML.

As I mentioned there, it does a good job for all languages I looked at except C# and VB .NET, where I prefer the colour coding generated by Visual Studio, since that highlights class names defined in referenced .NET assemblies as well as the built-in .NET ones.

To retrieve the Visual Studio colour coding and insert it into my HTML page, I still use CopySourceAsHtml 3.0. It defines some CSS styles for different colours and adds the corresponding HTML span tags around the keywords to highlight them.

On Windows, I post-processed the output using a Python script pycolorize.py to read the CopySourceAsHtml output from the Windows clipboard and convert its CSS style definitions to my own ones already pre-defined in the main blog stylesheet.

Now that I am working almost exclusively on the Mac, I converted this script from Windows to Mac as well.

Thanks to the platform independence provided by Python, the only thing that needs changing is to replace the Windows clipboard access by Mac pasteboard read and write operations.

For Windows, I was making use of a Python Windows API library. Similar libraries are available for Mac as well, but the Unix pbcopy and pbpaste command line utilities offer an even simpler alternative, as demonstrated by the Pyperclip cross-platform copy and paste sample.

Here is my new Mac version of the colour-coded HTML CopySourceAsHtml output cleanup script:

#!/usr/bin/python
# -*- coding: iso-8859-15 -*-
#
# pycolorize.py - massage colorised HTML source code copied from Visual Studio
#
# jeremy tammik, autodesk inc, 2009-02-05
#
# History:
#
# 2009-02-05 initial version
# 2009-05-22 updated to support Visual Studio 2008
# 2011-04-19 updated to support Visual Studio 2010 and CopySourceAsHtml 3.0
# 2013-05-21 migrated to mac os x unix, cf. http://coffeeghost.net/src/pyperclip.py
#
# read a block of text from a file or the windows clipboard
# replace cb[12345] by the appropriate colour
# remove the style and pre tags
#
# example of original text block:
#
# <style type="text/css">
# .cf { font-family: Courier New; font-size: 10pt; color: black; background: white; }
# .cl { margin: 0px; }
# .gray { color: gray; }
# .green { color: green; }
# .blue { color: blue; }
# .teal { color: teal; }
# .maroon { color: maroon; }
# </style>
# <div class="cf">
# <pre class="cl"><span class="gray">///</span><span class="green"> </span><span class="gray"><summary></span></pre>
# <pre class="cl"><span class="gray">///</span><span class="green"> Return the specified double parameter </span></pre>
# <pre class="cl"><span class="gray">///</span><span class="green"> value for the given wall.</span></pre>
# <pre class="cl"><span class="gray">///</span><span class="green"> </span><span class="gray"></summary></span></pre>
# <pre class="cl"><span class="blue">double</span> GetWallParameter( </pre>
# <pre class="cl">  <span class="teal">Wall</span> wall,</pre>
# <pre class="cl">  <span class="teal">BuiltInParameter</span> bip )</pre>
# <pre class="cl">{</pre>
# <pre class="cl">  <span class="teal">Parameter</span> p = wall.get_Parameter( bip );</pre>
# <pre class="cl"> </pre>
# <pre class="cl">  <span class="teal">Debug</span>.Assert( <span class="blue">null</span> != p, </pre>
# <pre class="cl">    <span class="maroon">"expected wall to have "</span></pre>
# <pre class="cl">    + <span class="maroon">"HOST_AREA_COMPUTED and "</span></pre>
# <pre class="cl">    + <span class="maroon">"HOST_VOLUME_COMPUTED parameters"</span> );</pre>
# <pre class="cl"> </pre>
# <pre class="cl">  <span class="blue">return</span> p.AsDouble();</pre>
# <pre class="cl">}</pre>
# </div>
#
# example of resulting text block:
#
# <span class="gray">///</span><span class="green"> </span><span class="gray"><summary></span>
# <span class="gray">///</span><span class="green"> Return the specified double parameter </span>
# <span class="gray">///</span><span class="green"> value for the given wall.</span>
# <span class="gray">///</span><span class="green"> </span><span class="gray"></summary></span>
# <span class="blue">double</span> GetWallParameter(
#   <span class="teal">Wall</span> wall,
#   <span class="teal">BuiltInParameter</span> bip )
# {
#   <span class="teal">Parameter</span> p = wall.get_Parameter( bip );
#  
#   <span class="teal">Debug</span>.Assert( <span class="blue">null</span> != p,
#     <span class="maroon">"expected wall to have "</span>
#     + <span class="maroon">"HOST_AREA_COMPUTED and "</span>
#     + <span class="maroon">"HOST_VOLUME_COMPUTED parameters"</span> );
#  
#   <span class="blue">return</span> p.AsDouble();
# }
#
# 2009-05-22:
#
# example of original text block:
#
# <style type="text/css">
# .cf { font-family: Courier New; font-size: 10pt; color: black; background: white; }
# .cl { margin: 0px; }
# .cb1 { color: blue; }
# .cb2 { color: green; }
# .cb3 { color: #2b91af; }
# .cb4 { color: #a31515; }
# </style>
# <div class="cf">
# <pre class="cl">        <span class="cb1">int</span> n = imports.Count;</pre>
# <pre class="cl">        <span class="cb2">// test</span></pre>
# <pre class="cl"> </pre>
# <pre class="cl">        <span class="cb3">Debug</span>.Print(</pre>
# <pre class="cl">          <span class="cb4">"Family '{0}' contains {1} import instance{2}{3}"</span>,</pre>
# <pre class="cl">          key, n, <span class="cb3">Util</span>.PluralSuffix( n ),</pre>
# <pre class="cl">          <span class="cb3">Util</span>.DotOrColon( n ) );</pre>
# </div>
#
# example of resulting text block:
#
#         <span class="blue">int</span> n = imports.Count;
#         <span class="green">// test</span>
#  
#         <span class="teal">Debug</span>.Print(
#           <span class="maroon">"Family '{0}' contains {1} import instance{2}{3}"</span>,
#           key, n, <span class="teal">Util</span>.PluralSuffix( n ),
#           <span class="teal">Util</span>.DotOrColon( n ) );
#
import os, re

color_map = { '#2b91af' : 'teal', '#a31515' : 'maroon' }

def getTextMac():
  outf = os.popen('pbpaste', 'r')
  content = outf.read()
  outf.close()
  return content

def setTextMac(text):
  outf = os.popen('pbcopy', 'w')
  outf.write(text)
  outf.close()

def getTextWin():
  w.OpenClipboard()
  d = w.GetClipboardData( win32con.CF_TEXT )
  w.CloseClipboard()
  return d

def setTextWin( aType, aString ):
  w.OpenClipboard()
  w.EmptyClipboard()
  w.SetClipboardData( aType, aString )
  w.CloseClipboard()

_regexColor = re.compile( '\.(cb[1-9]) \{ color\: ([#0-9a-z]+); \}' )
_regexStyle = re.compile( '(<style type="text/css">.*</style>\s*<div class="cf">\s*)', re.DOTALL )
_regexEnd = re.compile( '(</pre>\s*</div>)', re.DOTALL )

def replace_cb_by_color( s ):
  "Search for '.cb1 { color: blue; }' and globally replace cb[1-9] by the appropriate colour."
  m = _regexColor.search( s )
  if m:
    a = m.groups()
    if 2 == len( a ):
      color = a[1]
      if color_map.has_key( color ): color = color_map[color]
      return True, s.replace( a[0], color )
  return False, s

def main():
  'Convert Visual Studio CopySourceAsHtml colour styles to a more compact form.'

  s = getTextMac()

  go = True
  while go: go, s = replace_cb_by_color( s )

  m = _regexStyle.match( s )

  if m:
    s = s.replace( m.group( 1 ), '' )
    s = s.replace( '<pre class="cl">', '' )
    m = _regexEnd.search( s )

  if m:
    s = s.replace( m.group( 1 ), '' )
    s = s.strip().replace( '</pre>', '' )

  setTextMac( s )

if __name__ == '__main__':
  main()

Here is the original un-HTML-ised version of this script, pycolorize_mac.py.

Middlesex Fells Reservation

I met up with Harry Mattison of Boost your BIM yesterday, and he suggested that I might like to visit the Fells just a few miles north of Cambridge.

Middlesex Fells Reservation

That was a great idea! Thank you for that, Harry.

I ended up spending all day here on the bank of the lake, reading, sleeping, watching the sun cross the sky, taking an occasional swim. My first day in months of doing almost absolutely nothing.

Friday night I visited Dance Freedom in Cambridge, the original barefoot weekly dance, continuously running for 44 years, according to their own words. I especially enjoyed it, since I regularly participate in similar events in Switzerland. One of them is the Barfussdisco in Basle, which just inaugurated its very own Bafudi facebook page.

Bafudi