Dynamically positioning of elements in pyFPDF - python

I am using pyFPDF to automate reports (trying at least), and I am having problems dynamically adjusting the positions of multi_cells and graphs after page 01.
I tried two things:
I was using fixed distances between each graph and text, using something like:
pdf = FPDF()
pdf.add_page()
pdf.set_font('Arial', style = 'B', size = 12)
position_y = 100
position_x = (A4_size_x)/2
text = 80
var = [0,1,2,3,4,5]
for i in var:
pdf.image(f'1. Gráficos/{cpf}-{fator}.png', #Graphs that were dynamically generated
x = position_x - 25,
y = position_y + (text * var),
w = 50,
h = 50)
pdf.set_xy(position_x - 50, ((position_y + (text * var)) - 45))
pdf.multi_cell(w = 100,
h = 5,
txt = 'Some Dynamic Text',
align='C')
pdf.output(f'2. Reports/{cpf}.pdf', 'F')
The problem here is that when it reaches a page break, the position values calculate from the new page and they break the whole document.
I also tried disabling page break:
pdf.set_auto_page_break(auto = False, margin = 0.0)
But then there is only one page in the document.
The question here is, how to keep adding objects in the same intervals across multiple pages?
Thank you!

Related

Creating PDF with Python using FPDF char by char

I'm trying to create a pdf with python and I want to put a text in pdf char by char.
I can't find out how to do it and when it saves output pdf all of the characters are on each other.
this is my code snippet:
from fpdf import FPDF
pdf = FPDF('P', 'mm', (100,100))
# Add a page
pdf.add_page()
# set style and size of font
# that you want in the pdf
pdf.add_font('ariblk', '', "ArialBlack.ttf", uni=True)
pdf.set_font("ariblk",size = int(50*0.8))
text = [['a','b','c','d','e','w','q'],['f','g','h','i','j','k','l']]
print("creating pdf...")
line = 0
for w in range(0,len(text)):
for h in range(0,len(text[w])):
# create a cell
r = int (50)
g = int (100)
b = int (10)
pdf.set_text_color(r, g, b)
text_out = text[w][h]
pdf.cell(0,line, txt = text_out, ln = 2)
# save the pdf with name .pdf
pdf.output(name = "img/output.pdf", dest='F')
print("pdf created!")
and this is what my code output is:
(this is copy-paste from the output pdf): iljfbeqaghdckw
(this is a screenshot of the output):
I don't know fpdf module but I think that your problem only comes from the fact that you don't change the X, Y coordinates of printing of each character.
You have to use 'pdf.set_xy()` to set the X and Y coordinates of each of your characters
I made small changes to the font and colors for my tests.
from fpdf import FPDF
import random
pdf = FPDF('P', 'mm', (100,100))
# Add a page
pdf.add_page()
# set style and size of font
# that you want in the pdf
#pdf.add_font('ariblk', '', "ArialBlack.ttf", uni=True)
pdf.set_font("Arial",size = int(24))
text = [['a','b','c','d','e','w','q'],['f','g','h','i','j','k','l']]
print("creating pdf...")
line = 10
for w in range(len(text)):
for h in range(len(text[w])):
# create a cell
r = random.randint(1, 255)
g = random.randint(1, 255)
b = random.randint(1, 255)
pdf.set_text_color(r, g, b)
text_out = text[w][h]
pdf.set_xy(10*w, 10*h)
pdf.cell(10, 10, txt=text_out, ln=0, align='C')
# save the pdf with name .pdf
pdf.output(name = "output.pdf", dest='F')
print("pdf created!")
Then, you have to adapt the offset of X and/or Y according to the display you want to obtain in print.
Remark: As you don't change the values of r, g, b in your for loops, the best is to go up the assignment of variables r, g and b before the for loops
Output in the PDF:
a f
b g
c h
d i
e j
w k
q l

Search/Update Graphics in a PDF

