diff --git a/CONTRIBUTORS.md b/CONTRIBUTORS.md index 7f7c39aca..d9e30952d 100644 --- a/CONTRIBUTORS.md +++ b/CONTRIBUTORS.md @@ -54,6 +54,7 @@ history and [GitHubs 'Contributors' feature](https://github.com/py-pdf/pypdf/gra * [WevertonGomes](https://github.com/WevertonGomesCosta) * [Wilson, Huon](https://github.com/huonw) * ztravis +* [Stober, Marc](https://github.com/marcstober) ## Adding a new contributor diff --git a/docs/index.rst b/docs/index.rst index 83d81a0ef..7c8627ffe 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -35,6 +35,7 @@ You can contribute to `pypdf on GitHub `_. user/add-watermark user/reading-pdf-annotations user/adding-pdf-annotations + user/viewer-preferences user/forms user/streaming-data user/file-size diff --git a/docs/user/viewer-preferences.md b/docs/user/viewer-preferences.md new file mode 100644 index 000000000..d242bcae8 --- /dev/null +++ b/docs/user/viewer-preferences.md @@ -0,0 +1,79 @@ +# Adding Viewer Preferences + +It is possible to set viewer preferences of the PDF file. +These properties are described in Section 12.2 of the [PDF 1.7 specification](https://opensource.adobe.com/dc-acrobat-sdk-docs/pdfstandards/PDF32000_2008.pdf). + +Note that the `\ViewerPreferences` dictionary does not exist by default. +If it's not already present, it must be created by calling the `create_viewer_preferences` method +of the `PdfWriter` object. + +If viewer preferences exist in a PDF file being read with `PdfReader`, +you can access them as properties of the `viewer_preferences` properties. +Otherwise, the `viewer_preferences` property will be set to `None`. + +## Example + +```python +from pypdf import PdfWriter +from pypdf.generic import ArrayObject, NumberObject + +writer = PdfWriter() + +writer.create_viewer_preferences() + +# /HideToolbar +writer.viewer_preferences.hide_toolbar = True +# /HideMenubar +writer.viewer_preferences.hide_menubar = True +# /HideWindowUI +writer.viewer_preferences.hide_windowui = True +# /FitWindow +writer.viewer_preferences.fit_window = True +# /CenterWindow +writer.viewer_preferences.center_window = True +# /DisplayDocTitle +writer.viewer_preferences.display_doctitle = True + +# /NonFullScreenPageMode +writer.viewer_preferences.non_fullscreen_pagemode = "/UseNone" # default +writer.viewer_preferences.non_fullscreen_pagemode = "/UseOutlines" +writer.viewer_preferences.non_fullscreen_pagemode = "/UseThumbs" +writer.viewer_preferences.non_fullscreen_pagemode = "/UseOC" + +# /Direction +writer.viewer_preferences.direction = "/L2R" # default, +writer.viewer_preferences.direction = "/R2L" + +# /ViewArea +writer.viewer_preferences.view_area = "/CropBox" +# /ViewClip +writer.viewer_preferences.view_clip = "/CropBox" +# /PrintArea +writer.viewer_preferences.print_area = "/CropBox" +# /PrintClip +writer.viewer_preferences.print_clip = "/CropBox" + +# /PrintScaling +writer.viewer_preferences.print_scaling = "/None" +writer.viewer_preferences.print_scaling = "/AppDefault" # default according to PDF spec + +# /Duplex +writer.viewer_preferences.duplex = "/Simplex" +writer.viewer_preferences.duplex = "/DuplexFlipShortEdge" +writer.viewer_preferences.duplex = "/DuplexFlipLongEdge" + +# /PickTrayByPDFSize +writer.viewer_preferences.pick_tray_by_pdfsize = True +# /PrintPageRange +writer.viewer_preferences.print_pagerange = ArrayObject( + [NumberObject("1"), NumberObject("10"), NumberObject("20"), NumberObject("30")] +) +# /NumCopies +writer.viewer_preferences.num_copies = 2 + +for i in range(40): + writer.add_blank_page(10, 10) + +with open("output.pdf", "wb") as output_stream: + writer.write(output_stream) +``` diff --git a/pypdf/_writer.py b/pypdf/_writer.py index 25a6444d3..e3fe5bb07 100644 --- a/pypdf/_writer.py +++ b/pypdf/_writer.py @@ -384,7 +384,7 @@ def viewer_preferences(self) -> Optional[ViewerPreferences]: self._root_object[NameObject(CD.VIEWER_PREFERENCES)] = o return o - def create_viewer_preference(self) -> ViewerPreferences: + def create_viewer_preferences(self) -> ViewerPreferences: o = ViewerPreferences() self._root_object[NameObject(CD.VIEWER_PREFERENCES)] = self._add_object(o) return o diff --git a/pypdf/generic/_viewerpref.py b/pypdf/generic/_viewerpref.py index 763f4d166..3b874e476 100644 --- a/pypdf/generic/_viewerpref.py +++ b/pypdf/generic/_viewerpref.py @@ -140,8 +140,6 @@ def _add_prop_int(key: str, deft: Optional[int]) -> property: cls.print_pagerange = _add_prop_arr("/PrintPageRange", None) cls.num_copies = _add_prop_int("/NumCopies", None) - # still to be done /PrintPageRange and /NumCopies - return DictionaryObject.__new__(cls) def __init__(self, obj: Optional[DictionaryObject] = None) -> None: diff --git a/tests/test_writer.py b/tests/test_writer.py index d956fa9e7..adf4075b0 100644 --- a/tests/test_writer.py +++ b/tests/test_writer.py @@ -1774,10 +1774,7 @@ def test_damaged_pdf_length_returning_none(): @pytest.mark.enable_socket() def test_viewerpreferences(): - """ - Add Tests for ViewerPreferences - https://github.com/py-pdf/pypdf/issues/140#issuecomment-1685380549 - """ + """Add Tests for ViewerPreferences""" url = "https://github.com/py-pdf/pypdf/files/9175966/2015._pb_decode_pg0.pdf" name = "2015._pb_decode_pg0.pdf" reader = PdfReader(BytesIO(get_data_from_url(url, name=name))) @@ -1828,8 +1825,10 @@ def test_viewerpreferences(): v.print_pagerange = ArrayObject() assert len(v.print_pagerange) == 0 - writer.create_viewer_preference() + writer.create_viewer_preferences() assert len(writer._root_object["/ViewerPreferences"]) == 0 + writer.viewer_preferences.direction = "/R2L" + assert len(writer._root_object["/ViewerPreferences"]) == 1 del reader.trailer["/Root"]["/ViewerPreferences"] assert reader.viewer_preferences is None