Compare commits

..

10 commits
0.5 ... main

6 changed files with 118 additions and 38 deletions

View file

@ -2,6 +2,16 @@
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)
----------------

View file

@ -48,9 +48,8 @@ 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. 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
appveyor. The resulting artifacts are attached to each release:
https://gitlab.mister-muffin.de/josch/plakativ/releases
Complex Layouter
================

View file

@ -14,6 +14,9 @@ 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

@ -25,6 +25,11 @@ 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
@ -46,7 +51,7 @@ except ImportError:
tkinter.Menubutton = dummy
tkinter.LabelFrame = dummy
VERSION = "0.5"
VERSION = "0.5.2"
PAGE_SIZES = OrderedDict(
[
@ -267,7 +272,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,
@ -281,7 +286,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,
@ -326,17 +331,30 @@ class Plakativ:
return len(self.doc)
def get_input_page_size(self):
width = self.doc[self.pagenr].getDisplayList().rect.width
height = self.doc[self.pagenr].getDisplayList().rect.height
return (width, height)
# 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)
def get_image(self, zoom):
mat_0 = fitz.Matrix(zoom, zoom)
pix = (
self.doc[self.pagenr].getDisplayList().getPixmap(matrix=mat_0, alpha=False)
)
# the getImageData() function was only introduced in pymupdf 1.14.5
# 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")
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()
@ -369,8 +387,18 @@ class Plakativ:
printable_height = self.layout["output_pagesize"][1] - (
border_top + border_bottom
)
inpage_width = pt_to_mm(self.doc[self.pagenr].getDisplayList().rect.width)
inpage_height = pt_to_mm(self.doc[self.pagenr].getDisplayList().rect.height)
# 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)
if mode in ["size", "mult"]:
if mode == "size":
@ -570,7 +598,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:
@ -625,7 +653,12 @@ class Plakativ:
if not hasattr(self, "layout"):
raise LayoutNotComputedException()
inpage_width = pt_to_mm(self.doc[self.pagenr].getDisplayList().rect.width)
# 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)
outdoc = fitz.open()
@ -644,7 +677,13 @@ class Plakativ:
)
/ (self.layout["overallsize"][1]),
)
page = outdoc.newPage(
# 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(
-1, # insert after last page
width=mm_to_pt(self.layout["output_pagesize"][0]),
height=mm_to_pt(self.layout["output_pagesize"][1]),
@ -674,8 +713,15 @@ class Plakativ:
bottom = self.layout["border_right"] * zoom_1
left = self.layout["border_bottom"] * zoom_1
# inner rectangle
shape = page.newShape()
shape.drawRect(
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(
fitz.Rect(
x0,
y0,
@ -685,7 +731,7 @@ class Plakativ:
)
shape.finish(color=(0, 0, 1))
# outer rectangle
shape.drawRect(
dr(
fitz.Rect(
x0 - left,
y0 - top,
@ -714,7 +760,12 @@ class Plakativ:
else:
page_width = mm_to_pt(self.layout["output_pagesize"][1])
page_height = mm_to_pt(self.layout["output_pagesize"][0])
page = outdoc.newPage(
# 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(
-1, width=page_width, height=page_height # insert after last page
)
@ -758,17 +809,29 @@ class Plakativ:
mm_to_pt(factor * (target_y + target_height)),
)
page.showPDFpage(
# 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(
targetrect, # fill the whole page
self.doc, # input document
self.pagenr, # input page number
clip=sourcerect, # part of the input page to use
)
shape = page.newShape()
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
if guides:
if portrait:
shape.drawRect(
dr(
fitz.Rect(
mm_to_pt(self.layout["border_left"]),
mm_to_pt(self.layout["border_top"]),
@ -777,7 +840,7 @@ class Plakativ:
)
)
else:
shape.drawRect(
dr(
fitz.Rect(
mm_to_pt(self.layout["border_bottom"]),
mm_to_pt(self.layout["border_left"]),
@ -813,7 +876,7 @@ class Plakativ:
)
if border:
if portrait:
shape.drawRect(
dr(
fitz.Rect(
mm_to_pt(self.layout["border_left"] - x),
mm_to_pt(self.layout["border_top"] - y),
@ -830,7 +893,7 @@ class Plakativ:
)
)
else:
shape.drawRect(
dr(
fitz.Rect(
mm_to_pt(self.layout["border_bottom"] - x),
mm_to_pt(self.layout["border_left"] - y),
@ -988,7 +1051,10 @@ class Application(tkinter.Frame):
top_frame = tkinter.Frame(frame_right)
top_frame.pack(fill=tkinter.X)
tkinter.Button(top_frame, text="Open PDF", command=self.on_open_button).pack(
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(
side=tkinter.LEFT, expand=tkinter.TRUE, fill=tkinter.X
)
tkinter.Button(top_frame, text="Help", state=tkinter.DISABLED).pack(
@ -1142,10 +1208,13 @@ 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 "Open PDF" button in the upper right.',
text='Click on the "%s" button in the upper right.' % button_text,
fill="white",
)
return
@ -1201,7 +1270,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
@ -1289,8 +1358,6 @@ 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)
@ -1647,6 +1714,7 @@ 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]):

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.newPage(pno=-1, width=width, height=height)
img = page.newShape()
page = doc.new_page(pno=-1, width=width, height=height)
img = page.new_shape()
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.pageCount), expected):
for pnum, (bbox, matrix) in zip(range(doc.page_count), 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.xrefObject(xref).splitlines()
for line in doc.xref_object(xref).splitlines()
if " " in line.strip()
)
assert "/BBox" in keyvals

View file

@ -1,6 +1,6 @@
from setuptools import setup
VERSION = "0.5"
VERSION = "0.5.2"
setup(
name="plakativ",