Python3: Reportlab Image - ResourceWarning: unclosed file <_io.BufferedReader name=...> - python

When I run a unit test, I'm getting Python 3 unclosed buffer error on the "logo" image in the following code. How do I close the logo image buffer correctly? Please be aware that the Image class is coming from reportlab.platypus.
I have tried logo.close() and with Image(logo_path) as logo:, both of them does not work.
>>python -m unittest tests.test_sample_pdf
>>/tests/test_sample_pdf.py:51: ResourceWarning: unclosed file <_io.BufferedReader name='/Users/my_prj/statics/my-logo.gif'>
get_pdf()
Source Code
import unittest
import os
from io import BytesIO
from os.path import abspath, dirname
from reportlab.lib.colors import HexColor
from reportlab.lib.enums import TA_RIGHT
from reportlab.lib.styles import getSampleStyleSheet, ParagraphStyle
from reportlab.lib.units import inch, cm, mm
from reportlab.platypus import SimpleDocTemplate, Table, TableStyle, BaseDocTemplate, Paragraph, Image, Spacer
COL_SORT = [{"headerName": "name",
"field": "name",
"width": 1000,}]
def get_pdf():
# setup PDF template
buffer = BytesIO()
side_margin = 12
col_widths = [row['width'] for row in COL_SORT]
page_width = sum(col_widths) + side_margin * 3
pdf = SimpleDocTemplate(buffer, pagesize=(page_width, 8.5 * inch), rightMargin=side_margin, leftMargin=side_margin,
topMargin=side_margin, bottomMargin=side_margin)
elements = []
# logo
parent_dir = dirname(dirname(abspath(__file__)))
logo_path = os.path.join(parent_dir, 'statics', 'my-logo.gif')
logo = Image(logo_path)
logo.hAlign = 'LEFT'
heading_style = ParagraphStyle(name='heading', fontSize=16, leading=20, spaceAfter=0,
textColor=HexColor('#ffffff'), backColor=HexColor('#465a81'))
heading_right_style = ParagraphStyle(name='heading', fontSize=16, leading=20, spaceAfter=0,
textColor=HexColor('#ffffff'), backColor=HexColor('#465a81'),
alignment=TA_RIGHT)
logo_tbl = Table([[logo]], colWidths=sum(col_widths))
logo_tbl.hAlign = 'LEFT'
logo_tbl.setStyle(TableStyle([('BACKGROUND', (0, 0), (-1, -1), HexColor('#B90002'))]))
elements.append(logo_tbl)
# build PDF
pdf.build(elements)
pdf_string = buffer.getvalue()
buffer.close()
class TestPDF(unittest.TestCase):
def test_pdf(self):
get_pdf()

It seems that reportlab expects that you open and close the image file. Use with open(logo_path, 'rb') as image_fd:.
This workaround solves the Warning. I've added the mentioned with and indented its following lines.
def get_pdf():
# setup PDF template
buffer = BytesIO()
side_margin = 12
col_widths = [row['width'] for row in COL_SORT]
page_width = sum(col_widths) + side_margin * 3
pdf = SimpleDocTemplate(buffer, pagesize=(page_width, 8.5 * inch), rightMargin=side_margin, leftMargin=side_margin,
topMargin=side_margin, bottomMargin=side_margin)
elements = []
# logo
parent_dir = dirname(dirname(abspath(__file__)))
logo_path = os.path.join(parent_dir, 'statics', 'nci-logo.gif')
with open(logo_path, 'rb') as image_fd: # edited this line
logo = Image(image_fd) # ... and this line
logo.hAlign = 'LEFT'
heading_style = ParagraphStyle(name='heading', fontSize=16, leading=20, spaceAfter=0,
textColor=HexColor('#ffffff'), backColor=HexColor('#465a81'))
heading_right_style = ParagraphStyle(name='heading', fontSize=16, leading=20, spaceAfter=0,
textColor=HexColor('#ffffff'), backColor=HexColor('#465a81'),
alignment=TA_RIGHT)
logo_tbl = Table([[logo]], colWidths=sum(col_widths))
logo_tbl.hAlign = 'LEFT'
logo_tbl.setStyle(TableStyle([('BACKGROUND', (0, 0), (-1, -1), HexColor('#B90002'))]))
elements.append(logo_tbl)
# build PDF
pdf.build(elements)
pdf_string = buffer.getvalue()
buffer.close()
Output:
$ python -m unittest tests.test_sample_pdf
.
----------------------------------------------------------------------
Ran 1 test in 0.042s
OK
I've put the complete example in Github

