@@ -47,12 +47,13 @@ def _get_antialias_mode(pd: dict) -> int:
4747
4848from ...core import types as ps
4949from ..common .cairo_renderer import render_display_list
50+ from ..common .orientation import detect_landscape
5051
5152# Check for PySide6 availability
5253try :
5354 from PySide6 .QtWidgets import QApplication , QMainWindow , QWidget
5455 from PySide6 .QtCore import Qt
55- from PySide6 .QtGui import QImage , QPainter , QKeyEvent , QWheelEvent , QMouseEvent , QCursor
56+ from PySide6 .QtGui import QImage , QPainter , QKeyEvent , QWheelEvent , QMouseEvent , QCursor , QTransform
5657 PYSIDE6_AVAILABLE = True
5758except ImportError :
5859 PYSIDE6_AVAILABLE = False
@@ -302,9 +303,9 @@ def _ensure_window(page_width: float | None = None, page_height: float | None =
302303 If the rendered image is smaller than what would fill the default window,
303304 the window shrinks to fit the image at 1:1 pixel size instead of enlarging it.
304305
305- Width is capped at 60% of screen width and height at 85% of screen height,
306- so landscape content doesn't produce overly wide windows while portrait
307- content still uses most of the vertical space.
306+ Portrait pages use 60% width / 85% height caps so windows stay narrow.
307+ Landscape pages (after auto-rotation) use 85% width / 85% height so the
308+ wider content gets adequate horizontal space.
308309
309310 Args:
310311 page_width: Page width (any units, used for aspect ratio)
@@ -327,12 +328,15 @@ def _ensure_window(page_width: float | None = None, page_height: float | None =
327328 screen_width = available .width ()
328329 screen_height = available .height ()
329330
330- # Calculate maximum window size — 60% width, 85% height
331- max_width = int (screen_width * 0.60 )
331+ # Landscape pages get more horizontal space; portrait pages stay narrow
332+ if page_width > page_height :
333+ max_width = int (screen_width * 0.85 )
334+ else :
335+ max_width = int (screen_width * 0.60 )
332336 max_height = int (screen_height * 0.85 )
333337
334338 if image_width and image_height :
335- # Use exact image pixel dimensions, capped at max screen size
339+ # Use image pixel dimensions, capped at max screen size
336340 win_width = image_width
337341 win_height = image_height
338342
@@ -428,6 +432,31 @@ def _wait_for_keypress(ctxt: ps.Context) -> None:
428432 _app .processEvents ()
429433
430434
435+ def _detect_page_rotation (pd : dict , display_list : list ,
436+ width_pts : float , height_pts : float ) -> int :
437+ """Detect page rotation for landscape content on portrait pages.
438+
439+ Uses DSC ``%%Orientation`` when available (set by cli_runner from DSC
440+ parsing), otherwise falls back to CTM heuristic analysis. Only applies
441+ to portrait pages — landscape MediaBox pages need no rotation.
442+
443+ Args:
444+ pd: Page device dictionary.
445+ display_list: Display list elements from the context.
446+ width_pts: Page width in points.
447+ height_pts: Page height in points.
448+
449+ Returns:
450+ Rotation angle (0, 90, or 270).
451+ """
452+ if width_pts >= height_pts :
453+ return 0
454+ dsc_orient = pd .get (b'DSCOrientation' )
455+ if dsc_orient is not None and dsc_orient .val == b'Landscape' :
456+ return 90
457+ return detect_landscape (display_list , width_pts , height_pts )
458+
459+
431460def _render_to_window (ctxt : ps .Context , pd : dict ) -> None :
432461 """Render the current display list to the Qt window.
433462
@@ -457,43 +486,31 @@ def _render_to_window(ctxt: ps.Context, pd: dict) -> None:
457486 device_w = pd [b"MediaSize" ].get (ps .Int (0 ))[1 ].val
458487 device_h = pd [b"MediaSize" ].get (ps .Int (1 ))[1 ].val
459488
460- # Cap render dimensions to avoid Cairo surface allocation failures
461- # on screen display. When the device resolution produces surfaces
462- # larger than this limit, render to a smaller surface and scale the
463- # Cairo context so the display list (in device coords) fits.
464- MAX_SURFACE_PIXELS = 16384
465- downscale = 1.0
466- render_w = device_w
467- render_h = device_h
468- if device_w > MAX_SURFACE_PIXELS or device_h > MAX_SURFACE_PIXELS :
469- downscale = MAX_SURFACE_PIXELS / max (device_w , device_h )
470- render_w = int (device_w * downscale )
471- render_h = int (device_h * downscale )
472-
473- # Calculate page dimensions in points for window sizing (from actual HWResolution)
489+ # Calculate page dimensions in points (from actual HWResolution)
474490 hw_dpi = pd [b"HWResolution" ].get (ps .Int (0 ))[1 ].val
475- scale = hw_dpi / 72.0
476- _page_width = device_w / scale
477- _page_height = device_h / scale
478-
479- # Create Cairo surface at (possibly capped) render resolution
491+ dpi_scale = hw_dpi / 72.0
492+ _page_width = device_w / dpi_scale
493+ _page_height = device_h / dpi_scale
494+
495+ # Detect rotation for landscape content on portrait pages
496+ page_rotate = _detect_page_rotation (
497+ pd , ctxt .display_list , _page_width , _page_height )
498+
499+ # Create Cairo surface at device resolution (DPI was pre-computed by
500+ # _auto_set_qt_resolution to fill ~85% of screen height)
501+ render_w = int (device_w )
502+ render_h = int (device_h )
480503 _surface = cairo .ImageSurface (cairo .FORMAT_RGB24 , render_w , render_h )
481504 cc = cairo .Context (_surface )
482505
483506 cc .identity_matrix ()
484- # Scale Cairo context to map device coordinates into the capped surface
485- if downscale != 1.0 :
486- cc .scale (downscale , downscale )
487507 # Convert PostScript flatness to Cairo tolerance (PS default 1.0 → Cairo default 0.1)
488508 cc .set_tolerance (ctxt .gstate .flatness / 10.0 )
489509
490- # Fill white background (in surface coordinates)
491- cc .save ()
492- cc .identity_matrix ()
510+ # Fill white background
493511 cc .set_source_rgb (1.0 , 1.0 , 1.0 )
494512 cc .rectangle (0 , 0 , render_w , render_h )
495513 cc .fill ()
496- cc .restore ()
497514
498515 cc .set_antialias (_get_antialias_mode (pd ))
499516
@@ -503,12 +520,17 @@ def _render_to_window(ctxt: ps.Context, pd: dict) -> None:
503520 # Convert Cairo surface to QImage
504521 _qimage = _cairo_surface_to_qimage (_surface )
505522
523+ # Auto-rotate landscape content rendered on portrait pages
524+ if page_rotate in (90 , 270 ):
525+ _qimage = _qimage .transformed (QTransform ().rotate (page_rotate ))
526+ _page_width , _page_height = _page_height , _page_width
527+
506528 # Reset view state for new page
507529 _zoom_level = 1.0
508530 _pan_x = 0
509531 _pan_y = 0
510532
511- # Ensure window exists and update (use page dimensions in points for aspect ratio)
533+ # Ensure window exists and update
512534 _ensure_window (_page_width , _page_height , _qimage .width (), _qimage .height ())
513535
514536 _canvas .update ()
0 commit comments