def generate_content_page(header_to_pagenumber, headers_and_subheaders, page_height, page_width): """ Generates a document that serves as a Table of Contents, with header and subheader information. """ doc = fitz.open() page = doc.newPage(height=page_height, width=page_width) horizontal_start_point = 40 vertical_start_point = 60 spacing = 15 num_lines = 1 tab = 30 # Add Table of Contents heading (centered) rect_topleft = fitz.Point(0, vertical_start_point + num_lines * spacing) num_lines += 4 rect_bottomright = fitz.Point(page_width, vertical_start_point + num_lines * spacing) rect = fitz.Rect(rect_topleft, rect_bottomright) page.insertTextbox(rect, "Table of Contents", fontsize=32, align=fitz.TEXT_ALIGN_CENTER) num_lines += 2 # Create a TextWriter (per page) wr = fitz.TextWriter(page.rect) for h1_item, h2_items in headers_and_subheaders.items(): # Insert the h1_item p = fitz.Point( horizontal_start_point, vertical_start_point + num_lines * spacing ) wr.append(p, h1_item, fontsize=24, font=fitz.Font("Arial")) num_lines += 2 for h2_item in h2_items: # Insert each h2_item p_tab = fitz.Point( tab + horizontal_start_point, vertical_start_point + num_lines * spacing ) wr.append(p_tab, h2_item, fontsize=16) # Insert ... between h2_item and page number p_tab_number = fitz.Point( tab + horizontal_start_point + 500, vertical_start_point + num_lines * spacing, ) add_dot_connector(wr, wr.lastPoint, p_tab_number) # Insert page number for h2_item wr.append(p_tab_number, str(header_to_pagenumber[h2_item]), fontsize=16) num_lines += 1 # Move to new page if nearing end of page if num_lines >= 45: wr.writeText(page) page = doc.newPage(height=page_height, width=page_width) wr = fitz.TextWriter(page.rect) num_lines = 0 num_lines += 2 wr.writeText(page) return doc
def generate_content_page(header_to_pagenumber, headers_and_subheaders, page_height, page_width): doc = fitz.open() page = doc.newPage(height=page_height, width=page_width) horizontal_start_point = 40 vertical_start_point = 60 spacing = 15 num_lines = 1 tab = 30 p = fitz.Point(horizontal_start_point + 250, vertical_start_point + num_lines * spacing) page.insertText(p, "Table of Contents", fontname="helv", fontsize=32) num_lines += 4 # Create a TextWriter (per page) wr = fitz.TextWriter(page.rect) for h1_item, h2_items in headers_and_subheaders.items(): # Insert the h1_item p = fitz.Point(horizontal_start_point, vertical_start_point + num_lines * spacing) wr.append(p, h1_item, fontsize=24) num_lines += 2 for h2_item in h2_items: # Insert each h2_item p_tab = fitz.Point(tab + horizontal_start_point, vertical_start_point + num_lines * spacing) wr.append(p_tab, h2_item, fontsize=16) # Insert ... between h2_item and page number p_tab_number = fitz.Point( tab + horizontal_start_point + 500, vertical_start_point + num_lines * spacing, ) add_dot_connector(wr, wr.lastPoint, p_tab_number) # Insert page number for h2_item wr.append(p_tab_number, str(header_to_pagenumber[h2_item]), fontsize=16) num_lines += 1 # Move to new page if nearing end of page if num_lines >= 45: wr.writeText(page) page = doc.newPage(height=page_height, width=page_width) wr = fitz.TextWriter(page.rect) num_lines = 0 num_lines += 2 wr.writeText(page) return doc
def generate_pdf(kanji_code, kanji): try: file_handle = fitz.open(template) page = file_handle[0] page.clean_contents() image_path = os.path.join( settings.BASE_DIR, 'kanjis/kanjivg/kanji/{}.svg'.format(kanji_code)) # x0, y0, x1, y1 - https://pymupdf.readthedocs.io/en/latest/rect.html#rect img_rect = fitz.Rect(page.rect.width - 67, 28, page.rect.width - 20, 82) png_path = os.path.join(settings.BASE_DIR, 'kanjis/pdf/tmp/{}.png'.format(kanji_code)) svg2png(url=image_path, write_to=png_path) page.insertImage(img_rect, filename=png_path) page.insertText((page.rect.width - 62, 18), 'JLPT' + str(kanji.jlpt) or '?') text_writer = fitz.TextWriter( (0, 0, page.rect.width - 70, page.rect.height)) text_writer.append(fitz.Point(20, 38), 'Kun: ' + ', '.join(kanji.kun_readings)) text_writer.append(fitz.Point(20, 53), 'On: ' + ', '.join(kanji.on_readings)) text_writer.append(fitz.Point(20, 68), 'Meanings: ' + ', '.join(kanji.meanings)) text_writer.writeText(page) file_handle.save('{}/{}.pdf'.format(settings.MEDIA_ROOT, kanji_code), deflate=True) os.remove(png_path) except Exception as e: raise e
def __init__( self, path: str, width: int, height: int, buffer_width: int, buffer_height: int, supplementary_path: str = None, turbo_skip: int = 2, zoom_multiplier: float = 1.5, ) -> None: self.path = path self.width = width self.height = height self.buffer_width = buffer_width self.buffer_height = buffer_height self.supplementary_path = supplementary_path self.zoom_multiplier = zoom_multiplier self.zoom_level = 0 self.page_number = None self.page_image = None self.clip_rect = fitz.Rect() if self.path and Path(self.path).exists(): self.document = fitz.open(self.path) else: # Create an empty pdf self.document = fitz.open() # Calculate width of text text_width = math.ceil( fitz.get_text_length(self.NO_MANUAL_TEXT, fontsize=self.NO_MANUAL_FONTSIZE)) # Create page that's wider than the text so that it scales well page = self.document.new_page(width=text_width * 3, height=self.NO_MANUAL_HEIGHT) # Draw a black background page.draw_rect(page.rect, color=(0, 0, 0), fill=(0, 0, 0), overlay=False) # Draw the text, positioned in the center text_writer = fitz.TextWriter(page.rect) text_writer.fill_textbox( page.rect, self.NO_MANUAL_TEXT, pos=(int((page.rect.width - text_width) / 2), self.NO_MANUAL_FONTSIZE), fontsize=self.NO_MANUAL_FONTSIZE, ) text_writer.write_text(page, color=(1, 1, 1)) # Add supplementary material (usually reference info) if self.supplementary_path and Path(self.supplementary_path).exists(): pdf = fitz.open(self.supplementary_path) self.document.insert_pdf(pdf) pdf.close()
def print_descr(annot): """Print a short description to the right of the annot rect.""" rect = annot.rect page = annot.parent writer = fitz.TextWriter(page_rect, color=red) writer.append(rect.br + (10, -5), "%s annotation" % annot.type[1], font=font) writer.writeText(page)
def _change_font_and_update_bbox(self, font_name: str): '''Set new font, and update font size, span/char bbox accordingly. It's generally used for span with unnamed fonts. See this `issue <https://github.com/pymupdf/PyMuPDF/issues/642>`_. In corner case, where the PDF file containing unnamed and not embedded fonts, the span bbox extracted from ``PyMuPDF`` is not correct. ``PyMuPDF`` provides feature to replace these unnamed fonts with specified fonts, then extract correct bbox from the updated PDF. Since we care less about the original PDF itself but its layout, the idea here is to set a default font for text spans with unnamed fonts, and estimate the updated bbox with method from ``fitz.TextWriter``. Args: font_name (str): Font name. ''' # set new font property self.font = font_name # compute text length under new font with that size font = fitz.Font(font_name) new_length = font.text_length(self.text, fontsize=self.size) if new_length > self.bbox.width: self.size *= self.bbox.width / new_length # estimate occupied rect when added with TextWriter x0, y0, x1, y1 = self.bbox tw = fitz.TextWriter((0, 0, x1, y1)) rect, _ = tw.append( self.chars[0]. origin, # the bottom left point of the first character self.text, font=font, fontsize=self.size) # update span bbox # - x-direction: use original horizontal range # - y-direction: centerline defined by estimated vertical range, and height by font size buff = (rect.height - self.size) / 2.0 y0 = rect.y0 + buff y1 = rect.y1 - buff self.update_bbox((x0, y0, x1, y1)) # update contained char bbox for char in self.chars: x0, _, x1, _ = char.bbox char.update_bbox((x0, y0, x1, y1))
def test_textbox1(): """Use TextWriter for text insertion.""" doc = fitz.open() page = doc.new_page() rect = fitz.Rect(50, 50, 400, 400) blue = (0, 0, 1) tw = fitz.TextWriter(page.rect, color=blue) tw.fill_textbox( rect, text, align=fitz.TEXT_ALIGN_LEFT, fontsize=12, ) tw.write_text(page, morph=(rect.tl, fitz.Matrix(1, 1))) # check text containment assert page.get_text() == page.get_text(clip=rect) page.write_text(writers=tw)
def tilted_span(page, wdir, span, font): """Output a non-horizontal span.""" cos, sin = wdir # writing direction from the line matrix = fitz.Matrix(cos, -sin, sin, cos, 0, 0) # corresp. matrix text = span["text"] # text to write bbox = fitz.Rect(span["bbox"]) fontsize = span["size"] # adjust fontsize tl = font.text_length(text, fontsize) # text length with new font m = max(bbox.width, bbox.height) # must not exceed max bbox dimension if tl > m: fontsize *= m / tl # otherwise adjust opa = 0.1 if fontsize > 100 else 1 # fake opacity for large fontsizes tw = fitz.TextWriter(page.rect, opacity=opa, color=fitz.sRGB_to_pdf(span["color"])) origin = fitz.Point(span["origin"]) if sin > 0: # clockwise rotation origin.y = bbox.y0 tw.append(origin, text, font=font, fontsize=fontsize) tw.writeText(page, morph=(origin, matrix))
def test_textbox4(): """Use TextWriter for text insertion.""" doc = fitz.open() ocg = doc.add_ocg("ocg1") page = doc.new_page() rect = fitz.Rect(50, 50, 400, 600) blue = (0, 0, 1) tw = fitz.TextWriter(page.rect, color=blue) tw.fill_textbox( rect, text, align=fitz.TEXT_ALIGN_LEFT, fontsize=12, font=fitz.Font("cour"), right_to_left=True, ) tw.write_text(page, oc=ocg, morph=(rect.tl, fitz.Matrix(1, 1))) # check text containment assert page.get_text() == page.get_text(clip=rect)
def test_textbox3(): """Use TextWriter for text insertion.""" doc = fitz.open() page = doc.new_page() font = fitz.Font("cjk") rect = fitz.Rect(50, 50, 400, 400) blue = (0, 0, 1) tw = fitz.TextWriter(page.rect, color=blue) tw.fill_textbox( rect, text, align=fitz.TEXT_ALIGN_LEFT, font=font, fontsize=12, right_to_left=True, ) tw.write_text(page, morph=(rect.tl, fitz.Matrix(1, 1))) # check text containment assert page.get_text() == page.get_text(clip=rect) doc.scrub() doc.subset_fonts()
continue font = fitz.Font(fontbuffer=font_buffers[new_fontname]) text = span["text"].replace(chr(0xFFFD), chr(0xB6)) # guard against non-utf8 characters textb = text.encode("utf8", errors="backslashreplace") text = textb.decode("utf8", errors="backslashreplace") span["text"] = text if wdir != [1, 0]: # special treatment for tilted text tilted_span(page, wdir, span, font) continue color = span["color"] # make or reuse textwriter for the color if color in textwriters.keys(): # already have a textwriter? tw = textwriters[color] # re-use it else: # make new tw = fitz.TextWriter(page.rect) # make text writer textwriters[color] = tw # store it for later use try: tw.append( span["origin"], text, font=font, fontsize=resize(span, font), # use adjusted fontsize wmode=wmode, markup_dir=markup_dir, bidi_level=bidi_level, ) except: print("page %i exception:" % page.number, text) # now write all text stored in the list of text writers
annot.setBorder(width=0.3, dashes=[2]) annot.update(text_color=blue, fill_color=gold) print_descr(annot) r = annot.rect + displ annot = page.addTextAnnot(r.tl, t1) print_descr(annot) # Adding text marker annotations: For each annotation type, first insert a # a (unique) text and tilt it a bit. Then calculate the quad of the text # and feed it into annot creation. pos = annot.rect.tl + displ.tl mat = fitz.Matrix(-15) writer = fitz.TextWriter(page_rect) # create TextWriter object writer.append(pos, highlight, font=font) # append text writer.writeText(page, morph=(pos, mat)) # write to the page with rotation writer.textRect.x0 = pos.x # use actual text start / end writer.textRect.x1 = writer.lastPoint.x quad_highlight = writer.textRect.morph(pos, ~mat) # calculate text quad pos = quad_highlight.rect.bl # the next writing position writer = fitz.TextWriter(page_rect) writer.append(pos, underline, font=font) writer.writeText(page, morph=(pos, mat)) writer.textRect.x0 = pos.x writer.textRect.x1 = writer.lastPoint.x quad_underline = writer.textRect.morph(pos, ~mat) pos = quad_underline.rect.bl
text = """This is a text of mixed languages to demonstrate MuPDF's text output capabilities. Font used for the non-CJK characters: '%s', font size: %g, color: %s. Euro: €, some special signs: |~°²³, general Latin: ñäöüßâ Japan: 熊野三山本願所は、15世紀末以降における熊野三山(熊野本宮、熊野新宮 Greece: Στα ερείπια της πόλης, που ήταν ένα σημαντικό Korea: 에듀롬은 하나의 계정으로 전 세계 고등교육 기관의 인터넷에 접속할 Russia: Ко времени восшествия на престол Якова I в значительной China: 北京作为城市的历史可以追溯到3,000年前。西周初年,周武王封召公奭于燕國。 This longer text part checks, whether the very last line will not be justified either.""" % ( font.name, fsize, blue, ) fill_rect = fitz.Rect(72, 72, 372, 372) # keep above text in here writer = fitz.TextWriter(page_rect, color=blue) # start a text writer writer.fillTextbox( # fill in above text fill_rect, # keep text inside this text, # the text align=fitz.TEXT_ALIGN_JUSTIFY, # alignment warn=True, # keep going if too much text fontsize=fsize, font=font, ) # write our results to the PDF page. writer.writeText(page) # To show what happened, draw the rectangles, etc. shape = page.newShape()
def page_out(doc, text): page = doc.newPage(width=w, height=h) # make new page tw = fitz.TextWriter(page_rect) # make text writer tw.fillTextbox(print_rect, text, font=font, fontsize=fontsize) tw.writeText(page) # write the text to the page
import fitz, os thisdir = lambda f: os.path.join(os.path.dirname(__file__), f) thisfile = os.path.abspath(__file__) outfile = thisfile.replace(".py", ".pdf") font1 = fitz.Font("helv") font2 = fitz.Font("tiro") doc = fitz.open() page = doc.newPage() point = fitz.Point(50, 72) matrix = fitz.Matrix(-20) wrt1 = fitz.TextWriter(page.rect, color=(0, 0, 1)) wrt2 = fitz.TextWriter(page.rect, color=(1, 0, 0)) _, last = wrt1.append(point, "This text changes color,", font1, 11) _, last = wrt2.append(last, " font and fontsize", font2, 18) _, last = wrt1.append(last, " several", font1, 11) _, last = wrt2.append(last, " times!", font2, 24) # output both text writers on current page in arbitrary sequence wrt1.writeText(page, morph=(point, matrix)) # using the same morph parameter wrt2.writeText(page, morph=(point, matrix)) # also preserves the joint text. # make a new page page = doc.newPage() rect = wrt1.textRect | wrt2.textRect # join rect of blue and red text # make new rectangle from it, rotated by 90 degrees nrect = fitz.Rect( rect.tl, # same top-left, but width and height exchanged