You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
doc.xref_stream(xref).decode().splitlines() does NOT split the line #3419
after upgrade to PyMuPDF 1.24.2, the following code will print one paragraph for each page, not split lines.
Downgrade to PyMuPDF 1.22.5, doc.xref_stream(xref).decode().splitlines() works again, and it will print "line", not "paragraph" for one page.
How to reproduce the bug
for page in doc:
page.clean_contents() # cleanup page painting commands
xref = page.get_contents()[0] # get xref of the resulting source
cont0 = doc.xref_stream(xref).decode("utf8").splitlines() # and read it as lines of strings
for line in cont0:
print(line)
@huahua74 There still is a way to get a page's contents with line breaks. It is with less comfort - on the other hand, MuPDF team had good reasons for their change.
Line breaks between PDF commands are generated as part of saving the file with the option pretty=True. So if you use this little function with the page as parameter, it will return you the contents as desired:
defpretty_contents(page):
"""Invoke with some PDF page."""doc=page.parent# owing documentnew=fitz.open() # make temp PDF, copy page into itnew.insert_pdf(doc, from_page=page.number, to_page=page.number)
# save temp pdf with pretty optionpdfdata=new.tobytes(pretty=True, clean=True)
new.close() # close and re-opennew=fitz.open("pdf", pdfdata)
# read and return /Contents ... now with linebreakscontents=new[0].read_contents()
new.close()
returncontentscontents_lines=pretty_contents(page).splitlines()
many thanks! it is extremely helpful! I also find your reply earlier without "splitline()". Runing the script below under PyMuPDF 1.22.5 and 1.24 will end with different results. The following work smooth with 1.24 but not 1.22.5.
page.clean_contents()
xref = page.get_contents()[0] # get xref of resulting /Contents object
cont = bytearray(page.read_contents()) # read the contents source as a (modifyable) bytearray
if cont.find(b"/Subtype/Watermark") > 0: # this will confirm a marked-content watermark is present
print("marked-content watermark present")
while True:
i1 = cont.find(b"/Artifact") # start of definition
if i1 < 0: break # none more left: done
i2 = cont.find(b"EMC", i1) # end of definition
cont[i1-2 : i2+3] = b"" # remove the full definition source "q ... EMC"
doc.update_stream(xref, cont) # replace the original source
doc.ez_save("x.pdf") # save to new file
I comment out the page.clean_contents(), then the splitlines works ok now.
The code iss revised a little bit from previous code in this discussion #1855. It works ok on current pymupdf release.
pip install PyMuPDF
importpymupdfdefprocess_page(page : pymupdf.Page):
"""Process one page."""# doc = page.parent # the page's owning document# page.clean_contents() # clean page painting syntaxxref=page.get_contents()[0] # get xref of resulting /Contentschanged=0# this will be returned# read sanitized contents, splitted by line breakscont_lines=page.read_contents().splitlines()
print(len(cont_lines))
# print(cont_lines)foriinrange(len(cont_lines)): # iterate over the linesline=cont_lines[i]
# print(line)ifnot (line.startswith(b"/Artifact") andb"/Watermark"inline):
continue# this was not for us# line number i starts the definition, j ends it:print(line)
j=cont_lines.index(b"EMC", i)
forkinrange(i, j):
# look for image / xobject invocations in this line rangedo_line=cont_lines[k]
ifdo_line.endswith(b"Do"): # this invokes an image / xobjectcont_lines[k] =b""# remove / empty this linechanged+=1ifchanged>0: # if we did anything, write back modified /Contentsdoc.update_stream(xref, b"\n".join(cont_lines))
returnchangedfpath='your_pdf_file_path/file_name.pdf'doc=pymupdf.open(fpath)
changed=0# indicates successful removalsforpageindoc:
changed+=process_page(page) # increase number of changesifchanged>0:
x="s"ifdoc.page_count>1else""print(f"{changed} watermarks have been removed on {doc.page_count} page{x}.")
doc.ez_save(doc.name.replace(".pdf", "-nowm.pdf"))
else:
print("Nothing to change")
There have been changes to the underlying MuPDF code for clean_contents():
It does no longer generate line breaks between PDF contents commands, but instead 1 space.
This has happened for an overall improved compressibility of content streams.
Here is a little trick to get back these line breaks:
importpymupdfdefpretty_contents(page):
doc=page.parentnew=pymupdf.open()
new.insert_pdf(doc, from_page=page.number, to_page=page.number)
pdfdata=new.tobytes(pretty=True, clean=True)
new.close()
new=pymupdf.open("pdf", pdfdata)
xrefs=new[0].get_contents()
ifnotxrefs:
returnb""contents=new.xref_stream(xrefs[0])
new.close()
returncontentsdoc=pymupdf.open("input.pdf")
page=doc[0]
cont=pretty_contents(page)
# "cont" has line breaks again
Activity
JorjMcKie commentedon Apr 27, 2024
This is not a bug.
MuPDF has changed to no longer generate line breaks between PDF commands, but spaces.
JorjMcKie commentedon Apr 28, 2024
@huahua74 There still is a way to get a page's contents with line breaks. It is with less comfort - on the other hand, MuPDF team had good reasons for their change.
Line breaks between PDF commands are generated as part of saving the file with the option
pretty=True. So if you use this little function with the page as parameter, it will return you the contents as desired:ragebear00 commentedon May 8, 2024
many thanks! it is extremely helpful! I also find your reply earlier without "splitline()". Runing the script below under PyMuPDF 1.22.5 and 1.24 will end with different results. The following work smooth with 1.24 but not 1.22.5.
firezym commentedon Dec 4, 2024
I comment out the
page.clean_contents(), then the splitlines works ok now.The code iss revised a little bit from previous code in this discussion #1855. It works ok on current pymupdf release.
pip install PyMuPDFJorjMcKie commentedon Dec 4, 2024
There have been changes to the underlying MuPDF code for
clean_contents():It does no longer generate line breaks between PDF contents commands, but instead 1 space.
This has happened for an overall improved compressibility of content streams.
Here is a little trick to get back these line breaks: