<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Rahul Anand</title>
    <description>The latest articles on DEV Community by Rahul Anand (@thanioruvan).</description>
    <link>https://dev.to/thanioruvan</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F4099940%2F55f80942-c77a-40bb-9cf5-737d7149b3ba.png</url>
      <title>DEV Community: Rahul Anand</title>
      <link>https://dev.to/thanioruvan</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/thanioruvan"/>
    <language>en</language>
    <item>
      <title>I generated 25 file formats at exact byte sizes. Here's every way it broke.</title>
      <dc:creator>Rahul Anand</dc:creator>
      <pubDate>Sat, 29 Aug 2026 07:58:02 +0000</pubDate>
      <link>https://dev.to/thanioruvan/i-generated-25-file-formats-at-exact-byte-sizes-heres-every-way-it-broke-91f</link>
      <guid>https://dev.to/thanioruvan/i-generated-25-file-formats-at-exact-byte-sizes-heres-every-way-it-broke-91f</guid>
      <description>&lt;h2&gt;
  
  
  The problem sounds trivial
&lt;/h2&gt;

&lt;p&gt;You need a 25 MB PDF to test an upload limit. Or a 105-page DOCX for pagination. Or a 1 GB binary to benchmark throughput.&lt;/p&gt;

&lt;p&gt;The obvious answer is &lt;code&gt;dd if=/dev/urandom of=test.pdf bs=1M count=25&lt;/code&gt;. You get exactly 25 MB. You also get a file that any format-sniffing validator rejects instantly, because it isn't a PDF — it's noise wearing a &lt;code&gt;.pdf&lt;/code&gt; extension.&lt;/p&gt;

&lt;p&gt;So you need real headers &lt;em&gt;and&lt;/em&gt; an exact byte count. Those two requirements fight each other, and every format loses the fight differently.&lt;/p&gt;

&lt;p&gt;I built a generator for 25 formats. Here's what actually went wrong.&lt;/p&gt;

&lt;h2&gt;
  
  
  PDF: the hardcoded &lt;code&gt;/Length&lt;/code&gt;
&lt;/h2&gt;

&lt;p&gt;A minimal PDF is a graph of objects and a cross-reference table of byte offsets. My first version emitted a content stream like this:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;4 0 obj
&amp;lt;&amp;lt; /Length 44 &amp;gt;&amp;gt;
stream
BT /F1 24 Tf 100 700 Td (Sample) Tj ET
endstream
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That &lt;code&gt;/Length 44&lt;/code&gt; was hardcoded. The actual stream is &lt;strong&gt;38 bytes&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;Browser viewers rendered it fine. Acrobat rejected it, and so did &lt;code&gt;pypdf&lt;/code&gt;. A strict reader trusts &lt;code&gt;/Length&lt;/code&gt;, reads 44 bytes, sails past &lt;code&gt;endstream&lt;/code&gt;, and hits a parse error.&lt;/p&gt;

&lt;p&gt;The second bug in the same object: &lt;code&gt;/Resources &amp;lt;&amp;lt; &amp;gt;&amp;gt;&lt;/code&gt; was empty while the content stream referenced &lt;code&gt;/F1&lt;/code&gt;. &lt;strong&gt;A page that draws text with an undeclared font is invalid&lt;/strong&gt;, even though lenient viewers substitute one silently.&lt;/p&gt;