Related

reportlab.pdfgen generating corrupted PDF

I have a Django Python 3.6.5 view that's intended to write and export a report to PDF using reportlab.pdfgen. I can get the PDF file to generate when the view is called, but the .pdf file that's generated is corrupted. I'm following reportlab's documentation as best as I can tell, is there an issue with how I'm creating and writing the PDF?
from datetime import datetime
from django.http import FileResponse
from io import BytesIO
from reportlab.lib.pagesizes import letter
from reportlab.pdfgen import canvas
from django.conf import settings
from django.core.files.storage import get_storage_class
from django.http import HttpResponse
from django.shortcuts import redirect
from app.models import Organization
from app.models import Question, Report
# add a footer to the PDF
def add_pdf_footer(pdf):
date_str = datetime.now().strftime("%Y-%m-%d")
pdf.saveState()
width, height = letter
pdf.setFont("Helvetica", 9)
text = f"Exported on {date_str}"
pdf.drawRightString(width - 50, 700, text)
pdf.restoreState()
def export_report_pdf(request, report_id):
org = Organization.objects.get(id=request.session["org"])
try:
report = Report.objects.get(id=report_id, org=org)
# build file
response = HttpResponse(content_type='application/pdf')
filename = f"{org.name}_{report.report_type.standard}_report_{report.year}_export_{datetime.now().strftime('%Y-%m-%d_%H-%M-%S')}.pdf"
response["Content-Disposition"] = f"attachment; filename={filename}"
buffer = BytesIO()
sections = report.get_sections()
headers = ["standard", "topic", "code", "question", "value", "units", "note"]
if len(sections) > 1:
headers = ["standard", "topic", "section", "code", "question", "value", "units", "note"]
answers = report.get_or_create_answers(request.user)
answers_to_export = []
for answer in answers:
question = answer.question
if question.dependency is not None:
dependency = question.dependency
dep_answer = list(filter(lambda x: x.question == dependency, answers))[0]
if question.is_dependency_resolved(dep_answer):
answers_to_export.append(answer)
else:
answers_to_export.append(answer)
pdf = canvas.Canvas(buffer, pagesize=letter)
pdf.setFont("Helvetica", 15)
pdf.drawString(100,800, "{org.name} - {report.year} {report.report_type.standard} - {report.report_type.name} Report")
pdf.setFont("Helvetica", 12)
y = 750
for header in headers:
pdf.drawString(100, y, header)
y -= 25
for answer in answers_to_export:
qtype = answer.question.question_type
if qtype == Question.QUESTION_TYPE_MULTISELECT:
value = ", ".join([ans.value for ans in answer.values_selected.all()])
elif qtype == Question.QUESTION_TYPE_SELECT:
value = answer.value_selected.value if answer.value_selected else ""
elif qtype == Question.QUESTION_TYPE_DATE:
value = answer.value_date.strftime("%Y-%m-%d")
else:
value = answer.value
if len(sections) > 1:
pdf.drawString(180, y, answer.question.section)
pdf.drawString(260, y, answer.question.code)
if answer.question.text:
pdf.drawString(340, y, answer.question.text)
if value:
pdf.drawString(420, y, value)
if answer.question.units:
pdf.drawString(500, y, answer.question.units)
if answer.note:
pdf.drawString(580, y, answer.note)
y -= 30
else:
pdf.drawString(260, y, answer.question.code)
if answer.question.text:
pdf.drawString(340, y, answer.question.text)
if value:
pdf.drawString(420, y, value)
if answer.question.units:
pdf.drawString(500, y, answer.question.units)
if answer.note:
pdf.drawString(580, y, answer.note)
y -= 30
# set PDF export footer
add_pdf_footer(pdf)
pdf.save()
# return file
return response
except:
log.error(traceback.print_exc())
return redirect("admin-report")

Reportlab balanced cols control split of flowables

