Repository: py-pdf/pypdf
Pull request: ROB: MissingWidth is IndirectObject
Fixes #2286

Problem statement:
TypeError while loading some documents, caused by _cmap.py, line 93
While using the library I'm getting the following error:
`TypeError: unsupported operand type(s) for /: 'IndirectObject' and 'int'`

## Environment

Which environment were you using when you encountered the problem?

macOS-10.16-x86_64-i386-64bit
pypdf==3.17.0, crypt_provider=('cryptography', '37.0.4'), PIL=9.0.1

It happens locally as well as during Azure-deployment.

## Code + PDF

This is a minimal, complete example that shows the issue:

```
reader = PdfReader(file)
pages = reader.pages
```

Unfortunately I cannot share the document that causes the problem as it contains sensitive information. I also couldn't reproduce it with other documents. This adjustment after line 89, however, solves the problem:

```
import pypdf
...
sp_width = compute_space_width(ft, sp, space_width)
sp_width = sp_width if type(sp_width) != pypdf.generic._base.IndirectObject else sp_width.get_object()
```

## Traceback

This is the complete Traceback I see:

```
  File "..././scripts/prepdocs.py", line 262, in <module>
    loop.run_until_complete(main(file_strategy, azd_credential, args))
  File ".../opt/anaconda3/lib/python3.9/asyncio/base_events.py", line 642, in run_until_complete
    return future.result()
  File "..././scripts/prepdocs.py", line 137, in main
    await strategy.run(search_info)
  File ".../scripts/prepdocslib/filestrategy.py", line 58, in run
    pages = [page async for page in self.pdf_parser.parse(content=file.content)]
  File ".../scripts/prepdocslib/filestrategy.py", line 58, in <listcomp>
    pages = [page async for page in self.pdf_parser.parse(content=file.content)]
  File ".../scripts/prepdocslib/pdfparser.py", line 52, in parse
    page_text = p.extract_text()
  File ".../scripts/.venv/lib/python3.9/site-packages/pypdf/_page.py", line 2284, in extract_text
    return self._extract_text(
  File ".../scripts/.venv/lib/python3.9/site-packages/pypdf/_page.py", line 1903, in _extract_text
    cmaps[f] = build_char_map(f, space_width, obj)
  File ".../scripts/.venv/lib/python3.9/site-packages/pypdf/_cmap.py", line 29, in build_char_map
    font_subtype, font_halfspace, font_encoding, font_map = build_char_map_from_dict(
  File ".../scripts/.venv/lib/python3.9/site-packages/pypdf/_cmap.py", line 93, in build_char_map_from_dict
    float(sp_width / 2),
TypeError: unsupported operand type(s) for /: 'IndirectObject' and 'int'

```

Hints from issue discussion:
@elhele 
can you in _cmap.py file, at the end of function `compute_space_width` (line460 normally)
```
    if isinstance(sp_width,indirect_object):    ## to be added
        sp_width = sp_width.get_object()      ## to be added
    return sp_width
```

and check weither it fixes the error
Hello @pubpub-zz thank you so much for such a fast reply!

"indirect_object" in the code above doesn't seem to be defined. If I change it in such a way, it works perfectly and fixes the error:

```
    indirect_object = pypdf.generic._base.IndirectObject ## added

    if isinstance(sp_width, indirect_object):  ## added

        sp_width = sp_width.get_object()  ## added

    return sp_width
```

Commit message: ROB: MissingWidth is IndirectObject

Patch to review:
diff --git a/pypdf/_cmap.py b/pypdf/_cmap.py
index 6392805be..8c75baa79 100644
--- a/pypdf/_cmap.py
+++ b/pypdf/_cmap.py
@@ -6,7 +6,7 @@
 from ._codecs import adobe_glyphs, charset_encoding
 from ._utils import b_, logger_warning
 from .errors import PdfReadWarning
-from .generic import DecodedStreamObject, DictionaryObject, StreamObject
+from .generic import DecodedStreamObject, DictionaryObject, IndirectObject, StreamObject
 
 
 # code freely inspired from @twiggy ; see #711
@@ -457,6 +457,17 @@ def compute_space_width(
                         m += x
                         cpt += 1
                 sp_width = m / max(1, cpt) / 2
+
+    if isinstance(sp_width, IndirectObject):
+        # According to
+        # 'Table 122 - Entries common to all font descriptors (continued)'
+        # the MissingWidth should be a number, but according to #2286 it can
+        # be an indirect object
+        obj = sp_width.get_object()
+        if obj is None:
+            return 0.0
+        return obj  # type: ignore
+
     return sp_width
 
 


Code review comments:
diff_hunk: @@ -457,6 +457,17 @@ def compute_space_width(
                         m += x
                         cpt += 1
                 sp_width = m / max(1, cpt) / 2
+
+    if isinstance(sp_width, IndirectObject):
+        # According to
+        # 'Table 122 - Entries common to all font descriptors (continued)'
+        # the MissingWidth should be a number, but according to #2286 it can
+        # be an indirect object
+        obj = sp_width.get_object()
+        if obj is None:
path: pypdf/_cmap.py
text: @user1:
```suggestion
        if obj is None or isinstance(obj, NullObject):
```

diff_hunk: @@ -6,7 +6,7 @@
 from ._codecs import adobe_glyphs, charset_encoding
 from ._utils import b_, logger_warning
 from .errors import PdfReadWarning
-from .generic import DecodedStreamObject, DictionaryObject, StreamObject
+from .generic import DecodedStreamObject, DictionaryObject, IndirectObject, StreamObject
path: pypdf/_cmap.py
text: @user1:
```suggestion
from .generic import DecodedStreamObject, DictionaryObject, IndirectObject, NullObject, StreamObject
```