&lt;p&gt;Both only surfaced because I ran the output through a strict parser:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;pypdf&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;PdfReader&lt;/span&gt;
&lt;span class="n"&gt;r&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;PdfReader&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;out.pdf&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;           &lt;span class="c1"&gt;# raises on malformed structure
&lt;/span&gt;&lt;span class="k"&gt;assert&lt;/span&gt; &lt;span class="n"&gt;r&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;pages&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;][&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;/Resources&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;][&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;/Font&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;][&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;/F1&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;][&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;/BaseFont&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;==&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;/Helvetica&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The fix is to compute everything from the assembled bytes — stream lengths &lt;em&gt;and&lt;/em&gt; xref offsets — rather than predicting them. Accumulate offsets as you append each object:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;offsets&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[];&lt;/span&gt;
&lt;span class="k"&gt;for &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;obj&lt;/span&gt; &lt;span class="k"&gt;of&lt;/span&gt; &lt;span class="nx"&gt;objects&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;offsets&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;obj&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;body&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;length&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;   &lt;span class="c1"&gt;// measure, never estimate&lt;/span&gt;
  &lt;span class="nx"&gt;body&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="nx"&gt;obj&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;render&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Padding then goes &lt;strong&gt;after &lt;code&gt;%%EOF&lt;/code&gt;&lt;/strong&gt;. That's the one region a conforming reader is required to ignore, which is what makes an exact byte count possible without corrupting the document.&lt;/p&gt;

&lt;h2&gt;
  
  
  DOCX and XLSX: padding that breaks XML
&lt;/h2&gt;

&lt;p&gt;An Office file is a ZIP containing &lt;code&gt;word/document.xml&lt;/code&gt;. To hit a target size I padded the XML — by appending filler after the closing tag.&lt;/p&gt;

&lt;p&gt;Word opened it. &lt;code&gt;python-docx&lt;/code&gt; did not:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;lxml.etree.XMLSyntaxError: Extra content at the end of the document
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Which is the worst possible failure mode, because &lt;strong&gt;the entire audience for generated Office files is automated pipelines that parse them.&lt;/strong&gt; A fixture that only opens in Word is useless to CI.&lt;/p&gt;

&lt;p&gt;The fix is to put the padding somewhere the XML spec permits — inside a comment, before the closing tag:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight xml"&gt;&lt;code&gt;&lt;span class="nt"&gt;&amp;lt;w:body&amp;gt;&lt;/span&gt;...&lt;span class="nt"&gt;&amp;lt;/w:body&amp;gt;&lt;/span&gt;&lt;span class="c"&gt;&amp;lt;!--AAAAAAAA...--&amp;gt;&lt;/span&gt;&lt;span class="nt"&gt;&amp;lt;/w:document&amp;gt;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now &lt;code&gt;python-docx&lt;/code&gt; and &lt;code&gt;openpyxl&lt;/code&gt; both accept it, and the byte count is still exact.&lt;/p&gt;

&lt;h2&gt;
  
  
  PNG and JPEG: two different legal hiding places
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;PNG&lt;/strong&gt; is a chunk stream. Padding goes in a &lt;code&gt;tEXt&lt;/code&gt; ancillary chunk inserted &lt;em&gt;before&lt;/em&gt; &lt;code&gt;IEND&lt;/code&gt;, with a correct CRC-32. Decoders skip unknown ancillary chunks, so the image still decodes.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;JPEG&lt;/strong&gt; uses &lt;code&gt;FFFE&lt;/code&gt; COM markers, each holding up to 65,533 bytes, chained for larger targets. Placement matters: &lt;strong&gt;put them immediately after SOI, not near EOI.&lt;/strong&gt; Padding near the end decodes unreliably across libraries — I never fully root-caused why, and moving it made the problem disappear.&lt;/p&gt;

&lt;p&gt;Verify with something strict:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;PIL&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;Image&lt;/span&gt;
&lt;span class="n"&gt;Image&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;open&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;out.png&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;verify&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;   &lt;span class="c1"&gt;# catches CRC and chunk errors
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Structured text has a minimum viable size
&lt;/h2&gt;

&lt;p&gt;Ask for a 20-byte JSON file and there is no correct answer. The smallest valid JSON document with any structure is larger than that.&lt;/p&gt;

&lt;p&gt;My generator quietly fell back to emitting whitespace — which produced a file of exactly the right size that failed &lt;code&gt;JSON.parse()&lt;/code&gt;. Silent, and much worse than an error.&lt;/p&gt;

&lt;p&gt;Empirically measured floors for my output shape:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;JSON   69 bytes
XML   114 bytes
SVG   170 bytes
PDF   600 bytes
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Below those, round up and tell the user. Don't emit something that satisfies the size check and violates the format.&lt;/p&gt;

&lt;h2&gt;
  
  
  PPTX: don't hand-roll the theme
&lt;/h2&gt;

&lt;p&gt;PPTX needs &lt;code&gt;presentation.xml&lt;/code&gt;, &lt;code&gt;presProps.xml&lt;/code&gt;, a slide master, a slide layout, and a theme — and the master and layout reference &lt;strong&gt;each other&lt;/strong&gt;. Get it slightly wrong and PowerPoint shows a repair prompt, which fails the whole "produces valid files" promise.&lt;/p&gt;

&lt;p&gt;I stopped trying. Instead I ship a known-good minimal deck as a static asset, fetch it at runtime, clone its blank slide &lt;em&gt;N&lt;/em&gt; times, and patch three files: &lt;code&gt;[Content_Types].xml&lt;/code&gt;, &lt;code&gt;presentation.xml.rels&lt;/code&gt;, and &lt;code&gt;presentation.xml&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Exact sizing comes from a binary search on an &lt;strong&gt;orphan part&lt;/strong&gt; — &lt;code&gt;ppt/pad.xml&lt;/code&gt;, covered by the default XML content type and referenced by nothing. PowerPoint ignores unreferenced parts entirely.&lt;/p&gt;

&lt;p&gt;Borrowing a validated artifact beat generating one from spec.&lt;/p&gt;

&lt;h2&gt;
  
  
  The actual lesson
&lt;/h2&gt;

&lt;p&gt;Every bug above passed the test I was running and failed the test my users would run.&lt;/p&gt;

&lt;p&gt;&lt;code&gt;dd&lt;/code&gt; and &lt;code&gt;fsutil&lt;/code&gt; hit the size exactly and produce nothing parseable. My early versions produced something parseable &lt;em&gt;by lenient readers&lt;/em&gt; and broken for everyone else. Both are the same category of mistake: validating against a tolerant consumer.&lt;/p&gt;

&lt;p&gt;So validate with the strictest parser your users will realistically reach for. For this project that meant a matrix across &lt;code&gt;pypdf&lt;/code&gt;, &lt;code&gt;python-docx&lt;/code&gt;, &lt;code&gt;openpyxl&lt;/code&gt;, &lt;code&gt;Pillow&lt;/code&gt;, &lt;code&gt;wave&lt;/code&gt; and a hand-written MP4 box-walker — every format, at several sizes, including the degenerate small ones where most of the bugs lived.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;"It opens on my machine" is not validation. It's a single data point from the most forgiving reader you own.&lt;/strong&gt;&lt;/p&gt;




&lt;p&gt;&lt;em&gt;I maintain a &lt;a href="https://sarvkit.com/DevKit/sample-file-generator/" rel="noopener noreferrer"&gt;browser-based sample file generator&lt;/a&gt; that implements all of this — it runs entirely client-side, so nothing is uploaded. Happy to answer format-specific questions in the comments.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>testing</category>
      <category>webdev</category>
      <category>python</category>
      <category>javascript</category>
    </item>
  </channel>
</rss>
