Skip to content

Commit

Permalink
Improve handling of scaling and rotation at printing
Browse files Browse the repository at this point in the history
New print options:
* No page scaling
* Fit to printable area
* Fit to full page
* Auto rotate page
The options can be set in print dialog (Linux & Windows 10)
and also in preferences dialog (Only way in Windows 11)
* Allow change of landscape/portrait and page size (Linux & Windows 10)
Close pdfarranger#1015
  • Loading branch information
kbengs committed Jan 26, 2024
1 parent 7b73781 commit d290202
Show file tree
Hide file tree
Showing 2 changed files with 103 additions and 6 deletions.
43 changes: 38 additions & 5 deletions pdfarranger/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -113,6 +113,8 @@ def __init__(self, domain):
self.data.read(Config._config_file(domain))
if 'preferences' not in self.data:
self.data.add_section('preferences')
if 'print-settings' not in self.data:
self.data.add_section('print-settings')
if 'accelerators' not in self.data:
self.data.add_section('accelerators')
a = self.data['accelerators']
Expand Down Expand Up @@ -185,6 +187,18 @@ def theme(self):
def set_theme(self, theme):
self.data.set('preferences', 'theme', theme)

def scaling_mode(self):
return self.data.getint('print-settings', 'scaling-mode', fallback=1)

def set_scaling_mode(self, mode):
self.data.set('print-settings', 'scaling-mode', str(mode))

def auto_rotate(self):
return self.data.getboolean('print-settings', 'auto-rotate', fallback=False)

def set_auto_rotate(self, enabled):
self.data.set('print-settings', 'auto-rotate', str(enabled))

def save(self):
conffile = Config._config_file(self.domain)
os.makedirs(os.path.dirname(conffile), exist_ok=True)
Expand Down Expand Up @@ -222,7 +236,7 @@ def get_accels(self):
]

