diff --git a/CHANGELOG.md b/CHANGELOG.md
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,5 +1,12 @@
 # Changelog
 
+## 0.4.6.4 (2026-07-06)
+
+### Fixed
+
+- Content-stream number parsing no longer corrupts negative fractions with an omitted integer part (e.g. `-.24` in a `cm` matrix); the previous bug turned them into large integers, broke the CTM, and left geometry/tagged text extraction empty while legacy/TUI output still looked fine.
+- Golden fixture `data/fixtures/negative-fraction-ctm.pdf` and unit tests for `normalizePdfNumber` / `parsePdfNumber` in both the geometry and legacy lexers.
+
 ## 0.4.6.3 (2026-07-06)
 
 ### Added
diff --git a/hpdft.cabal b/hpdft.cabal
--- a/hpdft.cabal
+++ b/hpdft.cabal
@@ -1,6 +1,6 @@
 cabal-version:       3.8
 name:                hpdft
-version:             0.4.6.3
+version:             0.4.6.4
 synopsis:            PDF parsing library and CLI for text, layout, diff, images, and forms
 description:
     hpdft is a Haskell library and command-line tool for parsing PDF files.
diff --git a/src/PDF/ContentStream.hs b/src/PDF/ContentStream.hs
--- a/src/PDF/ContentStream.hs
+++ b/src/PDF/ContentStream.hs
@@ -3,6 +3,8 @@
 module PDF.ContentStream 
        ( parseStream
        , parseColorSpace
+       , normalizePdfNumber
+       , parsePdfNumber
        ) where
 
 import Data.Char (chr, ord)
@@ -871,7 +873,7 @@
 normalizePdfNumber s
   | null s = s
   | head s == '.' = '0' : s
-  | length s >= 2 && head s == '-' && s !! 1 == '.' = '-' : '0' : drop 2 s
+  | length s >= 2 && head s == '-' && s !! 1 == '.' = '-' : '0' : drop 1 s
   | otherwise = s
 
 parsePdfNumber :: String -> Double
diff --git a/src/PDF/Interpret.hs b/src/PDF/Interpret.hs
--- a/src/PDF/Interpret.hs
+++ b/src/PDF/Interpret.hs
@@ -17,6 +17,8 @@
   , bytesToCodes
   , encodingUnicode
   , unicodeBytesToCodes
+  , normalizePdfNumber
+  , parsePdfNumber
   ) where
 
 import PDF.Definition
@@ -1023,7 +1025,7 @@
 normalizePdfNumber s
   | null s = s
   | head s == '.' = '0' : s
-  | length s >= 2 && head s == '-' && s !! 1 == '.' = '-' : '0' : drop 2 s
+  | length s >= 2 && head s == '-' && s !! 1 == '.' = '-' : '0' : drop 1 s
   | otherwise = s
 
 parsePdfNumber :: String -> Double
diff --git a/test/Unit.hs b/test/Unit.hs
--- a/test/Unit.hs
+++ b/test/Unit.hs
@@ -12,6 +12,8 @@
   , encodingUnicode
   , unicodeBytesToCodes
   )
+import qualified PDF.Interpret as Interpret
+import qualified PDF.ContentStream as ContentStream
 import PDF.Layout (LayoutOptions(..), defaultLayoutOptions, needsAozoraBar, aozoraRuby, layoutParagraphs, layoutParagraphsWith, layoutPageText, layoutDocument, sortLinesByReadingOrder, linesFromGlyphs, Line(..))
 import PDF.Structure (StructElem(..), StructKid(..), structTree, logicalOrder)
 import PDF.Document (Document(..), openDocument)
@@ -187,6 +189,7 @@
           ++ taggedEndToEndResults
           ++ textStreamResults
           ++ cmapEncodingResults
+          ++ normalizePdfNumberResults
           ++ tuiScrollResults
   let failures = [msg | Fail msg <- results]
       passed = length results - length failures
@@ -1376,4 +1379,43 @@
       , assertBool "bytesToCodes JISmap 2-byte fixed"
           (bytesToCodes jfi [0x46, 0x7C, 0x4B, 0x5C] == [0x467C, 0x4B5C])
       ]
+
+normalizePdfNumberResults :: [Result]
+normalizePdfNumberResults =
+  let cases =
+        [ (".5", 0.5)
+        , ("-.5", -0.5)
+        , ("-.23999999", -0.23999999)
+        , ("1.5", 1.5)
+        , ("-1.5", -1.5)
+        , ("5.", 5.0)
+        ]
+      abnormal =
+        [ ""
+        , "-"
+        , "+"
+        , "-.5."
+        , "."
+        , "-."
+        ]
+   in [ assertDoubleEq ("Interpret.parsePdfNumber " ++ s) n (Interpret.parsePdfNumber s)
+      | (s, n) <- cases
+      ]
+      ++ [ assertDoubleEq ("ContentStream.parsePdfNumber " ++ s) n (ContentStream.parsePdfNumber s)
+         | (s, n) <- cases
+         ]
+      ++ [ assertBool ("Interpret.parsePdfNumber abnormal " ++ show s)
+             (Interpret.parsePdfNumber s `seq` True)
+         | s <- abnormal
+         ]
+      ++ [ assertBool ("ContentStream.parsePdfNumber abnormal " ++ show s)
+             (ContentStream.parsePdfNumber s `seq` True)
+         | s <- abnormal
+         ]
+      ++ [ assertTextEq ("Interpret.normalizePdfNumber " ++ s) (T.pack expected) (T.pack (Interpret.normalizePdfNumber s))
+         | (s, expected) <- [(".5", "0.5"), ("-.5", "-0.5"), ("-.23999999", "-0.23999999")]
+         ]
+      ++ [ assertTextEq ("ContentStream.normalizePdfNumber " ++ s) (T.pack expected) (T.pack (ContentStream.normalizePdfNumber s))
+         | (s, expected) <- [(".5", "0.5"), ("-.5", "-0.5"), ("-.23999999", "-0.23999999")]
+         ]
 
