Compare commits

..

No commits in common. "main" and "441ec3a6213233b714130f35ebc9ec77da68fcd5" have entirely different histories.

7 changed files with 43 additions and 128 deletions

View file

@ -2,21 +2,6 @@
CHANGES
=======
0.5.2 (2024-01-12)
------------------
- support for pymupdf 1.23.0
0.5.1 (2022-07-23)
------------------
- support for pymupdf 1.20.0
0.5 (2021-10-11)
----------------
- support for HiDPI displays
0.4 (2021-03-04)
----------------

View file

@ -48,8 +48,9 @@ Plakativ is available from pypi: https://pypi.org/project/plakativ/
Thus you can just run `pip install plakativ` on your platform of choice.
For Microsoft Windows users, PyInstaller based .exe files are produced by
appveyor. The resulting artifacts are attached to each release:
https://gitlab.mister-muffin.de/josch/plakativ/releases
appveyor. If you don't want to install Python before using plakativ you can
head to appveyor and click on "Artifacts" to download the latest version:
https://ci.appveyor.com/project/josch/plakativ
Complex Layouter
================

View file

@ -14,9 +14,6 @@ environment:
# - PYTHON: "C:\\Python35-x64"
# - PYTHON: "C:\\Python36-x64"
- PYTHON: "C:\\Python37-x64"
platform: x64
- PYTHON: "C:\\Python37"
platform: x86
install:
- "%PYTHON%\\python.exe -m pip install wheel PyMuPDF pytest pyinstaller"

View file

@ -7,7 +7,7 @@
<metadata_license>FSFAP</metadata_license>
<project_license>GPL-3.0</project_license>
<developer_name>Johannes Schauer Marin Rodrigues</developer_name>
<developer_name>Johannes Schauer</developer_name>
<description>
<p>

View file