I am using BalancedColumns to generate multiple column layout.
I am not sure, how to resolve an issue of the split happening of the Flowables across the column frames.
I have a heading and it's content. I don't want BalancedColumns to split the flowables in such a way that heading is part of one column and its content is part of another.
The content of the paragraph can split.
The basic python code:
from reportlab.platypus.flowables import BalancedColumns, CondPageBreak
from reportlab.platypus import BaseDocTemplate, Paragraph, Spacer, Frame, PageTemplate, PageBreak
from reportlab.lib.units import mm
from reportlab.lib.pagesizes import A4, LETTER
import os
from reportlab.lib.colors import HexColor
framewidth = A4[0] - 10*mm
frameheight = A4[1] - 20*mm
portrait_frame = Frame(5*mm, 10*mm, framewidth, frameheight,
leftPadding=0,
bottomPadding=0,
rightPadding=0,
topPadding=0,
id=0,
showBoundary=False )
pTemplate = PageTemplate(id=0,frames=[portrait_frame])
templates = [pTemplate]
pdfPath = os.path.abspath(os.path.join(os.path.dirname(__file__), 'balancedColTest.pdf'))
doc = BaseDocTemplate(pdfPath, pagesize=A4, rightMargin=2*mm, leftMargin=2*mm,topMargin=5*mm,bottomMargin=5*mm,showBoundary=0)
doc.addPageTemplates(templates)
# generate stories for balanced columns
story = []
minPadding = 2
for i in range(3):
fs = []
numOfFlowables = random.choice([2, 5, 6, 1, 3])
padding = minPadding + 5*i
for ii in range(numOfFlowables):
text = '<b> <font color="#77D179"> Heading </font></b> <br/>'
heading = Paragraph(text)
heading.keepWithNext = True
fs.append(heading)
fs.append(Paragraph("This is another text in new Para flowable."))
# numCols = 2 if i%2 == 0 else 3
numCols = 2
bCols = BalancedColumns(fs, nCols=numCols, spaceAfter=5*mm, vLinesStrokeColor=HexColor('#77D179'), vLinesStrokeWidth=0.5)
story.append(bCols)
doc.build(story)
Even if I try to use keepWithNext=True the flowables are not part of same frame.
If I use KeepTogether, the BalanceColumns takes the entire space of the page.
heading = Paragraph(text)
content = Paragraph("This is another text in the paragraph")
f= KeepTogether([heading, content])
fs.append(f)
Can anyone suggest a solution so that the heading and its underlying paragraph can remain in the same frame?

Set Author, Title, and Subject for PDF using Reportlab