Is there any way to search for a graphic in a PDF page?
For example, I have used the PyFPDF library to generate a PDF with few rectangles and circles (code below). Now I would like to
get a list of ellipses that are printed in each page and their location.
and update the color of all the circles to blue
Is there any library which allows this?
PDF generation with few rectangles and circles:
Sample PDF generated using code below: Download the PDF
from fpdf import FPDF
# Prepare PDF generator
pdf = FPDF(orientation = 'L', unit = 'in', format = 'A4')
pdf.add_page()
pdf.set_fill_color(0, 0, 0)
# Draw the rectangle
pdf.rect(x = 1, y = 1, w = 2, h = 2, style = 'S')
pdf.rect(x = 1.5, y = 1.5, w = 5, h = 1, style = 'S')
pdf.ellipse(x = 1, y = 4, w = 1, h = 1, style = 'S')
pdf.ellipse(x = 4, y = 5, w = 1, h = 1, style = 'S')
pdf.ellipse(x = 9, y = 2, w = 1, h = 1, style = 'F')
pdf.rect(x = 6, y = 5, w = 2, h = 1, style = 'F')
# Write to file
pdf.output("test.pdf")
NOT YOUR ANSWER just a visual comment
Its not been easy to dissect and rebuild by hand so used some shortcuts. but the pdf supplied has all vectors as only one black encoded stream object that looks something like this
4 0 obj
<</Filter /FlateDecode /Length 251>>
stream
xœU’»q!„sª 3舜ºÆ¿#'Nܾ-.‚ïЮVßCN¥Åß¹äœãWh<7±°$¶Hª‹Þ°þ|Æ#ÙÖ^­­
©yW¸Ì¼˜­‚ïcÂÕHÜ’M]Þr˜F0KÄÝ:€›ÄäBIstK¹,A# á9E>£ŽóPïÌÔWQ¹¼„õÑI²^9ØìR‚à;ÂáÖW(©Êåš¾Þõœ"!”¼k‚Æû‹Rñ ÏTëóöÒ²'®¶;F¨uç8§j—òaZ°ô²r#)­_¾ ù·¼+‰ |æñTøº×óÿ_Ø+ü)„Ÿ
endstream
endobj
Thus at a simple level everything at once could possibly be prefixed as blue. but if we decode the stream, it is easier to inject colours per part of stroked stream here I marked the stack targets in green and have injected 0 0 1 rg for blue (end of square rect & 1 group of the chords and 0 g for stop Graphics change between times
Thus as with many a PDF question:-
Can I reverse engineer PDF to
"other the and that ,this Do" the answer is do it at source
$pdf->SetDrawColor(0,0,0);
$pdf->Circle(100,50,30,'D');
$pdf->SetFillColor(0,0,255);
$pdf->Circle(110,47,7,'F');

Having trouble getting subplots to show up correctly with larger data sets

I am having trouble getting subplots to show up correctly with larger data sets.
I am ok with having the figure grow for my application. I am also ok with having the figure grow such that all the graphs would be about the size of the ones showing up in the small data set example if that is possible. (anaconda3/v4.2.0/python)
plt.rcParams['figure.autolayout']=True
figa, axa = plt.subplots(rowcnt, colcnt)
figa.suptitle("Users Disk Space Usage Over Time.\n")
ax_index = 0
for r in range(rowcnt)
for c in range(colcnt):
n = r * c
user = gr.columns[n]
ur = gr[user]
x = ur.index
y = ur.values
while is_color_like(colorpairs[colorindex]) == False or is_color_like(colorpairs[colorindex+1]) == False :
colorindex = int((colorindex + 2) % (len(colorpairs)/2))
axa[r,c].plot(x, y, color=colorpairs[colorindex+1], alpha=0.6)
plt.setp(axa[r,c].get_xticklabels(), rotation=30)
if len(x) > 1:
axa[r,c].fill_between(x, y, color=colorpairs[colorindex],alpha=0.4)
axa[r,c].set_ylim(0,disksizebytes)
axa[r,c].set_title(user)
axa[r,c].set_xlabel('date')
axa[r,c].set_ylabel('space used')
axa[r,c].grid(True)
i += 1
colorindex = int((colorindex + 2) % (len(colorpairs)/2))
detailarryimage = "{}/detailarryimage.png".format(datafolder)
figa.savefig(detailarryimage)
Small Set Image
Large Set Image

TypeError: Int object is not iterable

I am trying to generate dataset for OCR using different fonts, but upon a certain for loop, the iteration giving me and error Typeerror: int object is not iterable.
I have searched enough to conclude that most of the answers on StackOverFlow suggests to use the range in my for loop including (len) but I am not sure if I follow that.
The function is as follows:
def gen_rand_string_data(data_count,
min_char_count=3,
max_char_count=8,
max_char=16,
x_pos='side',
img_size=(32, 256, 1),
font=cv2.FONT_HERSHEY_SIMPLEX,
font_scale=np.arange(0.7, 1, 0.1),
thickness=range(1, 3, 1)):
'''
random string data generation
'''
start_time = dt.datetime.now()
images = []
labels = []
color = (255, 255, 255)
count = 0
char_list = list(string.ascii_letters) \
+ list(string.digits) \
+ list(' ')
while (1):
for fs in font_scale:
for thick in thickness:
for f in font:
img = np.zeros(img_size, np.uint8)
char_count = np.random.randint(min_char_count, \
(max_char_count + 1))
rand_str = ''.join(np.random.choice(char_list, \
char_count))
# generate image data
text_size = cv2.getTextSize(rand_str, f, fs, thick)[0]
if x_pos == 'side':
org_x = 0
else:
org_x = (img_size[1] - text_size[0]) // 2
org_y = (img_size[0] + text_size[1]) // 2
cv2.putText(img, rand_str, (org_x, org_y), f, fs, \
color, thick, cv2.LINE_AA)
label = list(rand_str) + [' '] \
* (max_char - len(rand_str))
for i, t in enumerate(label):
label[i] = char_list.index(t)
label = np.uint8(label)
images.append(img)
labels.append(label)
count += 1
if count == data_count:
break
else:
continue
break
else:
continue
break
else:
continue
break
end_time = dt.datetime.now()
print("time taken to generate data", end_time - start_time)
return images, labels
The error raised is at line : for f in font:
What am I doing wrong here? Do I have to use the range()?
font=cv2.FONT_HERSHEY_SIMPLEX
for f in font:
...
In CV2, the font is a simple integer representing the font itself, I'm not entirely sure(a) why you're trying to iterate over it.
If you wanted to iterate over sizes of the font, you would have to use (for example) the fontScale parameter of putText().
If you want to iterate over a collection of fonts, you have to provide that collection, such as with one of:
font = [cv2.FONT_HERSHEY_SIMPLEX] # one font as a collection
font = [cv2.FONT_HERSHEY_SIMPLEX, cv2.FONT_HERSHEY_PLAIN] # two fonts
If you only have the one font, then don't iterate over it at all. Get rid of the for f in font line (unindenting the stuff currently "inside" it) and just use font wherever you're currently using f.
(a) Python is having similar troubles trying to figure out your intent :-)