@ -6,7 +6,7 @@
# pieces and putting each of them onto a paper size that can be printed
# normally. The result can then be glued together into a bigger poster.
#
# Copyright (C) 2019 - 2021 Johannes Schauer Marin Rodrigues <josch@mister-muffin.de>
# Copyright (C) 2019 Johannes 'josch' Schauer
#
# This program is free software: you can redistribute it and/or modify it under
# the terms of the GNU General Public License version 3 as published by the
@ -25,11 +25,6 @@ import logging
have_img2pdf = True
try:
from PIL import Image
# ignore PIL limit because this software is meant to create posters which
# naturally can be very large in size
Image.MAX_IMAGE_PIXELS = None
import img2pdf
except ImportError:
have_img2pdf = False
@ -51,7 +46,7 @@ except ImportError:
tkinter.Menubutton = dummy
tkinter.LabelFrame = dummy
VERSION = "0.5.2"
VERSION = "0.4"
PAGE_SIZES = OrderedDict(
[
@ -272,7 +267,7 @@ def complex_cover(n, m, x, y):
if X4 > 0 and Y4 > 0:
simple_config, (sx, sy) = simple_cover(X4, Y4, x, y)
# shift the results such that they are in the center
for cx, cy, p in simple_config:
for (cx, cy, p) in simple_config:
newconfig.append(
(
w0 * X(r, 0) + (X4 - sx) / 2 + cx,
@ -286,7 +281,7 @@ def complex_cover(n, m, x, y):
if X4 > 0 and Y4 > 0:
simple_config, (sx, sy) = simple_cover(X4, Y4, x, y)
# shift the results such that they are in the center
for cx, cy, p in simple_config:
for (cx, cy, p) in simple_config:
newconfig.append(
(
w3 * X(r, 3) + (X4 - sx) / 2 + cx,
@ -331,30 +326,17 @@ class Plakativ:
return len(self.doc)
def get_input_page_size(self):
# since pymupdf 1.19.0 a warning will be issued if the deprecated names are used
if hasattr(self.doc[self.pagenr], "get_displaylist"):
gdl = self.doc[self.pagenr].get_displaylist
else:
gdl = self.doc[self.pagenr].getDisplayList
rect = gdl().rect
return (rect.width, rect.height)
width = self.doc[self.pagenr].getDisplayList().rect.width
height = self.doc[self.pagenr].getDisplayList().rect.height
return (width, height)
def get_image(self, zoom):
mat_0 = fitz.Matrix(zoom, zoom)
# since pymupdf 1.19.0 a warning will be issued if the deprecated names are used
if hasattr(self.doc[self.pagenr], "get_displaylist"):
gdl = self.doc[self.pagenr].get_displaylist()
else:
gdl = self.doc[self.pagenr].getDisplayList()
if hasattr(gdl, "get_pixmap"):
pix = gdl.get_pixmap(matrix=mat_0, alpha=False)
else:
pix = gdl.getPixmap(matrix=mat_0, alpha=False)
if hasattr(pix, "tobytes"):
# getImageData was deprecated in pymupdf 1.19.0
return pix.tobytes("ppm")
pix = (
self.doc[self.pagenr].getDisplayList().getPixmap(matrix=mat_0, alpha=False)
)
# the getImageData() function was only introduced in pymupdf 1.14.5
if hasattr(pix, "getImageData"):
# the getImageData() function was only introduced in pymupdf 1.14.5
return pix.getImageData("ppm")
else:
# this is essentially the same thing that the getImageData()
@ -387,18 +369,8 @@ class Plakativ:
printable_height = self.layout["output_pagesize"][1] - (
border_top + border_bottom
)
# since pymupdf 1.19.0 a warning will be issued if the deprecated names are used
if hasattr(self.doc[self.pagenr], "get_displaylist"):
gdl = self.doc[self.pagenr].get_displaylist
else:
gdl = self.doc[self.pagenr].getDisplayList
# this may fail with "RuntimeError: image is too wide"
# from pdf_load_image_imp() in pdf-image.c from mupdf for sizes larger
# than 1<<16 pixels:
# https://bugs.ghostscript.com/show_bug.cgi?id=703839
rect = gdl().rect
inpage_width = pt_to_mm(rect.width)
inpage_height = pt_to_mm(rect.height)
inpage_width = pt_to_mm(self.doc[self.pagenr].getDisplayList().rect.width)
inpage_height = pt_to_mm(self.doc[self.pagenr].getDisplayList().rect.height)
if mode in ["size", "mult"]:
if mode == "size":
@ -598,7 +570,7 @@ class Plakativ:
# the computed positions and storing the largest border size in
# each dimension
poster_top = poster_right = poster_bottom = poster_left = 0
for posx, posy, p in self.layout["positions"]:
for (posx, posy, p) in self.layout["positions"]:
if p:
top = posy - border_top
if top < 0 and -top > poster_top:
@ -653,12 +625,7 @@ class Plakativ:
if not hasattr(self, "layout"):
raise LayoutNotComputedException()
# since pymupdf 1.19.0 a warning will be issued if the deprecated names are used
if hasattr(self.doc[self.pagenr], "get_displaylist"):
gdl = self.doc[self.pagenr].get_displaylist
else:
gdl = self.doc[self.pagenr].getDisplayList
inpage_width = pt_to_mm(gdl().rect.width)
inpage_width = pt_to_mm(self.doc[self.pagenr].getDisplayList().rect.width)
outdoc = fitz.open()
@ -677,13 +644,7 @@ class Plakativ:
)
/ (self.layout["overallsize"][1]),
)
# since pymupdf 1.19.0 a warning will be issued if the deprecated names are used
if hasattr(outdoc, "new_page"):
np = outdoc.new_page
else:
np = outdoc.newPage
page = np(
page = outdoc.newPage(
-1, # insert after last page
width=mm_to_pt(self.layout["output_pagesize"][0]),
height=mm_to_pt(self.layout["output_pagesize"][1]),
@ -713,15 +674,8 @@ class Plakativ:
bottom = self.layout["border_right"] * zoom_1
left = self.layout["border_bottom"] * zoom_1
# inner rectangle
if hasattr(page, "new_shape"):
shape = page.new_shape()
else:
shape = page.newShape()
if hasattr(shape, "draw_rect"):
dr = shape.draw_rect
else:
dr = shape.drawRect
dr(
shape = page.newShape()
shape.drawRect(
fitz.Rect(
x0,
y0,
@ -731,7 +685,7 @@ class Plakativ:
)
shape.finish(color=(0, 0, 1))
# outer rectangle
dr(
shape.drawRect(
fitz.Rect(
x0 - left,
y0 - top,
@ -760,12 +714,7 @@ class Plakativ:
else:
page_width = mm_to_pt(self.layout["output_pagesize"][1])
page_height = mm_to_pt(self.layout["output_pagesize"][0])
# since pymupdf 1.19.0 a warning will be issued if the deprecated names are used
if hasattr(outdoc, "new_page"):
np = outdoc.new_page
else:
np = outdoc.newPage
page = np(
page = outdoc.newPage(
-1, width=page_width, height=page_height # insert after last page
)
@ -809,29 +758,17 @@ class Plakativ:
mm_to_pt(factor * (target_y + target_height)),
)
# since pymupdf 1.19.0 a warning will be issued if the deprecated names are used
if hasattr(page, "show_pdf_page"):
spp = page.show_pdf_page
else:
spp = page.showPDFpage
spp(
page.showPDFpage(
targetrect, # fill the whole page
self.doc, # input document
self.pagenr, # input page number
clip=sourcerect, # part of the input page to use
)
if hasattr(page, "new_shape"):
shape = page.new_shape()
else:
shape = page.newShape()
if hasattr(shape, "draw_rect"):
dr = shape.draw_rect
else:
dr = shape.drawRect
shape = page.newShape()
if guides:
if portrait:
dr(
shape.drawRect(
fitz.Rect(
mm_to_pt(self.layout["border_left"]),
mm_to_pt(self.layout["border_top"]),
@ -840,7 +777,7 @@ class Plakativ:
)
)
else:
dr(
shape.drawRect(
fitz.Rect(
mm_to_pt(self.layout["border_bottom"]),
mm_to_pt(self.layout["border_left"]),
@ -876,7 +813,7 @@ class Plakativ:
)
if border:
if portrait:
dr(
shape.drawRect(
fitz.Rect(
mm_to_pt(self.layout["border_left"] - x),
mm_to_pt(self.layout["border_top"] - y),
@ -893,7 +830,7 @@ class Plakativ:
)
)
else:
dr(
shape.drawRect(
fitz.Rect(
mm_to_pt(self.layout["border_bottom"] - x),
mm_to_pt(self.layout["border_left"] - y),
@ -942,7 +879,7 @@ class VerticalScrolledFrame(tkinter.Frame):
borderwidth=0,
highlightthickness=0,
yscrollcommand=vscrollbar.set,
width=240 * parent.winfo_fpixels("1i") / 96.0,
width=240,
)
canvas.pack(side=tkinter.LEFT, fill=tkinter.BOTH, expand=tkinter.TRUE)
vscrollbar.config(command=canvas.yview)
@ -1051,10 +988,7 @@ class Application(tkinter.Frame):
top_frame = tkinter.Frame(frame_right)
top_frame.pack(fill=tkinter.X)
button_text = "Open PDF"
if have_img2pdf:
button_text = "Open PDF, JPG, PNG, TIF"
tkinter.Button(top_frame, text=button_text, command=self.on_open_button).pack(
tkinter.Button(top_frame, text="Open PDF", command=self.on_open_button).pack(
side=tkinter.LEFT, expand=tkinter.TRUE, fill=tkinter.X
)
tkinter.Button(top_frame, text="Help", state=tkinter.DISABLED).pack(
@ -1208,13 +1142,10 @@ class Application(tkinter.Frame):
self.canvas.delete(tkinter.ALL)
if not hasattr(self, "plakativ"):
button_text = "Open PDF"
if have_img2pdf:
button_text = "Open PDF, JPG, PNG, TIF"
self.canvas.create_text(
self.canvas_size[0] / 2,
self.canvas_size[1] / 2,
text='Click on the "%s" button in the upper right.' % button_text,
text='Click on the "Open PDF" button in the upper right.',
fill="white",
)
return
@ -1270,7 +1201,7 @@ class Application(tkinter.Frame):
# draw rectangles
# TODO: also draw numbers indicating the page number
for x, y, portrait in self.plakativ.layout["positions"]:
for (x, y, portrait) in self.plakativ.layout["positions"]:
x0 = (x + self.plakativ.layout["posterpos"][0]) * zoom_1 + (
self.canvas_size[0] - zoom_1 * self.plakativ.layout["overallsize"][0]
) / 2
@ -1358,6 +1289,8 @@ class Application(tkinter.Frame):
)
# remove alpha channel
if remove_alpha:
from PIL import Image
img = Image.open(self.filename).convert("RGBA")
background = Image.new("RGBA", img.size, (255, 255, 255))
img = Image.alpha_composite(background, img)
@ -1714,7 +1647,6 @@ class BorderSizeWidget(tkinter.LabelFrame):
]
):
self.variables[n] = tkinter.DoubleVar()
# need to pass k and v as function arguments so that their value
# does not get overwritten each loop iteration
def callback(varname, idx, op, k_copy=n, v_copy=self.variables[n]):
@ -2270,7 +2202,7 @@ you can instruct plakativ to remove the alpha channel for you with the
$ plakativ --size A1 --output=poster.pdf --remove-alpha input.png
Written by Johannes Schauer Marin Rodrigues <josch@mister-muffin.de>
Written by Johannes 'josch' Schauer <josch@mister-muffin.de>
Report bugs at https://gitlab.mister-muffin.de/josch/plakativ/issues
"""

View file

@ -149,8 +149,8 @@ def test_cases(postersize, input_pagesize, output_pagesize, strategy, expected):
height = mm_to_pt(input_pagesize[1])
doc = fitz.open()
page = doc.new_page(pno=-1, width=width, height=height)
img = page.new_shape()
page = doc.newPage(pno=-1, width=width, height=height)
img = page.newShape()
red = fitz.utils.getColor("red")
green = fitz.utils.getColor("green")
@ -189,7 +189,7 @@ def test_cases(postersize, input_pagesize, output_pagesize, strategy, expected):
doc = fitz.open(outfile)
for pnum, (bbox, matrix) in zip(range(doc.page_count), expected):
for pnum, (bbox, matrix) in zip(range(doc.pageCount), expected):
xreflist = doc._getPageInfo(pnum, 3)
assert len(xreflist) >= 1
xref, name, _, _ = xreflist[0]
@ -209,7 +209,7 @@ def test_cases(postersize, input_pagesize, output_pagesize, strategy, expected):
# >>
keyvals = dict(
tuple(line.strip().split(maxsplit=1))
for line in doc.xref_object(xref).splitlines()
for line in doc.xrefObject(xref).splitlines()
if " " in line.strip()
)
assert "/BBox" in keyvals

View file

@ -1,11 +1,11 @@
from setuptools import setup
VERSION = "0.5.2"
VERSION = "0.4"
setup(
name="plakativ",
version=VERSION,
author="Johannes Schauer Marin Rodrigues",
author="Johannes 'josch' Schauer",
author_email="josch@mister-muffin.de",
description="Convert a PDF into a large poster that can be printed on multiple smaller pages.",
long_description="file: README.md, CHANGELOG.rst",