11"""Generate placeholder VuMark images for the mock API."""
22
33import io
4+ from xml .sax .saxutils import escape
45
56from beartype import beartype
67from 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 >>\n endobj\n "
90+ obj2 = "2 0 obj\n << /Type /Pages /Kids [3 0 R] /Count 1 >>\n endobj\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\n endobj\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\n 0 { len (offsets ) + 1 } \n 0000000000 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