xd is a crossword puzzle processing pipeline. It defines the .xd plain-text format and provides tools to convert from other formats (puz, ipuz, XML, JSON) into .xd.
xdfile/— Main Python package: parser, converter modules, utilitiesxdfile/tests/— pytest testscrossword/— Local fork of thecrosswordPython library (used by puz2xd)scripts/— Utility scripts for processing and web servingdoc/— Documentation includingxd-format.md(the format spec)gxd/— puzzle archive (this is a different git repo that is cloned into this location by scripts).
xdfile/xdfile.py— Corexdfileclass: parse_xd(), to_unicode(), grid/clue iterationxdfile/utils.py— Utilities: parse_pathname(), parse_date_from_filename(), file I/Oxdfile/puz2xd.py— .puz → .xd converter (module version, used as library)puz2xd-standalone.py— .puz → .xd converter (standalone script, duplicates decode/xdfile logic)xdfile/__init__.py— Justfrom .xdfile import *
Sections separated by double blank lines:
- Headers (key: value pairs)
- Grid (rows of characters,
#= block) - Clues (
A1. Clue text ~ ANSWER) - Notes (optional)
puz2xd-standalone.pyduplicates thexdfileclass anddecode()fromxdfile/puz2xd.py— changes to one should be mirrored in the other- The
decode()function handles character encoding cleanup for .puz files. It chains: byte replacements → urllib.parse.unquote → html.unescape → invalid entity cleanup to_unicode()automatically inserts blank lines between clue direction changes (A→D), soparse_xd()should NOT insert clue breaks for blank lines between directionsappend_clue_break()adds(('', ''), '', '')sentinel entries to the clues list — used as separators, not real clues
# Needs venv with dependencies
python -m venv .venv && source .venv/bin/activate
pip install -r requirements.txt
pip install pytest
pytest xdfile/tests/