Skip to content

Commit

Permalink
Merge branch 'crop-white-border'
Browse files Browse the repository at this point in the history
  • Loading branch information
jeromerobert committed Aug 12, 2020
2 parents 9ad0770 + 9016e07 commit c5c41cb
Show file tree
Hide file tree
Showing 5 changed files with 300 additions and 148 deletions.
8 changes: 8 additions & 0 deletions data/menu.ui
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,10 @@ along with PDF-Arranger. If not, see <http://www.gnu.org/licenses/>.
<attribute name="label" translatable="yes">Cro_p</attribute>
<attribute name="action">win.crop</attribute>
</item>
<item>
<attribute name="label" translatable="yes">Crop White Borders</attribute>
<attribute name="action">win.crop-white-borders</attribute>
</item>
<item>
<attribute name="label" translatable="yes">_Delete</attribute>
<attribute name="action">win.delete</attribute>
Expand Down Expand Up @@ -262,6 +266,10 @@ along with PDF-Arranger. If not, see <http://www.gnu.org/licenses/>.
<attribute name="label" translatable="yes">Cro_p</attribute>
<attribute name="action">win.crop</attribute>
</item>
<item>
<attribute name="label" translatable="yes">Crop White Borders</attribute>
<attribute name="action">win.crop-white-borders</attribute>
</item>
<item>
<attribute name="label" translatable="yes">Dupl_icate</attribute>
<attribute name="action">win.duplicate</attribute>
Expand Down
172 changes: 172 additions & 0 deletions pdfarranger/croputils.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,172 @@
# Copyright (C) 2020 pdfarranger contributors
#
# pdfarranger is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 3 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License along
# with this program; if not, write to the Free Software Foundation, Inc.,
# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.

from gi.repository import Gtk
import gettext
import cairo


_ = gettext.gettext


def dialog(model, selection, window):
"""Opens a dialog box to define margins for page cropping"""

sides = ('L', 'R', 'T', 'B')
side_names = {'L': _('Left'), 'R': _('Right'), 'T': _('Top'), 'B': _('Bottom')}
opposite_sides = {'L': 'R', 'R': 'L', 'T': 'B', 'B': 'T'}

def set_crop_value(spinbutton, side):
opp_side = opposite_sides[side]
adjustment = spin_list[sides.index(opp_side)].get_adjustment()
adjustment.set_upper(99.0 - spinbutton.get_value())

crop = [0.0, 0.0, 0.0, 0.0]
if selection:
path = selection[0]
pos = model.get_iter(path)
crop = [model.get_value(pos, 7 + side) for side in range(4)]

dialog = Gtk.Dialog(
title=(_('Crop Selected Pages')),
parent=window,
flags=Gtk.DialogFlags.MODAL,
buttons=(
Gtk.STOCK_CANCEL,
Gtk.ResponseType.CANCEL,
Gtk.STOCK_OK,
Gtk.ResponseType.OK,
),
)
dialog.set_default_response(Gtk.ResponseType.OK)
dialog.set_resizable(False)
margin = 12
label = Gtk.Label(
label=_(
'Cropping does not remove any content\n'
'from the PDF file, it only hides it.'
)
)
dialog.vbox.pack_start(label, False, False, 0)
frame = Gtk.Frame(label=_('Crop Margins'))
frame.props.margin = margin
dialog.vbox.pack_start(frame, True, True, 0)
grid = Gtk.Grid()
grid.set_column_spacing(margin)
grid.set_row_spacing(margin)
grid.props.margin = margin
frame.add(grid)

spin_list = []
units = 2 * [_('% of width')] + 2 * [_('% of height')]
for row, side in enumerate(sides):
label = Gtk.Label(label=side_names[side])
label.set_alignment(0, 0)
grid.attach(label, 0, row, 1, 1)

adj = Gtk.Adjustment(
value=100.0 * crop.pop(0),
lower=0.0,
upper=99.0,
step_increment=1.0,
page_increment=5.0,
page_size=0.0,
)
spin = Gtk.SpinButton(adjustment=adj, climb_rate=0, digits=1)
spin.set_activates_default(True)
spin.connect('value-changed', set_crop_value, side)
spin_list.append(spin)
grid.attach(spin, 1, row, 1, 1)

label = Gtk.Label(label=units.pop(0))
label.set_alignment(0, 0)
grid.attach(label, 2, row, 1, 1)

dialog.show_all()
result = dialog.run()

crop = None
if result == Gtk.ResponseType.OK:
crop = [spin.get_value() / 100.0 for spin in spin_list]
crop = [crop] * len(selection)
dialog.destroy()
return crop


def white_borders(model, selection, pdfqueue):
crop = []
for path in selection:
it = model.get_iter(path)
nfile, npage = model.get(it, 2, 3,)
pdfdoc = pdfqueue[nfile - 1]

page = pdfdoc.document.get_page(npage - 1)
w, h = page.get_size()
w = int(w)
h = int(h)
thumbnail = cairo.ImageSurface(cairo.FORMAT_ARGB32, w, h)
cr = cairo.Context(thumbnail)
page.render(cr)
data = thumbnail.get_data().cast("i", shape=[h, w]).tolist()

crop_this_page = [0.0, 0.0, 0.0, 0.0]

# Left
allwhite = True
for col in range(w - 1):
for row in range(h - 1):
if data[row][col] != 0:
allwhite = False
crop_this_page[0] = (col) / w
break
if not allwhite:
break

