Swiss Chess Table Cleaner


The tournament manager Swiss Chess, which is widely used in German chess competitions, has a very old-fashioned HTML export. In fact, it is still HTML 1 as usual in the last millenium, when there were practically only 14-, 15- and 17-inch monitors. Today especially the cell phones pose high demands on responsive designs.

An often seen method to circumvent the flaws of the export function is to publish tables as screenshots. This is ugly for two reasons:

  • Fonts do not scale proportionally but degrade.
  • My experience tells me that personal names are quite often searched for. So it lowers your SEO score to not present them to the search engines.

Therefore I coded a script that converts these old fashioned tables in HTML 5 ones. It may well work with tables from other sources but Swiss Chess is the only thing I have tested with. As the main purpose of tables is to visualize the relation between data, we want to avoid horizontal scrolling at all costs. This is achieved by the following means:

  • Remove all hard-coded padding employing non-breakable spaces ( ).
  • Shorten results and scores: 0,5 – 0,5 ½:½.
  • Remove empty colums.
  • Remove columns where all cells contain nothing else than a single divis.
  • Set a couple of CSS selectors into the table that makes it easy to apply your corporate design to the table.
  • Provide a sample CSS file (find a detailed explanation below the upload form).
  • Remove everything outside the <table> tags.


Only one table per file and no nested tables!

HTML explained

Look at the source code of a converted table:

