Spaces:
Sleeping
Sleeping
File size: 6,469 Bytes
ab6af8b 287ad78 6cb62b2 1cd76ea ab6af8b 287ad78 5b86a84 f10ad79 230e407 aefd9d9 5b86a84 aefd9d9 5b86a84 aefd9d9 230e407 5b86a84 aefd9d9 230e407 5b86a84 230e407 5b86a84 287ad78 ab6af8b 3230f87 5b86a84 230e407 aefd9d9 9143b24 287ad78 aefd9d9 ab6af8b aefd9d9 230e407 3230f87 aefd9d9 287ad78 ab6af8b 287ad78 d796e5c ab6af8b 287ad78 f10ad79 ab6af8b 0f59ca5 d796e5c 287ad78 5b86a84 230e407 5b86a84 230e407 287ad78 ab6af8b 9143b24 ab6af8b 9318248 9143b24 ab6af8b 287ad78 0f59ca5 d796e5c 287ad78 0f59ca5 ab6af8b 0f59ca5 ab6af8b d796e5c 287ad78 d796e5c 0153e54 b374e53 6cb62b2 d796e5c 0f59ca5 287ad78 ab6af8b d796e5c 287ad78 d796e5c 287ad78 d796e5c 9318248 287ad78 c300287 287ad78 d796e5c ab6af8b 287ad78 ab6af8b 0f59ca5 d796e5c ab6af8b d796e5c 9318248 287ad78 9318248 287ad78 9318248 287ad78 e02df51 ea2c488 3230f87 | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 | from flask import Flask, request, jsonify, send_from_directory
from PIL import Image, ImageDraw, ImageFont
import io, base64, struct, math
app = Flask(__name__, static_folder="static")
BG_COLOR = (88, 28, 135)
TEXT_COLOR = (255, 255, 255)
BITS_PER_CHANNEL = 2
MASK = (1 << BITS_PER_CHANNEL) - 1
MAX_BYTES = 5 * 1024 * 1024
MIN_SIZE = 200
def get_font_for_height(target_px):
font_path = "/usr/share/fonts/truetype/dejavu/DejaVuSans-Bold.ttf"
size = max(1, target_px)
for _ in range(50):
try:
font = ImageFont.truetype(font_path, size)
except:
return ImageFont.load_default()
dummy = Image.new("RGB", (1, 1))
draw = ImageDraw.Draw(dummy)
bbox = draw.textbbox((0, 0), "CC", font=font)
actual_h = bbox[3] - bbox[1]
if abs(actual_h - target_px) <= 1:
break
if actual_h == 0:
size += 1
else:
size = max(1, int(size * target_px / actual_h))
return font
def make_base_image(w, h):
img = Image.new("RGB", (w, h), BG_COLOR)
draw = ImageDraw.Draw(img)
# Make the text 5 times bigger
target_h = int(min(w, h) * 10.0) # Increased from 2.0 to 10.0 (5x larger)
target_h = min(target_h, min(w, h) - 4)
font = get_font_for_height(target_h)
bbox = draw.textbbox((0, 0), "CC", font=font)
tw = bbox[2] - bbox[0]
th = bbox[3] - bbox[1]
x = (w - tw) // 2 - bbox[0]
y = (h - th) // 2 - bbox[1]
draw.text((x, y), "CC", fill=TEXT_COLOR, font=font)
return img
def calc_size(payload_bytes):
total_bits = (len(payload_bytes) + 4) * 8
pixels_needed = math.ceil(total_bits / 6)
# Increase size calculation to accommodate larger text
side = max(MIN_SIZE, math.ceil(math.sqrt(pixels_needed * 4.5 * 25))) # 25 = 5^2 (area scaling factor)
return side, side
def get_carriers(w, h):
base = make_base_image(w, h)
pixels = base.load()
carriers = []
for y in range(h):
for x in range(w):
if pixels[x, y] == BG_COLOR:
carriers.append((x, y))
return carriers, base
def bits_from_bytes(data):
bits = []
for byte in data:
for i in range(7, -1, -1):
bits.append((byte >> i) & 1)
return bits
def bytes_from_bits(bits):
result = []
for i in range(0, len(bits), 8):
chunk = bits[i:i+8]
if len(chunk) < 8:
break
byte = 0
for b in chunk:
byte = (byte << 1) | b
result.append(byte)
return bytes(result)
def embed(payload):
w, h = calc_size(payload)
for _ in range(10):
carriers, img = get_carriers(w, h)
header = struct.pack(">I", len(payload))
data = header + payload
bits = bits_from_bytes(data)
if len(bits) <= len(carriers) * BITS_PER_CHANNEL * 3:
break
w = math.ceil(w * 1.2)
h = math.ceil(h * 1.2)
else:
raise ValueError("Data too large even after resizing.")
pixels = img.load()
bit_idx = 0
for (x, y) in carriers:
if bit_idx >= len(bits):
break
r, g, b = pixels[x, y]
new_channels = []
for c in [r, g, b]:
chunk = 0
for _ in range(BITS_PER_CHANNEL):
if bit_idx < len(bits):
chunk = (chunk << 1) | bits[bit_idx]
bit_idx += 1
else:
chunk = chunk << 1
new_channels.append((c & ~MASK) | chunk)
pixels[x, y] = tuple(new_channels)
return img
def extract(img):
w, h = img.size
carriers, _ = get_carriers(w, h)
pixels = img.load()
bits = []
for (x, y) in carriers:
r, g, b = pixels[x, y]
for c in [r, g, b]:
for i in range(BITS_PER_CHANNEL - 1, -1, -1):
bits.append((c >> i) & 1)
length = struct.unpack(">I", bytes_from_bits(bits[:32]))[0]
if length == 0 or length > MAX_BYTES + 10:
return None, None
payload = bytes_from_bits(bits[32:32 + length * 8])
return length, payload
@app.route("/")
def index():
return send_from_directory("static", "index.html")
@app.route("/encode/text", methods=["POST"])
def encode_text():
data = request.json
text = data.get("text", "")
if not text:
return jsonify({"error": "No text provided"}), 400
payload = b'\x00' + text.encode("utf-8")
if len(payload) > MAX_BYTES:
return jsonify({"error": "Text too large. Max 5MB."}), 400
try:
img = embed(payload)
buf = io.BytesIO()
img.save(buf, format="PNG")
img_b64 = base64.b64encode(buf.getvalue()).decode()
w, h = img.size
return jsonify({"image": img_b64, "size": f"{w}×{h}"})
except Exception as e:
return jsonify({"error": str(e)}), 500
@app.route("/encode/file", methods=["POST"])
def encode_file():
if "file" not in request.files:
return jsonify({"error": "No file uploaded"}), 400
f = request.files["file"]
raw = f.read()
if len(raw) > MAX_BYTES:
return jsonify({"error": f"File too large: {len(raw)/1024/1024:.2f}MB. Max is 5MB."}), 400
fname = f.filename or ""
mode = b'\x02' if fname.lower().endswith(".pdf") else b'\x01'
payload = mode + raw
try:
img = embed(payload)
buf = io.BytesIO()
img.save(buf, format="PNG")
img_b64 = base64.b64encode(buf.getvalue()).decode()
w, h = img.size
return jsonify({"image": img_b64, "size": f"{w}×{h}"})
except Exception as e:
return jsonify({"error": str(e)}), 500
@app.route("/decode", methods=["POST"])
def decode_route():
if "file" not in request.files:
return jsonify({"error": "No file uploaded"}), 400
f = request.files["file"]
img = Image.open(f).convert("RGB")
length, payload = extract(img)
if payload is None:
return jsonify({"error": "Nothing encoded or invalid image"}), 400
mode = payload[0]
content = payload[1:]
if mode == 0:
return jsonify({"type": "text", "text": content.decode("utf-8", errors="replace")})
elif mode == 1:
return jsonify({"type": "image", "image": base64.b64encode(content).decode()})
elif mode == 2:
return jsonify({"type": "pdf", "pdf": base64.b64encode(content).decode()})
return jsonify({"error": "Unknown mode"}), 400
if __name__ == "__main__":
app.run(host="0.0.0.0", port=7860)
|