How can you correctly set the Author, Title and Subject attributes for a PDF File using Reportlab?
I have found the methods in the Reportlab User Guide on page 56, but I am not sure how to implement them correctly.
Below in my PDF cropping and scaling script, I have added the annotations method, but I don't know where to call them from, or if a whole new Canvas object is needed. Please excuse the lengthy code, but only after line 113 is the doc being created, above are mostly auxiliary methods, including the annotations method on line 30.
# All the necessary parameters are accessible after line 92,
# but can of course be changed manually in the Code
# imports for the crop, rename to avoid conflict with reportlab Image import
from PIL import Image as imgPIL
from PIL import ImageChops, ImageOps, ImageFilter
import os.path, sys
# import for the PDF creation
import glob
from reportlab.lib.pagesizes import A4
from reportlab.lib import utils
from reportlab.platypus import Image, SimpleDocTemplate, Spacer
from reportlab.pdfgen import canvas
# get os path for Cropping
path = (os.path.dirname(os.path.abspath("cropPDF.py")))
dirs = os.listdir(path)
def trim(im, border="white"):
bg = imgPIL.new(im.mode, im.size, border)
diff = ImageChops.difference(im, bg)
bbox = diff.getbbox()
if bbox:
return im.crop(bbox)
def annotations(canvas):
canvas.setAuthor("the ReportLab Team")
canvas.setTitle("ReportLab PDF Generation User Guide")
canvas.setSubject("How to Generate PDF files using the ReportLab modules")
def findMaxWidth():
maxWidth = 0
for item in dirs:
try:
fullpath = os.path.join(path, item)
if os.path.isfile(fullpath):
im = imgPIL.open(fullpath)
maxWidth = max(maxWidth, im.size[0])
except:
pass
return maxWidth
def padImages(docHeight):
maxWidth = findMaxWidth()
for item in dirs:
try:
fullpath = os.path.join(path, item)
if os.path.isfile(fullpath):
im = imgPIL.open(fullpath)
f, e = os.path.splitext(fullpath)
width, height = im.size # get the image dimensions, the height is needed for the blank image
if not docHeight <= height: # to prevent oversized images from bein padded, such that they remain centered
image = imgPIL.new('RGB', (maxWidth, height),
(255, 255, 255)) # create a white image with the max width
image.paste(im, (0, 0)) # paste the original image overtop the blank one, flush on the left side
image.save(f + ".png", "PNG", quality=100)
except:
pass
def crop():
for item in dirs:
try:
fullpath = os.path.join(path, item)
if os.path.isfile(fullpath):
im = imgPIL.open(fullpath)
f, e = os.path.splitext(fullpath)
imCrop = trim(im, "white")
imCrop.save(f + ".png", "PNG", quality=100)
except:
pass
def add_page_number(canvas, doc):
canvas.saveState()
canvas.setFont('Times-Roman', numberFontSize)
page_number_text = "%d" % (doc.page)
canvas.drawCentredString(
pageNumberSpacing * mm,
pageNumberSpacing * mm,
page_number_text
)
canvas.restoreState()
#############################
executeCrop = True
executePad = True
outputName = "output.pdf" #The name of the file that will be created
fileAuthor = "Roman Stadler" #these 3 attributes are visible in the file info menu
fileTitle = ""
fileSubject = ""
margin = 0.5
imageWidthDefault = 550
spacerHeight = 7
scalingIfImageTooTall = 0.95 # larger than 95 can result in an empty page after the image
includePagenumbers = True
numberFontSize = 10
pageNumberSpacing = 5
############################
doc = SimpleDocTemplate(
outputName,
topMargin=margin * mm,
leftMargin=margin * mm,
rightMargin=margin * mm,
bottomMargin=margin * mm,
pagesize=A4
)
if executeCrop:
crop()
if executePad:
padImages(doc.height)
filelist = glob.glob("*.png") # Get a list of files in the current directory
filelist.sort()
story = [] # create the list of images for the PDF
for fn in filelist:
img = utils.ImageReader(fn)
img_width, img_height = img.getSize() # necessary for the aspect ratio
aspect = img_height / float(img_width)
documentHeight = doc.height
imageWidth = imageWidthDefault
imageHeight = imageWidth * aspect
if imageHeight > documentHeight:
imageHeight = documentHeight * scalingIfImageTooTall
imageWidth = imageHeight / aspect
img = Image(
fn,
width=imageWidth,
height=imageHeight
)
story.append(img)
space = Spacer(width=0, height=spacerHeight)
story.append(space)
if includePagenumbers and not len(filelist) == 0: # if pagenumbers are desired, or not
doc.build(
story,
onFirstPage=add_page_number,
onLaterPages=add_page_number,
)
elif not len(filelist) == 0:
doc.build(story)
else: # to prevent an empty PDF that can't be opened
print("no files found")
In the meantime, I have found another way, that does not use reportlab, but instead relies on PyPDF2:
The following import is needed:
# PyPDF2 for the metadata modification
from PyPDF2 import PdfFileReader, PdfFileWriter
Then the metadata can be edited like this:
author = "Roman Stadler"
title = "CropPDF"
subject = "Stackoverflow"
#rest of the script
#attemp the metadate edit
try:
file = open('output.pdf', 'rb+')
reader = PdfFileReader(file)
writer = PdfFileWriter()
writer.appendPagesFromReader(reader)
metadata = reader.getDocumentInfo()
writer.addMetadata(metadata)
writer.addMetadata({
'/Author': author,
'/Title': title,
'/Subject' : subject,
'/Producer' : "CropPDF",
'/Creator' : "CropPDF",
})
writer.write(file)
file.close()
except:
print("Error while editing metadata")
You can define attributes like the author when defining the doc as a SimpleDocTemplate
doc = SimpleDocTemplate(
outputName,
topMargin=margin * mm,
leftMargin=margin * mm,
rightMargin=margin * mm,
bottomMargin=margin * mm,
pagesize=A4,
title="This is the title of the document", #exchange with your title
author="John Smith", #exchange with your authors name
subject"Adding metadata to pdf via reportlab" #exchange with your subject
)

Arabic text is not properly wrapped in reportlab's Paragraph flowable