<table id='sctab-Kreuz-R3_html' class='sc_table'>
<caption class="sc_caption">Rangliste: Stand nach der 3. Runde</caption>
<thead class="sc_thead>
<tr class="sc_tr">
<th class='sc_th sc_col1'>Rang</th>
<th class='sc_th sc_col2'>Teilnehmer</th>
<th class='sc_th sc_col4'>TWZ</th>
<th class='sc_th sc_col6'>Verein/Ort</th>
<th class='sc_th sc_col7'>1</th>
<th class='sc_th sc_col8'>2</th>
<th class='sc_th sc_col9'>3</th>
<th class='sc_th sc_col10'>4</th>
<th class='sc_th sc_col11'>Punkte</th>
<th class='sc_th sc_col12'>SoBer</th>
<th class='sc_th sc_col13'>Siege</th>
<tr class="sc_tr">
<td class='sc_cell sc_col1'>1.</td>
<td class='sc_cell sc_col2'>Mustermann,Erika</td>
<td class='sc_cell sc_col4'>1567</td>
<td class='sc_cell sc_col6'>SC Hintertupfingen</td>
<td class='sc_cell sc_col7'>×</td>
<td class='sc_cell sc_col8'>&frac12;</td>
<td class='sc_cell sc_col9'>&frac12;</td>
<td class='sc_cell sc_col10'>1</td>
<td class='sc_cell sc_col11'>2.0</td>
<td class='sc_cell sc_col12'>2.50</td>
<td class='sc_cell sc_col13'>1</td>
<tr class="sc_tr"><td class='sc_cell sc_col1'>1.</td>
<td class='sc_cell sc_col2'>Normalverbraucher,Otto</td>
<td class='sc_cell sc_col4'>1340</td>
<td class='sc_cell sc_col6'>JWD Berlin</td>
<td class='sc_cell sc_col7'>&frac12;</td>
<td class='sc_cell sc_col8'>×</td>
<td class='sc_cell sc_col9'>&frac12;</td>
<td class='sc_cell sc_col10'>1</td>
<td class='sc_cell sc_col11'>2.0</td>
<td class='sc_cell sc_col12'>2.50</td>
<td class='sc_cell sc_col13'>1</td>
<tr class="sc_tr"><td class='sc_cell sc_col1'>3.</td>
<td class='sc_cell sc_col2'>von Kohlen und Reibach</td>
<td class='sc_cell sc_col4'>2145</td>
<td class='sc_cell sc_col6'>SF &quot;Villa H&uuml;gel&quot; Essen</td>
<td class='sc_cell sc_col7'>&frac12;</td>
<td class='sc_cell sc_col8'>&frac12;</td>
<td class='sc_cell sc_col9'>×</td>
<td class='sc_cell sc_col10'>0</td>
<td class='sc_cell sc_col11'>1.0</td>
<td class='sc_cell sc_col12'>2.00</td>
<td class='sc_cell sc_col13'>0</td>
<tr class="sc_tr"><td class='sc_cell sc_col1'>4.</td>
<td class='sc_cell sc_col2'>Doe,John</td>
<td class='sc_cell sc_col4 sc_empty'></td>
<td class='sc_cell sc_col6'>Lasker New York</td>
<td class='sc_cell sc_col7'>0</td>
<td class='sc_cell sc_col8'>0</td>
<td class='sc_cell sc_col9'>1</td>
<td class='sc_cell sc_col10'>×</td>
<td class='sc_cell sc_col11'>1.0</td>
<td class='sc_cell sc_col12'>1.00</td>
<td class='sc_cell sc_col13'>1</td>
  • The <table> tag bears the original file name as ID and class name sc_tab. The ID is sanitized for use as CSS selector. Number sign (#) and dot are substituted by an underscore.
  • The table header (<thead>) is of class sc_head.
  • The <tr> tags (rows) are of class sc_tr.
  • Every cell has a class sc_col that counts its column in the source table. So changed column numbers still match correctly after refreshing. Avoid <colgroup> since it does not work with all CSS properties (fi. text_align). The counting might change from table to table. Use the ID to match various tables on the same page.
  • The th tags (heading cells) are of class sc_th.
  • The data cells (td) are of class sc_cell.
  • Empty cells are of class sc_empty.

A hint for WordPress users: WP quite often complains about invalide HTML in the Custom HTML or Code block even if the source code was autogenerated. In the latter case you can simply ignore it. And WP does not use the <caption> tag with tables but puts them into a <figure> environment and employs <figcaption> there.

CSS explained

Now we can glue some CSS rules to the table for our desired layout:

Swiss Chess Table Cleaner 1
/* Use condensed fonts for a narrow character spacing. Look for font families which have a
/* "condensed" or "narrow" variant. Fi. Bahnschrift (Windows 10), "Noto Sans" (Linux, Android), 
/* Roboto (Linux, Android), "DejaVu Sans" (Linux), Arial (old Windows, MacOS) should cover almost
/* all your visitors. */
.sc_table {
	font-family: Bahnschrift, "Noto Sans", Roboto, "DejaVu Sans", Arial, sans-serif;
	font-stretch: condensed;
	font-size: smaller !important;
/* Allow word wrapping at random postions. The looks ugly, of course. But
/* it is better than horizontal scrolling as the order of data supersedes
/* aesthetics here. */	
	max-width: 100%;
	table-layout: fixed;
	overflow-wrap: break-word;
/* Cells need a small lateral padding. */
.sc_cell, .sc_th {
	padding-left: 2px !important;
	padding-right: 2px !important;
/* Avoid borders with tables! This is a general rule for better typesetting. */
.sc_table, .sc_thead, .sc_tr, .sc_cell, .sc_th {
	border: none;
	border-spacing: 0;
/* If a table is to big to simply leave it blank we can stripe it instead. */
.sc_tr:nth-child(odd) {
	background-color: #CCC;
.sc_tr:nth-child(even) {
	background-color: #FFF;
/* Cell flushing */
.sc_cell {
	text-align: center;
#sctab-Kreuz-R3_html .sc_col1, #sctab-Kreuz-R3_html .sc_col2, #sctab-Kreuz-R3_html .sc_col6 {
	text-align: left;
#sctab-Kreuz-R3_html .html.sc_col11, #sctab-Kreuz-R3_html .sc_col12, #sctab-Kreuz-R3_html .sc_col13 {
	text-align: right;
.sc_th {
	text-align: center;
/* The name colums get a bit more space than the number columns.*/
#sctab-Kreuz-R3_html .sc_col2, #sctab-Kreuz-R3_html .sc_col6 {
	width: 25%;
/* Mark empty fields. */
.sc_empty {


Rangliste: Stand nach der 3. Runde
1.Mustermann,Erika1567SC Hintertupfingen×½½12.02.501
1.Normalverbraucher,Otto1340JWD Berlin½×½12.02.501
3.von Kohlen und Reibach2145SF “Villa Hügel” Essen½½×01.02.000
4.Doe,JohnLasker New York001×1.01.001

Leave a Reply

Your email address will not be published. Required fields are marked *

Post comment