def preferences_dialog(self, parent, localedir, handy_available):
"""A dialog where language and theme can be selected."""
"""A dialog for some application preferences."""
d = Gtk.Dialog(title=_("Preferences"),
parent=parent,
flags=Gtk.DialogFlags.MODAL,
Expand All @@ -248,11 +262,23 @@ def preferences_dialog(self, parent, localedir, handy_available):
hbox2.pack_start(label2, False, False, 8)
frame2.add(hbox2)
d.vbox.pack_start(frame2, False, False, 8)
t = _("For more options see:")
frame3 = Gtk.Frame(label=t, shadow_type=Gtk.ShadowType.NONE, margin=8)
label3 = Gtk.Label(self._config_file(self.domain), selectable=True, margin=8)
frame3.add(label3)
hbox3 = Gtk.Box(spacing=12, margin=8)
frame3 = Gtk.Frame(label=_("Printing"), margin=8)
label3 = Gtk.Label(_("Page Scaling:"))
combo3 = Gtk.ComboBoxText()
hbox3.pack_start(label3, False, False, 0)
hbox3.pack_start(combo3, False, False, 0)
cb3 = Gtk.CheckButton(label=_("Auto Rotate"), margin=8)
vbox3 = Gtk.Box(margin=8, orientation=Gtk.Orientation.VERTICAL)
vbox3.pack_start(hbox3, False, False, 0)
vbox3.pack_start(cb3, False, False, 0)
frame3.add(vbox3)
d.vbox.pack_start(frame3, False, False, 8)
t = _("For more options see:")
frame4 = Gtk.Frame(label=t, shadow_type=Gtk.ShadowType.NONE, margin=8)
label4 = Gtk.Label(self._config_file(self.domain), selectable=True, margin=8)
frame4.add(label4)
d.vbox.pack_start(frame4, False, False, 8)

langs = []
if os.path.isdir(localedir):
Expand All @@ -276,6 +302,11 @@ def preferences_dialog(self, parent, localedir, handy_available):
else:
combo2.set_active(0)
combo2.set_sensitive(handy_available)
combo3.append(None, _("None"))
combo3.append(None, _("Fit to Printable Area"))
combo3.append(None, _("Fit to Full Page"))
combo3.set_active(self.scaling_mode())
cb3.set_active(self.auto_rotate())

d.show_all()
result = d.run()
Expand All @@ -286,4 +317,6 @@ def preferences_dialog(self, parent, localedir, handy_available):
num2 = combo2.get_active()
theme = themes[num2] if num2 != 0 else ""
self.set_theme(theme)
self.set_scaling_mode(combo3.get_active())
self.set_auto_rotate(cb3.get_active())
d.destroy()
66 changes: 65 additions & 1 deletion pdfarranger/exporter.py
Original file line number Diff line number Diff line change
Expand Up @@ -585,8 +585,11 @@ def generate_booklet(pdfqueue, tmp_dir, pages):
class PrintOperation(Gtk.PrintOperation):
MESSAGE=_("Printing…")
def __init__(self, app):
super().__init__()
super().__init__(embed_page_setup=True)
self.app = app
self.connect("create-custom-widget", self.create_custom_widget)
self.connect("custom-widget-apply", self.custom_widget_apply)
self.connect("request-page-setup", self.request_page_setup)
self.connect("begin-print", self.begin_print, None)
self.connect("end-print", self.end_print, None)
self.connect("draw-page", self.draw_page, None)
Expand All @@ -595,6 +598,41 @@ def __init__(self, app):
self.message = self.MESSAGE
self.pages = [row[0].duplicate(incl_thumbnail=False) for row in app.model]
app.apply_hide_margins_on_pages(self.pages)
self.scaling_mode = self.app.config.scaling_mode()
self.auto_rotate = self.app.config.auto_rotate()
self.set_use_full_page(self.scaling_mode != 1)

def create_custom_widget(self, operation):
self.set_custom_tab_label(_("Page Handling"))
grid = Gtk.Grid(margin=0, row_spacing=6, column_spacing=12, border_width=12)
lbl = Gtk.Label(_("Page Scaling:"), margin=0)
combo = Gtk.ComboBoxText(margin=0)
combo.append(None, _("None"))
combo.append(None, _("Fit to Printable Area"))
combo.append(None, _("Fit to Full Page"))
combo.set_active(self.scaling_mode)
cb = Gtk.CheckButton(label=_("Auto Rotate"), margin=0)
cb.set_active(self.auto_rotate)
grid.attach(lbl, 0, 1, 1, 1)
grid.attach(combo, 1, 1, 2, 1)
grid.attach(cb, 0, 2, 2, 1)
grid.show_all()
return grid

def custom_widget_apply(self, operation, grid):
checkbutton, combo, _label = grid.get_children()
self.scaling_mode = combo.get_active()
self.auto_rotate = checkbutton.get_active()
self.set_use_full_page(self.scaling_mode != 1)

def request_page_setup(self, operation, print_ctx, page_num, setup):
if self.auto_rotate:
w_page = self.pages[page_num].width_in_points()
h_page = self.pages[page_num].height_in_points()
if w_page >= h_page:
setup.set_orientation(Gtk.PageOrientation.LANDSCAPE)
else:
setup.set_orientation(Gtk.PageOrientation.PORTRAIT)

def preview(self, operation, preview_op, print_ctx, parent, user_data):
self.message = _("Rendering Preview…")
Expand All @@ -618,13 +656,39 @@ def begin_print(self, operation, print_ctx, print_data):
def end_print(self, operation, print_ctx, print_data):
self.app.set_export_state(False)
self.message = self.MESSAGE
self.app.config.set_scaling_mode(self.scaling_mode)
self.app.config.set_auto_rotate(self.auto_rotate)

def draw_page(self, operation, print_ctx, page_num, print_data):
cairo_ctx = print_ctx.get_cairo_context()
# Poppler context is always 72 dpi
cairo_ctx.scale(print_ctx.get_dpi_x() / 72, print_ctx.get_dpi_y() / 72)
if page_num >= len(self.app.model):
return
setup = print_ctx.get_page_setup()
if self.scaling_mode == 1:
# Fit to printable area
w_paper = setup.get_page_width(Gtk.Unit.POINTS)
h_paper = setup.get_page_height(Gtk.Unit.POINTS)
else:
# Use full paper
w_paper = setup.get_paper_width(Gtk.Unit.POINTS)
h_paper = setup.get_paper_height(Gtk.Unit.POINTS)
w_page = self.pages[page_num].width_in_points()
h_page = self.pages[page_num].height_in_points()
print_scale = self.get_print_settings().get_scale() / 100
scale = 1
if self.scaling_mode != 0:
w_scale = w_paper / (w_page * print_scale)
h_scale = h_paper / (h_page * print_scale)
scale = min(w_scale, h_scale)
cairo_ctx.scale(scale, scale)

# Center page on paper
dx = w_paper / (scale * print_scale) - w_page
dy = h_paper / (scale * print_scale) - h_page
cairo_ctx.translate(dx / 2, dy / 2)

p = self.pages[page_num]
if p.unmodified():
pdfdoc = self.app.pdfqueue[p.nfile - 1]
Expand Down

0 comments on commit d290202

Please sign in to comment.