#!/usr/bin/env python3 import argparse import json import random import sys # --- Configuration for Crop Fuzzing --- MAX_SUM = 0.99 MIN_CROP_SIZE = 0.05 PRECISION = 4 # --- Configuration for Canvas Fuzzing --- # The desired size of the fuzzed rectangle. FUZZED_RECT_WIDTH = 12 FUZZED_RECT_HEIGHT = 8 # The boundaries of the entire canvas. # A=1, B=2, ..., Z=26, AA=27, ..., AE=31 CANVAS_MIN_COL = 1 # 'A' CANVAS_MIN_ROW = 1 CANVAS_MAX_COL = 31 # 'AE' CANVAS_MAX_ROW = 55 def num_to_col_str(n): """Converts a 1-indexed column number to its spreadsheet-style string. e.g., 1 -> 'A', 27 -> 'AA' """ string = "" while n > 0: n, remainder = divmod(n - 1, 26) string = chr(65 + remainder) + string return string def generate_fuzzed_crop_values(): """ Generates a random offset and size that adhere to the constraint: offset + size <= MAX_SUM """ offset = random.uniform(0.0, MAX_SUM - MIN_CROP_SIZE) max_size_allowed = MAX_SUM - offset size = random.uniform(MIN_CROP_SIZE, max_size_allowed) return round(offset, PRECISION), round(size, PRECISION) def generate_fuzzed_canvas_rect(): """ Generates a random canvas_rect of a fixed size (12x8) within the overall canvas boundary (A1:AE55). """ # 1. Determine the valid range for the top-left corner. # To fit a 12-wide rect inside a 31-wide canvas, the top-left # column can start at 1 and must end early enough. # Last possible start col = 31 - 12 + 1 = 20 max_start_col = CANVAS_MAX_COL - FUZZED_RECT_WIDTH + 1 # Same logic for rows. # Last possible start row = 55 - 8 + 1 = 48 max_start_row = CANVAS_MAX_ROW - FUZZED_RECT_HEIGHT + 1 # 2. Pick a random top-left corner from the valid range. start_col = random.randint(CANVAS_MIN_COL, max_start_col) start_row = random.randint(CANVAS_MIN_ROW, max_start_row) # 3. Calculate the bottom-right corner based on the fuzzed size. end_col = start_col + FUZZED_RECT_WIDTH - 1 end_row = start_row + FUZZED_RECT_HEIGHT - 1 # 4. Convert numerical columns back to string representation. start_col_str = num_to_col_str(start_col) end_col_str = num_to_col_str(end_col) # 5. Format the final string. return f"{start_col_str}{start_row}:{end_col_str}{end_row}" def main(): """ Main function to read a JSON file, process the data, and either print to the console or write to an output file. """ parser = argparse.ArgumentParser( description="Fuzzes 'canvas_rect', 'crop_offset', and 'crop_size' in a JSON file.", formatter_class=argparse.RawTextHelpFormatter, ) parser.add_argument( "input_file", help="Path to the input JSON file to be processed." ) parser.add_argument( "-o", "--output_file", help="Path to the output JSON file. If not provided, the result is printed to the console.", ) args = parser.parse_args() try: with open(args.input_file, "r") as f: print(f"Reading data from '{args.input_file}'...") data = json.load(f) except FileNotFoundError: print(f"Error: The file '{args.input_file}' was not found.", file=sys.stderr) sys.exit(1) except json.JSONDecodeError: print( f"Error: The file '{args.input_file}' is not a valid JSON file.", file=sys.stderr, ) sys.exit(1) except Exception as e: print(f"An unexpected error occurred: {e}", file=sys.stderr) sys.exit(1) # Iterate through each item and update it with fuzzed values for item in data: # Fuzz crop offset and size x_offset, x_size = generate_fuzzed_crop_values() y_offset, y_size = generate_fuzzed_crop_values() item["crop_offset"] = [x_offset, y_offset] item["crop_size"] = [x_size, y_size] # Fuzz the canvas rect item["canvas_rect"] = generate_fuzzed_canvas_rect() # Handle the output if args.output_file: try: with open(args.output_file, "w") as f: json.dump(data, f, indent=2) print(f"Successfully wrote fuzzed data to '{args.output_file}'.") except IOError as e: print( f"Error: Could not write to file '{args.output_file}': {e}", file=sys.stderr, ) sys.exit(1) else: print("\n--- Fuzzed JSON ---") print(json.dumps(data, indent=2)) if __name__ == "__main__": main()