How to fix vertical text filling in PDF?
Fixing PDF Forms That Show Sideways / Vertical Text When Filled
Some PDF forms (often government forms such as LDSS-4411) are saved as landscape pages stored rotated 90°. Each form field also carries a matching 90° rotation flag. When you fill the form with a tool that ignores that flag, your typed text is written in the page's true orientation and ends up rotated sideways / vertical.
The fix is to flatten the rotation: bake the page rotation into the page content, reset each page to 0°, move every field box into the natural orientation, and clear the per-field rotation flags. After this, any fill tool writes upright, horizontal text. The visible layout, field names, and any existing values are unchanged.
Requirements
- Python 3.8+
- The
pypdflibrary:
pip install pypdfPython script
Save this as fix_rotated_pdf_form.py.
#!/usr/bin/env python3
"""
fix_rotated_pdf_form.py
Flattens page rotation in a PDF form so filled-in text renders horizontally
instead of sideways. Layout, field names, and values are preserved.
Usage:
python fix_rotated_pdf_form.py input.pdf
python fix_rotated_pdf_form.py input.pdf -o output.pdf
Requires: pypdf -> pip install pypdf
"""
import argparse
import sys
try:
from pypdf import PdfReader, PdfWriter, Transformation
from pypdf.generic import (
NameObject, NumberObject, ArrayObject, FloatObject,
IndirectObject, BooleanObject,
)
except ImportError:
sys.exit("pypdf is required. Install it with: pip install pypdf")
def _resolve(obj):
return obj.get_object() if isinstance(obj, IndirectObject) else obj
def flatten_rotation(src_path, out_path):
reader = PdfReader(src_path)
writer = PdfWriter()
writer.append(reader)
changed_pages = 0
for page in writer.pages:
rot = int(page.get("/Rotate") or 0) % 360
if rot == 0:
continue
box = [float(v) for v in page.mediabox]
w = box[2] - box[0]
h = box[3] - box[1]
# Matrix that reproduces the visual rotation in un-rotated page space.
if rot == 90:
ctm = (0, -1, 1, 0, 0, w)
new_w, new_h = h, w
elif rot == 270:
ctm = (0, 1, -1, 0, h, 0)
new_w, new_h = h, w
elif rot == 180:
ctm = (-1, 0, 0, -1, w, h)
new_w, new_h = w, h
else:
continue # non-orthogonal rotation; skip safely
page.add_transformation(Transformation(ctm))
page.mediabox.lower_left = (0, 0)
page.mediabox.upper_right = (new_w, new_h)
if "/CropBox" in page:
del page[NameObject("/CropBox")]
page[NameObject("/Rotate")] = NumberObject(0)
a, b, c, d, e, f = ctm
def tx(x, y):
return (a * x + c * y + e, b * x + d * y + f)
annots = _resolve(page.get("/Annots")) or []
for ann in annots:
o = ann.get_object()
if o.get("/Subtype") != "/Widget":
continue
r = [float(v) for v in o["/Rect"]]
pts = [tx(r[0], r[1]), tx(r[2], r[3]), tx(r[0], r[3]), tx(r[2], r[1])]
xs = [p[0] for p in pts]
ys = [p[1] for p in pts]
o[NameObject("/Rect")] = ArrayObject([
FloatObject(min(xs)), FloatObject(min(ys)),
FloatObject(max(xs)), FloatObject(max(ys)),
])
mk = o.get("/MK")
if mk is not None:
mk = mk.get_object()
if "/R" in mk:
mk[NameObject("/R")] = NumberObject(0)
if "/AP" in o:
del o[NameObject("/AP")] # drop stale appearance
changed_pages += 1
acro = writer._root_object.get("/AcroForm")
if acro is not None:
acro = acro.get_object()
acro[NameObject("/NeedAppearances")] = BooleanObject(True)
with open(out_path, "wb") as fh:
writer.write(fh)
return changed_pages
def main():
ap = argparse.ArgumentParser(
description="Flatten page rotation in a PDF form so filled text is horizontal."
)
ap.add_argument("input", help="Path to the rotated PDF form")
ap.add_argument("-o", "--output", help="Output path (default: <input>_fixed.pdf)")
args = ap.parse_args()
out = args.output or (args.input.rsplit(".", 1)[0] + "_fixed.pdf")
n = flatten_rotation(args.input, out)
if n:
print(f"Fixed {n} rotated page(s). Saved: {out}")
else:
print(f"No rotated pages found; nothing to change. Copy saved: {out}")
if __name__ == "__main__":
main()Run it
# Creates LDSS-4411_fixed.pdf next to the input
python fix_rotated_pdf_form.py LDSS-4411.pdf
# Or choose your own output name
python fix_rotated_pdf_form.py LDSS-4411.pdf -o LDSS-4411_fixed.pdfOne-step bash wrapper (optional)
Save as fix_pdf.sh, then chmod +x fix_pdf.sh. It installs pypdf if needed and runs the fixer.
#!/usr/bin/env bash
# Usage: ./fix_pdf.sh input.pdf [output.pdf]
set -euo pipefail
if [ $# -lt 1 ]; then
echo "Usage: $0 input.pdf [output.pdf]"
exit 1
fi
IN="$1"
OUT="${2:-${1%.pdf}_fixed.pdf}"
# Ensure pypdf is available
python3 -c "import pypdf" 2>/dev/null || pip install --quiet pypdf
# Run the fixer (expects fix_rotated_pdf_form.py in the same folder)
DIR="$(cd "$(dirname "$0")" && pwd)"
python3 "$DIR/fix_rotated_pdf_form.py" "$IN" -o "$OUT"
echo "Saved: $OUT"./fix_pdf.sh LDSS-4411.pdfHow to confirm it worked
Open the fixed PDF, type into any field, and the text should appear upright and horizontal. You can also check that the rotation is gone:
python3 - <<'PY'
from pypdf import PdfReader
r = PdfReader("LDSS-4411_fixed.pdf")
for i, p in enumerate(r.pages):
print(f"page {i}: /Rotate = {p.get('/Rotate')}")
PYEvery page should now report /Rotate = 0.
Notes & troubleshooting
- Nothing changes in the form itself. Only page orientation metadata and field box coordinates are adjusted. Static text, field names, and existing values stay identical.
- "No rotated pages found" means the form was not rotated — the sideways text has a different cause (e.g. a font/encoding issue in your fill tool).
- Already-filled PDFs: this script also works on filled forms, but if the bad (rotated) appearance was already baked in by another tool, re-open the fixed file in your form filler and re-save so appearances regenerate.
- Works for 90°, 180°, and 270° rotations. Non-right-angle rotations are left untouched.
- This is a generic fix for any rotated AcroForm PDF, not just LDSS-4411.
Updated about 2 hours ago