Let’s say I have this Arabic snippet:
إذا أخذنا بعين الإعتبار طبيعة تقلب المناخ و المتغيرات البينية السنوية و تلك على المدى الطويل إضافة إلى عدم دقة القياسات والحسابات المتبعة
In English this should mean something like: “If we take into account the nature of climate variability and inter-annual variability and those on long-term addition to the lack of accuracy of measurements and calculations used….”
Now I want to render it as Reportlab PDF doc (python):
arabic_text = u'إذا أخذنا بعين الإعتبار طبيعة تقلب المناخ و المتغيرات البينية السنوية و تلك على المدى الطويل إضافة إلى عدم دقة القياسات والحسابات المتبعة'
arabic_text = arabic_reshaper.reshape(arabic_text) # join characters
arabic_text = get_display(arabic_text) # change orientation by using bidi
pdf_file=open('disclaimer.pdf','w')
pdf_doc = SimpleDocTemplate(pdf_file, pagesize=A4)
pdfmetrics.registerFont(TTFont('Arabic-normal', '../fonts/KacstOne.ttf'))
style = ParagraphStyle(name='Normal', fontName='Arabic-normal', fontSize=12, leading=12. * 1.2)
style.alignment=TA_RIGHT
pdf_doc.build([Paragraph(arabic_text, style)])
pdf_file.close()
The result is here https://www.dropbox.com/s/gdyt6930jlad8id/disclaimer.pdf. You can see the text itself is correct and readable (at least for Google Translate), but not wrapped as expected for RTL script.
If you use this branch of Report lab that adds RTL support
and remove this line from your code:
arabic_text = get_display(arabic_text) # change orientation by using bidi
your code will run properly
since they have already been solved with the branch using PyFriBiDi
as you can see here:
Some users from the community notably Ury Marshak, Moshe Wagner and Hosam Aly have contributed batches to get PyFriBibi working with ReportLab. We have created an SVN branch for this development and it is available at ...
(The SVN link on that page doesn't work anymore so you should use the bitbucket link I've included!)
I have managed to run this modified version of your code and produce the correct result :
from libs import arabic_reshaper
from bidi.algorithm import get_display
from reportlab.platypus import SimpleDocTemplate, Paragraph
from reportlab.pdfbase import pdfmetrics
from reportlab.lib.styles import ParagraphStyle
from reportlab.lib.enums import TA_RIGHT
from reportlab.lib.pagesizes import A4
from reportlab.pdfbase.ttfonts import TTFont
arabic_text = u'إذا أخذنا بعين الإعتبار طبيعة تقلب المناخ و المتغيرات البينية السنوية و تلك على المدى الطويل إضافة إلى عدم دقة القياسات والحسابات المتبعة'
arabic_text = arabic_reshaper.reshape(arabic_text) # join characters
# arabic_text = get_display(arabic_text) # change orientation by using bidi
pdf_file=open('disclaimer.pdf','w')
pdf_doc = SimpleDocTemplate(pdf_file, pagesize=A4)
pdfmetrics.registerFont(TTFont('Arabic-normal', 'fonts/misc/KacstOne.ttf'))
style = ParagraphStyle(name='Normal', fontName='Arabic-normal', fontSize=12, leading=12. * 1.2)
style.alignment=TA_RIGHT
pdf_doc.build([Paragraph(arabic_text, style)])
pdf_file.close()
use below code. it will wrap it properly.
import arabic_reshaper
from bidi.algorithm import get_display
from reportlab.platypus import SimpleDocTemplate, Paragraph
from reportlab.pdfbase import pdfmetrics
from reportlab.lib.styles import ParagraphStyle
from reportlab.lib.enums import TA_RIGHT
from reportlab.lib.pagesizes import A4
from reportlab.pdfbase.ttfonts import TTFont
from reportlab.pdfbase.pdfmetrics import stringWidth
from reportlab.lib.units import inch
def text_wraping(text,aW):
text_width = stringWidth(text, style.fontName, style.fontSize)
space_width = stringWidth(' ', style.fontName, style.fontSize)
if text_width > aW:
lines = []
text = arabic_text.split(' ')
text.reverse()
line = ''
for word in text:
line_width = stringWidth(line, style.fontName, style.fontSize)
word_width = stringWidth(word, style.fontName, style.fontSize)
if (line_width < aW) and (line_width + word_width + space_width < aW):
line += word + ' '
else:
line = line.split(' ')
line.reverse()
tmp = ' '
line = tmp.join(line)
lines.append(line)
line = word + ' '
line = line.split(' ')
line.reverse()
tmp = ' '
line = tmp.join(line)
lines.append(line)
return(lines)
pdf_doc = SimpleDocTemplate('disclaimer.pdf', pagesize=A4)
pdfmetrics.registerFont(TTFont('Aims', 'Aims.ttf'))
style = ParagraphStyle(name='Normal', fontName='Aims', fontSize=12, leading=12. * 1.2,wordWrap='RTL')
style.alignment=TA_RIGHT
aW = A4[0]-2*inch # change this line to the specified width for example paragraph width or table width
arabic_text = 'إذا أخذنا بعين الإعتبار طبيعة تقلب المناخ و المتغيرات البينية السنوية و تلك على المدى الطويل إضافة إلى عدم دقة القياسات والحسابات المتبعة'
arabic_text = arabic_reshaper.reshape(arabic_text) # join characters
arabic_text = get_display(arabic_text) # change orientation by using bidi
lines = text_wraping(arabic_text,aW)
elements = []
for line in lines:
elements.append(Paragraph(line,style))
pdf_doc.build(elements)
Using the wordwrap module and <br> markup to split up lines; this is not perfect because there is a blank line at the top of each paragraph, but it is a simple solution to some use cases
import textwrap
def ShowArabictext(Text):
#style_comment.alignment = TA_RIGHT
wrkText=Text
isArabic=False
isBidi=False
for c in wrkText:
cat=unicodedata.bidirectional(c)
if cat=="AL" or cat=="AN":
isArabic=True
isBidi=True
break
elif cat=="R" or cat=="RLE" or cat=="RLO":
isBidi=True
if isArabic:
#wrkText=arabic_table(wrkText)
wrkText=textwrap.wrap( wrkText,70)
wrkTexttemp=[]
l=u''
i=0
for w in wrkText:
# break each line with html markup allowed in reportlab
l=l+u'<br></br>'+arabic_rtlize.process.shape(arabic_reshaper.reshape(w ))
wrkText=l
if isBidi:
wrkText=get_display(wrkText)
return [wrkText,isArabic,isBidi]