# Right
allwhite = True
for col in range(w - 1, 0, -1):
for row in range(h - 1):
if data[row][col] != 0:
allwhite = False
crop_this_page[1] = (w - col) / w
break
if not allwhite:
break

# Top
allwhite = True
for row in range(h - 1):
for col in range(w - 1):
if data[row][col] != 0:
allwhite = False
crop_this_page[2] = (row) / h
break
if not allwhite:
break

# Bottom
allwhite = True
for row in range(h - 1, 0, -1):
for col in range(w - 1):
if data[row][col] != 0:
allwhite = False
crop_this_page[3] = (h - row) / h
break
if not allwhite:
break

crop.append(crop_this_page)
return crop
81 changes: 12 additions & 69 deletions pdfarranger/pdfarranger.py
Original file line number Diff line number Diff line change
Expand Up @@ -115,6 +115,7 @@
from . import undo
from . import exporter
from . import metadata
from . import croputils
from .iconview import CellRendererImage
GObject.type_register(CellRendererImage)

Expand Down Expand Up @@ -335,6 +336,7 @@ def __create_actions(self):
('delete', self.on_action_delete),
('duplicate', self.duplicate),
('crop', self.crop_page_dialog),
('crop-white-borders', self.crop_white_borders),
('export-selection', self.choose_export_selection_pdf_name),
('reverse-order', self.reverse_order),
('save', self.on_action_save),
Expand Down Expand Up @@ -1526,80 +1528,21 @@ def edit_metadata(self, _action, _parameter, _unknown):

def crop_page_dialog(self, _action, _parameter, _unknown):
"""Opens a dialog box to define margins for page cropping"""

sides = ('L', 'R', 'T', 'B')
side_names = {'L': _('Left'), 'R': _('Right'),
'T': _('Top'), 'B': _('Bottom')}
opposite_sides = {'L': 'R', 'R': 'L', 'T': 'B', 'B': 'T'}

def set_crop_value(spinbutton, side):
opp_side = opposite_sides[side]
adjustment = spin_list[sides.index(opp_side)].get_adjustment()
adjustment.set_upper(99.0 - spinbutton.get_value())

model = self.iconview.get_model()
selection = self.iconview.get_selected_items()

crop = [0., 0., 0., 0.]
if selection:
path = selection[0]
pos = model.get_iter(path)
crop = [model.get_value(pos, 7 + side) for side in range(4)]

dialog = Gtk.Dialog(title=(_('Crop Selected Pages')),
parent=self.window,
flags=Gtk.DialogFlags.MODAL,
buttons=(Gtk.STOCK_CANCEL, Gtk.ResponseType.CANCEL,
Gtk.STOCK_OK, Gtk.ResponseType.OK))
dialog.set_default_response(Gtk.ResponseType.OK)
dialog.set_resizable(False)
margin = 12
label = Gtk.Label(label=_('Cropping does not remove any content\n'
'from the PDF file, it only hides it.'))
dialog.vbox.pack_start(label, False, False, 0)
frame = Gtk.Frame(label=_('Crop Margins'))
frame.props.margin = margin
dialog.vbox.pack_start(frame, True, True, 0)
grid = Gtk.Grid()
grid.set_column_spacing(margin)
grid.set_row_spacing(margin)
grid.props.margin = margin
frame.add(grid)

spin_list = []
units = 2 * [_('% of width')] + 2 * [_('% of height')]
for row, side in enumerate(sides):
label = Gtk.Label(label=side_names[side])
label.set_alignment(0, 0)
grid.attach(label, 0, row, 1, 1)

adj = Gtk.Adjustment(value=100. * crop.pop(0),
lower=0.0,
upper=99.0,
step_increment=1.0,
page_increment=5.0,
page_size=0.0)
spin = Gtk.SpinButton(adjustment=adj, climb_rate=0, digits=1)
spin.set_activates_default(True)
spin.connect('value-changed', set_crop_value, side)
spin_list.append(spin)
grid.attach(spin, 1, row, 1, 1)

label = Gtk.Label(label=units.pop(0))
label.set_alignment(0, 0)
grid.attach(label, 2, row, 1, 1)

dialog.show_all()
result = dialog.run()

if result == Gtk.ResponseType.OK:
crop = [spin.get_value() / 100. for spin in spin_list]
crop = [crop] * len(selection)
crop = croputils.dialog(self.iconview.get_model(), selection, self.window)
if crop is not None:
self.undomanager.commit("Crop")
oldcrop = self.crop(selection, crop)
if oldcrop != crop:
self.set_unsaved(True)
dialog.destroy()

def crop_white_borders(self, _action, _parameter, _unknown):
selection = self.iconview.get_selected_items()
crop = croputils.white_borders(self.iconview.get_model(), selection, self.pdfqueue)
self.undomanager.commit("Crop white Borders")
oldcrop = self.crop(selection, crop)
if oldcrop != crop:
self.set_unsaved(True)

def crop(self, selection, newcrop):
oldcrop = [[0] * 4 for __ in range(len(selection))]
Expand Down
1 change: 1 addition & 0 deletions po/POTFILES.in
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
pdfarranger/pdfarranger.py
pdfarranger/metadata.py
pdfarranger/croputils.py
data/pdfarranger.ui
data/menu.ui

0 comments on commit c5c41cb

Please sign in to comment.