diff --git a/README.md b/README.md
index 3641ae8..70b9683 100644
--- a/README.md
+++ b/README.md
@@ -1,6 +1,6 @@
# colorhash
-
+
A tool for creating distinct art based on input data or hash.
diff --git a/examples/commithash.svg b/examples/commithash.svg
new file mode 100644
index 0000000..cc5b131
--- /dev/null
+++ b/examples/commithash.svg
@@ -0,0 +1,44 @@
+
\ No newline at end of file
diff --git a/examples/fullsource-md5-nibble.svg b/examples/fullsource-md5-nibble.svg
deleted file mode 100644
index a9059b0..0000000
--- a/examples/fullsource-md5-nibble.svg
+++ /dev/null
@@ -1,34 +0,0 @@
-
\ No newline at end of file
diff --git a/examples/fullsource-md5-randomart.svg b/examples/fullsource-md5-randomart.svg
deleted file mode 100644
index 985a5a0..0000000
--- a/examples/fullsource-md5-randomart.svg
+++ /dev/null
@@ -1,44 +0,0 @@
-
\ No newline at end of file
diff --git a/examples/fullsource-sha1-nibble.svg b/examples/fullsource-sha1-nibble.svg
deleted file mode 100644
index 621ebb5..0000000
--- a/examples/fullsource-sha1-nibble.svg
+++ /dev/null
@@ -1,42 +0,0 @@
-
\ No newline at end of file
diff --git a/examples/fullsource-sha1-randomart.svg b/examples/fullsource-sha1-randomart.svg
deleted file mode 100644
index a890533..0000000
--- a/examples/fullsource-sha1-randomart.svg
+++ /dev/null
@@ -1,44 +0,0 @@
-
\ No newline at end of file
diff --git a/examples/fullsource-sha224-nibble.svg b/examples/fullsource-sha224-nibble.svg
deleted file mode 100644
index 60d5e84..0000000
--- a/examples/fullsource-sha224-nibble.svg
+++ /dev/null
@@ -1,58 +0,0 @@
-
\ No newline at end of file
diff --git a/examples/fullsource-sha224-randomart.svg b/examples/fullsource-sha224-randomart.svg
deleted file mode 100644
index bc04e9f..0000000
--- a/examples/fullsource-sha224-randomart.svg
+++ /dev/null
@@ -1,58 +0,0 @@
-
\ No newline at end of file
diff --git a/examples/fullsource-sha256-nibble.svg b/examples/fullsource-sha256-nibble.svg
deleted file mode 100644
index ff458cc..0000000
--- a/examples/fullsource-sha256-nibble.svg
+++ /dev/null
@@ -1,66 +0,0 @@
-
\ No newline at end of file
diff --git a/examples/fullsource-sha256-randomart.svg b/examples/fullsource-sha256-randomart.svg
deleted file mode 100644
index eff6163..0000000
--- a/examples/fullsource-sha256-randomart.svg
+++ /dev/null
@@ -1,58 +0,0 @@
-
\ No newline at end of file
diff --git a/examples/fullsource-sha384-nibble.svg b/examples/fullsource-sha384-nibble.svg
deleted file mode 100644
index b7dc7ff..0000000
--- a/examples/fullsource-sha384-nibble.svg
+++ /dev/null
@@ -1,98 +0,0 @@
-
\ No newline at end of file
diff --git a/examples/fullsource-sha384-randomart.svg b/examples/fullsource-sha384-randomart.svg
deleted file mode 100644
index 5072200..0000000
--- a/examples/fullsource-sha384-randomart.svg
+++ /dev/null
@@ -1,112 +0,0 @@
-
\ No newline at end of file
diff --git a/examples/fullsource-sha512-nibble.svg b/examples/fullsource-sha512-nibble.svg
deleted file mode 100644
index 10acdcc..0000000
--- a/examples/fullsource-sha512-nibble.svg
+++ /dev/null
@@ -1,130 +0,0 @@
-
\ No newline at end of file
diff --git a/examples/fullsource-sha512-randomart.svg b/examples/fullsource-sha512-randomart.svg
deleted file mode 100644
index 3136ea4..0000000
--- a/examples/fullsource-sha512-randomart.svg
+++ /dev/null
@@ -1,112 +0,0 @@
-
\ No newline at end of file
diff --git a/examples/fullsource.in b/examples/fullsource.in
deleted file mode 100644
index c68e132..0000000
--- a/examples/fullsource.in
+++ /dev/null
@@ -1,757 +0,0 @@
-"Generate a graphic based on the hash of an input file."
-from .cli import cli_main
-
-
-if __name__ == '__main__':
- cli_main()
-"All things that turn a numeric matrix into a colored matrix."
-import abc
-from typing import Sequence
-
-from .color import Color
-from .matricizer import Matrix
-from .palettes import Palette
-
-
-ColorMatrix = Sequence[Sequence[Color]]
-
-
-class Colorizer(metaclass=abc.ABCMeta):
- """
- The base Colorizer class.
-
- A colorizer turns a numeric matrix into a color matrix, colors being represented by strings of
- HTML colors.
- """
-
- @abc.abstractmethod
- def colorize(self, matrix: Matrix) -> ColorMatrix:
- """
- Colorize a matrix.
-
- :param matrix: the matrix to colorize.
- :returns: the colorized matrix.
- """
-
-
-class PaletteColorizer(Colorizer):
- """
- A palette colorizer.
-
- This colorizer will use a palette to colorize its inputs. A palette is 16 colors.
- """
- def __init__(self, palette: Palette) -> None:
- """
- Create a new palette colorizer for a given palette.
-
- :param palette: the palette to use for this colorizer.
- """
- self.palette = palette
-
- def colorize(self, matrix: Matrix) -> ColorMatrix:
- """
- Colorize the given matrix using this colorizer's palette.
-
- :param matrix: the matrix to colorize.
- :returns: the colorized matrix.
- """
- return [[self.palette[v] for v in row] for row in matrix]
-"All things that turn a hash into a matrix."
-import abc
-from typing import Mapping, Sequence
-
-from .palettes import Palette, DEFAULT_PALETTES, GRADIENT_PALETTES, MULTICOLOR_PALETTES
-
-
-Matrix = Sequence[Sequence[int]]
-
-
-class Matricizer(metaclass=abc.ABCMeta):
- """
- The base Matricizer class.
-
- A matricizer turns a collection of hash bytes into a matrix of values between 0x0 and 0xf
- (inclusive). The method by which this is done is up to the matricizer.
- """
-
- def __init__(self, w: int, h: int) -> None:
- """
- Create a new matricizer for the given dimensions.
-
- :param w: the width of the output matrix.
- :param h: the height of hte output matrix.
- """
- self.w = w
- self.h = h
-
- @abc.abstractmethod
- def matricize(self, data: bytes) -> Matrix:
- """
- Convert a hash to a matrix of given width and height.
-
- :param data: the hash data to turn into a matrix.
- :returns: the matrix converted from the hash data.
- """
-
- @staticmethod
- @abc.abstractmethod
- def choose_dimensions(hash: str) -> tuple[int, int]:
- """
- Choose the dimensions for this matrix based on the hash algorithm.
-
- :param hash: the hash algorithm being used.
- :returns: a width and height as a tuple.
- """
-
- def choose_palette(
- self, data: bytes, palettes: Mapping[str, Palette] | None = None
- ) -> Palette:
- """
- Choose a palette based on the give data and palettes.
-
- By default, this method will choose the Nth palette from the sum of the data mod the length
- of all palettes provided (using all palettes as the default).
- """
- if palettes is None:
- palettes = DEFAULT_PALETTES
- return list(palettes.values())[sum(data) % len(palettes)]
-
-
-class NibbleMatricizer(Matricizer):
- """
- A matricizer that converts a hash based on all of the nibbles in the hash.
-
- While dimensions are not enforced by this matricizer, it is strongly recommended to use the
- dimensions provided by the `NibbleMatricizer.DIMENSIONS` member.
- """
-
- DIMENSIONS = {
- "md5": (8, 4),
- "sha1": (8, 5),
- "sha224": (8, 7),
- "sha256": (8, 8),
- "sha384": (12, 8),
- "sha512": (16, 8),
- }
-
- def matricize(self, data: bytes) -> Matrix:
- """
- Convert a set of bytes to a list of rows of nibbles.
-
- :param data: the hash data to turn into a matrix.
- :returns: the matrix converted from the hash data.
- """
-
- nibbles = []
- for b in data:
- top = (b & 0xF0) >> 4
- bottom = b & 0x0F
- nibbles += [top, bottom]
-
- if len(nibbles) != self.w * self.h:
- raise ValueError(
- f"input data length ({len(nibbles)}) must match matrix dimensions "
- f"({self.w}x{self.h} = {self.w * self.h})"
- )
-
- cols = []
- row = []
- for b in nibbles:
- row += [b]
- if len(row) == self.w:
- cols += [row]
- row = []
-
- return cols
-
- @staticmethod
- def choose_dimensions(hash: str) -> tuple[int, int]:
- return NibbleMatricizer.DIMENSIONS[hash]
-
- def choose_palette(
- self, data: bytes, palettes: Mapping[str, Palette] | None = None
- ) -> Palette:
- return super().choose_palette(data, palettes or GRADIENT_PALETTES)
-
-
-class RandomartMatricizer(Matricizer):
- """
- A matricizer that converts hash data into a matrix based on the "randomart" algorithm from
- ssh-keygen.
-
- See: https://github.com/openssh/openssh-portable/blob/fc5dc092830de23767c6ef67baa18310a64ee533/sshkey.c#L1014
- """
-
- DIMENSIONS = {
- "md5": (7, 6),
- "sha1": (7, 6),
- "sha224": (8, 7),
- "sha256": (8, 7),
- "sha384": (11, 10),
- "sha512": (11, 10),
- }
-
- def matricize(self, data: bytes) -> Matrix:
- """
- Create a matrix based on the "randomart" algorithm from ssh-keygen.
-
- The algorithm is as follows:
-
- 1. Choose the point in the middle of the matrix.
- 2. Iterate through the data, two bits at a time.
- 3. If the low bit is set, then move the pointer right. Otherwise, move left.
- 4. If the high bit is set, then move the pointer down. Otherwise, move up.
- 5. If stepping in either direction would move us outside of the matrix, then don't move in
- that direction.
- 6. At the end of each step, increment the value in the matrix by one.
-
- :param data: the hash data to turn into a matrix.
- :returns: the matrix converted from the hash data.
- """
-
- rows = [[0] * self.w for _ in range(self.h)]
- c = self.w // 2
- r = self.h // 2
- for value in data:
- for _ in range(4):
- if value & 0x1:
- c += 1
- else:
- c -= 1
- if value & 0x2:
- r += 1
- else:
- r -= 1
- c = min(max(c, 0), self.w - 1)
- r = min(max(r, 0), self.h - 1)
- # max value is 0xf
- if rows[r][c] < 0xF:
- rows[r][c] += 1
- return rows
-
- @staticmethod
- def choose_dimensions(hash: str) -> tuple[int, int]:
- return RandomartMatricizer.DIMENSIONS[hash]
-
- def choose_palette(
- self, data: bytes, palettes: Mapping[str, Palette] | None = None
- ) -> Palette:
- return super().choose_palette(data, palettes or MULTICOLOR_PALETTES)
-"Base color palette definitions."
-import abc
-from typing import Sequence
-
-from .color import Color, HSLColor
-
-
-class Palette(metaclass=abc.ABCMeta):
- """
- A 16-color palette.
-
- All colors must be a `colorhash.Color`.
- """
-
- @abc.abstractmethod
- def choose(self, color: int) -> Color:
- """
- Chooses the given color in this palette.
- """
-
- def __getitem__(self, color: int) -> Color:
- return self.choose(color)
-
-
-class StaticPalette(Palette):
- """
- A static color palette with discrete colors.
- """
-
- def __init__(self, colors: Sequence[Color]) -> None:
- """
- Creates a new static color palette.
-
- :param colors: the colors for this palette. Must be exactly 16 colors.
- """
- if len(colors) != 16:
- raise ValueError(f"palette must have exactly 16 colors (got {len(colors)})")
- self.colors = colors
-
- def choose(self, color: int) -> Color:
- if not isinstance(color, int):
- raise KeyError("palette color indices must be an integer")
- return self.colors[color]
-
-
-HSLRange = range | float | int | list[float | int]
-
-
-def quantize(r: range, steps: int = 16) -> list[float]:
- """
- Given a range and a number of steps, create a list of numbers starting and ending in the range
- (inclusive) with that number of steps.
-
- :param r: the range to quantize.
- :param steps: the number of steps to use.
- :returns: a list of the quantized range.
- """
- dist = abs(r.stop - r.start)
- return [r.start + (i * dist / (steps - 1)) for i in range(steps)]
-
-
-def hsl_colors(hue: HSLRange, sat: HSLRange, light: HSLRange) -> list[HSLColor]:
- """
- Utility method to create 16 colors using HSL.
-
- :param hue: the hue, or range of hues, to use for this palette.
- :param sat: the saturation, or range of saturations, to use for this palette.
- :param light: the light value, or range of light values, to use for this palette.
- """
-
- if isinstance(hue, (float, int)):
- hue = [hue] * 16
- elif isinstance(hue, range):
- hue = quantize(hue)
- assert len(hue) == 16, "hue values must be a list of 16 elements"
-
- if isinstance(sat, (float, int)):
- sat = [sat] * 16
- elif isinstance(sat, range):
- sat = quantize(sat)
- assert len(sat) == 16, "saturation values must be a list of 16 elements"
-
- if isinstance(light, (float, int)):
- light = [light] * 16
- elif isinstance(light, range):
- light = quantize(light)
- assert len(light) == 16, "light values must be a list of 16 elements"
-
- return [HSLColor(round(h), round(s), round(l)) for h, s, l in zip(hue, sat, light)]
-
-
-GRADIENT_PALETTES = {
- # Interesting thing with human perception.
- # Between red and yellow, we can perceive "orange". We have a name for it and see it as a
- # distinct color. However, between yellow and green, we see a sickly green; between green and
- # cyan, a seafoam green; between cyan and blue, a lighter blue.
- #
- # Beside these, I think that between blue and magenta gives a color you could safely call
- # "purple", and between magenta and red, you get a color you could safely call "pink". It seems
- # that reds are more distinct to the human eye.
- #
- # For this reason, I have decided to pick these palettes as the "defaults", with a "dark" and
- # "light" variant of each (lightness 0-50%, and 50-100% respectively), with an additional
- # fully-saturated "rainbow" palette with all of the colors:
- #
- # red, orange, yellow, green, cyan, blue, purple, magenta, pink, gray, rainbow
- #
- # Also disabling yellow-light, that one just gives me a headache. It's hard to look at.
-
- "red-light": StaticPalette(hsl_colors(0, 100, range(50, 100))),
- "red-dark": StaticPalette(hsl_colors(0, 100, range(0, 50))),
-
- "orange-light": StaticPalette(hsl_colors(30, 100, range(50, 100))),
- "orange-dark": StaticPalette(hsl_colors(30, 100, range(0, 50))),
-
- #"yellow-light": StaticPalette(hsl_colors(60, 100, range(50, 100))),
- "yellow-dark": StaticPalette(hsl_colors(60, 100, range(0, 50))),
-
- #"lime-light": StaticPalette(hsl_colors(90, 100, range(50, 100))),
- #"lime-dark": StaticPalette(hsl_colors(90, 100, range(0, 50))),
-
- "green-light": StaticPalette(hsl_colors(120, 100, range(50, 100))),
- "green-dark": StaticPalette(hsl_colors(120, 100, range(0, 50))),
-
- #"seafoam-light": StaticPalette(hsl_colors(150, 100, range(50, 100))),
- #"seafoam-dark": StaticPalette(hsl_colors(150, 100, range(0, 50))),
-
- "cyan-light": StaticPalette(hsl_colors(180, 100, range(50, 100))),
- "cyan-dark": StaticPalette(hsl_colors(180, 100, range(0, 50))),
-
- #"teal-light": StaticPalette(hsl_colors(210, 100, range(50, 100))),
- #"teal-dark": StaticPalette(hsl_colors(210, 100, range(0, 50))),
-
- "blue-light": StaticPalette(hsl_colors(240, 100, range(50, 100))),
- "blue-dark": StaticPalette(hsl_colors(240, 100, range(0, 50))),
-
- "purple-light": StaticPalette(hsl_colors(270, 100, range(50, 100))),
- "purple-dark": StaticPalette(hsl_colors(270, 100, range(0, 50))),
-
- "magenta-light": StaticPalette(hsl_colors(300, 100, range(50, 100))),
- "magenta-dark": StaticPalette(hsl_colors(300, 100, range(0, 50))),
-
- "pink-light": StaticPalette(hsl_colors(330, 100, range(50, 100))),
- "pink-dark": StaticPalette(hsl_colors(330, 100, range(0, 50))),
-
- "gray-light": StaticPalette(hsl_colors(0, 0, range(50, 100))),
- "gray-dark": StaticPalette(hsl_colors(0, 0, range(0, 50))),
-}
-
-
-MULTICOLOR_PALETTES = {
- "rainbow": StaticPalette(hsl_colors(range(0, 360), 100, 50)),
-}
-
-DEFAULT_PALETTES = {
- **GRADIENT_PALETTES, **MULTICOLOR_PALETTES,
-}
-
-
-PALETTES = {**DEFAULT_PALETTES}
-import abc
-import colorsys
-import dataclasses
-
-
-class Color(metaclass=abc.ABCMeta):
- """
- An abstract color class.
-
- This can be used to convert any color format into another color format.
- """
-
- def to_html_color(self) -> str:
- """
- Convert this color to an HTML color.
-
- This may produce unexpected results if you are expecting an RGB color. If you are expecting
- RGB, then you should use `color.to_rgb().to_html_color()` instead.
- """
-
- @abc.abstractmethod
- def to_rgb(self) -> "RGBColor":
- "Convert this color into an RGB color."
-
- @abc.abstractmethod
- def to_hsl(self) -> "HSLColor":
- "Convert this color into an HSL color."
-
-
-@dataclasses.dataclass
-class RGBColor(Color):
- """
- An RGB color. Colors are expected to be a floating point value from [0.0-255.0).
- """
- r: float
- g: float
- b: float
-
- def to_html_color(self) -> str:
- r, g, b = round(self.r), round(self.g), round(self.b)
- return f"#{r:02x}{g:02x}{b:02x}"
-
- def to_rgb(self) -> "RGBColor":
- return self
-
- def to_hsl(self) -> "HSLColor":
- r = self.r / 255.0
- g = self.g / 255.0
- b = self.b / 255.0
- h, l, s = colorsys.rgb_to_hls(r, g, b)
- return HSLColor(h * 360.0, s * 100.0, l * 100.0)
-
-
-@dataclasses.dataclass
-class HSLColor(Color):
- h: float
- s: float
- l: float
-
- def to_html_color(self) -> str:
- return f"hsl({self.h:.02f},{self.s:.02f}%,{self.l:.02f}%)"
-
- def to_rgb(self) -> "RGBColor":
- h = self.h / 360.0
- s = self.s / 100.0
- l = self.l / 100.0
- r, g, b = colorsys.hls_to_rgb(h, l, s)
- return RGBColor(r * 255.0, g * 255.0, b * 255.0)
-
-
- def to_hsl(self) -> "HSLColor":
- return self
-import abc
-
-from .color import Color
-from .colorizer import ColorMatrix
-
-
-class Writer(metaclass=abc.ABCMeta):
- """
- Base writer class.
-
- This is used to write an input colorized matrix to a string, which is then forwarded to the
- appropriate output.
- """
-
- @abc.abstractmethod
- def write(self, matrix: ColorMatrix) -> str:
- "Write the color matrix to a string."
-
-
-class ANSIWriter(Writer):
- def write(self, matrix: ColorMatrix) -> str:
- ESC = '\x1b'
- RESET = f"{ESC}[0m"
- C = "██"
- def ansi_color(c: Color) -> str:
- c = c.to_rgb()
- return f"{ESC}[38;2;{round(c.r)};{round(c.g)};{round(c.b)}m"
- out = ''
- for row in matrix:
- for col in row:
- out += ansi_color(col)
- out += C
- out += "\n"
- out += RESET
- return out
-
-
-class SVGWriter(Writer):
- def __init__(self, square_size: int) -> None:
- """
- Create a new SVG writer that uses the given square size.
-
- :param square_size: the size of the squares generated, in pixels.
- """
- self.square_size = square_size
-
- def write(self, matrix: ColorMatrix) -> str:
- """
- Generate an SVG based on a given matrix.
-
- :param matrix: the color matrix to generate the SVG for.
- :returns: the full generated SVG as a string.
- """
- h = len(matrix)
- w = len(matrix[0])
-
- # Start SVG string
- svg = f'"
- return svg
-
-"Main driver for the colorhash program."
-import argparse
-import hashlib
-from pathlib import Path
-import sys
-import textwrap
-
-from .colorizer import PaletteColorizer
-from .matricizer import Matricizer, NibbleMatricizer, RandomartMatricizer
-from .palettes import Palette, DEFAULT_PALETTES, PALETTES
-from .writer import ANSIWriter, SVGWriter
-
-
-# TODO - WASM compile for embedding directly in HTML
-# - this may not be an option, sadly. might have to just port it to JS
-# TODO - option to add a caption based on the filename (for SVG)
-# TODO - load palettes from a file
-# TODO - PNG output
-
-
-def cli_main() -> None:
- "Main function entrypoint."
- # pylint: disable=invalid-name
-
- MATRIX_CHOICES = {
- "nibble": "Use each nibble (4 bits) of the hash to generate a matrix",
- "randomart": "Use the SSH 'randomart' algorithm to generate a matrix",
- }
- MATRIX_HELP = "MATRIX STRATEGY (-m, --matrix)\n" + "\n".join(
- f" {choice} - {desc}" for choice, desc in MATRIX_CHOICES.items()
- )
- PALETTE_CHOICES = [
- "auto",
- ] + list(PALETTES.keys())
- PALETTE_HELP = "\n".join(
- [
- "PALETTE CHOICES",
- '\n'.join(textwrap.wrap(
- ", ".join(PALETTE_CHOICES),
- initial_indent=" ",
- subsequent_indent=" ",
- )),
- ]
- )
- HASH_CHOICES = ["md5", "sha1", "sha224", "sha256", "sha384", "sha512"]
- INPUT_TYPE_CHOICES = {
- "path": "the input should be treated as a path and data is read from the path",
- "hash": "the input should be treated as a hexadecimal hash (requires -a or --hash to be supplied)",
- "data": "the input should be treated as raw data",
- }
- INPUT_TYPE_HELP = "INPUT TYPE (-x, --input-type)\n" + "\n".join(
- [f" {choice} - {desc}" for choice, desc in INPUT_TYPE_CHOICES.items()]
- )
- OUTPUT_TYPE_CHOICES = {
- "ansi": "the output should be colored for ANSI terminals using 24 bit true color",
- "svg": "the output should be an SVG format",
- }
- OUTPUT_TYPE_HELP = "OUTPUT TYPE (-y, --output-type)\n" + "\n".join(
- [f" {choice} - {desc}" for choice, desc in OUTPUT_TYPE_CHOICES.items()]
- )
- EPILOGUE = "\n\n".join([MATRIX_HELP, PALETTE_HELP, INPUT_TYPE_HELP, OUTPUT_TYPE_HELP])
-
- progname: str = sys.argv[0]
- if progname.endswith("__main__.py"):
- progname = "colorhash"
-
- ap = argparse.ArgumentParser(
- prog=progname,
- description="Create a piece of art based on the hash of a file.",
- epilog=EPILOGUE,
- formatter_class=argparse.RawDescriptionHelpFormatter,
- )
-
- ap.add_argument(
- "input",
- type=str,
- default="-",
- help="The input to use. When acting as a path, set to '-' or blank for STDIN. Use -x or --input-type to control how input is treated. default: -",
- )
- ap.add_argument(
- "-o",
- "--out",
- metavar="OUTFILE",
- type=Path,
- default="-",
- help="The output file to use. Set to '-' or blank for STDOUT. default: STDOUT",
- )
- ap.add_argument(
- "-m",
- "--matrix",
- metavar="MATRIX",
- choices=MATRIX_CHOICES.keys(),
- default="nibble",
- help="Choose the strategy that turns the hash into a matrix. default: nibble",
- )
- ap.add_argument(
- "-p",
- "--palette",
- metavar="PALETTE",
- choices=PALETTE_CHOICES,
- default="auto",
- help="Choose the palette. default: auto",
- )
- ap.add_argument(
- "-a", # the "a" is for "algorithm" (since -h is taken)
- "--hash",
- metavar="ALGORITHM",
- choices=HASH_CHOICES,
- # default="sha512",
- required=False,
- help="Choose the hash algorithm. default: sha512",
- )
- ap.add_argument(
- "--svg-square-size",
- metavar="PX",
- type=int,
- default=32,
- help="For SVG outputs, decide how big the output squares are, in pixels. default: 32",
- )
- ap.add_argument(
- "-x",
- "--input-type",
- default="path",
- choices=INPUT_TYPE_CHOICES.keys(),
- help="Determines how the input should be treated. default: path",
- )
- ap.add_argument(
- "-y",
- "--output-type",
- default="ansi",
- choices=OUTPUT_TYPE_CHOICES.keys(),
- help="Determines how the output should be generated. default: ansi",
- )
- args = ap.parse_args()
-
- ############################################################################
- # End arg parsing
- ############################################################################
-
- # -a/--hash arg is not required when we're using file and data input types. only required for
- # hash input type
- if args.input_type in ("data", "path") and args.hash is None:
- args.hash = "sha512"
-
- # Get the hash
- match args.input_type:
- case "path":
- if args.input == "-":
- infile = sys.stdin.buffer
- else:
- # TODO - pretty error message for when the file doesn't exist
- infile = open(args.input, "rb")
- # file_digest (I hope) will not load too much into memory
- hashdata = hashlib.file_digest(infile, args.hash).digest() # type: ignore
- # NOTE : previous line has typing ignored because file_digest requires a
- # "_BytesIOLike | _FileDigestFileObj", both of which look like API leaks. Specifying
- # infile to be BinaryIO is not enough and causes the same error.
- case "hash":
- # TODO - maybe a better error message?
- if args.hash is None:
- print(
- "ERROR: -a or --hash should be supplied on the command line when using the hash input type",
- file=sys.stderr,
- )
- raise SystemExit(1)
- # TODO - pretty error message for malformed input
- hashdata = bytes([int(byte, 16) for byte in textwrap.wrap(args.input, 2)])
- case "data":
- hashdata = hashlib.new(args.hash, args.input.encode()).digest()
- case _:
- assert False, f"unknown input type {args.input_type}"
-
- # Choose the dimensions and the matricizer
- matricizer: Matricizer
- match args.matrix:
- case "nibble":
- w, h = NibbleMatricizer.DIMENSIONS[args.hash]
- matricizer = NibbleMatricizer(w, h)
- case "randomart":
- # 17x9 is what openssh uses
- # TODO - allow configuring dimensions, maybe
- w, h = RandomartMatricizer.DIMENSIONS[args.hash]
- matricizer = RandomartMatricizer(w, h)
- case _:
- assert False, f"invalid args.matrix: {args.matrix}"
-
- # Choose the palette
- palette: Palette
- if args.palette == "auto":
- palette = matricizer.choose_palette(hashdata)
- else:
- palette = PALETTES[args.palette]
-
- # Choose the colorizer
- colorizer = PaletteColorizer(palette)
-
- # Print SVG
- matrix = matricizer.matricize(hashdata)
- colors = colorizer.colorize(matrix)
-
- # Choose the output writer
- writer: Writer
- match args.output_type:
- case "ansi":
- writer = ANSIWriter()
- case "svg":
- writer = SVGWriter(args.svg_square_size)
-
- output = writer.write(colors)
-
- if str(args.out) == "-":
- sys.stdout.write(output)
- else:
- args.out.write_text(output)
-
diff --git a/tools/genexamples.sh b/tools/genexamples.sh
index ed614cf..3934367 100755
--- a/tools/genexamples.sh
+++ b/tools/genexamples.sh
@@ -7,9 +7,6 @@ matrices=(nibble randomart)
cd "$here/.."
-# Additionally, get the full source code output as an "in" file, and use that as an example too.
-find "colorhash" -type f -name '*.py' -exec cat '{}' ';' >examples/fullsource.in
-
find "examples" -type f -name '*.in' | \
while read infile; do
for hash in "${hashes[@]}"; do
@@ -23,3 +20,7 @@ while read infile; do
done
done
done
+
+# Additionally, create an SVG for the current commit hash
+echo "Generating examples/commithash.svg"
+python3 -m colorhash "$(git rev-parse HEAD)" --input-type hash --hash sha1 --matrix randomart --output-type svg --out examples/commithash.svg