Take a screenshot via a Python script on Linux

I want to take a screenshot via a python script and unobtrusively save it.
I'm only interested in the Linux solution, and should support any X based environment.
This works without having to use scrot or ImageMagick.
import gtk.gdk
w = gtk.gdk.get_default_root_window()
sz = w.get_size()
print "The size of the window is %d x %d" % sz
pb = gtk.gdk.Pixbuf(gtk.gdk.COLORSPACE_RGB,False,8,sz[0],sz[1])
pb = pb.get_from_drawable(w,w.get_colormap(),0,0,0,0,sz[0],sz[1])
if (pb != None):
pb.save("screenshot.png","png")
print "Screenshot saved to screenshot.png."
else:
print "Unable to get the screenshot."
Borrowed from http://ubuntuforums.org/showpost.php?p=2681009&postcount=5
Just for completeness:
Xlib - But it's somewhat slow when capturing the whole screen:
from Xlib import display, X
import Image #PIL
W,H = 200,200
dsp = display.Display()
try:
root = dsp.screen().root
raw = root.get_image(0, 0, W,H, X.ZPixmap, 0xffffffff)
image = Image.fromstring("RGB", (W, H), raw.data, "raw", "BGRX")
image.show()
finally:
dsp.close()
One could try to trow some types in the bottleneck-files in PyXlib, and then compile it using Cython. That could increase the speed a bit.
Edit:
We can write the core of the function in C, and then use it in python from ctypes, here is something I hacked together:
#include <stdio.h>
#include <X11/X.h>
#include <X11/Xlib.h>
//Compile hint: gcc -shared -O3 -lX11 -fPIC -Wl,-soname,prtscn -o prtscn.so prtscn.c
void getScreen(const int, const int, const int, const int, unsigned char *);
void getScreen(const int xx,const int yy,const int W, const int H, /*out*/ unsigned char * data)
{
Display *display = XOpenDisplay(NULL);
Window root = DefaultRootWindow(display);
XImage *image = XGetImage(display,root, xx,yy, W,H, AllPlanes, ZPixmap);
unsigned long red_mask = image->red_mask;
unsigned long green_mask = image->green_mask;
unsigned long blue_mask = image->blue_mask;
int x, y;
int ii = 0;
for (y = 0; y < H; y++) {
for (x = 0; x < W; x++) {
unsigned long pixel = XGetPixel(image,x,y);
unsigned char blue = (pixel & blue_mask);
unsigned char green = (pixel & green_mask) >> 8;
unsigned char red = (pixel & red_mask) >> 16;
data[ii + 2] = blue;
data[ii + 1] = green;
data[ii + 0] = red;
ii += 3;
}
}
XDestroyImage(image);
XDestroyWindow(display, root);
XCloseDisplay(display);
}
And then the python-file:
import ctypes
import os
from PIL import Image
LibName = 'prtscn.so'
AbsLibPath = os.path.dirname(os.path.abspath(__file__)) + os.path.sep + LibName
grab = ctypes.CDLL(AbsLibPath)
def grab_screen(x1,y1,x2,y2):
w, h = x2-x1, y2-y1
size = w * h
objlength = size * 3
grab.getScreen.argtypes = []
result = (ctypes.c_ubyte*objlength)()
grab.getScreen(x1,y1, w, h, result)
return Image.frombuffer('RGB', (w, h), result, 'raw', 'RGB', 0, 1)
if __name__ == '__main__':
im = grab_screen(0,0,1440,900)
im.show()
Compile all answers in one class.
Outputs PIL image.
#!/usr/bin/env python
# encoding: utf-8
"""
screengrab.py
Created by Alex Snet on 2011-10-10.
Copyright (c) 2011 CodeTeam. All rights reserved.
"""
import sys
import os
import Image
class screengrab:
def __init__(self):
try:
import gtk
except ImportError:
pass
else:
self.screen = self.getScreenByGtk
try:
import PyQt4
except ImportError:
pass
else:
self.screen = self.getScreenByQt
try:
import wx
except ImportError:
pass
else:
self.screen = self.getScreenByWx
try:
import ImageGrab
except ImportError:
pass
else:
self.screen = self.getScreenByPIL
def getScreenByGtk(self):
import gtk.gdk
w = gtk.gdk.get_default_root_window()
sz = w.get_size()
pb = gtk.gdk.Pixbuf(gtk.gdk.COLORSPACE_RGB,False,8,sz[0],sz[1])
pb = pb.get_from_drawable(w,w.get_colormap(),0,0,0,0,sz[0],sz[1])
if pb is None:
return False
else:
width,height = pb.get_width(),pb.get_height()
return Image.fromstring("RGB",(width,height),pb.get_pixels() )
def getScreenByQt(self):
from PyQt4.QtGui import QPixmap, QApplication
from PyQt4.Qt import QBuffer, QIODevice
import StringIO
app = QApplication(sys.argv)
buffer = QBuffer()
buffer.open(QIODevice.ReadWrite)
QPixmap.grabWindow(QApplication.desktop().winId()).save(buffer, 'png')
strio = StringIO.StringIO()
strio.write(buffer.data())
buffer.close()
del app
strio.seek(0)
return Image.open(strio)
def getScreenByPIL(self):
import ImageGrab
img = ImageGrab.grab()
return img
def getScreenByWx(self):
import wx
wx.App() # Need to create an App instance before doing anything
screen = wx.ScreenDC()
size = screen.GetSize()
bmp = wx.EmptyBitmap(size[0], size[1])
mem = wx.MemoryDC(bmp)
mem.Blit(0, 0, size[0], size[1], screen, 0, 0)
del mem # Release bitmap
#bmp.SaveFile('screenshot.png', wx.BITMAP_TYPE_PNG)
myWxImage = wx.ImageFromBitmap( myBitmap )
PilImage = Image.new( 'RGB', (myWxImage.GetWidth(), myWxImage.GetHeight()) )
PilImage.fromstring( myWxImage.GetData() )
return PilImage
if __name__ == '__main__':
s = screengrab()
screen = s.screen()
screen.show()
This one works on X11, and perhaps on Windows too (someone, please check). Needs PyQt4:
import sys
from PyQt4.QtGui import QPixmap, QApplication
app = QApplication(sys.argv)
QPixmap.grabWindow(QApplication.desktop().winId()).save('test.png', 'png')
I have a wrapper project (pyscreenshot) for scrot, imagemagick, pyqt, wx and pygtk.
If you have one of them, you can use it.
All solutions are included from this discussion.
Install:
easy_install pyscreenshot
Example:
import pyscreenshot as ImageGrab
# fullscreen
im=ImageGrab.grab()
im.show()
# part of the screen
im=ImageGrab.grab(bbox=(10,10,500,500))
im.show()
# to file
ImageGrab.grab_to_file('im.png')
Cross platform solution using wxPython:
import wx
wx.App() # Need to create an App instance before doing anything
screen = wx.ScreenDC()
size = screen.GetSize()
bmp = wx.EmptyBitmap(size[0], size[1])
mem = wx.MemoryDC(bmp)
mem.Blit(0, 0, size[0], size[1], screen, 0, 0)
del mem # Release bitmap
bmp.SaveFile('screenshot.png', wx.BITMAP_TYPE_PNG)
import ImageGrab
img = ImageGrab.grab()
img.save('test.jpg','JPEG')
this requires Python Imaging Library
You can use this
import os
os.system("import -window root screen_shot.png")
I couldn't take screenshot in Linux with pyscreenshot or scrot because output of pyscreenshot was just a black screen png image file.
but thank god there was another very easy way for taking screenshot in Linux without installing anything. just put below code in your directory and run with python demo.py
import os
os.system("gnome-screenshot --file=this_directory.png")
also there is many available options for gnome-screenshot --help
Application Options:
-c, --clipboard Send the grab directly to the clipboard
-w, --window Grab a window instead of the entire screen
-a, --area Grab an area of the screen instead of the entire screen
-b, --include-border Include the window border with the screenshot
-B, --remove-border Remove the window border from the screenshot
-p, --include-pointer Include the pointer with the screenshot
-d, --delay=seconds Take screenshot after specified delay [in seconds]
-e, --border-effect=effect Effect to add to the border (shadow, border, vintage or none)
-i, --interactive Interactively set options
-f, --file=filename Save screenshot directly to this file
--version Print version information and exit
--display=DISPLAY X display to use
bit late but nevermind easy one is
import autopy
import time
time.sleep(2)
b = autopy.bitmap.capture_screen()
b.save("C:/Users/mak/Desktop/m.png")
There is a python package for this Autopy
The bitmap module can to screen grabbing (bitmap.capture_screen)
It is multiplateform (Windows, Linux, Osx).
for ubuntu this work for me, you can take a screenshot of select window with this:
import gi
gi.require_version('Gtk', '3.0')
gi.require_version('Gdk', '3.0')
from gi.repository import Gdk
from gi.repository import GdkPixbuf
import numpy as np
from Xlib.display import Display
#define the window name
window_name = 'Spotify'
#define xid of your select 'window'
def locate_window(stack,window):
disp = Display()
NET_WM_NAME = disp.intern_atom('_NET_WM_NAME')
WM_NAME = disp.intern_atom('WM_NAME')
name= []
for i, w in enumerate(stack):
win_id =w.get_xid()
window_obj = disp.create_resource_object('window', win_id)
for atom in (NET_WM_NAME, WM_NAME):
window_name=window_obj.get_full_property(atom, 0)
name.append(window_name.value)
for l in range(len(stack)):
if(name[2*l]==window):
return stack[l]
window = Gdk.get_default_root_window()
screen = window.get_screen()
stack = screen.get_window_stack()
myselectwindow = locate_window(stack,window_name)
img_pixbuf = Gdk.pixbuf_get_from_window(myselectwindow,*myselectwindow.get_geometry())
to transform pixbuf into array
def pixbuf_to_array(p):
w,h,c,r=(p.get_width(), p.get_height(), p.get_n_channels(), p.get_rowstride())
assert p.get_colorspace() == GdkPixbuf.Colorspace.RGB
assert p.get_bits_per_sample() == 8
if p.get_has_alpha():
assert c == 4
else:
assert c == 3
assert r >= w * c
a=np.frombuffer(p.get_pixels(),dtype=np.uint8)
if a.shape[0] == w*c*h:
return a.reshape( (h, w, c) )
else:
b=np.zeros((h,w*c),'uint8')
for j in range(h):
b[j,:]=a[r*j:r*j+w*c]
return b.reshape( (h, w, c) )
beauty_print = pixbuf_to_array(img_pixbuf)
From this thread:
import os
os.system("import -window root temp.png")
It's an old question. I would like to answer it using new tools.
Works with python 3 (should work with python 2, but I haven't test it) and PyQt5.
Minimal working example. Copy it to the python shell and get the result.
from PyQt5.QtWidgets import QApplication
app = QApplication([])
screen = app.primaryScreen()
screenshot = screen.grabWindow(QApplication.desktop().winId())
screenshot.save('/tmp/screenshot.png')
Try it:
#!/usr/bin/python
import gtk.gdk
import time
import random
import socket
import fcntl
import struct
import getpass
import os
import paramiko
while 1:
# generate a random time between 120 and 300 sec
random_time = random.randrange(20,25)
# wait between 120 and 300 seconds (or between 2 and 5 minutes)
print "Next picture in: %.2f minutes" % (float(random_time) / 60)
time.sleep(random_time)
w = gtk.gdk.get_default_root_window()
sz = w.get_size()
print "The size of the window is %d x %d" % sz
pb = gtk.gdk.Pixbuf(gtk.gdk.COLORSPACE_RGB,False,8,sz[0],sz[1])
pb = pb.get_from_drawable(w,w.get_colormap(),0,0,0,0,sz[0],sz[1])
ts = time.asctime( time.localtime(time.time()) )
date = time.strftime("%d-%m-%Y")
timer = time.strftime("%I:%M:%S%p")
filename = timer
filename += ".png"
if (pb != None):
username = getpass.getuser() #Get username
newpath = r'screenshots/'+username+'/'+date #screenshot save path
if not os.path.exists(newpath): os.makedirs(newpath)
saveas = os.path.join(newpath,filename)
print saveas
pb.save(saveas,"png")
else:
print "Unable to get the screenshot."

Categories

Resources