Skip to content

Commit a24762c

Browse files
adamtheturtleclaude
andcommitted
Fix BugBot issues in VuMark image generators
- PNG: Use white background (was black, making text invisible) - SVG: Escape instance_id with xml.sax.saxutils.escape to prevent malformed XML from special characters - PDF: Compute stream length, xref offsets, and startxref dynamically instead of hardcoding them. Escape PDF string literal characters. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
1 parent dcb8ec3 commit a24762c

1 file changed

Lines changed: 64 additions & 49 deletions

File tree

src/mock_vws/_vumark_generators.py

Lines changed: 64 additions & 49 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
"""Generate placeholder VuMark images for the mock API."""
22

33
import io
4+
from xml.sax.saxutils import escape
45

56
from beartype import beartype
67
from PIL import Image, ImageDraw
@@ -16,6 +17,7 @@ def generate_svg(instance_id: str) -> bytes:
1617
Returns:
1718
SVG image data as bytes.
1819
"""
20+
escaped_id = escape(data=instance_id)
1921
svg_content = (
2022
'<?xml version="1.0" encoding="UTF-8"?>'
2123
'<svg xmlns="http://www.w3.org/2000/svg" width="200" height="200" '
@@ -25,7 +27,7 @@ def generate_svg(instance_id: str) -> bytes:
2527
'<text x="100" y="90" text-anchor="middle" font-family="Arial" '
2628
'font-size="14">VuMark Mock</text>'
2729
'<text x="100" y="120" text-anchor="middle" font-family="monospace" '
28-
f'font-size="10">{instance_id}</text>'
30+
f'font-size="10">{escaped_id}</text>'
2931
"</svg>"
3032
)
3133
return svg_content.encode()
@@ -41,18 +43,13 @@ def generate_png(instance_id: str) -> bytes:
4143
Returns:
4244
PNG image data as bytes.
4345
"""
44-
# Create a simple 200x200 image
45-
img = Image.new(mode="RGB", size=(200, 200))
46+
img = Image.new(mode="RGB", size=(200, 200), color=(255, 255, 255))
4647
draw = ImageDraw.Draw(im=img)
4748

48-
# Draw a border
4949
draw.rectangle(xy=[0, 0, 199, 199], outline="black")
50-
51-
# Add text
5250
draw.text(xy=(100, 80), text="VuMark Mock", fill="black")
5351
draw.text(xy=(100, 110), text=instance_id[:20], fill="black")
5452

55-
# Save to bytes
5653
buffer = io.BytesIO()
5754
img.save(fp=buffer, format="PNG")
5855
return buffer.getvalue()
@@ -70,45 +67,63 @@ def generate_pdf(instance_id: str) -> bytes:
7067
Returns:
7168
PDF document data as bytes.
7269
"""
73-
# Create a minimal valid PDF
74-
# This is a simple PDF 1.4 document with one page and text
75-
pdf_content = f"""%PDF-1.4
76-
1 0 obj
77-
<< /Type /Catalog /Pages 2 0 R >>
78-
endobj
79-
2 0 obj
80-
<< /Type /Pages /Kids [3 0 R] /Count 1 >>
81-
endobj
82-
3 0 obj
83-
<< /Type /Page /Parent 2 0 R /MediaBox [0 0 200 200] /Contents 4 0 R
84-
/Resources << /Font << /F1 5 0 R >> >> >>
85-
endobj
86-
4 0 obj
87-
<< /Length 100 >>
88-
stream
89-
BT
90-
/F1 12 Tf
91-
50 150 Td
92-
(VuMark Mock) Tj
93-
0 -20 Td
94-
({instance_id}) Tj
95-
ET
96-
endstream
97-
endobj
98-
5 0 obj
99-
<< /Type /Font /Subtype /Type1 /BaseFont /Helvetica >>
100-
endobj
101-
xref
102-
0 6
103-
0000000000 65535 f
104-
0000000009 00000 n
105-
0000000058 00000 n
106-
0000000115 00000 n
107-
0000000266 00000 n
108-
0000000416 00000 n
109-
trailer
110-
<< /Size 6 /Root 1 0 R >>
111-
startxref
112-
496
113-
%%EOF"""
114-
return pdf_content.encode()
70+
# Escape parentheses in instance_id for PDF string literals.
71+
safe_id = instance_id.replace("\\", "\\\\")
72+
safe_id = safe_id.replace("(", "\\(")
73+
safe_id = safe_id.replace(")", "\\)")
74+
75+
# Build the stream content first so we can measure its length.
76+
stream = (
77+
"BT\n"
78+
"/F1 12 Tf\n"
79+
"50 150 Td\n"
80+
"(VuMark Mock) Tj\n"
81+
"0 -20 Td\n"
82+
f"({safe_id}) Tj\n"
83+
"ET\n"
84+
)
85+
stream_bytes = stream.encode()
86+
stream_length = len(stream_bytes)
87+
88+
# Build each object, tracking byte offsets for the xref table.
89+
obj1 = "1 0 obj\n<< /Type /Catalog /Pages 2 0 R >>\nendobj\n"
90+
obj2 = "2 0 obj\n<< /Type /Pages /Kids [3 0 R] /Count 1 >>\nendobj\n"
91+
obj3 = (
92+
"3 0 obj\n"
93+
"<< /Type /Page /Parent 2 0 R"
94+
" /MediaBox [0 0 200 200]"
95+
" /Contents 4 0 R"
96+
" /Resources << /Font << /F1 5 0 R >> >> >>\n"
97+
"endobj\n"
98+
)
99+
obj4 = (
100+
f"4 0 obj\n<< /Length {stream_length} >>\n"
101+
f"stream\n{stream}endstream\nendobj\n"
102+
)
103+
obj5 = (
104+
"5 0 obj\n"
105+
"<< /Type /Font /Subtype /Type1 /BaseFont /Helvetica >>\n"
106+
"endobj\n"
107+
)
108+
109+
header = "%PDF-1.4\n"
110+
offsets: list[int] = []
111+
body = header
112+
for obj in (obj1, obj2, obj3, obj4, obj5):
113+
offsets.append(len(body.encode()))
114+
body += obj
115+
116+
xref_offset = len(body.encode())
117+
xref = f"xref\n0 {len(offsets) + 1}\n0000000000 65535 f \n"
118+
for offset in offsets:
119+
xref += f"{offset:010d} 00000 n \n"
120+
121+
trailer = (
122+
"trailer\n"
123+
f"<< /Size {len(offsets) + 1} /Root 1 0 R >>\n"
124+
"startxref\n"
125+
f"{xref_offset}\n"
126+
"%%EOF"
127+
)
128+
129+
return (body + xref + trailer).encode()

0 commit comments

Comments
 (0)