Platypus - Using Multiple Frames in a PageTemplate

I'm working on writing some PDF generation code for a Django based website using ReportLab / Platypus to generate the PDF.
I've subclassed PageTemplate so I can have some consistent page trim and have included code to generate multi-column layouts for me to suit requirements. I've currently got showBoundary=1 on for debugging.
However, I'm only seeing the first frame boundary appearing when I render a two column layout. What might be going wrong?
class ReportPageTemplate(PageTemplate):
def __init__(self, id='basic', columns=1, pagesize=A4, leftMargin=(2*cm), bottomMargin=(2.1*cm), colmargin=(0.5*cm)):
(right, top) = pagesize
right -= leftMargin
top -= bottomMargin
height = top - bottomMargin
width = (right - leftMargin)
# Subtract out blank space between columns
colwidth = (width - ((columns - 1) * colmargin)) / columns
frames = []
for col in range(columns):
left = leftMargin + (col * (colwidth + colmargin))
frames.append(Frame(left, bottomMargin, colwidth, height, showBoundary=1))
PageTemplate.__init__(self, id=id, frames=frames, pagesize=pagesize)
def beforeDrawPage(self, canvas, doc):
print self.id
(width, height) = canvas._pagesize
canvas.setLineWidth(0.2 * cm)
canvas.line(0.5*cm, height - (2*cm), width - (0.5*cm), height - (2*cm))
canvas.line(0.5*cm, (2*cm), width - (0.5*cm), (2*cm))
Ah, well I feel foolish.
The second frame is only rendered if the first one fills up. For testing purposes I needed to include a FrameBreak object to force it to draw both columns.
The code is actually already working.

Categories

Resources